Skip to content

Commit

Permalink
add file_argument function
Browse files Browse the repository at this point in the history
This allows for handling special arguments like
`--version-script=<path>`, where the `=` is required and may not be
passed as two separate arguments. A new dsl function `file_argument` is
added, and is used like this:
```meson
executable(
  ...,
  c_args : [file_argument('--file-arg=', files('foo.arg'))]
)
```
This returns an opaque object, which meson will correctly add to either
the build or link depends (depending on whether it was passed to a
compile argument or to a link argument), and will convert the argument
into `--file-arg=relative/path/to/foo.arg`
  • Loading branch information
dcbaker committed Sep 25, 2023
1 parent 544937d commit e5b931c
Show file tree
Hide file tree
Showing 12 changed files with 119 additions and 25 deletions.
1 change: 1 addition & 0 deletions data/syntax-highlighting/vim/syntax/meson.vim
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ syn keyword mesonBuiltin
\ error
\ executable
\ files
\ file_argument
\ find_program
\ generator
\ get_option
Expand Down
18 changes: 18 additions & 0 deletions docs/markdown/file_argument.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
## New file_argument() function

Sometimes arguments need to be passed as a single string, but that string needs
to contain a File as part of the string. Consider how linker scripts work with GCC:
`-Wl,--version-script,<file>`. This is painful to deal with when the `<file>` is
a `files()` object. with `file_argument()` this becomes easier.

```meson
build_target(
...,
c_args : [file_argument('--file-arg=', files('foo.file'))],
link_args : [file_argument('-Wl,--version-script,', file('file.map'))],
)
```

Meson will automatically create the correct strings, relative to the build
directory, and will automatically add the file to the correct depends, so that
compilation and/or linking will correctly depend on that file.
32 changes: 32 additions & 0 deletions docs/yaml/functions/file_argument.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: file_argument
since: 1.3.0
returns: file_arg
description: |
Passes a string argument that must be joined to a file path in [[@build_tgt]]
This allows passing [[@file]] objects to the `<lang>_args` and `link_args`
parameters of a [[@build_tgt]]. Once passed they will be converted to a string
argument, and the [[@file]] will be added to the appropriate dependencies
(either build or link).
example: |
This is particularly useful when working with linker scripts:
```meson
tgt = build_tgt(
...,
link_with : file_argument('-Wl,--linker-script,' files('link.map')),
)
```
In this case Meson will pass `-Wl,--linker-script,link.map` (or a relative
path for `link.map`), and will add it to the link dependencies for `tgt`
posargs:
arg:
type: str
description: The string part of the argument

file:
type: file
description: The file part of the argument
3 changes: 3 additions & 0 deletions docs/yaml/objects/file_arg.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
name: file_arg
long_name: file argument
description: Opaque object returned by [[file_argument]]
1 change: 1 addition & 0 deletions mesonbuild/ast/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ def __init__(self, source_root: str, subdir: str, subproject: str, visitors: T.O
'add_languages': self.func_do_nothing,
'declare_dependency': self.func_do_nothing,
'files': self.func_do_nothing,
'file_argument': self.func_do_nothing,
'executable': self.func_do_nothing,
'static_library': self.func_do_nothing,
'shared_library': self.func_do_nothing,
Expand Down
9 changes: 5 additions & 4 deletions mesonbuild/backend/ninjabackend.py
Original file line number Diff line number Diff line change
Expand Up @@ -1461,7 +1461,7 @@ def generate_cs_target(self, target: build.BuildTarget):
src_list = target.get_sources()
compiler = target.compilers['cs']
rel_srcs = [os.path.normpath(s.rel_to_builddir(self.build_to_src)) for s in src_list]
deps = []
deps = self.get_target_depend_files(target)
commands = compiler.compiler_args(target.extra_args['cs'])
commands += compiler.get_buildtype_args(buildtype)
commands += compiler.get_optimization_args(target.get_option(OptionKey('optimization')))
Expand Down Expand Up @@ -1878,6 +1878,7 @@ def generate_rust_target(self, target: build.BuildTarget) -> None:
os.path.join(t.subdir, t.get_filename())
for t in itertools.chain(target.link_targets, target.link_whole_targets)
]
deps.extend(self.get_target_depend_files(target))

# Dependencies for rust-project.json
project_deps: T.List[RustDep] = []
Expand Down Expand Up @@ -2196,8 +2197,8 @@ def determine_swift_dep_modules(self, target):
result.append(self.swift_module_file_name(l))
return result

