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 a5a96a0 commit 23e83e2
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 11 deletions.
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 a [[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`
pos_args:
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]]
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
1 change: 1 addition & 0 deletions mesonbuild/interpreter/kwargs.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ class _BaseBuildTarget(TypedDict):
"""

override_options: T.Dict[OptionKey, T.Union[str, int, bool, T.List[str]]]
link_args: T.List[T.Union[str, build.FileArgument]]


class _BuildTarget(_BaseBuildTarget):
Expand Down
28 changes: 19 additions & 9 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,28 @@ 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[T.Union[str, FileArgument]]] = KwargInfo(
'UNKNOWN',
ContainerTypeInfo(list, (str, File, FileArgument)),
listify=True,
default=[],
since_values={FileArgument: '1.3.0'},
)

_LANGUAGE_KWS: T.List[KwargInfo[T.List[str]]] = [
KwargInfo(f'{lang}_args', ContainerTypeInfo(list, (str, File)), listify=True, default=[])
_BASE_LANG_KW.evolve(name=f'{lang}_args')
for lang in compilers.all_languages - {'rust', 'vala', 'java'}
]
_LANGUAGE_KWS.append(KwargInfo(
'vala_args', ContainerTypeInfo(list, (str, File)), listify=True, default=[]))
_LANGUAGE_KWS.append(KwargInfo(
'rust_args', ContainerTypeInfo(list, str), listify=True, default=[], since='0.41.0'))
_LANGUAGE_KWS.append(KwargInfo( # cannot use _BASE_LANG_KW because type is not evolveable
'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.
_JAVA_LANG_KW: KwargInfo[T.List[str]] = KwargInfo(
'java_args', ContainerTypeInfo(list, str), listify=True, default=[],
deprecated='1.3.0', deprecated_message='This does not, and never has, done anything. It should be removed')
_JAVA_LANG_KW: KwargInfo[T.List[str]] = _BASE_LANG_KW.evolve(
name='java_args',
deprecated='1.3.0',
deprecated_message='This does not, and never has, done anything. It should be removed'
)

# Applies to all build_target like classes
_ALL_TARGET_KWS: T.List[KwargInfo] = [
Expand All @@ -536,6 +545,7 @@ def link_whole_validator(values: T.List[T.Union[StaticLibrary, CustomTarget, Cus
_BUILD_TARGET_KWS: T.List[KwargInfo] = [
*_ALL_TARGET_KWS,
*_LANGUAGE_KWS,
_BASE_LANG_KW.evolve(name='link_args'),
RUST_CRATE_TYPE_KW,
]

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 23e83e2

Please sign in to comment.