Skip to content

Commit

Permalink
build: Use typed_kwargs for language args
Browse files Browse the repository at this point in the history
This also moves the repacking into the interpreter, making the build
implementation simpler and removing a layering violation. This also
makes use a defaultdict to remove the need to call `.get()`
  • Loading branch information
dcbaker committed Jul 24, 2023
1 parent 3a4b874 commit b693d01
Show file tree
Hide file tree
Showing 6 changed files with 43 additions and 28 deletions.
2 changes: 1 addition & 1 deletion docs/yaml/functions/_build_target_base.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ kwargs:
description: precompiled header file to use for the given language

<lang>_args:
type: list[str]
type: list[str | file]
description: |
compiler flags to use for the given language;
eg: `cpp_args` for C++
Expand Down
6 changes: 3 additions & 3 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 @@ -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 @@ -1718,7 +1718,7 @@ def generate_vala_compile(self, target: build.BuildTarget) -> \
args += ['--gresources=' + gres_xml]
extra_args = []

for a in target.extra_args.get('vala', []):
for a in target.extra_args['vala']:
if isinstance(a, File):
relname = a.rel_to_builddir(self.build_to_src)
extra_dep_files.append(relname)
Expand Down
27 changes: 8 additions & 19 deletions mesonbuild/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -720,7 +720,7 @@ def __init__(
objects: T.List[ObjectTypes],
environment: environment.Environment,
compilers: T.Dict[str, 'Compiler'],
kwargs):
kwargs: T.Dict[str, T.Any]):
super().__init__(name, subdir, subproject, True, for_machine, environment, install=kwargs.get('install', False))
self.all_compilers = compilers
self.compilers = OrderedDict() # type: OrderedDict[str, Compiler]
Expand All @@ -740,7 +740,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[FileOrString]] = 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 @@ -818,6 +818,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 @@ -1091,10 +1093,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 @@ -1273,8 +1271,8 @@ def get_filename(self) -> 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[FileOrString]:
return self.extra_args[language]

@lru_cache(maxsize=None)
def get_dependencies(self) -> OrderedSet[Target]:
Expand Down Expand Up @@ -1529,16 +1527,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 @@ -2788,6 +2776,7 @@ def __init__(self,
compilers = {compiler.get_language(): compiler}
kwargs = {
'build_by_default': False,

f'{compiler.language}_args': compile_args,
'include_directories': include_directories,
'dependencies': dependencies,
Expand Down Expand Up @@ -2905,7 +2894,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
21 changes: 19 additions & 2 deletions mesonbuild/interpreter/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -3204,10 +3204,26 @@ def build_library(self, node, args, kwargs):
else:
raise InterpreterException(f'Unknown default_library value: {default_library}.')

@staticmethod
def __extract_language_args(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.
"""
args: T.DefaultDict[str, T.List[mesonlib.FileOrString]] = collections.defaultdict(list)
for l in compilers.all_languages:
# We need to a copy here because the receiving code assumes that it
# owns the language_args, and without the copy in `both_library` we can
# end up with two classes that think they own these
args[l] = kwargs[f'{l}_args'].copy()
kwargs['language_args'] = args

def build_target(self, node: mparser.BaseNode, args, kwargs, targetclass):
@FeatureNewKwargs('build target', '1.2.0', ['rust_dependency_map'])
@FeatureNewKwargs('build target', '0.42.0', ['rust_crate_type', '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 @@ -3243,10 +3259,11 @@ 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.__extract_language_args(kwargs)

# 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
8 changes: 8 additions & 0 deletions mesonbuild/interpreter/type_checking.py
Original file line number Diff line number Diff line change
Expand Up @@ -480,8 +480,16 @@ def link_whole_validator(values: T.List[T.Union[StaticLibrary, CustomTarget, Cus
KwargInfo('verbose', bool, default=False, since='0.62.0'),
]

_LANGUAGE_KWS: T.List[KwargInfo[T.List[str]]] = [
KwargInfo(f'{lang}_args', ContainerTypeInfo(list, (str, File)), listify=True, default=[])
for lang in compilers.all_languages - {'rust'}
]
_LANGUAGE_KWS.append(KwargInfo(
'rust_args', ContainerTypeInfo(list, str), listify=True, default=[], since='0.41.0'))

# Applies to all build_target like classes
_ALL_TARGET_KWS: T.List[KwargInfo] = [
*_LANGUAGE_KWS,
OVERRIDE_OPTIONS_KW,
]

Expand Down
7 changes: 4 additions & 3 deletions mesonbuild/modules/rust.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import annotations

import os
import typing as T

Expand Down Expand Up @@ -156,12 +155,14 @@ def test(self, state: ModuleState, args: T.Tuple[str, BuildTarget], kwargs: Func
new_target_kwargs = base_target.original_kwargs.copy()
# Don't mutate the shallow copied list, instead replace it with a new
# one
new_target_kwargs['rust_args'] = \
new_target_kwargs.get('rust_args', []) + kwargs['rust_args'] + ['--test']
new_target_kwargs['install'] = False
new_target_kwargs['dependencies'] = new_target_kwargs.get('dependencies', []) + kwargs['dependencies']
new_target_kwargs['link_with'] = new_target_kwargs.get('link_with', []) + kwargs['link_with']

lang_args = base_target.extra_args.copy()
lang_args['rust'] = base_target.extra_args['rust'] + kwargs['rust_args'] + ['--test']
new_target_kwargs['language_args'] = lang_args

sources = T.cast('T.List[SourceOutputs]', base_target.sources.copy())
sources.extend(base_target.generated)

Expand Down

0 comments on commit b693d01

Please sign in to comment.