def get_swift_link_deps(self, target):
result = []
def get_swift_link_deps(self, target: build.BuildTarget) -> T.List[str]:
result = self.get_target_depend_files(target)
for l in target.link_targets:
result.append(self.get_target_filename(l))
return result
Expand Down Expand Up @@ -3035,7 +3036,7 @@ def generate_single_compile(self, target: build.BuildTarget, src,
pch_dep = arr

compiler_name = self.compiler_to_rule_name(compiler)
extra_deps = []
extra_deps = self.get_target_depend_files(target)
if compiler.get_language() == 'fortran':
# Can't read source file to scan for deps if it's generated later
# at build-time. Skip scanning for deps, and just set the module
Expand Down
8 changes: 8 additions & 0 deletions mesonbuild/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,14 @@ def get_target_macos_dylib_install_name(ld) -> str:
class InvalidArguments(MesonException):
pass


@dataclass
class FileArgument(HoldableObject):

arg: str
file: File


@dataclass(eq=False)
class DependencyOverride(HoldableObject):
dep: dependencies.Dependency
Expand Down
24 changes: 23 additions & 1 deletion mesonbuild/interpreter/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,7 @@ def build_func_dict(self) -> None:
'error': self.func_error,
'executable': self.func_executable,
'files': self.func_files,
'file_argument': self.func_file_argument,
'find_program': self.func_find_program,
'generator': self.func_generator,
'get_option': self.func_get_option,
Expand Down Expand Up @@ -435,6 +436,7 @@ def build_holder_map(self) -> None:

# Meson types
mesonlib.File: OBJ.FileHolder,
build.FileArgument: OBJ.FileArgumentHolder,
build.SharedLibrary: OBJ.SharedLibraryHolder,
build.StaticLibrary: OBJ.StaticLibraryHolder,
build.BothLibraries: OBJ.BothLibrariesHolder,
Expand Down Expand Up @@ -676,6 +678,12 @@ def func_import(self, node: mparser.BaseNode, args: T.Tuple[str],
def func_files(self, node: mparser.FunctionNode, args: T.Tuple[T.List[str]], kwargs: 'TYPE_kwargs') -> T.List[mesonlib.File]:
return self.source_strings_to_files(args[0])

@FeatureNew('file_argument', '1.3.0')
@typed_pos_args('file_argument', str, mesonlib.File)
@noKwargs
def func_file_argument(self, node: mparser.FunctionNode, args: T.Tuple[str, mesonlib.File], kwargs: TYPE_kwargs) -> build.FileArgument:
return build.FileArgument(*args)

@noPosargs
@typed_kwargs(
'declare_dependency',
Expand Down Expand Up @@ -3224,7 +3232,7 @@ def build_library(self, node, args, kwargs):
else:
raise InterpreterException(f'Unknown default_library value: {default_library}.')

def __convert_file_args(self, raw: T.List[mesonlib.FileOrString]) -> T.Tuple[T.List[mesonlib.File], T.List[str]]:
def __convert_file_args(self, raw: T.List[T.Union[str, mesonlib.File, build.FileArgument]]) -> T.Tuple[T.List[mesonlib.File], T.List[str]]:
"""Convert raw target arguments from File | str to File.
This removes files from the command line and replaces them with string
Expand All @@ -3242,6 +3250,9 @@ def __convert_file_args(self, raw: T.List[mesonlib.FileOrString]) -> T.Tuple[T.L
if isinstance(a, mesonlib.File):
depend_files.append(a)
args.append(a.rel_to_builddir(build_to_source))
elif isinstance(a, build.FileArgument):
depend_files.append(a.file)
args.append(f'{a.arg}{a.file.rel_to_builddir(build_to_source)}')
else:
args.append(a)

Expand Down Expand Up @@ -3307,6 +3318,17 @@ def build_target_decorator_caller(self, node, args, kwargs):
raise RuntimeError('Unreachable code')
self.kwarg_strings_to_includedirs(kwargs)
self.__process_language_args(kwargs)
if 'link_args' in kwargs:
new_link_args: T.List[str] = []
ld: T.List[mesonlib.File] = kwargs.setdefault('link_depends', [])
build_to_source = mesonlib.relpath(self.environment.source_dir, self.environment.build_dir)
for l in kwargs['link_args']:
if isinstance(l, build.FileArgument):
ld.append(l.file)
new_link_args.append(f'{l.arg}{l.file.rel_to_builddir(build_to_source)}')
else:
new_link_args.append(l)
kwargs['link_args'] = new_link_args

# Filter out kwargs from other target types. For example 'soversion'
# passed to library() when default_library == 'static'.
Expand Down
3 changes: 3 additions & 0 deletions mesonbuild/interpreter/interpreterobjects.py
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,9 @@ class IncludeDirsHolder(ObjectHolder[build.IncludeDirs]):
class FileHolder(ObjectHolder[mesonlib.File]):
pass

class FileArgumentHolder(ObjectHolder[build.FileArgument]):
pass

class HeadersHolder(ObjectHolder[build.Headers]):
pass

Expand Down
30 changes: 15 additions & 15 deletions mesonbuild/interpreter/kwargs.py
Original file line number Diff line number Diff line change
Expand Up @@ -331,21 +331,21 @@ class _BuildTarget(_BaseBuildTarget):

"""Arguments shared by non-JAR functions"""

c_args: T.List[str]
cpp_args: T.List[str]
cuda_args: T.List[str]
fortran_args: T.List[str]
d_args: T.List[str]
objc_args: T.List[str]
objcpp_args: T.List[str]
rust_args: T.List[str]
vala_args: T.List[T.Union[str, File]] # Yes, Vala is really special
cs_args: T.List[str]
swift_args: T.List[str]
cython_args: T.List[str]
nasm_args: T.List[str]
masm_args: T.List[str]
link_args: T.List[str]
c_args: T.List[T.Union[str, build.FileArgument]]
cpp_args: T.List[T.Union[str, build.FileArgument]]
cuda_args: T.List[T.Union[str, build.FileArgument]]
fortran_args: T.List[T.Union[str, build.FileArgument]]
d_args: T.List[T.Union[str, build.FileArgument]]
objc_args: T.List[T.Union[str, build.FileArgument]]
objcpp_args: T.List[T.Union[str, build.FileArgument]]
rust_args: T.List[T.Union[str, build.FileArgument]]
vala_args: T.List[T.Union[str, build.FileArgument, File]] # Yes, Vala is really special
cs_args: T.List[T.Union[str, build.FileArgument]]
swift_args: T.List[T.Union[str, build.FileArgument]]
cython_args: T.List[T.Union[str, build.FileArgument]]
nasm_args: T.List[T.Union[str, build.FileArgument]]
masm_args: T.List[T.Union[str, build.FileArgument]]
link_args: T.List[T.Union[str, build.FileArgument]]


class Executable(_BuildTarget):
Expand Down
9 changes: 5 additions & 4 deletions mesonbuild/interpreter/type_checking.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import typing as T

from .. import compilers
from ..build import (CustomTarget, BuildTarget,
from ..build import (CustomTarget, BuildTarget, FileArgument,
CustomTargetIndex, ExtractedObjects, GeneratedList, IncludeDirs,
BothLibraries, SharedLibrary, StaticLibrary, Jar, Executable, StructuredSources)
from ..coredata import UserFeatureOption
Expand Down Expand Up @@ -513,19 +513,20 @@ def link_whole_validator(values: T.List[T.Union[StaticLibrary, CustomTarget, Cus
since='1.3.0',
validator=in_set_validator({'rust', 'c'}))

_BASE_LANG_KW: KwargInfo[T.List[str]] = KwargInfo(
_BASE_LANG_KW: KwargInfo[T.List[T.Union[str, FileArgument]]] = KwargInfo(
'UNKNOWN',
ContainerTypeInfo(list, (str)),
ContainerTypeInfo(list, (str, FileArgument)),
listify=True,
default=[],
since_values={FileArgument: '1.3.0'},
)

_LANGUAGE_KWS: T.List[KwargInfo[T.List[str]]] = [
_BASE_LANG_KW.evolve(name=f'{lang}_args')
for lang in compilers.all_languages - {'rust', 'vala', 'java'}
]
_LANGUAGE_KWS.append(KwargInfo( # cannot use _BASE_LANG_KW because type is not evolveable
'vala_args', ContainerTypeInfo(list, (str, File)), listify=True, default=[], since_values={FileArgument: '1.3.0'}))
'vala_args', ContainerTypeInfo(list, (str, File, FileArgument)), listify=True, default=[], since_values={FileArgument: '1.3.0'}))
_LANGUAGE_KWS.append(_BASE_LANG_KW.evolve(name='rust_args', since='0.41.0'))

# We need this deprecated values more than the non-deprecated values. So we'll evolve them out elsewhere.
Expand Down
6 changes: 5 additions & 1 deletion test cases/linuxlike/3 linker script/meson.build
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
project('linker script', 'c')
project('linker script', 'c', meson_version : '>= 1.3.0')

# Solaris 11.4 ld supports --version-script only when you also specify
# -z gnu-version-script-compat
Expand All @@ -14,6 +14,10 @@ l = shared_library('bob', 'bob.c', link_args : vflag, link_depends : mapfile)
e = executable('prog', 'prog.c', link_with : l)
test('core', e)

l2 = shared_library('bob-file-argument', 'bob.c', link_args : file_argument('-Wl,--version-script,', files('bob.map')))
e2 = executable('prog2', 'prog.c', link_with : l2)
test('using file_argument', e2)

# configure_file
conf = configuration_data()
conf.set('in', 'bobMcBob')
Expand Down

0 comments on commit e5b931c

Please sign in to comment.