Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add <lang>_(static|shared)_args #11981

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions docs/markdown/snippets/shared_static_only_args.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
## `<lang>_(shared|static)_args` for both_library, library, and build_target

We now allow passing arguments like `c_static_args` and `c_shared_args`. This
allows a [[both_libraries]] to have arguments specific to either the shared or
static library, as well as common arguments to both.
xclaesse marked this conversation as resolved.
Show resolved Hide resolved

There is a drawback to this, since Meson now cannot re-use object files between
the static and shared targets. This could lead to much higher compilation time
when using a [[both_libraries]] if there are many sources.
5 changes: 5 additions & 0 deletions docs/yaml/functions/_build_target_base.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ kwargs:
compiler flags to use for the given language;
eg: `cpp_args` for C++

vala_args:
type: list[str | file]
description: |
Compiler flags for Vala. Unlike other languages this may contain Files

sources:
type: str | file | custom_tgt | custom_idx | generated_list | structured_src
description: Additional source files. Same as the source varargs.
Expand Down
34 changes: 34 additions & 0 deletions docs/yaml/functions/library.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ description: |
The keyword arguments for this are the same as for
[[build_target]]
warnings:
- using <lang>_shared_args and/or <lang>_static_args may lead to much higher
compilation times with both_library, as object files cannot be shared between
the static and shared targets. It is guaranteed to not duplicate the build if
these arguments are empty arrays

posargs_inherit: _build_target_base
varargs_inherit: _build_target_base
kwargs_inherit:
Expand All @@ -32,3 +38,31 @@ kwargs:
type being build.
- 'c': Create a "cdylib" or "staticlib" crate depending on the library
type being build.
<lang>_static_args:
type: list[str]
since: 1.3.0
description:
Arguments that are only passed to a static library

vala_static_args:
type: list[str | file]
since: 1.3.0
description:
Arguments that are only passed to a static library

Like `vala_args`, [[files]] is allowed in addition to string

<lang>_shared_args:
type: list[str]
since: 1.3.0
description:
Arguments that are only passed to a shared library

vala_shared_args:
type: list[str | file]
since: 1.3.0
description:
Arguments that are only passed to a shared library

Like `vala_args`, [[files]] is allowed in addition to string
2 changes: 1 addition & 1 deletion mesonbuild/backend/backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -1475,7 +1475,7 @@ def get_custom_target_sources(self, target: build.CustomTarget) -> T.List[str]:
srcs += fname
return srcs

def get_custom_target_depend_files(self, target: build.CustomTarget, absolute_paths: bool = False) -> T.List[str]:
def get_target_depend_files(self, target: T.Union[build.CustomTarget, build.BuildTarget], absolute_paths: bool = False) -> T.List[str]:
deps: T.List[str] = []
for i in target.depend_files:
if isinstance(i, mesonlib.File):
Expand Down
22 changes: 7 additions & 15 deletions mesonbuild/backend/ninjabackend.py
Original file line number Diff line number Diff line change
Expand Up @@ -1068,7 +1068,7 @@ def should_use_dyndeps_for_target(self, target: 'build.BuildTarget') -> bool:
return True
if 'cpp' not in target.compilers:
return False
if '-fmodules-ts' in target.extra_args.get('cpp', []):
if '-fmodules-ts' in target.extra_args['cpp']:
return True
# Currently only the preview version of Visual Studio is supported.
cpp = target.compilers['cpp']
Expand Down Expand Up @@ -1151,7 +1151,7 @@ def generate_custom_target(self, target: build.CustomTarget):
self.custom_target_generator_inputs(target)
(srcs, ofilenames, cmd) = self.eval_custom_target_command(target)
deps = self.unwrap_dep_list(target)
deps += self.get_custom_target_depend_files(target)
deps += self.get_target_depend_files(target)
if target.build_always_stale:
deps.append('PHONY')
if target.depfile is None:
Expand Down Expand Up @@ -1214,7 +1214,7 @@ def generate_run_target(self, target: build.RunTarget):
elem.add_item('description', f'Running external command {target.name}{cmd_type}')
elem.add_item('pool', 'console')
deps = self.unwrap_dep_list(target)
deps += self.get_custom_target_depend_files(target)
deps += self.get_target_depend_files(target)
elem.add_dep(deps)
self.add_build(elem)
self.processed_targets.add(target.get_id())
Expand Down Expand Up @@ -1462,7 +1462,7 @@ def generate_cs_target(self, target: build.BuildTarget):
compiler = target.compilers['cs']
rel_srcs = [os.path.normpath(s.rel_to_builddir(self.build_to_src)) for s in src_list]
deps = []
commands = compiler.compiler_args(target.extra_args.get('cs', []))
commands = compiler.compiler_args(target.extra_args['cs'])
commands += compiler.get_buildtype_args(buildtype)
commands += compiler.get_optimization_args(target.get_option(OptionKey('optimization')))
commands += compiler.get_debug_args(target.get_option(OptionKey('debug')))
Expand Down Expand Up @@ -1712,18 +1712,10 @@ def generate_vala_compile(self, target: build.BuildTarget) -> \
if isinstance(gensrc, modules.GResourceTarget):
gres_xml, = self.get_custom_target_sources(gensrc)
args += ['--gresources=' + gres_xml]
extra_args = []

