From b9e00285a5eb61ada34d02b3e9772d1b0ad295eb Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 28 Jan 2026 11:06:39 -0800 Subject: [PATCH 01/16] interpreter: remove useless call to `extract_as_list` `typed_kwargs` has already taken care of that for us. --- mesonbuild/interpreter/interpreter.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index d18d7f5f280b..9d81b175ac1a 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -19,7 +19,7 @@ from .. import mesonlib from ..mesonlib import (EnvironmentVariables, ExecutableSerialisation, MesonBugException, MesonException, HoldableObject, FileMode, MachineChoice, is_parent_path, listify, - extract_as_list, has_path_sep, path_has_root, path_is_in_root, PerMachine) + has_path_sep, path_has_root, path_is_in_root, PerMachine) from ..options import OptionKey from ..programs import ExternalProgram, NonExistingExternalProgram, Program from ..dependencies import Dependency @@ -3476,7 +3476,6 @@ def build_target(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargs if not isinstance(s, (build.BuildTarget, build.ExtractedObjects))] sources = self.source_strings_to_files(sources) objs = kwargs['objects'] - kwargs['dependencies'] = extract_as_list(kwargs, 'dependencies') # TODO: When we can do strings -> Files in the typed_kwargs validator, do this there too kwargs['extra_files'] = mesonlib.unique_list(self.source_strings_to_files(kwargs['extra_files'])) self.check_sources_exist(os.path.join(self.source_root, self.subdir), sources) From fbbed9056478cd1910cba161df57fd7d1d9b874b Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 28 Jan 2026 11:14:27 -0800 Subject: [PATCH 02/16] interpreter: fix sources typing in `build_target` Also, move `check_sources_exist` before string -> File conversion, since otherwise it does nothing. --- mesonbuild/interpreter/interpreter.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 9d81b175ac1a..40709bece8af 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -3192,6 +3192,10 @@ def source_strings_to_files(self, sources: T.List['mesonlib.FileOrString'], stri @T.overload def source_strings_to_files(self, sources: T.List[T.Union[mesonlib.FileOrString, build.GeneratedTypes]]) -> T.List[T.Union[mesonlib.File, build.GeneratedTypes]]: ... # noqa: F811 + @T.overload + def source_strings_to_files(self, sources: T.List[T.Union[mesonlib.FileOrString, build.GeneratedTypes, build.StructuredSources]] + ) -> T.List[T.Union[mesonlib.File, build.GeneratedTypes, build.StructuredSources]]: ... # noqa: F811 + @T.overload def source_strings_to_files(self, sources: T.List['SourceInputs'], strict: bool = True) -> T.List['SourceOutputs']: ... # noqa: F811 @@ -3454,31 +3458,30 @@ def build_target(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargs # Because who owns this isn't clear kwargs = kwargs.copy() - name, sources = args + name, raw_sources = args for_machine = kwargs['native'] if kwargs.get('rust_crate_type') == 'proc-macro': # Silently force to native because that's the only sensible value # and rust_crate_type is deprecated any way. for_machine = MachineChoice.BUILD # Avoid mutating, since there could be other references to sources - sources = sources + kwargs['sources'] - if any(isinstance(s, build.BuildTarget) for s in sources): + raw_sources = raw_sources + T.cast('SourcesVarargsType', kwargs['sources']) + if any(isinstance(s, build.BuildTarget) for s in raw_sources): FeatureBroken.single_use('passing references to built targets as a source file', '1.1.0', self.subproject, 'Consider using `link_with` or `link_whole` if you meant to link, or dropping them as otherwise they are ignored.', node) - if any(isinstance(s, build.ExtractedObjects) for s in sources): + if any(isinstance(s, build.ExtractedObjects) for s in raw_sources): FeatureBroken.single_use('passing object files as sources', '1.1.0', self.subproject, 'Pass these to the `objects` keyword instead, they are ignored when passed as sources.', node) # Go ahead and drop these here, since they're only allowed through for # backwards compatibility anyway - sources = [s for s in sources - if not isinstance(s, (build.BuildTarget, build.ExtractedObjects))] - sources = self.source_strings_to_files(sources) + self.check_sources_exist(os.path.join(self.source_root, self.subdir), raw_sources) + sources = self.source_strings_to_files([ + s for s in raw_sources if not isinstance(s, (build.BuildTarget, build.ExtractedObjects))]) objs = kwargs['objects'] # TODO: When we can do strings -> Files in the typed_kwargs validator, do this there too kwargs['extra_files'] = mesonlib.unique_list(self.source_strings_to_files(kwargs['extra_files'])) - self.check_sources_exist(os.path.join(self.source_root, self.subdir), sources) self.__process_language_args(kwargs) if targetclass is build.StaticLibrary: kwargs = T.cast('kwtypes.StaticLibrary', kwargs) @@ -3595,7 +3598,7 @@ def add_stdlib_info(self, target): if dep: target.add_deps([dep]) - def check_sources_exist(self, subdir, sources): + def check_sources_exist(self, subdir: str, sources: SourcesVarargsType) -> None: for s in sources: if not isinstance(s, str): continue # This means a generated source and they always exist. From 9eadc3268f66b3ab6d9dd6bbf9ba7126b89d89c5 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 28 Jan 2026 11:25:53 -0800 Subject: [PATCH 03/16] interpreter/kwargs: fix type definitions of *_pch arguments --- mesonbuild/interpreter/kwargs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index 402f1afd8fa2..4819a43afe9c 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -378,8 +378,8 @@ class _BuildTarget(_BaseBuildTarget): swift_module_name: str sources: SourcesVarargsType link_args: T.List[str] - c_pch: T.List[str] - cpp_pch: T.List[str] + c_pch: T.Optional[T.Tuple[str, T.Optional[str]]] + cpp_pch: T.Optional[T.Tuple[str, T.Optional[str]]] c_args: T.List[str] cpp_args: T.List[str] cuda_args: T.List[str] From 267546395268bc2937ba4771731d9800fd12a8c2 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 28 Jan 2026 11:43:15 -0800 Subject: [PATCH 04/16] build: correct override_options annotations Which should be `dict[str, str]` --- mesonbuild/build.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 69797e04b49c..99adc9227858 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -47,6 +47,7 @@ from .linkers.linkers import StaticLinker from .mesonlib import ExecutableSerialisation, FileMode, FileOrString from .mparser import BaseNode + from .options import ElementaryOptionValues GeneratedTypes: TypeAlias = T.Union['CustomTarget', 'CustomTargetIndex', 'GeneratedList'] LibTypes: TypeAlias = T.Union['SharedLibrary', 'StaticLibrary', 'CustomTarget', 'CustomTargetIndex'] @@ -95,7 +96,7 @@ class BuildTargetKeywordArguments(TypedDict, total=False): name_prefix: T.Optional[str] name_suffix: T.Optional[str] native: MachineChoice - override_options: T.Dict[OptionKey, str] + override_options: T.Dict[str, ElementaryOptionValues] resources: T.List[str] swift_interoperability_mode: Literal['c', 'cpp'] swift_module_name: str @@ -788,7 +789,7 @@ def id(self) -> str: def get_id(self) -> str: return self.id - def get_override(self, name: str) -> T.Optional[str]: + def get_override(self, name: str) -> T.Optional[ElementaryOptionValues]: return self.raw_overrides.get(name, None) def is_linkable_target(self) -> bool: From ba1bf8d56d217c89aad88e3077e4322517407223 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 28 Jan 2026 11:46:55 -0800 Subject: [PATCH 05/16] build: win_subystem is only used in Executables --- mesonbuild/build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 99adc9227858..35ded9ac5f45 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -105,7 +105,6 @@ class BuildTargetKeywordArguments(TypedDict, total=False): vala_gir: T.Optional[str] vala_header: T.Optional[str] vala_vapi: T.Optional[str] - win_subsystem: str _allow_no_sources: bool @@ -116,6 +115,7 @@ class ExecutableKeywordArguments(BuildTargetKeywordArguments, total=False): export_dynamic: bool pie: bool vs_module_defs: T.Union[str, File, CustomTarget, CustomTargetIndex] + win_subsystem: str class SharedModuleKeywordArguments(BuildTargetKeywordArguments, total=False): From e488fb40c0855460155411be288aa9bb5aa483dc Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 28 Jan 2026 12:42:09 -0800 Subject: [PATCH 06/16] interpreter/kwargs: fix typing of include_directories It should be `list[str | IncludeDirs]`, not just `list[IncludeDirs]` --- mesonbuild/interpreter/kwargs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index 4819a43afe9c..bf0834dd223d 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -341,7 +341,7 @@ class _BaseBuildTarget(TypedDict): dependencies: T.List[Dependency] extra_files: T.List[FileOrString] gnu_symbol_visibility: str - include_directories: T.List[build.IncludeDirs] + include_directories: T.List[T.Union[str, build.IncludeDirs]] install: bool install_mode: FileMode install_tag: T.Optional[str] From 47fa1d0e9f12023a178137df36ed87259e77008c Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 28 Jan 2026 12:51:45 -0800 Subject: [PATCH 07/16] build|interpreter: fix typing mismatches between build and interpreter --- mesonbuild/build.py | 2 +- mesonbuild/interpreter/kwargs.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 35ded9ac5f45..2122d00559db 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -89,7 +89,7 @@ class BuildTargetKeywordArguments(TypedDict, total=False): install_tag: T.List[T.Optional[str]] language_args: T.DefaultDict[Language, T.List[str]] link_args: T.List[str] - link_depends: T.List[T.Union[str, File, CustomTarget, CustomTargetIndex]] + link_depends: T.List[T.Union[File, BuildTargetTypes]] link_language: Language link_whole: T.List[StaticTargetTypes] link_with: T.List[BuildTargetTypes] diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index bf0834dd223d..b5ca0ea6d394 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -347,7 +347,7 @@ class _BaseBuildTarget(TypedDict): install_tag: T.Optional[str] install_rpath: str implicit_include_directories: bool - link_depends: T.List[T.Union[str, File, build.GeneratedTypes]] + link_depends: T.List[T.Union[str, File, build.BuildTargetTypes]] link_language: T.Optional[Language] link_whole: T.List[build.StaticTargetTypes] link_with: T.List[build.BuildTargetTypes] From 0446735bee6011a9a09088cf43a24c97aae3f6b6 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 28 Jan 2026 12:55:46 -0800 Subject: [PATCH 08/16] build: Add depend_files to BuildTargetKeywordArguments It's used, but not listed --- mesonbuild/build.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 2122d00559db..6f07a7768f66 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -78,6 +78,7 @@ class BuildTargetKeywordArguments(TypedDict, total=False): d_module_versions: T.List[T.Union[str, int]] d_unittest: bool dependencies: T.List[dependencies.Dependency] + depend_files: T.List[File] extra_files: T.List[File] gnu_symbol_visibility: Literal['default', 'internal', 'hidden', 'protected', 'inlineshidden', ''] implicit_include_directories: bool From 684f55e3386696e91e2171f0814991b32efd05e6 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 28 Jan 2026 13:36:45 -0800 Subject: [PATCH 09/16] build: Add TypedDict for Jar kwargs --- mesonbuild/build.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 6f07a7768f66..f89c3342a8b8 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -134,6 +134,13 @@ class StaticLibraryKeywordArguments(BuildTargetKeywordArguments, total=False): pic: bool prelink: bool + class JarKeywordArguments(BuildTargetKeywordArguments, total=False): + + java_args: T.List[str] + java_resources: T.Optional[StructuredSources] + main_class: str + + _T = T.TypeVar('_T') DEFAULT_STATIC_LIBRARY_NAMES: T.Mapping[str, T.Tuple[str, str]] = { @@ -3189,7 +3196,7 @@ class Jar(BuildTarget): def __init__(self, name: str, subdir: str, subproject: str, for_machine: MachineChoice, sources: T.List[SourceOutputs], structured_sources: T.Optional['StructuredSources'], objects, environment: Environment, compilers: CompilerDict, - kwargs): + kwargs: JarKeywordArguments): super().__init__(name, subdir, subproject, for_machine, sources, structured_sources, objects, environment, compilers, kwargs) for s in self.sources: From d9aa033d755cf1c28d7b20e68332c7beca2ff332 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 28 Jan 2026 15:17:18 -0800 Subject: [PATCH 10/16] build: Actually set depend_files We set this in the interpreter for Vala related arguments, but never pass it into the build layer. --- mesonbuild/build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index f89c3342a8b8..5479d36c8a66 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -846,7 +846,7 @@ def __init__( self.link_language: T.Optional[Language] = kwargs.get('link_language') self.link_targets: T.List[BuildTargetTypes] = [] self.link_whole_targets: T.List[StaticTargetTypes] = [] - self.depend_files: T.List[File] = [] + self.depend_files = kwargs.get('depend_files', []) self.link_depends: T.List[T.Union[File, BuildTargetTypes]] = [] self.added_deps = set() self.name_prefix_set = False From aedea23531967fe327c1b20b8f10044df3901ddf Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 28 Jan 2026 15:20:34 -0800 Subject: [PATCH 11/16] backend/ninja: Add depend_files dependencies to BuildTargets Ironically, the VS backend already does this correctly. --- mesonbuild/backend/ninjabackend.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index c117d0149889..83da888c5158 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -3210,7 +3210,8 @@ def generate_single_compile(self, target: build.BuildTarget, src, compiler_name = self.compiler_to_rule_name(compiler) else: compiler_name = self.compiler_to_rule_name(compiler) - extra_deps = [] + extra_deps: T.List[str] = [] + extra_deps.extend(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 From 4c575e112910f45c01b1bbcaaabef4ccee39604d Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 28 Jan 2026 13:41:37 -0800 Subject: [PATCH 12/16] interpreter/kwargs: Add shortname to SharedLibrary --- mesonbuild/interpreter/kwargs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index b5ca0ea6d394..0c244b541f81 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -428,6 +428,7 @@ class _SharedLibMixin(TypedDict): soversion: T.Optional[str] version: T.Optional[str] vs_module_defs: T.Optional[T.Union[str, File, build.CustomTarget, build.CustomTargetIndex]] + shortname: str class SharedLibrary(_BuildTarget, _SharedLibMixin, _LibraryMixin): From cdc2993020811f53dcff95af8bcb0c3d155b8eb2 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 28 Jan 2026 15:59:03 -0800 Subject: [PATCH 13/16] interpreter/type_checking: Add `vs_module_defs` to build_target --- mesonbuild/interpreter/type_checking.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py index 04abe09506ab..a15b08bc2621 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -988,6 +988,7 @@ def _shortname_validator(shortname: T.Optional[str]) -> T.Optional[str]: *_EXCLUSIVE_STATIC_LIB_KWS, *EXCLUSIVE_EXECUTABLE_KWS, *_SHARED_STATIC_ARGS, + _VS_MODULE_DEFS_KW, RUST_ABI_KW.evolve(since='1.10.0'), *[a.evolve(deprecated='1.3.0', deprecated_message='The use of "jar" in "build_target()" is deprecated, and this argument is only used by jar()') for a in _EXCLUSIVE_JAR_KWS], From 41de864f13f282a2b040a72b269d647bd91fa26b Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 28 Jan 2026 16:02:38 -0800 Subject: [PATCH 14/16] build|interpreter: Add build_subdir to the TypedDicts for kwargs --- mesonbuild/build.py | 1 + mesonbuild/interpreter/kwargs.py | 1 + 2 files changed, 2 insertions(+) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 5479d36c8a66..f3b5e67467b4 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -71,6 +71,7 @@ class BuildTargetKeywordArguments(TypedDict, total=False): build_by_default: bool build_rpath: str + build_subdir: str c_pch: T.Optional[T.Tuple[str, T.Optional[str]]] cpp_pch: T.Optional[T.Tuple[str, T.Optional[str]]] d_debug: T.List[T.Union[str, int]] diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index 0c244b541f81..f92392054447 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -338,6 +338,7 @@ class _BaseBuildTarget(TypedDict): build_by_default: bool build_rpath: str + build_subdir: str dependencies: T.List[Dependency] extra_files: T.List[FileOrString] gnu_symbol_visibility: str From 299e5c94397fdf201e7e331eb6a4faf75687de8f Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 28 Jan 2026 15:14:50 -0800 Subject: [PATCH 15/16] build: Remove known kwargs checking We have the Interpreter layer which does DSL translation and the build layer has type annotations. --- mesonbuild/build.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index f3b5e67467b4..4969446499b6 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -906,7 +906,6 @@ def __init__( mlog.warning(f'Build target {name} has no sources. ' 'This was never supposed to be allowed but did because of a bug, ' 'support will be removed in a future release of Meson') - self.check_unknown_kwargs(kwargs) self.validate_install() self.check_module_linking() @@ -962,21 +961,6 @@ def validate_install(self): else: mlog.warning('Installing target build for the build machine. This will fail in a cross build.') - def check_unknown_kwargs(self, kwargs: BuildTargetKeywordArguments) -> None: - # Override this method in derived classes that have more - # keywords. - self.check_unknown_kwargs_int(kwargs, self.known_kwargs) - - def check_unknown_kwargs_int(self, kwargs: BuildTargetKeywordArguments, known_kwargs: T.Set[str]) -> None: - unknowns = [] - for k in kwargs: - if k == 'language_args': - continue - if k not in known_kwargs: - unknowns.append(k) - if len(unknowns) > 0: - mlog.warning('Unknown keyword argument(s) in target {}: {}.'.format(self.name, ', '.join(unknowns))) - def process_objectlist(self, objects): assert isinstance(objects, list) deprecated_non_objects = [] From a3b3f6ffa602e9af8908cda1a34ed7b56381a9a5 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 28 Jan 2026 13:33:22 -0800 Subject: [PATCH 16/16] interpreter: Convert BuildTarget kwargs explicitly from DSL to build format We currently sort of manipulate the dictionary in place and kinda hope everything works. This moves the conversion to be explicit, building a new dictionary (not requiring a copy) and getting help from static type checkers to ensure that everything is correct. --- mesonbuild/interpreter/interpreter.py | 312 +++++++++++++++++++------- mesonbuild/interpreter/kwargs.py | 12 +- 2 files changed, 239 insertions(+), 85 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 40709bece8af..55f0ec841c19 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -3189,6 +3189,9 @@ def source_strings_to_files(self, sources: T.List['mesonlib.FileOrString'], stri @T.overload def source_strings_to_files(self, sources: T.List['mesonlib.FileOrString'], strict: bool = False) -> T.List['mesonlib.FileOrString']: ... # noqa: F811 + @T.overload + def source_strings_to_files(self, sources: T.List[T.Union[mesonlib.FileOrString, build.BuildTargetTypes]]) -> T.List[T.Union[mesonlib.File, build.BuildTargetTypes]]: ... + @T.overload def source_strings_to_files(self, sources: T.List[T.Union[mesonlib.FileOrString, build.GeneratedTypes]]) -> T.List[T.Union[mesonlib.File, build.GeneratedTypes]]: ... # noqa: F811 @@ -3376,7 +3379,8 @@ def __convert_file_args(self, raw: T.List[mesonlib.FileOrString]) -> T.Tuple[T.L return depend_files, args - def __process_language_args(self, kwargs: T.Dict[str, T.List[mesonlib.FileOrString]]) -> None: + def __process_language_args(self, kwargs: kwtypes.BuildTarget + ) -> T.Tuple[T.DefaultDict[Language, T.List[str]], T.List[mesonlib.File]]: """Convert split language args into a combined dictionary. The Meson DSL takes arguments in the form `_args : args`, but in the @@ -3384,14 +3388,14 @@ def __process_language_args(self, kwargs: T.Dict[str, T.List[mesonlib.FileOrStri 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) + deps: T.List[mesonlib.File] = [] + new_args: T.DefaultDict[Language, T.List[str]] = collections.defaultdict(list) for l in compilers.all_languages: - deps, args = self.__convert_file_args(kwargs[f'{l}_args']) + deps, args = self.__convert_file_args(kwargs[f'{l}_args']) # type: ignore[literal-required] new_args[l] = args - d.extend(deps) - kwargs['language_args'] = new_args + deps.extend(deps) + return new_args, deps @staticmethod def _handle_rust_abi(abi: T.Optional[Literal['c', 'rust']], @@ -3427,6 +3431,219 @@ def _handle_rust_abi(abi: T.Optional[Literal['c', 'rust']], crate_type = default_rust_type return crate_type + def __convert_build_target_base_kwargs(self, kwargs: kwtypes.BuildTarget, final: build.BuildTargetKeywordArguments) -> None: + """Convert shared arguments for BuildTargets to the Build layer form. + + This takes the raw DSL form, and builds a new dict in the form that the BuildTarget expects + + :param kwargs: The arguments in raw DSL form + :param final: A new dictionary to fill in with the Build layer + :raises InvalidArguments: If one the PCH files doesn't exit + """ + # copy common arguments directly + for arg in ('build_by_default', 'build_rpath', 'build_subdir', 'c_pch', + 'cpp_pch', 'd_debug', 'd_module_versions', 'd_unittest', + 'dependencies', 'gnu_symbol_visibility', 'install', + 'install_mode', 'install_rpath', + 'implicit_include_directories', 'link_args', + 'link_language', 'link_with', 'link_whole', 'name_prefix', + 'name_suffix', 'native', 'resources', 'vala_header', + 'vala_gir', 'vala_vapi', 'swift_interoperability_mode', + 'swift_module_name', 'rust_crate_type', + 'rust_dependency_map', 'override_options'): + final[arg] = kwargs[arg] + + # this is not accessable from the DSL, so we need to initialize it + # ourselves + final['depend_files'] = [] + + # Check for non-existant PCH files + missing: T.List[str] = [] + for each in itertools.chain(kwargs['c_pch'] or [], kwargs['cpp_pch'] or []): + if each is not None: + if not os.path.isfile(os.path.join(self.environment.source_dir, self.subdir, each)): + missing.append(os.path.join(self.subdir, each)) + if missing: + raise InvalidArguments('The following PCH files do not exist: {}'.format(', '.join(missing))) + + # The build layer and interpreter don't agree here, and it's not clear + # that we're hanlding this correctly + final['install_dir'] = kwargs['install_dir'] # type: ignore[typeddict-item] + final['link_depends'] = self.source_strings_to_files(kwargs['link_depends']) + final['install_tag'] = [kwargs['install_tag']] + final['extra_files'] = mesonlib.unique_list(self.source_strings_to_files(kwargs['extra_files'])) + + # Convert into IncludeDirs objects + final['include_directories'] = self.extract_incdirs(kwargs['include_directories']) + final['d_import_dirs'] = self.extract_incdirs(kwargs['d_import_dirs'], True) + + # Convert language arguments + lang_args, deps = self.__process_language_args(kwargs) + final['language_args'] = lang_args + final['depend_files'].extend(deps) + + def __convert_executable_kwargs(self, node: mparser.BaseNode, kwargs: kwtypes.Executable) -> build.ExecutableKeywordArguments: + """Convert Executable arguments from DSL form to the build layer form. + + :param node: The Node being evaluated + :param kwargs: The DSL keyword arguments + :raises InvalidArguments: If both gui_app and win_subsystem are set + :raises InvalidArguments: If implib is false and export_dynamic is true + :raises InvalidArguments: If the rust_crate_type is set to anything except bin + :return: The keyword arguments as the Build layer expects + """ + final: build.ExecutableKeywordArguments = {} + self.__convert_build_target_base_kwargs(kwargs, final) + + # Exe exclusive arguments + for exe_arg in ('android_exe_type', 'pie', 'vs_module_defs'): + final[exe_arg] = kwargs[exe_arg] + + # rewrite `gui_app` to `win_subsystem` + if kwargs['gui_app'] is not None: + if kwargs['win_subsystem'] is not None: + raise InvalidArguments.from_node( + 'Executable got both "gui_app", and "win_subsystem" arguments, which are mutually exclusive', + node=node) + final['win_subsystem'] = 'windows' + elif kwargs['win_subsystem'] is not None: + final['win_subsystem'] = kwargs['win_subsystem'] + else: + final['win_subsystem'] = 'console' + + # handle interactions between implib and export_dynamic + if kwargs['implib']: + if kwargs['export_dynamic'] is False: + FeatureDeprecated.single_use( + 'implib overrides explicit export_dynamic off', '1.3.0', self.subproject, + 'Do not set ths if want export_dynamic disabled if implib is enabled', + location=node) + kwargs['export_dynamic'] = True + elif kwargs['export_dynamic']: + if kwargs['implib'] is False: + raise InvalidArguments.from_node( + '"implib" keyword" must not be false if "export_dynamic" is set and not false.', + node=node) + if kwargs['export_dynamic'] is None: + kwargs['export_dynamic'] = False + if isinstance(kwargs['implib'], bool): + final['implib'] = None + else: + final['implib'] = kwargs['implib'] + final['export_dynamic'] = kwargs['export_dynamic'] + + # Handle executable speicific rust_crate_type + if kwargs['rust_crate_type'] not in {None, 'bin'}: + raise InvalidArguments.from_node( + 'Crate type for executable must be "bin"', node=node) + final['rust_crate_type'] = 'bin' + + return final + + def __convert_static_library_kwargs(self, node: mparser.BaseNode, kwargs: kwtypes.StaticLibrary) -> build.StaticLibraryKeywordArguments: + """Convert StaticLibrary arguments to the Build format. + + :param node: The Node currently be evaluated. + :param kwargs: The DSL form of the keyword arguments. + :return: The arguments in Build layer format. + """ + final: build.StaticLibraryKeywordArguments = {} + self.__convert_build_target_base_kwargs(kwargs, final) + + for arg in ('pic', 'prelink'): + final[arg] = kwargs[arg] + + for lang in compilers.all_languages - {'java'}: + deps, args = self.__convert_file_args(kwargs.get(f'{lang}_static_args', [])) # type: ignore[arg-type] + final['language_args'][lang].extend(args) + final['depend_files'].extend(deps) + final['rust_crate_type'] = self._handle_rust_abi( + kwargs['rust_abi'], kwargs['rust_crate_type'], 'rlib', 'staticlib', + build.StaticLibrary.typename) + + return final + + def __convert_shared_library_kwargs(self, node: mparser.BaseNode, kwargs: kwtypes.SharedLibrary) -> build.SharedLibraryKeywordArguments: + """Convert SharedLibrary arguments to the Build format. + + :param node: The Node currently be evaluated. + :param kwargs: The DSL form of the keyword arguments. + :return: The arguments in Build layer format. + """ + final: build.SharedLibraryKeywordArguments = {} + self.__convert_build_target_base_kwargs(kwargs, final) + + for arg in ('version', 'soversion', 'darwin_versions', 'shortname'): + final[arg] = kwargs[arg] + + for lang in compilers.all_languages - {'java'}: + deps, args = self.__convert_file_args(kwargs.get(f'{lang}_shared_args', [])) # type: ignore[arg-type] + final['language_args'][lang].extend(args) + final['depend_files'].extend(deps) + final['rust_crate_type'] = self._handle_rust_abi( + kwargs['rust_abi'], kwargs['rust_crate_type'], 'dylib', 'cdylib', + build.SharedLibrary.typename, extra_valid_types={'proc-macro'}) + + return final + + def __convert_shared_module_kwargs(self, node: mparser.BaseNode, kwargs: kwtypes.SharedModule) -> build.SharedModuleKeywordArguments: + """Convert SharedModule arguments to the Build format. + + :param node: The Node currently be evaluated. + :param kwargs: The DSL form of the keyword arguments. + :return: The arguments in Build layer format. + """ + final: build.SharedModuleKeywordArguments = {} + self.__convert_build_target_base_kwargs(kwargs, final) + + for arg in ('vs_module_defs', ): + final[arg] = kwargs[arg] + + for lang in compilers.all_languages - {'java'}: + deps, args = self.__convert_file_args(kwargs.get(f'{lang}_shared_args', [])) # type: ignore[arg-type] + final['language_args'][lang].extend(args) + final['depend_files'].extend(deps) + final['rust_crate_type'] = self._handle_rust_abi( + kwargs['rust_abi'], kwargs['rust_crate_type'], 'dylib', 'cdylib', + build.SharedModule.typename, extra_valid_types={'proc-macro'}) + + return final + + def __convert_jar_kwargs(self, node: mparser.BaseNode, kwargs: kwtypes.Jar) -> build.JarKeywordArguments: + """Convert Jar arguments to the Build format. + + :param node: The Node currently be evaluated. + :param kwargs: The DSL form of the keyword arguments. + :return: The arguments in Build layer format. + """ + final: build.JarKeywordArguments = {} + + # copy common arguments directly + for arg in ('build_by_default', 'build_subdir', 'dependencies', + 'install', 'install_mode', 'implicit_include_directories', + 'link_with', 'java_args', 'java_resources', 'main_class'): + final[arg] = kwargs[arg] + + # this is not accessable from the DSL, so we need to initialize it + # ourselves + final['depend_files'] = [] + + # The build layer and interpreter don't agree here, and it's not clear + # that we're hanlding this correctly + final['install_dir'] = kwargs['install_dir'] # type: ignore[typeddict-item] + final['install_tag'] = [kwargs['install_tag']] + final['extra_files'] = mesonlib.unique_list(self.source_strings_to_files(kwargs['extra_files'])) + + # Convert into IncludeDirs objects + final['include_directories'] = self.extract_incdirs(kwargs['include_directories']) + + # Convert language arguments + lang_args, deps = self.__process_language_args(kwargs) + final['language_args'] = lang_args + final['depend_files'].extend(deps) + + return final + @T.overload def build_target(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargsType], kwargs: kwtypes.Executable, targetclass: T.Type[build.Executable]) -> build.Executable: ... @@ -3455,9 +3672,6 @@ def build_target(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargs mlog.debug('Unknown target type:', str(targetclass)) raise RuntimeError('Unreachable code') - # Because who owns this isn't clear - kwargs = kwargs.copy() - name, raw_sources = args for_machine = kwargs['native'] if kwargs.get('rust_crate_type') == 'proc-macro': @@ -3480,49 +3694,6 @@ def build_target(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargs sources = self.source_strings_to_files([ s for s in raw_sources if not isinstance(s, (build.BuildTarget, build.ExtractedObjects))]) objs = kwargs['objects'] - # TODO: When we can do strings -> Files in the typed_kwargs validator, do this there too - kwargs['extra_files'] = mesonlib.unique_list(self.source_strings_to_files(kwargs['extra_files'])) - self.__process_language_args(kwargs) - if targetclass is build.StaticLibrary: - kwargs = T.cast('kwtypes.StaticLibrary', kwargs) - 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) - kwargs['rust_crate_type'] = self._handle_rust_abi( - kwargs['rust_abi'], kwargs['rust_crate_type'], 'rlib', 'staticlib', targetclass.typename) - - elif targetclass is build.SharedLibrary: - kwargs = T.cast('kwtypes.SharedLibrary', kwargs) - 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) - kwargs['rust_crate_type'] = self._handle_rust_abi( - kwargs['rust_abi'], kwargs['rust_crate_type'], 'dylib', 'cdylib', targetclass.typename, - extra_valid_types={'proc-macro'}) - - elif targetclass is build.Executable: - kwargs = T.cast('kwtypes.Executable', kwargs) - if kwargs['rust_crate_type'] not in {None, 'bin'}: - raise InvalidArguments('Crate type for executable must be "bin"') - kwargs['rust_crate_type'] = 'bin' - - if targetclass is not build.Jar: - self.check_for_jar_sources(sources, targetclass) - kwargs['include_directories'] = self.extract_incdirs(kwargs['include_directories']) - kwargs['d_import_dirs'] = self.extract_incdirs(kwargs['d_import_dirs'], True) - missing: T.List[str] = [] - for each in itertools.chain(kwargs['c_pch'] or [], kwargs['cpp_pch'] or []): - if each is not None: - if not os.path.isfile(os.path.join(self.environment.source_dir, self.subdir, each)): - missing.append(os.path.join(self.subdir, each)) - if missing: - raise InvalidArguments('The following PCH files do not exist: {}'.format(', '.join(missing))) - - # 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 | {'language_args'}} srcs: T.List['SourceInputs'] = [] struct: T.Optional[build.StructuredSources] = build.StructuredSources() @@ -3556,35 +3727,18 @@ def build_target(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargs outputs.update(o) if targetclass is build.Executable: - kwargs = T.cast('kwtypes.Executable', kwargs) - if kwargs['gui_app'] is not None: - if kwargs['win_subsystem'] is not None: - raise InvalidArguments.from_node( - 'Executable got both "gui_app", and "win_subsystem" arguments, which are mutually exclusive', - node=node) - if kwargs['gui_app']: - kwargs['win_subsystem'] = 'windows' - if kwargs['win_subsystem'] is None: - kwargs['win_subsystem'] = 'console' - - if kwargs['implib']: - if kwargs['export_dynamic'] is False: - FeatureDeprecated.single_use('implib overrides explicit export_dynamic off', '1.3.0', self.subproject, - 'Do not set ths if want export_dynamic disabled if implib is enabled', - location=node) - kwargs['export_dynamic'] = True - elif kwargs['export_dynamic']: - if kwargs['implib'] is False: - raise InvalidArguments('"implib" keyword" must not be false if "export_dynamic" is set and not false.') - if kwargs['export_dynamic'] is None: - kwargs['export_dynamic'] = False - if isinstance(kwargs['implib'], bool): - kwargs['implib'] = None - - kwargs['install_tag'] = [kwargs['install_tag']] + nkwargs = self.__convert_executable_kwargs(node, kwargs) + elif targetclass is build.StaticLibrary: + nkwargs = self.__convert_static_library_kwargs(node, kwargs) + elif targetclass is build.SharedLibrary: + nkwargs = self.__convert_shared_library_kwargs(node, kwargs) + elif targetclass is build.SharedModule: + nkwargs = self.__convert_shared_module_kwargs(node, kwargs) + else: + nkwargs = self.__convert_jar_kwargs(node, kwargs) target = targetclass(name, self.subdir, self.subproject, for_machine, srcs, struct, objs, - self.environment, self.compilers[for_machine], kwargs) + self.environment, self.compilers[for_machine], nkwargs) if objs and target.uses_rust(): FeatureNew.single_use('objects in Rust targets', '1.8.0', self.subproject) diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index f92392054447..4acf93ae99ea 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -364,7 +364,7 @@ class _BaseBuildTarget(TypedDict): vala_gir: T.Optional[str] -class _BuildTarget(_BaseBuildTarget): +class BuildTarget(_BaseBuildTarget): """Arguments shared by non-JAR functions""" @@ -402,7 +402,7 @@ class _LibraryMixin(TypedDict): rust_abi: T.Optional[RustAbi] -class Executable(_BuildTarget): +class Executable(BuildTarget): export_dynamic: T.Optional[bool] gui_app: T.Optional[bool] @@ -419,7 +419,7 @@ class _StaticLibMixin(TypedDict): pic: T.Optional[bool] -class StaticLibrary(_BuildTarget, _StaticLibMixin, _LibraryMixin): +class StaticLibrary(BuildTarget, _StaticLibMixin, _LibraryMixin): pass @@ -432,16 +432,16 @@ class _SharedLibMixin(TypedDict): shortname: str -class SharedLibrary(_BuildTarget, _SharedLibMixin, _LibraryMixin): +class SharedLibrary(BuildTarget, _SharedLibMixin, _LibraryMixin): pass -class SharedModule(_BuildTarget, _LibraryMixin): +class SharedModule(BuildTarget, _LibraryMixin): vs_module_defs: T.Optional[T.Union[str, File, build.CustomTarget, build.CustomTargetIndex]] -class Library(_BuildTarget, _SharedLibMixin, _StaticLibMixin, _LibraryMixin): +class Library(BuildTarget, _SharedLibMixin, _StaticLibMixin, _LibraryMixin): """For library, both_library, and as a base for build_target"""