for a in target.extra_args.get('vala', []):
if isinstance(a, File):
relname = a.rel_to_builddir(self.build_to_src)
extra_dep_files.append(relname)
extra_args.append(relname)
else:
extra_args.append(a)
dependency_vapis = self.determine_dep_vapis(target)
extra_dep_files += dependency_vapis
args += extra_args
extra_dep_files.extend(self.get_target_depend_files(target))
args += target.get_extra_args('vala')
element = NinjaBuildElement(self.all_outputs, valac_outputs,
self.compiler_to_rule_name(valac),
all_files + dependency_vapis)
Expand Down Expand Up @@ -2622,7 +2614,7 @@ def generate_genlist_for_target(self, genlist: build.GeneratedList, target: buil
exe = generator.get_exe()
infilelist = genlist.get_inputs()
outfilelist = genlist.get_outputs()
extra_dependencies = self.get_custom_target_depend_files(genlist)
extra_dependencies = self.get_target_depend_files(genlist)
for i, curfile in enumerate(infilelist):
if len(generator.outputs) == 1:
sole_output = os.path.join(self.get_target_private_dir(target), outfilelist[i])
Expand Down
6 changes: 3 additions & 3 deletions mesonbuild/backend/vs2010backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ def generate_genlist_for_target(self, genlist: T.Union[build.GeneratedList, buil
else:
sole_output = ''
infilename = os.path.join(down, curfile.rel_to_builddir(self.build_to_src, target_private_dir))
deps = self.get_custom_target_depend_files(genlist, True)
deps = self.get_target_depend_files(genlist, True)
base_args = generator.get_arglist(infilename)
outfiles_rel = genlist.get_outputs_for(curfile)
outfiles = [os.path.join(target_private_dir, of) for of in outfiles_rel]
Expand Down Expand Up @@ -699,7 +699,7 @@ def gen_run_target_vcxproj(self, target: build.RunTarget, ofname: str, guid: str
(root, type_config) = self.create_basic_project(target.name,
temp_dir=target.get_id(),
guid=guid)
depend_files = self.get_custom_target_depend_files(target)
depend_files = self.get_target_depend_files(target)

if not target.command:
# This is an alias target and thus doesn't run any command. It's
Expand Down Expand Up @@ -738,7 +738,7 @@ def gen_custom_target_vcxproj(self, target: build.CustomTarget, ofname: str, gui
# from the target dir, not the build root.
target.absolute_paths = True
(srcs, ofilenames, cmd) = self.eval_custom_target_command(target, True)
depend_files = self.get_custom_target_depend_files(target, True)
depend_files = self.get_target_depend_files(target, True)
# Always use a wrapper because MSBuild eats random characters when
# there are many arguments.
tdir_abs = os.path.join(self.environment.get_build_dir(), self.get_target_dir(target))
Expand Down
27 changes: 8 additions & 19 deletions mesonbuild/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -724,7 +724,7 @@ def __init__(
objects: T.List[ObjectTypes],
environment: environment.Environment,
compilers: T.Dict[str, 'Compiler'],
kwargs):
kwargs: T.Dict[str, T.Any]):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't we use TYPE_kwargs here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that's accurate though, since TYPE_kwargs includes several Interpreter types in its values

Copy link
Member

@eli-schwartz eli-schwartz Oct 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, it is narrower than Any which explicitly allows anything. However that may not be too big of a deal... I guess it is easier to grep later on than incorrect use of TYPE_kwargs... and it allows checking the keys of course.

super().__init__(name, subdir, subproject, True, for_machine, environment, install=kwargs.get('install', False))
self.all_compilers = compilers
self.compilers: OrderedDict[str, Compiler] = OrderedDict()
Expand All @@ -735,6 +735,7 @@ def __init__(
self.link_language = kwargs.get('link_language')
self.link_targets: T.List[LibTypes] = []
self.link_whole_targets: T.List[T.Union[StaticLibrary, CustomTarget, CustomTargetIndex]] = []
self.depend_files: T.List[File] = []
self.link_depends = []
self.added_deps = set()
self.name_prefix_set = False
Expand All @@ -746,7 +747,7 @@ def __init__(
# as Vala which generates .vapi and .h besides the compiled output.
self.outputs = [self.filename]
self.pch: T.Dict[str, T.List[str]] = {}
self.extra_args: T.Dict[str, T.List['FileOrString']] = {}
self.extra_args: T.DefaultDict[str, T.List[str]] = kwargs.get('language_args', defaultdict(list))
self.sources: T.List[File] = []
self.generated: T.List['GeneratedTypes'] = []
self.extra_files: T.List[File] = []
Expand Down Expand Up @@ -830,6 +831,8 @@ def check_unknown_kwargs(self, kwargs):
def check_unknown_kwargs_int(self, kwargs, known_kwargs):
unknowns = []
for k in kwargs:
if k == 'language_args':
continue
if k not in known_kwargs:
unknowns.append(k)
if len(unknowns) > 0:
Expand Down Expand Up @@ -1103,10 +1106,6 @@ def process_kwargs(self, kwargs):
self.process_kwargs_base(kwargs)
self.original_kwargs = kwargs

for lang in all_languages:
lang_args = extract_as_list(kwargs, f'{lang}_args')
self.add_compiler_args(lang, lang_args)

self.add_pch('c', extract_as_list(kwargs, 'c_pch'))
self.add_pch('cpp', extract_as_list(kwargs, 'cpp_pch'))

Expand Down Expand Up @@ -1278,8 +1277,8 @@ def get_debug_filename(self) -> T.Optional[str]:
def get_outputs(self) -> T.List[str]:
return self.outputs

def get_extra_args(self, language):
return self.extra_args.get(language, [])
def get_extra_args(self, language: str) -> T.List[str]:
return self.extra_args[language]

@lru_cache(maxsize=None)
def get_dependencies(self) -> OrderedSet[Target]:
Expand Down Expand Up @@ -1538,16 +1537,6 @@ def add_include_dirs(self, args: T.Sequence['IncludeDirs'], set_is_system: T.Opt
ids = [IncludeDirs(x.get_curdir(), x.get_incdirs(), is_system, x.get_extra_build_dirs()) for x in ids]
self.include_dirs += ids

def add_compiler_args(self, language: str, args: T.List['FileOrString']) -> None:
args = listify(args)
for a in args:
if not isinstance(a, (str, File)):
raise InvalidArguments('A non-string passed to compiler args.')
if language in self.extra_args:
self.extra_args[language] += args
else:
self.extra_args[language] = args

def get_aliases(self) -> T.List[T.Tuple[str, str, str]]:
return []

Expand Down Expand Up @@ -2875,7 +2864,7 @@ def __init__(self, name: str, subdir: str, subproject: str, for_machine: Machine
raise InvalidArguments('structured sources are not supported in Java targets.')
self.filename = self.name + '.jar'
self.outputs = [self.filename]
self.java_args = kwargs.get('java_args', [])
self.java_args = self.extra_args['java']
self.main_class = kwargs.get('main_class', '')
self.java_resources: T.Optional[StructuredSources] = kwargs.get('java_resources', None)

Expand Down
70 changes: 63 additions & 7 deletions mesonbuild/interpreter/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,9 @@ def dump_value(self, arr, list_sep, indent):

known_library_kwargs = (
build.known_shlib_kwargs |
build.known_stlib_kwargs
build.known_stlib_kwargs |
{f'{l}_shared_args' for l in compilers.all_languages - {'java'}} |
{f'{l}_static_args' for l in compilers.all_languages - {'java'}}
)

known_build_target_kwargs = (
Expand Down Expand Up @@ -3186,7 +3188,7 @@ def add_target(self, name: str, tobj: build.Target) -> None:
self.coredata.target_guids[idname] = str(uuid.uuid4()).upper()

@FeatureNew('both_libraries', '0.46.0')
def build_both_libraries(self, node, args, kwargs):
def build_both_libraries(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargsType], kwargs: kwtypes.Library) -> build.BothLibraries:
shared_lib = self.build_target(node, args, kwargs, build.SharedLibrary)
static_lib = self.build_target(node, args, kwargs, build.StaticLibrary)

Expand All @@ -3202,6 +3204,9 @@ def build_both_libraries(self, node, args, kwargs):
# FIXME: rustc supports generating both libraries in a single invocation,
# but for now compile twice.
reuse_object_files = False
elif any(k.endswith(('static_args', 'shared_args')) and v for k, v in kwargs.items()):
# Ensure not just the keyword arguments exist, but that they are non-empty.
reuse_object_files = False
else:
reuse_object_files = static_lib.pic

Expand All @@ -3219,17 +3224,58 @@ def build_both_libraries(self, node, args, kwargs):

return build.BothLibraries(shared_lib, static_lib)

def build_library(self, node, args, kwargs):
def build_library(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargsType], kwargs: kwtypes.Library):
default_library = self.coredata.get_option(OptionKey('default_library', subproject=self.subproject))
assert isinstance(default_library, str), 'for mypy'
if default_library == 'shared':
return self.build_target(node, args, kwargs, build.SharedLibrary)
return self.build_target(node, args, T.cast('kwtypes.StaticLibrary', kwargs), build.SharedLibrary)
elif default_library == 'static':
return self.build_target(node, args, kwargs, build.StaticLibrary)
return self.build_target(node, args, T.cast('kwtypes.SharedLibrary', kwargs), build.StaticLibrary)
elif default_library == 'both':
return self.build_both_libraries(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]]:
"""Convert raw target arguments from File | str to File.

This removes files from the command line and replaces them with string
values, but adds the files to depends list

:param raw: the raw arguments
:return: A tuple of file dependencies and raw arguments
"""
depend_files: T.List[mesonlib.File] = []
args: T.List[str] = []
build_to_source = mesonlib.relpath(self.environment.get_source_dir(),
self.environment.get_build_dir())

for a in raw:
if isinstance(a, mesonlib.File):
depend_files.append(a)
args.append(a.rel_to_builddir(build_to_source))
else:
args.append(a)

return depend_files, args

def __process_language_args(self, kwargs: T.Dict[str, T.List[mesonlib.FileOrString]]) -> None:
"""Convert split language args into a combined dictionary.

The Meson DSL takes arguments in the form `<lang>_args : args`, but in the
build layer we store these in a single dictionary as `{<lang>: args}`.
This function extracts the arguments from the DSL format and prepares
them for the IR.
"""
d = kwargs.setdefault('depend_files', [])
new_args: T.DefaultDict[str, T.List[str]] = collections.defaultdict(list)

for l in compilers.all_languages:
deps, args = self.__convert_file_args(kwargs[f'{l}_args'])
new_args[l] = args
d.extend(deps)
kwargs['language_args'] = new_args

@T.overload
def build_target(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargsType],
kwargs: kwtypes.Executable, targetclass: T.Type[build.Executable]) -> build.Executable: ...
Expand All @@ -3255,7 +3301,6 @@ def build_target(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargs
targetclass: T.Type[T.Union[build.Executable, build.StaticLibrary, build.SharedModule, build.SharedLibrary, build.Jar]]
) -> T.Union[build.Executable, build.StaticLibrary, build.SharedModule, build.SharedLibrary, build.Jar]:
@FeatureNewKwargs('build target', '0.42.0', ['build_rpath', 'implicit_include_directories'])
@FeatureNewKwargs('build target', '0.41.0', ['rust_args'])
@FeatureNewKwargs('build target', '0.38.0', ['build_by_default'])
@FeatureNewKwargs('build target', '0.48.0', ['gnu_symbol_visibility'])
def build_target_decorator_caller(self, node, args, kwargs):
Expand Down Expand Up @@ -3295,10 +3340,21 @@ def build_target_decorator_caller(self, node, args, kwargs):
mlog.debug('Unknown target type:', str(targetclass))
raise RuntimeError('Unreachable code')
self.kwarg_strings_to_includedirs(kwargs)
self.__process_language_args(kwargs)
if targetclass is build.StaticLibrary:
for lang in compilers.all_languages - {'java'}:
deps, args = self.__convert_file_args(kwargs.get(f'{lang}_static_args', []))
kwargs['language_args'][lang].extend(args)
kwargs['depend_files'].extend(deps)
elif targetclass is build.SharedLibrary:
for lang in compilers.all_languages - {'java'}:
deps, args = self.__convert_file_args(kwargs.get(f'{lang}_shared_args', []))
kwargs['language_args'][lang].extend(args)
kwargs['depend_files'].extend(deps)

# Filter out kwargs from other target types. For example 'soversion'
# passed to library() when default_library == 'static'.
kwargs = {k: v for k, v in kwargs.items() if k in targetclass.known_kwargs}
kwargs = {k: v for k, v in kwargs.items() if k in targetclass.known_kwargs | {'language_args'}}

srcs: T.List['SourceInputs'] = []
struct: T.Optional[build.StructuredSources] = build.StructuredSources()
Expand Down
Loading
Loading