From 25edb2cb9bc72605f2eb22577a0327ec39efe4ed Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Mon, 13 Apr 2026 17:48:50 +0300 Subject: [PATCH 001/681] Bump version number for new development. --- mesonbuild/coredata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 35fddc2c1c62..fb19d59b6ac8 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -44,7 +44,7 @@ # # Pip requires that RCs are named like this: '0.1.0.rc1' # But the corresponding Git tag needs to be '0.1.0rc1' -version = '1.11.0' +version = '1.11.99' # The next stable version when we are in dev. This is used to allow projects to # require meson version >=1.2.0 when using 1.1.99. FeatureNew won't warn when From 1b64242f88aa1e99f96d3a5a11098866c250d2c5 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 3 Apr 2026 09:07:31 -0700 Subject: [PATCH 002/681] compilers: Don't pass Environment into compiler methods Compilers and Linkers already have an Environment reference, so passing them in complicates the API needlessly. --- mesonbuild/backend/vs2010backend.py | 2 +- mesonbuild/compilers/asm.py | 14 +++++++------- mesonbuild/compilers/compilers.py | 13 +++++++------ mesonbuild/compilers/cuda.py | 10 +++++----- mesonbuild/compilers/d.py | 16 ++++++++-------- mesonbuild/compilers/mixins/clang.py | 9 ++++----- mesonbuild/compilers/mixins/clike.py | 8 ++++---- mesonbuild/compilers/mixins/visualstudio.py | 5 ++--- mesonbuild/compilers/rust.py | 6 +++--- mesonbuild/dependencies/boost.py | 2 +- 10 files changed, 42 insertions(+), 43 deletions(-) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 4f78f6708454..049b86a7074e 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -1349,7 +1349,7 @@ def add_non_makefile_vcxproj_elements( ET.SubElement(clconf, 'OpenMPSupport').text = 'true' # CRT type; debug or release vscrt_type = self.get_target_option(target, 'b_vscrt') - vscrt_val = compiler.get_crt_val(vscrt_type, self.environment) + vscrt_val = compiler.get_crt_val(vscrt_type) if vscrt_val == 'mdd': ET.SubElement(type_config, 'UseDebugLibraries').text = 'true' ET.SubElement(clconf, 'RuntimeLibrary').text = 'MultiThreadedDebugDLL' diff --git a/mesonbuild/compilers/asm.py b/mesonbuild/compilers/asm.py index c8dfc2371f83..6902652e8509 100644 --- a/mesonbuild/compilers/asm.py +++ b/mesonbuild/compilers/asm.py @@ -148,16 +148,16 @@ def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str], parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:])) return parameter_list - def get_crt_compile_args(self, crt_val: str, env: Environment) -> T.List[str]: + def get_crt_compile_args(self, crt_val: str) -> T.List[str]: return [] # Linking ASM-only objects into an executable or DLL # require this, otherwise it'll fail to find # _WinMain or _DllMainCRTStartup. - def get_crt_link_args(self, crt_val: str, env: Environment) -> T.List[str]: + def get_crt_link_args(self, crt_val: str) -> T.List[str]: if not isinstance(self.linker, VisualStudioLikeLinkerMixin): return [] - return self.crt_args[self.get_crt_val(crt_val, env)] + return self.crt_args[self.get_crt_val(crt_val)] class YasmCompiler(NasmCompiler): id = 'yasm' @@ -233,7 +233,7 @@ def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str], parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:])) return parameter_list - def get_crt_compile_args(self, crt_val: str, env: Environment) -> T.List[str]: + def get_crt_compile_args(self, crt_val: str) -> T.List[str]: return [] def depfile_for_object(self, objfile: str) -> T.Optional[str]: @@ -281,7 +281,7 @@ def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str], parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:])) return parameter_list - def get_crt_compile_args(self, crt_val: str, env: Environment) -> T.List[str]: + def get_crt_compile_args(self, crt_val: str) -> T.List[str]: return [] def get_depfile_format(self) -> str: @@ -309,7 +309,7 @@ def needs_static_linker(self) -> bool: def get_always_args(self) -> T.List[str]: return [] - def get_crt_compile_args(self, crt_val: str, env: Environment) -> T.List[str]: + def get_crt_compile_args(self, crt_val: str) -> T.List[str]: return [] def get_depfile_suffix(self) -> str: @@ -334,7 +334,7 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, 'everything': []} self.can_compile_suffixes.add('s') - def get_crt_compile_args(self, crt_val: str, env: Environment) -> T.List[str]: + def get_crt_compile_args(self, crt_val: str) -> T.List[str]: return [] def get_optimization_args(self, optimization_level: str) -> T.List[str]: diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 370ccc3f2b07..9c6aec5c96eb 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -344,7 +344,7 @@ def get_base_compile_args(target: 'BuildTarget', compiler: 'Compiler', env: 'Env crt_val = env.coredata.get_option_for_target(target, 'b_vscrt') assert isinstance(crt_val, str) try: - args += compiler.get_crt_compile_args(crt_val, env) + args += compiler.get_crt_compile_args(crt_val) except AttributeError: pass except KeyError: @@ -432,7 +432,7 @@ def get_base_link_args(target: 'BuildTarget', crt_val = env.coredata.get_option_for_target(target, 'b_vscrt') assert isinstance(crt_val, str) try: - crtargs = linker.get_crt_link_args(crt_val, env) + crtargs = linker.get_crt_link_args(crt_val) assert isinstance(crtargs, list) args += crtargs except AttributeError: @@ -1160,7 +1160,7 @@ def get_assert_args(self, disable: bool) -> T.List[str]: """ return [] - def get_crt_val(self, crt_val: str, env: Environment) -> str: + def get_crt_val(self, crt_val: str) -> str: if crt_val in options.MSCRT_VALS: return crt_val assert crt_val in {'from_buildtype', 'static_from_buildtype'} @@ -1172,7 +1172,8 @@ def get_crt_val(self, crt_val: str, env: Environment) -> str: rel = 'mt' # Match what build type flags used to do. - buildtype = env.coredata.optstore.get_value_for('buildtype') + buildtype = self.environment.coredata.optstore.get_value_for('buildtype') + assert isinstance(buildtype, str), 'for mypy' if buildtype == 'plain': return 'none' elif buildtype == 'debug': @@ -1183,10 +1184,10 @@ def get_crt_val(self, crt_val: str, env: Environment) -> str: assert buildtype == 'custom' raise EnvironmentException('Requested C runtime based on buildtype, but buildtype is "custom".') - def get_crt_compile_args(self, crt_val: str, env: Environment) -> T.List[str]: + def get_crt_compile_args(self, crt_val: str) -> T.List[str]: raise EnvironmentException('This compiler does not support Windows CRT selection') - def get_crt_link_args(self, crt_val: str, env: Environment) -> T.List[str]: + def get_crt_link_args(self, crt_val: str) -> T.List[str]: raise EnvironmentException('This compiler does not support Windows CRT selection') def get_compile_only_args(self) -> T.List[str]: diff --git a/mesonbuild/compilers/cuda.py b/mesonbuild/compilers/cuda.py index 485adddfc695..73a73331602c 100644 --- a/mesonbuild/compilers/cuda.py +++ b/mesonbuild/compilers/cuda.py @@ -725,17 +725,17 @@ def find_library(self, libname: str, extra_dirs: T.List[str], libtype: LibType = skip_link_check: bool = False) -> T.Optional[T.List[str]]: return self.host_compiler.find_library(libname, extra_dirs, libtype, lib_prefix_warning, ignore_system_dirs, skip_link_check) - def get_crt_compile_args(self, crt_val: str, env: Environment) -> T.List[str]: - return self._to_host_flags(self.host_compiler.get_crt_compile_args(crt_val, env)) + def get_crt_compile_args(self, crt_val: str) -> T.List[str]: + return self._to_host_flags(self.host_compiler.get_crt_compile_args(crt_val)) - def get_crt_link_args(self, crt_val: str, env: Environment) -> T.List[str]: + def get_crt_link_args(self, crt_val: str) -> T.List[str]: # nvcc defaults to static, release version of msvc runtime and provides no # native option to override it; override it with /NODEFAULTLIB host_link_arg_overrides = [] - host_crt_compile_args = self.host_compiler.get_crt_compile_args(crt_val, env) + host_crt_compile_args = self.host_compiler.get_crt_compile_args(crt_val) if any(arg in {'/MDd', '/MD', '/MTd'} for arg in host_crt_compile_args): host_link_arg_overrides += ['/NODEFAULTLIB:LIBCMT.lib'] - return self._to_host_flags(host_link_arg_overrides + self.host_compiler.get_crt_link_args(crt_val, env), Phase.LINKER) + return self._to_host_flags(host_link_arg_overrides + self.host_compiler.get_crt_link_args(crt_val), Phase.LINKER) def get_target_link_args(self, target: 'BuildTarget') -> T.List[str]: return self._to_host_flags(super().get_target_link_args(target), Phase.LINKER) diff --git a/mesonbuild/compilers/d.py b/mesonbuild/compilers/d.py index 38997fb28ade..31c342303a55 100644 --- a/mesonbuild/compilers/d.py +++ b/mesonbuild/compilers/d.py @@ -371,10 +371,10 @@ def get_debug_args(self, is_debug: bool) -> T.List[str]: return clike_debug_args[is_debug] + ddebug_args - def _get_crt_args(self, crt_val: str, env: Environment) -> T.List[str]: + def _get_crt_args(self, crt_val: str) -> T.List[str]: if not self.info.is_windows(): return [] - return self.mscrt_args[self.get_crt_val(crt_val, env)] + return self.mscrt_args[self.get_crt_val(crt_val)] def get_soname_args(self, prefix: str, shlib_name: str, suffix: str, soversion: str, darwin_versions: T.Tuple[str, str]) -> T.List[str]: @@ -527,10 +527,10 @@ def _get_target_arch_args(self) -> T.List[str]: return ['-m32'] return [] - def get_crt_compile_args(self, crt_val: str, env: Environment) -> T.List[str]: + def get_crt_compile_args(self, crt_val: str) -> T.List[str]: return [] - def get_crt_link_args(self, crt_val: str, env: Environment) -> T.List[str]: + def get_crt_link_args(self, crt_val: str) -> T.List[str]: return [] def _get_compile_extra_args(self, extra_args: T.Union[T.List[str], T.Callable[[CompileCheckMode], T.List[str]], None] = None) -> T.List[str]: @@ -718,8 +718,8 @@ def get_warn_args(self, level: str) -> T.List[str]: def get_pic_args(self) -> T.List[str]: return ['-relocation-model=pic'] - def get_crt_link_args(self, crt_val: str, env: Environment) -> T.List[str]: - return self._get_crt_args(crt_val, env) + def get_crt_link_args(self, crt_val: str) -> T.List[str]: + return self._get_crt_args(crt_val) def unix_args_to_native(self, args: T.List[str]) -> T.List[str]: return self._unix_args_to_native(args, self.info, self.linker.id) @@ -803,8 +803,8 @@ def _get_target_arch_args(self) -> T.List[str]: return ['-m32'] return [] - def get_crt_compile_args(self, crt_val: str, env: Environment) -> T.List[str]: - return self._get_crt_args(crt_val, env) + def get_crt_compile_args(self, crt_val: str) -> T.List[str]: + return self._get_crt_args(crt_val) def unix_args_to_native(self, args: T.List[str]) -> T.List[str]: return self._unix_args_to_native(args, self.info, self.linker.id) diff --git a/mesonbuild/compilers/mixins/clang.py b/mesonbuild/compilers/mixins/clang.py index f52f4e1181ab..69d29b70f1bc 100644 --- a/mesonbuild/compilers/mixins/clang.py +++ b/mesonbuild/compilers/mixins/clang.py @@ -21,7 +21,6 @@ from ...options import MutableKeyedOptionDictType from ...dependencies import Dependency # noqa: F401 from ...build import BuildTarget - from ...environment import Environment from ..compilers import Compiler CompilerMixinBase = Compiler @@ -91,16 +90,16 @@ def __init__(self, defines: T.Optional[T.Dict[str, str]]): # All Clang backends can also do LLVM IR self.can_compile_suffixes.add('ll') - def get_crt_compile_args(self, crt_val: str, env: Environment) -> T.List[str]: + def get_crt_compile_args(self, crt_val: str) -> T.List[str]: if not isinstance(self.linker, VisualStudioLikeLinkerMixin): return [] - crt_val = self.get_crt_val(crt_val, env) + crt_val = self.get_crt_val(crt_val) return self.CRT_D_ARGS[crt_val] - def get_crt_link_args(self, crt_val: str, env: Environment) -> T.List[str]: + def get_crt_link_args(self, crt_val: str) -> T.List[str]: if not isinstance(self.linker, VisualStudioLikeLinkerMixin): return [] - crt_val = self.get_crt_val(crt_val, env) + crt_val = self.get_crt_val(crt_val) return self.CRT_ARGS[crt_val] def get_colorout_args(self, colortype: str) -> T.List[str]: diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py index 9cbf64d5a4d7..e54ad7538162 100644 --- a/mesonbuild/compilers/mixins/clike.py +++ b/mesonbuild/compilers/mixins/clike.py @@ -333,8 +333,8 @@ def _get_basic_compiler_args(self, mode: CompileCheckMode) -> T.Tuple[T.List[str try: crt_val = self.environment.coredata.optstore.get_value_for('b_vscrt') assert isinstance(crt_val, str), 'for mypy' - cargs += self.get_crt_compile_args(crt_val, self.environment) - largs += self.get_crt_link_args(crt_val, self.environment) + cargs += self.get_crt_compile_args(crt_val) + largs += self.get_crt_link_args(crt_val) except (KeyError, AttributeError): pass @@ -1242,11 +1242,11 @@ def find_framework(self, name: str, extra_dirs: T.List[str], # TODO: should probably check for macOS? return self._find_framework_impl(name, extra_dirs, allow_system) - def get_crt_compile_args(self, crt_val: str, env: Environment) -> T.List[str]: + def get_crt_compile_args(self, crt_val: str) -> T.List[str]: # TODO: does this belong here or in GnuLike or maybe PosixLike? return [] - def get_crt_link_args(self, crt_val: str, env: Environment) -> T.List[str]: + def get_crt_link_args(self, crt_val: str) -> T.List[str]: # TODO: does this belong here or in GnuLike or maybe PosixLike? return [] diff --git a/mesonbuild/compilers/mixins/visualstudio.py b/mesonbuild/compilers/mixins/visualstudio.py index a809dbf62218..acf1bbc7bdc1 100644 --- a/mesonbuild/compilers/mixins/visualstudio.py +++ b/mesonbuild/compilers/mixins/visualstudio.py @@ -20,7 +20,6 @@ if T.TYPE_CHECKING: from ...build import BuildTarget - from ...environment import Environment from .clike import CLikeCompiler as Compiler else: # This is a bit clever, for mypy we pretend that these mixins descend from @@ -317,8 +316,8 @@ def get_default_include_dirs(self) -> T.List[str]: return [] return os.environ['INCLUDE'].split(os.pathsep) - def get_crt_compile_args(self, crt_val: str, env: Environment) -> T.List[str]: - crt_val = self.get_crt_val(crt_val, env) + def get_crt_compile_args(self, crt_val: str) -> T.List[str]: + crt_val = self.get_crt_val(crt_val) return self.crt_args[crt_val] def has_func_attribute(self, name: str) -> T.Tuple[bool, bool]: diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index 919baca86db4..20b3cff1f37f 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -394,17 +394,17 @@ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = args.append('--edition=' + std) return args - def get_crt_compile_args(self, crt_val: str, env: Environment) -> T.List[str]: + def get_crt_compile_args(self, crt_val: str) -> T.List[str]: # Rust handles this for us, we don't need to do anything return [] - def get_crt_link_args(self, crt_val: str, env: Environment) -> T.List[str]: + def get_crt_link_args(self, crt_val: str) -> T.List[str]: if not isinstance(self.linker, VisualStudioLikeLinkerMixin): return [] # Rustc always use non-debug Windows runtime. Inject the one selected # by Meson options instead. # https://github.com/rust-lang/rust/issues/39016 - return self.MSVCRT_ARGS[self.get_crt_val(crt_val, env)] + return self.MSVCRT_ARGS[self.get_crt_val(crt_val)] def get_colorout_args(self, colortype: str) -> T.List[str]: if colortype in {'always', 'never', 'auto'}: diff --git a/mesonbuild/dependencies/boost.py b/mesonbuild/dependencies/boost.py index 2a86c8264120..fe3fade03a2c 100644 --- a/mesonbuild/dependencies/boost.py +++ b/mesonbuild/dependencies/boost.py @@ -606,7 +606,7 @@ def filter_libraries(self, libs: T.List[BoostLibraryFile], lib_vers: str) -> T.L try: crt_val = self.env.coredata.optstore.get_value_for('b_vscrt') assert isinstance(crt_val, str) - vscrt = self.clib_compiler.get_crt_compile_args(crt_val, self.env)[0] + vscrt = self.clib_compiler.get_crt_compile_args(crt_val)[0] except (KeyError, IndexError, AttributeError): pass From e85f38361f94cc63154dfc3353cee57be4469cec Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 16 Mar 2026 09:17:22 +0100 Subject: [PATCH 003/681] interpreter, modules: use REQUIRED_KW Signed-off-by: Paolo Bonzini --- mesonbuild/interpreter/compiler.py | 4 ++-- mesonbuild/modules/_qt.py | 5 ++--- mesonbuild/modules/python.py | 5 ++--- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/mesonbuild/interpreter/compiler.py b/mesonbuild/interpreter/compiler.py index b0224db1fe9b..051a88e9033a 100644 --- a/mesonbuild/interpreter/compiler.py +++ b/mesonbuild/interpreter/compiler.py @@ -12,7 +12,6 @@ from .. import build from .. import dependencies -from .. import options from .. import mesonlib from .. import mlog from ..compilers import SUFFIX_TO_LANG, RunResult @@ -26,6 +25,7 @@ from .type_checking import INCLUDE_DIRECTORIES, REQUIRED_KW, in_set_validator, NoneType if T.TYPE_CHECKING: + from .. import options from ..interpreter import Interpreter from ..compilers import Compiler from ..interpreterbase import TYPE_var, TYPE_kwargs @@ -666,7 +666,7 @@ def notfound_library(self, libname: str) -> 'dependencies.ExternalLibrary': @typed_pos_args('compiler.find_library', str) @typed_kwargs( 'compiler.find_library', - KwargInfo('required', (bool, options.UserFeatureOption), default=True), + REQUIRED_KW, KwargInfo('has_headers', ContainerTypeInfo(list, str), listify=True, default=[], since='0.50.0'), KwargInfo('static', (bool, NoneType), since='0.51.0'), KwargInfo('disabler', bool, default=False, since='0.49.0'), diff --git a/mesonbuild/modules/_qt.py b/mesonbuild/modules/_qt.py index 19ee19b09286..8cba3efa78be 100644 --- a/mesonbuild/modules/_qt.py +++ b/mesonbuild/modules/_qt.py @@ -12,12 +12,11 @@ from . import ModuleReturnValue, ExtensionModule from .. import build -from .. import options from .. import mlog from ..dependencies import DependencyMethods, find_external_dependency, Dependency, ExternalLibrary, InternalDependency from ..mesonlib import MachineChoice, MesonException, File, FileMode, version_compare, Popen_safe from ..interpreter import extract_required_kwarg -from ..interpreter.type_checking import DEPENDENCY_METHOD_KW, INSTALL_DIR_KW, INSTALL_KW, NoneType +from ..interpreter.type_checking import DEPENDENCY_METHOD_KW, INSTALL_DIR_KW, INSTALL_KW, REQUIRED_KW, NoneType from ..interpreterbase import ContainerTypeInfo, FeatureDeprecated, KwargInfo, noPosargs, FeatureNew, typed_kwargs, typed_pos_args from ..programs import NonExistingExternalProgram @@ -368,7 +367,7 @@ def _parse_qrc_deps(self, state: ModuleState, @typed_kwargs( 'qt.has_tools', DEPENDENCY_METHOD_KW, - KwargInfo('required', (bool, options.UserFeatureOption), default=False), + REQUIRED_KW.evolve(default=False), KwargInfo('tools', ContainerTypeInfo(list, str), listify=True, default=['moc', 'uic', 'rcc', 'lrelease'], validator=_list_in_set_validator(_set_of_qt_tools), diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py index 881b54e4ca50..7a33a02ce1ec 100644 --- a/mesonbuild/modules/python.py +++ b/mesonbuild/modules/python.py @@ -9,14 +9,13 @@ from . import ExtensionModule, ModuleInfo from .. import mesonlib from .. import mlog -from ..options import UserFeatureOption from ..build import known_shmod_kwargs, CustomTarget, CustomTargetIndex, BuildTarget, GeneratedList, StructuredSources, ExtractedObjects, SharedModule from ..dependencies import NotFoundDependency from ..dependencies.detect import get_dep_identifier, find_external_dependency from ..dependencies.python import BasicPythonExternalProgram, python_factory, _PythonDependencyBase from ..interpreter import extract_required_kwarg, primitives as P_OBJ from ..interpreter.interpreterobjects import ProgramHolder -from ..interpreter.type_checking import NoneType, DEPENDENCY_KWS, PRESERVE_PATH_KW, SHARED_MOD_KWS +from ..interpreter.type_checking import NoneType, DEPENDENCY_KWS, PRESERVE_PATH_KW, REQUIRED_KW, SHARED_MOD_KWS from ..interpreterbase import ( noPosargs, noKwargs, permittedKwargs, ContainerTypeInfo, InvalidArguments, typed_pos_args, typed_kwargs, KwargInfo, @@ -507,7 +506,7 @@ def _find_installation_impl(self, state: 'ModuleState', display_name: str, name_ @typed_pos_args('python.find_installation', optargs=[str]) @typed_kwargs( 'python.find_installation', - KwargInfo('required', (bool, UserFeatureOption), default=True), + REQUIRED_KW, KwargInfo('disabler', bool, default=False, since='0.49.0'), KwargInfo('modules', ContainerTypeInfo(list, str), listify=True, default=[], since='0.51.0'), _PURE_KW.evolve(default=True, since='0.64.0'), From af120502facec12c56e1b48025a901fdf0f5b18c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 16 Mar 2026 09:18:41 +0100 Subject: [PATCH 004/681] interpreter: remove use of UserFeatureOption Introduce a dataclass to wrap the result of get_option() for features. This removes a layering violation, where UserFeatureOptions are created outside options.py just to apply modifiers like disable_if(). Signed-off-by: Paolo Bonzini --- mesonbuild/ast/introspection.py | 8 ++--- mesonbuild/interpreter/compiler.py | 5 ++- mesonbuild/interpreter/interpreter.py | 17 ++++------ mesonbuild/interpreter/interpreterobjects.py | 34 +++++++++---------- mesonbuild/interpreter/kwargs.py | 3 +- mesonbuild/interpreter/type_checking.py | 7 ++-- mesonbuild/interpreterbase/__init__.py | 2 ++ mesonbuild/interpreterbase/helpers.py | 25 ++++++++++++-- .../meson.build | 2 +- .../meson.build | 2 +- unittests/failuretests.py | 2 +- 11 files changed, 62 insertions(+), 45 deletions(-) diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py index 3d2211677056..6f27ac7ea1aa 100644 --- a/mesonbuild/ast/introspection.py +++ b/mesonbuild/ast/introspection.py @@ -9,10 +9,10 @@ import os import typing as T -from .. import compilers, environment, mesonlib, options +from .. import compilers, environment, mesonlib from ..build import Executable, Jar, SharedLibrary, SharedModule, StaticLibrary from ..compilers import detect_compiler_for -from ..interpreterbase import InvalidArguments, SubProject, UnknownValue +from ..interpreterbase import InvalidArguments, SubProject, UnknownValue, Feature from ..mesonlib import MachineChoice from ..options import OptionKey from ..mparser import BaseNode, ArrayNode, ElementaryNode, IdNode, FunctionNode, StringNode @@ -149,8 +149,8 @@ def do_subproject(self, dirname: SubProject) -> None: def func_add_languages(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> UnknownValue: kwargs = self.flatten_kwargs(kwargs) required = kwargs.get('required', True) - assert isinstance(required, (bool, options.UserFeatureOption, UnknownValue)), 'for mypy' - if isinstance(required, options.UserFeatureOption): + assert isinstance(required, (bool, Feature, UnknownValue)), 'for mypy' + if isinstance(required, Feature): required = required.is_enabled() if 'native' in kwargs: native = kwargs.get('native', False) diff --git a/mesonbuild/interpreter/compiler.py b/mesonbuild/interpreter/compiler.py index 051a88e9033a..f697f2bc3444 100644 --- a/mesonbuild/interpreter/compiler.py +++ b/mesonbuild/interpreter/compiler.py @@ -25,10 +25,9 @@ from .type_checking import INCLUDE_DIRECTORIES, REQUIRED_KW, in_set_validator, NoneType if T.TYPE_CHECKING: - from .. import options from ..interpreter import Interpreter from ..compilers import Compiler - from ..interpreterbase import TYPE_var, TYPE_kwargs + from ..interpreterbase import Feature, TYPE_var, TYPE_kwargs from .kwargs import ExtractRequired, ExtractSearchDirs from .interpreter import SourceOutputs from ..mlog import TV_LoggableList @@ -89,7 +88,7 @@ class FindLibraryKW(ExtractRequired, ExtractSearchDirs): header_include_directories: T.List[T.Union[build.IncludeDirs, str]] header_no_builtin_args: bool header_prefix: str - header_required: T.Union[bool, options.UserFeatureOption] + header_required: T.Union[bool, Feature] class PreprocessKW(TypedDict): output: str diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index eb82c9cd137a..d3a819e0b95b 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -30,7 +30,7 @@ from ..interpreterbase import Disabler, disablerIfNotFound from ..interpreterbase import FeatureNew, FeatureDeprecated, FeatureBroken, FeatureNewKwargs from ..interpreterbase import ObjectHolder, ContextManagerObject -from ..interpreterbase import stringifyUserArguments +from ..interpreterbase import stringifyUserArguments, Feature from ..modules import ExtensionModule, ModuleObject, MutableModuleObject, NewExtensionModule, NotFoundExtensionModule from ..optinterpreter import optname_regex @@ -109,7 +109,6 @@ import typing as T import textwrap import importlib -import copy import itertools if T.TYPE_CHECKING: @@ -171,9 +170,9 @@ def add_section(self, section: str, values: T.Dict[str, T.Any], bool_yn: bool, elif isinstance(i, Disabler): FeatureNew.single_use('disabler in summary', '0.64.0', subproject) formatted_values.append(mlog.red('NO')) - elif isinstance(i, options.UserOption): + elif isinstance(i, Feature): FeatureNew.single_use('feature option in summary', '0.58.0', subproject) - formatted_values.append(i.printable_value()) + formatted_values.append(str(i)) else: m = 'Summary value in section {!r}, key {!r}, must be string, integer, boolean, dependency, disabler, or external program' raise InterpreterException(m.format(section, k)) @@ -430,7 +429,7 @@ def build_holder_map(self) -> None: build.StructuredSources: OBJ.StructuredSourcesHolder, compilers.RunResult: compilerOBJ.TryRunResultHolder, dependencies.ExternalLibrary: OBJ.ExternalLibraryHolder, - options.UserFeatureOption: OBJ.FeatureOptionHolder, + Feature: OBJ.FeatureOptionHolder, envconfig.MachineInfo: OBJ.MachineHolder, build.ConfigurationData: OBJ.ConfigurationDataHolder, }) @@ -1097,11 +1096,9 @@ def func_get_option(self, node: mparser.BaseNode, args: T.Tuple[str], if self.subproject: raise MesonException(f'Option {optname} does not exist for subproject {self.subproject}.') raise MesonException(f'Option {optname} does not exist.') + if isinstance(option_object, options.UserFeatureOption): - ocopy = copy.copy(option_object) - ocopy.name = optname - ocopy.value = value - return ocopy + return Feature(optname, value) elif optname == 'b_sanitize': assert isinstance(option_object, options.UserStringArrayOption) # To ensure backwards compatibility this always returns a string. @@ -1838,7 +1835,7 @@ def func_dependency(self, node: mparser.BaseNode, args: T.Tuple[T.List[str]], kw return dependencies.NotFoundDependency(names[0], self.environment) nkwargs = T.cast('dependencies.base.DependencyObjectKWs', kwargs.copy()) - nkwargs['required'] = required # to replace a possible UserFeatureOption with a bool + nkwargs['required'] = required # to replace a possible Feature with a bool try: d = df.lookup(nkwargs) diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index 2399fba0f179..c5e265f5ec84 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -10,7 +10,6 @@ from pathlib import Path, PurePath from .. import mesonlib -from .. import options from .. import build from .. import mlog @@ -22,7 +21,8 @@ FeatureNew, FeatureDeprecated, typed_pos_args, typed_kwargs, typed_operator, noArgsFlattening, noPosargs, noKwargs, unholder_return, - flatten, resolve_second_level_holders, InterpreterException, InvalidArguments, InvalidCode) + flatten, resolve_second_level_holders, Feature, + InterpreterException, InvalidArguments, InvalidCode) from ..interpreter.type_checking import NoneType, ENV_KW, ENV_SEPARATOR_KW, PKGCONFIG_DEFINE_KW from ..dependencies import Dependency, ExternalLibrary, InternalDependency from ..programs import ExternalProgram, Program @@ -66,7 +66,7 @@ def extract_required_kwarg(kwargs: 'kwargs.ExtractRequired', :param subproject: The subproject this is :param feature_check: A custom feature check for this use of `required` with a - `UserFeatureOption`, defaults to None. + `Feature`, defaults to None. :param default: The default value is `required` is not set in `kwargs`, defaults to True @@ -77,7 +77,8 @@ def extract_required_kwarg(kwargs: 'kwargs.ExtractRequired', """ val = kwargs.get('required', default) required = False - if isinstance(val, options.UserFeatureOption): + feature: T.Optional[str] = None + if isinstance(val, Feature): if not feature_check: feature_check = FeatureNew('User option "feature"', '0.47.0') feature_check.use(subproject) @@ -110,25 +111,22 @@ def extract_search_dirs(kwargs: 'kwargs.ExtractSearchDirs') -> T.List[str]: raise InvalidCode(f'Search directory {d} is not an absolute path.') return [str(s) for s in search_dirs] -class FeatureOptionHolder(ObjectHolder[options.UserFeatureOption]): - def __init__(self, option: options.UserFeatureOption, interpreter: 'Interpreter'): +class FeatureOptionHolder(ObjectHolder[Feature]): + def __init__(self, option: Feature, interpreter: 'Interpreter'): super().__init__(option, interpreter) if option and option.is_auto(): - # TODO: we need to cast here because options is not a TypedDict - auto = T.cast('options.UserFeatureOption', self.env.coredata.optstore.resolve_option('auto_features')) - self.held_object = copy.copy(auto) - self.held_object.name = option.name + option.value = T.cast('str', self.env.coredata.optstore.get_value_for('auto_features')) @property def value(self) -> str: return 'disabled' if not self.held_object else self.held_object.value - def as_disabled(self) -> options.UserFeatureOption: + def as_disabled(self) -> Feature: disabled = copy.deepcopy(self.held_object) disabled.value = 'disabled' return disabled - def as_enabled(self) -> options.UserFeatureOption: + def as_enabled(self) -> Feature: enabled = copy.deepcopy(self.held_object) enabled.value = 'enabled' return enabled @@ -158,7 +156,7 @@ def allowed_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool: def auto_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool: return self.value == 'auto' - def _disable_if(self, condition: bool, message: T.Optional[str]) -> options.UserFeatureOption: + def _disable_if(self, condition: bool, message: T.Optional[str]) -> Feature: if not condition: return copy.deepcopy(self.held_object) @@ -176,7 +174,7 @@ def _disable_if(self, condition: bool, message: T.Optional[str]) -> options.User _ERROR_MSG_KW, ) @InterpreterObject.method('require') - def require_method(self, args: T.Tuple[bool], kwargs: 'kwargs.FeatureOptionRequire') -> options.UserFeatureOption: + def require_method(self, args: T.Tuple[bool], kwargs: 'kwargs.FeatureOptionRequire') -> Feature: return self._disable_if(not args[0], kwargs['error_message']) @FeatureNew('feature_option.disable_if()', '1.1.0') @@ -186,7 +184,7 @@ def require_method(self, args: T.Tuple[bool], kwargs: 'kwargs.FeatureOptionRequi _ERROR_MSG_KW, ) @InterpreterObject.method('disable_if') - def disable_if_method(self, args: T.Tuple[bool], kwargs: 'kwargs.FeatureOptionRequire') -> options.UserFeatureOption: + def disable_if_method(self, args: T.Tuple[bool], kwargs: 'kwargs.FeatureOptionRequire') -> Feature: return self._disable_if(args[0], kwargs['error_message']) @FeatureNew('feature_option.enable_if()', '1.1.0') @@ -196,7 +194,7 @@ def disable_if_method(self, args: T.Tuple[bool], kwargs: 'kwargs.FeatureOptionRe _ERROR_MSG_KW, ) @InterpreterObject.method('enable_if') - def enable_if_method(self, args: T.Tuple[bool], kwargs: 'kwargs.FeatureOptionRequire') -> options.UserFeatureOption: + def enable_if_method(self, args: T.Tuple[bool], kwargs: 'kwargs.FeatureOptionRequire') -> Feature: if not args[0]: return copy.deepcopy(self.held_object) @@ -211,14 +209,14 @@ def enable_if_method(self, args: T.Tuple[bool], kwargs: 'kwargs.FeatureOptionReq @noKwargs @typed_pos_args('feature_option.disable_auto_if', bool) @InterpreterObject.method('disable_auto_if') - def disable_auto_if_method(self, args: T.Tuple[bool], kwargs: TYPE_kwargs) -> options.UserFeatureOption: + def disable_auto_if_method(self, args: T.Tuple[bool], kwargs: TYPE_kwargs) -> Feature: return copy.deepcopy(self.held_object) if self.value != 'auto' or not args[0] else self.as_disabled() @FeatureNew('feature_option.enable_auto_if()', '1.1.0') @noKwargs @typed_pos_args('feature_option.enable_auto_if', bool) @InterpreterObject.method('enable_auto_if') - def enable_auto_if_method(self, args: T.Tuple[bool], kwargs: TYPE_kwargs) -> options.UserFeatureOption: + def enable_auto_if_method(self, args: T.Tuple[bool], kwargs: TYPE_kwargs) -> Feature: return self.as_enabled() if self.value == 'auto' and args[0] else copy.deepcopy(self.held_object) diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index 8763b46e7e33..06ccd6b0314d 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -14,6 +14,7 @@ from ..compilers import Compiler from ..compilers.compilers import Language from ..dependencies.base import Dependency, DependencyMethods, IncludeType +from ..interpreterbase import Feature from ..mesonlib import EnvironmentVariables, MachineChoice, File, FileMode, FileOrString from ..options import OptionKey from ..modules.cmake import CMakeSubprojectOptions @@ -85,7 +86,7 @@ class ExtractRequired(TypedDict): a boolean or a feature option should inherit its arguments from this class. """ - required: T.Union[bool, options.UserFeatureOption] + required: T.Union[bool, Feature] class ExtractSearchDirs(TypedDict): diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py index 8a16fb8e6404..65d87a106b13 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -11,8 +11,9 @@ from ..build import (CustomTarget, BuildTarget, CustomTargetIndex, ExtractedObjects, GeneratedList, IncludeDirs, BothLibraries, SharedLibrary, StaticLibrary, Jar, Executable, StructuredSources) -from ..options import OptionKey, UserFeatureOption +from ..options import OptionKey from ..dependencies import Dependency, DependencyMethods, InternalDependency +from ..interpreterbase import Feature from ..interpreterbase.decorators import KwargInfo, ContainerTypeInfo, FeatureBroken, FeatureDeprecated from ..mesonlib import (File, FileMode, MachineChoice, has_path_sep, listify, stringlistify, EnvironmentVariables) @@ -184,9 +185,9 @@ def variables_convertor(contents: T.Union[str, T.List[str], T.Dict[str, str]]) - convertor=_install_mode_convertor, ) -REQUIRED_KW: KwargInfo[T.Union[bool, UserFeatureOption]] = KwargInfo( +REQUIRED_KW: KwargInfo[T.Union[bool, Feature]] = KwargInfo( 'required', - (bool, UserFeatureOption), + (bool, Feature), default=True, # TODO: extract_required_kwarg could be converted to a convertor ) diff --git a/mesonbuild/interpreterbase/__init__.py b/mesonbuild/interpreterbase/__init__.py index 88fa706b9e90..fca54bb2efc7 100644 --- a/mesonbuild/interpreterbase/__init__.py +++ b/mesonbuild/interpreterbase/__init__.py @@ -25,6 +25,7 @@ 'flatten', 'resolve_second_level_holders', 'stringifyUserArguments', + 'Feature', 'noPosargs', 'noKwargs', @@ -125,6 +126,7 @@ flatten, resolve_second_level_holders, stringifyUserArguments, + Feature, ) from .interpreterbase import InterpreterBase from .operator import MesonOperator diff --git a/mesonbuild/interpreterbase/helpers.py b/mesonbuild/interpreterbase/helpers.py index 0b0436209dbe..cd9a8af31d8b 100644 --- a/mesonbuild/interpreterbase/helpers.py +++ b/mesonbuild/interpreterbase/helpers.py @@ -2,10 +2,11 @@ # Copyright 2013-2021 The Meson development team from __future__ import annotations +from dataclasses import dataclass from .. import mesonlib, mparser from .exceptions import InterpreterException, InvalidArguments -from ..options import UserOption +from ..mesonlib import HoldableObject import collections.abc @@ -60,8 +61,26 @@ def stringifyUserArguments(args: TYPE_var, subproject: SubProject, quote: bool = l = ['{} : {}'.format(stringifyUserArguments(k, subproject, True), stringifyUserArguments(v, subproject, True)) for k, v in args.items()] return '{%s}' % ', '.join(l) - elif isinstance(args, UserOption): + elif isinstance(args, Feature): from .decorators import FeatureNew FeatureNew.single_use('User option in string format', '1.3.0', subproject) - return stringifyUserArguments(args.printable_value(), subproject) + return str(args) raise InvalidArguments('Value other than strings, integers, bools, options, dictionaries and lists thereof.') + + +@dataclass +class Feature(HoldableObject): + name: str + value: str + + def is_enabled(self) -> bool: + return self.value == 'enabled' + + def is_disabled(self) -> bool: + return self.value == 'disabled' + + def is_auto(self) -> bool: + return self.value == 'auto' + + def __str__(self) -> str: + return str(self.value) diff --git a/test cases/common/264 required keyword in has functions/meson.build b/test cases/common/264 required keyword in has functions/meson.build index afd07eab1a50..c4a409e552cd 100644 --- a/test cases/common/264 required keyword in has functions/meson.build +++ b/test cases/common/264 required keyword in has functions/meson.build @@ -50,7 +50,7 @@ if not ['pgi', 'msvc', 'clang-cl', 'intel-cl'].contains(cc.get_id()) assert(not cc.has_function_attribute(a, required: opt)) endif -testcase expect_error('''compiler.has_function keyword argument 'required' was of type str but should have been one of: bool, UserFeatureOption''') +testcase expect_error('''compiler.has_function keyword argument 'required' was of type str but should have been one of: bool, Feature''') cc.has_function('printf', required : 'not a bool') endtestcase diff --git a/test cases/common/276 required keyword in compiles functions/meson.build b/test cases/common/276 required keyword in compiles functions/meson.build index d0f5b7f4a9b4..5518a9bef883 100644 --- a/test cases/common/276 required keyword in compiles functions/meson.build +++ b/test cases/common/276 required keyword in compiles functions/meson.build @@ -22,7 +22,7 @@ if meson.can_run_host_binaries() assert(res.stderr() == '') endif -testcase expect_error('''compiler.compiles keyword argument 'required' was of type str but should have been one of: bool, UserFeatureOption''') +testcase expect_error('''compiler.compiles keyword argument 'required' was of type str but should have been one of: bool, Feature''') cc.compiles(valid, name: 'valid', required : 'not a bool') endtestcase diff --git a/unittests/failuretests.py b/unittests/failuretests.py index b08a6958b4e5..073ea5bd9353 100644 --- a/unittests/failuretests.py +++ b/unittests/failuretests.py @@ -148,7 +148,7 @@ def test_dependency(self): a = (("dependency('zlib', method : 'fail')", 'dependency keyword argument "method" must be one of auto, builtin, cmake, config-tool, cups-config, dub, extraframework, libwmf-config, pcap-config, pkg-config, qmake, sdlconfig, sysconfig, system, not fail'), ("dependency('zlib', static : '1')", "dependency keyword argument 'static' was of type str but should have been one of: bool, NoneType"), ("dependency('zlib', version : 1)", r"dependency keyword argument 'version' was of type array\[int\] but should have been array\[str\]"), - ("dependency('zlib', required : 1)", "dependency keyword argument 'required' was of type int but should have been one of: bool, UserFeatureOption"), + ("dependency('zlib', required : 1)", "dependency keyword argument 'required' was of type int but should have been one of: bool, Feature"), ("dependency('zlib', method : 1)", "dependency keyword argument 'method' was of type int but should have been str"), ("dependency('zlibfail')", self.dnf),) for contents, match in a: From 7a8b79fc294213d4fb954f0a528a4064962a20df Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 3 Apr 2026 14:27:22 +0200 Subject: [PATCH 005/681] interpreterobjects: simplify FeatureOptionHolder There is no need anymore to perform copies, or to check for None held_object. Signed-off-by: Paolo Bonzini --- mesonbuild/interpreter/interpreterobjects.py | 43 +++++++------------- mesonbuild/interpreterbase/helpers.py | 11 +++++ 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index c5e265f5ec84..061e77eb19e9 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -113,59 +113,46 @@ def extract_search_dirs(kwargs: 'kwargs.ExtractSearchDirs') -> T.List[str]: class FeatureOptionHolder(ObjectHolder[Feature]): def __init__(self, option: Feature, interpreter: 'Interpreter'): + if option.is_auto(): + auto_value = T.cast('str', interpreter.environment.coredata.optstore.get_value_for('auto_features')) + option = option.with_value(auto_value) super().__init__(option, interpreter) - if option and option.is_auto(): - option.value = T.cast('str', self.env.coredata.optstore.get_value_for('auto_features')) - - @property - def value(self) -> str: - return 'disabled' if not self.held_object else self.held_object.value - - def as_disabled(self) -> Feature: - disabled = copy.deepcopy(self.held_object) - disabled.value = 'disabled' - return disabled - - def as_enabled(self) -> Feature: - enabled = copy.deepcopy(self.held_object) - enabled.value = 'enabled' - return enabled @noPosargs @noKwargs @InterpreterObject.method('enabled') def enabled_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool: - return self.value == 'enabled' + return self.held_object.is_enabled() @noPosargs @noKwargs @InterpreterObject.method('disabled') def disabled_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool: - return self.value == 'disabled' + return self.held_object.is_disabled() @noPosargs @noKwargs @FeatureNew('feature_option.allowed()', '0.59.0') @InterpreterObject.method('allowed') def allowed_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool: - return self.value != 'disabled' + return not self.held_object.is_disabled() @noPosargs @noKwargs @InterpreterObject.method('auto') def auto_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool: - return self.value == 'auto' + return self.held_object.is_auto() def _disable_if(self, condition: bool, message: T.Optional[str]) -> Feature: if not condition: - return copy.deepcopy(self.held_object) + return self.held_object - if self.value == 'enabled': + if self.held_object.is_enabled(): err_msg = f'Feature {self.held_object.name} cannot be enabled' if message: err_msg += f': {message}' raise InterpreterException(err_msg) - return self.as_disabled() + return self.held_object.as_disabled() @FeatureNew('feature_option.require()', '0.59.0') @typed_pos_args('feature_option.require', bool) @@ -196,28 +183,28 @@ def disable_if_method(self, args: T.Tuple[bool], kwargs: 'kwargs.FeatureOptionRe @InterpreterObject.method('enable_if') def enable_if_method(self, args: T.Tuple[bool], kwargs: 'kwargs.FeatureOptionRequire') -> Feature: if not args[0]: - return copy.deepcopy(self.held_object) + return self.held_object - if self.value == 'disabled': + if self.held_object.is_disabled(): err_msg = f'Feature {self.held_object.name} cannot be disabled' if kwargs['error_message']: err_msg += f': {kwargs["error_message"]}' raise InterpreterException(err_msg) - return self.as_enabled() + return self.held_object.as_enabled() @FeatureNew('feature_option.disable_auto_if()', '0.59.0') @noKwargs @typed_pos_args('feature_option.disable_auto_if', bool) @InterpreterObject.method('disable_auto_if') def disable_auto_if_method(self, args: T.Tuple[bool], kwargs: TYPE_kwargs) -> Feature: - return copy.deepcopy(self.held_object) if self.value != 'auto' or not args[0] else self.as_disabled() + return self.held_object.as_disabled() if self.held_object.is_auto() and args[0] else self.held_object @FeatureNew('feature_option.enable_auto_if()', '1.1.0') @noKwargs @typed_pos_args('feature_option.enable_auto_if', bool) @InterpreterObject.method('enable_auto_if') def enable_auto_if_method(self, args: T.Tuple[bool], kwargs: TYPE_kwargs) -> Feature: - return self.as_enabled() if self.value == 'auto' and args[0] else copy.deepcopy(self.held_object) + return self.held_object.as_enabled() if self.held_object.is_auto() and args[0] else self.held_object class RunProcess(MesonInterpreterObject): diff --git a/mesonbuild/interpreterbase/helpers.py b/mesonbuild/interpreterbase/helpers.py index cd9a8af31d8b..f792c66dbfc4 100644 --- a/mesonbuild/interpreterbase/helpers.py +++ b/mesonbuild/interpreterbase/helpers.py @@ -82,5 +82,16 @@ def is_disabled(self) -> bool: def is_auto(self) -> bool: return self.value == 'auto' + def with_value(self, value: FeatureValue) -> Feature: + if value == self.value: + return self + return Feature(self.name, value) + + def as_enabled(self) -> Feature: + return self.with_value('enabled') + + def as_disabled(self) -> Feature: + return self.with_value('disabled') + def __str__(self) -> str: return str(self.value) From c80d8532d5c5e45ed431560c192a6baae2d48cc6 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 3 Apr 2026 18:56:42 +0200 Subject: [PATCH 006/681] remove unused methods from UserFeatureOption Signed-off-by: Paolo Bonzini --- mesonbuild/mconf.py | 3 --- mesonbuild/options.py | 9 --------- 2 files changed, 12 deletions(-) diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py index 4de7bc2afdcf..b8da4e8b7857 100644 --- a/mesonbuild/mconf.py +++ b/mesonbuild/mconf.py @@ -241,14 +241,11 @@ def print_options(self, title: str, opts: T.Union[options.MutableKeyedOptionDict return if title: self.add_title(title) - #auto = T.cast('options.UserFeatureOption', self.coredata.optstore.get_value_for('auto_features')) for k, o in sorted(opts.items()): printable_value = o.printable_value() #root = k.as_root() #if o.yielding and k.subproject and root in self.coredata.options: # printable_value = '' - #if isinstance(o, options.UserFeatureOption) and o.is_auto(): - # printable_value = auto.printable_value() self.add_option(k, o.description, printable_value, o.printable_choices()) def print_conf(self, pager: bool) -> None: diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 8a831ad520f2..5e63b4ac275f 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -547,15 +547,6 @@ class UserFeatureOption(UserComboOption): # Ensure we get a copy with the lambda default_factory=lambda: ['enabled', 'disabled', 'auto'], init=False) - def is_enabled(self) -> bool: - return self.value == 'enabled' - - def is_disabled(self) -> bool: - return self.value == 'disabled' - - def is_auto(self) -> bool: - return self.value == 'auto' - _U = T.TypeVar('_U', bound=UserOption) From fa3720f6db89ae399bb2e9ec57414450a581272d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 3 Apr 2026 19:50:35 +0200 Subject: [PATCH 007/681] interpreter: make Feature in turn wrap an Enum Until now, Feature was just a str wrapper to match the older code that used UserFeatureOption. Introduce a FeatureValue enum for improved checking and faster is_*() methods. Suggested-by: Dylan Baker Signed-off-by: Paolo Bonzini --- mesonbuild/interpreter/interpreter.py | 4 ++-- mesonbuild/interpreter/interpreterobjects.py | 4 ++-- mesonbuild/interpreterbase/__init__.py | 2 ++ mesonbuild/interpreterbase/helpers.py | 24 ++++++++++++++------ 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index d3a819e0b95b..de92cf050059 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -30,7 +30,7 @@ from ..interpreterbase import Disabler, disablerIfNotFound from ..interpreterbase import FeatureNew, FeatureDeprecated, FeatureBroken, FeatureNewKwargs from ..interpreterbase import ObjectHolder, ContextManagerObject -from ..interpreterbase import stringifyUserArguments, Feature +from ..interpreterbase import stringifyUserArguments, Feature, FeatureValue from ..modules import ExtensionModule, ModuleObject, MutableModuleObject, NewExtensionModule, NotFoundExtensionModule from ..optinterpreter import optname_regex @@ -1098,7 +1098,7 @@ def func_get_option(self, node: mparser.BaseNode, args: T.Tuple[str], raise MesonException(f'Option {optname} does not exist.') if isinstance(option_object, options.UserFeatureOption): - return Feature(optname, value) + return Feature(optname, FeatureValue(value)) elif optname == 'b_sanitize': assert isinstance(option_object, options.UserStringArrayOption) # To ensure backwards compatibility this always returns a string. diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index 061e77eb19e9..c3bac94a40a0 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -21,7 +21,7 @@ FeatureNew, FeatureDeprecated, typed_pos_args, typed_kwargs, typed_operator, noArgsFlattening, noPosargs, noKwargs, unholder_return, - flatten, resolve_second_level_holders, Feature, + flatten, resolve_second_level_holders, Feature, FeatureValue, InterpreterException, InvalidArguments, InvalidCode) from ..interpreter.type_checking import NoneType, ENV_KW, ENV_SEPARATOR_KW, PKGCONFIG_DEFINE_KW from ..dependencies import Dependency, ExternalLibrary, InternalDependency @@ -115,7 +115,7 @@ class FeatureOptionHolder(ObjectHolder[Feature]): def __init__(self, option: Feature, interpreter: 'Interpreter'): if option.is_auto(): auto_value = T.cast('str', interpreter.environment.coredata.optstore.get_value_for('auto_features')) - option = option.with_value(auto_value) + option = option.with_value(FeatureValue(auto_value)) super().__init__(option, interpreter) @noPosargs diff --git a/mesonbuild/interpreterbase/__init__.py b/mesonbuild/interpreterbase/__init__.py index fca54bb2efc7..88999d8b74df 100644 --- a/mesonbuild/interpreterbase/__init__.py +++ b/mesonbuild/interpreterbase/__init__.py @@ -26,6 +26,7 @@ 'resolve_second_level_holders', 'stringifyUserArguments', 'Feature', + 'FeatureValue', 'noPosargs', 'noKwargs', @@ -127,6 +128,7 @@ resolve_second_level_holders, stringifyUserArguments, Feature, + FeatureValue, ) from .interpreterbase import InterpreterBase from .operator import MesonOperator diff --git a/mesonbuild/interpreterbase/helpers.py b/mesonbuild/interpreterbase/helpers.py index f792c66dbfc4..6ac2e1ce36b3 100644 --- a/mesonbuild/interpreterbase/helpers.py +++ b/mesonbuild/interpreterbase/helpers.py @@ -3,6 +3,7 @@ from __future__ import annotations from dataclasses import dataclass +from enum import Enum from .. import mesonlib, mparser from .exceptions import InterpreterException, InvalidArguments @@ -68,30 +69,39 @@ def stringifyUserArguments(args: TYPE_var, subproject: SubProject, quote: bool = raise InvalidArguments('Value other than strings, integers, bools, options, dictionaries and lists thereof.') +class FeatureValue(Enum): + ENABLED = 'enabled' + DISABLED = 'disabled' + AUTO = 'auto' + + def __str__(self) -> str: + return self.value + + @dataclass class Feature(HoldableObject): name: str - value: str + value: FeatureValue def is_enabled(self) -> bool: - return self.value == 'enabled' + return self.value is FeatureValue.ENABLED def is_disabled(self) -> bool: - return self.value == 'disabled' + return self.value is FeatureValue.DISABLED def is_auto(self) -> bool: - return self.value == 'auto' + return self.value is FeatureValue.AUTO def with_value(self, value: FeatureValue) -> Feature: - if value == self.value: + if value is self.value: return self return Feature(self.name, value) def as_enabled(self) -> Feature: - return self.with_value('enabled') + return self.with_value(FeatureValue.ENABLED) def as_disabled(self) -> Feature: - return self.with_value('disabled') + return self.with_value(FeatureValue.DISABLED) def __str__(self) -> str: return str(self.value) From 745351bf416f4223488ded0265c7ff44baa634ee Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 4 Oct 2025 15:18:33 +0200 Subject: [PATCH 008/681] mintro: make get_meson_introspection_types() constant Pass the arguments directly to the list_* functions instead of going through a lambda, which is just what the no_bd case already does. Signed-off-by: Paolo Bonzini --- mesonbuild/mintro.py | 128 ++++++++++++++++++++----------------------- 1 file changed, 60 insertions(+), 68 deletions(-) diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index e3fcf217ecf6..658535662fb0 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -11,7 +11,6 @@ project files and don't need this info.""" from contextlib import redirect_stdout -import collections import dataclasses import json import os @@ -48,64 +47,20 @@ def get_meson_introspection_required_version() -> T.List[str]: class IntroCommand: def __init__(self, desc: str, - func: T.Optional[T.Callable[[], T.Union[dict, list]]] = None, + func: T.Optional[T.Callable[[cdata.CoreData, build.Build, backends.Backend], T.Union[dict, list]]] = None, no_bd: T.Optional[T.Callable[[IntrospectionInterpreter], T.Union[dict, list]]] = None) -> None: self.desc = desc + '.' self.func = func self.no_bd = no_bd -def get_meson_introspection_types(coredata: T.Optional[cdata.CoreData] = None, - builddata: T.Optional[build.Build] = None, - backend: T.Optional[backends.Backend] = None) -> T.Mapping[str, IntroCommand]: - if backend and builddata: - benchmarkdata = backend.create_test_serialisation(builddata.get_benchmarks()) - testdata = backend.create_test_serialisation(builddata.get_tests()) - installdata = backend.create_install_data() - else: - benchmarkdata = testdata = installdata = None - - # Enforce key order for argparse - return collections.OrderedDict([ - ('ast', IntroCommand('Dump the AST of the meson file', no_bd=dump_ast)), - ('benchmarks', IntroCommand('List all benchmarks', func=lambda: list_benchmarks(benchmarkdata))), - ('buildoptions', IntroCommand('List all build options', func=lambda: list_buildoptions(coredata), no_bd=list_buildoptions_from_source)), - ('buildsystem_files', IntroCommand('List files that make up the build system', func=lambda: list_buildsystem_files(builddata))), - ('compilers', IntroCommand('List used compilers', func=lambda: list_compilers(coredata))), - ('dependencies', IntroCommand('List external dependencies', func=lambda: list_deps(coredata, backend), no_bd=list_deps_from_source)), - ('scan_dependencies', IntroCommand('Scan for dependencies used in the meson.build file', no_bd=list_deps_from_source)), - ('installed', IntroCommand('List all installed files and directories', func=lambda: list_installed(installdata))), - ('install_plan', IntroCommand('List all installed files and directories with their details', func=lambda: list_install_plan(installdata))), - ('machines', IntroCommand('Information about host, build, and target machines', func=lambda: list_machines(builddata))), - ('projectinfo', IntroCommand('Information about projects', func=lambda: list_projinfo(builddata), no_bd=list_projinfo_from_source)), - ('targets', IntroCommand('List top level targets', func=lambda: list_targets(builddata, installdata, backend), no_bd=list_targets_from_source)), - ('tests', IntroCommand('List all unit tests', func=lambda: list_tests(testdata))), - ]) - -# Note: when adding arguments, please also add them to the completion -# scripts in $MESONSRC/data/shell-completions/ -def add_arguments(parser: argparse.ArgumentParser) -> None: - intro_types = get_meson_introspection_types() - for key, val in intro_types.items(): - flag = '--' + key.replace('_', '-') - parser.add_argument(flag, action='store_true', dest=key, default=False, help=val.desc) - - parser.add_argument('--backend', choices=sorted(options.backendlist), dest='backend', default='ninja', - help='The backend to use for the --buildoptions introspection.') - parser.add_argument('-a', '--all', action='store_true', dest='all', default=False, - help='Print all available information.') - parser.add_argument('-i', '--indent', action='store_true', dest='indent', default=False, - help='Enable pretty printed JSON.') - parser.add_argument('-f', '--force-object-output', action='store_true', dest='force_dict', default=False, - help='Always use the new JSON format for multiple entries (even for 0 and 1 introspection commands)') - parser.add_argument('builddir', nargs='?', default='.', help='The build directory') - def dump_ast(intr: IntrospectionInterpreter) -> T.Dict[str, T.Any]: printer = AstJSONPrinter() intr.ast.accept(printer) return printer.result -def list_installed(installdata: backends.InstallData) -> T.Dict[str, str]: +def list_installed(coredata: cdata.CoreData, builddata: build.Build, backend: backends.Backend) -> T.Dict[str, str]: res = {} + installdata = backend.create_install_data() if installdata is not None: for t in installdata.targets: res[os.path.join(installdata.build_dir, t.fname)] = \ @@ -123,7 +78,8 @@ def list_installed(installdata: backends.InstallData) -> T.Dict[str, str]: res[basename] = os.path.join(installdata.prefix, s.install_path, basename) return res -def list_install_plan(installdata: backends.InstallData) -> T.Dict[str, T.Dict[str, T.Dict[str, T.Union[str, T.List[str], None]]]]: +def list_install_plan(coredata: cdata.CoreData, builddata: build.Build, backend: backends.Backend) -> T.Dict[str, T.Dict[str, T.Dict[str, T.Union[str, T.List[str], None]]]]: + installdata = backend.create_install_data() plan: T.Dict[str, T.Dict[str, T.Dict[str, T.Union[str, T.List[str], None]]]] = { 'targets': { os.path.join(installdata.build_dir, target.fname): { @@ -204,13 +160,14 @@ def list_targets_from_source(intr: IntrospectionInterpreter) -> T.List[T.Dict[st return tlist -def list_targets(builddata: build.Build, installdata: backends.InstallData, backend: backends.Backend) -> T.List[T.Any]: +def list_targets(coredata: cdata.CoreData, builddata: build.Build, backend: backends.Backend) -> T.List[T.Any]: tlist: T.List[T.Any] = [] build_dir = builddata.environment.get_build_dir() src_dir = builddata.environment.get_source_dir() # Fast lookup table for installation files install_lookuptable = {} + installdata = backend.create_install_data() for i in installdata.targets: basename = os.path.basename(i.fname) install_lookuptable[basename] = [str(PurePath(installdata.prefix, i.outdir, basename))] @@ -259,11 +216,14 @@ def list_targets(builddata: build.Build, installdata: backends.InstallData, back tlist.append(t) return tlist +def list_buildoptions(coredata: cdata.CoreData, builddata: build.Build, backend: backends.Backend) -> T.List[T.Dict[str, T.Union[str, bool, int, T.List[str]]]]: + return _list_buildoptions(coredata) + def list_buildoptions_from_source(intr: IntrospectionInterpreter) -> T.List[T.Dict[str, T.Union[str, bool, int, T.List[str]]]]: subprojects = [i['name'] for i in intr.project_data['subprojects']] - return list_buildoptions(intr.coredata, subprojects) + return _list_buildoptions(intr.coredata, subprojects) -def list_buildoptions(coredata: cdata.CoreData, subprojects: T.Optional[T.List[str]] = None) -> T.List[T.Dict[str, T.Union[str, bool, int, T.List[str]]]]: +def _list_buildoptions(coredata: cdata.CoreData, subprojects: T.Optional[T.List[str]] = None) -> T.List[T.Dict[str, T.Union[str, bool, int, T.List[str]]]]: optlist: T.List[T.Dict[str, T.Union[str, bool, int, T.List[str]]]] = [] subprojects = subprojects or [] @@ -338,12 +298,12 @@ def find_buildsystem_files_list(src_dir: str) -> T.List[str]: for f in build_files.intersection(files)) return filelist -def list_buildsystem_files(builddata: build.Build) -> T.List[str]: +def list_buildsystem_files(coredata: cdata.CoreData, builddata: build.Build, backend: backends.Backend) -> T.List[str]: src_dir = builddata.environment.get_source_dir() filelist = [PurePath(src_dir, x).as_posix() for x in builddata.def_files] return filelist -def list_compilers(coredata: cdata.CoreData) -> T.Dict[str, T.Dict[str, T.Dict[str, str]]]: +def list_compilers(coredata: cdata.CoreData, builddata: build.Build, backend: backends.Backend) -> T.Dict[str, T.Dict[str, T.Dict[str, str]]]: compilers: T.Dict[str, T.Dict[str, T.Dict[str, str]]] = {} for machine in ('host', 'build'): compilers[machine] = {} @@ -372,7 +332,7 @@ def list_deps_from_source(intr: IntrospectionInterpreter) -> T.List[T.Dict[str, }] return result -def list_deps(coredata: cdata.CoreData, backend: backends.Backend) -> T.List[T.Dict[str, T.Union[str, T.List[str]]]]: +def list_deps(coredata: cdata.CoreData, builddata: build.Build, backend: backends.Backend) -> T.List[T.Dict[str, T.Union[str, T.List[str]]]]: result: T.Dict[str, T.Dict[str, T.Union[str, T.List[str]]]] = {} def _src_to_str(src_file: T.Union[mesonlib.FileOrString, build.GeneratedTypes, build.StructuredSources]) -> T.List[str]: @@ -432,13 +392,15 @@ def get_test_list(testdata: T.List[backends.TestSerialisation]) -> T.List[T.Dict result.append(to) return result -def list_tests(testdata: T.List[backends.TestSerialisation]) -> T.List[T.Dict[str, T.Union[str, int, T.List[str], T.Dict[str, str]]]]: +def list_tests(coredata: cdata.CoreData, builddata: build.Build, backend: backends.Backend) -> T.List[T.Dict[str, T.Union[str, int, T.List[str], T.Dict[str, str]]]]: + testdata = backend.create_test_serialisation(builddata.get_tests()) return get_test_list(testdata) -def list_benchmarks(benchdata: T.List[backends.TestSerialisation]) -> T.List[T.Dict[str, T.Union[str, int, T.List[str], T.Dict[str, str]]]]: +def list_benchmarks(coredata: cdata.CoreData, builddata: build.Build, backend: backends.Backend) -> T.List[T.Dict[str, T.Union[str, int, T.List[str], T.Dict[str, str]]]]: + benchdata = backend.create_test_serialisation(builddata.get_benchmarks()) return get_test_list(benchdata) -def list_machines(builddata: build.Build) -> T.Dict[str, T.Dict[str, T.Union[str, bool]]]: +def list_machines(coredata: cdata.CoreData, builddata: build.Build, backend: backends.Backend) -> T.Dict[str, T.Dict[str, T.Union[str, bool]]]: machines: T.Dict[str, T.Dict[str, T.Union[str, bool]]] = {} for m in ('host', 'build', 'target'): machine = getattr(builddata.environment.machines, m) @@ -448,7 +410,7 @@ def list_machines(builddata: build.Build) -> T.Dict[str, T.Dict[str, T.Union[str machines[m]['object_suffix'] = machine.get_object_suffix() return machines -def list_projinfo(builddata: build.Build) -> T.Dict[str, T.Union[str, T.List[str], T.List[T.Dict[str, str]]]]: +def list_projinfo(coredata: cdata.CoreData, builddata: build.Build, backend: backends.Backend) -> T.Dict[str, T.Union[str, T.List[str], T.List[T.Dict[str, str]]]]: result: T.Dict[str, T.Union[str, T.List[str], T.List[T.Dict[str, str]]]] = { 'version': builddata.project_version, 'descriptive_name': builddata.project_name, @@ -511,6 +473,39 @@ def load_info_file(infodir: str, kind: T.Optional[str] = None) -> T.Any: with open(get_info_file(infodir, kind), encoding='utf-8') as fp: return json.load(fp) +INTRO_TYPES: T.Mapping[str, IntroCommand] = { + 'ast': IntroCommand('Dump the AST of the meson file', no_bd=dump_ast), + 'benchmarks': IntroCommand('List all benchmarks', func=list_benchmarks), + 'buildoptions': IntroCommand('List all build options', func=list_buildoptions, no_bd=list_buildoptions_from_source), + 'buildsystem_files': IntroCommand('List files that make up the build system', func=list_buildsystem_files), + 'compilers': IntroCommand('List used compilers', func=list_compilers), + 'dependencies': IntroCommand('List external dependencies', func=list_deps, no_bd=list_deps_from_source), + 'scan_dependencies': IntroCommand('Scan for dependencies used in the meson.build file', no_bd=list_deps_from_source), + 'installed': IntroCommand('List all installed files and directories', func=list_installed), + 'install_plan': IntroCommand('List all installed files and directories with their details', func=list_install_plan), + 'machines': IntroCommand('Information about host, build, and target machines', func=list_machines), + 'projectinfo': IntroCommand('Information about projects', func=list_projinfo, no_bd=list_projinfo_from_source), + 'targets': IntroCommand('List top level targets', func=list_targets, no_bd=list_targets_from_source), + 'tests': IntroCommand('List all unit tests', func=list_tests), +} + +# Note: when adding arguments, please also add them to the completion +# scripts in $MESONSRC/data/shell-completions/ +def add_arguments(parser: argparse.ArgumentParser) -> None: + for key, val in INTRO_TYPES.items(): + flag = '--' + key.replace('_', '-') + parser.add_argument(flag, action='store_true', dest=key, default=False, help=val.desc) + + parser.add_argument('--backend', choices=sorted(options.backendlist), dest='backend', default='ninja', + help='The backend to use for the --buildoptions introspection.') + parser.add_argument('-a', '--all', action='store_true', dest='all', default=False, + help='Print all available information.') + parser.add_argument('-i', '--indent', action='store_true', dest='indent', default=False, + help='Enable pretty printed JSON.') + parser.add_argument('-f', '--force-object-output', action='store_true', dest='force_dict', default=False, + help='Always use the new JSON format for multiple entries (even for 0 and 1 introspection commands)') + parser.add_argument('builddir', nargs='?', default='.', help='The build directory') + def run(options: argparse.Namespace) -> int: datadir = 'meson-private' infodir = get_infodir(options.builddir) @@ -518,7 +513,6 @@ def run(options: argparse.Namespace) -> int: datadir = os.path.join(options.builddir, datadir) indent = 4 if options.indent else None results: T.List[T.Tuple[str, T.Union[dict, T.List[T.Any]]]] = [] - intro_types = get_meson_introspection_types() # TODO: This if clause is undocumented. if os.path.basename(options.builddir) == environment.build_filename: @@ -530,7 +524,7 @@ def run(options: argparse.Namespace) -> int: intr = IntrospectionInterpreter(sourcedir, '', backend.name, visitors = [AstIDGenerator(), AstIndentationGenerator(), AstConditionLevel()]) intr.analyze() - for key, val in intro_types.items(): + for key, val in INTRO_TYPES.items(): if (not options.all and not getattr(options, key, False)) or not val.no_bd: continue results += [(key, val.no_bd(intr))] @@ -558,7 +552,7 @@ def run(options: argparse.Namespace) -> int: return 1 # Extract introspection information from JSON - for i, v in intro_types.items(): + for i, v in INTRO_TYPES.items(): if not v.func: continue if not options.all and not getattr(options, i, False): @@ -585,19 +579,18 @@ def write_intro_info(intro_info: T.Sequence[T.Tuple[str, T.Union[dict, T.List[T. def generate_introspection_file(builddata: build.Build, backend: backends.Backend) -> None: coredata = builddata.environment.get_coredata() - intro_types = get_meson_introspection_types(coredata=coredata, builddata=builddata, backend=backend) intro_info: T.List[T.Tuple[str, T.Union[dict, T.List[T.Any]]]] = [] - for key, val in intro_types.items(): + for key, val in INTRO_TYPES.items(): if not val.func: continue - intro_info += [(key, val.func())] + intro_info += [(key, val.func(coredata, builddata, backend))] write_intro_info(intro_info, builddata.environment.info_dir) def update_build_options(coredata: cdata.CoreData, info_dir: str) -> None: intro_info = [ - ('buildoptions', list_buildoptions(coredata)) + ('buildoptions', _list_buildoptions(coredata)) ] write_intro_info(intro_info, info_dir) @@ -614,10 +607,9 @@ def split_version_string(version: str) -> T.Dict[str, T.Union[str, int]]: def write_meson_info_file(builddata: build.Build, errors: list, build_files_updated: bool = False) -> None: info_dir = builddata.environment.info_dir info_file = get_meson_info_file(info_dir) - intro_types = get_meson_introspection_types() intro_info = {} - for i, v in intro_types.items(): + for i, v in INTRO_TYPES.items(): if not v.func: continue intro_info[i] = { From c26257d5e7c8f114a64ede4cd0d41cb07c847d52 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 7 Feb 2026 10:14:49 +0100 Subject: [PATCH 009/681] installdata is never None This is now clearer to mypy, so remove some useless checks. Signed-off-by: Paolo Bonzini --- mesonbuild/mintro.py | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 658535662fb0..514efb035c55 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -61,21 +61,20 @@ def dump_ast(intr: IntrospectionInterpreter) -> T.Dict[str, T.Any]: def list_installed(coredata: cdata.CoreData, builddata: build.Build, backend: backends.Backend) -> T.Dict[str, str]: res = {} installdata = backend.create_install_data() - if installdata is not None: - for t in installdata.targets: - res[os.path.join(installdata.build_dir, t.fname)] = \ - os.path.join(installdata.prefix, t.outdir, os.path.basename(t.fname)) - for i in installdata.data: - res[i.path] = os.path.join(installdata.prefix, i.install_path) - for i in installdata.headers: - res[i.path] = os.path.join(installdata.prefix, i.install_path, os.path.basename(i.path)) - for i in installdata.man: - res[i.path] = os.path.join(installdata.prefix, i.install_path) - for i in installdata.install_subdirs: - res[i.path] = os.path.join(installdata.prefix, i.install_path) - for s in installdata.symlinks: - basename = os.path.basename(s.name) - res[basename] = os.path.join(installdata.prefix, s.install_path, basename) + for t in installdata.targets: + res[os.path.join(installdata.build_dir, t.fname)] = \ + os.path.join(installdata.prefix, t.outdir, os.path.basename(t.fname)) + for i in installdata.data: + res[i.path] = os.path.join(installdata.prefix, i.install_path) + for i in installdata.headers: + res[i.path] = os.path.join(installdata.prefix, i.install_path, os.path.basename(i.path)) + for i in installdata.man: + res[i.path] = os.path.join(installdata.prefix, i.install_path) + for i in installdata.install_subdirs: + res[i.path] = os.path.join(installdata.prefix, i.install_path) + for s in installdata.symlinks: + basename = os.path.basename(s.name) + res[basename] = os.path.join(installdata.prefix, s.install_path, basename) return res def list_install_plan(coredata: cdata.CoreData, builddata: build.Build, backend: backends.Backend) -> T.Dict[str, T.Dict[str, T.Dict[str, T.Union[str, T.List[str], None]]]]: @@ -207,7 +206,7 @@ def list_targets(coredata: cdata.CoreData, builddata: build.Build, backend: back if win_subsystem is not None: t['win_subsystem'] = win_subsystem - if installdata and target.should_install(): + if target.should_install(): t['installed'] = True ifn = [install_lookuptable.get(x, [None]) for x in target.get_outputs()] t['install_filename'] = [x for sublist in ifn for x in sublist] # flatten the list From 737291ad8c89f08b960cf7a92ee107242b385746 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 17 Nov 2025 18:00:09 +0100 Subject: [PATCH 010/681] mintro: change IntroCommand to a dataclass Signed-off-by: Paolo Bonzini --- mesonbuild/mintro.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 514efb035c55..42bd13dea9f4 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -44,15 +44,6 @@ def get_meson_introspection_version() -> str: def get_meson_introspection_required_version() -> T.List[str]: return ['>=1.0', '<2.0'] -class IntroCommand: - def __init__(self, - desc: str, - func: T.Optional[T.Callable[[cdata.CoreData, build.Build, backends.Backend], T.Union[dict, list]]] = None, - no_bd: T.Optional[T.Callable[[IntrospectionInterpreter], T.Union[dict, list]]] = None) -> None: - self.desc = desc + '.' - self.func = func - self.no_bd = no_bd - def dump_ast(intr: IntrospectionInterpreter) -> T.Dict[str, T.Any]: printer = AstJSONPrinter() intr.ast.accept(printer) @@ -472,6 +463,15 @@ def load_info_file(infodir: str, kind: T.Optional[str] = None) -> T.Any: with open(get_info_file(infodir, kind), encoding='utf-8') as fp: return json.load(fp) +@dataclasses.dataclass +class IntroCommand: + desc: str + func: T.Optional[T.Callable[[cdata.CoreData, build.Build, backends.Backend], T.Union[dict, list]]] = None + no_bd: T.Optional[T.Callable[[IntrospectionInterpreter], T.Union[dict, list]]] = None + + def __post_init__(self) -> None: + self.desc += '.' + INTRO_TYPES: T.Mapping[str, IntroCommand] = { 'ast': IntroCommand('Dump the AST of the meson file', no_bd=dump_ast), 'benchmarks': IntroCommand('List all benchmarks', func=list_benchmarks), From 6fe3765457cf851df74aa170b5cfb4ee6fa2144c Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 6 Apr 2026 08:50:31 -0700 Subject: [PATCH 011/681] modules/gnome: Fix annotations of GenerateGir There are some missing values here, and some places where we are casting back to `dict[str, Any]` because of this. Let's fix the annotations --- mesonbuild/modules/gnome.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 6dec8c43ba87..b4ac147f534b 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2015-2016 The Meson development team -# Copyright © 2023-2025 Intel Corporation +# Copyright © 2023-2026 Intel Corporation '''This module provides helper functions for Gnome/GLib related functionality such as gobject-introspection, gresources and gtk-doc''' @@ -44,7 +44,7 @@ from ..compilers import Compiler from ..interpreter import Interpreter from ..interpreterbase import TYPE_var, TYPE_kwargs - from ..mesonlib import FileOrString + from ..mesonlib import EnvironmentVariables, FileOrString from ..programs import Program, CommandList, CommandListEntry class PostInstall(TypedDict): @@ -84,6 +84,7 @@ class GenerateGir(TypedDict): build_by_default: bool dependencies: T.List[Dependency] doc_format: T.Optional[str] + env: EnvironmentVariables export_packages: T.List[str] extra_args: T.List[str] fatal_warnings: bool @@ -92,8 +93,10 @@ class GenerateGir(TypedDict): include_directories: T.List[T.Union[build.IncludeDirs, str]] includes: T.List[T.Union[str, GirTarget]] install: bool - install_dir_gir: T.Optional[str] - install_dir_typelib: T.Optional[str] + install_gir: T.Optional[bool] + install_dir_gir: T.Union[str, None, Literal[False]] + install_typelib: T.Optional[bool] + install_dir_typelib: T.Union[str, None, Literal[False]] link_with: T.List[T.Union[build.SharedLibrary, build.StaticLibrary]] namespace: str nsversion: str @@ -972,7 +975,7 @@ def _make_gir_target( generated_files: T.Sequence[T.Union[str, mesonlib.File, build.GeneratedTypes]], depends: T.Sequence[T.Union['FileOrString', build.BuildTarget, 'build.GeneratedTypes', build.StructuredSources]], env_flags: T.Sequence[str], - kwargs: T.Dict[str, T.Any]) -> GirTarget: + kwargs: GenerateGir) -> GirTarget: install = kwargs['install_gir'] if install is None: install = kwargs['install'] @@ -1023,7 +1026,7 @@ def _make_gir_target( def _make_typelib_target(state: 'ModuleState', typelib_output: str, typelib_cmd: T.Sequence[T.Union[str, CustomTarget, Program]], generated_files: T.Sequence[T.Union[str, mesonlib.File, build.GeneratedTypes]], - kwargs: T.Dict[str, T.Any]) -> TypelibTarget: + kwargs: GenerateGir) -> TypelibTarget: install = kwargs['install_typelib'] if install is None: install = kwargs['install'] @@ -1244,9 +1247,8 @@ def generate_gir(self, state: 'ModuleState', args: T.Tuple[T.List[T.Union[Execut generated_files = [f for f in libsources if isinstance(f, (GeneratedList, CustomTarget, CustomTargetIndex))] scan_target = self._make_gir_target( - state, girfile, scan_command, generated_files, depends, scan_env_ldflags, - # We have to cast here because mypy can't figure this out - T.cast('T.Dict[str, T.Any]', kwargs)) + state, girfile, scan_command, generated_files, depends, + scan_env_ldflags, kwargs) typelib_output = f'{ns}-{nsversion}.typelib' typelib_cmd: T.List[T.Union[str, Program, CustomTarget]] @@ -1256,7 +1258,8 @@ def generate_gir(self, state: 'ModuleState', args: T.Tuple[T.List[T.Union[Execut for incdir in typelib_includes: typelib_cmd += ["--includedir=" + incdir] - typelib_target = self._make_typelib_target(state, typelib_output, typelib_cmd, generated_files, T.cast('T.Dict[str, T.Any]', kwargs)) + typelib_target = self._make_typelib_target( + state, typelib_output, typelib_cmd, generated_files, kwargs) self._devenv_prepend('GI_TYPELIB_PATH', os.path.join(state.environment.get_build_dir(), state.subdir)) From ba3aba7ad86113a7f7719298122c8ef882cbadec Mon Sep 17 00:00:00 2001 From: Joel Rosdahl Date: Tue, 24 Mar 2026 13:02:03 +0100 Subject: [PATCH 012/681] compilers: Remove _StdCPPLibMixin.language_stdlib_provider env param It's unused. Also remove doc string trace of an env parameter to _StdCPPLibMixin.language_stdlib_only_link_flags. --- mesonbuild/compilers/cpp.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index 5d319f9939eb..5e905fd71098 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -184,7 +184,7 @@ class _StdCPPLibMixin(CompilerMixinBase): """Detect whether to use libc++ or libstdc++.""" - def language_stdlib_provider(self, env: Environment) -> str: + def language_stdlib_provider(self) -> str: # https://stackoverflow.com/a/31658120 header = 'version' if self.has_header('version', '')[0] else 'ciso646' is_libcxx = self.has_header_symbol(header, '_LIBCPP_VERSION', '')[0] @@ -197,7 +197,6 @@ def language_stdlib_only_link_flags(self) -> T.List[str]: As an optimization, this method will cache the value, to avoid building the same values over and over - :param env: An Environment object :raises MesonException: If a stdlib cannot be determined """ @@ -207,7 +206,7 @@ def language_stdlib_only_link_flags(self) -> T.List[str]: # fortran. search_dirs = [f'-L{d}' for d in self.get_compiler_dirs('libraries')] - lib = self.language_stdlib_provider(self.environment) + lib = self.language_stdlib_provider() if self.find_library(lib, []) is not None: return search_dirs + [f'-l{lib}'] @@ -317,7 +316,7 @@ def get_assert_args(self, disable: bool) -> T.List[str]: if self.defines.get(macro) is not None: return [] - if self.language_stdlib_provider(self.environment) == 'stdc++': + if self.language_stdlib_provider() == 'stdc++': return ['-D_GLIBCXX_ASSERTIONS=1'] return ['-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST'] @@ -532,7 +531,7 @@ def get_assert_args(self, disable: bool) -> T.List[str]: # For GCC, we can assume that the libstdc++ version is the same as # the compiler itself. Anything else isn't supported. - if self.language_stdlib_provider(self.environment) == 'stdc++': + if self.language_stdlib_provider() == 'stdc++': return ['-D_GLIBCXX_ASSERTIONS=1'] else: # One can use -stdlib=libc++ with GCC, it just (as of 2025) requires From 2c72dbe12b8162bb36760c11e983b5d4ec2c0c1e Mon Sep 17 00:00:00 2001 From: Joel Rosdahl Date: Tue, 24 Mar 2026 11:09:54 +0100 Subject: [PATCH 013/681] Speed up generation of Ninja rules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Use `lru_cache` for `quote_arg`, `_StdCPPLibMixin.language_stdlib_provider` and `NinjaBackend._generate_single_compile_base_args`. - Cache calls to `platform.system().lower()` and `platform.release().lower()`. With this, I see a ≈10% speedup of Ninja rule generation for a somewhat large project. --- mesonbuild/backend/ninjabackend.py | 7 +++++++ mesonbuild/compilers/cpp.py | 9 +++++---- mesonbuild/utils/universal.py | 30 +++++++++++++++++------------- 3 files changed, 29 insertions(+), 17 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index b45dbaa9b53a..6279500bed05 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -3062,6 +3062,13 @@ def _generate_single_compile(self, target: build.BuildTarget, compiler: Compiler return commands def _generate_single_compile_base_args(self, target: build.BuildTarget, compiler: 'Compiler') -> 'CompilerArgs': + # Return a mutable CompilerArgs populated from the cached immutable + # CompilerArgs so that callers can safely modify it. + cached_args = self._generate_single_compile_base_args_cached(target, compiler) + return compiler.compiler_args(cached_args) + + @lru_cache(maxsize=None) + def _generate_single_compile_base_args_cached(self, target: build.BuildTarget, compiler: 'Compiler') -> 'CompilerArgs': # Create an empty commands list, and start adding arguments from # various sources in the order in which they must override each other commands = compiler.compiler_args() diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index 5e905fd71098..21614f2c99db 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -9,7 +9,7 @@ from .. import options from .. import mlog -from ..mesonlib import MesonException, version_compare +from ..mesonlib import MesonException, version_compare, lazy_property from .compilers import ( gnu_winlibs, @@ -184,6 +184,7 @@ class _StdCPPLibMixin(CompilerMixinBase): """Detect whether to use libc++ or libstdc++.""" + @lazy_property def language_stdlib_provider(self) -> str: # https://stackoverflow.com/a/31658120 header = 'version' if self.has_header('version', '')[0] else 'ciso646' @@ -206,7 +207,7 @@ def language_stdlib_only_link_flags(self) -> T.List[str]: # fortran. search_dirs = [f'-L{d}' for d in self.get_compiler_dirs('libraries')] - lib = self.language_stdlib_provider() + lib = self.language_stdlib_provider if self.find_library(lib, []) is not None: return search_dirs + [f'-l{lib}'] @@ -316,7 +317,7 @@ def get_assert_args(self, disable: bool) -> T.List[str]: if self.defines.get(macro) is not None: return [] - if self.language_stdlib_provider() == 'stdc++': + if self.language_stdlib_provider == 'stdc++': return ['-D_GLIBCXX_ASSERTIONS=1'] return ['-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST'] @@ -531,7 +532,7 @@ def get_assert_args(self, disable: bool) -> T.List[str]: # For GCC, we can assume that the libstdc++ version is the same as # the compiler itself. Anything else isn't supported. - if self.language_stdlib_provider() == 'stdc++': + if self.language_stdlib_provider == 'stdc++': return ['-D_GLIBCXX_ASSERTIONS=1'] else: # One can use -stdlib=libc++ with GCC, it just (as of 2025) requires diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index 45ea8ccf5d56..965095bba742 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -626,36 +626,38 @@ def default_missing(self) -> PerThreeMachine[_T]: return PerThreeMachine(self.build, host, target) +_PLATFORM_SYSTEM_LOWER = platform.system().lower() +_PLATFORM_RELEASE_LOWER = platform.release().lower() + def is_sunos() -> bool: - return platform.system().lower() == 'sunos' + return _PLATFORM_SYSTEM_LOWER == 'sunos' def is_osx() -> bool: - return platform.system().lower() == 'darwin' + return _PLATFORM_SYSTEM_LOWER == 'darwin' def is_linux() -> bool: - return platform.system().lower() == 'linux' + return _PLATFORM_SYSTEM_LOWER == 'linux' def is_android() -> bool: - return platform.system().lower() == 'android' + return _PLATFORM_SYSTEM_LOWER == 'android' def is_haiku() -> bool: - return platform.system().lower() == 'haiku' + return _PLATFORM_SYSTEM_LOWER == 'haiku' def is_openbsd() -> bool: - return platform.system().lower() == 'openbsd' + return _PLATFORM_SYSTEM_LOWER == 'openbsd' def is_windows() -> bool: - platname = platform.system().lower() - return platname == 'windows' + return _PLATFORM_SYSTEM_LOWER == 'windows' def is_wsl() -> bool: - return is_linux() and 'microsoft' in platform.release().lower() + return is_linux() and 'microsoft' in _PLATFORM_RELEASE_LOWER def is_cygwin() -> bool: return sys.platform == 'cygwin' @@ -666,18 +668,18 @@ def is_debianlike() -> bool: def is_dragonflybsd() -> bool: - return platform.system().lower() == 'dragonfly' + return _PLATFORM_SYSTEM_LOWER == 'dragonfly' def is_netbsd() -> bool: - return platform.system().lower() == 'netbsd' + return _PLATFORM_SYSTEM_LOWER == 'netbsd' def is_freebsd() -> bool: - return platform.system().lower() == 'freebsd' + return _PLATFORM_SYSTEM_LOWER == 'freebsd' def is_irix() -> bool: - return platform.system().startswith('irix') + return _PLATFORM_SYSTEM_LOWER == 'irix' def is_hurd() -> bool: return platform.system().lower() == 'gnu' @@ -1176,6 +1178,7 @@ def has_path_sep(name: str, sep: str = '/\\') -> bool: _whitespace = ' \t\n\r' _find_unsafe_char = re.compile(fr'[{_whitespace}"]').search + @lru_cache(maxsize=4096) def quote_arg(arg: str) -> str: if arg and not _find_unsafe_char(arg): return arg @@ -1233,6 +1236,7 @@ def split_args(cmd: str) -> T.List[str]: return result else: + @lru_cache(maxsize=4096) def quote_arg(arg: str) -> str: return shlex.quote(arg) From 755dbc2dd7477d3b0e7a67eed72de1ac4dacf0ff Mon Sep 17 00:00:00 2001 From: Joel Rosdahl Date: Wed, 25 Mar 2026 11:05:13 +0100 Subject: [PATCH 014/681] build: Precalculate single compile base args --- mesonbuild/backend/ninjabackend.py | 28 ++++------------------------ mesonbuild/build.py | 25 ++++++++++++++++++++++++- mesonbuild/compilers/compilers.py | 4 ++-- 3 files changed, 30 insertions(+), 27 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 6279500bed05..b8994f8c8776 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -3057,30 +3057,10 @@ def generate_inc_dir(self, compiler: 'Compiler', d: str, basedir: str, is_system return (sargs, bargs) def _generate_single_compile(self, target: build.BuildTarget, compiler: Compiler) -> CompilerArgs: - commands = self._generate_single_compile_base_args(target, compiler) + commands = target.get_single_compile_base_args(compiler) commands += self._generate_single_compile_target_args(target, compiler) return commands - def _generate_single_compile_base_args(self, target: build.BuildTarget, compiler: 'Compiler') -> 'CompilerArgs': - # Return a mutable CompilerArgs populated from the cached immutable - # CompilerArgs so that callers can safely modify it. - cached_args = self._generate_single_compile_base_args_cached(target, compiler) - return compiler.compiler_args(cached_args) - - @lru_cache(maxsize=None) - def _generate_single_compile_base_args_cached(self, target: build.BuildTarget, compiler: 'Compiler') -> 'CompilerArgs': - # Create an empty commands list, and start adding arguments from - # various sources in the order in which they must override each other - commands = compiler.compiler_args() - # Start with symbol visibility. - commands += compiler.gnu_symbol_visibility_args(target.gnu_symbol_visibility) - # Add compiler args for compiling this target derived from 'base' build - # options passed on the command-line, in default_options, etc. - # These have the lowest priority. - commands += compilers.get_base_compile_args(target, - compiler, self.environment) - return commands - @lru_cache(maxsize=None) def _generate_single_compile_target_args(self, target: build.BuildTarget, compiler: Compiler) -> ImmutableListProtocol[str]: # Add compiler args and include paths from several sources; defaults, @@ -3166,7 +3146,7 @@ def generate_common_compile_args_per_src_type(self, target: build.BuildTarget) - for src_type_str in target.compilers: compiler = target.compilers[src_type_str] - commands = self._generate_single_compile_base_args(target, compiler) + commands = target.get_single_compile_base_args(compiler) # Include PCH header as first thing as it must be the first one or it will be # ignored by gcc https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100462 @@ -3200,7 +3180,7 @@ def generate_single_compile(self, target: build.BuildTarget, src, raise AssertionError(f'BUG: sources should not contain headers {src!r}') compiler = get_compiler_for_source(target.compilers.values(), src) - commands = self._generate_single_compile_base_args(target, compiler) + commands = target.get_single_compile_base_args(compiler) # Include PCH header as first thing as it must be the first one or it will be # ignored by gcc https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100462 @@ -3716,7 +3696,7 @@ def generate_prelink(self, target, obj_list) -> T.List[str]: obj_list, args = prelinker.get_prelink_args(prelink_name, obj_list) cmd += args if prelinker.get_prelink_append_compile_args(): - compile_args = self._generate_single_compile_base_args(target, prelinker) + compile_args = target.get_single_compile_base_args(prelinker) compile_args += self._generate_single_compile_target_args(target, prelinker) compile_args = compile_args.compiler.compiler_args(compile_args) cmd += compile_args.to_native() diff --git a/mesonbuild/build.py b/mesonbuild/build.py index d688af892fee..321f152b827f 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -31,13 +31,15 @@ from .compilers import ( is_header, is_object, is_source, clink_langs, sort_clink, all_languages, - is_known_suffix, detect_static_linker, LANGUAGES_USING_LDFLAGS + is_known_suffix, detect_static_linker, LANGUAGES_USING_LDFLAGS, + get_base_compile_args ) from .interpreterbase import FeatureNew, FeatureDeprecated, SubProject if T.TYPE_CHECKING: from typing_extensions import Literal, TypeAlias, TypedDict + from .arglist import CompilerArgs from .environment import Environment from ._typing import ImmutableListProtocol from .backend.backends import Backend @@ -865,6 +867,7 @@ def __init__( self.process_objectlist(objects) self.process_kwargs(kwargs) self.missing_languages = self.process_compilers() + self.single_compile_base_args: T.Dict[Compiler, CompilerArgs] = {} # self.link_targets and self.link_whole_targets contains libraries from # dependencies (see add_deps()). They have not been processed yet because @@ -956,6 +959,9 @@ def post_init(self) -> None: self.outputs.append(self.vala_gir) self.install_tag.append('devel') + for compiler in self.compilers.values(): + self.single_compile_base_args[compiler] = self._generate_single_compile_base_args(compiler) + def __repr__(self) -> str: repr_str = "<{0} {1}: {2}>" return repr_str.format(self.__class__.__name__, self.get_id(), self.filename) @@ -1342,6 +1348,23 @@ def get_link_dep_subdirs(self) -> T.AbstractSet[str]: result.update(i.get_link_dep_subdirs()) return result + def get_single_compile_base_args(self, compiler: Compiler) -> CompilerArgs: + # Return a mutable CompilerArgs populated from the cached immutable + # arguments so that callers can safely modify it. + return compiler.compiler_args(self.single_compile_base_args[compiler]) + + def _generate_single_compile_base_args(self, compiler: Compiler) -> ImmutableListProtocol[str]: + # Create an empty commands list, and start adding arguments from + # various sources in the order in which they must override each other + commands = compiler.compiler_args() + # Start with symbol visibility. + commands += compiler.gnu_symbol_visibility_args(self.gnu_symbol_visibility) + # Add compiler args for compiling this target derived from 'base' build + # options passed on the command-line, in default_options, etc. + # These have the lowest priority. + commands += get_base_compile_args(self, compiler, self.environment) + return list(commands) # Avoid call to iterable.flush_pre_post in CompilerArgs.__init__ + def get_default_install_dir(self) -> T.Tuple[str, str]: return self.environment.get_libdir(), '{libdir}' diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 9c6aec5c96eb..2652a7f5e613 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -345,7 +345,7 @@ def get_base_compile_args(target: 'BuildTarget', compiler: 'Compiler', env: 'Env assert isinstance(crt_val, str) try: args += compiler.get_crt_compile_args(crt_val) - except AttributeError: + except EnvironmentException: pass except KeyError: pass @@ -435,7 +435,7 @@ def get_base_link_args(target: 'BuildTarget', crtargs = linker.get_crt_link_args(crt_val) assert isinstance(crtargs, list) args += crtargs - except AttributeError: + except EnvironmentException: pass except KeyError: pass From c238c65b3f42f9232c0dd129dc52463eac6e92bb Mon Sep 17 00:00:00 2001 From: Princeton Ferro Date: Tue, 31 Mar 2026 01:30:16 -0700 Subject: [PATCH 015/681] ninjabackend: pass external {c,cpp}_args to detect_vs_dep_prefix probe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit detect_vs_dep_prefix() runs cl.exe /showIncludes /c incdetect.c with no include paths. When the INCLUDE environment variable is not set — for example when using a bundled MSVC toolchain outside a VS Developer Shell — cl.exe cannot locate stdio.h and Meson raises: Could not determine vs dep dependency prefix string The fix is to also pass the compiler's external args (c_args / cpp_args from a native or cross file) to the probe subprocess. This picks up any user-defined include flags, enabling hermetic builds where all headers are specified explicitly rather than via environment variables. --- mesonbuild/backend/ninjabackend.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index b8994f8c8776..1b5a0162fadc 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -564,8 +564,15 @@ def detect_vs_dep_prefix(self, tempfilename: str) -> T.TextIO: # and locale dependent. Any attempt at converting it to # Python strings leads to failure. We _must_ do this detection # in raw byte mode and write the result in raw bytes. + # + # Pass the compiler's external args (e.g. -I flags from c_args / + # cpp_args in a native file) so that cl.exe can locate system headers + # even when the INCLUDE environment variable is not set — for example, + # when using a bundled MSVC toolchain outside a VS Developer Shell. + extra_args = self.environment.coredata.get_external_args( + MachineChoice.HOST, compiler.language) pc = subprocess.Popen(compiler.get_exelist() + - ['/showIncludes', '/c', filebase], + ['/showIncludes', '/c', filebase] + extra_args, cwd=self.environment.get_scratch_dir(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = pc.communicate() From 797decc3e47823c8ae49db421597a5affd0810ad Mon Sep 17 00:00:00 2001 From: Princeton Ferro Date: Fri, 3 Apr 2026 04:15:59 -0700 Subject: [PATCH 016/681] compilers: pass external args to sanity check; add #include Two improvements so the compiler sanity check exercises the same code paths as cc.links(): 1. Compiler._sanity_check_compile_args (the base class) now passes get_external_args() and get_external_link_args() to the probe, just as build_wrapper_args() does. CLikeCompiler already did this via _get_basic_compiler_args(); the base class fix extends it to non-CLike compilers (Fortran, Swift, etc.). Duplicates in the CLike path are harmless because arglist deduplicates them. 2. CCompiler and CPPCompiler sanity check sources now include . This ensures that a broken or missing include path is caught at compiler detection time with a natural compiler error, rather than surfacing later as a cryptic Meson exception. --- mesonbuild/compilers/c.py | 2 +- mesonbuild/compilers/compilers.py | 4 +++- mesonbuild/compilers/cpp.py | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index 2bff42b1d97a..a478c79616b6 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -75,7 +75,7 @@ def get_no_stdinc_args(self) -> T.List[str]: return ['-nostdinc'] def _sanity_check_source_code(self) -> str: - return 'int main(void) { int class=0; return class; }\n' + return '#include \nint main(void) { int class=0; return class; }\n' def has_header_symbol(self, hname: str, symbol: str, prefix: str, *, extra_args: T.Union[None, T.List[str], T.Callable[['CompileCheckMode'], T.List[str]]] = None, diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 2652a7f5e613..deafa3eea7e0 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -1366,7 +1366,9 @@ def _sanity_check_compile_args(self, sourcename: str, binname: str :return: a tuple of arguments, the first is the executable and compiler arguments, the second is linker arguments """ - return self.exelist_no_ccache + self.get_always_args() + self.get_output_args(binname) + [sourcename], [] + cargs = list(self.environment.coredata.get_external_args(self.for_machine, self.language)) + largs = list(self.environment.coredata.get_external_link_args(self.for_machine, self.language)) + return self.exelist_no_ccache + self.get_always_args() + self.get_output_args(binname) + [sourcename] + cargs, largs @abc.abstractmethod def _sanity_check_source_code(self) -> str: diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index 21614f2c99db..7718f501796d 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -89,7 +89,7 @@ def get_cpp_modules_args(self) -> T.List[str]: return [] def _sanity_check_source_code(self) -> str: - return 'class breakCCompiler;int main(void) { return 0; }\n' + return '#include \nclass breakCCompiler;int main(void) { return 0; }\n' def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]: # -fpermissive allows non-conforming code to compile which is necessary From 38834792d164e0cba3f610e0ea11243048050ca7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 15 Apr 2026 09:14:27 +0200 Subject: [PATCH 017/681] vs2010backend: allow missing b_lto or b_pgo options If the options are missing, do not enable LTO or PGO. As a side effect of cleaning up the code, the WholeProgramOptimization element is always emitted as was the case before commit 6835b6ed6 ("vs2010backend: integrate b_lto/b_pgo with vcxproj backends", 2026-02-06). Fixes: 6835b6ed6 ("vs2010backend: integrate b_lto/b_pgo with vcxproj backends", 2026-02-06) Fixes: #15702 Signed-off-by: Paolo Bonzini --- mesonbuild/backend/vs2010backend.py | 30 ++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 049b86a7074e..6e71c41a5204 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -1234,6 +1234,22 @@ def get_nmake_base_meson_command_and_exe_search_paths() -> T.Tuple[str, str]: return (nmake_base_meson_command, exe_search_paths) + def get_target_whole_program_opt(self, target: build.BuildTarget) -> str: + lto = False + pgo = 'off' + try: + lto = self.get_target_option(target, 'b_lto') + pgo = self.get_target_option(target, 'b_pgo') + except KeyError: + pass + + if pgo == 'off': + return 'true' if lto else 'false' + + if not lto: + raise MesonException("b_pgo requires b_lto on MSVC.") + return 'PGInstrument' if pgo == 'generate' else 'PGOptimize' + def add_gen_lite_makefile_vcxproj_elements(self, root: ET.Element, platform: str, @@ -1322,19 +1338,7 @@ def add_non_makefile_vcxproj_elements( # FIXME: Should the following just be set in create_basic_project(), even if # irrelevant for current target? - lto = self.get_target_option(target, 'b_lto') - pgo = self.get_target_option(target, 'b_pgo') - - if lto: - if pgo == 'off': - ET.SubElement(type_config, 'WholeProgramOptimization').text = 'true' - elif pgo == 'generate': - ET.SubElement(type_config, 'WholeProgramOptimization').text = 'PGInstrument' - elif pgo == 'use': - ET.SubElement(type_config, 'WholeProgramOptimization').text = 'PGOptimize' - else: - if pgo != 'off': - raise MesonException("b_pgo requires b_lto on MSVC.") + ET.SubElement(type_config, 'WholeProgramOptimization').text = self.get_target_whole_program_opt(target) # Let VS auto-set the RTC level ET.SubElement(type_config, 'BasicRuntimeChecks').text = 'Default' From 1607d96fe4bd5059f1b928600f3576bfb2ecb53b Mon Sep 17 00:00:00 2001 From: Adel KARA SLIMANE Date: Fri, 10 Apr 2026 03:57:11 +0200 Subject: [PATCH 018/681] docs: yaml ref manual: change 'Qt5' to simply 'Qt' Qt5 is deprecated, no need to actually give a version. --- docs/yaml/functions/dependency.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/yaml/functions/dependency.yaml b/docs/yaml/functions/dependency.yaml index c337769b674a..0eac52b977e8 100644 --- a/docs/yaml/functions/dependency.yaml +++ b/docs/yaml/functions/dependency.yaml @@ -57,7 +57,7 @@ description: | notes: - This function supports additional [library-specific](Dependencies.md#dependencies-with-custom-lookup-functionality) keyword arguments that may also be accepted (e.g. `modules` specifies submodules to use for - dependencies such as Qt5 or Boost. `components` allows the user to manually + dependencies such as Qt or Boost. `components` allows the user to manually add CMake `COMPONENTS` for the `find_package` lookup) varargs: From 1ba70c1ea0723477629fd76642eea8666ee791c3 Mon Sep 17 00:00:00 2001 From: Adel KARA SLIMANE Date: Fri, 10 Apr 2026 04:10:20 +0200 Subject: [PATCH 019/681] docs: Qt6 module: move introductory sections up makes more sense to more generic information first, i.e. - How to declare a dependency to Qt - Examples of how to build a Qt project with Qt --- docs/markdown/Qt6-module.md | 99 +++++++++++++++++++------------------ 1 file changed, 50 insertions(+), 49 deletions(-) diff --git a/docs/markdown/Qt6-module.md b/docs/markdown/Qt6-module.md index 6121b8393224..f5ea9186ecf4 100644 --- a/docs/markdown/Qt6-module.md +++ b/docs/markdown/Qt6-module.md @@ -11,7 +11,50 @@ Qt 6.1 or later due to the Qt tools having moved to the libexec subdirectory, and tool names being suffixed with only the Qt major version number e.g. qmake6. -## compile_resources +## Dependencies + +See [Qt dependencies](Dependencies.md#qt) + +The 'modules' argument is used to include Qt modules in the project. +See the Qt documentation for the [list of +modules](https://doc.qt.io/qt-6/qtmodules.html). + +The 'private_headers' argument allows usage of Qt's modules private +headers. + +## Example +A simple example would look like this: + +```meson +qt6 = import('qt6') +qt6_dep = dependency('qt6', modules: ['Core', 'Gui']) +inc = include_directories('includes') +moc_files = qt6.compile_moc(headers : 'myclass.h', + extra_arguments: ['-DMAKES_MY_MOC_HEADER_COMPILE'], + include_directories: inc, + dependencies: qt6_dep) +translations = qt6.compile_translations(ts_files : 'myTranslation_fr.ts', build_by_default : true) +executable('myprog', 'main.cpp', 'myclass.cpp', moc_files, + include_directories: inc, + dependencies : qt6_dep) +``` + +Sometimes, translations are embedded inside the binary using qresource +files. In this case the ts files do not need to be explicitly listed, +but will be inferred from the built qm files listed in the qresource +file. For example: + +```meson +qt6 = import('qt6') +qt6_dep = dependency('qt6', modules: ['Core', 'Gui']) +lang_cpp = qt6.compile_translations(qresource: 'lang.qrc') +executable('myprog', 'main.cpp', lang_cpp, + dependencies: qt6_dep) +``` + +## Functions + +### compile_resources *New in 0.59.0* @@ -27,7 +70,7 @@ It takes no positional arguments, and the following keyword arguments: - `extra_args` string[]: Extra arguments to pass directly to `qt-rcc` - `method` string: The method to use to detect Qt, see [[dependency]] -## compile_ui +### compile_ui *New in 0.59.0* @@ -45,7 +88,7 @@ It takes no positional arguments, and the following keyword arguments: it generates a file `{target private directory}/subdir/one.out` when `true`, and `{target private directory}/one.out` when `false` (default). -## compile_moc +### compile_moc *New in 0.59.0* @@ -72,7 +115,7 @@ It takes no positional arguments, and the following keyword arguments: - `output_json` bool: *New in 1.7.0*. If `true`, generates additionally a JSON representation which may be used by external tools such as qmltyperegistrar -## preprocess +### preprocess Consider using `compile_resources`, `compile_ui`, and `compile_moc` instead. @@ -118,7 +161,7 @@ This method takes the following keyword arguments: It returns an array of targets and sources to pass to a compilation target. -## compile_translations +### compile_translations This method generates the necessary targets to build translation files with lrelease, it takes no positional arguments, and the following keyword arguments: @@ -141,7 +184,7 @@ translations, or, if using a `qresource` file, a single custom target containing the processed source file, which should be passed to a main build target. -## has_tools +### has_tools This method returns `true` if all tools used by this module are found, `false` otherwise. @@ -166,7 +209,7 @@ This method takes the following keyword arguments: - `version` str | array[str]: *Since 1.11.0*. Specifies the required version, a string containing a comparison operator followed by the version string. -## qml_module +### qml_module *New in 1.7.0* @@ -239,45 +282,3 @@ replaced with `_`. Type registration may be invoked explicitly using See [Qt documentation](https://doc.qt.io/qt-6/resources.html#explicit-loading-and-unloading-of-embedded-resources) for more information - - -## Dependencies - -See [Qt dependencies](Dependencies.md#qt) - -The 'modules' argument is used to include Qt modules in the project. -See the Qt documentation for the [list of -modules](https://doc.qt.io/qt-6/qtmodules.html). - -The 'private_headers' argument allows usage of Qt's modules private -headers. - -## Example -A simple example would look like this: - -```meson -qt6 = import('qt6') -qt6_dep = dependency('qt6', modules: ['Core', 'Gui']) -inc = include_directories('includes') -moc_files = qt6.compile_moc(headers : 'myclass.h', - extra_arguments: ['-DMAKES_MY_MOC_HEADER_COMPILE'], - include_directories: inc, - dependencies: qt6_dep) -translations = qt6.compile_translations(ts_files : 'myTranslation_fr.ts', build_by_default : true) -executable('myprog', 'main.cpp', 'myclass.cpp', moc_files, - include_directories: inc, - dependencies : qt6_dep) -``` - -Sometimes, translations are embedded inside the binary using qresource -files. In this case the ts files do not need to be explicitly listed, -but will be inferred from the built qm files listed in the qresource -file. For example: - -```meson -qt6 = import('qt6') -qt6_dep = dependency('qt6', modules: ['Core', 'Gui']) -lang_cpp = qt6.compile_translations(qresource: 'lang.qrc') -executable('myprog', 'main.cpp', lang_cpp, - dependencies: qt6_dep) -``` From d959752268517759fa7a48794497412bdd79f933 Mon Sep 17 00:00:00 2001 From: Adel KARA SLIMANE Date: Fri, 10 Apr 2026 04:04:28 +0200 Subject: [PATCH 020/681] docs: dependencies: Qt: simplify section Only explain how to define the Qt dependency here, refer to the Qt6 module for more information --- docs/markdown/Dependencies.md | 58 +++++++++++++---------------------- 1 file changed, 22 insertions(+), 36 deletions(-) diff --git a/docs/markdown/Dependencies.md b/docs/markdown/Dependencies.md index 09b74cfd7b83..4f85783aa12c 100644 --- a/docs/markdown/Dependencies.md +++ b/docs/markdown/Dependencies.md @@ -761,50 +761,36 @@ but dependency tries `pkg-config` first. ## Qt -Meson has native Qt support. Its usage is best demonstrated with an -example. +Meson has native support for Qt ```meson -qt5_mod = import('qt5') -qt5widgets = dependency('qt5', modules : 'Widgets') - -processed = qt5_mod.preprocess( - moc_headers : 'mainWindow.h', # Only headers that need moc should be put here - moc_sources : 'helperFile.cpp', # must have #include"moc_helperFile.cpp" - ui_files : 'mainWindow.ui', - qresources : 'resources.qrc', -) - -q5exe = executable('qt5test', - sources : ['main.cpp', - 'mainWindow.cpp', - processed], - dependencies: qt5widgets) +qt6_dep = dependency('qt6', modules : ['Core', 'Gui', 'Widgets']) ``` -Here we have an UI file created with Qt Designer and one source and -header file each that require preprocessing with the `moc` tool. We -also define a resource file to be compiled with `rcc`. We just have to -tell Meson which files are which and it will take care of invoking all -the necessary tools in the correct order, which is done with the -`preprocess` method of the `qt5` module. Its output is simply put in -the list of sources for the target. The `modules` keyword of -`dependency` works just like it does with Boost. It tells which -subparts of Qt the program uses. +An optional `method` keyword argument can be set: `auto` (default), `pkg-config`, `config-tool` or `qmake` (*deprecated +since 0.58.0*; use `config-tool` instead). -You can set the `main` keyword argument to `true` to use the -`WinMain()` function provided by qtmain static library (this argument -does nothing on platforms other than Windows). +The `modules` keyword receives an array of Qt module names that will be required +and linked against. -Setting the optional `private_headers` keyword to true adds the -private header include path of the given module(s) to the compiler -flags. (since v0.47.0) +Obtaining the list of possible is not straightforward, here is a +non exhaustive list of possible Qt6 modules: -**Note** using private headers in your project is a bad idea, do so at -your own risk. +* `Core` +* `Gui` +* `Widgets` +* `Network` +* `Svg` +* `Quick` +* `Qml` +* `QuickWidgets` +* `QmlIntegration` -`method` may be `auto`, `pkg-config`, `config-tool` or `qmake` (*deprecated -since 0.58.0*; use `config-tool` instead). +**Notes:** + +* In Qt's documentation, the module names are referenced with and extra `Qt` prefix, +e.g. `QtCore` or `QtQmlIntegration`. +* For more information on how to build a Qt application with meson, see the [Qt6 module](Qt6-module.md) ## SDL2 From 1aa0f396f5f00e12d342675a28bce2eaaf7f773c Mon Sep 17 00:00:00 2001 From: Adel KARA SLIMANE Date: Fri, 10 Apr 2026 04:14:55 +0200 Subject: [PATCH 021/681] docs: Qt6 module: improve "Dependencies" section Simply explain what meson can do and refer to the "Dependencies" page on Qt --- docs/markdown/Qt6-module.md | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/docs/markdown/Qt6-module.md b/docs/markdown/Qt6-module.md index f5ea9186ecf4..4ab9cf22bc9b 100644 --- a/docs/markdown/Qt6-module.md +++ b/docs/markdown/Qt6-module.md @@ -13,14 +13,8 @@ and tool names being suffixed with only the Qt major version number e.g. qmake6. ## Dependencies -See [Qt dependencies](Dependencies.md#qt) - -The 'modules' argument is used to include Qt modules in the project. -See the Qt documentation for the [list of -modules](https://doc.qt.io/qt-6/qtmodules.html). - -The 'private_headers' argument allows usage of Qt's modules private -headers. +Qt is a framework with many sub-modules, meson can selectively look for and link with any combination, +see [Qt dependencies](Dependencies.md#qt). ## Example A simple example would look like this: From f4fb0425d90822851050075b8579eae5849f60e9 Mon Sep 17 00:00:00 2001 From: Adel KARA SLIMANE Date: Fri, 10 Apr 2026 04:16:08 +0200 Subject: [PATCH 022/681] docs: Qt6 module: add meson example with qml_module The approach can be hard to naturally come up with, better document it. --- docs/markdown/Qt6-module.md | 104 +++++++++++++++++++++++++++++++++++- 1 file changed, 102 insertions(+), 2 deletions(-) diff --git a/docs/markdown/Qt6-module.md b/docs/markdown/Qt6-module.md index 4ab9cf22bc9b..39b21f76c123 100644 --- a/docs/markdown/Qt6-module.md +++ b/docs/markdown/Qt6-module.md @@ -16,8 +16,11 @@ and tool names being suffixed with only the Qt major version number e.g. qmake6. Qt is a framework with many sub-modules, meson can selectively look for and link with any combination, see [Qt dependencies](Dependencies.md#qt). -## Example -A simple example would look like this: +## Examples + +We provide examples to showcase how meson can be used to build Qt applications. + +### Simple example ```meson qt6 = import('qt6') @@ -46,6 +49,103 @@ executable('myprog', 'main.cpp', lang_cpp, dependencies: qt6_dep) ``` +### QML-C++ integration + +A complex Qt app that uses + +- A Qt resource collection file + ([documentation here](https://doc.qt.io/qt-6/resources.html)) +- `QObject` (C++ only) derived C++ classes +- `QML_ELEMENT` and `Q_GADGET` annotated classes that are exposed to QML + ([documentation here](https://doc.qt.io/qt-6/qtqml-cppintegration-definetypes.html)) + +can also be build with meson using the following methods of the qt module object: + +- [compile_resources](#compile_resources) +- [compile_moc](#compile_moc) +- [qml_module](#qml_module) + +**Note:** when using [qml_module](#qml_module) with C++ `QML_ELEMENT` or `Q_GADGET` annotated headers in sub-folders, +their direct parent folder needs to be added to "include directories". This will cause problems is the header names are not unique. + +Here's an example meson code + +```meson + +# these sources do not contain any QObject derived classes +source_files = [ + 'main.cpp', + 'non-qt-sources/foo.cpp', + 'non-qt-sources/foo.h', +] + +# these sources contain QObject derived classes but are not exposed to QML +moc_header_files = [ + 'qt-cpp-only-sources/bar.cpp', + 'qt-cpp-only-sources/bar.h', +] + +# classes that are Q_GADGET or QML_ELEMENT + QObject "tainted" +# that are exposed to QML and can be used in QML code +qmlcpp_files = [ + 'qt-cpp-qml-integration/quux.cpp', + 'qt-cpp-qml-integration/quux.h', +] + +# actual QML files that will be able to use +# C++ classes defined in 'qmlcpp_files' +qml_files = [ + 'QML/Baz.qml', +] + +fs = import('fs') + +# workaround a Qt bug / design limitation: +# we add the parent folder of every C++ header involved with QML to the include dirs +# see https://bugreports.qt.io/browse/QTBUG-93443 +# see https://bugreports.qt.io/browse/QTBUG-87221 +# Note: qmlcpp_file needs to be a list of str and not files() otherwise the logic bellow gets tripped +qmlcpp_inc_dirs = [] +foreach qmlcpp_file: qmlcpp_files + qmlcpp_inc_dirs += [include_directories(fs.parent(qmlcpp_file))] +endforeach + +inc = include_directories('.') + +qt6 = import('qt6') + +# Qml and QmlIntegration are needed here +qt6_dep = dependency('qt6', modules: ['Core', 'Gui', 'Network', 'Svg', 'Quick', 'Qml', 'QuickWidgets', 'QmlIntegration']) + +compiled_resources = qt6.compile_resources(sources: files('resources.qrc')) + +# compile 'moc_header_files' list with moc +moc_files = qt6.compile_moc( + headers : moc_header_files, + include_directories: inc, + dependencies: qt6_dep +) + +# everything involved in C++-QML interaction goes in here +# the can be split into many modules, but works with a single one too +qml_mods = qt6.qml_module( + 'MyModule', + qml_sources: qml_files, + moc_headers: qmlcpp_files, + include_directories: qmlcpp_inc_dirs, + dependencies: [qt6_dep], +) + +# Build the final executable and give it all the resources built above +executable('MyExecutable', + sources: [source_files, compiled_ressources, moc_files, qml_mods], + include_directories: [inc] + qmlcpp_inc_dirs, + dependencies: [qt6_dep], + win_subsystem: 'windows', # useful for Windows: displays the app without spawning + # a console alongside it, no-op on other platforms. + install: true) +``` + ## Functions ### compile_resources From 335202cbfdc18b19744170404575c3db27d9b0e5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 15 Apr 2026 12:36:07 +0200 Subject: [PATCH 023/681] modules: qt: fix typing issue in _detect_tools() The version argument was incorrectly passed as None when looking for the qt dependency from e.g. the preprocess() method. Signed-off-by: Paolo Bonzini --- mesonbuild/modules/_qt.py | 3 +- .../41 qt module no dep/meson.build | 18 +++++++ .../41 qt module no dep/meson_options.txt | 1 + .../frameworks/41 qt module no dep/test.json | 11 ++++ .../41 qt module no dep/ui/mainWindow.ui | 54 +++++++++++++++++++ 5 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 test cases/frameworks/41 qt module no dep/meson.build create mode 100644 test cases/frameworks/41 qt module no dep/meson_options.txt create mode 100644 test cases/frameworks/41 qt module no dep/test.json create mode 100644 test cases/frameworks/41 qt module no dep/ui/mainWindow.ui diff --git a/mesonbuild/modules/_qt.py b/mesonbuild/modules/_qt.py index 8cba3efa78be..b2386f8708a1 100644 --- a/mesonbuild/modules/_qt.py +++ b/mesonbuild/modules/_qt.py @@ -265,11 +265,12 @@ def get_version(p: Program) -> str: if p.found(): self.tools[name] = p - def _detect_tools(self, state: ModuleState, method: DependencyMethods, required: bool = True, version: T.List[str] = None) -> None: + def _detect_tools(self, state: ModuleState, method: DependencyMethods, required: bool = True, version: T.Optional[T.List[str]] = None) -> None: if self._tools_detected: return self._tools_detected = True mlog.log(f'Detecting Qt{self.qt_version} tools') + version = version or [] kwargs: DependencyObjectKWs = {'required': required, 'modules': ['Core'], 'method': method, 'native': MachineChoice.HOST, 'version': version} # Just pick one to make mypy happy qt = T.cast('QtPkgConfigDependency', find_external_dependency(f'qt{self.qt_version}', state.environment, kwargs)) diff --git a/test cases/frameworks/41 qt module no dep/meson.build b/test cases/frameworks/41 qt module no dep/meson.build new file mode 100644 index 000000000000..54f381d7aa48 --- /dev/null +++ b/test cases/frameworks/41 qt module no dep/meson.build @@ -0,0 +1,18 @@ +project('qt5 module method test', 'cpp', meson_version: '>=1.4.0') + +qtmodule = import('qt5') + +# because methods like .compile_ui() request the Qt dependency +# with required: true, it is not possible to skip the test when +# Qt is absent. +# +# always ending the test with an error checks at least that there +# were no internal errors. The actual functionality of the +# methods is checked in the "4 qt" test. +testcase expect_error('.*not found.*', how: 're') + prep = qtmodule.compile_ui( + sources : 'ui/mainWindow.ui', + method: get_option('method'), + preserve_paths: true) + error('dummy not found error') +endtestcase diff --git a/test cases/frameworks/41 qt module no dep/meson_options.txt b/test cases/frameworks/41 qt module no dep/meson_options.txt new file mode 100644 index 000000000000..bc1069ebc881 --- /dev/null +++ b/test cases/frameworks/41 qt module no dep/meson_options.txt @@ -0,0 +1 @@ +option('method', type : 'string', value : 'auto', description : 'The method to use to find Qt') diff --git a/test cases/frameworks/41 qt module no dep/test.json b/test cases/frameworks/41 qt module no dep/test.json new file mode 100644 index 000000000000..4c70060473c2 --- /dev/null +++ b/test cases/frameworks/41 qt module no dep/test.json @@ -0,0 +1,11 @@ +{ + "matrix": { + "options": { + "method": [ + { "val": "config-tool" }, + { "val": "qmake" }, + { "val": "pkg-config" } + ] + } + } +} diff --git a/test cases/frameworks/41 qt module no dep/ui/mainWindow.ui b/test cases/frameworks/41 qt module no dep/ui/mainWindow.ui new file mode 100644 index 000000000000..c01b8bf9eee7 --- /dev/null +++ b/test cases/frameworks/41 qt module no dep/ui/mainWindow.ui @@ -0,0 +1,54 @@ + + + MainWindow + + + + 0 + 0 + 260 + 313 + + + + MainWindow + + + + + + 10 + 10 + 241 + 91 + + + + I am a button + + + + + + 10 + 112 + 241 + 91 + + + + + + + 10 + 212 + 241 + 91 + + + + + + + + From 772b6a7891c9ef6014a13904bd3dd971e1e78680 Mon Sep 17 00:00:00 2001 From: pixl_xip Date: Wed, 15 Apr 2026 21:06:40 -0600 Subject: [PATCH 024/681] `install_dir` wrongly changed to `install_dirs` in docs for Vala --- docs/markdown/Vala.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/markdown/Vala.md b/docs/markdown/Vala.md index c9b6aa02054c..6a7c1f43ff83 100644 --- a/docs/markdown/Vala.md +++ b/docs/markdown/Vala.md @@ -304,7 +304,7 @@ foo_lib = shared_library('foo', 'foo.vala', *(Before Meson 1.11)* the `install_vala_*` keyword arguments did not exist, and setting additional outputs to install was done via passing an array to -`install_dirs`. This array was in the form `[build_target, vala_header, +`install_dir`. This array was in the form `[build_target, vala_header, vala_vapi, vala_gir]`. Any elements not specified are treated as `false`. Whether using the array format or the keywords, the accepted values are: `true`, which means "install in default location"; `false`, which means "do not @@ -316,7 +316,7 @@ foo_lib = shared_library('foo', 'foo.vala', vala_vapi: 'foo-1.0.vapi', dependencies: [glib_dep, gobject_dep], install: true, - install_dirs : [true, true, true], + install_dir: [true, true, true], ) ``` From 55a83d0064e3ac09cb9a2225a7550e2e80ff119c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 17 Apr 2026 11:01:06 +0200 Subject: [PATCH 025/681] type_checking: allow empty string in dependencies This was used by Pandas, and generates an error starting from Meson 1.11.0. Related: pandas-dev/pandas#65252 Signed-off-by: Paolo Bonzini --- mesonbuild/interpreter/type_checking.py | 1 + test cases/common/1 trivial/meson.build | 4 +++- test cases/common/1 trivial/test.json | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py index 65d87a106b13..814aa0fde1db 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -430,6 +430,7 @@ def _default_options_convertor(raw: T.Union[str, T.List[str], T.Dict[str, Elemen extra_types={ BuildTarget: lambda arg: f'Tried to use a build_target "{T.cast("BuildTarget", arg).name}" as a dependency. This should be in `link_with` or `link_whole` instead.', }, + as_default=[('', ('1.11.1', "Replace an empty string with an empty array: `dependencies : ''` -> `dependencies : []`"))], ) D_MODULE_VERSIONS_KW: KwargInfo[T.List[T.Union[str, int]]] = KwargInfo( diff --git a/test cases/common/1 trivial/meson.build b/test cases/common/1 trivial/meson.build index 259fe00cffe0..c00c665e20fd 100644 --- a/test cases/common/1 trivial/meson.build +++ b/test cases/common/1 trivial/meson.build @@ -14,7 +14,9 @@ elif cc.get_id() == 'intel-cl' add_project_arguments('/Qdiag-error:10159', language : 'c') endif -exe = executable('trivialprog', sources : sources, link_args : '') +exe = executable('trivialprog', sources : sources, + link_args : '', + dependencies: '') assert(exe.name() == 'trivialprog') test('runtest', exe) # This is a comment diff --git a/test cases/common/1 trivial/test.json b/test cases/common/1 trivial/test.json index 71fa3a73ed48..772898079954 100644 --- a/test cases/common/1 trivial/test.json +++ b/test cases/common/1 trivial/test.json @@ -1,5 +1,8 @@ { "stdout": [ + { + "line": "test cases/common/1 trivial/meson.build:17: DEPRECATION: Project uses feature that was always broken, and is now deprecated since '1.11.1': Using '' as an empty value in dependencies. Replace an empty string with an empty array: `dependencies : ''` -> `dependencies : []`" + }, { "line": "test cases/common/1 trivial/meson.build:17: DEPRECATION: Project uses feature that was always broken, and is now deprecated since '1.10.1': Using '' as an empty value in link_args. Replace an empty string with an empty array: `link_args : ''` -> `link_args : []`" } From 445635091fe8c8f0f0bc2c26cc465cee7e8e7ff9 Mon Sep 17 00:00:00 2001 From: Sam James Date: Fri, 17 Apr 2026 21:47:08 +0100 Subject: [PATCH 026/681] dub: fix 'dependencies' typo in error --- mesonbuild/dependencies/dub.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/dependencies/dub.py b/mesonbuild/dependencies/dub.py index 7b4daf37c26d..d76f71b18322 100644 --- a/mesonbuild/dependencies/dub.py +++ b/mesonbuild/dependencies/dub.py @@ -357,7 +357,7 @@ def get_build_command() -> T.List[str]: if len(self.version_reqs) > 1: mlog.error('Multiple version requirements are not supported for raw dub dependencies.') mlog.error("Please specify only an exact version like '1.2.3'") - raise DependencyException('Multiple version requirements are not solvable for raw dub depencies') + raise DependencyException('Multiple version requirements are not solvable for raw dub dependencies') elif len(self.version_reqs) == 1: pack_spec += '@' + self.version_reqs[0] From 6e1157e476bade57c71032cb2055094607ad3053 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 15 Apr 2026 08:24:26 -0700 Subject: [PATCH 027/681] ci/cygwin: Remove python2 package It's not longer available --- .github/workflows/cygwin.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/cygwin.yml b/.github/workflows/cygwin.yml index 407dcff49834..90070f0f1621 100644 --- a/.github/workflows/cygwin.yml +++ b/.github/workflows/cygwin.yml @@ -67,7 +67,6 @@ jobs: libxslt-devel make ninja - python2-devel python3-devel python3-libxml2 python3-libxslt From e1ee518ce6e2c6b86a62abd81abab7857b444ce1 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 15 Apr 2026 08:40:34 -0700 Subject: [PATCH 028/681] ci/ubuntu-rolling: install npm for hotdog build Also uninstall it before committing the package --- ci/ciimage/ubuntu-rolling/install.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ci/ciimage/ubuntu-rolling/install.sh b/ci/ciimage/ubuntu-rolling/install.sh index 3460be48e9f5..aa4f9f137f40 100755 --- a/ci/ciimage/ubuntu-rolling/install.sh +++ b/ci/ciimage/ubuntu-rolling/install.sh @@ -31,6 +31,11 @@ pkgs=( jq ) +# Packages that are used at build time but should be removed from the image +transitivepkgs=( + npm # Only needed for hotdoc +) + sed -i '/^Types: deb/s/deb/deb deb-src/' /etc/apt/sources.list.d/ubuntu.sources apt-get -y update apt-get -y upgrade @@ -40,7 +45,7 @@ apt-get -y install eatmydata eatmydata apt-get -y build-dep meson # packages -eatmydata apt-get -y install "${pkgs[@]}" +eatmydata apt-get -y install "${pkgs[@]}" "${transitivepkgs[@]}" eatmydata apt-get -y install --no-install-recommends wine-stable # Wine is special # HACK: build hotdoc from git repo since current sdist is broken on modern compilers @@ -98,5 +103,6 @@ cd lcov make install # cleanup +apt-get -y purge "${transitivepkgs[@]}" apt-get -y clean apt-get -y autoclean From c0163e0e78a846e079fd2d85c0cfbbad9f3f0f53 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 15 Apr 2026 08:42:17 -0700 Subject: [PATCH 029/681] ci/ubuntu-rolling: lcov in `questing` and `resolute` is fine Hack no longer needed --- ci/ciimage/ubuntu-rolling/install.sh | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/ci/ciimage/ubuntu-rolling/install.sh b/ci/ciimage/ubuntu-rolling/install.sh index aa4f9f137f40..f3cef5fe30c3 100755 --- a/ci/ciimage/ubuntu-rolling/install.sh +++ b/ci/ciimage/ubuntu-rolling/install.sh @@ -29,6 +29,7 @@ pkgs=( itstool openjdk-11-jre jq + lcov ) # Packages that are used at build time but should be removed from the image @@ -95,13 +96,6 @@ cp LICENSE /usr/share/doc/zig cd .. rm -rf "$ZIG_BASE" -# Hack for https://github.com/linux-test-project/lcov/issues/245 -# https://github.com/linux-test-project/lcov/commit/bf135caf5f626e02191c42bd2773e08a0bb9b7e5 -# XXX: Drop this once Ubuntu has lcov-2.1* -git clone https://github.com/linux-test-project/lcov -cd lcov -make install - # cleanup apt-get -y purge "${transitivepkgs[@]}" apt-get -y clean From a70c9da139b4ca8966cda2fefdf32549a2570fa6 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 15 Apr 2026 08:44:59 -0700 Subject: [PATCH 030/681] ci/opensuse: Remove gettext-csharp package from install list It isn't packaged anymore --- ci/ciimage/opensuse/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/ciimage/opensuse/install.sh b/ci/ciimage/opensuse/install.sh index 7adb3fdfc980..44818d2f47c1 100755 --- a/ci/ciimage/opensuse/install.sh +++ b/ci/ciimage/opensuse/install.sh @@ -14,7 +14,7 @@ pkgs=( doxygen vulkan-devel vulkan-validationlayers openssh mercurial libpcap-devel libgpgme-devel libqt5-qtbase-devel libqt5-qttools-devel libqt5-linguist libqt5-qtbase-private-headers-devel qt6-declarative-devel qt6-base-devel qt6-tools qt6-tools-linguist qt6-declarative-tools qt6-core-private-devel - libwmf-devel valgrind cmake nasm gnustep-base-devel gettext-tools gettext-runtime gettext-csharp ncurses-devel + libwmf-devel valgrind cmake nasm gnustep-base-devel gettext-tools gettext-runtime ncurses-devel libxml2-devel libxslt-devel libyaml-devel glib2-devel json-glib-devel boost-devel libboost_date_time-devel libboost_filesystem-devel libboost_locale-devel libboost_headers-devel libboost_test-devel libboost_log-devel libboost_regex-devel From 5988be3a4f1f6cb5394c1d44a7b2c8ef2549af6b Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 15 Apr 2026 09:10:27 -0700 Subject: [PATCH 031/681] ci/opensuse: Install libgcrypt This is expected to be installed, but no longer is. It was probably being installed as a dependency before. --- ci/ciimage/opensuse/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/ciimage/opensuse/install.sh b/ci/ciimage/opensuse/install.sh index 44818d2f47c1..7b1e24e8d862 100755 --- a/ci/ciimage/opensuse/install.sh +++ b/ci/ciimage/opensuse/install.sh @@ -18,7 +18,7 @@ pkgs=( libxml2-devel libxslt-devel libyaml-devel glib2-devel json-glib-devel boost-devel libboost_date_time-devel libboost_filesystem-devel libboost_locale-devel libboost_headers-devel libboost_test-devel libboost_log-devel libboost_regex-devel - libboost_python3-devel libboost_regex-devel + libboost_python3-devel libboost_regex-devel libgcrypt-devel # HACK: remove npm once we switch back to hotdoc sdist npm ) From b01e88d0da9c2ff8b88c93e27308c383479b366b Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 15 Apr 2026 09:27:59 -0700 Subject: [PATCH 032/681] ci/arch: fix the cleanup phase Use pacman to cleanup the pacman cache, and yay only to clean up the auro cache, as the aur user doesn't have permission to clear the pacman cache --- ci/ciimage/arch/install.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ci/ciimage/arch/install.sh b/ci/ciimage/arch/install.sh index 402bb040221e..cf725f860ded 100755 --- a/ci/ciimage/arch/install.sh +++ b/ci/ciimage/arch/install.sh @@ -61,4 +61,6 @@ su $AUR_USER -c "yay -S $PACMAN_OPTS ${aur_pkgs[*]}" # cleanup pacman -Rs --noconfirm "${cleanup_pkgs[@]}" -su $AUR_USER -c "yes | yay -Scc" +# clear the pacman cache first, since the yay user may not have permission +pacman -Sc --noconfirm +su $AUR_USER -c "yes | yay -Sc --aur" From 5b7fa5bd0759f3a499ce22baf36c90cd1a3c93e1 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 17 Apr 2026 13:57:02 -0700 Subject: [PATCH 033/681] ci/arch: install zlib-static and boost-libs Which the tests expect to be present --- ci/ciimage/arch/install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/ciimage/arch/install.sh b/ci/ciimage/arch/install.sh index cf725f860ded..5ff339e9ee0c 100755 --- a/ci/ciimage/arch/install.sh +++ b/ci/ciimage/arch/install.sh @@ -10,12 +10,12 @@ pkgs=( python python-pip pypy3 ninja make git sudo fakeroot autoconf automake patch libelf gcc gcc-fortran gcc-objc vala rust byacc flex cython go dlang-dmd - mono boost qt5-base gtkmm3 gtest gmock protobuf gobject-introspection + mono boost boost-libs qt5-base gtkmm3 gtest gmock protobuf gobject-introspection itstool glib2-devel gtk3 java-environment=8 gtk-doc llvm clang sdl2 graphviz doxygen vulkan-headers vulkan-icd-loader vulkan-validation-layers openssh mercurial gtk-sharp-3 qt5-tools libwmf cmake netcdf-fortran openmpi nasm gnustep-base gettext python-lxml hotdoc rust-bindgen qt6-base qt6-tools qt6-declarative wayland wayland-protocols - intel-oneapi-mkl + intel-oneapi-mkl zlib-static # cuda ) From f44f1e987bb57ac093ae74b6cb21d4e966a88f27 Mon Sep 17 00:00:00 2001 From: Joel Rosdahl Date: Fri, 13 Mar 2026 16:10:36 +0100 Subject: [PATCH 034/681] Improve printing of options with different value than build type default Align columns in the "meson configure" section that prints options with different values than the build type default. Also add coloring, indent the section and make headers look more like those for other sections. Before: The following option(s) have a different value than the build type default current default optimization 3 0 debug 0 1 After: The following option(s) have a different value than the build type default: Current Default ------- ------- optimization 3 0 debug false true --- mesonbuild/mconf.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py index b8da4e8b7857..f7db6b56bf73 100644 --- a/mesonbuild/mconf.py +++ b/mesonbuild/mconf.py @@ -338,10 +338,19 @@ def print_nondefault_buildtype_options(self) -> None: mismatching = self.coredata.get_nondefault_buildtype_args() if not mismatching: return - mlog.log("\nThe following option(s) have a different value than the build type default\n") - mlog.log(' current default') - for m in mismatching: - mlog.log(f'{m[0]:21}{m[1]:10}{m[2]:10}') + items = [(stringify(m[0]), stringify(m[1]), stringify(m[2])) for m in mismatching] + max_name_len = max(len(item[0]) for item in items) + max_current_len = max(len(item[1]) for item in items) + padding1 = ' ' * (max_name_len + 1) + padding2 = ' ' * (max_current_len - len('Current')) + mlog.log("\nThe following option(s) have a different value than the build type default:\n") + mlog.log(' ', padding1, mlog.cyan('Current'), padding2, mlog.cyan('Default')) + mlog.log(' ', padding1, '-' * len('Current'), padding2, '-' * len('Default')) + for item in items: + name, current, default = item + padding1 = ' ' * (max_name_len - len(name)) + padding2 = ' ' * (max(max_current_len, len('Current')) - len(current)) + mlog.log(' ', mlog.green(name), padding1, mlog.yellow(current), padding2, mlog.blue(default)) def print_augments(self) -> None: if self.coredata.optstore.augments: From f02636b3f0ab0c9da52482a2e126c2835e2b4d97 Mon Sep 17 00:00:00 2001 From: Sam James Date: Fri, 17 Apr 2026 23:31:09 +0100 Subject: [PATCH 035/681] tests/d: fix compat w/ >=dub-1.41.0 dub-1.41.0 starts to reject invalid dub.json (5cf450f5fd2f62a3d60207ba4bdeb9599783cd0f) which hits us in multi-configuration: ``` (dmd-2.112.0)[root multi-configuration]# dub Error /tmp/meson/test cases/d/17 dub and meson project/multi-configuration/dub.json(3:21): configurations: Expected to be sequence (array), but is a mapping ``` Correct it based on the docs at https://dub.pm/dub-guide/recipe/#configurations Closes: https://github.com/mesonbuild/meson/issues/15719 Signed-off-by: Sam James --- .../multi-configuration/dub.json | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test cases/d/17 dub and meson project/multi-configuration/dub.json b/test cases/d/17 dub and meson project/multi-configuration/dub.json index 373176d9ecc8..9e28d1141192 100644 --- a/test cases/d/17 dub and meson project/multi-configuration/dub.json +++ b/test cases/d/17 dub and meson project/multi-configuration/dub.json @@ -1,14 +1,16 @@ { "name": "multi-configuration", - "configurations": { - "app": { + "configurations": [ + { + "name": "app", "targetType": "executable" }, - "lib": { + { + "name": "lib", "targetType": "library", "excludedSourceFiles": [ "source/app.d" ] } - } + ] } From 2dc0f8d8e310d9705ac26ec62f225258930b2079 Mon Sep 17 00:00:00 2001 From: Sam James Date: Fri, 17 Apr 2026 23:39:31 +0100 Subject: [PATCH 036/681] dub: log when first `dub describe` call fails When the `dub describe` call for raw dependencies fails, log the output rather than swallowing it. We still carry on though as I think some failures may be expected there, and I'm not a D expert at all, so just take the change that clearly makes debugging easier. The later describe call is already handled properly. Bug: https://github.com/mesonbuild/meson/issues/15719 Signed-off-by: Sam James --- mesonbuild/dependencies/dub.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mesonbuild/dependencies/dub.py b/mesonbuild/dependencies/dub.py index d76f71b18322..b534cf4c9b10 100644 --- a/mesonbuild/dependencies/dub.py +++ b/mesonbuild/dependencies/dub.py @@ -351,6 +351,8 @@ def get_build_command() -> T.List[str]: ret, res, err = self._call_dubbin(describe_cmd) if ret == 0: return (json.loads(res), helper_build, source) + else: + mlog.debug('DUB describe (raw) failed: ' + err) pack_spec = self.name if self.version_reqs is not None: From d124ce5342c35fd1cb97ef96b13d791c69da3b73 Mon Sep 17 00:00:00 2001 From: Sam James Date: Fri, 17 Apr 2026 23:48:26 +0100 Subject: [PATCH 037/681] Spell 'DragonFly BSD' correctly They style it with a capital F and a space. Signed-off-by: Sam James --- docs/markdown/Dependencies.md | 2 +- docs/markdown/Release-notes-for-0.64.0.md | 2 +- mesonbuild/envconfig.py | 2 +- test cases/common/221 zlib/meson.build | 2 +- unittests/allplatformstests.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/markdown/Dependencies.md b/docs/markdown/Dependencies.md index 4f85783aa12c..27fc7a6fc7b9 100644 --- a/docs/markdown/Dependencies.md +++ b/docs/markdown/Dependencies.md @@ -868,7 +868,7 @@ $ wx-config --libs std stc ## Zlib Zlib ships with pkg-config and cmake support, but on some operating -systems (windows, macOs, FreeBSD, dragonflybsd, android), it is provided as +systems (windows, macOs, FreeBSD, DragonFly BSD, android), it is provided as part of the base operating system without pkg-config support. The new System finder can be used on these OSes to link with the bundled version. diff --git a/docs/markdown/Release-notes-for-0.64.0.md b/docs/markdown/Release-notes-for-0.64.0.md index 5cfde82b5a0f..3f4537ec505a 100644 --- a/docs/markdown/Release-notes-for-0.64.0.md +++ b/docs/markdown/Release-notes-for-0.64.0.md @@ -94,7 +94,7 @@ method. ## BSD support for the `jni` dependency This system dependency now supports all BSD systems that Meson currently -supports, including FreeBSD, NetBSD, OpenBSD, and DragonflyBSD. +supports, including FreeBSD, NetBSD, OpenBSD, and DragonFly BSD. ## Credentials from `~/.netrc` for `https` URLs diff --git a/mesonbuild/envconfig.py b/mesonbuild/envconfig.py index 7e19505dc4ff..b0072a49b874 100644 --- a/mesonbuild/envconfig.py +++ b/mesonbuild/envconfig.py @@ -363,7 +363,7 @@ def is_openbsd(self) -> bool: return self.system == 'openbsd' def is_dragonflybsd(self) -> bool: - """Machine is DragonflyBSD?""" + """Machine is DragonFly BSD?""" return self.system == 'dragonfly' def is_freebsd(self) -> bool: diff --git a/test cases/common/221 zlib/meson.build b/test cases/common/221 zlib/meson.build index b5b813c76b9c..993193af983a 100644 --- a/test cases/common/221 zlib/meson.build +++ b/test cases/common/221 zlib/meson.build @@ -1,7 +1,7 @@ project('zlib system dependency', 'c') if not ['darwin', 'freebsd', 'dragonfly', 'windows', 'android'].contains(host_machine.system()) - error('MESON_SKIP_TEST only applicable on macOS, FreeBSD, DragonflyBSD, Windows, and Android.') + error('MESON_SKIP_TEST only applicable on macOS, FreeBSD, DragonFly BSD, Windows, and Android.') endif cc = meson.get_compiler('c') diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 8026d9d95562..2769370bbfe4 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -1752,7 +1752,7 @@ def test_rpath_uses_ORIGIN(self): rpath = get_rpath(os.path.join(self.builddir, each)) self.assertTrue(rpath, f'Rpath could not be determined for {each}.') if is_dragonflybsd(): - # DragonflyBSD will prepend /usr/lib/gccVERSION to the rpath, + # DragonFly BSD will prepend /usr/lib/gccVERSION to the rpath, # so ignore that. self.assertTrue(rpath.startswith('/usr/lib/gcc')) rpaths = rpath.split(':')[1:] From f8dbedd70c688effb35eb8aa0a03672a89c41c2a Mon Sep 17 00:00:00 2001 From: Sam James Date: Fri, 17 Apr 2026 23:51:07 +0100 Subject: [PATCH 038/681] docs: dependencies: tweak casing of OSes Signed-off-by: Sam James --- docs/markdown/Dependencies.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/markdown/Dependencies.md b/docs/markdown/Dependencies.md index 27fc7a6fc7b9..09e1138056cc 100644 --- a/docs/markdown/Dependencies.md +++ b/docs/markdown/Dependencies.md @@ -868,7 +868,7 @@ $ wx-config --libs std stc ## Zlib Zlib ships with pkg-config and cmake support, but on some operating -systems (windows, macOs, FreeBSD, DragonFly BSD, android), it is provided as +systems (Windows, macOS, FreeBSD, DragonFly BSD, Android), it is provided as part of the base operating system without pkg-config support. The new System finder can be used on these OSes to link with the bundled version. From 1a694385529ebc8534c062ec2667631f4bd9df0a Mon Sep 17 00:00:00 2001 From: Daniel Foster Date: Sat, 4 Apr 2026 13:45:16 +1000 Subject: [PATCH 039/681] mtest: add -k as alias to --maxfail An equivalent option is available on ninja as -k, so let's alias that. --- data/shell-completions/bash/meson | 3 ++- data/shell-completions/zsh/_meson | 1 + mesonbuild/mtest.py | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/data/shell-completions/bash/meson b/data/shell-completions/bash/meson index 90f5095cb373..3ee3ea025c29 100644 --- a/data/shell-completions/bash/meson +++ b/data/shell-completions/bash/meson @@ -561,6 +561,7 @@ _meson-test() { shortopts=( h j + k q v t @@ -595,7 +596,7 @@ _meson-test() { local cur prev if _get_comp_words_by_ref -n ':' cur prev &>/dev/null; then case $prev in - --maxfail | --repeat) + -k | --maxfail | --repeat) # number, can't be completed return ;; diff --git a/data/shell-completions/zsh/_meson b/data/shell-completions/zsh/_meson index d0c3bcf463d2..5ec3a2a1a125 100644 --- a/data/shell-completions/zsh/_meson +++ b/data/shell-completions/zsh/_meson @@ -182,6 +182,7 @@ local -a meson_commands=( # TODO: complete test suites local -a specs=( + '(--maxfail -k)'{'--maxfail','-k'}'=[number of failing tests before aborting]:number of failing tests: ' '--repeat[number of times to run the tests]:number of times to repeat: ' '--no-rebuild[do not rebuild before running tests]' '--gdb[run tests under gdb]' diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index aca4e23b41b3..18760eb51ecd 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -127,7 +127,7 @@ def test_slice(arg: str) -> T.Tuple[int, int]: # Note: when adding arguments, please also add them to the completion # scripts in $MESONSRC/data/shell-completions/ def add_arguments(parser: argparse.ArgumentParser) -> None: - parser.add_argument('--maxfail', default=0, type=int, + parser.add_argument('-k', '--maxfail', default=0, type=int, help='Number of failing tests before aborting the ' 'test run. (default: 0, to disable aborting on failure)') parser.add_argument('--repeat', default=1, dest='repeat', type=int, From aceb4d82ce0e6ec2123de5e1721864e37336c89e Mon Sep 17 00:00:00 2001 From: Frankie Robertson Date: Mon, 17 Nov 2025 11:11:47 +0200 Subject: [PATCH 040/681] Pass -qopenmp to Intel LLVM based compilers --- mesonbuild/compilers/c.py | 4 ++-- mesonbuild/compilers/cpp.py | 4 ++-- mesonbuild/compilers/fortran.py | 4 ++-- mesonbuild/compilers/mixins/intel.py | 5 +++++ 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index a478c79616b6..2d88082beb80 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -22,7 +22,7 @@ from .mixins.visualstudio import MSVCCompiler, ClangClCompiler from .mixins.gnu import GnuCompiler, GnuCStds from .mixins.gnu import gnu_common_warning_args, gnu_c_warning_args -from .mixins.intel import IntelGnuLikeCompiler, IntelVisualStudioLikeCompiler +from .mixins.intel import IntelGnuLikeCompiler, IntelLLVMLikeCompiler, IntelVisualStudioLikeCompiler from .mixins.clang import ClangCompiler, ClangCStds from .mixins.elbrus import ElbrusCompiler from .mixins.pgi import PGICompiler @@ -391,7 +391,7 @@ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = return args -class IntelLLVMCCompiler(ClangCCompiler): +class IntelLLVMCCompiler(IntelLLVMLikeCompiler, ClangCCompiler): id = 'intel-llvm' diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index 7718f501796d..5a41f6a92cdc 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -25,7 +25,7 @@ from .mixins.arm import ArmCompiler, ArmclangCompiler from .mixins.visualstudio import MSVCCompiler, ClangClCompiler from .mixins.gnu import GnuCompiler, GnuCPPStds, gnu_common_warning_args, gnu_cpp_warning_args -from .mixins.intel import IntelGnuLikeCompiler, IntelVisualStudioLikeCompiler +from .mixins.intel import IntelGnuLikeCompiler, IntelLLVMLikeCompiler, IntelVisualStudioLikeCompiler from .mixins.clang import ClangCompiler, ClangCPPStds from .mixins.elbrus import ElbrusCompiler from .mixins.pgi import PGICompiler @@ -764,7 +764,7 @@ def get_option_link_args(self, target: 'BuildTarget', subproject: T.Optional[str return [] -class IntelLLVMCPPCompiler(ClangCPPCompiler): +class IntelLLVMCPPCompiler(IntelLLVMLikeCompiler, ClangCPPCompiler): id = 'intel-llvm' diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py index d468bd547bb7..bba860d923fd 100644 --- a/mesonbuild/compilers/fortran.py +++ b/mesonbuild/compilers/fortran.py @@ -17,7 +17,7 @@ ) from .mixins.clike import CLikeCompiler from .mixins.gnu import GnuCompiler, gnu_optimization_args -from .mixins.intel import IntelGnuLikeCompiler, IntelVisualStudioLikeCompiler +from .mixins.intel import IntelGnuLikeCompiler, IntelLLVMLikeCompiler, IntelVisualStudioLikeCompiler from .mixins.clang import ClangCompiler from .mixins.elbrus import ElbrusCompiler from .mixins.pgi import PGICompiler @@ -437,7 +437,7 @@ def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]: return ['-gen-dep=' + outtarget, '-gen-depformat=make'] -class IntelLLVMFortranCompiler(IntelFortranCompiler): +class IntelLLVMFortranCompiler(IntelLLVMLikeCompiler, IntelFortranCompiler): id = 'intel-llvm' diff --git a/mesonbuild/compilers/mixins/intel.py b/mesonbuild/compilers/mixins/intel.py index b417944fda43..822710a18d1c 100644 --- a/mesonbuild/compilers/mixins/intel.py +++ b/mesonbuild/compilers/mixins/intel.py @@ -112,6 +112,11 @@ def get_has_func_attribute_extra_args(self, name: str) -> T.List[str]: return ['-diag-error', '1292'] +class IntelLLVMLikeCompiler: + def openmp_flags(self, env: Environment) -> T.List[str]: + return ['-qopenmp'] + + class IntelVisualStudioLikeCompiler(VisualStudioLikeCompiler): """Abstractions for ICL, the Intel compiler on Windows.""" From 376d68215b0b9b4cbaf635d86e5175cafe243431 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 20 Apr 2026 07:50:26 -0700 Subject: [PATCH 041/681] compilers: fix arguments to Intel openmp_flags Which do not take an `Environment` parameter. Fixes: aceb4d82c ("Pass -qopenmp to Intel LLVM based compilers") --- mesonbuild/compilers/mixins/intel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/compilers/mixins/intel.py b/mesonbuild/compilers/mixins/intel.py index 822710a18d1c..5b48ce0cc779 100644 --- a/mesonbuild/compilers/mixins/intel.py +++ b/mesonbuild/compilers/mixins/intel.py @@ -113,7 +113,7 @@ def get_has_func_attribute_extra_args(self, name: str) -> T.List[str]: class IntelLLVMLikeCompiler: - def openmp_flags(self, env: Environment) -> T.List[str]: + def openmp_flags(self) -> T.List[str]: return ['-qopenmp'] From 9efa9a556ed35dd32509b393de10c3f5d068efda Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 4 Mar 2026 16:26:16 +0100 Subject: [PATCH 042/681] modules: qt: tighten some types Signed-off-by: Paolo Bonzini --- mesonbuild/modules/_qt.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mesonbuild/modules/_qt.py b/mesonbuild/modules/_qt.py index b2386f8708a1..4ce82d6fabae 100644 --- a/mesonbuild/modules/_qt.py +++ b/mesonbuild/modules/_qt.py @@ -124,7 +124,7 @@ class GenQmlCachegenKwArgs(TypedDict): target_name: str qml_sources: T.Sequence[T.Union[FileOrString, build.GeneratedTypes]] - qml_qrc: T.Union[FileOrString, build.GeneratedTypes] + qml_qrc: File extra_args: T.List[str] module_prefix: str method: DependencyMethods @@ -138,7 +138,7 @@ class GenQmlTypeRegistrarKwArgs(TypedDict): namespace: str typeinfo: str generate_qmltype: bool - collected_json: T.Optional[T.Union[FileOrString, build.CustomTarget]] + collected_json: T.Optional[T.Union[File, build.CustomTarget]] extra_args: T.List[str] method: DependencyMethods install: bool @@ -951,9 +951,9 @@ def _qml_type_registrar(self, state: ModuleState, kwargs: GenQmlTypeRegistrarKwA namespace: str = kwargs['namespace'] typeinfo: str = kwargs['typeinfo'] target_name: str = kwargs['target_name'] - collected_json: T.Optional[T.Union[FileOrString, build.CustomTarget]] = kwargs['collected_json'] + collected_json: T.Optional[T.Union[File, build.CustomTarget]] = kwargs['collected_json'] - inputs: T.Sequence[T.Union[FileOrString, build.CustomTarget]] = [collected_json] if collected_json else [] + inputs: T.Sequence[T.Union[File, build.CustomTarget]] = [collected_json] if collected_json else [] outputs: T.List[str] = [f'{target_name}_qmltyperegistrations.cpp'] install_dir: T.List[T.Union[str, Literal[False]]] = [False] install_tag: T.List[T.Union[str, None]] = [None] @@ -1102,7 +1102,7 @@ def qml_module(self, state: ModuleState, args: T.Tuple[str], kwargs: QmlModuleKw self.interpreter.install_data_impl(all_qml_files, module_install_dir, FileMode(), all_qml_basename, 'devel') - collected_json: T.Optional[T.Union[FileOrString, build.CustomTarget]] = None + collected_json: T.Optional[T.Union[File, build.CustomTarget]] = None if kwargs['moc_headers']: compile_moc_kwargs: MocCompilerKwArgs = { 'sources': [], From dcc5eab2524c5aea648a50c6f40b467aaebefab6 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 13 Apr 2026 18:39:34 +0200 Subject: [PATCH 043/681] modules: gnome: tighten types Signed-off-by: Paolo Bonzini --- mesonbuild/modules/gnome.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index b4ac147f534b..caac531e587c 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -972,7 +972,7 @@ def _make_gir_target( state: 'ModuleState', girfile: str, scan_command: T.Sequence[T.Union['FileOrString', Executable, Program]], - generated_files: T.Sequence[T.Union[str, mesonlib.File, build.GeneratedTypes]], + generated_files: T.Sequence[build.GeneratedTypes], depends: T.Sequence[T.Union['FileOrString', build.BuildTarget, 'build.GeneratedTypes', build.StructuredSources]], env_flags: T.Sequence[str], kwargs: GenerateGir) -> GirTarget: @@ -1025,7 +1025,7 @@ def _make_gir_target( @staticmethod def _make_typelib_target(state: 'ModuleState', typelib_output: str, typelib_cmd: T.Sequence[T.Union[str, CustomTarget, Program]], - generated_files: T.Sequence[T.Union[str, mesonlib.File, build.GeneratedTypes]], + generated_files: T.Sequence[build.GeneratedTypes], kwargs: GenerateGir) -> TypelibTarget: install = kwargs['install_typelib'] if install is None: From d9e920d8787a4c2cec5c3a727754a5187c1696e1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 10 Mar 2026 17:02:42 +0100 Subject: [PATCH 044/681] modules: qt: raise error for non-working case of qt.compile_resources() The changes introduced by PR 9012 never worked for compile_resources because the call to _parse_qrc_deps fails to find the generated file. Synchronize qt.compile_resources() and gnome.compile_resources(), raising a similar error and noting that dyndeps could be used to fix it. Signed-off-by: Paolo Bonzini --- mesonbuild/modules/_qt.py | 15 +++++++-------- mesonbuild/modules/gnome.py | 1 + 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/mesonbuild/modules/_qt.py b/mesonbuild/modules/_qt.py index 4ce82d6fabae..a9c2edbc4920 100644 --- a/mesonbuild/modules/_qt.py +++ b/mesonbuild/modules/_qt.py @@ -326,14 +326,9 @@ def _qrc_nodes(state: ModuleState, rcc_file: FileOrString) -> T.Tuple[str, T.Lis except Exception: raise MesonException(f'Unable to parse resource file {abspath}') - def _parse_qrc_deps(self, state: ModuleState, - rcc_file_: T.Union[FileOrString, build.GeneratedTypes]) -> T.List[File]: + def _parse_qrc_deps(self, state: ModuleState, rcc_file_: FileOrString) -> T.List[File]: result: T.List[File] = [] - inputs: T.Sequence['FileOrString'] = [] - if isinstance(rcc_file_, (str, File)): - inputs = [rcc_file_] - else: - inputs = rcc_file_.get_outputs() + inputs: T.Sequence['FileOrString'] = [rcc_file_] for rcc_file in inputs: rcc_dirname, nodes = self._qrc_nodes(state, rcc_file) @@ -438,7 +433,11 @@ def _compile_resources_impl(self, state: 'ModuleState', kwargs: 'ResourceCompile if isinstance(s, (str, File)): sources.append(s) else: - sources.extend(s.get_outputs()) + # TODO: this could be fixed with dyndeps + raise MesonException('Resource xml files generated at build-time cannot be used with ' + 'qt.compile_resources() because we need to scan the xml for ' + 'dependencies.\nUse configure_file() instead to generate it at ' + 'configure-time.') extra_args = kwargs['extra_args'] # If a name was set generate a single .cpp file from all of the qrc diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index caac531e587c..8819fbd3698d 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -436,6 +436,7 @@ def compile_resources(self, state: 'ModuleState', args: T.Tuple[str, 'FileOrStri ifile = os.path.join(input_file.subdir, input_file.fname) elif isinstance(input_file, (CustomTarget, CustomTargetIndex, GeneratedList)): + # TODO: this could be fixed with dyndeps raise MesonException('Resource xml files generated at build-time cannot be used with ' 'gnome.compile_resources() in the current version of glib-compile-resources ' 'because we need to scan the xml for dependencies due to ' From ef9bb6d9900151bcf83a25f56ca50fdc82adee3a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 4 Mar 2026 16:15:34 +0100 Subject: [PATCH 045/681] interpreter: define another source_strings_to_files overload for custom_target input Signed-off-by: Paolo Bonzini --- mesonbuild/interpreter/interpreter.py | 6 ++++++ mesonbuild/interpreter/kwargs.py | 5 +++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index de92cf050059..5a93e489589b 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -128,6 +128,9 @@ # Input source types passed to the build.Target classes SourceOutputs = T.Union[mesonlib.File, build.GeneratedTypes, build.BuildTarget, build.ExtractedObjects, build.StructuredSources] + # Sources for custom targets, which can also include ExternalProgram + CustomTargetSources = T.Union[mesonlib.File, build.GeneratedTypes, build.BuildTarget, + build.ExtractedObjects, ExternalProgram] BuildTargetSource = T.Union[mesonlib.FileOrString, build.GeneratedTypes, build.StructuredSources] @@ -3218,6 +3221,9 @@ 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[kwtypes.CustomTargetInputs]) -> T.List['CustomTargetSources']: ... # noqa: F811 + @T.overload def source_strings_to_files(self, sources: T.List['SourceInputs'], strict: bool = True) -> T.List['SourceOutputs']: ... # noqa: F811 diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index 06ccd6b0314d..7502addbecc7 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -22,6 +22,8 @@ from .type_checking import PkgConfigDefineType, SourcesVarargsType TestArgs = T.Union[str, File, build.Target, ExternalProgram] +CustomTargetInputs = T.Union[str, build.BuildTarget, build.GeneratedTypes, + build.ExtractedObjects, ExternalProgram, File] RustAbi = Literal['rust', 'c'] class NativeKW(TypedDict): @@ -200,8 +202,7 @@ class CustomTarget(TypedDict): depfile: T.Optional[str] env: EnvironmentVariables feed: bool - input: T.List[T.Union[str, build.BuildTarget, build.GeneratedTypes, - build.ExtractedObjects, ExternalProgram, File]] + input: T.List[CustomTargetInputs] install: bool install_dir: T.List[T.Union[str, T.Literal[False]]] install_mode: FileMode From b5f482a9600c530f25a22e6097ec24ae6a2f8734 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 10 Mar 2026 16:58:13 +0100 Subject: [PATCH 046/681] modules: icestorm: do not pass strings to CustomTargets Signed-off-by: Paolo Bonzini --- mesonbuild/modules/icestorm.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mesonbuild/modules/icestorm.py b/mesonbuild/modules/icestorm.py index c89acd38b08b..b688ce7e2c3a 100644 --- a/mesonbuild/modules/icestorm.py +++ b/mesonbuild/modules/icestorm.py @@ -61,6 +61,7 @@ def project(self, state: ModuleState, proj_name, arg_sources = args all_sources = self.interpreter.source_strings_to_files( list(itertools.chain(arg_sources, kwargs['sources']))) + constraint_file = self.interpreter.source_strings_to_files([kwargs['constraint_file']])[0] blif_target = build.CustomTarget( f'{proj_name}_blif', @@ -78,7 +79,7 @@ def project(self, state: ModuleState, state.subproject, state.environment, [self.tools['arachne'], '-q', '-d', '1k', '-p', '@INPUT@', '-o', '@OUTPUT@'], - [kwargs['constraint_file'], blif_target], + [constraint_file, blif_target], [f'{proj_name}.asc'], ) From 22e9df56842b6c670bd8cc5f86624a6c35a8da1d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 10 Mar 2026 16:58:13 +0100 Subject: [PATCH 047/681] modules: java: do not pass strings to CustomTargets Signed-off-by: Paolo Bonzini --- mesonbuild/modules/java.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mesonbuild/modules/java.py b/mesonbuild/modules/java.py index abef4acdfffa..2794129ef1ee 100644 --- a/mesonbuild/modules/java.py +++ b/mesonbuild/modules/java.py @@ -89,12 +89,14 @@ def __native_headers(self, state: ModuleState, args: T.Tuple[T.List[mesonlib.Fil prefix = classes[0] if not package else package + sources = [mesonlib.File.from_source_file(state.environment.source_dir, state.subdir, s) + if isinstance(s, str) else s for s in args[0]] target = CustomTarget(f'{prefix}-native-headers', state.subdir, state.subproject, state.environment, command, - sources=args[0], outputs=headers, backend=state.backend) + sources=sources, outputs=headers, backend=state.backend) # It is only known that 1.8.0 won't pre-create the directory. 11 and 16 # do not exhibit this behavior. From 6a22034c04942221c3c83dbf280647be0272ad51 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 10 Mar 2026 16:58:13 +0100 Subject: [PATCH 048/681] modules: windows: do not pass strings to CustomTargets Signed-off-by: Paolo Bonzini --- mesonbuild/modules/windows.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/modules/windows.py b/mesonbuild/modules/windows.py index 727eb736d0ab..198cbebd828e 100644 --- a/mesonbuild/modules/windows.py +++ b/mesonbuild/modules/windows.py @@ -152,10 +152,10 @@ def compile_resources(self, state: 'ModuleState', res_targets: T.List[build.CustomTarget] = [] - def get_names() -> T.Iterable[T.Tuple[str, str, T.Union[str, mesonlib.File, build.CustomTargetIndex]]]: + def get_names() -> T.Iterable[T.Tuple[str, str, T.Union[mesonlib.File, build.CustomTargetIndex]]]: for src in args[0]: if isinstance(src, str): - yield os.path.join(state.subdir, src), src, src + yield os.path.join(state.subdir, src), src, mesonlib.File.from_source_file(state.environment.source_dir, state.subdir, src) elif isinstance(src, mesonlib.File): yield src.relative_name(), src.fname, src elif isinstance(src, build.CustomTargetIndex): From 68b6733c49b29d3986ae1613a965aec9f0e7deab Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 10 Mar 2026 16:58:13 +0100 Subject: [PATCH 049/681] modules: gnome: do not pass strings to CustomTargets Signed-off-by: Paolo Bonzini --- docs/markdown/Gnome-module.md | 3 +- mesonbuild/modules/gnome.py | 67 ++++++++++++++++++++--------------- 2 files changed, 41 insertions(+), 29 deletions(-) diff --git a/docs/markdown/Gnome-module.md b/docs/markdown/Gnome-module.md index 84bcc6172e1a..d97eb11d0d7a 100644 --- a/docs/markdown/Gnome-module.md +++ b/docs/markdown/Gnome-module.md @@ -326,7 +326,8 @@ library. * `install_dir`: location to install the VAPI file (defaults to datadir/vala/vapi) * `metadata_dirs`: extra directories to include for metadata files * `packages`: VAPI packages that are depended upon -* `sources`: the gir source to generate the VAPI from +* `sources`: the gir source to generate the VAPI from (File objects + only supported *since 1.12.0*) * `vapi_dirs`: extra directories to include for VAPI files Returns a custom dependency that can be included when building other diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 8819fbd3698d..cf4280bd257b 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -43,6 +43,8 @@ from ..build import BuildTarget from ..compilers import Compiler from ..interpreter import Interpreter + from ..interpreter.interpreter import CustomTargetSources + from ..interpreter.kwargs import CustomTargetInputs from ..interpreterbase import TYPE_var, TYPE_kwargs from ..mesonlib import EnvironmentVariables, FileOrString from ..programs import Program, CommandList, CommandListEntry @@ -393,7 +395,7 @@ def post_install(self, state: 'ModuleState', args: T.List['TYPE_var'], kwargs: ' KwargInfo('gresource_bundle', bool, default=False, since='0.37.0'), KwargInfo('source_dir', ContainerTypeInfo(list, str), default=[], listify=True), ) - def compile_resources(self, state: 'ModuleState', args: T.Tuple[str, 'FileOrString'], + def compile_resources(self, state: 'ModuleState', args: T.Tuple[str, CustomTargetInputs], kwargs: 'CompileResources') -> 'ModuleReturnValue': self.__print_gresources_warning(state) glib_version = self._get_native_glib_version(state) @@ -403,8 +405,14 @@ def compile_resources(self, state: 'ModuleState', args: T.Tuple[str, 'FileOrStri source_dirs = kwargs['source_dir'] dependencies = kwargs['dependencies'] + target_name, input_file_arg = args - target_name, input_file = args + input_file: CustomTargetSources + if isinstance(input_file_arg, str): + input_file = mesonlib.File.from_source_file(state.environment.get_source_dir(), + state.subdir, input_file_arg) + else: + input_file = input_file_arg # Validate dependencies subdirs: T.List[str] = [] @@ -442,8 +450,6 @@ def compile_resources(self, state: 'ModuleState', args: T.Tuple[str, 'FileOrStri 'because we need to scan the xml for dependencies due to ' '\nUse ' 'configure_file() instead to generate it at configure-time.') - else: - ifile = os.path.join(state.subdir, input_file) depend_files, depends, subdirs = self._get_gresource_dependencies( state, ifile, source_dirs, dependencies) @@ -1385,11 +1391,11 @@ def yelp(self, state: 'ModuleState', args: T.Tuple[str, T.List[str]], kwargs: 'Y mesonlib.FileMode(), state.subproject, install_tag='doc') targets.append(l_data) - po_file = l + '.po' + po_fname = l + '.po' po_args: CommandList = [ msgmerge, '-q', '-o', - os.path.join('@SOURCE_ROOT@', l_subdir, po_file), - os.path.join('@SOURCE_ROOT@', l_subdir, po_file), pot_file] + os.path.join('@SOURCE_ROOT@', l_subdir, po_fname), + os.path.join('@SOURCE_ROOT@', l_subdir, po_fname), pot_file] potarget = build.RunTarget(f'help-{project_id}-{l}-update-po', po_args, [pottarget], l_subdir, state.subproject, state.environment) @@ -1397,6 +1403,7 @@ def yelp(self, state: 'ModuleState', args: T.Tuple[str, T.List[str]], kwargs: 'Y potargets.append(potarget) gmo_file = project_id + '-' + l + '.gmo' + po_file = mesonlib.File.from_source_file(state.environment.source_dir, l_subdir, l + '.po') gmotarget = CustomTarget( f'help-{project_id}-{l}-gmo', l_subdir, @@ -1650,7 +1657,8 @@ def gtkdoc_html_dir(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'TYP def gdbus_codegen(self, state: 'ModuleState', args: T.Tuple[str, T.Optional[T.Union['FileOrString', build.GeneratedTypes]]], kwargs: 'GdbusCodegen') -> ModuleReturnValue: namebase = args[0] - xml_files: T.List[T.Union['FileOrString', build.GeneratedTypes]] = [args[1]] if args[1] else [] + xml_files: T.List[T.Union[mesonlib.File, build.GeneratedTypes]] = \ + self.interpreter.source_strings_to_files([args[1]]) if args[1] else [] cmd: CommandList = [self._find_tool(state, 'gdbus-codegen')] cmd.extend(kwargs['extra_args']) @@ -1674,7 +1682,7 @@ def gdbus_codegen(self, state: 'ModuleState', args: T.Tuple[str, T.Optional[T.Un cmd.extend(['--c-namespace', kwargs['namespace']]) if kwargs['object_manager']: cmd.extend(['--c-generate-object-manager']) - xml_files.extend(kwargs['sources']) + xml_files.extend(self.interpreter.source_strings_to_files(kwargs['sources'])) build_by_default = kwargs['build_by_default'] # Annotations are a bit ugly in that they are a list of lists of strings... @@ -1864,11 +1872,11 @@ def mkenums(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'MkEnums') - basename = args[0] c_template = kwargs['c_template'] - if isinstance(c_template, mesonlib.File): - c_template = c_template.absolute_path(state.environment.source_dir, state.environment.build_dir) + if isinstance(c_template, str): + c_template = mesonlib.File.from_source_file(state.environment.source_dir, state.subdir, c_template) h_template = kwargs['h_template'] - if isinstance(h_template, mesonlib.File): - h_template = h_template.absolute_path(state.environment.source_dir, state.environment.build_dir) + if isinstance(h_template, str): + h_template = mesonlib.File.from_source_file(state.environment.source_dir, state.subdir, h_template) cmd: T.List[str] = [] known_kwargs = ['comments', 'eprod', 'fhead', 'fprod', 'ftail', @@ -1879,28 +1887,27 @@ def mkenums(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'MkEnums') - if kwargs[arg]: # type: ignore cmd += ['--' + arg.replace('_', '-'), kwargs[arg]] # type: ignore + sources = self.interpreter.source_strings_to_files(kwargs['sources']) targets: T.List[CustomTarget] = [] h_target: T.Optional[CustomTarget] = None if h_template is not None: - h_output = os.path.basename(os.path.splitext(h_template)[0]) + h_output = os.path.basename(os.path.splitext(h_template.fname)[0]) # We always set template as the first element in the source array # so --template consumes it. h_cmd = cmd + ['--template', '@INPUT@'] - h_sources: T.List[T.Union[FileOrString, 'build.GeneratedTypes']] = [h_template] - h_sources.extend(kwargs['sources']) + h_sources: T.List[CustomTargetSources] = [h_template, *sources] h_target = self._make_mkenum_impl( state, h_sources, h_output, h_cmd, install=kwargs['install_header'], install_dir=kwargs['install_dir']) targets.append(h_target) if c_template is not None: - c_output = os.path.basename(os.path.splitext(c_template)[0]) + c_output = os.path.basename(os.path.splitext(c_template.fname)[0]) # We always set template as the first element in the source array # so --template consumes it. c_cmd = cmd + ['--template', '@INPUT@'] - c_sources: T.List[T.Union[FileOrString, 'build.GeneratedTypes']] = [c_template] - c_sources.extend(kwargs['sources']) + c_sources: T.List[CustomTargetSources] = [c_template, *sources] depends = kwargs['depends'].copy() if h_target is not None: @@ -1912,7 +1919,7 @@ def mkenums(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'MkEnums') - if c_template is None and h_template is None: generic_cmd = cmd + ['@INPUT@'] target = self._make_mkenum_impl( - state, kwargs['sources'], basename, generic_cmd, + state, sources, basename, generic_cmd, install=kwargs['install_header'], install_dir=kwargs['install_dir']) return ModuleReturnValue(target, [target]) @@ -1997,7 +2004,8 @@ def mkenums_simple(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'MkEn }''')) c_cmd.append('@INPUT@') - c_file = self._make_mkenum_impl(state, kwargs['sources'], body_filename, c_cmd) + sources = self.interpreter.source_strings_to_files(kwargs['sources']) + c_file = self._make_mkenum_impl(state, sources, body_filename, c_cmd) # .h file generation h_cmd = cmd.copy() @@ -2039,7 +2047,7 @@ def mkenums_simple(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'MkEn h_cmd.append('@INPUT@') h_file = self._make_mkenum_impl( - state, kwargs['sources'], hdr_filename, h_cmd, + state, sources, hdr_filename, h_cmd, install=kwargs['install_header'], install_dir=kwargs['install_dir']) @@ -2048,7 +2056,7 @@ def mkenums_simple(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'MkEn def _make_mkenum_impl( self, state: 'ModuleState', - sources: T.Sequence[T.Union[str, mesonlib.File, build.GeneratedTypes]], + sources: T.Sequence[CustomTargetSources], output: str, cmd: T.List[str], *, @@ -2098,7 +2106,7 @@ def _make_mkenum_impl( ) def genmarshal(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'GenMarshal') -> ModuleReturnValue: output = args[0] - sources = kwargs['sources'] + sources = self.interpreter.source_strings_to_files(kwargs['sources']) new_genmarshal = mesonlib.version_compare(self._get_native_glib_version(state), '>= 2.53.3') @@ -2234,9 +2242,10 @@ def _get_vapi_link_with(self, target: CustomTarget) -> T.List[build.LibTypes]: INSTALL_DIR_KW, KwargInfo( 'sources', - ContainerTypeInfo(list, (str, GirTarget), allow_empty=False), + ContainerTypeInfo(list, (str, mesonlib.File, GirTarget), allow_empty=False), listify=True, required=True, + since_values={ContainerTypeInfo(list, (mesonlib.File), allow_empty=False): '1.12.0'}, ), KwargInfo('vapi_dirs', ContainerTypeInfo(list, str), listify=True, default=[]), KwargInfo('metadata_dirs', ContainerTypeInfo(list, str), listify=True, default=[]), @@ -2256,18 +2265,20 @@ def generate_vapi(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'Gener cmd += pkg_cmd cmd += ['--metadatadir=' + source_dir] - inputs = kwargs['sources'] - + inputs: T.List[T.Union[mesonlib.File, GirTarget]] = [] link_with: T.List[build.LibTypes] = [] - for i in inputs: + i: CustomTargetInputs + for i in kwargs['sources']: if isinstance(i, str): cmd.append(os.path.join(source_dir, i)) + i = mesonlib.File.from_source_file(state.environment.source_dir, state.subdir, i) elif isinstance(i, GirTarget): link_with += self._get_vapi_link_with(i) subdir = os.path.join(state.environment.get_build_dir(), i.get_subdir()) gir_file = os.path.join(subdir, i.get_outputs()[0]) cmd.append(gir_file) + inputs.append(i) vapi_output = library + '.vapi' datadir = state.environment.coredata.optstore.get_value_for(OptionKey('datadir')) From 68f11883c9bab4c9b736e4caa00a34e0bfd200ab Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 10 Mar 2026 16:58:13 +0100 Subject: [PATCH 050/681] modules: i18n: do not pass strings to CustomTargets Signed-off-by: Paolo Bonzini --- mesonbuild/modules/i18n.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/mesonbuild/modules/i18n.py b/mesonbuild/modules/i18n.py index db728dbeaa69..25e9244610ce 100644 --- a/mesonbuild/modules/i18n.py +++ b/mesonbuild/modules/i18n.py @@ -27,15 +27,14 @@ from . import ModuleState from ..build import Target from ..interpreter import Interpreter + from ..interpreter.interpreter import CustomTargetSources + from ..interpreter.kwargs import CustomTargetInputs from ..interpreterbase import TYPE_var from ..programs import Program class MergeFile(TypedDict): - input: T.List[T.Union[ - str, build.BuildTarget, build.CustomTarget, build.CustomTargetIndex, - build.ExtractedObjects, build.GeneratedList, Program, - mesonlib.File]] + input: T.List[CustomTargetInputs] output: str build_by_default: bool install: bool @@ -57,9 +56,7 @@ class Gettext(TypedDict): class ItsJoinFile(TypedDict): - input: T.List[T.Union[ - str, build.BuildTarget, build.GeneratedTypes, - build.ExtractedObjects, Program, mesonlib.File]] + input: T.List[CustomTargetInputs] output: str build_by_default: bool install: bool @@ -329,13 +326,14 @@ def merge_file(self, state: 'ModuleState', args: T.List['TYPE_var'], kwargs: 'Me install_tag = [kwargs['install_tag']] if kwargs['install_tag'] is not None else None + inputs: T.List[CustomTargetSources] = self.interpreter.source_strings_to_files(kwargs['input']) ct = build.CustomTarget( '', state.subdir, state.subproject, state.environment, command, - kwargs['input'], + inputs, [kwargs['output']], build_by_default=build_by_default, install=kwargs['install'], @@ -516,13 +514,14 @@ def itstool_join(self, state: 'ModuleState', args: T.List['TYPE_var'], kwargs: ' install_tag = [kwargs['install_tag']] if kwargs['install_tag'] is not None else None + inputs: T.List[CustomTargetSources] = self.interpreter.source_strings_to_files(kwargs['input']) ct = build.CustomTarget( '', state.subdir, state.subproject, state.environment, command, - kwargs['input'], + inputs, [kwargs['output']], build_by_default=build_by_default, extra_depends=mo_targets, From b7fade8db63141391fd06faca78b91b67c24375f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 10 Mar 2026 16:58:13 +0100 Subject: [PATCH 051/681] modules: qt: do not pass strings to CustomTargets Signed-off-by: Paolo Bonzini --- mesonbuild/modules/_qt.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/mesonbuild/modules/_qt.py b/mesonbuild/modules/_qt.py index a9c2edbc4920..8397ccbfc497 100644 --- a/mesonbuild/modules/_qt.py +++ b/mesonbuild/modules/_qt.py @@ -26,6 +26,7 @@ from ..dependencies.base import DependencyObjectKWs from ..interpreter import Interpreter from ..interpreter import kwargs + from ..interpreter.interpreter import CustomTargetSources from ..mesonlib import FileOrString from ..programs import CommandList, Program from typing_extensions import Literal @@ -428,9 +429,11 @@ def _compile_resources_impl(self, state: 'ModuleState', kwargs: 'ResourceCompile DEPFILE_ARGS: T.List[str] = ['--depfile', '@DEPFILE@'] if self._rcc_supports_depfiles else [] name = kwargs['name'] - sources: T.List['FileOrString'] = [] + sources: T.List[File] = [] for s in kwargs['sources']: - if isinstance(s, (str, File)): + if isinstance(s, str): + sources.append(File.from_source_file(state.environment.source_dir, state.subdir, s)) + elif isinstance(s, File): sources.append(s) else: # TODO: this could be fixed with dyndeps @@ -464,10 +467,7 @@ def _compile_resources_impl(self, state: 'ModuleState', kwargs: 'ResourceCompile else: for rcc_file in sources: qrc_deps = self._parse_qrc_deps(state, rcc_file) - if isinstance(rcc_file, str): - basename = os.path.basename(rcc_file) - else: - basename = os.path.basename(rcc_file.fname) + basename = os.path.basename(rcc_file.fname) name = f'qt{self.qt_version}-{basename.replace(".", "_")}' cmd = [self.tools['rcc'], '-name', '@BASENAME@', '-o', '@OUTPUT@', *extra_args, '@INPUT@', *DEPFILE_ARGS] res_target = build.CustomTarget( @@ -725,6 +725,7 @@ def compile_translations(self, state: ModuleState, args: T.Tuple, kwargs: Compil if not self.tools['lrelease'].found(): raise MesonException('qt.compile_translations: ' + self.tools['lrelease'].name + ' not found') + ts_file: CustomTargetSources if qresource: # In this case we know that ts_files is always a List[str], as # it's generated above and no ts_files are passed in. However, @@ -732,9 +733,13 @@ def compile_translations(self, state: ModuleState, args: T.Tuple, kwargs: Compil # what we're doing is safe assert isinstance(ts, str), 'for mypy' outdir = os.path.dirname(os.path.normpath(os.path.join(state.subdir, ts))) - ts = os.path.basename(ts) + ts_file = File.from_source_file(state.environment.source_dir, outdir, os.path.basename(ts)) + elif isinstance(ts, str): + outdir = state.subdir + ts_file = File.from_source_file(state.environment.source_dir, state.subdir, ts) else: outdir = state.subdir + ts_file = ts cmd: CommandList = [self.tools['lrelease'], '@INPUT@', '-qm', '@OUTPUT@'] lrelease_target = build.CustomTarget( f'qt{self.qt_version}-compile-{ts}', @@ -742,7 +747,7 @@ def compile_translations(self, state: ModuleState, args: T.Tuple, kwargs: Compil state.subproject, state.environment, cmd, - [ts], + [ts_file], ['@BASENAME@.qm'], install=kwargs['install'], install_dir=[kwargs['install_dir']], From 0ba579401070d193dbb46d5fd11f99d0cd939036 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 10 Mar 2026 17:14:06 +0100 Subject: [PATCH 052/681] build: use different variable for command vs. custom target input "j" is used in an earlier loop iterating target.sources. Before changing the type of the sources, rename the variable. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/backends.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 7494b1ac19ba..4e923692f8b4 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -1967,17 +1967,17 @@ def get_introspection_data(self, target_id: str, target: build.Target) -> T.List compiler: T.List[str] = [] if isinstance(target, build.CustomTarget): tmp_compiler = target.command - for j in tmp_compiler: - if isinstance(j, mesonlib.File): - compiler += [j.absolute_path(self.source_dir, self.build_dir)] - elif isinstance(j, str): - compiler += [j] - elif isinstance(j, (build.BuildTarget, build.CustomTarget)): - compiler += j.get_outputs() - elif isinstance(j, programs.Program): - compiler += j.get_command() + for k in tmp_compiler: + if isinstance(k, mesonlib.File): + compiler += [k.absolute_path(self.source_dir, self.build_dir)] + elif isinstance(k, str): + compiler += [k] + elif isinstance(k, (build.BuildTarget, build.CustomTarget)): + compiler += k.get_outputs() + elif isinstance(k, programs.Program): + compiler += k.get_command() else: - raise RuntimeError(f'Type "{type(j).__name__}" is not supported in get_introspection_data. This is a bug') + raise RuntimeError(f'Type "{type(k).__name__}" is not supported in get_introspection_data. This is a bug') return [{ 'language': 'unknown', From 77a45ab1d322ad661655493fd920acef310f0964 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 11 Mar 2026 08:45:36 +0100 Subject: [PATCH 053/681] build: remove str from CustomTarget sources Signed-off-by: Paolo Bonzini --- mesonbuild/backend/backends.py | 2 -- mesonbuild/backend/xcodebackend.py | 44 +++++++++--------------------- mesonbuild/build.py | 10 ++----- 3 files changed, 16 insertions(+), 40 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 4e923692f8b4..91b0807e10f0 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -1958,8 +1958,6 @@ def get_introspection_data(self, target_id: str, target: build.Target) -> T.List for j in source_list_raw: if isinstance(j, mesonlib.File): source_list += [j.absolute_path(self.source_dir, self.build_dir)] - elif isinstance(j, str): - source_list += [os.path.join(self.source_dir, target.subdir, j)] elif isinstance(j, (build.CustomTarget, build.BuildTarget)): source_list += [os.path.join(self.build_dir, j.get_builddir(), o) for o in j.get_outputs()] source_list = [os.path.normpath(s) for s in source_list] diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index dae1f9b99011..62ca85615891 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -591,10 +591,9 @@ def generate_target_file_maps(self) -> None: def generate_target_file_maps_impl(self, targets) -> None: for tname, t in targets.items(): for s in t.sources: - if isinstance(s, mesonlib.File): - s = os.path.join(s.subdir, s.fname) - if not isinstance(s, str): + if not isinstance(s, mesonlib.File): continue + s = os.path.join(s.subdir, s.fname) k = (tname, s) assert k not in self.buildfile_ids self.buildfile_ids[k] = self.gen_id() @@ -724,14 +723,10 @@ def generate_pbx_build_file(self, objects_dict: PbxDict) -> None: fw_dict.add_item('fileRef', self.native_frameworks_fileref[f], f) for s in t.sources: - in_build_dir = False - if isinstance(s, mesonlib.File): - if s.is_built: - in_build_dir = True - s = os.path.join(s.subdir, s.fname) - - if not isinstance(s, str): + if not isinstance(s, mesonlib.File): continue + in_build_dir = s.is_built + s = os.path.join(s.subdir, s.fname) sdict = PbxDict() k = (tname, s) idval = self.buildfile_ids[k] @@ -884,13 +879,10 @@ def generate_pbx_file_reference(self, objects_dict: PbxDict) -> None: fw_dict.add_item('path', f'System/Library/Frameworks/{f}.framework') fw_dict.add_item('sourceTree', 'SDKROOT') for s in t.sources: - in_build_dir = False - if isinstance(s, mesonlib.File): - if s.is_built: - in_build_dir = True - s = os.path.join(s.subdir, s.fname) - if not isinstance(s, str): + if not isinstance(s, mesonlib.File): continue + in_build_dir = s.is_built + s = os.path.join(s.subdir, s.fname) idval = self.fileref_ids[(tname, s)] fullpath = os.path.join(self.environment.get_source_dir(), s) src_dict = PbxDict() @@ -999,12 +991,9 @@ def generate_pbx_file_reference(self, objects_dict: PbxDict) -> None: continue (srcs, ofilenames, cmd) = self.eval_custom_target_command(t) for s in t.sources: - if isinstance(s, mesonlib.File): - s = os.path.join(s.subdir, s.fname) - elif isinstance(s, str): - s = os.path.join(t.subdir, s) - else: + if not isinstance(s, mesonlib.File): continue + s = os.path.join(s.subdir, s.fname) custom_dict = PbxDict() typestr = self.get_xcodetype(s) custom_dict.add_item('isa', 'PBXFileReference') @@ -1137,12 +1126,9 @@ def generate_pbx_group(self, objects_dict: PbxDict) -> None: source_file_children = PbxArray() source_files_dict.add_item('children', source_file_children) for s in t.sources: - if isinstance(s, mesonlib.File): - s = os.path.join(s.subdir, s.fname) - elif isinstance(s, str): - s = os.path.join(t.subdir, s) - else: + if not isinstance(s, mesonlib.File): continue + s = os.path.join(s.subdir, s.fname) source_file_children.add_item(self.fileref_ids[(tname, s)], s) source_files_dict.add_item('name', 'Source files') source_files_dict.add_item('sourceTree', '') @@ -1185,11 +1171,7 @@ def write_group_target_entry(self, objects_dict: PbxDict, t) -> str: folder_ids.add(self.foldermap[(folder, t)]) continue s = os.path.join(s.subdir, s.fname) - elif isinstance(s, str): - s = os.path.join(t.subdir, s) - else: - continue - target_children.add_item(self.fileref_ids[(tid, s)], s) + target_children.add_item(self.fileref_ids[(tid, s)], s) for o in t.objects: if isinstance(o, build.ExtractedObjects): # Do not show built object files in the project tree. diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 321f152b827f..45c0ad860710 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -44,7 +44,7 @@ from ._typing import ImmutableListProtocol from .backend.backends import Backend from .compilers.compilers import Compiler, CompilerDict, Language - from .interpreter.interpreter import SourceOutputs, Interpreter + from .interpreter.interpreter import CustomTargetSources, SourceOutputs, Interpreter from .interpreter.interpreterobjects import Test, Doctest from .linkers.linkers import StaticLinker from .mesonlib import ExecutableSerialisation, FileMode, FileOrString @@ -2993,9 +2993,7 @@ def __init__(self, command: T.Sequence[T.Union[ str, BuildTargetTypes, GeneratedList, programs.Program, File]], - sources: T.Sequence[T.Union[ - str, File, BuildTargetTypes, ExtractedObjects, - GeneratedList, programs.Program]], + sources: T.Sequence[CustomTargetSources], outputs: T.List[str], *, build_always_stale: bool = False, @@ -3593,9 +3591,7 @@ def get_sources_string_names(sources, backend): ''' names = [] for s in sources: - if isinstance(s, str): - names.append(s) - elif isinstance(s, (BuildTarget, CustomTarget, CustomTargetIndex, GeneratedList)): + if isinstance(s, (BuildTarget, CustomTarget, CustomTargetIndex, GeneratedList)): names += s.get_outputs() elif isinstance(s, ExtractedObjects): names += backend.determine_ext_objs(s) From 4a66eeefd96877cf5b92230cbe9eb500f743dbc5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 4 Mar 2026 16:39:47 +0100 Subject: [PATCH 054/681] xcodebackend: cleanup code Signed-off-by: Paolo Bonzini --- mesonbuild/backend/xcodebackend.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index 62ca85615891..60a28afcb36a 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -1157,21 +1157,22 @@ def write_group_target_entry(self, objects_dict: PbxDict, t) -> str: target_dict.add_item('sourceTree', '') source_files_dict = PbxDict() for s in t.sources: - if isinstance(s, mesonlib.File): - # If the file is in a folder, add it to the group representing that folder. - if '/' in s.fname: - folder = '/'.join(s.fname.split('/')[:-1]) - folder_dict = objects_dict.get_item(self.foldermap[(folder, t)]).value.get_item('children').value - temp = os.path.join(s.subdir, s.fname) - folder_dict.add_item(self.fileref_ids[(tid, temp)], temp) - if self.foldermap[(folder, t)] in folder_ids: - continue - if len(folder.split('/')) == 1: - target_children.add_item(self.foldermap[(folder, t)], folder) - folder_ids.add(self.foldermap[(folder, t)]) + if not isinstance(s, mesonlib.File): + continue + # If the file is in a folder, add it to the group representing that folder. + if '/' in s.fname: + folder = '/'.join(s.fname.split('/')[:-1]) + folder_dict = objects_dict.get_item(self.foldermap[(folder, t)]).value.get_item('children').value + temp = os.path.join(s.subdir, s.fname) + folder_dict.add_item(self.fileref_ids[(tid, temp)], temp) + if self.foldermap[(folder, t)] in folder_ids: continue - s = os.path.join(s.subdir, s.fname) - target_children.add_item(self.fileref_ids[(tid, s)], s) + if len(folder.split('/')) == 1: + target_children.add_item(self.foldermap[(folder, t)], folder) + folder_ids.add(self.foldermap[(folder, t)]) + continue + s = os.path.join(s.subdir, s.fname) + target_children.add_item(self.fileref_ids[(tid, s)], s) for o in t.objects: if isinstance(o, build.ExtractedObjects): # Do not show built object files in the project tree. From c005d15b460612b57347860d09d8ceb08386d299 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 11 Mar 2026 13:09:06 +0100 Subject: [PATCH 055/681] gnome: allow custom targets as a source for gnome.genmarshal The function is using source_strings_to_files, and that makes it trivial to pass generated sources to the CustomTarget constructor. Fixes: #2748 Signed-off-by: Paolo Bonzini --- docs/markdown/Gnome-module.md | 2 +- mesonbuild/modules/gnome.py | 7 +++++-- test cases/frameworks/7 gnome/genmarshal/meson.build | 4 +++- test cases/frameworks/7 gnome/meson.build | 1 + test cases/frameworks/7 gnome/test.json | 2 ++ 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/docs/markdown/Gnome-module.md b/docs/markdown/Gnome-module.md index d97eb11d0d7a..af759df8c3f2 100644 --- a/docs/markdown/Gnome-module.md +++ b/docs/markdown/Gnome-module.md @@ -144,7 +144,7 @@ argument is the basename of the output files. * `nostdinc`: if true, don't include the standard marshallers from glib * `prefix`: the prefix to use for symbols * `skip_source`: if true, skip source location comments -* `sources` [](str | File) *required*: the list of sources to use as inputs +* `sources` [](str | File | CustomTarget | CustomTargetIndex | generator_output) *required*: the list of sources to use as inputs (build-time generated files only allowed *since 1.12.0*) * `stdinc`: if true, include the standard marshallers from glib * `valist_marshallers`: if true, generate va_list marshallers diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index cf4280bd257b..02e3034863eb 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -159,7 +159,7 @@ class GenMarshal(TypedDict): nostdinc: bool prefix: T.Optional[str] skip_source: bool - sources: T.List[FileOrString] + sources: T.List[CustomTargetInputs] stdinc: bool valist_marshallers: bool @@ -2100,7 +2100,10 @@ def _make_mkenum_impl( KwargInfo('nostdinc', bool, default=False), KwargInfo('prefix', (str, NoneType)), KwargInfo('skip_source', bool, default=False), - KwargInfo('sources', ContainerTypeInfo(list, (str, mesonlib.File), allow_empty=False), listify=True, required=True), + KwargInfo('sources', ContainerTypeInfo(list, (str, mesonlib.File, CustomTarget, CustomTargetIndex, GeneratedList), allow_empty=False), listify=True, required=True, + since_values={ContainerTypeInfo(list, (CustomTarget, CustomTargetIndex, GeneratedList), allow_empty=False): '1.12.0'}, + + ), KwargInfo('stdinc', bool, default=False), KwargInfo('valist_marshallers', bool, default=False), ) diff --git a/test cases/frameworks/7 gnome/genmarshal/meson.build b/test cases/frameworks/7 gnome/genmarshal/meson.build index 9a2cd7a0007d..6ece0bcbed0c 100644 --- a/test cases/frameworks/7 gnome/genmarshal/meson.build +++ b/test cases/frameworks/7 gnome/genmarshal/meson.build @@ -2,8 +2,10 @@ m_list = configure_file(input: 'marshaller.list', output: 'm.list', copy: true) +ct_list = fs.copyfile('marshaller.list', 'ct.list') + idx = 0 -mlists = ['marshaller.list', files('marshaller.list'), m_list] +mlists = ['marshaller.list', files('marshaller.list'), m_list, ct_list] foreach mlist : mlists marshallers = gnome.genmarshal('marshaller-@0@'.format(idx), diff --git a/test cases/frameworks/7 gnome/meson.build b/test cases/frameworks/7 gnome/meson.build index b22738912de5..211685e4323f 100644 --- a/test cases/frameworks/7 gnome/meson.build +++ b/test cases/frameworks/7 gnome/meson.build @@ -43,6 +43,7 @@ if res.returncode() == 0 endif gnome = import('gnome') +fs = import('fs') gio = dependency('gio-2.0') giounix = dependency('gio-unix-2.0') glib = dependency('glib-2.0') diff --git a/test cases/frameworks/7 gnome/test.json b/test cases/frameworks/7 gnome/test.json index a1fcf3e29e21..786b3e11cf2b 100644 --- a/test cases/frameworks/7 gnome/test.json +++ b/test cases/frameworks/7 gnome/test.json @@ -12,6 +12,8 @@ {"type": "file", "file": "usr/include/subdir-3/marshaller-3.h"}, {"type": "file", "file": "usr/include/subdir-4/marshaller-4.h"}, {"type": "file", "file": "usr/include/subdir-5/marshaller-5.h"}, + {"type": "file", "file": "usr/include/subdir-6/marshaller-6.h"}, + {"type": "file", "file": "usr/include/subdir-7/marshaller-7.h"}, {"type": "expr", "file": "usr/lib/?libpython_gir_lib.so"}, {"type": "file", "platform": "cygwin", "file": "usr/lib/libpython_gir_lib.dll.a"}, {"type": "expr", "file": "usr/lib/?libgir_lib.so"}, From a5c0e85c3a737563de916f28c4e32348304a2183 Mon Sep 17 00:00:00 2001 From: Paul Caprioli Date: Fri, 20 Jun 2025 16:47:38 -0700 Subject: [PATCH 056/681] docs: Add FAQ entry about build target destination (Extracted from Paul's patch in #14734). --- docs/markdown/FAQ.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/markdown/FAQ.md b/docs/markdown/FAQ.md index 9be653e3baf4..df81dbde7d08 100644 --- a/docs/markdown/FAQ.md +++ b/docs/markdown/FAQ.md @@ -729,3 +729,8 @@ Even though [MSVC documentation](https://learn.microsoft.com/en-us/cpp/build/ref uses `/D` for preprocessor defines, its [command-line syntax](https://learn.microsoft.com/en-us/cpp/build/reference/compiler-command-line-syntax) accepts `-` instead of `/`. It's not necessary to treat preprocessor defines specially in Meson ([GH-6269](https://github.com/mesonbuild/meson/issues/6269#issuecomment-560003922)). + +## Why do all outputs go to the build directory that corresponds to the source directory in which they are declared? + +This simplifies debugging build problems, since you always know that files in +build directory X must come from the corresponding source directory X. From 958d3b2a3e64251e89a0b1b132993405717929f9 Mon Sep 17 00:00:00 2001 From: Paul Caprioli Date: Fri, 20 Jun 2025 16:47:38 -0700 Subject: [PATCH 057/681] modules: fs: Add build_subdir argument to fs.copyfile() Co-developed-by: Paul Caprioli --- docs/markdown/Fs-module.md | 11 +++++++---- docs/markdown/snippets/fs-copyfile-build-subdir.md | 4 ++++ docs/yaml/functions/configure_file.yaml | 6 +++++- mesonbuild/interpreter/type_checking.py | 9 ++++++++- mesonbuild/modules/fs.py | 6 +++++- 5 files changed, 29 insertions(+), 7 deletions(-) create mode 100644 docs/markdown/snippets/fs-copyfile-build-subdir.md diff --git a/docs/markdown/Fs-module.md b/docs/markdown/Fs-module.md index 91c706eb9de6..abff9bcad987 100644 --- a/docs/markdown/Fs-module.md +++ b/docs/markdown/Fs-module.md @@ -186,7 +186,7 @@ new = fs.replace_suffix(original, '') # /opt/foo.dll ### parent -Returns the parent directory (i.e. dirname). +Returns the parent directory (i.e., dirname). ```meson new = fs.parent('foo/bar') # foo @@ -195,7 +195,7 @@ new = fs.parent('foo/bar/baz.dll') # foo/bar ### name -Returns the last component of the path (i.e. basename). +Returns the last component of the path (i.e., basename). ```meson fs.name('foo/bar/baz.dll.a') # baz.dll.a @@ -256,20 +256,23 @@ returns: *Since 0.64.0* -Copy a file from the source directory to the build directory at build time +Copy a file from the source directory to the build directory at build time. Has the following positional arguments: - src `File | str`: the file to copy Has the following optional arguments: - dest `str`: the name of the output file. If unset will be the basename of - the src argument + the src argument (i.e., the last component of its path). Has the following keyword arguments: - install `bool`: Whether to install the copied file, defaults to false - install_dir `str`: Where to install the file to - install_tag: `str`: the install tag to assign to this target - install_mode `array[str | int]`: the mode to install the file with + - build_subdir `str`: *since 1.12.0*. Places the build results in a subdirectory + of the given name rather than directly into the build directory. + For more information, see [[custom_target]]. returns: - a [[custom_target]] object diff --git a/docs/markdown/snippets/fs-copyfile-build-subdir.md b/docs/markdown/snippets/fs-copyfile-build-subdir.md new file mode 100644 index 000000000000..f1664256a42c --- /dev/null +++ b/docs/markdown/snippets/fs-copyfile-build-subdir.md @@ -0,0 +1,4 @@ +## `fs.copyfile()` now has a `build_subdir` argument + +`fs.copyfile()`'s new `build_subdir` argument allows creating a file +inside a subdirectory of the current build directory. diff --git a/docs/yaml/functions/configure_file.yaml b/docs/yaml/functions/configure_file.yaml index 32cb55964ec5..050e863ee989 100644 --- a/docs/yaml/functions/configure_file.yaml +++ b/docs/yaml/functions/configure_file.yaml @@ -20,7 +20,11 @@ description: | *(since 0.47.0)* When the `copy:` keyword argument is set to `true`, this function will copy the file provided in `input:` to a file in the - build directory with the name `output:` in the current directory. + current build directory with the name provided in `output:`. + The copy happens at configuration. If the input file is subsequently + modified, Meson will regenerate the build files. + To perform the copy at build time (with the usual dependency on the source + file) use `copyfile()` from the FS (filesystem) module. warnings: - the `install_mode` kwarg ignored integer values between 0.62 -- 1.1.0. diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py index 814aa0fde1db..ef6c4cdbb7e4 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -580,6 +580,13 @@ def suite_convertor(suite: T.List[str]) -> T.List[str]: deprecated_message='This does not, and never has, done anything. It should be removed' ) +BUILD_SUBDIR_KW: KwargInfo[str] = KwargInfo( + 'build_subdir', + str, + default='', + since='1.10.0' +) + def _objects_validator(vals: T.List[ObjectTypes]) -> T.Optional[str]: non_objects: T.List[str] = [] @@ -665,7 +672,7 @@ def _bt_install_dir_deprecated(args: T.List[T.Union[str, bool]]) -> T.Iterator[F ('1.1.0', 'generated sources as positional "objects" arguments') }, ), - KwargInfo('build_subdir', str, default='', since='1.10.0') + BUILD_SUBDIR_KW, ] diff --git a/mesonbuild/modules/fs.py b/mesonbuild/modules/fs.py index 57a6b6dc8eda..ba7449026fcb 100644 --- a/mesonbuild/modules/fs.py +++ b/mesonbuild/modules/fs.py @@ -12,7 +12,8 @@ from . import ExtensionModule, ModuleReturnValue, ModuleInfo from .. import mlog from ..build import BuildTarget, CustomTarget, CustomTargetIndex, InvalidArguments -from ..interpreter.type_checking import INSTALL_KW, INSTALL_MODE_KW, INSTALL_TAG_KW, NoneType +from ..interpreter.type_checking import INSTALL_KW, INSTALL_MODE_KW, INSTALL_TAG_KW, \ + BUILD_SUBDIR_KW, NoneType from ..interpreterbase import FeatureNew, KwargInfo, typed_kwargs, typed_pos_args, noKwargs from ..mesonlib import File, MesonException, has_path_sep, is_windows, path_is_in_root, relpath @@ -34,6 +35,7 @@ class CopyKw(TypedDict): """Kwargs for fs.copy""" + build_subdir: str install: bool install_dir: T.Optional[str] install_mode: FileMode @@ -285,6 +287,7 @@ def read(self, state: ModuleState, args: T.Tuple[FileOrString], kwargs: ReadKwAr INSTALL_MODE_KW, INSTALL_TAG_KW, KwargInfo('install_dir', (str, NoneType)), + BUILD_SUBDIR_KW.evolve(since='1.12.0'), ) def copyfile(self, state: ModuleState, args: T.Tuple[FileOrString, T.Optional[str]], kwargs: CopyKw) -> ModuleReturnValue: @@ -315,6 +318,7 @@ def copyfile(self, state: ModuleState, args: T.Tuple[FileOrString, T.Optional[st install_tag=[kwargs['install_tag']], backend=state.backend, description='Copying file {}', + build_subdir=kwargs['build_subdir'], ) return ModuleReturnValue(ct, [ct]) From 3d7600790a33abe2d5b1a04bc5c65bab7d79674f Mon Sep 17 00:00:00 2001 From: Rachel Date: Wed, 17 Dec 2025 17:36:05 -0800 Subject: [PATCH 058/681] Support depends in generator.process --- .../snippets/generator_process_depends.md | 4 +++ docs/yaml/objects/generator.yaml | 8 +++++ mesonbuild/backend/vs2010backend.py | 4 +++ mesonbuild/backend/xcodebackend.py | 2 ++ mesonbuild/build.py | 11 +++++-- mesonbuild/interpreter/interpreterobjects.py | 6 ++-- mesonbuild/interpreter/kwargs.py | 1 + .../generate_config.py | 11 +++++++ .../generate_main.py | 7 +++++ .../main.config.ini | 2 ++ .../main.template | 5 ++++ .../meson.build | 30 +++++++++++++++++++ 12 files changed, 86 insertions(+), 5 deletions(-) create mode 100644 docs/markdown/snippets/generator_process_depends.md create mode 100644 test cases/common/295 depends in generator.process/generate_config.py create mode 100644 test cases/common/295 depends in generator.process/generate_main.py create mode 100644 test cases/common/295 depends in generator.process/main.config.ini create mode 100644 test cases/common/295 depends in generator.process/main.template create mode 100644 test cases/common/295 depends in generator.process/meson.build diff --git a/docs/markdown/snippets/generator_process_depends.md b/docs/markdown/snippets/generator_process_depends.md new file mode 100644 index 000000000000..c9c98b72a64f --- /dev/null +++ b/docs/markdown/snippets/generator_process_depends.md @@ -0,0 +1,4 @@ +## Added `depends` kwarg to `generator.process()` + +The [[generator.process]] method now supports specifying `depends` as a kwarg +to extend the list of dependencies for generated files. diff --git a/docs/yaml/objects/generator.yaml b/docs/yaml/objects/generator.yaml index 3dbcdd5d93d5..55bf1ed6f3f9 100644 --- a/docs/yaml/objects/generator.yaml +++ b/docs/yaml/objects/generator.yaml @@ -43,3 +43,11 @@ methods: `{'NAME1': 'value1', 'NAME2': 'value2'}` or `['NAME1=value1', 'NAME2=value2']`, or an [[@env]] object which allows more sophisticated environment juggling. + + depends: + type: array[build_tgt | custom_tgt | custom_idx] + since: 1.12.0 + description: | + An array of build targets that must be built before attempting to run this + generator against the source files. This extends the `depends` value of the + generator to be run. diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 6e71c41a5204..cb3bf78fb184 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -186,6 +186,10 @@ def generate_genlist_for_target(self, genlist: build.GeneratedTypes, target: bui sole_output = '' infilename = os.path.join(down, curfile.rel_to_builddir(self.build_to_src, self.get_target_private_dir(target))) deps = self.get_target_depend_files(genlist, True) + for d in genlist.extra_depends: + # Add a dependency on all the outputs of this target + for output in d.get_outputs(): + deps.append(os.path.join(self.get_target_dir(d), output)) 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] diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index 60a28afcb36a..bc66baf9d3ce 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -528,6 +528,8 @@ def gen_single_target_map(self, genlist: build.GeneratedList, tname: str, t: build.AnyTargetType, generator_id: int) -> None: k = (tname, generator_id) assert k not in self.shell_targets + if genlist.extra_depends: + raise NotImplementedError('XCode backend does not support depends for generator.process') self.shell_targets[k] = self.gen_id() ofile_abs = [] for i in genlist.get_inputs(): diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 45c0ad860710..6986a6c575f9 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2104,13 +2104,15 @@ def process_files(self, files: T.Iterable[T.Union[str, File, GeneratedTypes]], subdir: str = '', preserve_path_from: T.Optional[str] = None, extra_args: T.Optional[T.List[str]] = None, - env: T.Optional[EnvironmentVariables] = None) -> 'GeneratedList': + env: T.Optional[EnvironmentVariables] = None, + extra_depends: T.Optional[T.List[GeneratedTypes]] = None) -> 'GeneratedList': output = GeneratedList( self, subdir, preserve_path_from, extra_args=extra_args if extra_args is not None else [], - env=env if env is not None else EnvironmentVariables()) + env=env if env is not None else EnvironmentVariables(), + extra_depends=extra_depends if extra_depends is not None else []) for e in files: if isinstance(e, (CustomTarget, CustomTargetIndex)): @@ -2146,6 +2148,7 @@ class GeneratedList(HoldableObject): preserve_path_from: T.Optional[str] extra_args: T.List[str] env: T.Optional[EnvironmentVariables] + extra_depends: T.List[GeneratedTypes] def __post_init__(self) -> None: self.name = self.generator.exe @@ -2153,7 +2156,6 @@ def __post_init__(self) -> None: self.infilelist: T.List[FileMaybeInTargetPrivateDir] = [] self.outfilelist: T.List[str] = [] self.outmap: T.Dict[FileMaybeInTargetPrivateDir, T.List[str]] = {} - self.extra_depends: T.List[BuildTargetTypes] = [] self.depend_files: T.List[File] = [] if self.extra_args is None: @@ -2162,6 +2164,9 @@ def __post_init__(self) -> None: if self.env is None: self.env: EnvironmentVariables = EnvironmentVariables() + if self.extra_depends is None: + self.extra_depends: T.List[GeneratedTypes] = [] + if isinstance(self.generator.exe, programs.Program): if not self.generator.exe.found(): raise InvalidArguments('Tried to use not-found external program as generator') diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index c3bac94a40a0..0d5b8df68743 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -1200,7 +1200,8 @@ def __init__(self, gen: build.Generator, interpreter: 'Interpreter'): 'generator.process', KwargInfo('preserve_path_from', (str, NoneType), since='0.45.0'), KwargInfo('extra_args', ContainerTypeInfo(list, str), listify=True, default=[]), - ENV_KW.evolve(since='1.3.0') + ENV_KW.evolve(since='1.3.0'), + KwargInfo('depends', ContainerTypeInfo(list, (build.CustomTarget, build.CustomTargetIndex, build.GeneratedList)), listify=True, default=[], since='1.12.0'), ) @InterpreterObject.method('process') def process_method(self, @@ -1219,7 +1220,8 @@ def process_method(self, '0.57.0', self.interpreter.subproject) gl = self.held_object.process_files(args[0], self.interpreter.subdir, - preserve_path_from, extra_args=kwargs['extra_args'], env=kwargs['env']) + preserve_path_from, extra_args=kwargs['extra_args'], env=kwargs['env'], + extra_depends=kwargs['depends']) return gl diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index 7502addbecc7..813e64f28a34 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -119,6 +119,7 @@ class GeneratorProcess(TypedDict): preserve_path_from: T.Optional[str] extra_args: T.List[str] env: EnvironmentVariables + depends: T.List[build.GeneratedTypes] class DependencyMethodPartialDependency(TypedDict): diff --git a/test cases/common/295 depends in generator.process/generate_config.py b/test cases/common/295 depends in generator.process/generate_config.py new file mode 100644 index 000000000000..f8b82c400b22 --- /dev/null +++ b/test cases/common/295 depends in generator.process/generate_config.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python3 + +import configparser +import sys + +with open(sys.argv[2], 'w') as outfile: + config = configparser.ConfigParser() + config.read(sys.argv[1]) + + for key, val in config["DEFAULT"].items(): + outfile.write(f"#define {key.upper()} {val}\n") diff --git a/test cases/common/295 depends in generator.process/generate_main.py b/test cases/common/295 depends in generator.process/generate_main.py new file mode 100644 index 000000000000..c7f91514beae --- /dev/null +++ b/test cases/common/295 depends in generator.process/generate_main.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python3 +import sys + +with open(sys.argv[1], 'r') as infile, \ + open(sys.argv[2], 'w') as outfile: + + outfile.write(infile.read()) diff --git a/test cases/common/295 depends in generator.process/main.config.ini b/test cases/common/295 depends in generator.process/main.config.ini new file mode 100644 index 000000000000..1148277a42c0 --- /dev/null +++ b/test cases/common/295 depends in generator.process/main.config.ini @@ -0,0 +1,2 @@ +[DEFAULT] +CONFIG_VALUE = 0 diff --git a/test cases/common/295 depends in generator.process/main.template b/test cases/common/295 depends in generator.process/main.template new file mode 100644 index 000000000000..47361245c48d --- /dev/null +++ b/test cases/common/295 depends in generator.process/main.template @@ -0,0 +1,5 @@ +#include "main.config.h" + +int main(void) { + return CONFIG_VALUE; +} diff --git a/test cases/common/295 depends in generator.process/meson.build b/test cases/common/295 depends in generator.process/meson.build new file mode 100644 index 000000000000..c434d21e2a9b --- /dev/null +++ b/test cases/common/295 depends in generator.process/meson.build @@ -0,0 +1,30 @@ +project('test_depends_in_generator_process', 'c') + +generate_main_py = find_program('generate_main.py') +generate_config_py = find_program('generate_config.py') + +main_generator = generator( + generate_main_py, + arguments: ['@INPUT@', '@OUTPUT@'], + output: '@BASENAME@' + '.c', +) + +main_template = files('main.template') +main_config_ini = files('main.config.ini') + +my_config_h = custom_target( + 'main.config.h', + output: 'main.config.h', + input: main_config_ini, + command: [ generate_config_py, '@INPUT@', '@OUTPUT@' ], +) + +my_executable = executable( + 'myexecutable', + sources: main_generator.process( + main_template, + depends: my_config_h, + ) +) + +test('depends in generator.process', my_executable) From c5618e3f5001dcf41e5c3d4c83a00e8c454f0800 Mon Sep 17 00:00:00 2001 From: Rachel Date: Sun, 5 Apr 2026 21:15:05 -0700 Subject: [PATCH 059/681] meson_exe: Always emit stderr when it has content --- mesonbuild/scripts/meson_exe.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mesonbuild/scripts/meson_exe.py b/mesonbuild/scripts/meson_exe.py index e928e9c1ee85..1d892c252421 100644 --- a/mesonbuild/scripts/meson_exe.py +++ b/mesonbuild/scripts/meson_exe.py @@ -77,6 +77,9 @@ def run_exe(exe: ExecutableSerialisation, extra_env: T.Optional[T.Dict[str, str] print('--- stderr ---') print(stderr.decode(encoding=encoding, errors='replace')) return p.returncode + elif stderr: # Allow a wrapped subprocess to gracefully communicate warnings + encoding = locale.getpreferredencoding() + print(stderr.decode(encoding=encoding, errors='replace')) if exe.capture: skip_write = False From 74faddbb44475b0671fd1acbaedace2e8a596263 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 22 Apr 2026 12:02:24 +0400 Subject: [PATCH 060/681] modules: fs: accept build targets as fs.copyfile() source MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow CustomTarget, CustomTargetIndex, and BuildTarget as the first argument to fs.copyfile(), so that build artifacts can be copied without resorting to a manual custom_target. Co-Authored-By: Claude Opus 4.6 Signed-off-by: Marc-André Lureau --- docs/markdown/Fs-module.md | 4 +++- .../snippets/fs-copyfile-build-targets.md | 5 ++++ mesonbuild/modules/fs.py | 23 ++++++++++++++----- test cases/common/220 fs module/meson.build | 13 +++++++++++ 4 files changed, 38 insertions(+), 7 deletions(-) create mode 100644 docs/markdown/snippets/fs-copyfile-build-targets.md diff --git a/docs/markdown/Fs-module.md b/docs/markdown/Fs-module.md index abff9bcad987..7ca854ea1479 100644 --- a/docs/markdown/Fs-module.md +++ b/docs/markdown/Fs-module.md @@ -259,7 +259,9 @@ returns: Copy a file from the source directory to the build directory at build time. Has the following positional arguments: - - src `File | str`: the file to copy + - src `File | str | build_tgt | custom_tgt | custom_idx`: the file to copy. + *Since 1.12.0* build targets, custom targets, and custom target indices are + also accepted. Has the following optional arguments: - dest `str`: the name of the output file. If unset will be the basename of diff --git a/docs/markdown/snippets/fs-copyfile-build-targets.md b/docs/markdown/snippets/fs-copyfile-build-targets.md new file mode 100644 index 000000000000..284a860fc1fe --- /dev/null +++ b/docs/markdown/snippets/fs-copyfile-build-targets.md @@ -0,0 +1,5 @@ +## `fs.copyfile()` now accepts build targets as input + +`fs.copyfile()` now accepts `build_tgt`, `custom_tgt`, and `custom_idx` +as the source argument, in addition to `File` and `str`. This makes it +possible to copy build artifacts without resorting to a `custom_target`. diff --git a/mesonbuild/modules/fs.py b/mesonbuild/modules/fs.py index ba7449026fcb..d6a266f2fd25 100644 --- a/mesonbuild/modules/fs.py +++ b/mesonbuild/modules/fs.py @@ -280,7 +280,7 @@ def read(self, state: ModuleState, args: T.Tuple[FileOrString], kwargs: ReadKwAr return data @FeatureNew('fs.copyfile', '0.64.0') - @typed_pos_args('fs.copyfile', (File, str), optargs=[str]) + @typed_pos_args('fs.copyfile', (File, str, CustomTarget, CustomTargetIndex, BuildTarget), optargs=[str]) @typed_kwargs( 'fs.copyfile', INSTALL_KW, @@ -289,17 +289,28 @@ def read(self, state: ModuleState, args: T.Tuple[FileOrString], kwargs: ReadKwAr KwargInfo('install_dir', (str, NoneType)), BUILD_SUBDIR_KW.evolve(since='1.12.0'), ) - def copyfile(self, state: ModuleState, args: T.Tuple[FileOrString, T.Optional[str]], + def copyfile(self, state: ModuleState, args: T.Tuple[T.Union[FileOrString, BuildTargetTypes], T.Optional[str]], kwargs: CopyKw) -> ModuleReturnValue: """Copy a file into the build directory at build time.""" if kwargs['install'] and not kwargs['install_dir']: raise InvalidArguments('"install_dir" must be specified when "install" is true') - src = self.interpreter.source_strings_to_files([args[0]])[0] + src: T.Union[File, BuildTarget, CustomTarget, CustomTargetIndex] + if isinstance(args[0], (CustomTarget, CustomTargetIndex, BuildTarget)): + FeatureNew.single_use('fs.copyfile with build_tgt, custom_tgt, and custom_idx', '1.12.0', state.subproject, location=state.current_node) + if isinstance(args[0], CustomTarget) and len(args[0].get_outputs()) > 1: + raise InvalidArguments( + f'custom_target {args[0].name!r} has more than one output! ' + f'Use `{args[0].name}[]` to select the output to copy.') + src = args[0] + default_name = src.get_filename() + else: + src = self.interpreter.source_strings_to_files([args[0]])[0] + # The input is allowed to have path separators, but the output may not, + # so use the basename for the default case + default_name = os.path.basename(src.fname) - # The input is allowed to have path separators, but the output may not, - # so use the basename for the default case - dest = args[1] if args[1] else os.path.basename(src.fname) + dest = args[1] if args[1] else default_name if has_path_sep(dest): raise InvalidArguments('Destination path may not have path separators') diff --git a/test cases/common/220 fs module/meson.build b/test cases/common/220 fs module/meson.build index 383b263ab7bf..7492854b00b2 100644 --- a/test cases/common/220 fs module/meson.build +++ b/test cases/common/220 fs module/meson.build @@ -7,6 +7,9 @@ fs = import('fs') f = files('meson.build') btgt = executable('btgt', 'btgt.c') ctgt = fs.copyfile('ctgt.txt') +ctgt_copy = fs.copyfile(ctgt, 'ctgt_copy.txt') +ctgt_idx_copy = fs.copyfile(ctgt[0], 'ctgt_idx_copy.txt') +btgt_copy = fs.copyfile(btgt, 'btgt_copy') assert(fs.exists('meson.build'), 'Existing file reported as missing.') assert(not fs.exists('nonexisting'), 'Nonexisting file was found.') @@ -199,3 +202,13 @@ subproject('subbie') testcase expect_error('File notfound does not exist.') fs.read('notfound') endtestcase + +multi_out = custom_target('multi_out', + output: ['a.txt', 'b.txt'], + command: [find_program('cp'), '@INPUT@', '@OUTPUT@'], + input: 'meson.build', +) + +testcase expect_error('custom_target \'multi_out\' has more than one output! Use `multi_out[]` to select the output to copy.') + fs.copyfile(multi_out) +endtestcase From 51af04b3c4c2c6016354f72394be1a321d0793c4 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 20 Apr 2026 12:01:43 -0700 Subject: [PATCH 061/681] tests/8 flex lex: Make the test cross platform --- test cases/frameworks/8 flex/meson.build | 2 +- test cases/frameworks/8 flex/parser.y | 3 +++ test cases/frameworks/8 flex/prog.c | 18 ++++++------------ 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/test cases/frameworks/8 flex/meson.build b/test cases/frameworks/8 flex/meson.build index 0b110701030a..54b93d3bc9b2 100644 --- a/test cases/frameworks/8 flex/meson.build +++ b/test cases/frameworks/8 flex/meson.build @@ -23,7 +23,7 @@ endif codegen = import('unstable-codegen') lex = codegen.lex(implementations : ['flex', 'reflex', 'lex']) message('lex implementation:', lex.implementation()) -lfiles = lex.generate('lexer.l') +lfiles = lex.generate('lexer.l', header: '@PLAINNAME@.h') yacc = codegen.yacc(implementations : ['byacc', 'bison', 'yacc']) message('yacc implementation:', yacc.implementation()) diff --git a/test cases/frameworks/8 flex/parser.y b/test cases/frameworks/8 flex/parser.y index ba8004efdf79..9b4a3d5a0113 100644 --- a/test cases/frameworks/8 flex/parser.y +++ b/test cases/frameworks/8 flex/parser.y @@ -1,6 +1,9 @@ %{ +#include + extern int yylex(void); extern int yyerror(char *s); +extern FILE *yyin; %} %token BOOLEAN diff --git a/test cases/frameworks/8 flex/prog.c b/test cases/frameworks/8 flex/prog.c index 840a0644ae81..e235944cae7d 100644 --- a/test cases/frameworks/8 flex/prog.c +++ b/test cases/frameworks/8 flex/prog.c @@ -1,22 +1,16 @@ -#include"parser.tab.h" -#include -#include -#include -#include -#include -#include -extern int yyparse(); +#include "lexer.l.h" +#include "parser.tab.h" + +#include + int main(int argc, char **argv) { - int input; if(argc != 2) { printf("%s \n", argv[0]); return 1; } - input = open(argv[1], O_RDONLY); - dup2(input, STDIN_FILENO); - close(input); + yyin = fopen(argv[1], "r"); return yyparse(); } From c0542be2cd0aa7f656f5aa53cc5e0030c2bbef9d Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 20 Apr 2026 12:03:42 -0700 Subject: [PATCH 062/681] tests/8 flex lex: Allow running with win_flex_bison --- test cases/frameworks/8 flex/meson.build | 11 ++++------- test cases/frameworks/8 flex/prog.c | 2 ++ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/test cases/frameworks/8 flex/meson.build b/test cases/frameworks/8 flex/meson.build index 54b93d3bc9b2..acbed7aa7f6c 100644 --- a/test cases/frameworks/8 flex/meson.build +++ b/test cases/frameworks/8 flex/meson.build @@ -7,25 +7,22 @@ project('flex and bison', 'c', meson_version : '>= 1.10.0') # may output headers that are necessary to build # the sources of a different generator. -# TODO: handle win_flex/win_bison - -flex = find_program('reflex', 'flex', 'lex', required: false) -bison = find_program('bison', 'byacc', 'yacc', required: false) - +flex = find_program('reflex', 'flex', 'lex', 'win_flex', required: false) if not flex.found() error('MESON_SKIP_TEST flex not found.') endif +bison = find_program('bison', 'byacc', 'yacc', 'win_bison', required: false) if not bison.found() error('MESON_SKIP_TEST bison not found.') endif codegen = import('unstable-codegen') -lex = codegen.lex(implementations : ['flex', 'reflex', 'lex']) +lex = codegen.lex() message('lex implementation:', lex.implementation()) lfiles = lex.generate('lexer.l', header: '@PLAINNAME@.h') -yacc = codegen.yacc(implementations : ['byacc', 'bison', 'yacc']) +yacc = codegen.yacc() message('yacc implementation:', yacc.implementation()) pfiles = yacc.generate('parser.y', header : '@BASENAME@.tab.h') diff --git a/test cases/frameworks/8 flex/prog.c b/test cases/frameworks/8 flex/prog.c index e235944cae7d..98538e782adb 100644 --- a/test cases/frameworks/8 flex/prog.c +++ b/test cases/frameworks/8 flex/prog.c @@ -4,6 +4,8 @@ #include +int yyparse(void); + int main(int argc, char **argv) { if(argc != 2) { From 5dfc08283c9fab68a8d2be013261dff405628e54 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 20 Apr 2026 12:03:17 -0700 Subject: [PATCH 063/681] CI/windows: install win_flex_bison from chocolatey So that we can run the flex/lex tests --- .github/workflows/windows.yml | 4 ++-- azure-pipelines.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 9c163a425b32..bfe2fe3ccfc4 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -98,9 +98,9 @@ jobs: Invoke-WebRequest -Uri "https://raw.githubusercontent.com/mesonbuild/cidata/refs/heads/master/win32/pkg-config.exe" -OutFile "$pkgconfigDir\pkg-config.exe" echo "$pkgconfigDir" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - - name: Install NASM + - name: Install Chocolatey Dependencies shell: pwsh - run: choco install -y nasm + run: choco install -y nasm winflexbison3 - name: Setup Rust uses: dtolnay/rust-toolchain@stable diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 82b4d2e58165..c1f0dce40ec3 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -80,8 +80,8 @@ jobs: - bash: ci/intel-scripts/cache_exclude_windows.sh displayName: exclude unused files from cache condition: and(ne(variables.CACHE_RESTORED, 'true'), eq(variables.ifort, 'true')) - - script: choco install -y nasm - displayName: install NASM + - script: choco install -y nasm winflexbison3 + displayName: install Chocolatey Dependencies - task: UsePythonVersion@0 inputs: versionSpec: '3.7' From d05eb7daeede52d7319d17496300aac3ec4c0c7a Mon Sep 17 00:00:00 2001 From: Jon Turney Date: Mon, 9 Mar 2026 18:08:06 +0000 Subject: [PATCH 064/681] CI: Cygwin: update workflow to use python3.12 --- .github/workflows/cygwin.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/cygwin.yml b/.github/workflows/cygwin.yml index 90070f0f1621..ba2ba27fc9f4 100644 --- a/.github/workflows/cygwin.yml +++ b/.github/workflows/cygwin.yml @@ -67,12 +67,12 @@ jobs: libxslt-devel make ninja - python3-devel - python3-libxml2 - python3-libxslt - python3-gi - python39-pip - python39-wheel + python312-devel + python312-libxml2 + python312-libxslt + python312-gi + python312-pip + python312-wheel vala zlib-devel From 56c0c01b1516b42da5cdfb0a081595be91c9a8fd Mon Sep 17 00:00:00 2001 From: Jon Turney Date: Mon, 9 Mar 2026 19:19:22 +0000 Subject: [PATCH 065/681] CI: Cygwin: don't pip install things a package is available for Also drop some packages which were added as "gcovr dependencies" back in commit 5bb7f743. --- .github/workflows/cygwin.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cygwin.yml b/.github/workflows/cygwin.yml index ba2ba27fc9f4..c7f8ff3816b5 100644 --- a/.github/workflows/cygwin.yml +++ b/.github/workflows/cygwin.yml @@ -57,20 +57,19 @@ jobs: gcc-fortran gcc-objc++ gcc-objc + gcovr git gobject-introspection gtk-doc libboost-devel libglib2.0-devel libgtk3-devel - libxml2-devel - libxslt-devel make ninja python312-devel - python312-libxml2 - python312-libxslt + python312-fastjsonschema python312-gi + python312-pefile python312-pip python312-wheel vala @@ -93,7 +92,7 @@ jobs: - name: Run pip run: | export PATH=/usr/bin:/usr/local/bin:$(cygpath ${SYSTEMROOT})/system32 - python3 -m pip --disable-pip-version-check install gcovr fastjsonschema pefile pytest pytest-subtests pytest-xdist + python3 -m pip --disable-pip-version-check install pytest pytest-subtests pytest-xdist shell: bash --noprofile --norc -o igncr -eo pipefail '{0}' - uses: actions/cache/save@v4 From 98e8f737b008265c8be6200c2580bb9ffb3be0cb Mon Sep 17 00:00:00 2001 From: Jon Turney Date: Mon, 9 Mar 2026 22:52:11 +0000 Subject: [PATCH 066/681] Revert "cygwin: Fix gnome tests for CI" This partially reverts commit 3fad380c074ad4c7f50d9bfdc3aeb5fed5ccd3c4. (retain the correction to the expected installed files, because that's right irrespective of whether the test is expected to run or not.) Needed until the python-gi cygwin package is rebuilt for python3.12. --- .github/workflows/cygwin.yml | 1 - test cases/frameworks/11 gir subproject/test.json | 2 +- test cases/frameworks/7 gnome/test.json | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cygwin.yml b/.github/workflows/cygwin.yml index c7f8ff3816b5..298f3ec75ea0 100644 --- a/.github/workflows/cygwin.yml +++ b/.github/workflows/cygwin.yml @@ -68,7 +68,6 @@ jobs: ninja python312-devel python312-fastjsonschema - python312-gi python312-pefile python312-pip python312-wheel diff --git a/test cases/frameworks/11 gir subproject/test.json b/test cases/frameworks/11 gir subproject/test.json index cbf25d9a8312..9106642981d0 100644 --- a/test cases/frameworks/11 gir subproject/test.json +++ b/test cases/frameworks/11 gir subproject/test.json @@ -9,5 +9,5 @@ {"type": "expr", "file": "usr/lib/?libgirlib.so"}, {"type": "file", "platform": "cygwin", "file": "usr/lib/libgirsubproject.dll.a"} ], - "expect_skip_on_jobname": ["azure", "macos", "msys2", "pypy", "windows"] + "expect_skip_on_jobname": ["azure", "cygwin", "macos", "msys2", "pypy", "windows"] } diff --git a/test cases/frameworks/7 gnome/test.json b/test cases/frameworks/7 gnome/test.json index 786b3e11cf2b..c83bdc7fcd25 100644 --- a/test cases/frameworks/7 gnome/test.json +++ b/test cases/frameworks/7 gnome/test.json @@ -40,5 +40,5 @@ {"type": "file", "file": "usr/include/simple-resources.h"}, {"type": "file", "file": "usr/include/generated-gdbus.h"} ], - "expect_skip_on_jobname": ["azure", "macos", "msys2", "pypy", "windows"] + "expect_skip_on_jobname": ["azure", "cygwin", "macos", "msys2", "pypy", "windows"] } From 0e6e589564e585f2c52557f5bd5db6a294749c0d Mon Sep 17 00:00:00 2001 From: Jon Turney Date: Thu, 23 Apr 2026 15:12:10 +0100 Subject: [PATCH 067/681] Fix 'vala/7 shared library' on cygwin The installed file check for vala_custom as written in aa8f5617f59f ("vala: Fix handling of install_dir being an array with fewer elements than outputs") is not right due to the obscure way platform_fix_name works. --- test cases/vala/7 shared library/test.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test cases/vala/7 shared library/test.json b/test cases/vala/7 shared library/test.json index 31ee82447b17..5e236f909f30 100644 --- a/test cases/vala/7 shared library/test.json +++ b/test cases/vala/7 shared library/test.json @@ -2,7 +2,7 @@ "installed": [ {"type": "expr", "file": "usr/lib/?libinstalled_vala_lib.so"}, {"type": "file", "platform": "cygwin", "file": "usr/lib/libinstalled_vala_lib.dll.a"}, - {"type": "expr", "file": "usr/custom/?libinstalled_vala_custom.so"}, + {"type": "expr", "file": "usr/custom/libinstalled_vala_custom?so"}, {"type": "file", "platform": "cygwin", "file": "usr/custom/libinstalled_vala_custom.dll.a"}, {"type": "expr", "file": "usr/lib/?libinstalled_vala_all.so"}, {"type": "file", "platform": "cygwin", "file": "usr/lib/libinstalled_vala_all.dll.a"}, From be8fa9d468776099c44b1ed8d62ac19ceca83a3e Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 19 Dec 2025 08:58:52 -0800 Subject: [PATCH 068/681] ci: Drop ubuntu bionic There is no support for Python 3.10, so we can drop this. --- .github/workflows/images.yml | 4 +- .github/workflows/os_comp.yml | 13 +--- ci/ciimage/bionic/image.json | 8 --- ci/ciimage/bionic/install.sh | 69 ------------------- docs/markdown/Continuous-Integration.md | 27 -------- run_shell_checks.py | 1 - test cases/frameworks/16 sdl2/test.json | 2 +- test cases/frameworks/26 netcdf/test.json | 2 +- test cases/frameworks/30 scalapack/test.json | 2 +- .../frameworks/34 gir static lib/test.json | 2 +- test cases/frameworks/40 qt qml/test.json | 2 +- 11 files changed, 7 insertions(+), 125 deletions(-) delete mode 100644 ci/ciimage/bionic/image.json delete mode 100755 ci/ciimage/bionic/install.sh diff --git a/.github/workflows/images.yml b/.github/workflows/images.yml index 369c91e2d31c..7d83920de575 100644 --- a/.github/workflows/images.yml +++ b/.github/workflows/images.yml @@ -43,11 +43,9 @@ jobs: - { name: Fedora, id: fedora } - { name: Gentoo, id: gentoo } - { name: OpenSUSE, id: opensuse } - - { name: Ubuntu Bionic, id: bionic } - { name: Ubuntu Rolling, id: ubuntu-rolling } steps: - # Need v3 because of bionic - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 # Login to dockerhub - name: Docker login diff --git a/.github/workflows/os_comp.yml b/.github/workflows/os_comp.yml index 4b9b7a4a6eae..135635822c15 100644 --- a/.github/workflows/os_comp.yml +++ b/.github/workflows/os_comp.yml @@ -42,24 +42,13 @@ jobs: - { name: Fedora, id: fedora } - { name: Gentoo, id: gentoo } - { name: OpenSUSE, id: opensuse } - - { name: Ubuntu Bionic, id: bionic } container: image: mesonbuild/${{ matrix.cfg.id }}:latest - volumes: - - ${{ matrix.cfg.id == 'bionic' && '/node20217:/node20217:rw,rshared' || ' ' }} - - ${{ matrix.cfg.id == 'bionic' && '/node20217:/__e/node20:ro,rshared' || ' ' }} env: MESON_CI_JOBNAME: linux-${{ matrix.cfg.id }}-gcc steps: - - name: install nodejs20glibc2.17 - if: ${{ matrix.cfg.id == 'bionic' }} - run: | - apt install curl -y - curl -LO https://unofficial-builds.nodejs.org/download/release/v20.9.0/node-v20.9.0-linux-x64-glibc-217.tar.xz - tar -xf node-v20.9.0-linux-x64-glibc-217.tar.xz --strip-components 1 -C /node20217 - ldd /__e/node20/bin/node - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Run tests # All environment variables are stored inside the docker image in /ci/env_vars.sh diff --git a/ci/ciimage/bionic/image.json b/ci/ciimage/bionic/image.json deleted file mode 100644 index 5df709e7ee7a..000000000000 --- a/ci/ciimage/bionic/image.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "base_image": "ubuntu:bionic", - "env": { - "CI": "1", - "DC": "gdc", - "MESON_CI_JOBNAME": "linux-bionic-gcc" - } -} diff --git a/ci/ciimage/bionic/install.sh b/ci/ciimage/bionic/install.sh deleted file mode 100755 index 23e0774fac65..000000000000 --- a/ci/ciimage/bionic/install.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/bash - -set -e - -source /ci/common.sh - -export DEBIAN_FRONTEND=noninteractive -export LANG='C.UTF-8' -export DC=gdc - -pkgs=( - python3-pip libxml2-dev libxslt1-dev libyaml-dev libjson-glib-dev - python3.7 python3.7-dev - wget unzip cmake doxygen - clang - pkg-config-arm-linux-gnueabihf - qt4-linguist-tools qt5-default qtbase5-private-dev - python-dev - libomp-dev - llvm lcov - ldc - libclang-dev - libgcrypt20-dev - libgpgme-dev - libhdf5-dev openssh-server - libboost-python-dev libboost-regex-dev - libblocksruntime-dev - libperl-dev libscalapack-mpi-dev libncurses-dev - itstool - openjdk-11-jre -) - -boost_pkgs=(atomic chrono date-time filesystem log regex serialization system test thread) - -sed -i '/^#\sdeb-src /s/^#//' "/etc/apt/sources.list" -apt-get -y update -apt-get -y upgrade -apt-get -y install eatmydata - -# Base stuff -eatmydata apt-get -y build-dep meson - -# Add boost packages -for i in "${boost_pkgs[@]}"; do - for j in "1.62.0" "1.65.1"; do - pkgs+=("libboost-${i}${j}") - done -done - -# packages -eatmydata apt-get -y install "${pkgs[@]}" - -# Actually select the right python version -update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.6 1 \ - --slave /usr/lib/x86_64-linux-gnu/pkgconfig/python3.pc python3.pc /usr/lib/x86_64-linux-gnu/pkgconfig/python-3.6.pc -update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.7 2 \ - --slave /usr/lib/x86_64-linux-gnu/pkgconfig/python3.pc python3.pc /usr/lib/x86_64-linux-gnu/pkgconfig/python-3.7.pc - -python3 -m pip install -U "${base_python_pkgs[@]}" "${python_pkgs[@]}" - -# Install the ninja 0.10 -wget https://github.com/ninja-build/ninja/releases/download/v1.10.0/ninja-linux.zip -unzip ninja-linux.zip -d /ci - -# cleanup -apt-get -y remove ninja-build -apt-get -y clean -apt-get -y autoclean -rm ninja-linux.zip diff --git a/docs/markdown/Continuous-Integration.md b/docs/markdown/Continuous-Integration.md index 1e93fa57febf..89c86c42d224 100644 --- a/docs/markdown/Continuous-Integration.md +++ b/docs/markdown/Continuous-Integration.md @@ -233,33 +233,6 @@ The following statement is sufficient for Meson to find Boost: - cmd: set BOOST_ROOT=C:\Libraries\boost_1_67_0 ``` -## Travis without Docker - -Non-Docker Travis-CI builds can use Linux, MacOS or Windows. -Set the desired compiler(s) in the build **matrix**. -This example is for **Linux** (Ubuntu 18.04) and **C**. - -```yaml -dist: bionic -group: travis_latest - -os: linux -language: python - -matrix: - include: - - env: CC=gcc - - env: CC=clang - -install: - - pip install meson ninja - -script: - - meson setup builddir - - meson compile -C builddir - - meson test -C builddir -``` - ## GitHub Actions GitHub Actions provides a versatile platform for continuous integration diff --git a/run_shell_checks.py b/run_shell_checks.py index f929d80e1b0a..a22005397d2b 100755 --- a/run_shell_checks.py +++ b/run_shell_checks.py @@ -23,7 +23,6 @@ 'ci/ciimage/ubuntu-rolling/test.sh', 'ci/ciimage/cuda-cross/install.sh', 'ci/ciimage/cuda/install.sh', - 'ci/ciimage/bionic/install.sh', 'ci/ciimage/fedora/install.sh', 'ci/ciimage/arch/install.sh', 'ci/ciimage/gentoo/install.sh', diff --git a/test cases/frameworks/16 sdl2/test.json b/test cases/frameworks/16 sdl2/test.json index b95067fd2ee6..a2eb08b75971 100644 --- a/test cases/frameworks/16 sdl2/test.json +++ b/test cases/frameworks/16 sdl2/test.json @@ -7,7 +7,7 @@ { "val": "config-tool" }, { "val": "sdlconfig" }, { "val": "extraframework", "expect_skip_on_os": ["!darwin"], "expect_skip_on_jobname": ["macos"] }, - { "val": "cmake", "expect_skip_on_jobname": ["bionic"] } + { "val": "cmake" } ] } }, diff --git a/test cases/frameworks/26 netcdf/test.json b/test cases/frameworks/26 netcdf/test.json index b2149f5482b8..99a9d7d4a9f1 100644 --- a/test cases/frameworks/26 netcdf/test.json +++ b/test cases/frameworks/26 netcdf/test.json @@ -1,3 +1,3 @@ { - "expect_skip_on_jobname": ["azure", "bionic", "cygwin", "fedora", "macos", "msys2", "opensuse", "ubuntu", "windows"] + "expect_skip_on_jobname": ["azure", "cygwin", "fedora", "macos", "msys2", "opensuse", "ubuntu", "windows"] } diff --git a/test cases/frameworks/30 scalapack/test.json b/test cases/frameworks/30 scalapack/test.json index 412f39d210e0..ed10af0c9fdb 100644 --- a/test cases/frameworks/30 scalapack/test.json +++ b/test cases/frameworks/30 scalapack/test.json @@ -1,3 +1,3 @@ { - "expect_skip_on_jobname": ["azure", "bionic", "cygwin", "fedora", "msys2", "opensuse", "windows"] + "expect_skip_on_jobname": ["azure", "cygwin", "fedora", "msys2", "opensuse", "windows"] } diff --git a/test cases/frameworks/34 gir static lib/test.json b/test cases/frameworks/34 gir static lib/test.json index 093722cbf910..bbd38c9ccdd5 100644 --- a/test cases/frameworks/34 gir static lib/test.json +++ b/test cases/frameworks/34 gir static lib/test.json @@ -5,5 +5,5 @@ {"type": "file", "platform": "cygwin", "file": "usr/lib/libgirlib.dll.a"}, {"type": "file", "file": "usr/share/gir-1.0/Meson-1.0.gir"} ], - "expect_skip_on_jobname": ["azure", "bionic", "macos", "msys2", "windows"] + "expect_skip_on_jobname": ["azure", "macos", "msys2", "windows"] } diff --git a/test cases/frameworks/40 qt qml/test.json b/test cases/frameworks/40 qt qml/test.json index 1beecd18d78f..0f6a2c3fcac3 100644 --- a/test cases/frameworks/40 qt qml/test.json +++ b/test cases/frameworks/40 qt qml/test.json @@ -18,5 +18,5 @@ {"type": "file", "file": "usr/qml/My/Module5/qmldir"}, {"type": "file", "file": "usr/qml/My/Module5/My_Module5.qmltypes"} ], - "expect_skip_on_jobname": ["cygwin", "msys2", "azure", "bionic", "macos", "windows"] + "expect_skip_on_jobname": ["cygwin", "msys2", "azure", "macos", "windows"] } From 20a2ebaf3951b67e3820b8f55300b196e00cfe2e Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 23 Apr 2026 09:04:29 -0700 Subject: [PATCH 069/681] uninttests: Skip python2 tests if the python2 binary isn't installed This provides faster skipping on macos and on Linux before bother to call into pkg-config. This is needed on macOS to keep things running after the next patch that is forced to take a heavy handed approach to prevent the wrong python from being used. --- unittests/machinefiletests.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/unittests/machinefiletests.py b/unittests/machinefiletests.py index 861d81826576..6eb050ae3f8e 100644 --- a/unittests/machinefiletests.py +++ b/unittests/machinefiletests.py @@ -252,8 +252,12 @@ def test_python_module(self): raise SkipTest('bat indirection breaks internal sanity checks.') elif is_osx(): binary = 'python' + if not shutil.which(binary): + raise SkipTest('Not running Python2 tests because it was not found.') else: binary = 'python2' + if not shutil.which(binary): + raise SkipTest('Not running Python2 tests because it was not found.') # We not have python2, check for it for v in ['2', '2.7', '-2.7']: From 2e36748b279613fa171c3dead855b7d426ddf85b Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 15 Apr 2026 09:01:46 -0700 Subject: [PATCH 070/681] ci/macos: use brew python for unit tests --- .github/workflows/macos.yml | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 6f2cd3c5ef97..fe89b6529f5f 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -32,10 +32,20 @@ jobs: runs-on: macos-latest steps: - uses: actions/checkout@v4 + - run: brew update + # github actions overwrites brew's python. Force it to reassert itself, by running in a separate step. + - name: unbreak python in github actions + run: | + find /usr/local/bin -lname '*/Library/Frameworks/Python.framework/*' -delete + sudo rm -rf /Library/Frameworks/Python.framework/ + brew install --force python3 && brew unlink python3 && brew unlink python3 && brew link --overwrite python3 + # Delete nonsensical PEP 668 breakage. It is the wrong solution to the problem and isn't designed to be + # productive -- only victim blaming -- however it bites particularly badly because this is a container/VM + # See commit 5c479d7a13a518c18ccb4dc3b6bdd7bfc2a9bdb5 for a more thorough analysis. + find /opt/homebrew/Cellar/python* -name EXTERNALLY-MANAGED -print0 | xargs -0 rm -vf - run: | - export PATH="$HOME/Library/Python/3.9/bin:$PATH" - /usr/bin/python3 -m pip install --upgrade pip - /usr/bin/python3 -m pip install pytest pytest-xdist pytest-subtests fastjsonschema + python3 -m pip install --upgrade pip + python3 -m pip install pytest pytest-xdist pytest-subtests fastjsonschema - run: brew install pkg-config ninja llvm qt@5 - env: CPPFLAGS: "-I/opt/homebrew/include" @@ -46,9 +56,9 @@ jobs: # These cannot evaluate anything, so we cannot set PATH or SDKROOT here run: | export SDKROOT="$(xcodebuild -version -sdk macosx Path)" - export PATH="$HOME/Library/Python/3.9/bin:$HOME/tools:/opt/homebrew/opt/qt@5/bin:/opt/homebrew/opt/llvm/bin:$PATH" + export PATH="$HOME/tools:/opt/homebrew/opt/qt@5/bin:/opt/homebrew/opt/llvm/bin:$PATH" export PKG_CONFIG_PATH="/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/Current/lib/pkgconfig:/opt/homebrew/opt/qt@5/lib/pkgconfig:$PKG_CONFIG_PATH" - /usr/bin/python3 ./run_unittests.py + python3 ./run_unittests.py project-tests-appleclang: From b6c59f78ffa81609e124aaa041e068131092acb2 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 20 Apr 2026 13:10:12 -0700 Subject: [PATCH 071/681] ci: use ubuntu-rolling for non-cuda cross testing --- .github/workflows/nonnative.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nonnative.yml b/.github/workflows/nonnative.yml index c616f5199b3d..3c97e4a00434 100644 --- a/.github/workflows/nonnative.yml +++ b/.github/workflows/nonnative.yml @@ -28,7 +28,7 @@ permissions: jobs: cross-only-armhf: runs-on: ubuntu-latest - container: mesonbuild/eoan:latest + container: mesonbuild/ubuntu-rolling:latest env: MESON_CI_JOBNAME: ubuntu-${{ github.job }} From aee0e302d06577a34b83e23b3b6bc9e3a12dd5a9 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 15 Apr 2026 09:25:56 -0700 Subject: [PATCH 072/681] ci/windows: Use Python 3.10 --- .github/workflows/windows.yml | 2 +- azure-pipelines.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index bfe2fe3ccfc4..278620369f18 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -61,7 +61,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v6 with: - python-version: '3.7' + python-version: '3.10' - name: Setup MSVC uses: ilammy/msvc-dev-cmd@v1 diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c1f0dce40ec3..4362b2b24a8e 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -84,7 +84,7 @@ jobs: displayName: install Chocolatey Dependencies - task: UsePythonVersion@0 inputs: - versionSpec: '3.7' + versionSpec: '3.10' addToPath: true architecture: 'x64' - task: BatchScript@1 From 30599dfb98d1d59c2d59866c54c2cdd718299356 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 19 Dec 2025 08:49:24 -0800 Subject: [PATCH 073/681] Drop Python < 3.10 support --- README.md | 2 +- ci/run.ps1 | 4 ++-- docs/markdown/Getting-meson.md | 4 ++-- docs/markdown/Getting-meson_ptbr.md | 4 ++-- docs/markdown/Getting-meson_zh.md | 2 +- docs/yaml/builtins/build_machine.yaml | 4 ++-- meson.py | 4 ++-- mesonbuild/mesondata.py | 20 +++++++++----------- mesonbuild/minstall.py | 4 ++-- mesonbuild/modules/pkgconfig.py | 2 +- run_mypy.py | 2 +- setup.cfg | 6 ++---- setup.py | 4 ++-- 13 files changed, 29 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 9556bc5133e5..87b579a76846 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ build system. #### Dependencies - - [Python](https://python.org) (version 3.7 or newer) + - [Python](https://python.org) (version 3.10 or newer) - [Ninja](https://ninja-build.org) (version 1.8.2 or newer) Latest Meson version supporting previous Python versions: diff --git a/ci/run.ps1 b/ci/run.ps1 index db684ac79676..167d62513431 100644 --- a/ci/run.ps1 +++ b/ci/run.ps1 @@ -20,7 +20,7 @@ if ($env:arch -eq 'x64') { # Rust puts its shared stdlib in a secret place, but it is needed to run tests. $env:Path += ";$HOME/.rustup/toolchains/stable-i686-pc-windows-msvc/bin" # Need 32-bit Python for tests that need the Python dependency - $env:Path = "C:\hostedtoolcache\windows\Python\3.7.9\x86;C:\hostedtoolcache\windows\Python\3.7.9\x86\Scripts;$env:Path" + $env:Path = "C:\hostedtoolcache\windows\Python\3.10.19\x86;C:\hostedtoolcache\windows\Python\3.10.19\x86\Scripts;$env:Path" } # Set the CI env var for the meson test framework @@ -92,7 +92,7 @@ python --version # Needed for running unit tests in parallel. echo "" -python -m pip --disable-pip-version-check install --upgrade pefile pytest-xdist pytest-subtests fastjsonschema +python -m pip --disable-pip-version-check install --upgrade pefile pytest-xdist pytest-subtests fastjsonschema # Needed for running the Cython tests python -m pip --disable-pip-version-check install cython diff --git a/docs/markdown/Getting-meson.md b/docs/markdown/Getting-meson.md index c3c3cc8ced34..a5078d6bef5e 100644 --- a/docs/markdown/Getting-meson.md +++ b/docs/markdown/Getting-meson.md @@ -1,6 +1,6 @@ # Getting Meson -Meson is implemented in Python 3, and requires 3.7 or newer. If your +Meson is implemented in Python 3, and requires 3.10 or newer. If your operating system provides a package manager, you should install it with that. For platforms that don't have a package manager, you need to download it from [Python's home page]. See below for @@ -14,7 +14,7 @@ itself without doing anything special. On Windows, if you did not install Python with the installer options that make Python scripts executable, you will have to run `python -/path/to/meson.py`, where `python` is Python 3.7 or newer. +/path/to/meson.py`, where `python` is Python 3.10 or newer. The newest development code can be obtained directly from [Git], and we strive to ensure that it will always be working and usable. All diff --git a/docs/markdown/Getting-meson_ptbr.md b/docs/markdown/Getting-meson_ptbr.md index f4f3ac1d70dc..6f2aef04c075 100644 --- a/docs/markdown/Getting-meson_ptbr.md +++ b/docs/markdown/Getting-meson_ptbr.md @@ -1,6 +1,6 @@ # Obtendo o Meson -Meson é implementado em Python 3, e requer a versão 3.7 ou mais nova. +Meson é implementado em Python 3, e requer a versão 3.10 ou mais nova. se o seu sistema operacional provê um gerenciador de pacotes, você deve instalar o Meson com ele. Para plataformas que não tem um gerenciador de pacotes, você precisa baixa-lo da [página inicial do Python]. Veja abaixo @@ -14,7 +14,7 @@ do git sem fazer nada de especial. No Windows, se você não instalar o Python com a opção do instalador que fazem os *scripts* Python executáveis, você vai ter que executar `python -/path/to/meson.py`, onde `python` é o Python 3.7 ou mais novo. +/path/to/meson.py`, onde `python` é o Python 3.10 ou mais novo. O código de desenvolvimento mais recente pode ser obtido diretamente do [Git], e nós lutamos para garatir que ele vai estar sempre funcionando e usável. Todos diff --git a/docs/markdown/Getting-meson_zh.md b/docs/markdown/Getting-meson_zh.md index da0bdd5028d9..b3dd74d43f86 100644 --- a/docs/markdown/Getting-meson_zh.md +++ b/docs/markdown/Getting-meson_zh.md @@ -1,6 +1,6 @@ # 获取Meson -Meson基于Python3运行,要求Python版本3.7以上。 如果你的操作系统提供包管理器, 你应该用包管理器安装python;如果没有包管理器,你应该在[Python主页]下载合适的Python3。相关请参阅[特殊平台的安装特例](#特殊平台的安装特例). +Meson基于Python3运行,要求Python版本3.10以上。 如果你的操作系统提供包管理器, 你应该用包管理器安装python;如果没有包管理器,你应该在[Python主页]下载合适的Python3。相关请参阅[特殊平台的安装特例](#特殊平台的安装特例). ## 下载Meson diff --git a/docs/yaml/builtins/build_machine.yaml b/docs/yaml/builtins/build_machine.yaml index c894b0aead37..3964dffe10e9 100644 --- a/docs/yaml/builtins/build_machine.yaml +++ b/docs/yaml/builtins/build_machine.yaml @@ -5,8 +5,8 @@ description: | See [Cross-compilation](Cross-compilation.md). Currently, these values are populated using - [`platform.system()`](https://docs.python.org/3.7/library/platform.html#platform.system) and - [`platform.machine()`](https://docs.python.org/3.7/library/platform.html#platform.machine). + [`platform.system()`](https://docs.python.org/3.10/library/platform.html#platform.system) and + [`platform.machine()`](https://docs.python.org/3.10/library/platform.html#platform.machine). If you think the returned values for any of these are incorrect for your system or CPU, or if your OS is not in the linked table, please [file a bug](https://github.com/mesonbuild/meson/issues/new) report diff --git a/meson.py b/meson.py index 95d6c3a239bf..d7f22815abbf 100755 --- a/meson.py +++ b/meson.py @@ -7,8 +7,8 @@ # Check python version before importing anything else, we might have an older # Python that would error on f-string syntax for example. -if sys.version_info < (3, 7): - print('Meson works correctly only with python 3.7+.') +if sys.version_info < (3, 10): + print('Meson works correctly only with python 3.10+.') print('You have python {}.'.format(sys.version)) print('Please update your environment') sys.exit(1) diff --git a/mesonbuild/mesondata.py b/mesonbuild/mesondata.py index 4d53a492f442..9d706e12ca39 100644 --- a/mesonbuild/mesondata.py +++ b/mesonbuild/mesondata.py @@ -6,7 +6,6 @@ import importlib.resources from pathlib import PurePosixPath, Path -import sys import typing as T if T.TYPE_CHECKING: @@ -25,16 +24,15 @@ def write_once(self, path: Path) -> None: path.write_text(data, encoding='utf-8') def write_to_private(self, env: 'Environment') -> Path: - if sys.version_info >= (3, 9): - try: - # The issue that mypy/pyright see here is caused by a bug in typeshed: - # https://github.com/python/typeshed/pull/15108 - resource = importlib.resources.files('mesonbuild') / self.path # type: ignore[operator] - if isinstance(resource, Path): - return resource - except AttributeError: - # fall through to python 3.7 compatible code - pass + try: + # The issue that mypy/pyright see here is caused by a bug in typeshed: + # https://github.com/python/typeshed/pull/15108 + resource = importlib.resources.files('mesonbuild') / self.path # type: ignore[operator] + if isinstance(resource, Path): + return resource + except AttributeError: + # fall through to zipapp compatible code + pass out_file = Path(env.scratch_dir) / 'data' / self.path.name out_file.parent.mkdir(exist_ok=True) diff --git a/mesonbuild/minstall.py b/mesonbuild/minstall.py index e9eb9f070eef..664c8fa1285b 100644 --- a/mesonbuild/minstall.py +++ b/mesonbuild/minstall.py @@ -39,7 +39,7 @@ try: from typing import Protocol except AttributeError: - from typing_extensions import Protocol # type: ignore + from typing_extensions import Protocol class ArgumentType(Protocol): """Typing information for the object returned by argparse.""" @@ -165,7 +165,7 @@ def set_chown(path: str, user: T.Union[str, int, None] = None, if sys.version_info >= (3, 13): # pylint: disable=unexpected-keyword-arg # cannot handle sys.version_info, https://github.com/pylint-dev/pylint/issues/9622 - shutil.chown(path, user, group, dir_fd=dir_fd, follow_symlinks=follow_symlinks) # type: ignore[call-overload] + shutil.chown(path, user, group, dir_fd=dir_fd, follow_symlinks=follow_symlinks) else: real_os_chown = os.chown diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py index 332ec33c214a..dbd5b918204e 100644 --- a/mesonbuild/modules/pkgconfig.py +++ b/mesonbuild/modules/pkgconfig.py @@ -374,7 +374,7 @@ def _add_exclude(x: T.Union[str, build.CustomTarget, build.CustomTargetIndex, bu # pylance/pyright gets this right, but for mypy we have to ignore the # error @T.overload - def _fn(xs: T.List[str], libs: bool = False) -> T.List[str]: ... # type: ignore + def _fn(xs: T.List[str], libs: bool = False) -> T.List[str]: ... @T.overload def _fn(xs: T.List[LIBS], libs: bool = False) -> T.List[LIBS]: ... diff --git a/run_mypy.py b/run_mypy.py index 6c0ddaaa4752..08fe25debae0 100755 --- a/run_mypy.py +++ b/run_mypy.py @@ -157,7 +157,7 @@ def main() -> int: print('Running mypy (this can take some time) ...') if opts.allver: - versions = ['default'] + [f'3.{minor}' for minor in range(7, sys.version_info[1])] + versions = ['default'] + [f'3.{minor}' for minor in range(10, sys.version_info[1])] else: versions = ['default'] diff --git a/setup.cfg b/setup.cfg index 51b8aeeba3bd..5d377f394ef4 100644 --- a/setup.cfg +++ b/setup.cfg @@ -25,19 +25,17 @@ classifiers = Operating System :: POSIX :: BSD Operating System :: POSIX :: Linux Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 Programming Language :: Python :: 3.12 Programming Language :: Python :: 3.13 + Programming Language :: Python :: 3.14 Topic :: Software Development :: Build Tools long_description = Meson is a cross-platform build system designed to be both as fast and as user friendly as possible. It supports many languages and compilers, including GCC, Clang, PGI, Intel, and Visual Studio. Its build definitions are written in a simple non-Turing complete DSL. [options] packages = find: -python_requires = >= 3.7 +python_requires = >= 3.10 [options.entry_points] console_scripts = diff --git a/setup.py b/setup.py index 425d51d33b70..4b5cb6bf349a 100644 --- a/setup.py +++ b/setup.py @@ -5,9 +5,9 @@ import os, sys -if sys.version_info < (3, 7): +if sys.version_info < (3, 10): raise SystemExit('ERROR: Tried to install Meson with an unsupported Python version: \n{}' - '\nMeson requires Python 3.7.0 or greater'.format(sys.version)) + '\nMeson requires Python 3.10.0 or greater'.format(sys.version)) from setuptools import setup From 0f5f626e5ab3f16c79d3c1dce4dcf8da2359d467 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 19 Dec 2025 08:59:55 -0800 Subject: [PATCH 074/681] ci: Pin mypy to 1.19 so we can lint Python 3.10+ mypy 1.20 fails to correctly lint python 3.10 and > 3.10, raising warnings about unused ignore comments, when those comments are needed by 3.10 --- .github/workflows/lint.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 7161b6127d32..617d9a6ecdd6 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -52,8 +52,8 @@ jobs: - uses: actions/setup-python@v5 with: python-version: '3.x' - # Pin mypy to version 1.8, so we retain the ability to lint for Python 3.7 - - run: python -m pip install "mypy==1.8" strictyaml truststore types-PyYAML types-tqdm types-chevron + # Pin mypy to version 1.19, so we retain the ability to lint for Python 3.10 + - run: python -m pip install "mypy==1.19" strictyaml truststore types-PyYAML types-tqdm types-chevron - run: python run_mypy.py --allver env: PYTHONUNBUFFERED: 1 From 40023e00eadd06c4c44ae36552bcd6cd322a616f Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Thu, 23 Apr 2026 20:42:20 -0400 Subject: [PATCH 075/681] Revert "modules: fs: accept build targets as fs.copyfile() source" This reverts commit 74faddbb44475b0671fd1acbaedace2e8a596263. It is vibe-coded and also seems to have gotten merged without discussion of what the point of this is supposed to be. There's no motivating use case to point at, which normally should be a given? It's a lot of code in order to support cp'ing an executable()? Is this a sign that humans weren't involved in choosing to try implementing this? --- docs/markdown/Fs-module.md | 4 +--- .../snippets/fs-copyfile-build-targets.md | 5 ---- mesonbuild/modules/fs.py | 23 +++++-------------- test cases/common/220 fs module/meson.build | 13 ----------- 4 files changed, 7 insertions(+), 38 deletions(-) delete mode 100644 docs/markdown/snippets/fs-copyfile-build-targets.md diff --git a/docs/markdown/Fs-module.md b/docs/markdown/Fs-module.md index 7ca854ea1479..abff9bcad987 100644 --- a/docs/markdown/Fs-module.md +++ b/docs/markdown/Fs-module.md @@ -259,9 +259,7 @@ returns: Copy a file from the source directory to the build directory at build time. Has the following positional arguments: - - src `File | str | build_tgt | custom_tgt | custom_idx`: the file to copy. - *Since 1.12.0* build targets, custom targets, and custom target indices are - also accepted. + - src `File | str`: the file to copy Has the following optional arguments: - dest `str`: the name of the output file. If unset will be the basename of diff --git a/docs/markdown/snippets/fs-copyfile-build-targets.md b/docs/markdown/snippets/fs-copyfile-build-targets.md deleted file mode 100644 index 284a860fc1fe..000000000000 --- a/docs/markdown/snippets/fs-copyfile-build-targets.md +++ /dev/null @@ -1,5 +0,0 @@ -## `fs.copyfile()` now accepts build targets as input - -`fs.copyfile()` now accepts `build_tgt`, `custom_tgt`, and `custom_idx` -as the source argument, in addition to `File` and `str`. This makes it -possible to copy build artifacts without resorting to a `custom_target`. diff --git a/mesonbuild/modules/fs.py b/mesonbuild/modules/fs.py index d6a266f2fd25..ba7449026fcb 100644 --- a/mesonbuild/modules/fs.py +++ b/mesonbuild/modules/fs.py @@ -280,7 +280,7 @@ def read(self, state: ModuleState, args: T.Tuple[FileOrString], kwargs: ReadKwAr return data @FeatureNew('fs.copyfile', '0.64.0') - @typed_pos_args('fs.copyfile', (File, str, CustomTarget, CustomTargetIndex, BuildTarget), optargs=[str]) + @typed_pos_args('fs.copyfile', (File, str), optargs=[str]) @typed_kwargs( 'fs.copyfile', INSTALL_KW, @@ -289,28 +289,17 @@ def read(self, state: ModuleState, args: T.Tuple[FileOrString], kwargs: ReadKwAr KwargInfo('install_dir', (str, NoneType)), BUILD_SUBDIR_KW.evolve(since='1.12.0'), ) - def copyfile(self, state: ModuleState, args: T.Tuple[T.Union[FileOrString, BuildTargetTypes], T.Optional[str]], + def copyfile(self, state: ModuleState, args: T.Tuple[FileOrString, T.Optional[str]], kwargs: CopyKw) -> ModuleReturnValue: """Copy a file into the build directory at build time.""" if kwargs['install'] and not kwargs['install_dir']: raise InvalidArguments('"install_dir" must be specified when "install" is true') - src: T.Union[File, BuildTarget, CustomTarget, CustomTargetIndex] - if isinstance(args[0], (CustomTarget, CustomTargetIndex, BuildTarget)): - FeatureNew.single_use('fs.copyfile with build_tgt, custom_tgt, and custom_idx', '1.12.0', state.subproject, location=state.current_node) - if isinstance(args[0], CustomTarget) and len(args[0].get_outputs()) > 1: - raise InvalidArguments( - f'custom_target {args[0].name!r} has more than one output! ' - f'Use `{args[0].name}[]` to select the output to copy.') - src = args[0] - default_name = src.get_filename() - else: - src = self.interpreter.source_strings_to_files([args[0]])[0] - # The input is allowed to have path separators, but the output may not, - # so use the basename for the default case - default_name = os.path.basename(src.fname) + src = self.interpreter.source_strings_to_files([args[0]])[0] - dest = args[1] if args[1] else default_name + # The input is allowed to have path separators, but the output may not, + # so use the basename for the default case + dest = args[1] if args[1] else os.path.basename(src.fname) if has_path_sep(dest): raise InvalidArguments('Destination path may not have path separators') diff --git a/test cases/common/220 fs module/meson.build b/test cases/common/220 fs module/meson.build index 7492854b00b2..383b263ab7bf 100644 --- a/test cases/common/220 fs module/meson.build +++ b/test cases/common/220 fs module/meson.build @@ -7,9 +7,6 @@ fs = import('fs') f = files('meson.build') btgt = executable('btgt', 'btgt.c') ctgt = fs.copyfile('ctgt.txt') -ctgt_copy = fs.copyfile(ctgt, 'ctgt_copy.txt') -ctgt_idx_copy = fs.copyfile(ctgt[0], 'ctgt_idx_copy.txt') -btgt_copy = fs.copyfile(btgt, 'btgt_copy') assert(fs.exists('meson.build'), 'Existing file reported as missing.') assert(not fs.exists('nonexisting'), 'Nonexisting file was found.') @@ -202,13 +199,3 @@ subproject('subbie') testcase expect_error('File notfound does not exist.') fs.read('notfound') endtestcase - -multi_out = custom_target('multi_out', - output: ['a.txt', 'b.txt'], - command: [find_program('cp'), '@INPUT@', '@OUTPUT@'], - input: 'meson.build', -) - -testcase expect_error('custom_target \'multi_out\' has more than one output! Use `multi_out[]` to select the output to copy.') - fs.copyfile(multi_out) -endtestcase From 271e1e50d9a9d56e584ad832aa8287d56ea0dd75 Mon Sep 17 00:00:00 2001 From: Aditya Vidyadhar Kamath Date: Mon, 27 Jan 2025 05:45:08 -0600 Subject: [PATCH 076/681] depfixer: add RPATH handling for AIX's XCOFF object files. Ensures install_rpath used in shared library and shared modules targets is set at install time on AIX. --- mesonbuild/backend/backends.py | 19 ++- mesonbuild/linkers/linkers.py | 24 ++- mesonbuild/minstall.py | 2 +- mesonbuild/scripts/depfixer.py | 290 ++++++++++++++++++++++++++++++++- 4 files changed, 315 insertions(+), 20 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 91b0807e10f0..8161aa4a46df 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -144,6 +144,7 @@ class TargetInstallData: # TODO: install_mode should just always be a FileMode object install_mode: T.Optional['FileMode'] subproject: str + system: str optional: bool = False tag: T.Optional[str] = None can_strip: bool = False @@ -1749,7 +1750,8 @@ def generate_target_install(self, d: InstallData) -> None: first_outdir_name, should_strip, mappings, t.rpath_dirs_to_remove, t.install_rpath, install_mode, t.subproject, - tag=tag, can_strip=can_strip) + tag=tag, can_strip=can_strip, + system=self.environment.machines[t.for_machine].system) d.targets.append(i) for alias, to, tag in t.get_aliases(): @@ -1774,7 +1776,8 @@ def generate_target_install(self, d: InstallData) -> None: implib_install_dir, first_outdir_name, False, {}, set(), '', install_mode, t.subproject, optional=isinstance(t, build.SharedModule), - tag='devel') + tag='devel', + system=self.environment.machines[t.for_machine].system) d.targets.append(i) if not should_strip and t.get_debug_filename(): @@ -1783,7 +1786,8 @@ def generate_target_install(self, d: InstallData) -> None: first_outdir_name, False, {}, set(), '', install_mode, t.subproject, - optional=True, tag='devel') + optional=True, tag='devel', + system=self.environment.machines[t.for_machine].system) d.targets.append(i) # Install secondary outputs. Only used for Vala right now. if num_outdirs > 1: @@ -1794,7 +1798,8 @@ def generate_target_install(self, d: InstallData) -> None: f = os.path.join(self.get_target_dir(t), output) i = TargetInstallData(f, outdir, outdir_name, False, {}, set(), None, install_mode, t.subproject, - tag=tag) + tag=tag, + system=self.environment.machines[t.for_machine].system) d.targets.append(i) elif isinstance(t, build.CustomTarget): # If only one install_dir is specified, assume that all @@ -1815,7 +1820,8 @@ def generate_target_install(self, d: InstallData) -> None: i = TargetInstallData(f, first_outdir, first_outdir_name, False, {}, set(), None, install_mode, t.subproject, optional=not t.build_by_default, - tag=tag) + tag=tag, + system=self.environment.machines[t.for_machine].system) d.targets.append(i) else: for output, outdir, outdir_name, tag in zip(t.get_outputs(), outdirs, install_dir_names, t.install_tag): @@ -1827,7 +1833,8 @@ def generate_target_install(self, d: InstallData) -> None: i = TargetInstallData(f, outdir, outdir_name, False, {}, set(), None, install_mode, t.subproject, optional=not t.build_by_default, - tag=tag) + tag=tag, + system=self.environment.machines[t.for_machine].system) d.targets.append(i) def generate_custom_install_script(self, d: InstallData) -> None: diff --git a/mesonbuild/linkers/linkers.py b/mesonbuild/linkers/linkers.py index cb8956adf5be..f3e9314e2bf3 100644 --- a/mesonbuild/linkers/linkers.py +++ b/mesonbuild/linkers/linkers.py @@ -1698,14 +1698,24 @@ def get_link_whole_for(self, args: T.List[str]) -> T.List[str]: def build_rpath_args(self, build_dir: str, from_dir: str, target: BuildTarget, extra_paths: T.Optional[T.List[str]] = None ) -> T.Tuple[T.List[str], T.Set[bytes]]: + # Extract rpath information from target + rpath_paths = target.determine_rpath_dirs() + install_rpath = target.install_rpath + build_rpath = target.build_rpath + all_paths: mesonlib.OrderedSet[str] = mesonlib.OrderedSet() + rpath_dirs_to_remove: T.Set[bytes] = set() # install_rpath first, followed by other paths, and the system path last - if target.install_rpath != '': - all_paths.add(target.install_rpath) - if target.build_rpath != '': - all_paths.add(target.build_rpath) - for p in target.determine_rpath_dirs(): - all_paths.add(os.path.join(build_dir, p)) + if install_rpath != '': + all_paths.add(install_rpath) + if build_rpath != '': + all_paths.add(build_rpath) + for p in build_rpath.split(':'): + rpath_dirs_to_remove.add(p.encode('utf8')) + for p in rpath_paths: + path = os.path.join(build_dir, p) + all_paths.add(path) + rpath_dirs_to_remove.add(path.encode('utf8')) # We should consider allowing the $LIBPATH environment variable # to override sys_path. sys_path = self.environment.get_compiler_system_lib_dirs(self.for_machine) @@ -1720,7 +1730,7 @@ def build_rpath_args(self, build_dir: str, from_dir: str, target: BuildTarget, all_paths.add(p) if extra_paths: all_paths.update(extra_paths) - return (self._apply_prefix('-blibpath:' + ':'.join(all_paths)), set()) + return (self._apply_prefix('-blibpath:' + ':'.join(all_paths)), rpath_dirs_to_remove) def thread_flags(self) -> T.List[str]: return ['-pthread'] diff --git a/mesonbuild/minstall.py b/mesonbuild/minstall.py index 664c8fa1285b..39d966a2d34e 100644 --- a/mesonbuild/minstall.py +++ b/mesonbuild/minstall.py @@ -786,7 +786,7 @@ def install_targets(self, d: InstallData, dm: DirMaker, destdir: str, fullprefix self.did_install_something = True try: self.fix_rpath(outname, t.rpath_dirs_to_remove, install_rpath, final_path, - install_name_mappings, verbose=False) + install_name_mappings, t.system, verbose=False) except SystemExit as e: if isinstance(e.code, int) and e.code == 0: pass diff --git a/mesonbuild/scripts/depfixer.py b/mesonbuild/scripts/depfixer.py index 8adc35c4a9ca..dd74c89cafb4 100644 --- a/mesonbuild/scripts/depfixer.py +++ b/mesonbuild/scripts/depfixer.py @@ -25,6 +25,277 @@ # Global cache for tools INSTALL_NAME_TOOL = False +# -------------------AIX------------------------- +# Reference documents: +# https://www.ibm.com/docs/en/aix/7.1?topic=formats-xcoff-object-file-format +# https://www.ibm.com/docs/en/aix/7.2?topic=formats-ar-file-format-big + +# Size of magic number +XCOFF_MAGIC_SIZE = 2 + + +def align(value: int, align_bytes: int) -> int: + # Function used by AIX to align value to align_bytes + align_bytes = 1 << align_bytes + return ((value + align_bytes - 1) // align_bytes) * align_bytes + + +class XcoffFixedLengthHeader: + # AIX archive fixed length header (struct fl_hdr) + FL_HDR_SIZE = 128 + FL_HDR_FORMAT = '8s 20s 20s 20s 20s 20s 20s' + + def __init__(self, file: T.BinaryIO) -> None: + header_data = file.read(self.FL_HDR_SIZE) + fl_magic, fl_memoff, fl_gstoff, fl_gst64off, fl_fstmoff, fl_lstmoff, fl_freeoff = struct.unpack( + self.FL_HDR_FORMAT, header_data + ) + self.fl_magic = fl_magic + self.fl_fstmoff = int(fl_fstmoff) + + +class XcoffArchiveHeader: + # AIX archive header (struct ar_hdr) + AR_HDR_SIZE = 114 + AR_HDR_FORMAT = '20s 20s 20s 12s 12s 12s 12s 4s 2s' + + def __init__(self, file: T.BinaryIO) -> None: + header_data = file.read(self.AR_HDR_SIZE) + ar_size, ar_nxtmem, ar_prvmem, ar_date, ar_uid, ar_gid, ar_mode, ar_namlen, _ar_name = struct.unpack( + self.AR_HDR_FORMAT, header_data + ) + ar_namlen_int = int(ar_namlen.strip()) + # The Magic number always starts at the even byte boundary + self.ar_namlen = ar_namlen_int if ar_namlen_int % 2 == 0 else ar_namlen_int + 1 + # Seek past the archive member name + file.seek(self.ar_namlen, os.SEEK_CUR) + + +class XcoffCompositeFileHeader: + # XCOFF composite file header + CFH_HDR_SIZE = 24 + CFH_HDR_SIZE_32 = 20 + CFH_HDR_FORMAT = '2s 2s 4s 8s 2s 2s 4s' + CFH_HDR_FORMAT_32 = '2s 2s 4s 4s 4s 2s 2s' + + def __init__(self, file: T.BinaryIO, magic: int) -> None: + if magic == 0x01DF: + cfh_header_data = file.read(self.CFH_HDR_SIZE_32) + f_magic, f_nscns, f_timdat, f_symptr, f_nsyms, f_opthdr, f_flags = struct.unpack( + self.CFH_HDR_FORMAT_32, cfh_header_data + ) + else: + cfh_header_data = file.read(self.CFH_HDR_SIZE) + f_magic, f_nscns, f_timdat, f_symptr, f_opthdr, f_flags, f_nsyms = struct.unpack( + self.CFH_HDR_FORMAT, cfh_header_data + ) + self.f_flags = int.from_bytes(f_flags, byteorder='big') + self.f_opthdr = int.from_bytes(f_opthdr, byteorder='big') + + +class XcoffAuxiliaryHeader: + # XCOFF auxiliary header + AUX_HDR_SIZE = 120 + AUX_HDR_SIZE_32 = 72 + AUX_HDR_FORMAT = '2s 2s 4s 8s 8s 8s 2s 2s 2s 2s 2s 2s 2s 2s 2s 1s 1s 1s 1s 1s 1s 8s 8s 8s 8s 8s 8s 2s 2s 2s 10s' + AUX_HDR_FORMAT_32 = '2s 2s 4s 4s 4s 4s 4s 4s 4s 2s 2s 2s 2s 2s 2s 2s 2s 2s 1s 1s 4s 4s 4s 1s 1s 1s 1s 2s 2s' + + def __init__(self, file: T.BinaryIO, magic: int, opthdr_size: int) -> None: + if magic == 0x01DF: + if opthdr_size < self.AUX_HDR_SIZE_32: + sys.exit(f'Error: Auxiliary header size {opthdr_size} is too small for 32-bit XCOFF') + aux_header_data = file.read(self.AUX_HDR_SIZE_32) + if len(aux_header_data) < self.AUX_HDR_SIZE_32: + sys.exit(f'Error: Could not read auxiliary header, got {len(aux_header_data)} bytes, expected {self.AUX_HDR_SIZE_32}') + (o_mflags, o_vstamp, o_tsize, o_dsize, o_bsize, o_entry, o_text_start, o_data_start, o_toc, + o_snentry, o_sntext, o_sndata, o_sntoc, o_snloader, o_snbss, o_algntext, o_algndata, o_modtype, + o_cpuflag, o_cputype, o_maxstack, o_maxdata, o_debugger, o_textpsize, o_datapsize, o_stackpsize, + o_flags, o_sntdata, o_sntbss) = struct.unpack(self.AUX_HDR_FORMAT_32, aux_header_data) + else: + if opthdr_size < self.AUX_HDR_SIZE: + sys.exit(f'Error: Auxiliary header size {opthdr_size} is too small for 64-bit XCOFF') + aux_header_data = file.read(self.AUX_HDR_SIZE) + if len(aux_header_data) < self.AUX_HDR_SIZE: + sys.exit(f'Error: Could not read auxiliary header, got {len(aux_header_data)} bytes, expected {self.AUX_HDR_SIZE}') + (o_mflags, o_vstamp, o_debugger, o_text_start, o_data_start, o_toc, o_snentry, o_sntext, o_sndata, + o_sntoc, o_snloader, o_snbss, o_algntext, o_algndata, o_modtype, o_cpuflag, o_cputype, o_textpsize, + o_datapsize, o_stackpsize, o_flags, o_tsize, o_dsize, o_bsize, o_entry, o_maxstack, o_maxdata, + o_sntdata, o_sntbss, o_x64flags, dummy) = struct.unpack(self.AUX_HDR_FORMAT, aux_header_data) + + self.o_algntext = int.from_bytes(o_algntext, byteorder='big') + self.o_algndata = int.from_bytes(o_algndata, byteorder='big') + self.o_snloader = int.from_bytes(o_snloader, byteorder='big') + + +class XcoffSectionHeader: + # XCOFF section header + SCN_HDR_SIZE = 72 + SCN_HDR_SIZE_32 = 40 + SCN_HDR_FORMAT = '8s 8s 8s 8s 8s 8s 8s 4s 4s 4s 4s' + SCN_HDR_FORMAT_32 = '8s 4s 4s 4s 4s 4s 4s 2s 2s 2s 2s' + + def __init__(self, file: T.BinaryIO, magic: int) -> None: + if magic == 0x01DF: + scn_header_data = file.read(self.SCN_HDR_SIZE_32) + if len(scn_header_data) < self.SCN_HDR_SIZE_32: + raise EOFError('End of archive member reached') + (s_name, s_paddr, s_vaddr, s_size, s_scnptr, s_relptr, s_lnnoptr, s_nreloc, s_nlnno, s_flags, + dummy) = struct.unpack(self.SCN_HDR_FORMAT_32, scn_header_data) + else: + scn_header_data = file.read(self.SCN_HDR_SIZE) + if len(scn_header_data) < self.SCN_HDR_SIZE: + raise EOFError('End of archive member reached') + (s_name, s_paddr, s_vaddr, s_size, s_scnptr, s_relptr, s_lnnoptr, s_nreloc, s_nlnno, s_flags, + dummy) = struct.unpack(self.SCN_HDR_FORMAT, scn_header_data) + self.s_scnptr = int.from_bytes(s_scnptr, byteorder='big') + + +class XcoffLoaderHeader: + # XCOFF loader header + LDR_HDR_SIZE = 56 + LDR_HDR_SIZE_32 = 32 + LDR_HDR_FORMAT = '4s 4s 4s 4s 4s 4s 8s 8s 8s 8s' + LDR_HDR_FORMAT_32 = '4s 4s 4s 4s 4s 4s 4s 4s' + + def __init__(self, file: T.BinaryIO, magic: int) -> None: + if magic == 0x01DF: + ldr_header_data = file.read(self.LDR_HDR_SIZE_32) + (l_version, l_nsyms, l_nreloc, l_istlen, l_nimpid, l_impoff, l_stlen, + l_stoff) = struct.unpack(self.LDR_HDR_FORMAT_32, ldr_header_data) + else: + ldr_header_data = file.read(self.LDR_HDR_SIZE) + (l_version, l_nsyms, l_nreloc, l_istlen, l_nimpid, l_stlen, l_impoff, l_stoff, l_symoff, + l_rldoff) = struct.unpack(self.LDR_HDR_FORMAT, ldr_header_data) + self.l_impoff = int.from_bytes(l_impoff, byteorder='big') + self.l_istlen = int.from_bytes(l_istlen, byteorder='big') + + +def traverse_xcoff(file: T.BinaryIO, rpath_dirs_to_remove: T.Set[bytes], new_rpath: T.Optional[bytes], verbose: bool = True) -> None: + """Traverse XCOFF file or archive and modify Libpath entries. + + Handles both standalone XCOFF shared objects (.so) and XCOFF archives (.a). + Archives are detected by magic number and processed member by member. + """ + def dummy_print(*args: object) -> None: + pass + log_msg: T.Callable[..., None] + log_msg = print if verbose else dummy_print # type: ignore[assignment] + + # Detect if this is an archive by checking for magic number + magic_check = file.read(8) + file.seek(0) + + # Read archive headers if this is an archive + ar_header: T.Optional[XcoffArchiveHeader] = None + if magic_check == b'\n': + fl_header = XcoffFixedLengthHeader(file) + file.seek(fl_header.fl_fstmoff) + ar_header = XcoffArchiveHeader(file) + + composite_header_pos = file.tell() + # Read XCOFF magic number + magic_data = file.read(XCOFF_MAGIC_SIZE) + if len(magic_data) != XCOFF_MAGIC_SIZE: + sys.exit(0) + magic_number = struct.unpack('>H', magic_data)[0] # Big-endian 16-bit integer + if magic_number == 0x01DF: + log_msg(' Changing Libpath for a 32-bit Shared Object') + elif magic_number == 0x01F7: + log_msg(' Changing Libpath for a 64-bit Shared Object') + else: + log_msg(' Not a shared object') + sys.exit(0) + # Reposition to start of XCOFF headers + file.seek(composite_header_pos) + cfh_header = XcoffCompositeFileHeader(file, magic_number) + if not cfh_header.f_flags & 0x2000: + log_msg('Did not change rpath since not a shared library/archive') + return + # Read auxiliary header + aux_header = XcoffAuxiliaryHeader(file, magic_number, cfh_header.f_opthdr) + + # Calculate the bytes_to_align + bytes_to_align = max(aux_header.o_algntext, aux_header.o_algndata) + + # Seek to section header and read it + if magic_number == 0x01DF: + file.seek((aux_header.o_snloader - 1) * XcoffSectionHeader.SCN_HDR_SIZE_32, os.SEEK_CUR) + else: + file.seek((aux_header.o_snloader - 1) * XcoffSectionHeader.SCN_HDR_SIZE, os.SEEK_CUR) + try: + scn_header = XcoffSectionHeader(file, magic_number) + except EOFError: + return # End of archive member, nothing to fix + + header_len = 0 + if ar_header: + header_len = XcoffFixedLengthHeader.FL_HDR_SIZE + XcoffArchiveHeader.AR_HDR_SIZE + ar_header.ar_namlen + scnptrFromArchiveStart = scn_header.s_scnptr + align(header_len, bytes_to_align) + else: + scnptrFromArchiveStart = scn_header.s_scnptr + + file.seek(scnptrFromArchiveStart) + + # Read loader header + ldr_header = XcoffLoaderHeader(file, magic_number) + + if ar_header: + scnptrFromArchiveStartPlusOff = scn_header.s_scnptr + ldr_header.l_impoff + align(header_len, bytes_to_align) + else: + scnptrFromArchiveStartPlusOff = scn_header.s_scnptr + ldr_header.l_impoff + file.seek(scnptrFromArchiveStartPlusOff) + + # Read and update libpath + libpath = file.read(ldr_header.l_istlen) + build_libpath = libpath.split(b'\x00')[0] + # Build the new rpath by merging new_rpath with existing paths (excluding removed ones) + new_rpaths: OrderedSet[bytes] = OrderedSet() + if new_rpath: + new_rpaths.update(new_rpath.split(b':')) + + new_rpaths.update( + path + for path in build_libpath.split(b':') + if path and path not in rpath_dirs_to_remove + ) + + install_rpath = b':'.join(new_rpaths) + + # If the length of the build_rpath length is < install_rpath length then we do not have space to write + # the same hence exit. Otherwise, we can and the length has to be the same as build_rpath. + # If the install_rpath length is less than build_rpath length then pad the difference + if len(install_rpath) > len(build_libpath): + sys.exit('Error: install_rpath is bigger than build_rpath') + # Pad with colon bytes to match build_libpath length + install_rpath = install_rpath + (b':' * (len(build_libpath) - len(install_rpath))) + file.seek(scnptrFromArchiveStartPlusOff) + file.write(install_rpath) + log_msg(f'Successfully changed libpath from {build_libpath!r} to {install_rpath!r}') + + +def fix_aix(fname: str, rpath_dirs_to_remove: T.Set[bytes], new_rpath: T.Optional[bytes], verbose: bool = True) -> None: + """Writes Libpath to an xcoff shared object. + + In AIX, shared modules are .so files and shared libraries are in .a archives. + Follows the same calling convention as fix_elf: + - Errors: sys.exit('error message') + - Wrong file type: sys.exit(0) + - Success: return normally + """ + if new_rpath is None: + return + + try: + with open(fname, 'r+b') as file: + traverse_xcoff(file, rpath_dirs_to_remove, new_rpath, verbose) + except FileNotFoundError: + sys.exit(f'Error: File not found: {fname}') + except Exception as e: + sys.exit(f'Unexpected error processing {fname}: {e}') + + +# ----------------ELF------------------ + class DataSizes: def __init__(self, ptrsize: int, is_le: bool) -> None: if is_le: @@ -379,7 +650,7 @@ def fix_rpathtype_entry(self, fname: str, rpath_dirs_to_remove: T.Set[bytes], ne new_rpath = b':'.join(new_rpaths) if len(old_rpath) < len(new_rpath): - msg = "New rpath must not be longer than the old one.\n Old: {}\n New: {}".format(old_rpath.decode('utf-8'), new_rpath.decode('utf-8')) + msg = 'New rpath must not be longer than the old one.\n Old: {}\n New: {}'.format(old_rpath.decode('utf-8'), new_rpath.decode('utf-8')) sys.exit(msg) # The linker does read-only string deduplication. If there is a # string that shares a suffix with the rpath, they might get @@ -534,20 +805,27 @@ def fix_jar(fname: str) -> None: # than the beginning, but the spec doesn't forbid that. subprocess.check_call(['jar', 'ufM', fname, 'META-INF/MANIFEST.MF']) -def fix_rpath(fname: str, rpath_dirs_to_remove: T.Set[bytes], new_rpath: T.Union[str, bytes], final_path: str, install_name_mappings: T.Dict[str, str], verbose: bool = True) -> None: +def fix_rpath(fname: str, rpath_dirs_to_remove: T.Set[bytes], new_rpath: T.Union[str, bytes], final_path: str, install_name_mappings: T.Dict[str, str], system: str, verbose: bool = True) -> None: global INSTALL_NAME_TOOL # pylint: disable=global-statement # Static libraries, import libraries, debug information, headers, etc # never have rpaths # DLLs and EXE currently do not need runtime path fixing - if fname.endswith(('.a', '.lib', '.pdb', '.h', '.hpp', '.dll', '.exe')): + + if fname.endswith(('.lib', '.pdb', '.h', '.hpp', '.dll', '.exe')): return + # Shared libraries are .a files on AIX + if fname.endswith('.a') and system != 'aix': + return + if isinstance(new_rpath, str): + new_rpath = new_rpath.encode('utf8') try: if fname.endswith('.jar'): fix_jar(fname) return - if isinstance(new_rpath, str): - new_rpath = new_rpath.encode('utf8') - fix_elf(fname, rpath_dirs_to_remove, new_rpath, verbose) + if system == 'aix': + fix_aix(fname, rpath_dirs_to_remove, new_rpath, verbose) + else: + fix_elf(fname, rpath_dirs_to_remove, new_rpath, verbose) return except SystemExit as e: if isinstance(e.code, int) and e.code == 0: From 866d042b599195d0e6d1b489f81b58d65494ac80 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 18 Dec 2025 11:10:52 -0800 Subject: [PATCH 077/681] dependencies/base: Add build_config to DependencyObjectKWs --- mesonbuild/dependencies/base.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index 30aa4a43135d..1c1d91108fed 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -68,6 +68,7 @@ class DependencyObjectKWs(TypedDict, total=False): silent: bool tools: T.List[str] version_arg: str + build_config: str _MissingCompilerBase = Compiler else: From b5f61587c33ed0e823a9829b2658a345424349f9 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 18 Dec 2025 09:07:00 -0800 Subject: [PATCH 078/681] modules/python: rework extension_module method for typing This cleans up the typing by creating a new dictionary instead of mutating the one passed in. This gives better typing controls. I've also adding some missing keyword argument annotations, and done some casting because lists are invariant. --- mesonbuild/modules/python.py | 50 +++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py index 7a33a02ce1ec..af1410936065 100644 --- a/mesonbuild/modules/python.py +++ b/mesonbuild/modules/python.py @@ -9,7 +9,7 @@ from . import ExtensionModule, ModuleInfo from .. import mesonlib from .. import mlog -from ..build import known_shmod_kwargs, CustomTarget, CustomTargetIndex, BuildTarget, GeneratedList, StructuredSources, ExtractedObjects, SharedModule +from ..build import CustomTarget, CustomTargetIndex, BuildTarget, GeneratedList, StructuredSources, ExtractedObjects, SharedModule from ..dependencies import NotFoundDependency from ..dependencies.detect import get_dep_identifier, find_external_dependency from ..dependencies.python import BasicPythonExternalProgram, python_factory, _PythonDependencyBase @@ -17,7 +17,7 @@ from ..interpreter.interpreterobjects import ProgramHolder from ..interpreter.type_checking import NoneType, DEPENDENCY_KWS, PRESERVE_PATH_KW, REQUIRED_KW, SHARED_MOD_KWS from ..interpreterbase import ( - noPosargs, noKwargs, permittedKwargs, ContainerTypeInfo, + noPosargs, noKwargs, ContainerTypeInfo, InvalidArguments, typed_pos_args, typed_kwargs, KwargInfo, FeatureNew, disablerIfNotFound, InterpreterObject ) @@ -34,6 +34,7 @@ from ..interpreter import Interpreter from ..interpreter.interpreter import BuildTargetSource from ..interpreter.kwargs import ExtractRequired, SharedModule as SharedModuleKw, FuncDependency + from ..interpreter.type_checking import SourcesVarargsType from ..interpreterbase.baseobjects import TYPE_var, TYPE_kwargs class PyInstallKw(TypedDict): @@ -52,15 +53,12 @@ class ExtensionModuleKw(SharedModuleKw): # Yes, these are different between SharedModule and ExtensionModule install_dir: T.Union[str, bool, None] # type: ignore[misc] + limited_api: str subdir: NotRequired[T.Optional[str]] MaybePythonProg = T.Union[NonExistingExternalProgram, 'PythonExternalProgram'] -mod_kwargs = {'subdir', 'limited_api'} -mod_kwargs.update(known_shmod_kwargs) -mod_kwargs -= {'name_prefix', 'name_suffix'} - _MOD_KWARGS = [k for k in SHARED_MOD_KWS if k.name not in {'name_prefix', 'name_suffix', 'install_dir'}] @@ -139,7 +137,6 @@ def __init__(self, python: 'PythonExternalProgram', interpreter: 'Interpreter'): self.platlib_install_path = os.path.join(prefix, python.platlib) self.purelib_install_path = os.path.join(prefix, python.purelib) - @permittedKwargs(mod_kwargs) @typed_pos_args('python.extension_module', str, varargs=(str, mesonlib.File, CustomTarget, CustomTargetIndex, GeneratedList, StructuredSources, ExtractedObjects, BuildTarget)) @typed_kwargs( 'python.extension_module', @@ -150,22 +147,24 @@ def __init__(self, python: 'PythonExternalProgram', interpreter: 'Interpreter'): ) @InterpreterObject.method('extension_module') def extension_module_method(self, args: T.Tuple[str, T.List[BuildTargetSource]], kwargs: ExtensionModuleKw) -> 'SharedModule': + target_kwargs = T.cast('SharedModuleKw', {k: v for k, v in kwargs.items() if k not in {'install_dir', 'subdir', 'limited_api'}}) + if kwargs['install_dir'] is not None: if kwargs['subdir'] is not None: raise InvalidArguments('"subdir" and "install_dir" are mutually exclusive') # the build_target() method now expects this to be correct. - kwargs['install_dir'] = [kwargs['install_dir']] + target_kwargs['install_dir'] = [kwargs['install_dir']] else: # We want to remove 'subdir', but it may be None and we want to replace it with '' # It must be done this way since we don't allow both `install_dir` # and `subdir` to be set at the same time - subdir = kwargs.pop('subdir') or '' + subdir = kwargs.get('subdir') or '' - kwargs['install_dir'] = [self._get_install_dir_impl(False, subdir)] + target_kwargs['install_dir'] = [self._get_install_dir_impl(False, subdir)] target_suffix = self.suffix - new_deps = mesonlib.extract_as_list(kwargs, 'dependencies') + new_deps = kwargs['dependencies'].copy() pydep = next((dep for dep in new_deps if isinstance(dep, _PythonDependencyBase)), None) if pydep is None: pydep = self._dependency_method_impl({'native': kwargs['native']}) @@ -176,7 +175,7 @@ def extension_module_method(self, args: T.Tuple[str, T.List[BuildTargetSource]], '0.63.0', self.subproject, 'use python_installation.dependency()', self.current_node) - limited_api_version = kwargs.pop('limited_api') + limited_api_version = kwargs.get('limited_api') allow_limited_api = self.interpreter.environment.coredata.optstore.get_value_for(OptionKey('python.allow_limited_api')) if limited_api_version != '' and allow_limited_api: @@ -185,19 +184,20 @@ def extension_module_method(self, args: T.Tuple[str, T.List[BuildTargetSource]], limited_api_version_hex = self._convert_api_version_to_py_version_hex(limited_api_version, pydep.version) limited_api_definition = f'-DPy_LIMITED_API={limited_api_version_hex}' - new_c_args = mesonlib.extract_as_list(kwargs, 'c_args') + new_c_args = kwargs['c_args'].copy() new_c_args.append(limited_api_definition) - kwargs['c_args'] = new_c_args + target_kwargs['c_args'] = new_c_args - new_cpp_args = mesonlib.extract_as_list(kwargs, 'cpp_args') + new_cpp_args = kwargs['cpp_args'].copy() new_cpp_args.append(limited_api_definition) - kwargs['cpp_args'] = new_cpp_args + target_kwargs['cpp_args'] = new_cpp_args # On Windows, the limited API DLL is python3.dll, not python3X.dll. for_machine = kwargs['native'] if self.interpreter.environment.machines[for_machine].is_windows(): pydep_copy = copy.copy(pydep) - pydep_copy.find_libpy_windows(self.env, limited_api=True) + if isinstance(pydep_copy, _PythonDependencyBase): + pydep_copy.find_libpy_windows(self.env, limited_api=True) if not pydep_copy.found(): raise mesonlib.MesonException('Python dependency supporting limited API not found') @@ -214,7 +214,7 @@ def extension_module_method(self, args: T.Tuple[str, T.List[BuildTargetSource]], python_windows_debug_link_exception = f'/NODEFAULTLIB:python{pyver}_d.lib' python_windows_release_link_exception = f'/NODEFAULTLIB:python{pyver}.lib' - new_link_args = mesonlib.extract_as_list(kwargs, 'link_args') + new_link_args = kwargs['link_args'].copy() is_debug = self.interpreter.environment.coredata.optstore.get_value_for('debug') if is_debug: @@ -222,24 +222,26 @@ def extension_module_method(self, args: T.Tuple[str, T.List[BuildTargetSource]], else: new_link_args.append(python_windows_release_link_exception) - kwargs['link_args'] = new_link_args + target_kwargs['link_args'] = new_link_args - kwargs['dependencies'] = new_deps + target_kwargs['dependencies'] = new_deps # msys2's python3 has "-cpython-36m.dll", we have to be clever # FIXME: explain what the specific cleverness is here split, target_suffix = target_suffix.rsplit('.', 1) args = (args[0] + split, args[1]) - kwargs['name_prefix'] = '' - kwargs['name_suffix'] = target_suffix + target_kwargs['name_prefix'] = '' + target_kwargs['name_suffix'] = target_suffix if kwargs['gnu_symbol_visibility'] == '' and \ (self.is_pypy or mesonlib.version_compare(self.version, '>=3.9')): - kwargs['gnu_symbol_visibility'] = 'inlineshidden' + target_kwargs['gnu_symbol_visibility'] = 'inlineshidden' kwargs.setdefault('rust_abi', 'c') - return self.interpreter.build_target(self.current_node, args, kwargs, SharedModule) + return self.interpreter.build_target( + self.current_node, T.cast('T.Tuple[str, SourcesVarargsType]', args), + target_kwargs, SharedModule) def _convert_api_version_to_py_version_hex(self, api_version: str, detected_version: str) -> str: python_api_version_format = re.compile(r'[0-9]\.[0-9]{1,2}') From 7c1a7a9a23d6766a4c26a4c174d38295a85a5034 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 18 Dec 2025 09:19:18 -0800 Subject: [PATCH 079/681] modules/python: fix all remaining typing issues --- mesonbuild/modules/python.py | 26 +++++++++++++++++--------- run_mypy.py | 1 + 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py index af1410936065..a4861688a1b0 100644 --- a/mesonbuild/modules/python.py +++ b/mesonbuild/modules/python.py @@ -25,6 +25,8 @@ from ..options import OptionKey from ..programs import ExternalProgram, NonExistingExternalProgram +_T = T.TypeVar('_T') + if T.TYPE_CHECKING: from typing_extensions import TypedDict, NotRequired @@ -42,6 +44,7 @@ class PyInstallKw(TypedDict): pure: T.Optional[bool] subdir: str install_tag: T.Optional[str] + preserve_path: bool class FindInstallationKw(ExtractRequired): @@ -117,12 +120,16 @@ def __init__(self, python: 'PythonExternalProgram', interpreter: 'Interpreter'): assert isinstance(prefix, str), 'for mypy' if python.build_config: - self.version = python.build_config['language']['version'] - self.platform = python.build_config['platform'] - self.suffix = python.build_config['abi']['extension_suffix'] - self.limited_api_suffix = python.build_config['abi']['stable_abi_suffix'] - self.link_libpython = python.build_config['libpython']['link_extensions'] - self.is_pypy = python.build_config['implementation']['name'] == 'pypy' + def as_(obj: object, type_: T.Type[_T]) -> _T: + assert isinstance(obj, type_), 'for mypy' + return obj + + self.version = as_(python.build_config['language']['version'], str) + self.platform = as_(python.build_config['platform'], str) + self.suffix = as_(python.build_config['abi']['extension_suffix'], str) + self.limited_api_suffix = as_(python.build_config['abi']['stable_abi_suffix'], str) + self.link_libpython = as_(python.build_config['libpython']['link_extensions'], bool) + self.is_pypy = as_(python.build_config['implementation']['name'], str) == 'pypy' else: self.version = info['version'] self.platform = info['platform'] @@ -268,6 +275,7 @@ def _dependency_method_impl(self, kwargs: DependencyObjectKWs) -> Dependency: return dep build_config = self.interpreter.environment.coredata.optstore.get_value_for(OptionKey('python.build_config')) + assert isinstance(build_config, str), 'for mypy' new_kwargs = kwargs.copy() new_kwargs['required'] = False @@ -402,7 +410,7 @@ def __init__(self, interpreter: 'Interpreter') -> None: def _get_install_scripts(self) -> T.List[mesonlib.ExecutableSerialisation]: backend = self.interpreter.backend - ret = [] + ret: T.List[mesonlib.ExecutableSerialisation] = [] optlevel = self.interpreter.environment.coredata.optstore.get_value_for(OptionKey('python.bytecompile')) if optlevel == -1: return ret @@ -412,7 +420,7 @@ def _get_install_scripts(self) -> T.List[mesonlib.ExecutableSerialisation]: installdata = backend.create_install_data() py_files = [] - def should_append(f, isdir: bool = False): + def should_append(f: str, isdir: bool = False) -> bool: # This uses the install_plan decorated names to see if the original source was propagated via # install_sources() or get_install_dir(). return f.startswith(('{py_platlib}', '{py_purelib}')) and (f.endswith('.py') or isdir) @@ -434,7 +442,6 @@ def should_append(f, isdir: bool = False): for i in self.installations.values(): if isinstance(i, PythonExternalProgram) and i.run_bytecompile[i.info['version']]: - i = T.cast('PythonExternalProgram', i) manifest = f'python-{i.info["version"]}-installed.json' manifest_json = [] for name, f in py_files: @@ -471,6 +478,7 @@ def _get_win_pythonpath(name_or_path: str) -> T.Optional[str]: def _find_installation_impl(self, state: 'ModuleState', display_name: str, name_or_path: str, required: bool) -> MaybePythonProg: build_config = self.interpreter.environment.coredata.optstore.get_value_for(OptionKey('python.build_config')) + assert isinstance(build_config, str), 'for mypy' if not name_or_path: python = PythonExternalProgram('python3', mesonlib.python_command, build_config_path=build_config) diff --git a/run_mypy.py b/run_mypy.py index 08fe25debae0..fb67dfa20424 100755 --- a/run_mypy.py +++ b/run_mypy.py @@ -67,6 +67,7 @@ 'mesonbuild/modules/keyval.py', 'mesonbuild/modules/modtest.py', 'mesonbuild/modules/pkgconfig.py', + 'mesonbuild/modules/python.py', 'mesonbuild/modules/_qt.py', 'mesonbuild/modules/qt4.py', 'mesonbuild/modules/qt5.py', From 252877d096f680a9b3076b0bc91ba4c14dbcf90a Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 12 Dec 2025 07:57:53 -0800 Subject: [PATCH 080/681] interpreterbase: use more accurate annotations for get_callee_args --- mesonbuild/interpreterbase/decorators.py | 55 ++++++++++++++++++++---- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/mesonbuild/interpreterbase/decorators.py b/mesonbuild/interpreterbase/decorators.py index 967892e30225..c6514257d828 100644 --- a/mesonbuild/interpreterbase/decorators.py +++ b/mesonbuild/interpreterbase/decorators.py @@ -16,10 +16,14 @@ import typing as T if T.TYPE_CHECKING: - from typing_extensions import Protocol + from typing_extensions import Protocol, TypeAlias, TypeIs from .. import mparser - from .baseobjects import InterpreterObject, SubProject, TV_func, TYPE_var, TYPE_kwargs + from ..modules import ModuleObject, ModuleState + from ..mparser import FunctionNode + from ..optinterpreter import OptionInterpreter + from .baseobjects import InterpreterObject, ObjectHolder, SubProject, TV_func, TYPE_var, TYPE_kwargs + from .interpreterbase import InterpreterBase from .operator import MesonOperator _TV_IntegerObject = T.TypeVar('_TV_IntegerObject', bound=InterpreterObject, contravariant=True) @@ -29,21 +33,54 @@ class FN_Operator(Protocol[_TV_IntegerObject, _TV_ARG1]): def __call__(s, self: _TV_IntegerObject, other: _TV_ARG1) -> TYPE_var: ... _TV_FN_Operator = T.TypeVar('_TV_FN_Operator', bound=FN_Operator) -def get_callee_args(wrapped_args: T.Sequence[T.Any]) -> T.Tuple['mparser.BaseNode', T.List['TYPE_var'], 'TYPE_kwargs', 'SubProject']: - # First argument could be InterpreterBase, InterpreterObject or ModuleObject. - # In the case of a ModuleObject it is the 2nd argument (ModuleState) that - # contains the needed information. - s = wrapped_args[0] - if not hasattr(s, 'current_node'): + CalleeArgs: TypeAlias = T.Tuple[mparser.BaseNode, T.Optional[T.List[TYPE_var]], T.Optional[TYPE_kwargs], SubProject] + + +def is_module(obj: object) -> TypeIs[ModuleObject]: + return not hasattr(obj, 'current_node') + + +@T.overload +def get_callee_args(wrapped_args: T.Tuple[InterpreterObject, T.List[TYPE_var], TYPE_kwargs]) -> CalleeArgs: ... + + +@T.overload +def get_callee_args(wrapped_args: T.Tuple[ObjectHolder, object]) -> CalleeArgs: ... + + +@T.overload +def get_callee_args(wrapped_args: T.Tuple[InterpreterBase, FunctionNode, T.List[TYPE_var], TYPE_kwargs]) -> CalleeArgs: ... + + +@T.overload +def get_callee_args(wrapped_args: T.Tuple[ModuleObject, ModuleState, T.List[TYPE_var], TYPE_kwargs]) -> CalleeArgs: ... + + +@T.overload +def get_callee_args(wrapped_args: T.Tuple[OptionInterpreter, str, str, T.List[TYPE_var], TYPE_kwargs]) -> CalleeArgs: ... + + +def get_callee_args(wrapped_args: T.Union[ + T.Tuple[InterpreterObject, T.List[TYPE_var], TYPE_kwargs], + T.Tuple[ObjectHolder, object], + T.Tuple[InterpreterBase, FunctionNode, T.List[TYPE_var], TYPE_kwargs], + T.Tuple[ModuleObject, ModuleState, T.List[TYPE_var], TYPE_kwargs], + T.Tuple[OptionInterpreter, str, str, T.List[TYPE_var], TYPE_kwargs], + ]) -> CalleeArgs: + if is_module(wrapped_args[0]): s = wrapped_args[1] + else: + s = wrapped_args[0] node = s.current_node subproject = s.subproject - args = kwargs = None + args: T.Optional[T.List[TYPE_var]] = None + kwargs: T.Optional[TYPE_kwargs] = None if len(wrapped_args) >= 3: args = wrapped_args[-2] kwargs = wrapped_args[-1] return node, args, kwargs, subproject + def noPosargs(f: TV_func) -> TV_func: @wraps(f) def wrapped(*wrapped_args: T.Any, **wrapped_kwargs: T.Any) -> T.Any: From f1235fe3bbcc856da8b644be3937e30d0cb50a96 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 24 Apr 2026 15:03:16 +0200 Subject: [PATCH 081/681] depfixer: "type: ignore" not needed by newer mypy Fix rebase hiccup in #14188. Signed-off-by: Paolo Bonzini --- mesonbuild/scripts/depfixer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/scripts/depfixer.py b/mesonbuild/scripts/depfixer.py index dd74c89cafb4..b65b89bfd590 100644 --- a/mesonbuild/scripts/depfixer.py +++ b/mesonbuild/scripts/depfixer.py @@ -179,7 +179,7 @@ def traverse_xcoff(file: T.BinaryIO, rpath_dirs_to_remove: T.Set[bytes], new_rpa def dummy_print(*args: object) -> None: pass log_msg: T.Callable[..., None] - log_msg = print if verbose else dummy_print # type: ignore[assignment] + log_msg = print if verbose else dummy_print # Detect if this is an archive by checking for magic number magic_check = file.read(8) From c496e7448c6d5030149f3dba73ea9cbcc6ec4fbe Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 23 Apr 2026 14:53:49 -0700 Subject: [PATCH 082/681] modules/python3: Fix remaining type safety issues This will allow us to clear most of the old checking code away. --- mesonbuild/modules/python3.py | 30 +++++++++++++++++------------- run_mypy.py | 1 + 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/mesonbuild/modules/python3.py b/mesonbuild/modules/python3.py index 9ae7832449e2..24e12e78d7af 100644 --- a/mesonbuild/modules/python3.py +++ b/mesonbuild/modules/python3.py @@ -10,16 +10,18 @@ from . import ExtensionModule, ModuleInfo from ..build import ( BuildTarget, CustomTarget, CustomTargetIndex, ExtractedObjects, - GeneratedList, SharedModule, StructuredSources, known_shmod_kwargs + GeneratedList, SharedModule, StructuredSources ) from ..interpreter.type_checking import SHARED_MOD_KWS -from ..interpreterbase import typed_kwargs, typed_pos_args, noPosargs, noKwargs, permittedKwargs +from ..interpreterbase import typed_kwargs, typed_pos_args, noPosargs, noKwargs from ..programs import ExternalProgram if T.TYPE_CHECKING: from . import ModuleState - from ..interpreter.interpreter import BuildTargetSource + from ..interpreter.interpreter import BuildTargetSource, Interpreter from ..interpreter.kwargs import SharedModule as SharedModuleKW + from ..interpreterbase import TYPE_var, TYPE_kwargs + from ..interpreter.type_checking import SourcesVarargsType _MOD_KWARGS = [k for k in SHARED_MOD_KWS if k.name not in {'name_prefix', 'name_suffix'}] @@ -29,8 +31,8 @@ class Python3Module(ExtensionModule): INFO = ModuleInfo('python3', '0.38.0', deprecated='0.48.0') - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) + def __init__(self, interp: Interpreter): + super().__init__(interp) self.methods.update({ 'extension_module': self.extension_module, 'find_python': self.find_python, @@ -38,10 +40,9 @@ def __init__(self, *args, **kwargs): 'sysconfig_path': self.sysconfig_path, }) - @permittedKwargs(known_shmod_kwargs - {'name_prefix', 'name_suffix'}) @typed_pos_args('python3.extension_module', str, varargs=(str, mesonlib.File, CustomTarget, CustomTargetIndex, GeneratedList, StructuredSources, ExtractedObjects, BuildTarget)) @typed_kwargs('python3.extension_module', *_MOD_KWARGS) - def extension_module(self, state: ModuleState, args: T.Tuple[str, T.List[BuildTargetSource]], kwargs: SharedModuleKW): + def extension_module(self, state: ModuleState, args: T.Tuple[str, T.List[BuildTargetSource]], kwargs: SharedModuleKW) -> SharedModule: host_system = state.environment.machines.host.system if host_system == 'darwin': # Default suffix is 'dylib' but Python does not use it for extensions. @@ -53,11 +54,14 @@ def extension_module(self, state: ModuleState, args: T.Tuple[str, T.List[BuildTa suffix = None kwargs['name_prefix'] = '' kwargs['name_suffix'] = suffix - return self.interpreter.build_target(state.current_node, args, kwargs, SharedModule) + m = self.interpreter.build_target( + state.current_node, T.cast('T.Tuple[str, SourcesVarargsType]', args), kwargs, SharedModule) + assert isinstance(m, SharedModule), 'for mypy' + return m @noPosargs @noKwargs - def find_python(self, state, args, kwargs): + def find_python(self, state: ModuleState, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> ExternalProgram: command = state.environment.lookup_binary_entry(mesonlib.MachineChoice.HOST, 'python3') if command is not None: py3 = ExternalProgram.from_entry('python3', command) @@ -67,12 +71,12 @@ def find_python(self, state, args, kwargs): @noPosargs @noKwargs - def language_version(self, state, args, kwargs): + def language_version(self, state: ModuleState, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: return sysconfig.get_python_version() @noKwargs @typed_pos_args('python3.sysconfig_path', str) - def sysconfig_path(self, state, args, kwargs): + def sysconfig_path(self, state: ModuleState, args: T.Tuple[str], kwargs: TYPE_kwargs) -> str: path_name = args[0] valid_names = sysconfig.get_path_names() if path_name not in valid_names: @@ -82,5 +86,5 @@ def sysconfig_path(self, state, args, kwargs): return sysconfig.get_path(path_name, vars={'base': '', 'platbase': '', 'installed_base': ''})[1:] -def initialize(*args, **kwargs): - return Python3Module(*args, **kwargs) +def initialize(interp: Interpreter) -> Python3Module: + return Python3Module(interp) diff --git a/run_mypy.py b/run_mypy.py index fb67dfa20424..524b4f85bbdf 100755 --- a/run_mypy.py +++ b/run_mypy.py @@ -68,6 +68,7 @@ 'mesonbuild/modules/modtest.py', 'mesonbuild/modules/pkgconfig.py', 'mesonbuild/modules/python.py', + 'mesonbuild/modules/python3.py', 'mesonbuild/modules/_qt.py', 'mesonbuild/modules/qt4.py', 'mesonbuild/modules/qt5.py', From ee89525b91cf7d4e445dd069efc3d121f566488b Mon Sep 17 00:00:00 2001 From: Mohamed Akram Date: Tue, 21 Apr 2026 22:28:13 +0400 Subject: [PATCH 083/681] build: fix install name when linking with static library A static library can specify a dynamic library in `link_with`. --- mesonbuild/build.py | 3 --- test cases/darwin/1 rpath removal on install/main.c | 5 +++++ .../darwin/1 rpath removal on install/meson.build | 11 ++++++++++- unittests/darwintests.py | 8 ++++++++ 4 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 test cases/darwin/1 rpath removal on install/main.c diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 6986a6c575f9..7cc2dc092000 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2451,9 +2451,6 @@ def determine_default_prefix_and_suffix(self) -> T.Tuple[str, str]: suffix = 'ma' return (prefix, suffix) - def get_link_deps_mapping(self, prefix: str) -> T.Mapping[str, str]: - return {} - def get_default_install_dir(self) -> T.Tuple[str, str]: return self.environment.get_static_lib_dir(), '{libdir_static}' diff --git a/test cases/darwin/1 rpath removal on install/main.c b/test cases/darwin/1 rpath removal on install/main.c new file mode 100644 index 000000000000..735a406ce434 --- /dev/null +++ b/test cases/darwin/1 rpath removal on install/main.c @@ -0,0 +1,5 @@ +#include "foo/foo.h" + +int main() { + foo(); +} diff --git a/test cases/darwin/1 rpath removal on install/meson.build b/test cases/darwin/1 rpath removal on install/meson.build index 093d7deba3a8..5ce4c722a42a 100644 --- a/test cases/darwin/1 rpath removal on install/meson.build +++ b/test cases/darwin/1 rpath removal on install/meson.build @@ -5,4 +5,13 @@ subdir('foo') bar = library('bar', 'bar.c', link_with: foo, install: true, -) \ No newline at end of file +) + +bar_internal = static_library('bar-internal', 'bar.c', + link_with: foo, +) + +main = executable('main', 'main.c', + link_with: bar_internal, + install: true, +) diff --git a/unittests/darwintests.py b/unittests/darwintests.py index a7345b3c3092..fe292dd5cc1f 100644 --- a/unittests/darwintests.py +++ b/unittests/darwintests.py @@ -108,6 +108,12 @@ def _get_darwin_rpaths(self, fname: str) -> T.List[str]: rpaths = pattern.findall(out) return rpaths + def _get_darwin_rpath_libraries(self, fname: str) -> T.List[str]: + out = subprocess.check_output(['otool', '-L', fname], universal_newlines=True) + pattern = re.compile(r'@rpath/\S+') + libs = pattern.findall(out) + return libs + @skipIfNoPkgconfig def test_library_versioning(self): ''' @@ -174,6 +180,8 @@ def test_darwin_meson_rpaths_removed_on_install(self): # Those RPATHs are no longer valid and should not be present after installation rpaths = self._get_darwin_rpaths(os.path.join(self.installdir, 'usr/lib/libbar.dylib')) self.assertListEqual(rpaths, []) + libs = self._get_darwin_rpath_libraries(os.path.join(self.installdir, 'usr/bin/main')) + self.assertListEqual(libs, []) @skip_if_not_language('rust') def test_rust_apple_framework_rlib(self): From dd2c4203e9a4670ae9e643821fdaa33c9802193f Mon Sep 17 00:00:00 2001 From: Mohamed Akram Date: Sun, 26 Apr 2026 12:51:27 +0400 Subject: [PATCH 084/681] build: fix install name when using link_whole --- mesonbuild/build.py | 2 +- test cases/darwin/1 rpath removal on install/meson.build | 5 +++++ unittests/darwintests.py | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 7cc2dc092000..703feba80b9f 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -1331,7 +1331,7 @@ def get_link_deps_mapping(self, prefix: str) -> T.Mapping[str, str]: @lru_cache(maxsize=None) def get_transitive_link_deps_mapping(self, prefix: str) -> T.Mapping[str, str]: result: T.Dict[str, str] = {} - for i in self.link_targets: + for i in itertools.chain(self.link_targets, self.link_whole_targets): mapping = i.get_link_deps_mapping(prefix) #we are merging two dictionaries, while keeping the earlier one dominant result_tmp = mapping.copy() diff --git a/test cases/darwin/1 rpath removal on install/meson.build b/test cases/darwin/1 rpath removal on install/meson.build index 5ce4c722a42a..512701e46e6d 100644 --- a/test cases/darwin/1 rpath removal on install/meson.build +++ b/test cases/darwin/1 rpath removal on install/meson.build @@ -15,3 +15,8 @@ main = executable('main', 'main.c', link_with: bar_internal, install: true, ) + +main_whole = executable('main-whole', 'main.c', + link_whole: bar_internal, + install: true, +) diff --git a/unittests/darwintests.py b/unittests/darwintests.py index fe292dd5cc1f..7b31168538e9 100644 --- a/unittests/darwintests.py +++ b/unittests/darwintests.py @@ -182,6 +182,8 @@ def test_darwin_meson_rpaths_removed_on_install(self): self.assertListEqual(rpaths, []) libs = self._get_darwin_rpath_libraries(os.path.join(self.installdir, 'usr/bin/main')) self.assertListEqual(libs, []) + libs = self._get_darwin_rpath_libraries(os.path.join(self.installdir, 'usr/bin/main-whole')) + self.assertListEqual(libs, []) @skip_if_not_language('rust') def test_rust_apple_framework_rlib(self): From 16a74728022d35a0be80262a5a86daeba7d3db53 Mon Sep 17 00:00:00 2001 From: Sam James Date: Fri, 24 Apr 2026 01:57:09 +0100 Subject: [PATCH 085/681] mtest: support --exclude Support `--exclude name` where `name` is a full test name, optionally including the subproject name. Wildcards are not currently supported. Accept both `name` for the main project and `subproject:name`. We want `name` for convenience so users don't have to write out their project name when no subprojects are involved. Extended matching could be added in future, but the primary usecase for this is where distributions want to skip known-buggy or irrelevant tests, rather than developers working on their own project. The test changes are a bit noisy because needed to add a test with the same name in the main project and a subproject to check that --exclude w/ an unqualified name only affected the main parent, but adding 2 new failing tests required all the numbers to be adjusted. Closes: https://github.com/mesonbuild/meson/pull/11502 Closes: https://github.com/mesonbuild/meson/issues/6999 Signed-off-by: Sam James --- docs/markdown/Unit-tests.md | 26 ++++++++++ docs/markdown/snippets/tests-exclude.md | 6 +++ man/meson.1 | 3 ++ mesonbuild/mtest.py | 16 +++++- test cases/unit/4 suite selection/meson.build | 4 ++ .../subprojects/subprjfail/meson.build | 4 ++ unittests/allplatformstests.py | 49 ++++++++++--------- 7 files changed, 83 insertions(+), 25 deletions(-) create mode 100644 docs/markdown/snippets/tests-exclude.md diff --git a/docs/markdown/Unit-tests.md b/docs/markdown/Unit-tests.md index ab24d4b027e1..498ec4f9a18e 100644 --- a/docs/markdown/Unit-tests.md +++ b/docs/markdown/Unit-tests.md @@ -216,6 +216,32 @@ into `n` slices and execute the `ith` such slice. This allows you to distribute a set of long-running tests across multiple machines to decrease the overall runtime of tests. +Since version *1.12.0*, you can pass `--exclude NAME` to skip processing +of named tests: +```console +$ meson test --list +m:basic +m:buggy + +$ meson test +... +1/2 m:basic OK 0.01s +2/2 m:buggy FAIL 0.00s ... + +Ok: 1 +Fail: 1 + +$ meson test --exclude buggy +... +1/1 m:basic OK 0.00s + +Ok: 1 +Fail: 0 +``` + +For unqualified names (no subproject specified), `--exclude NAME` matches +the main project. + ### Other test options Sometimes you need to run the tests multiple times, which is done like this: diff --git a/docs/markdown/snippets/tests-exclude.md b/docs/markdown/snippets/tests-exclude.md new file mode 100644 index 000000000000..daf282ef59a6 --- /dev/null +++ b/docs/markdown/snippets/tests-exclude.md @@ -0,0 +1,6 @@ +## `meson test` now accepts `--exclude` + +`meson test` has a new `--exclude` argument to allow skipping named +tests. It takes a full test name and can be specified repeatedly. This +should help distributions that need to skip tests irrelevant for them +or known to be buggy. diff --git a/man/meson.1 b/man/meson.1 index c00d90e9bfdd..b655e299d534 100644 --- a/man/meson.1 +++ b/man/meson.1 @@ -303,6 +303,9 @@ run tests in this suite \fB\-\-no\-suite\fR do not run tests in this suite .TP +\fB\-\-exclude\fR +do not run named tests +.TP \fB\-\-no\-stdsplit\fR do not split stderr and stdout in test logs .TP diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index 18760eb51ecd..fb97f5beefc9 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -150,6 +150,8 @@ def add_arguments(parser: argparse.ArgumentParser) -> None: help='Only run tests belonging to the given suite.') parser.add_argument('--no-suite', default=[], dest='exclude_suites', action='append', metavar='SUITE', help='Do not run tests belonging to the given suite.') + parser.add_argument('--exclude', default=[], dest='exclude', action='append', + help='Exclude tests with the given name.') parser.add_argument('--no-stdsplit', default=True, dest='split', action='store_false', help='Do not split stderr and stdout in test logs.') parser.add_argument('--print-errorlogs', default=False, action='store_true', @@ -1970,10 +1972,18 @@ def test_in_suites(test: TestSerialisation, suites: T.List[str]) -> bool: return True return False - def test_suitable(self, test: TestSerialisation) -> bool: + def test_suitable(self, test: TestSerialisation, excluded_tests: set[str]) -> bool: if TestHarness.test_in_suites(test, self.options.exclude_suites): return False + # Accept both --exclude name and --exclude subproject:name. + # For the main project, we also accept 'name' without qualification + # for convenience. + if self.build_data.project_name == test.project_name and test.name in excluded_tests: + return False + if f'{test.project_name}:{test.name}' in excluded_tests: + return False + if self.options.include_suites: # Both force inclusion (overriding add_test_setup) and exclude # everything else @@ -2044,7 +2054,9 @@ def get_tests(self, errorfile: T.Optional[T.IO] = None) -> T.List[TestSerialisat print('No tests defined.', file=errorfile) return [] - tests = [t for t in self.tests if self.test_suitable(t)] + excluded_tests = set(self.options.exclude) + tests = [t for t in self.tests if self.test_suitable(t, excluded_tests)] + if self.options.args: tests = list(self.tests_from_args(tests)) if self.options.slice: diff --git a/test cases/unit/4 suite selection/meson.build b/test cases/unit/4 suite selection/meson.build index ea6db923cb84..e65d16baae87 100644 --- a/test cases/unit/4 suite selection/meson.build +++ b/test cases/unit/4 suite selection/meson.build @@ -15,3 +15,7 @@ test('mainprj-successful_test', test('mainprj-successful_test_no_suite', executable('no_suite_test', 'successful_test.c'), suite : []) + +test('buggy', + executable('failing_test_name_collision', 'failing_test.c'), + suite : 'fail') diff --git a/test cases/unit/4 suite selection/subprojects/subprjfail/meson.build b/test cases/unit/4 suite selection/subprojects/subprjfail/meson.build index e6270a8cf06c..ee6c6c2c03b4 100644 --- a/test cases/unit/4 suite selection/subprojects/subprjfail/meson.build +++ b/test cases/unit/4 suite selection/subprojects/subprjfail/meson.build @@ -7,3 +7,7 @@ test('subprjfail-failing_test', test('subprjfail-failing_test_no_suite', executable('failing_test_no_suite', 'failing_test.c'), suite : []) + +test('buggy', + executable('failing_test_name_collision', 'failing_test.c'), + suite : 'fail') diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 2769370bbfe4..4fdc9c04d6d1 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -906,48 +906,51 @@ def test_suite_selection(self): self.init(testdir) self.build() - self.assertFailedTestCount(4, self.mtest_command) + self.assertFailedTestCount(6, self.mtest_command) + self.assertFailedTestCount(4, self.mtest_command + ['--exclude', 'mainprj-failing_test', + '--exclude', 'subprjfail:subprjfail-failing_test_no_suite']) + self.assertFailedTestCount(5, self.mtest_command + ['--exclude', 'buggy']) self.assertFailedTestCount(0, self.mtest_command + ['--suite', ':success']) - self.assertFailedTestCount(3, self.mtest_command + ['--suite', ':fail']) - self.assertFailedTestCount(4, self.mtest_command + ['--no-suite', ':success']) + self.assertFailedTestCount(5, self.mtest_command + ['--suite', ':fail']) + self.assertFailedTestCount(6, self.mtest_command + ['--no-suite', ':success']) self.assertFailedTestCount(1, self.mtest_command + ['--no-suite', ':fail']) - self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'mainprj']) + self.assertFailedTestCount(2, self.mtest_command + ['--suite', 'mainprj']) self.assertFailedTestCount(0, self.mtest_command + ['--suite', 'subprjsucc']) - self.assertFailedTestCount(2, self.mtest_command + ['--suite', 'subprjfail']) + self.assertFailedTestCount(3, self.mtest_command + ['--suite', 'subprjfail']) self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'subprjmix']) - self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', 'mainprj']) - self.assertFailedTestCount(4, self.mtest_command + ['--no-suite', 'subprjsucc']) - self.assertFailedTestCount(2, self.mtest_command + ['--no-suite', 'subprjfail']) - self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', 'subprjmix']) + self.assertFailedTestCount(4, self.mtest_command + ['--no-suite', 'mainprj']) + self.assertFailedTestCount(6, self.mtest_command + ['--no-suite', 'subprjsucc']) + self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', 'subprjfail']) + self.assertFailedTestCount(5, self.mtest_command + ['--no-suite', 'subprjmix']) - self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'mainprj:fail']) + self.assertFailedTestCount(2, self.mtest_command + ['--suite', 'mainprj:fail']) self.assertFailedTestCount(0, self.mtest_command + ['--suite', 'mainprj:success']) - self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', 'mainprj:fail']) - self.assertFailedTestCount(4, self.mtest_command + ['--no-suite', 'mainprj:success']) + self.assertFailedTestCount(4, self.mtest_command + ['--no-suite', 'mainprj:fail']) + self.assertFailedTestCount(6, self.mtest_command + ['--no-suite', 'mainprj:success']) - self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'subprjfail:fail']) + self.assertFailedTestCount(2, self.mtest_command + ['--suite', 'subprjfail:fail']) self.assertFailedTestCount(0, self.mtest_command + ['--suite', 'subprjfail:success']) - self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', 'subprjfail:fail']) - self.assertFailedTestCount(4, self.mtest_command + ['--no-suite', 'subprjfail:success']) + self.assertFailedTestCount(4, self.mtest_command + ['--no-suite', 'subprjfail:fail']) + self.assertFailedTestCount(6, self.mtest_command + ['--no-suite', 'subprjfail:success']) self.assertFailedTestCount(0, self.mtest_command + ['--suite', 'subprjsucc:fail']) self.assertFailedTestCount(0, self.mtest_command + ['--suite', 'subprjsucc:success']) - self.assertFailedTestCount(4, self.mtest_command + ['--no-suite', 'subprjsucc:fail']) - self.assertFailedTestCount(4, self.mtest_command + ['--no-suite', 'subprjsucc:success']) + self.assertFailedTestCount(6, self.mtest_command + ['--no-suite', 'subprjsucc:fail']) + self.assertFailedTestCount(6, self.mtest_command + ['--no-suite', 'subprjsucc:success']) self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'subprjmix:fail']) self.assertFailedTestCount(0, self.mtest_command + ['--suite', 'subprjmix:success']) - self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', 'subprjmix:fail']) - self.assertFailedTestCount(4, self.mtest_command + ['--no-suite', 'subprjmix:success']) + self.assertFailedTestCount(5, self.mtest_command + ['--no-suite', 'subprjmix:fail']) + self.assertFailedTestCount(6, self.mtest_command + ['--no-suite', 'subprjmix:success']) - self.assertFailedTestCount(3, self.mtest_command + ['--suite', 'subprjfail', '--suite', 'subprjmix:fail']) - self.assertFailedTestCount(4, self.mtest_command + ['--suite', 'subprjfail', '--suite', 'subprjmix', '--suite', 'mainprj']) - self.assertFailedTestCount(3, self.mtest_command + ['--suite', 'subprjfail', '--suite', 'subprjmix', '--suite', 'mainprj', '--no-suite', 'subprjmix:fail']) + self.assertFailedTestCount(4, self.mtest_command + ['--suite', 'subprjfail', '--suite', 'subprjmix:fail']) + self.assertFailedTestCount(6, self.mtest_command + ['--suite', 'subprjfail', '--suite', 'subprjmix', '--suite', 'mainprj']) + self.assertFailedTestCount(5, self.mtest_command + ['--suite', 'subprjfail', '--suite', 'subprjmix', '--suite', 'mainprj', '--no-suite', 'subprjmix:fail']) self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'subprjfail', '--suite', 'subprjmix', '--suite', 'mainprj', '--no-suite', 'subprjmix:fail', 'mainprj-failing_test']) - self.assertFailedTestCount(2, self.mtest_command + ['--no-suite', 'subprjfail:fail', '--no-suite', 'subprjmix:fail']) + self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', 'subprjfail:fail', '--no-suite', 'subprjmix:fail']) def test_mtest_reconfigure(self): if self.backend is not Backend.ninja: From 101f7b395ae0b933592bac387f960ed0688b06ee Mon Sep 17 00:00:00 2001 From: Jon Turney Date: Tue, 17 Mar 2026 20:17:46 +0000 Subject: [PATCH 086/681] meson-test-run.xml: validate against JUnit XSD, if possible If lxml is available, validate the meson-test-run.xml output of run_project_tests against the JUnit XML Schema Definition. Require that to happen in CI, to ensure that test gets run *somewhere* to ensure such errors are noticed. Future work: it doesn't seem possible to get anything other than the *first* error out of the validator? --- .github/workflows/lint.yml | 2 +- run_project_tests.py | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 617d9a6ecdd6..1af3bcd40e6d 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -53,7 +53,7 @@ jobs: with: python-version: '3.x' # Pin mypy to version 1.19, so we retain the ability to lint for Python 3.10 - - run: python -m pip install "mypy==1.19" strictyaml truststore types-PyYAML types-tqdm types-chevron + - run: python -m pip install "mypy==1.19" strictyaml truststore types-PyYAML types-tqdm types-chevron types-lxml - run: python run_mypy.py --allver env: PYTHONUNBUFFERED: 1 diff --git a/run_project_tests.py b/run_project_tests.py index 13ac3f9e39c0..71eae59fbd7b 100755 --- a/run_project_tests.py +++ b/run_project_tests.py @@ -27,10 +27,21 @@ import tempfile import time import typing as T -import xml.etree.ElementTree as ET import collections import importlib.util +# use lxml, if available, otherwise fallback to xml.etree +try: + import lxml.etree as ET +except ImportError: + # assert that happens somewhere in our CI (although not everywhere, as we + # don't want to have to always install lxml), so that the validation which + # requires it, gets run. + if os.environ.get('MESON_CI_JOBNAME', 'thirdparty') == 'linux-fedora-gcc': + raise + + import xml.etree.ElementTree as ET # type: ignore + from mesonbuild import build from mesonbuild import environment from mesonbuild import compilers @@ -1374,6 +1385,12 @@ def tqdm_print(*args: mlog.TV_Loggable, sep: str = ' ') -> None: print("Total build time: %.2fs" % build_time) print("Total test time: %.2fs" % test_time) ET.ElementTree(element=junit_root).write(xmlname, xml_declaration=True, encoding='UTF-8') + + # validate the JUnit XML output against the JUnit schema, if possible + if hasattr(ET, 'XMLSchema'): + junit_schema = ET.XMLSchema(file='./data/schema.xsd') + junit_schema.assertValid(junit_root) + return passing_tests, failing_tests, skipped_tests def check_meson_commands_work(use_tmpdir: bool, extra_args: T.List[str]) -> None: From ed4fa41febb6cc25e9c4fe964fa0e6b2ba2074c4 Mon Sep 17 00:00:00 2001 From: Jon Turney Date: Sun, 22 Mar 2026 17:50:41 +0000 Subject: [PATCH 087/681] meson-test-run.xml: Replace characters which are illegal in XML lxml is a lot more fussy about not stuffing random binary data into our XML file, raising the following exception: > ValueError: All strings must be XML compatible: Unicode or ASCII, no NULL bytes or control characters Replace characters which are illegal in XML with a printable representation, by using the utility which exists in mtest for exactly that purpose here as well. --- run_project_tests.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/run_project_tests.py b/run_project_tests.py index 71eae59fbd7b..2d4e4e7f4890 100755 --- a/run_project_tests.py +++ b/run_project_tests.py @@ -1360,6 +1360,7 @@ def tqdm_print(*args: mlog.TV_Loggable, sep: str = ' ') -> None: mesonlib.windows_proof_rm(abspath) else: mesonlib.windows_proof_rmtree(abspath) + conf_time += result.conftime build_time += result.buildtime test_time += result.testtime @@ -1373,9 +1374,9 @@ def tqdm_print(*args: mlog.TV_Loggable, sep: str = ' ') -> None: if result.msg != '': ET.SubElement(current_test, 'failure', {'message': result.msg}) stdoel = ET.SubElement(current_test, 'system-out') - stdoel.text = result.stdo + stdoel.text = mtest.replace_unencodable_xml_chars(result.stdo) stdeel = ET.SubElement(current_test, 'system-err') - stdeel.text = result.stde + stdeel.text = mtest.replace_unencodable_xml_chars(result.stde) # Reset, just in case safe_print = default_print From 87168b0d63be396c85fa24043e3574a2f4772fd6 Mon Sep 17 00:00:00 2001 From: Jon Turney Date: Sat, 21 Mar 2026 23:52:11 +0000 Subject: [PATCH 088/681] mtest: Fix tty detection Check if the stdout we're connected to, rather than fd 1, is a tty. Specifically, this can happen when invoked via run_project_tests's run_test_inprocess(), which connects stdout to a StringIO() object. Also, don't print the 'erase line' sequence ('ESC[K') if output isn't a tty. Sending an ECMA-48 ("ANSI escape") sequence to a non-tty (in the absence of an explicit configuration to request it) doesn't make much sense. Future work: Maybe 'meson check' needs a specialized reporting mode more suited to non-interactive output? --- mesonbuild/mtest.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index fb97f5beefc9..74748a27992b 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -557,7 +557,7 @@ def __init__(self, max_lines: int) -> None: self.test_count = 0 self.started_tests = 0 self.spinner_index = 0 - self.is_tty = os.isatty(1) + self.is_tty = sys.stdout.isatty() self.cols = 80 if self.is_tty: try: @@ -584,7 +584,10 @@ def flush(self) -> None: def print_progress(self, line: str) -> None: print(self.should_erase_line, line, sep='', end='\r') - self.should_erase_line = '\x1b[K' + if self.is_tty: + self.should_erase_line = '\x1b[K' + else: + self.should_erase_line = '\n' def request_update(self) -> None: self.update.set() From a5129b150e26b2d7e5d07682eaf297bf0412595b Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 27 Apr 2026 09:48:30 -0700 Subject: [PATCH 089/681] CI: fix macos job trying to upgrade brew managed pip with pip managed pip --- .github/workflows/macos.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index fe89b6529f5f..e1e228369fd4 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -43,8 +43,8 @@ jobs: # productive -- only victim blaming -- however it bites particularly badly because this is a container/VM # See commit 5c479d7a13a518c18ccb4dc3b6bdd7bfc2a9bdb5 for a more thorough analysis. find /opt/homebrew/Cellar/python* -name EXTERNALLY-MANAGED -print0 | xargs -0 rm -vf - - run: | - python3 -m pip install --upgrade pip + - name: Install Python dependencies + run: | python3 -m pip install pytest pytest-xdist pytest-subtests fastjsonschema - run: brew install pkg-config ninja llvm qt@5 - env: From 678f450e1020c0381a52a747bcbdd075885a5b40 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 27 Apr 2026 09:58:53 -0700 Subject: [PATCH 090/681] CI/windows: Don't run on changes to non-windows CI yaml files --- .github/workflows/windows.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 278620369f18..53d14fa52733 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -10,7 +10,7 @@ on: - 'mesonbuild/**' - 'test cases/**' - 'unittests/**' - - '.github/workflows/*.yml' + - '.github/workflows/windows.yml' - 'ci/run.ps1' - 'run_project_tests.py' - 'run_tests.py' @@ -20,7 +20,7 @@ on: - 'mesonbuild/**' - 'test cases/**' - 'unittests/**' - - '.github/workflows/*.yml' + - '.github/workflows/windows.yml' - 'ci/run.ps1' - 'run_project_tests.py' - 'run_tests.py' From 07c0aa75f479c39ac6ce661709a14c89fcccf61b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volker=20Wei=C3=9Fmann?= Date: Sun, 26 Apr 2026 22:18:54 +0200 Subject: [PATCH 091/681] AstInterpreter: Slight Performance Improvement Creating an Object of type `UndefinedVariable` takes longer than assigning `None`, and `get_cur_value_if_defined` is executed a lot of times when you run `meson configure` without a build dir. See also: #15743 --- mesonbuild/ast/interpreter.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/mesonbuild/ast/interpreter.py b/mesonbuild/ast/interpreter.py index d3afb4de3006..3ec6c85a070b 100644 --- a/mesonbuild/ast/interpreter.py +++ b/mesonbuild/ast/interpreter.py @@ -494,14 +494,12 @@ def func_files(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, def get_cur_value_if_defined(self, var_name: str) -> T.Union[BaseNode, UnknownValue, UndefinedVariable]: if var_name in self.predefined_vars: return self.predefined_vars[var_name] - ret: T.Union[BaseNode, UnknownValue, UndefinedVariable] = UndefinedVariable() for nesting, value in reversed(self.cur_assignments[var_name]): if len(self.nesting) >= len(nesting) and self.nesting[:len(nesting)] == nesting: - ret = value - break - if isinstance(ret, UndefinedVariable) and self.tainted: + return value + if self.tainted: return UnknownValue() - return ret + return UndefinedVariable() def get_cur_value(self, var_name: str) -> T.Union[BaseNode, UnknownValue]: ret = self.get_cur_value_if_defined(var_name) From fe2c96ba02a261655cfdfd984380426e027bc6cb Mon Sep 17 00:00:00 2001 From: Jon Turney Date: Fri, 24 Apr 2026 23:12:35 +0100 Subject: [PATCH 092/681] CI: macos: Actually pass MESON_ARGS into meson At the moment, this purports to test unity builds on macos, but in fact we don't. --- .github/workflows/macos.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index e1e228369fd4..daf8d378a852 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -109,4 +109,4 @@ jobs: # We need this to avoid objfw test failures. export PATH="$HOME/tools:/opt/homebrew/opt/qt@5/bin:/opt/homebrew/opt/ncurses/bin:$PATH:/opt/homebrew/opt/llvm/bin" export PKG_CONFIG_PATH="/opt/homebrew/opt/qt@5/lib/pkgconfig:/opt/homebrew/opt/lapack/lib/pkgconfig:/opt/homebrew/opt/ncurses/lib/pkgconfig:$PKG_CONFIG_PATH" - ./run_project_tests.py --backend=ninja + ./run_project_tests.py --backend=ninja -- $MESON_ARGS From 07f76044e5a75a4494448ee0d17e224e4e7ce2a6 Mon Sep 17 00:00:00 2001 From: Jon Turney Date: Sun, 26 Apr 2026 16:29:25 +0100 Subject: [PATCH 093/681] Fix '131 override options' test with '--unity=off' As currently written, the 'common/131 override options' test fails when meson is invoked with an explicit '--unity=off' (since that takes precedence over the project level default). I'm not entirely sure of the intent of this test when first introduced in PR #1457 was to also test 'default_options', or if that's just a side-effect of the way it's written. It seems like we have some testing of that elsewhere. --- test cases/common/131 override options/meson.build | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test cases/common/131 override options/meson.build b/test cases/common/131 override options/meson.build index 4dd8d797ef84..788a816119ec 100644 --- a/test cases/common/131 override options/meson.build +++ b/test cases/common/131 override options/meson.build @@ -1,6 +1,8 @@ -project('option override', 'c', - default_options : 'unity=on') +project('option override', 'c') + +# exercise target-level option override +executable('mustunity', 'one.c', 'two.c', + override_options : ['unity=on']) -executable('mustunity', 'one.c', 'two.c') executable('notunity', 'three.c', 'four.c', override_options : ['unity=off']) From 6c3d7fff66adec72e3f8543aabf728c9388ae6fb Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Wed, 15 Oct 2025 23:07:40 -0400 Subject: [PATCH 094/681] devenv: drop incomprehensible fish support We don't test shell integration in CI -- as is obvious from the fact that commit 202d15d01b09ebb523d886b98ac668eb666de70d added additional code that, in the testsuite would fatally error. I don't think it is a good idea to add hacky workarounds for software nobody understands or uses and isn't tested in CI. Which is a description that has always fit "fish" like a glove (it is a shell that doesn't even support being run as a login shell, among numerous other issues). In https://github.com/mesonbuild/meson/pull/15086#pullrequestreview-3302711709 I raised concerns about its inclusion, but due to the holiday season was unable to respond in the narrow window of time between being asked for my reasons and the PR being merged without review. Still, the zsh changes are great, let's keep them. Zsh is a widely used shell that is well understood without hacks. --- mesonbuild/mdevenv.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/mesonbuild/mdevenv.py b/mesonbuild/mdevenv.py index bd1ed134a529..a5110435ab38 100644 --- a/mesonbuild/mdevenv.py +++ b/mesonbuild/mdevenv.py @@ -6,7 +6,6 @@ import shutil import sys import itertools -import signal import typing as T from pathlib import Path @@ -245,18 +244,6 @@ def run(options: argparse.Namespace) -> int: tmprc.flush() args.append("--rcfile") args.append(tmprc.name) - elif args[0].endswith('fish'): - # Ignore SIGINT while using fish as the shell to make it behave - # like other shells such as bash and zsh. - # See: https://gitlab.freedesktop.org/gstreamer/gst-build/issues/18 - signal.signal(signal.SIGINT, lambda _, __: True) - if prompt_prefix: - args.append('--init-command') - prompt_cmd = f'''functions --copy fish_prompt original_fish_prompt - function fish_prompt - echo -n '[{prompt_prefix}] '(original_fish_prompt) - end''' - args.append(prompt_cmd) elif args[0].endswith('zsh'): # Let the GC remove the tmp file tmpdir = tempfile.TemporaryDirectory() From ae0259e5c75c98920c184a7ce008055433ac513f Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Wed, 15 Oct 2025 23:22:30 -0400 Subject: [PATCH 095/681] actually do pass encoding to file I/O It was proposed to be added, but then questioned and removed in https://github.com/mesonbuild/meson/pull/15086#discussion_r2405334384 > I'm not sure this is correct. IIUC, this file is passed as a > configuration file to bash thus it needs to be written with the system > encoding, which in Python should be what you get when encoding is not > specified. It is insane(ly broken) to use a non-ascii-compatible encoding, and we exclusively write ascii. We also test in CI that the encoding= argument is *explicitly* passed, and we do so precisely because python 3.15 changes the meaning of `encoding=None` from "system encoding" to "utf-8", so in the most absolute sense, it is simply wrong to review an `encoding=` PR and say to remove the encoding to get the system encoding. The bash logic has had that problem forever; the zsh logic only since the recent PR. Switch both to explicitly require utf-8, which is ascii-compatible and everyone should use anyway. Fixes: ``` Traceback (most recent call last): File ".../mesonbuild/mesonmain.py", line 193, in run return options.run_func(options) ~~~~~~~~~~~~~~~~^^^^^^^^^ File ".../mesonbuild/mdevenv.py", line 226, in run tmprc = tempfile.NamedTemporaryFile(mode='w') File "/usr/lib/python3.13/tempfile.py", line 574, in NamedTemporaryFile encoding = _io.text_encoding(encoding) EncodingWarning: 'encoding' argument not specified ERROR: Unhandled python exception This is a Meson bug and should be reported! ``` --- mesonbuild/mdevenv.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/mdevenv.py b/mesonbuild/mdevenv.py index a5110435ab38..766593065407 100644 --- a/mesonbuild/mdevenv.py +++ b/mesonbuild/mdevenv.py @@ -235,7 +235,7 @@ def run(options: argparse.Namespace) -> int: args = [os.environ.get("SHELL", os.path.realpath("/bin/sh"))] if "bash" in args[0]: # Let the GC remove the tmp file - tmprc = tempfile.NamedTemporaryFile(mode='w') + tmprc = tempfile.NamedTemporaryFile(mode='w', encoding='utf-8') tmprc.write('[ -e ~/.bashrc ] && . ~/.bashrc\n') if prompt_prefix: tmprc.write(f'export PS1="{prompt_prefix} $PS1"\n') @@ -247,7 +247,7 @@ def run(options: argparse.Namespace) -> int: elif args[0].endswith('zsh'): # Let the GC remove the tmp file tmpdir = tempfile.TemporaryDirectory() - with open(os.path.join(tmpdir.name, '.zshrc'), 'w') as zshrc: # pylint: disable=unspecified-encoding + with open(os.path.join(tmpdir.name, '.zshrc'), 'w', encoding='utf-8') as zshrc: zshrc.write('[ -e ~/.zshrc ] && . ~/.zshrc\n') if prompt_prefix: zshrc.write(f'export PROMPT="[{prompt_prefix}] $PROMPT"\n') From 4f808400371cb70e76a4970e59b64a9ea7663caf Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Wed, 15 Oct 2025 23:46:25 -0400 Subject: [PATCH 096/681] mdevenv: fix double brackets in zsh prompt prompt_command already has "[]" pre-added, so zsh ended up with e.g. ``` [[trivial test]] $ ``` whereas bash used: ``` [trivial test] $ ``` --- mesonbuild/mdevenv.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/mdevenv.py b/mesonbuild/mdevenv.py index 766593065407..30fca9196149 100644 --- a/mesonbuild/mdevenv.py +++ b/mesonbuild/mdevenv.py @@ -250,7 +250,7 @@ def run(options: argparse.Namespace) -> int: with open(os.path.join(tmpdir.name, '.zshrc'), 'w', encoding='utf-8') as zshrc: zshrc.write('[ -e ~/.zshrc ] && . ~/.zshrc\n') if prompt_prefix: - zshrc.write(f'export PROMPT="[{prompt_prefix}] $PROMPT"\n') + zshrc.write(f'export PROMPT="{prompt_prefix} $PROMPT"\n') devenv['ZDOTDIR'] = tmpdir.name if 'DYLD_LIBRARY_PATH' in devenv and macos_sip_enabled(): mlog.warning('macOS System Integrity Protection is enabled: DYLD_LIBRARY_PATH cannot be set in the subshell') From 61e722c1f9acfedc0b5c2af3be2a82a6b2559574 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 24 Apr 2026 15:38:17 +0200 Subject: [PATCH 097/681] utils: remove ABCMeta from HoldableObject It has no abstract methods of its own. It is not instantiatable but that is it. Signed-off-by: Paolo Bonzini --- mesonbuild/utils/core.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/mesonbuild/utils/core.py b/mesonbuild/utils/core.py index 31afc4ead133..095e173a4a9e 100644 --- a/mesonbuild/utils/core.py +++ b/mesonbuild/utils/core.py @@ -12,7 +12,6 @@ from __future__ import annotations from dataclasses import dataclass import os -import abc import typing as T if T.TYPE_CHECKING: @@ -54,9 +53,13 @@ def __init__(self, msg: str, file: T.Optional[str] = None, super().__init__(msg + '\n\n This is a Meson bug and should be reported!', file=file, lineno=lineno, colno=colno) -class HoldableObject(metaclass=abc.ABCMeta): +class HoldableObject: ''' Dummy base class for all objects that can be held by an interpreter.baseobjects.ObjectHolder ''' + def __new__(cls, *args: T.Any, **kwargs: T.Any) -> HoldableObject: + if cls is HoldableObject: + raise TypeError(f"Can't instantiate abstract class {cls.__name__}") + return super().__new__(cls) class EnvironmentVariables(HoldableObject): def __init__(self, values: T.Optional[EnvInitValueType] = None, From 41c8235557b7231223386fb9a1aafb6b76b370d0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 24 Apr 2026 15:50:04 +0200 Subject: [PATCH 098/681] utils: introduce a faster variant of ABCMeta The virtual superclass feature of ABCMeta is never used by Meson. Replace with a simpler version that only checks for abstract methods, so that isinstance() against any of Meson's concrete subclasses now goes through the CPython C fast path. Signed-off-by: Paolo Bonzini --- mesonbuild/utils/universal.py | 32 +++++++++++++++++++++ unittests/internaltests.py | 52 ++++++++++++++++++++++++++++++++++- 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index 965095bba742..304c2079ce1c 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -59,6 +59,7 @@ class _VerPickleLoadable(Protocol): __all__ = [ 'GIT', + 'SimpleABC', 'python_command', 'NoProjectVersion', 'project_meson_versions', @@ -282,6 +283,37 @@ def check_direntry_issues(direntry_array: T.Union[T.Iterable[T.Union[str, bytes] not pure ASCII. This may cause problems. ''')) +class SimpleABC(type): + '''Lightweight replacement for ``abc.ABCMeta``. + + Supports ``@abc.abstractmethod`` but omits virtual subclass + registration and ``__subclasshook__``. This way, ``isinstance()`` + goes through the C fast path. ''' + + __abstractmethods__: T.FrozenSet[str] + + def __new__(mcs, name: str, bases: T.Tuple[type, ...], + namespace: T.Dict[str, T.Any], **kwargs: T.Any) -> SimpleABC: + cls = super().__new__(mcs, name, bases, namespace, **kwargs) + abstracts = {n for n, v in namespace.items() + if getattr(v, '__isabstractmethod__', False)} + for base in bases: + for n in getattr(base, '__abstractmethods__', ()): + if getattr(getattr(cls, n, None), '__isabstractmethod__', False): + abstracts.add(n) + cls.__abstractmethods__ = frozenset(abstracts) + return cls + + def __call__(cls, *args: T.Any, **kwargs: T.Any) -> T.Any: + if cls.__abstractmethods__: + raise TypeError( + f"Can't instantiate abstract class {cls.__name__} without an " + f"implementation for abstract method" + f"{'s' if len(cls.__abstractmethods__) > 1 else ''} " + f"{', '.join(repr(m) for m in sorted(cls.__abstractmethods__))}") + return super().__call__(*args, **kwargs) + + class SecondLevelHolder(HoldableObject, metaclass=abc.ABCMeta): ''' A second level object holder. The primary purpose of such objects is to hold multiple objects with one diff --git a/unittests/internaltests.py b/unittests/internaltests.py index fb0e4a2fd0ad..c5ea3b06f992 100644 --- a/unittests/internaltests.py +++ b/unittests/internaltests.py @@ -36,7 +36,7 @@ from mesonbuild.interpreterbase import typed_pos_args, InvalidArguments, ObjectHolder from mesonbuild.interpreterbase import typed_pos_args, InvalidArguments, typed_kwargs, ContainerTypeInfo, KwargInfo from mesonbuild.mesonlib import ( - LibType, MachineChoice, PerMachine, Version, is_windows, is_osx, + LibType, MachineChoice, PerMachine, SimpleABC, Version, is_windows, is_osx, is_cygwin, is_openbsd, search_version, MesonException, python_command, ) from mesonbuild.options import OptionKey @@ -75,6 +75,56 @@ def test_version_number(self): OS name: "linux", version: "5.12.17", arch: "amd64", family: "unix"'''), '3.8.1') + def test_simple_abc(self): + from abc import abstractmethod + + # The whole point is for isinstance() to stay on the C fast path + self.assertNotIn('__instancecheck__', vars(SimpleABC)) + self.assertNotIn('__subclasscheck__', vars(SimpleABC)) + + class A(metaclass=SimpleABC): + @abstractmethod + def foo(self): ... + + class B(A, metaclass=SimpleABC): + def foo(self): + return 1 + + @abstractmethod + def bar(self): ... + + class C(B): + def foo(self): + return 2 + + class D(B): + def bar(self): + return 3 + + self.assertEqual(A.__abstractmethods__, frozenset({'foo'})) + with self.assertRaises(TypeError): + A() + + self.assertEqual(B.__abstractmethods__, frozenset({'bar'})) + self.assertTrue(issubclass(B, A)) + with self.assertRaises(TypeError): + B() + + self.assertEqual(C.__abstractmethods__, frozenset({'bar'})) + self.assertTrue(issubclass(C, A)) + self.assertTrue(issubclass(C, B)) + with self.assertRaises(TypeError): + # subclass inheriting SimpleABC + C() + + self.assertEqual(D.__abstractmethods__, frozenset()) + self.assertTrue(issubclass(D, A)) + self.assertTrue(issubclass(D, B)) + self.assertTrue(issubclass(D, D)) + self.assertIsInstance(D(), A) + self.assertIsInstance(D(), B) + self.assertIsInstance(D(), D) + def test_mode_symbolic_to_bits(self): modefunc = mesonbuild.mesonlib.FileMode.perms_s_to_bits self.assertEqual(modefunc('---------'), 0) From b24583b638154428e5dae3d760471a904105bdd3 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 24 Apr 2026 15:50:04 +0200 Subject: [PATCH 099/681] replace ABCMeta with SimpleABC The virtual superclass feature of ABCMeta is never used by Meson. Replace it with SimpleABC, which results in a 3-4% speedup. Signed-off-by: Paolo Bonzini --- mesonbuild/build.py | 4 ++-- mesonbuild/compilers/compilers.py | 4 ++-- mesonbuild/compilers/mixins/gnu.py | 2 +- mesonbuild/compilers/mixins/visualstudio.py | 3 +-- mesonbuild/dependencies/qt.py | 4 ++-- mesonbuild/interpreterbase/baseobjects.py | 5 ++--- mesonbuild/interpreterbase/decorators.py | 4 ++-- mesonbuild/linkers/linkers.py | 2 +- mesonbuild/mdist.py | 4 ++-- mesonbuild/programs.py | 6 +++--- mesonbuild/templates/sampleimpl.py | 4 +++- mesonbuild/utils/universal.py | 2 +- 12 files changed, 22 insertions(+), 22 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 703feba80b9f..97925271543c 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -20,7 +20,7 @@ from . import mlog from . import programs from .mesonlib import ( - HoldableObject, SecondLevelHolder, + HoldableObject, SecondLevelHolder, SimpleABC, File, MesonException, MachineChoice, PerMachine, OrderedSet, listify, classify_unity_sources, get_filenames_templates_dict, substitute_values, has_path_sep, @@ -667,7 +667,7 @@ def needs_copy(self) -> bool: @dataclass(eq=False) -class Target(HoldableObject, metaclass=abc.ABCMeta): +class Target(HoldableObject, metaclass=SimpleABC): name: str subdir: str diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index deafa3eea7e0..335bafbbb9dc 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -16,7 +16,7 @@ from .. import mesonlib from .. import options from ..mesonlib import ( - HoldableObject, + HoldableObject, SimpleABC, EnvironmentException, MesonBugException, MesonException, Popen_safe_logged, LibType, TemporaryDirectoryWinProof, ) @@ -467,7 +467,7 @@ class CompileResult(HoldableObject): output_name: T.Optional[str] = field(default=None, init=False) cached: bool = field(default=False, init=False) -class Compiler(HoldableObject, metaclass=abc.ABCMeta): +class Compiler(HoldableObject, metaclass=SimpleABC): # Libraries to ignore in find_library() since they are provided by the # compiler or the C library. Currently only used for MSVC. diff --git a/mesonbuild/compilers/mixins/gnu.py b/mesonbuild/compilers/mixins/gnu.py index 776f9eda1626..d6ef7955ecfc 100644 --- a/mesonbuild/compilers/mixins/gnu.py +++ b/mesonbuild/compilers/mixins/gnu.py @@ -348,7 +348,7 @@ def gnulike_default_include_dirs(compiler: T.Tuple[str, ...], lang: str) -> 'Imm return paths -class GnuLikeCompiler(Compiler, metaclass=abc.ABCMeta): +class GnuLikeCompiler(Compiler, metaclass=mesonlib.SimpleABC): """ GnuLikeCompiler is a common interface to all compilers implementing the GNU-style commandline interface. This includes GCC, Clang diff --git a/mesonbuild/compilers/mixins/visualstudio.py b/mesonbuild/compilers/mixins/visualstudio.py index acf1bbc7bdc1..3608d706e475 100644 --- a/mesonbuild/compilers/mixins/visualstudio.py +++ b/mesonbuild/compilers/mixins/visualstudio.py @@ -8,7 +8,6 @@ interface. """ -import abc import os import typing as T @@ -65,7 +64,7 @@ } -class VisualStudioLikeCompiler(Compiler, metaclass=abc.ABCMeta): +class VisualStudioLikeCompiler(Compiler, metaclass=mesonlib.SimpleABC): """A common interface for all compilers implementing an MSVC-style interface. diff --git a/mesonbuild/dependencies/qt.py b/mesonbuild/dependencies/qt.py index aa321e92cdd7..e090215789cc 100644 --- a/mesonbuild/dependencies/qt.py +++ b/mesonbuild/dependencies/qt.py @@ -171,7 +171,7 @@ def _get_common_defines(self) -> T.List[str]: is_debug = self.env.coredata.optstore.get_value_for('debug') return ['-DQT_DEBUG' if is_debug else '-DQT_NO_DEBUG'] -class QtPkgConfigDependency(_QtBase, PkgConfigDependency, metaclass=abc.ABCMeta): +class QtPkgConfigDependency(_QtBase, PkgConfigDependency, metaclass=mesonlib.SimpleABC): """Specialization of the PkgConfigDependency for Qt.""" @@ -249,7 +249,7 @@ def log_info(self) -> str: return 'pkg-config' -class QmakeQtDependency(_QtBase, ConfigToolDependency, metaclass=abc.ABCMeta): +class QmakeQtDependency(_QtBase, ConfigToolDependency, metaclass=mesonlib.SimpleABC): """Find Qt using Qmake as a config-tool.""" diff --git a/mesonbuild/interpreterbase/baseobjects.py b/mesonbuild/interpreterbase/baseobjects.py index c7567612a85e..20867d6416d8 100644 --- a/mesonbuild/interpreterbase/baseobjects.py +++ b/mesonbuild/interpreterbase/baseobjects.py @@ -7,11 +7,10 @@ from .exceptions import InvalidCode, InvalidArguments from .helpers import flatten, resolve_second_level_holders from .operator import MesonOperator -from ..mesonlib import HoldableObject, MesonBugException +from ..mesonlib import HoldableObject, MesonBugException, SimpleABC import textwrap import typing as T -from abc import ABCMeta from contextlib import AbstractContextManager if T.TYPE_CHECKING: @@ -215,7 +214,7 @@ def op_not_equals(self, other: TYPE_var) -> bool: def __repr__(self) -> str: return f'<[{type(self).__name__}] holds [{type(self.held_object).__name__}]: {self.held_object!r}>' -class IterableObject(metaclass=ABCMeta): +class IterableObject(metaclass=SimpleABC): '''Base class for all objects that can be iterated over in a foreach loop''' def iter_tuple_size(self) -> T.Optional[int]: diff --git a/mesonbuild/interpreterbase/decorators.py b/mesonbuild/interpreterbase/decorators.py index c6514257d828..f4cac4a57977 100644 --- a/mesonbuild/interpreterbase/decorators.py +++ b/mesonbuild/interpreterbase/decorators.py @@ -641,7 +641,7 @@ def emit_feature_change(values: T.Dict[_T, T.Union[str, T.Tuple[str, str]]], fea # This cannot be a dataclass due to https://github.com/python/mypy/issues/5374 -class FeatureCheckBase(metaclass=abc.ABCMeta): +class FeatureCheckBase(metaclass=mesonlib.SimpleABC): "Base class for feature version checks" feature_registry: T.ClassVar[T.Dict[str, T.Dict[str, T.Set[T.Tuple[str, T.Optional['mparser.BaseNode']]]]]] @@ -859,7 +859,7 @@ def log_usage_warning(self, tv: T.Union[str, mesonlib.NoProjectVersion], locatio # This cannot be a dataclass due to https://github.com/python/mypy/issues/5374 -class FeatureCheckKwargsBase(metaclass=abc.ABCMeta): +class FeatureCheckKwargsBase(metaclass=mesonlib.SimpleABC): @property @abc.abstractmethod diff --git a/mesonbuild/linkers/linkers.py b/mesonbuild/linkers/linkers.py index f3e9314e2bf3..d9fba1d0453f 100644 --- a/mesonbuild/linkers/linkers.py +++ b/mesonbuild/linkers/linkers.py @@ -114,7 +114,7 @@ def rsp_file_syntax(self) -> RSPFileSyntax: raise EnvironmentException(f'{self.id} does not implement rsp format, this shouldn\'t be called') -class DynamicLinker(metaclass=abc.ABCMeta): +class DynamicLinker(metaclass=mesonlib.SimpleABC): """Base class for dynamic linkers.""" diff --git a/mesonbuild/mdist.py b/mesonbuild/mdist.py index 9fcde5bba06a..59e416511334 100644 --- a/mesonbuild/mdist.py +++ b/mesonbuild/mdist.py @@ -22,7 +22,7 @@ from pathlib import Path from mesonbuild.environment import Environment from mesonbuild.tooldetect import detect_ninja -from mesonbuild.mesonlib import (GIT, MesonException, RealPathAction, get_meson_command, quiet_git, +from mesonbuild.mesonlib import (GIT, MesonException, RealPathAction, SimpleABC, get_meson_command, quiet_git, windows_proof_rmtree, setup_vsenv, determine_worker_count) from .options import OptionKey from mesonbuild.msetup import add_arguments as msetup_argparse @@ -122,7 +122,7 @@ def is_hg(src_root: str) -> bool: @dataclass -class Dist(metaclass=abc.ABCMeta): +class Dist(metaclass=SimpleABC): dist_name: str src_root: str bld_root: str diff --git a/mesonbuild/programs.py b/mesonbuild/programs.py index 123f064b8283..9c6eb965a07f 100644 --- a/mesonbuild/programs.py +++ b/mesonbuild/programs.py @@ -13,11 +13,11 @@ import re import typing as T from pathlib import Path -from abc import ABCMeta, abstractmethod +from abc import abstractmethod from . import mesonlib from . import mlog -from .mesonlib import MachineChoice, OrderedSet +from .mesonlib import MachineChoice, OrderedSet, SimpleABC if T.TYPE_CHECKING: from typing_extensions import TypeAlias @@ -28,7 +28,7 @@ CommandList: TypeAlias = T.List[CommandListEntry] -class Program(mesonlib.HoldableObject, metaclass=ABCMeta): +class Program(mesonlib.HoldableObject, metaclass=SimpleABC): '''A base class for LocalProgram and ExternalProgram.''' name: str diff --git a/mesonbuild/templates/sampleimpl.py b/mesonbuild/templates/sampleimpl.py index d96efd92d75e..1925bde3f9d3 100644 --- a/mesonbuild/templates/sampleimpl.py +++ b/mesonbuild/templates/sampleimpl.py @@ -10,11 +10,13 @@ import re import typing as T +from ..mesonlib import SimpleABC + if T.TYPE_CHECKING: from ..minit import Arguments -class SampleImpl(metaclass=abc.ABCMeta): +class SampleImpl(metaclass=SimpleABC): def __init__(self, args: Arguments): self.name = args.name diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index 304c2079ce1c..76f260c6aa13 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -314,7 +314,7 @@ def __call__(cls, *args: T.Any, **kwargs: T.Any) -> T.Any: return super().__call__(*args, **kwargs) -class SecondLevelHolder(HoldableObject, metaclass=abc.ABCMeta): +class SecondLevelHolder(HoldableObject, metaclass=SimpleABC): ''' A second level object holder. The primary purpose of such objects is to hold multiple objects with one default option. ''' From 348d8610dbb5802cd78c81ffd8cdfa9ddd735991 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 24 Apr 2026 15:17:25 -0700 Subject: [PATCH 100/681] Set the encoding parameter for all open() calls that write in text mode This was generated with a regex search/replace in vscode using: `(open\(.*, 'w\+?')` -> `$1, encoding='utf-8')` This should silence a bunch of warnings, and seems to fix issues seen in the Windows CI. This seems to likely be to a "helpful feature" in msbuild that treats certain text in stdout/stderr as errors, which it sometimes decides that the warning about not having the encoding set triggers. --- packaging/createmsi.py | 4 ++-- packaging/createpkg.py | 2 +- run_meson_command_tests.py | 2 +- .../cmake/11 cmake_module_path/subprojects/cmMod/gen.py | 2 +- test cases/common/105 generatorcustom/catter.py | 2 +- test cases/common/105 generatorcustom/gen-resx.py | 2 +- test cases/common/105 generatorcustom/gen.py | 2 +- test cases/common/110 allgenerate/converter.py | 2 +- .../common/123 custom target directory install/docgen.py | 2 +- .../common/125 configure file in generator/src/gen.py | 2 +- .../128 build by default targets in tests/write_file.py | 2 +- test cases/common/129 build by default/mygen.py | 2 +- test cases/common/13 pch/generated/gen_custom.py | 2 +- test cases/common/13 pch/generated/gen_generator.py | 2 +- .../140 custom target multiple outputs/generator.py | 4 ++-- test cases/common/141 special characters/check_quoting.py | 2 +- .../common/144 link depends custom target/make_file.py | 2 +- test cases/common/152 index customtarget/gen_sources.py | 4 ++-- test cases/common/168 preserve gendir/genprog.py | 4 ++-- .../common/169 source in dep/generated/genheader.py | 2 +- test cases/common/170 generator link whole/generator.py | 4 ++-- test cases/common/186 test depends/gen.py | 2 +- .../com/mesonbuild/tooldir/genprog.py | 4 ++-- .../common/202 custom target build by default/docgen.py | 2 +- .../226 link depends indexed custom target/check_arch.py | 2 +- .../226 link depends indexed custom target/make_file.py | 4 ++-- test cases/common/228 custom_target source/x.py | 4 ++-- test cases/common/242 custom target feed/my_compiler.py | 2 +- .../common/271 env in generator.process/generate_main.py | 4 ++-- .../common/274 customtarget exe for test/generate.py | 2 +- .../295 depends in generator.process/generate_config.py | 2 +- .../295 depends in generator.process/generate_main.py | 2 +- test cases/common/49 custom target/depfile/dep.py | 4 ++-- test cases/common/49 custom target/my_compiler.py | 2 +- test cases/common/50 custom target chain/my_compiler.py | 2 +- test cases/common/50 custom target chain/my_compiler2.py | 2 +- .../common/50 custom target chain/usetarget/subcomp.py | 2 +- test cases/common/53 install script/customtarget.py | 4 ++-- test cases/common/53 install script/myinstall.py | 2 +- test cases/common/53 install script/src/myinstall.py | 2 +- .../common/54 custom target source output/generator.py | 4 ++-- .../common/57 custom header generator/makeheader.py | 2 +- test cases/common/58 multiple generators/mygen.py | 4 ++-- test cases/common/65 build always/version_gen.py | 2 +- .../69 configure file in custom target/src/mycompiler.py | 2 +- test cases/common/71 ctarget dependency/gen1.py | 2 +- test cases/common/71 ctarget dependency/gen2.py | 2 +- test cases/common/8 install/gendir.py | 2 +- test cases/common/86 private include/stlib/compiler.py | 4 ++-- .../common/88 dep fallback/subprojects/boblib/genbob.py | 2 +- test cases/common/90 gen extra/srcgen.py | 2 +- test cases/common/90 gen extra/srcgen2.py | 4 ++-- test cases/common/95 manygen/subdir/manygen.py | 8 ++++---- test cases/cython/2 generated sources/gen.py | 2 +- test cases/cython/2 generated sources/generator.py | 2 +- test cases/cython/2 generated sources/libdir/gen.py | 2 +- .../generator.py | 6 +++--- .../10 gtk-doc/include/generate-enums-docbook.py | 2 +- .../python/4 custom target depends extmodule/blaster.py | 2 +- .../python3/4 custom target depends extmodule/blaster.py | 2 +- test cases/rust/11 generated main/gen.py | 2 +- test cases/rust/19 structured sources/gen.py | 2 +- test cases/unit/26 install umask/myinstall.py | 2 +- test cases/unit/94 custominc/easytogrepfor/genh.py | 2 +- test cases/unit/98 install all targets/script.py | 2 +- test cases/unit/98 install all targets/subdir/script.py | 2 +- test cases/vala/10 mixed sources/c/writec.py | 2 +- test cases/vala/8 generated sources/src/write_wrapper.py | 2 +- .../subdir/make_def.py | 2 +- .../12 resources with custom targets/res/gen-res.py | 2 +- 70 files changed, 90 insertions(+), 90 deletions(-) diff --git a/packaging/createmsi.py b/packaging/createmsi.py index 29a889121525..2c0fad0f28f3 100755 --- a/packaging/createmsi.py +++ b/packaging/createmsi.py @@ -231,13 +231,13 @@ def generate_files(self): # ElementTree cannot do pretty-printing, so do it manually import xml.dom.minidom doc = xml.dom.minidom.parse(self.main_xml) - with open(self.main_xml, 'w') as open_file: + with open(self.main_xml, 'w', encoding='utf-8') as open_file: open_file.write(doc.toprettyxml()) # One last fix, add CDATA. with open(self.main_xml) as open_file: data = open_file.read() data = data.replace('X'*len(WINVER_CHECK), WINVER_CHECK) - with open(self.main_xml, 'w') as open_file: + with open(self.main_xml, 'w', encoding='utf-8') as open_file: open_file.write(data) def build_features(self, top_feature, staging_dir): diff --git a/packaging/createpkg.py b/packaging/createpkg.py index b5a1c9f82537..d1d180473d3e 100755 --- a/packaging/createpkg.py +++ b/packaging/createpkg.py @@ -91,7 +91,7 @@ def generate_distribution(self): # ElementTree cannot do pretty-printing, so do it manually import xml.dom.minidom doc = xml.dom.minidom.parse(self.distribution_file) - with open(self.distribution_file, 'w') as open_file: + with open(self.distribution_file, 'w', encoding='utf-8') as open_file: open_file.write(doc.toprettyxml()) def remove_tempfiles(self): diff --git a/run_meson_command_tests.py b/run_meson_command_tests.py index c2a621b79933..a5e5a2e5adfd 100755 --- a/run_meson_command_tests.py +++ b/run_meson_command_tests.py @@ -218,7 +218,7 @@ def test_meson_runpython(self): test_command = 'import sys; print(sys.argv[1])' env = os.environ.copy() del env['MESON_COMMAND_TESTS'] - with open(script_file, 'w') as f: + with open(script_file, 'w', encoding='utf-8') as f: f.write('#!/usr/bin/env python3\n\n') f.write(f'{test_command}\n') self.addCleanup(os.remove, script_file) diff --git a/test cases/cmake/11 cmake_module_path/subprojects/cmMod/gen.py b/test cases/cmake/11 cmake_module_path/subprojects/cmMod/gen.py index 5c7164604028..56d4b1b1d463 100644 --- a/test cases/cmake/11 cmake_module_path/subprojects/cmMod/gen.py +++ b/test cases/cmake/11 cmake_module_path/subprojects/cmMod/gen.py @@ -1,4 +1,4 @@ -with open('main.c', 'w') as fp: +with open('main.c', 'w', encoding='utf-8') as fp: print(''' #include diff --git a/test cases/common/105 generatorcustom/catter.py b/test cases/common/105 generatorcustom/catter.py index c2726723c4d8..be88ac1879e0 100755 --- a/test cases/common/105 generatorcustom/catter.py +++ b/test cases/common/105 generatorcustom/catter.py @@ -5,7 +5,7 @@ output = sys.argv[-1] inputs = sys.argv[1:-1] -with open(output, 'w') as ofile: +with open(output, 'w', encoding='utf-8') as ofile: ofile.write('#pragma once\n') for i in inputs: with open(i) as ifile: diff --git a/test cases/common/105 generatorcustom/gen-resx.py b/test cases/common/105 generatorcustom/gen-resx.py index 242a962d80fc..acd0306e8a66 100755 --- a/test cases/common/105 generatorcustom/gen-resx.py +++ b/test cases/common/105 generatorcustom/gen-resx.py @@ -5,5 +5,5 @@ ofile = sys.argv[1] num = sys.argv[2] -with open(ofile, 'w') as f: +with open(ofile, 'w', encoding='utf-8') as f: f.write(f'res{num}\n') diff --git a/test cases/common/105 generatorcustom/gen.py b/test cases/common/105 generatorcustom/gen.py index 1464008f9b27..8c3d6e4e0a47 100755 --- a/test cases/common/105 generatorcustom/gen.py +++ b/test cases/common/105 generatorcustom/gen.py @@ -9,5 +9,5 @@ resname = f.readline().strip() templ = 'const char %s[] = "%s";\n' -with open(ofile, 'w') as f: +with open(ofile, 'w', encoding='utf-8') as f: f.write(templ % (resname, resname)) diff --git a/test cases/common/110 allgenerate/converter.py b/test cases/common/110 allgenerate/converter.py index f8e2ca05ade6..04729a432244 100755 --- a/test cases/common/110 allgenerate/converter.py +++ b/test cases/common/110 allgenerate/converter.py @@ -5,4 +5,4 @@ ifile = sys.argv[1] ofile = sys.argv[2] -open(ofile, 'w').write(open(ifile).read()) +open(ofile, 'w', encoding='utf-8').write(open(ifile).read()) diff --git a/test cases/common/123 custom target directory install/docgen.py b/test cases/common/123 custom target directory install/docgen.py index 97a3f906ca82..4045e475f0fd 100644 --- a/test cases/common/123 custom target directory install/docgen.py +++ b/test cases/common/123 custom target directory install/docgen.py @@ -11,5 +11,5 @@ pass for name in ('a', 'b', 'c'): - with open(os.path.join(out, name + '.html'), 'w') as f: + with open(os.path.join(out, name + '.html'), 'w', encoding='utf-8') as f: f.write(name) diff --git a/test cases/common/125 configure file in generator/src/gen.py b/test cases/common/125 configure file in generator/src/gen.py index 426d0b74cc13..e40e929ac95c 100755 --- a/test cases/common/125 configure file in generator/src/gen.py +++ b/test cases/common/125 configure file in generator/src/gen.py @@ -9,5 +9,5 @@ resval = f.readline().strip() templ = '#define RESULT (%s)\n' -with open(ofile, 'w') as f: +with open(ofile, 'w', encoding='utf-8') as f: f.write(templ % (resval, )) diff --git a/test cases/common/128 build by default targets in tests/write_file.py b/test cases/common/128 build by default targets in tests/write_file.py index ff9c224f0d0f..2af4c7e4efab 100644 --- a/test cases/common/128 build by default targets in tests/write_file.py +++ b/test cases/common/128 build by default targets in tests/write_file.py @@ -2,5 +2,5 @@ import sys -with open(sys.argv[1], 'w') as f: +with open(sys.argv[1], 'w', encoding='utf-8') as f: f.write('Test') diff --git a/test cases/common/129 build by default/mygen.py b/test cases/common/129 build by default/mygen.py index 5a74153ee16b..0fe3b67e6148 100644 --- a/test cases/common/129 build by default/mygen.py +++ b/test cases/common/129 build by default/mygen.py @@ -3,6 +3,6 @@ import sys ifile = open(sys.argv[1]) -ofile = open(sys.argv[2], 'w') +ofile = open(sys.argv[2], 'w', encoding='utf-8') ofile.write(ifile.read()) diff --git a/test cases/common/13 pch/generated/gen_custom.py b/test cases/common/13 pch/generated/gen_custom.py index 650e03c2b312..c080b280bdad 100644 --- a/test cases/common/13 pch/generated/gen_custom.py +++ b/test cases/common/13 pch/generated/gen_custom.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 import sys -with open(sys.argv[1], 'w') as f: +with open(sys.argv[1], 'w', encoding='utf-8') as f: f.write("#define FOO 0") diff --git a/test cases/common/13 pch/generated/gen_generator.py b/test cases/common/13 pch/generated/gen_generator.py index a245e7aef342..4ee4ce0625f0 100644 --- a/test cases/common/13 pch/generated/gen_generator.py +++ b/test cases/common/13 pch/generated/gen_generator.py @@ -3,5 +3,5 @@ with open(sys.argv[1]) as f: content = f.read() -with open(sys.argv[2], 'w') as f: +with open(sys.argv[2], 'w', encoding='utf-8') as f: f.write(content) diff --git a/test cases/common/140 custom target multiple outputs/generator.py b/test cases/common/140 custom target multiple outputs/generator.py index 39dbd11c42e3..fb71a4281aeb 100755 --- a/test cases/common/140 custom target multiple outputs/generator.py +++ b/test cases/common/140 custom target multiple outputs/generator.py @@ -8,7 +8,7 @@ name = sys.argv[1] odir = sys.argv[2] -with open(os.path.join(odir, name + '.h'), 'w') as f: +with open(os.path.join(odir, name + '.h'), 'w', encoding='utf-8') as f: f.write('int func();\n') -with open(os.path.join(odir, name + '.sh'), 'w') as f: +with open(os.path.join(odir, name + '.sh'), 'w', encoding='utf-8') as f: f.write('#!/bin/bash') diff --git a/test cases/common/141 special characters/check_quoting.py b/test cases/common/141 special characters/check_quoting.py index d6e50ea69f29..f7440994f206 100644 --- a/test cases/common/141 special characters/check_quoting.py +++ b/test cases/common/141 special characters/check_quoting.py @@ -24,5 +24,5 @@ raise RuntimeError('{!r} is {!r} but should be {!r}'.format(name, value, expected[name])) if output is not None: - with open(output, 'w') as f: + with open(output, 'w', encoding='utf-8') as f: f.write('Success!') diff --git a/test cases/common/144 link depends custom target/make_file.py b/test cases/common/144 link depends custom target/make_file.py index ceb6e19aad38..5420a6282394 100755 --- a/test cases/common/144 link depends custom target/make_file.py +++ b/test cases/common/144 link depends custom target/make_file.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 import sys -with open(sys.argv[1], 'w') as f: +with open(sys.argv[1], 'w', encoding='utf-8') as f: print('# this file does nothing', file=f) diff --git a/test cases/common/152 index customtarget/gen_sources.py b/test cases/common/152 index customtarget/gen_sources.py index 3ea2940366d1..4bce7e15d286 100644 --- a/test cases/common/152 index customtarget/gen_sources.py +++ b/test cases/common/152 index customtarget/gen_sources.py @@ -27,10 +27,10 @@ def main(): parser.add_argument('--code') args = parser.parse_args() - with open(args.header, 'w') as f: + with open(args.header, 'w', encoding='utf-8') as f: f.write(HEADER) - with open(args.code, 'w') as f: + with open(args.code, 'w', encoding='utf-8') as f: f.write(CODE) diff --git a/test cases/common/168 preserve gendir/genprog.py b/test cases/common/168 preserve gendir/genprog.py index 681c43a84490..f62bd4ef5a7b 100755 --- a/test cases/common/168 preserve gendir/genprog.py +++ b/test cases/common/168 preserve gendir/genprog.py @@ -42,5 +42,5 @@ h_out = ofile_bases[i] + '.h' c_out = ofile_bases[i] + '.c' os.makedirs(os.path.split(ofile_bases[i])[0], exist_ok=True) - open(h_out, 'w').write(h_templ % (proto_name)) - open(c_out, 'w').write(c_templ % (proto_name, proto_name)) + open(h_out, 'w', encoding='utf-8').write(h_templ % (proto_name)) + open(c_out, 'w', encoding='utf-8').write(c_templ % (proto_name, proto_name)) diff --git a/test cases/common/169 source in dep/generated/genheader.py b/test cases/common/169 source in dep/generated/genheader.py index 489db23d3c11..8257373cc20a 100755 --- a/test cases/common/169 source in dep/generated/genheader.py +++ b/test cases/common/169 source in dep/generated/genheader.py @@ -14,4 +14,4 @@ funname = open(ifile).readline().strip() -open(ofile, 'w').write(templ % funname) +open(ofile, 'w', encoding='utf-8').write(templ % funname) diff --git a/test cases/common/170 generator link whole/generator.py b/test cases/common/170 generator link whole/generator.py index 18a6cc2f5809..e1787069d926 100755 --- a/test cases/common/170 generator link whole/generator.py +++ b/test cases/common/170 generator link whole/generator.py @@ -11,13 +11,13 @@ def main(): hname = os.path.join(out, name + '.h') cname = os.path.join(out, name + '.c') print(os.getcwd(), hname) - with open(hname, 'w') as hfile: + with open(hname, 'w', encoding='utf-8') as hfile: hfile.write(''' #pragma once #include "export.h" int DLL_PUBLIC {name}(void); '''.format(name=name)) - with open(cname, 'w') as cfile: + with open(cname, 'w', encoding='utf-8') as cfile: cfile.write(''' #include "{name}.h" int {name}(void) {{ diff --git a/test cases/common/186 test depends/gen.py b/test cases/common/186 test depends/gen.py index ee4ed9818074..d9e3c25f305b 100755 --- a/test cases/common/186 test depends/gen.py +++ b/test cases/common/186 test depends/gen.py @@ -4,7 +4,7 @@ def main(): - with open(sys.argv[1], 'w') as out: + with open(sys.argv[1], 'w', encoding='utf-8') as out: out.write(sys.argv[2]) out.write('\n') diff --git a/test cases/common/195 generator in subdir/com/mesonbuild/tooldir/genprog.py b/test cases/common/195 generator in subdir/com/mesonbuild/tooldir/genprog.py index 681c43a84490..f62bd4ef5a7b 100644 --- a/test cases/common/195 generator in subdir/com/mesonbuild/tooldir/genprog.py +++ b/test cases/common/195 generator in subdir/com/mesonbuild/tooldir/genprog.py @@ -42,5 +42,5 @@ h_out = ofile_bases[i] + '.h' c_out = ofile_bases[i] + '.c' os.makedirs(os.path.split(ofile_bases[i])[0], exist_ok=True) - open(h_out, 'w').write(h_templ % (proto_name)) - open(c_out, 'w').write(c_templ % (proto_name, proto_name)) + open(h_out, 'w', encoding='utf-8').write(h_templ % (proto_name)) + open(c_out, 'w', encoding='utf-8').write(c_templ % (proto_name, proto_name)) diff --git a/test cases/common/202 custom target build by default/docgen.py b/test cases/common/202 custom target build by default/docgen.py index f343f21787da..0be2d73dab8a 100644 --- a/test cases/common/202 custom target build by default/docgen.py +++ b/test cases/common/202 custom target build by default/docgen.py @@ -8,5 +8,5 @@ os.mkdir(out) for name in ('a', 'b', 'c'): - with open(os.path.join(out, name + '.txt'), 'w') as f: + with open(os.path.join(out, name + '.txt'), 'w', encoding='utf-8') as f: f.write(name) diff --git a/test cases/common/226 link depends indexed custom target/check_arch.py b/test cases/common/226 link depends indexed custom target/check_arch.py index 927bf87c0dfe..8dc5aad2090d 100644 --- a/test cases/common/226 link depends indexed custom target/check_arch.py +++ b/test cases/common/226 link depends indexed custom target/check_arch.py @@ -9,7 +9,7 @@ want_arch = sys.argv[2] dummy_output = sys.argv[3] -with open(dummy_output, 'w') as f: +with open(dummy_output, 'w', encoding='utf-8') as f: f.write('') if not shutil.which('dumpbin'): diff --git a/test cases/common/226 link depends indexed custom target/make_file.py b/test cases/common/226 link depends indexed custom target/make_file.py index 6a43b7d058b1..8c7134e75fe0 100644 --- a/test cases/common/226 link depends indexed custom target/make_file.py +++ b/test cases/common/226 link depends indexed custom target/make_file.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 import sys -with open(sys.argv[1], 'w') as f: +with open(sys.argv[1], 'w', encoding='utf-8') as f: print('# this file does nothing', file=f) -with open(sys.argv[2], 'w') as f: +with open(sys.argv[2], 'w', encoding='utf-8') as f: print('# this file does nothing', file=f) diff --git a/test cases/common/228 custom_target source/x.py b/test cases/common/228 custom_target source/x.py index 12f40c83f2c8..c226cccde6fd 100644 --- a/test cases/common/228 custom_target source/x.py +++ b/test cases/common/228 custom_target source/x.py @@ -1,5 +1,5 @@ #! /usr/bin/env python3 -with open('x.c', 'w') as f: +with open('x.c', 'w', encoding='utf-8') as f: print('int main(void) { return 0; }', file=f) -with open('y', 'w'): +with open('y', 'w', encoding='utf-8'): pass diff --git a/test cases/common/242 custom target feed/my_compiler.py b/test cases/common/242 custom target feed/my_compiler.py index 97d42087b034..393d520b4f32 100755 --- a/test cases/common/242 custom target feed/my_compiler.py +++ b/test cases/common/242 custom target feed/my_compiler.py @@ -10,5 +10,5 @@ if ifile != 'This is a text only input file.\n': print('Malformed input') sys.exit(1) - with open(sys.argv[1], 'w+') as f: + with open(sys.argv[1], 'w+', encoding='utf-8') as f: f.write('This is a binary output file.') diff --git a/test cases/common/271 env in generator.process/generate_main.py b/test cases/common/271 env in generator.process/generate_main.py index 993c5ac034e1..12b98c4ae054 100644 --- a/test cases/common/271 env in generator.process/generate_main.py +++ b/test cases/common/271 env in generator.process/generate_main.py @@ -6,6 +6,6 @@ assert ENV_VAR_VALUE is not None with open(sys.argv[1], 'r') as infile, \ - open(sys.argv[2], 'w') as outfile: - + open(sys.argv[2], 'w', encoding='utf-8') as outfile: + outfile.write(infile.read().replace('ENV_VAR_VALUE', ENV_VAR_VALUE)) diff --git a/test cases/common/274 customtarget exe for test/generate.py b/test cases/common/274 customtarget exe for test/generate.py index e66f1db70aad..c5e3e5ec5159 100644 --- a/test cases/common/274 customtarget exe for test/generate.py +++ b/test cases/common/274 customtarget exe for test/generate.py @@ -9,6 +9,6 @@ ''' for i, a in enumerate(sys.argv[1:]): - with open(a, 'w') as f: + with open(a, 'w', encoding='utf-8') as f: print(program.format(i), file=f) os.chmod(a, 0o755) diff --git a/test cases/common/295 depends in generator.process/generate_config.py b/test cases/common/295 depends in generator.process/generate_config.py index f8b82c400b22..13babe61ff48 100644 --- a/test cases/common/295 depends in generator.process/generate_config.py +++ b/test cases/common/295 depends in generator.process/generate_config.py @@ -3,7 +3,7 @@ import configparser import sys -with open(sys.argv[2], 'w') as outfile: +with open(sys.argv[2], 'w', encoding='utf-8') as outfile: config = configparser.ConfigParser() config.read(sys.argv[1]) diff --git a/test cases/common/295 depends in generator.process/generate_main.py b/test cases/common/295 depends in generator.process/generate_main.py index c7f91514beae..8de78694c57f 100644 --- a/test cases/common/295 depends in generator.process/generate_main.py +++ b/test cases/common/295 depends in generator.process/generate_main.py @@ -2,6 +2,6 @@ import sys with open(sys.argv[1], 'r') as infile, \ - open(sys.argv[2], 'w') as outfile: + open(sys.argv[2], 'w', encoding='utf-8') as outfile: outfile.write(infile.read()) diff --git a/test cases/common/49 custom target/depfile/dep.py b/test cases/common/49 custom target/depfile/dep.py index c9e8f94dc7db..803a904a1e73 100755 --- a/test cases/common/49 custom target/depfile/dep.py +++ b/test cases/common/49 custom target/depfile/dep.py @@ -9,7 +9,7 @@ quoted_depfiles = [x.replace(' ', r'\ ') for x in depfiles] -with open(output, 'w') as f: +with open(output, 'w', encoding='utf-8') as f: f.write('I am the result of globbing.') -with open(depfile, 'w') as f: +with open(depfile, 'w', encoding='utf-8') as f: f.write('{}: {}\n'.format(output, ' '.join(quoted_depfiles))) diff --git a/test cases/common/49 custom target/my_compiler.py b/test cases/common/49 custom target/my_compiler.py index c4c8d72c6e0a..a743db45aa7d 100755 --- a/test cases/common/49 custom target/my_compiler.py +++ b/test cases/common/49 custom target/my_compiler.py @@ -18,5 +18,5 @@ if ifile != 'This is a text only input file.\n': print('Malformed input') sys.exit(1) - with open(args[2].split('=', 1)[1], 'w') as ofile: + with open(args[2].split('=', 1)[1], 'w', encoding='utf-8') as ofile: ofile.write('This is a binary output file.\n') diff --git a/test cases/common/50 custom target chain/my_compiler.py b/test cases/common/50 custom target chain/my_compiler.py index d99029bccbbb..0a63d22ccba5 100755 --- a/test cases/common/50 custom target chain/my_compiler.py +++ b/test cases/common/50 custom target chain/my_compiler.py @@ -11,5 +11,5 @@ if ifile != 'This is a text only input file.\n': print('Malformed input') sys.exit(1) - with open(sys.argv[2], 'w') as ofile: + with open(sys.argv[2], 'w', encoding='utf-8') as ofile: ofile.write('This is a binary output file.\n') diff --git a/test cases/common/50 custom target chain/my_compiler2.py b/test cases/common/50 custom target chain/my_compiler2.py index 22ec7897b23c..e165af741edf 100755 --- a/test cases/common/50 custom target chain/my_compiler2.py +++ b/test cases/common/50 custom target chain/my_compiler2.py @@ -11,5 +11,5 @@ if ifile != 'This is a binary output file.\n': print('Malformed input') sys.exit(1) - with open(sys.argv[2], 'w') as ofile: + with open(sys.argv[2], 'w', encoding='utf-8') as ofile: ofile.write('This is a different binary output file.\n') diff --git a/test cases/common/50 custom target chain/usetarget/subcomp.py b/test cases/common/50 custom target chain/usetarget/subcomp.py index 52dc0bbb5d1e..ec2432a90a5b 100755 --- a/test cases/common/50 custom target chain/usetarget/subcomp.py +++ b/test cases/common/50 custom target chain/usetarget/subcomp.py @@ -3,5 +3,5 @@ import sys with open(sys.argv[1], 'rb') as ifile: - with open(sys.argv[2], 'w') as ofile: + with open(sys.argv[2], 'w', encoding='utf-8') as ofile: ofile.write('Everything ok.\n') diff --git a/test cases/common/53 install script/customtarget.py b/test cases/common/53 install script/customtarget.py index e28373a3930d..265da3742d91 100755 --- a/test cases/common/53 install script/customtarget.py +++ b/test cases/common/53 install script/customtarget.py @@ -9,9 +9,9 @@ def main() -> None: parser.add_argument('dirname') args = parser.parse_args() - with open(os.path.join(args.dirname, '1.txt'), 'w') as f: + with open(os.path.join(args.dirname, '1.txt'), 'w', encoding='utf-8') as f: f.write('') - with open(os.path.join(args.dirname, '2.txt'), 'w') as f: + with open(os.path.join(args.dirname, '2.txt'), 'w', encoding='utf-8') as f: f.write('') diff --git a/test cases/common/53 install script/myinstall.py b/test cases/common/53 install script/myinstall.py index 5e5a929fa5ac..51e11dfae25a 100755 --- a/test cases/common/53 install script/myinstall.py +++ b/test cases/common/53 install script/myinstall.py @@ -27,7 +27,7 @@ def main() -> None: if dry_run: print(f'DRYRUN: Writing file {name}') else: - with open(os.path.join(dirname, name), 'w') as f: + with open(os.path.join(dirname, name), 'w', encoding='utf-8') as f: f.write('') else: for name in args.files: diff --git a/test cases/common/53 install script/src/myinstall.py b/test cases/common/53 install script/src/myinstall.py index 3a9d89b50953..6da8590f844a 100644 --- a/test cases/common/53 install script/src/myinstall.py +++ b/test cases/common/53 install script/src/myinstall.py @@ -10,5 +10,5 @@ if not os.path.exists(dirname): os.makedirs(dirname) -with open(os.path.join(dirname, sys.argv[2] + '.in'), 'w') as f: +with open(os.path.join(dirname, sys.argv[2] + '.in'), 'w', encoding='utf-8') as f: f.write('') diff --git a/test cases/common/54 custom target source output/generator.py b/test cases/common/54 custom target source output/generator.py index 1bec8e855397..7cae0bca5c5c 100755 --- a/test cases/common/54 custom target source output/generator.py +++ b/test cases/common/54 custom target source output/generator.py @@ -7,9 +7,9 @@ odir = sys.argv[1] -with open(os.path.join(odir, 'mylib.h'), 'w') as f: +with open(os.path.join(odir, 'mylib.h'), 'w', encoding='utf-8') as f: f.write('int func(void);\n') -with open(os.path.join(odir, 'mylib.c'), 'w') as f: +with open(os.path.join(odir, 'mylib.c'), 'w', encoding='utf-8') as f: f.write('''int func(void) { return 0; } diff --git a/test cases/common/57 custom header generator/makeheader.py b/test cases/common/57 custom header generator/makeheader.py index f156834470dc..97ce84cc8737 100644 --- a/test cases/common/57 custom header generator/makeheader.py +++ b/test cases/common/57 custom header generator/makeheader.py @@ -8,5 +8,5 @@ template = '#define RET_VAL %s\n' with open(sys.argv[1]) as f: output = template % (f.readline().strip(), ) -with open(sys.argv[2], 'w') as f: +with open(sys.argv[2], 'w', encoding='utf-8') as f: f.write(output) diff --git a/test cases/common/58 multiple generators/mygen.py b/test cases/common/58 multiple generators/mygen.py index 99dc33136b67..15a4b78320c8 100755 --- a/test cases/common/58 multiple generators/mygen.py +++ b/test cases/common/58 multiple generators/mygen.py @@ -13,9 +13,9 @@ outhdr = os.path.join(outdir, 'source%s.h' % val) outsrc = os.path.join(outdir, 'source%s.cpp' % val) -with open(outhdr, 'w') as f: +with open(outhdr, 'w', encoding='utf-8') as f: f.write('int func%s();\n' % val) -with open(outsrc, 'w') as f: +with open(outsrc, 'w', encoding='utf-8') as f: f.write('''int func%s() { return 0; } diff --git a/test cases/common/65 build always/version_gen.py b/test cases/common/65 build always/version_gen.py index fbe2df93f7c7..922f79c1b329 100755 --- a/test cases/common/65 build always/version_gen.py +++ b/test cases/common/65 build always/version_gen.py @@ -19,7 +19,7 @@ def generate(infile, outfile, fallback): return except OSError: pass - with open(outfile, 'w') as f: + with open(outfile, 'w', encoding='utf-8') as f: f.write(newdata) if __name__ == '__main__': diff --git a/test cases/common/69 configure file in custom target/src/mycompiler.py b/test cases/common/69 configure file in custom target/src/mycompiler.py index b00c862db79e..281697f1e8e3 100644 --- a/test cases/common/69 configure file in custom target/src/mycompiler.py +++ b/test cases/common/69 configure file in custom target/src/mycompiler.py @@ -5,5 +5,5 @@ with open(sys.argv[1]) as ifile: if ifile.readline().strip() != '42': print('Incorrect input') -with open(sys.argv[2], 'w') as ofile: +with open(sys.argv[2], 'w', encoding='utf-8') as ofile: ofile.write('Success\n') diff --git a/test cases/common/71 ctarget dependency/gen1.py b/test cases/common/71 ctarget dependency/gen1.py index dbadb6d92338..29dec2f5173a 100755 --- a/test cases/common/71 ctarget dependency/gen1.py +++ b/test cases/common/71 ctarget dependency/gen1.py @@ -8,5 +8,5 @@ with open(sys.argv[1]) as f: contents = f.read() -with open(sys.argv[2], 'w') as f: +with open(sys.argv[2], 'w', encoding='utf-8') as f: f.write(contents) diff --git a/test cases/common/71 ctarget dependency/gen2.py b/test cases/common/71 ctarget dependency/gen2.py index 6fde55600b21..12979af57a71 100755 --- a/test cases/common/71 ctarget dependency/gen2.py +++ b/test cases/common/71 ctarget dependency/gen2.py @@ -6,5 +6,5 @@ files = glob(os.path.join(sys.argv[1], '*.tmp')) assert len(files) == 1 -with open(files[0]) as ifile, open(sys.argv[2], 'w') as ofile: +with open(files[0]) as ifile, open(sys.argv[2], 'w', encoding='utf-8') as ofile: ofile.write(ifile.read()) diff --git a/test cases/common/8 install/gendir.py b/test cases/common/8 install/gendir.py index b42327db3d1d..5190bf75af03 100755 --- a/test cases/common/8 install/gendir.py +++ b/test cases/common/8 install/gendir.py @@ -5,4 +5,4 @@ dirname = sys.argv[1] fname = os.path.join(dirname, 'file.txt') os.makedirs(dirname, exist_ok=True) -open(fname, 'w').close() +open(fname, 'w', encoding='utf-8').close() diff --git a/test cases/common/86 private include/stlib/compiler.py b/test cases/common/86 private include/stlib/compiler.py index 7a2effae7fbf..c92ec31124fa 100755 --- a/test cases/common/86 private include/stlib/compiler.py +++ b/test cases/common/86 private include/stlib/compiler.py @@ -26,7 +26,7 @@ c_code = c_templ % (base, base) h_code = h_templ % base -with open(cfile, 'w') as f: +with open(cfile, 'w', encoding='utf-8') as f: f.write(c_code) -with open(hfile, 'w') as f: +with open(hfile, 'w', encoding='utf-8') as f: f.write(h_code) diff --git a/test cases/common/88 dep fallback/subprojects/boblib/genbob.py b/test cases/common/88 dep fallback/subprojects/boblib/genbob.py index 34af7790efa7..d62a81ed1ed7 100644 --- a/test cases/common/88 dep fallback/subprojects/boblib/genbob.py +++ b/test cases/common/88 dep fallback/subprojects/boblib/genbob.py @@ -2,5 +2,5 @@ import sys -with open(sys.argv[1], 'w') as f: +with open(sys.argv[1], 'w', encoding='utf-8') as f: f.write('') diff --git a/test cases/common/90 gen extra/srcgen.py b/test cases/common/90 gen extra/srcgen.py index c64f54000607..ef8b23c8a0ea 100755 --- a/test cases/common/90 gen extra/srcgen.py +++ b/test cases/common/90 gen extra/srcgen.py @@ -23,5 +23,5 @@ if options.upper: funcname = funcname.upper() -with open(options.output, 'w') as f: +with open(options.output, 'w', encoding='utf-8') as f: f.write(c_templ % funcname) diff --git a/test cases/common/90 gen extra/srcgen2.py b/test cases/common/90 gen extra/srcgen2.py index 9cdf12d5947b..de275655c457 100644 --- a/test cases/common/90 gen extra/srcgen2.py +++ b/test cases/common/90 gen extra/srcgen2.py @@ -19,7 +19,7 @@ output_c = os.path.join(options.target_dir, options.stem + ".tab.c") -with open(output_c, 'w') as f: +with open(output_c, 'w', encoding='utf-8') as f: f.write(content) @@ -28,5 +28,5 @@ int myfun(void); ''' -with open(output_h, 'w') as f: +with open(output_h, 'w', encoding='utf-8') as f: f.write(h_content) diff --git a/test cases/common/95 manygen/subdir/manygen.py b/test cases/common/95 manygen/subdir/manygen.py index 931fb61de557..4231ee2d0b7d 100755 --- a/test cases/common/95 manygen/subdir/manygen.py +++ b/test cases/common/95 manygen/subdir/manygen.py @@ -40,21 +40,21 @@ tmpc = 'diibadaaba.c' tmpo = 'diibadaaba' + objsuffix -with open(outc, 'w') as f: +with open(outc, 'w', encoding='utf-8') as f: f.write('''#include"{}.h" int {}_in_src(void) {{ return 0; }} '''.format(funcname, funcname)) -with open(outh, 'w') as f: +with open(outh, 'w', encoding='utf-8') as f: f.write('''#pragma once int {}_in_lib(void); int {}_in_obj(void); int {}_in_src(void); '''.format(funcname, funcname, funcname)) -with open(tmpc, 'w') as f: +with open(tmpc, 'w', encoding='utf-8') as f: f.write('''int %s_in_obj(void) { return 0; } @@ -65,7 +65,7 @@ else: subprocess.check_call(compiler + ['-c', '-o', outo, tmpc]) -with open(tmpc, 'w') as f: +with open(tmpc, 'w', encoding='utf-8') as f: f.write('''int %s_in_lib() { return 0; } diff --git a/test cases/cython/2 generated sources/gen.py b/test cases/cython/2 generated sources/gen.py index 5c0a82d568ad..7ac7002cb61c 100644 --- a/test cases/cython/2 generated sources/gen.py +++ b/test cases/cython/2 generated sources/gen.py @@ -7,7 +7,7 @@ parser.add_argument('output') args = parser.parse_args() -with open(args.output, 'w') as f: +with open(args.output, 'w', encoding='utf-8') as f: f.write(textwrap.dedent('''\ cpdef func(): return "Hello, World!" diff --git a/test cases/cython/2 generated sources/generator.py b/test cases/cython/2 generated sources/generator.py index 9b0d94a145f3..9fb523be80f3 100755 --- a/test cases/cython/2 generated sources/generator.py +++ b/test cases/cython/2 generated sources/generator.py @@ -8,5 +8,5 @@ parser.add_argument('output') args = parser.parse_args() -with open(args.input) as i, open(args.output, 'w') as o: +with open(args.input) as i, open(args.output, 'w', encoding='utf-8') as o: o.write(i.read()) diff --git a/test cases/cython/2 generated sources/libdir/gen.py b/test cases/cython/2 generated sources/libdir/gen.py index 5c0a82d568ad..7ac7002cb61c 100644 --- a/test cases/cython/2 generated sources/libdir/gen.py +++ b/test cases/cython/2 generated sources/libdir/gen.py @@ -7,7 +7,7 @@ parser.add_argument('output') args = parser.parse_args() -with open(args.output, 'w') as f: +with open(args.output, 'w', encoding='utf-8') as f: f.write(textwrap.dedent('''\ cpdef func(): return "Hello, World!" diff --git a/test cases/failing/40 custom target outputs not matching install_dirs/generator.py b/test cases/failing/40 custom target outputs not matching install_dirs/generator.py index 4ac61795b5a9..1847904f5c7c 100755 --- a/test cases/failing/40 custom target outputs not matching install_dirs/generator.py +++ b/test cases/failing/40 custom target outputs not matching install_dirs/generator.py @@ -8,9 +8,9 @@ name = sys.argv[1] odir = sys.argv[2] -with open(os.path.join(odir, name + '.h'), 'w') as f: +with open(os.path.join(odir, name + '.h'), 'w', encoding='utf-8') as f: f.write('int func();\n') -with open(os.path.join(odir, name + '.c'), 'w') as f: +with open(os.path.join(odir, name + '.c'), 'w', encoding='utf-8') as f: f.write('int main(int argc, char *argv[]) { return 0; }') -with open(os.path.join(odir, name + '.sh'), 'w') as f: +with open(os.path.join(odir, name + '.sh'), 'w', encoding='utf-8') as f: f.write('#!/bin/bash') diff --git a/test cases/frameworks/10 gtk-doc/include/generate-enums-docbook.py b/test cases/frameworks/10 gtk-doc/include/generate-enums-docbook.py index 41c6121b2222..9eb6ae1578f6 100644 --- a/test cases/frameworks/10 gtk-doc/include/generate-enums-docbook.py +++ b/test cases/frameworks/10 gtk-doc/include/generate-enums-docbook.py @@ -49,7 +49,7 @@ if __name__ == '__main__': if len(sys.argv) >= 4: - with open(sys.argv[1], 'w') as doc_out: + with open(sys.argv[1], 'w', encoding='utf-8') as doc_out: enum_name = sys.argv[2] enum_type = sys.argv[3] diff --git a/test cases/python/4 custom target depends extmodule/blaster.py b/test cases/python/4 custom target depends extmodule/blaster.py index 939010080c09..2205f7324c69 100644 --- a/test cases/python/4 custom target depends extmodule/blaster.py +++ b/test cases/python/4 custom target depends extmodule/blaster.py @@ -22,7 +22,7 @@ result = tachyon.phaserize('shoot') if options.output: - with open(options.output, 'w') as f: + with open(options.output, 'w', encoding='utf-8') as f: f.write('success') if not isinstance(result, int): diff --git a/test cases/python3/4 custom target depends extmodule/blaster.py b/test cases/python3/4 custom target depends extmodule/blaster.py index 9cce64505e5d..f08db2f8f1ee 100644 --- a/test cases/python3/4 custom target depends extmodule/blaster.py +++ b/test cases/python3/4 custom target depends extmodule/blaster.py @@ -23,7 +23,7 @@ result = tachyon.phaserize('shoot') if options.output: - with open(options.output, 'w') as f: + with open(options.output, 'w', encoding='utf-8') as f: f.write('success') if not isinstance(result, int): diff --git a/test cases/rust/11 generated main/gen.py b/test cases/rust/11 generated main/gen.py index c8cfe76ec009..d37712f76c3a 100644 --- a/test cases/rust/11 generated main/gen.py +++ b/test cases/rust/11 generated main/gen.py @@ -9,7 +9,7 @@ def main() -> None: parser.add_argument('--mode', choices=['main', 'lib'], default='main') args = parser.parse_args() - with open(args.out, 'w') as f: + with open(args.out, 'w', encoding='utf-8') as f: if args.mode == 'main': f.write('fn main() { println!("I prefer tarnish, actually.") }') elif args.mode == 'lib': diff --git a/test cases/rust/19 structured sources/gen.py b/test cases/rust/19 structured sources/gen.py index 16e5c04e1c21..4202da097397 100755 --- a/test cases/rust/19 structured sources/gen.py +++ b/test cases/rust/19 structured sources/gen.py @@ -9,7 +9,7 @@ def main() -> None: parser.add_argument('output') args = parser.parse_args() - with open(args.output, 'w') as f: + with open(args.output, 'w', encoding='utf-8') as f: f.write(textwrap.dedent('''\ pub fn bar() -> () { println!("Hello, World!"); diff --git a/test cases/unit/26 install umask/myinstall.py b/test cases/unit/26 install umask/myinstall.py index db6a51c2b53c..ba03a1c812ec 100644 --- a/test cases/unit/26 install umask/myinstall.py +++ b/test cases/unit/26 install umask/myinstall.py @@ -13,5 +13,5 @@ if not os.path.isdir(dirname): raise -with open(os.path.join(dirname, sys.argv[2]), 'w') as f: +with open(os.path.join(dirname, sys.argv[2]), 'w', encoding='utf-8') as f: f.write('') diff --git a/test cases/unit/94 custominc/easytogrepfor/genh.py b/test cases/unit/94 custominc/easytogrepfor/genh.py index 48e033a728ee..acaf7b437016 100644 --- a/test cases/unit/94 custominc/easytogrepfor/genh.py +++ b/test cases/unit/94 custominc/easytogrepfor/genh.py @@ -2,6 +2,6 @@ import sys -f = open(sys.argv[1], 'w') +f = open(sys.argv[1], 'w', encoding='utf-8') f.write('#define RETURN_VALUE 0') f.close() diff --git a/test cases/unit/98 install all targets/script.py b/test cases/unit/98 install all targets/script.py index c5f3be9ed889..b243b405d977 100644 --- a/test cases/unit/98 install all targets/script.py +++ b/test cases/unit/98 install all targets/script.py @@ -3,5 +3,5 @@ import sys for f in sys.argv[1:]: - with open(f, 'w') as f: + with open(f, 'w', encoding='utf-8') as f: pass diff --git a/test cases/unit/98 install all targets/subdir/script.py b/test cases/unit/98 install all targets/subdir/script.py index c5f3be9ed889..b243b405d977 100644 --- a/test cases/unit/98 install all targets/subdir/script.py +++ b/test cases/unit/98 install all targets/subdir/script.py @@ -3,5 +3,5 @@ import sys for f in sys.argv[1:]: - with open(f, 'w') as f: + with open(f, 'w', encoding='utf-8') as f: pass diff --git a/test cases/vala/10 mixed sources/c/writec.py b/test cases/vala/10 mixed sources/c/writec.py index 2cc822bae47e..aa8367317fb7 100644 --- a/test cases/vala/10 mixed sources/c/writec.py +++ b/test cases/vala/10 mixed sources/c/writec.py @@ -8,5 +8,5 @@ } ''' -with open(sys.argv[1], 'w') as f: +with open(sys.argv[1], 'w', encoding='utf-8') as f: f.write(c) diff --git a/test cases/vala/8 generated sources/src/write_wrapper.py b/test cases/vala/8 generated sources/src/write_wrapper.py index b5608f7a3275..ef0df6ca57b6 100644 --- a/test cases/vala/8 generated sources/src/write_wrapper.py +++ b/test cases/vala/8 generated sources/src/write_wrapper.py @@ -8,5 +8,5 @@ } ''' -with open(sys.argv[1], 'w') as f: +with open(sys.argv[1], 'w', encoding='utf-8') as f: f.write(contents) diff --git a/test cases/windows/10 vs module defs generated custom target/subdir/make_def.py b/test cases/windows/10 vs module defs generated custom target/subdir/make_def.py index 50acfb5ef2c2..e20e99936301 100755 --- a/test cases/windows/10 vs module defs generated custom target/subdir/make_def.py +++ b/test cases/windows/10 vs module defs generated custom target/subdir/make_def.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 import sys -with open(sys.argv[1], 'w') as f: +with open(sys.argv[1], 'w', encoding='utf-8') as f: print('EXPORTS', file=f) print(' somedllfunc', file=f) diff --git a/test cases/windows/12 resources with custom targets/res/gen-res.py b/test cases/windows/12 resources with custom targets/res/gen-res.py index e5ef6b108d9a..71f9d00dec2f 100755 --- a/test cases/windows/12 resources with custom targets/res/gen-res.py +++ b/test cases/windows/12 resources with custom targets/res/gen-res.py @@ -2,5 +2,5 @@ import sys -with open(sys.argv[1]) as infile, open(sys.argv[2], 'w') as outfile: +with open(sys.argv[1]) as infile, open(sys.argv[2], 'w', encoding='utf-8') as outfile: outfile.write(infile.read().format(icon=sys.argv[3])) From 6d97521829e52e1b9653649c2db76c30f0e98ad5 Mon Sep 17 00:00:00 2001 From: Arthur Grillo Date: Fri, 14 Nov 2025 22:02:54 -0300 Subject: [PATCH 101/681] mtest: Log TAP directive explanation if present The TAP protocol allows directives (like SKIP or TODO) to include a "Reason" comment. Meson parses these but do not display them in the console output. Fix that by login the explanation next to the TestResult. --- mesonbuild/mtest.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index 74748a27992b..72621946088c 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -510,7 +510,8 @@ def start(self, harness: 'TestHarness') -> None: def start_test(self, harness: 'TestHarness', test: 'TestRun') -> None: pass - def log_subtest(self, harness: 'TestHarness', test: 'TestRun', s: str, res: TestResult) -> None: + def log_subtest(self, harness: 'TestHarness', test: 'TestRun', s: str, res: TestResult, + explanation: T.Optional[str]) -> None: pass def log(self, harness: 'TestHarness', result: 'TestRun') -> None: @@ -709,7 +710,7 @@ def print_log(self, harness: 'TestHarness', result: 'TestRun') -> None: print_safe(log) print(self.output_end) - def log_subtest(self, harness: 'TestHarness', test: 'TestRun', s: str, result: TestResult) -> None: + def log_subtest(self, harness: 'TestHarness', test: 'TestRun', s: str, result: TestResult, explanation: T.Optional[str]) -> None: if test.verbose or (harness.options.print_errorlogs and result.is_bad()): self.flush() print(harness.format(test, mlog.colorize_console(), max_left_width=self.max_left_width, @@ -717,6 +718,9 @@ def log_subtest(self, harness: 'TestHarness', test: 'TestRun', s: str, result: T middle=s, right=result.get_text(mlog.colorize_console())), flush=True) + if explanation is not None: + print(result.colorize(f"{' ' * len(self.sub)}Reason: {explanation}").get_text(mlog.colorize_console())) + self.request_update() def log(self, harness: 'TestHarness', result: 'TestRun') -> None: @@ -1161,12 +1165,12 @@ async def parse(self, harness: 'TestHarness', lines: T.AsyncIterator[str]) -> No version = i.version elif isinstance(i, TAPParser.Bailout): res = TestResult.ERROR - harness.log_subtest(self, i.message, res) + harness.log_subtest(self, i.message, res, None) elif isinstance(i, TAPParser.Test): self.results.append(i) if i.result.is_bad(): res = TestResult.FAIL - harness.log_subtest(self, i.name or f'subtest {i.number}', i.result) + harness.log_subtest(self, i.name or f'subtest {i.number}', i.result, i.explanation) elif isinstance(i, TAPParser.UnknownLine): warnings.append(i) elif isinstance(i, TAPParser.Error): @@ -1224,7 +1228,7 @@ def parse_res(n: int, name: str, result: str) -> TAPParser.Test: name = name.replace('::', '.') t = parse_res(n, name, result) self.results.append(t) - harness.log_subtest(self, name, t.result) + harness.log_subtest(self, name, t.result, None) n += 1 res = None @@ -2126,9 +2130,9 @@ def run_tests(self, runners: T.List[SingleTestRunner]) -> None: finally: self.close_logfiles() - def log_subtest(self, test: TestRun, s: str, res: TestResult) -> None: + def log_subtest(self, test: TestRun, s: str, res: TestResult, explanation: T.Optional[str]) -> None: for l in self.loggers: - l.log_subtest(self, test, s, res) + l.log_subtest(self, test, s, res, explanation) def log_start_test(self, test: TestRun) -> None: for l in self.loggers: From 56a4cefa0b01a6b7223f50dc51af8f703ae4f623 Mon Sep 17 00:00:00 2001 From: Katalin Rebhan Date: Sun, 16 Feb 2025 23:59:28 +0100 Subject: [PATCH 102/681] More robust handling of linker argument wrapping Not all compilers (swiftc in case of #14261) concatenate linker arguments with ','. This removes that assumption and instead always passes the arguments in a list where applicable, and reworks the linker argument wrapper syntaxes for the compilers to more accurately represent the three different option styles. Some Apple ld options, notably -install_name, are also not exposed directly on the compiler options interface, hence add the linker prefix here. --- mesonbuild/compilers/compilers.py | 71 +++++++++++++++++++++- mesonbuild/compilers/cuda.py | 4 +- mesonbuild/compilers/d.py | 55 +++-------------- mesonbuild/compilers/detect.py | 2 +- mesonbuild/compilers/fortran.py | 5 +- mesonbuild/compilers/mixins/gnu.py | 4 +- mesonbuild/compilers/mixins/tasking.py | 3 +- mesonbuild/compilers/swift.py | 4 +- mesonbuild/linkers/detect.py | 31 +++------- mesonbuild/linkers/linkers.py | 82 +++++++++++++------------- unittests/internaltests.py | 5 +- 11 files changed, 141 insertions(+), 125 deletions(-) diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 335bafbbb9dc..2bc2ec25d092 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -454,6 +454,75 @@ class RunResult(HoldableObject): cached: bool = False +class LinkerOptionStyle(metaclass=abc.ABCMeta): + @abc.abstractmethod + def wrap(self, group: T.List[str]) -> T.List[str]: + ... + + +@dataclass +class PrefixArgumentLinkerOptionStyle(LinkerOptionStyle): + """ + Represents a linker option style where each linker argument is preceded by a separate argument representing + the compiler's pass-through option, like Clang: ['-Xlinker', 'arg1', '-Xlinker', 'arg2'] passes ['arg1', 'arg2'] to the + linker. + """ + + prefix_arg: str + + def wrap(self, group: T.List[str]) -> T.List[str]: + return [compiler_arg for linker_arg in group for compiler_arg in [self.prefix_arg, linker_arg]] + + +@dataclass +class SimplePrefixLinkerOptionStyle(LinkerOptionStyle): + """ + Represents a linker option style where each linker argument is prefixed with the compiler's pass-through + option as part of the same argument, like DMD: ['-L=arg1', '-L=arg2'] passes ['arg1', 'arg2'] to the linker. + """ + + prefix: str + + def wrap(self, group: T.List[str]) -> T.List[str]: + return [self.prefix + linker_arg for linker_arg in group] + + +class LinkerArgumentWrapException(MesonException): + pass + + +@dataclass +class ManyInOneLinkerOptionStyle(LinkerOptionStyle): + """ + Represents a linker option style where multiple linker arguments are passed as a single argument to the + compiler combined with a prefix, like GCC: ['-Wl,arg1,arg2'] passes ['arg1', 'arg2'] to the linker. + """ + + prefix: str + separator: str + + fallback: T.Optional[LinkerOptionStyle] = None + + def wrap(self, group: T.List[str]) -> T.List[str]: + for el in group: + if self.separator not in el: + continue + + if self.fallback is not None: + return self.fallback.wrap(group) + + full = '' + if len(group) > 1: + full = f' (part of arguments list {group!r})' + stripped = f'{self.prefix}{el}' + raise LinkerArgumentWrapException(f'Cannot wrap linker argument {el!r}{full} for compiler interface: ' + f'would result in {stripped!r}, which the compiler would interpret as ' + f'multiple linker arguments since it treats {self.separator!r} as the ' + 'separator.') + + return [self.prefix + self.separator.join(group)] + + @dataclass class CompileResult(HoldableObject): @@ -476,7 +545,7 @@ class Compiler(HoldableObject, metaclass=SimpleABC): # manually searched. internal_libs: T.List[str] = [] - LINKER_PREFIX: T.Union[None, str, T.List[str]] = None + LINKER_PREFIX: T.Optional[LinkerOptionStyle] = None # If the compiler is used to fire a separate linking step, environment # variables like CFLAGS have to be passed to the linking step as well. diff --git a/mesonbuild/compilers/cuda.py b/mesonbuild/compilers/cuda.py index 73a73331602c..8b4428bfa550 100644 --- a/mesonbuild/compilers/cuda.py +++ b/mesonbuild/compilers/cuda.py @@ -10,7 +10,7 @@ from .. import options from ..mesonlib import is_windows, LibType, version_compare -from .compilers import Compiler, CompileCheckMode, CrossNoRunException +from .compilers import Compiler, CompileCheckMode, CrossNoRunException, SimplePrefixLinkerOptionStyle if T.TYPE_CHECKING: from ..build import BuildTarget @@ -45,7 +45,7 @@ class Phase(enum.Enum): class CudaCompiler(Compiler): - LINKER_PREFIX = '-Xlinker=' + LINKER_PREFIX = SimplePrefixLinkerOptionStyle('-Xlinker=') language = 'cuda' # NVCC flags taking no arguments. diff --git a/mesonbuild/compilers/d.py b/mesonbuild/compilers/d.py index 31c342303a55..002db7912e9c 100644 --- a/mesonbuild/compilers/d.py +++ b/mesonbuild/compilers/d.py @@ -11,7 +11,7 @@ from ..arglist import CompilerArgs from ..linkers import RSPFileSyntax from ..mesonlib import ( - EnvironmentException, version_compare, is_windows + EnvironmentException, MesonBugException, version_compare, is_windows ) from ..options import OptionKey @@ -19,13 +19,14 @@ clike_debug_args, Compiler, CompileCheckMode, + SimplePrefixLinkerOptionStyle, ) from .mixins.gnu import GnuCompiler from .mixins.gnu import gnu_common_warning_args if T.TYPE_CHECKING: from . import compilers - from ..build import BuildTarget, DFeatures + from ..build import DFeatures from ..dependencies import Dependency from ..envconfig import MachineInfo from ..environment import Environment @@ -108,7 +109,7 @@ def __init__(self, dmd_frontend_version: T.Optional[str]): def _get_target_arch_args(self) -> T.List[str]: ... - LINKER_PREFIX = '-L=' + LINKER_PREFIX = SimplePrefixLinkerOptionStyle('-L=') def get_output_args(self, outputname: str) -> T.List[str]: return ['-of=' + outputname] @@ -174,32 +175,6 @@ def get_optimization_link_args(self, optimization_level: str) -> T.List[str]: def gen_import_library_args(self, implibname: str) -> T.List[str]: return self.linker.import_library_args(implibname) - def build_rpath_args(self, build_dir: str, from_dir: str, target: BuildTarget, - extra_paths: T.Optional[T.List[str]] = None - ) -> T.Tuple[T.List[str], T.Set[bytes]]: - if self.info.is_windows(): - return ([], set()) - - # GNU ld, solaris ld, and lld acting like GNU ld - if self.linker.id.startswith('ld'): - # The way that dmd and ldc pass rpath to gcc is different than we would - # do directly, each argument -rpath and the value to rpath, need to be - # split into two separate arguments both prefaced with the -L=. - args: T.List[str] = [] - (rpath_args, rpath_dirs_to_remove) = super().build_rpath_args( - build_dir, from_dir, target) - for r in rpath_args: - if ',' in r: - a, b = r.split(',', maxsplit=1) - args.append(a) - args.append(self.LINKER_PREFIX + b) - else: - args.append(r) - return (args, rpath_dirs_to_remove) - - return super().build_rpath_args( - build_dir, from_dir, target) - @classmethod def _translate_args_to_nongnu(cls, args: T.List[str], info: MachineInfo, link_id: str) -> T.List[str]: # Translate common arguments to flags the LDC/DMD compilers @@ -380,24 +355,10 @@ def get_soname_args(self, prefix: str, shlib_name: str, suffix: str, soversion: darwin_versions: T.Tuple[str, str]) -> T.List[str]: sargs = super().get_soname_args(prefix, shlib_name, suffix, soversion, darwin_versions) - # LDC and DMD actually do use a linker, but they proxy all of that with - # their own arguments - soargs: T.List[str] = [] - if self.linker.id.startswith('ld.'): - for arg in sargs: - a, b = arg.split(',', maxsplit=1) - soargs.append(a) - soargs.append(self.LINKER_PREFIX + b) - return soargs - elif self.linker.id.startswith('ld64'): - for arg in sargs: - if not arg.startswith(self.LINKER_PREFIX): - soargs.append(self.LINKER_PREFIX + arg) - else: - soargs.append(arg) - return soargs - else: - return sargs + if not all(arg.startswith(self.LINKER_PREFIX.prefix) for arg in sargs): + raise MesonBugException(f'Not all soname arguments for the D compiler start with {repr(self.LINKER_PREFIX.prefix)}: {sargs}') + + return sargs def get_allow_undefined_link_args(self) -> T.List[str]: args = self.linker.get_allow_undefined_args() diff --git a/mesonbuild/compilers/detect.py b/mesonbuild/compilers/detect.py index 15aaef898393..dabe6f6df0d6 100644 --- a/mesonbuild/compilers/detect.py +++ b/mesonbuild/compilers/detect.py @@ -385,7 +385,7 @@ def sanitize(p: T.Optional[str]) -> T.Optional[str]: # linker. It'll exit with an error code, but still print the # linker version. with tempfile.NamedTemporaryFile(suffix='.c') as f: - cmd = compiler + [cls.LINKER_PREFIX + "--version", f.name] + cmd = [*compiler, *cls.LINKER_PREFIX.wrap(['--version']), f.name] _, o, _ = Popen_safe(cmd) linker = linkers.WASMDynamicLinker( diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py index bba860d923fd..eef4f298ed0b 100644 --- a/mesonbuild/compilers/fortran.py +++ b/mesonbuild/compilers/fortran.py @@ -14,6 +14,7 @@ clike_debug_args, Compiler, CompileCheckMode, + ManyInOneLinkerOptionStyle, ) from .mixins.clike import CLikeCompiler from .mixins.gnu import GnuCompiler, gnu_optimization_args @@ -346,7 +347,7 @@ def get_module_outdir_args(self, path: str) -> T.List[str]: class G95FortranCompiler(FortranCompiler): - LINKER_PREFIX = '-Wl,' + LINKER_PREFIX = ManyInOneLinkerOptionStyle('-Wl,', ',') id = 'g95' def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, @@ -367,7 +368,7 @@ def get_module_outdir_args(self, path: str) -> T.List[str]: class SunFortranCompiler(FortranCompiler): - LINKER_PREFIX = '-Wl,' + LINKER_PREFIX = ManyInOneLinkerOptionStyle('-Wl,', ',') id = 'sun' def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]: diff --git a/mesonbuild/compilers/mixins/gnu.py b/mesonbuild/compilers/mixins/gnu.py index d6ef7955ecfc..7cb9d9f500be 100644 --- a/mesonbuild/compilers/mixins/gnu.py +++ b/mesonbuild/compilers/mixins/gnu.py @@ -17,7 +17,7 @@ from ... import mesonlib from ... import mlog from ...options import OptionKey, UserStdOption -from mesonbuild.compilers.compilers import CompileCheckMode +from mesonbuild.compilers.compilers import CompileCheckMode, ManyInOneLinkerOptionStyle if T.TYPE_CHECKING: from ..._typing import ImmutableListProtocol @@ -356,7 +356,7 @@ class GnuLikeCompiler(Compiler, metaclass=mesonlib.SimpleABC): that the actual concrete subclass define their own implementation. """ - LINKER_PREFIX = '-Wl,' + LINKER_PREFIX = ManyInOneLinkerOptionStyle('-Wl,', ',') def __init__(self) -> None: self.base_options = { diff --git a/mesonbuild/compilers/mixins/tasking.py b/mesonbuild/compilers/mixins/tasking.py index 082cff073908..c2974929a41d 100644 --- a/mesonbuild/compilers/mixins/tasking.py +++ b/mesonbuild/compilers/mixins/tasking.py @@ -7,6 +7,7 @@ import os import typing as T +from ...compilers.compilers import SimplePrefixLinkerOptionStyle from ...mesonlib import EnvironmentException from ...options import OptionKey @@ -48,7 +49,7 @@ class TaskingCompiler(Compiler): Functionality that is common to all TASKING family compilers. ''' - LINKER_PREFIX = '-Wl' + LINKER_PREFIX = SimplePrefixLinkerOptionStyle('-Wl') def __init__(self) -> None: if not self.is_cross: diff --git a/mesonbuild/compilers/swift.py b/mesonbuild/compilers/swift.py index feaa009e0cd1..a2e0e5056cfc 100644 --- a/mesonbuild/compilers/swift.py +++ b/mesonbuild/compilers/swift.py @@ -9,7 +9,7 @@ from .. import mlog, options from ..mesonlib import first, MesonException, version_compare -from .compilers import Compiler, clike_debug_args +from .compilers import Compiler, clike_debug_args, PrefixArgumentLinkerOptionStyle if T.TYPE_CHECKING: from .. import build @@ -32,7 +32,7 @@ class SwiftCompiler(Compiler): - LINKER_PREFIX = ['-Xlinker'] + LINKER_PREFIX = PrefixArgumentLinkerOptionStyle('-Xlinker') language = 'swift' id = 'llvm' diff --git a/mesonbuild/linkers/detect.py b/mesonbuild/linkers/detect.py index cf6803a19418..69188bde109b 100644 --- a/mesonbuild/linkers/detect.py +++ b/mesonbuild/linkers/detect.py @@ -50,10 +50,8 @@ def guess_win_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty # Explicitly pass logo here so that we can get the version of link.exe if not use_linker_prefix or comp_class.LINKER_PREFIX is None: check_args = ['/logo', '--version'] - elif isinstance(comp_class.LINKER_PREFIX, str): - check_args = [comp_class.LINKER_PREFIX + '/logo', comp_class.LINKER_PREFIX + '--version'] - else: # list - check_args = comp_class.LINKER_PREFIX + ['/logo'] + comp_class.LINKER_PREFIX + ['--version'] + else: + check_args = comp_class.LINKER_PREFIX.wrap(['/logo', '--version']) check_args += env.coredata.get_external_link_args(for_machine, comp_class.language) @@ -85,7 +83,7 @@ def guess_win_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty rsp_syntax=rsp_syntax) return linkers.ClangClDynamicLinker( env, for_machine, [], - prefix=comp_class.LINKER_PREFIX if use_linker_prefix else [], + prefix=comp_class.LINKER_PREFIX if use_linker_prefix else None, exelist=compiler, version=search_version(o), direct=invoked_directly, rsp_syntax=rsp_syntax) elif 'OPTLINK' in o: @@ -101,7 +99,7 @@ def guess_win_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty return linkers.MSVCDynamicLinker( env, for_machine, [], machine=target, exelist=compiler, - prefix=comp_class.LINKER_PREFIX if use_linker_prefix else [], + prefix=comp_class.LINKER_PREFIX if use_linker_prefix else None, version=search_version(out), direct=invoked_directly, rsp_syntax=rsp_syntax) elif 'GNU coreutils' in o: @@ -132,11 +130,7 @@ def guess_nix_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty system = env.machines[for_machine].system ldflags = env.coredata.get_external_link_args(for_machine, comp_class.language) extra_args += comp_class._unix_args_to_native(ldflags, env.machines[for_machine]) - - if isinstance(comp_class.LINKER_PREFIX, str): - check_args = [comp_class.LINKER_PREFIX + '--version'] + extra_args - else: - check_args = comp_class.LINKER_PREFIX + ['--version'] + extra_args + check_args = comp_class.LINKER_PREFIX.wrap(['--version']) + extra_args override: T.List[str] = [] value = env.lookup_binary_entry(for_machine, comp_class.language + '_ld') @@ -153,10 +147,7 @@ def guess_nix_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty v = search_version(o + e) linker: DynamicLinker if 'LLD' in o.split('\n', maxsplit=1)[0] or 'tiarmlnk' in e: - if isinstance(comp_class.LINKER_PREFIX, str): - cmd = compiler + override + [comp_class.LINKER_PREFIX + '-v'] + extra_args - else: - cmd = compiler + override + comp_class.LINKER_PREFIX + ['-v'] + extra_args + cmd = compiler + override + comp_class.LINKER_PREFIX.wrap(['-v']) + extra_args _, newo, newerr = Popen_safe_logged(cmd, msg='Detecting LLD linker via') lld_cls: T.Type[DynamicLinker] @@ -213,10 +204,7 @@ def guess_nix_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty compiler, env, for_machine, comp_class.LINKER_PREFIX, override, version=v) elif 'ld: 0706-012 The -- flag is not recognized' in e: - if isinstance(comp_class.LINKER_PREFIX, str): - _, _, e = Popen_safe(compiler + [comp_class.LINKER_PREFIX + '-V'] + extra_args) - else: - _, _, e = Popen_safe(compiler + comp_class.LINKER_PREFIX + ['-V'] + extra_args) + _, _, e = Popen_safe(compiler + comp_class.LINKER_PREFIX.wrap(['-V']) + extra_args) linker = linkers.AIXDynamicLinker( compiler, env, for_machine, comp_class.LINKER_PREFIX, override, version=search_version(e)) @@ -237,10 +225,7 @@ def guess_nix_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty # First might be apple clang, second is for real gcc, the third is icc. # Note that "ld: unknown option: " sometimes instead is "ld: unknown options:". elif e.endswith('(use -v to see invocation)\n') or 'macosx_version' in e or 'ld: unknown option' in e: - if isinstance(comp_class.LINKER_PREFIX, str): - cmd = compiler + [comp_class.LINKER_PREFIX + '-v'] + extra_args - else: - cmd = compiler + comp_class.LINKER_PREFIX + ['-v'] + extra_args + cmd = compiler + comp_class.LINKER_PREFIX.wrap(['-v']) + extra_args _, newo, newerr = Popen_safe_logged(cmd, msg='Detecting Apple linker via') for line in newerr.split('\n'): diff --git a/mesonbuild/linkers/linkers.py b/mesonbuild/linkers/linkers.py index d9fba1d0453f..bb2c0e546f5f 100644 --- a/mesonbuild/linkers/linkers.py +++ b/mesonbuild/linkers/linkers.py @@ -19,6 +19,7 @@ from ..mesonlib import MachineChoice from ..build import BuildTarget from ..compilers import Compiler + from ..compilers.compilers import LinkerOptionStyle class StaticLinker: @@ -132,19 +133,16 @@ class DynamicLinker(metaclass=mesonlib.SimpleABC): def id(self) -> str: pass - def _apply_prefix(self, arg: T.Union[str, T.List[str]]) -> T.List[str]: - args = [arg] if isinstance(arg, str) else arg + def _apply_prefix(self, *args: T.Union[str, T.List[str]]) -> T.List[str]: if self.prefix_arg is None: - return args - elif isinstance(self.prefix_arg, str): - return [self.prefix_arg + arg for arg in args] - ret: T.List[str] = [] - for arg in args: - ret += self.prefix_arg + [arg] - return ret + return mesonlib.listify(list(args)) + + return [arg + for group in args + for arg in self.prefix_arg.wrap(mesonlib.stringlistify(group))] def __init__(self, exelist: T.List[str], env: Environment, - for_machine: mesonlib.MachineChoice, prefix_arg: T.Union[str, T.List[str]], + for_machine: mesonlib.MachineChoice, prefix_arg: T.Optional[LinkerOptionStyle], always_args: T.List[str], *, system: str = 'unknown system', version: str = 'unknown version'): self.exelist = exelist @@ -650,7 +648,7 @@ class GnuLikeDynamicLinkerMixin(DynamicLinkerBase): if T.TYPE_CHECKING: for_machine = MachineChoice.HOST - def _apply_prefix(self, arg: T.Union[str, T.List[str]]) -> T.List[str]: ... + def _apply_prefix(self, *args: T.Union[str, T.List[str]]) -> T.List[str]: ... _OPTIMIZATION_ARGS: T.Dict[str, T.List[str]] = { 'plain': [], @@ -739,7 +737,7 @@ def get_soname_args(self, prefix: str, shlib_name: str, suffix: str, # For PE/COFF the soname argument has no effect return [] sostr = '' if soversion is None else '.' + soversion - return self._apply_prefix(f'-soname,{prefix}{shlib_name}.{suffix}{sostr}') + return self._apply_prefix(['-soname', f'{prefix}{shlib_name}.{suffix}{sostr}']) def build_rpath_args(self, build_dir: str, from_dir: str, target: BuildTarget, extra_paths: T.Optional[T.List[str]] = None @@ -775,7 +773,7 @@ def build_rpath_args(self, build_dir: str, from_dir: str, target: BuildTarget, # by default, but is not on dragonfly/openbsd for some reason. Without this # $ORIGIN in the runtime path will be undefined and any binaries # linked against local libraries will fail to resolve them. - args.extend(self._apply_prefix('-z,origin')) + args.extend(self._apply_prefix(['-z', 'origin'])) # In order to avoid relinking for RPATH removal, the binary needs to contain just # enough space in the ELF header to hold the final installation RPATH. @@ -788,7 +786,7 @@ def build_rpath_args(self, build_dir: str, from_dir: str, target: BuildTarget, paths = padding else: paths = paths + ':' + padding - args.extend(self._apply_prefix('-rpath,' + paths)) + args.extend(self._apply_prefix(['-rpath', paths])) # TODO: should this actually be "for solaris/sunos"? # NOTE: Remove the zigcc check once zig support "-rpath-link" @@ -818,7 +816,7 @@ def build_rpath_args(self, build_dir: str, from_dir: str, target: BuildTarget, # -Wl,-rpath-link,/path/to/folder1:/path/to/folder2:... if self.id in {'ld.bfd', 'ld.gold'} and mesonlib.version_compare(self.version, '<2.28'): for p in rpath_paths: - args.extend(self._apply_prefix('-rpath-link,' + os.path.join(build_dir, p))) + args.extend(self._apply_prefix(['-rpath-link', os.path.join(build_dir, p)])) return (args, rpath_dirs_to_remove) @@ -835,7 +833,7 @@ def get_win_subsystem_args(self, value: str) -> T.List[str]: if newvalue is not None: if versionsuffix is not None: newvalue += f':{versionsuffix}' - args = [f'--subsystem,{newvalue}'] + args = ['--subsystem', newvalue] else: raise mesonlib.MesonBugException(f'win_subsystem: {value!r} not handled in MinGW linker. This should not be possible.') @@ -856,7 +854,7 @@ def get_allow_undefined_args(self) -> T.List[str]: if self.system == 'ios': return [] else: - return self._apply_prefix('-undefined,dynamic_lookup') + return self._apply_prefix(['-undefined', 'dynamic_lookup']) def get_std_shared_module_args(self, target: 'BuildTarget') -> T.List[str]: if self.system == 'ios': @@ -924,7 +922,7 @@ def get_soname_args(self, prefix: str, shlib_name: str, suffix: str, if darwin_versions: args.extend(['-compatibility_version', darwin_versions[0], '-current_version', darwin_versions[1]]) - return args + return self._apply_prefix(args) def build_rpath_args(self, build_dir: str, from_dir: str, target: BuildTarget, extra_paths: T.Optional[T.List[str]] = None @@ -945,12 +943,12 @@ def build_rpath_args(self, build_dir: str, from_dir: str, target: BuildTarget, all_paths.update(extra_paths) for rp in all_paths: rpath_dirs_to_remove.add(rp.encode('utf8')) - args.extend(self._apply_prefix('-rpath,' + rp)) + args.extend(self._apply_prefix(['-rpath', rp])) return (args, rpath_dirs_to_remove) def get_thinlto_cache_args(self, path: str) -> T.List[str]: - return ["-Wl,-cache_path_lto," + path] + return self._apply_prefix(['-cache_path_lto', path]) def get_lto_obj_cache_path(self, path: str) -> T.List[str]: # https://clang.llvm.org/docs/CommandGuide/clang.html#cmdoption-flto @@ -991,7 +989,7 @@ class GnuGoldDynamicLinker(GnuDynamicLinker): id = 'ld.gold' def get_thinlto_cache_args(self, path: str) -> T.List[str]: - return ['-Wl,-plugin-opt,cache-dir=' + path] + return self._apply_prefix(['-plugin-opt', 'cache-dir=' + path]) class GnuBFDDynamicLinker(GnuDynamicLinker): @@ -1004,7 +1002,7 @@ class MoldDynamicLinker(GnuDynamicLinker): id = 'ld.mold' def get_thinlto_cache_args(self, path: str) -> T.List[str]: - return ['-Wl,--thinlto-cache-dir=' + path] + return self._apply_prefix(['--thinlto-cache-dir=' + path]) class LLVMDynamicLinker(GnuLikeDynamicLinkerMixin, PosixDynamicLinkerMixin, DynamicLinker): @@ -1018,7 +1016,7 @@ class LLVMDynamicLinker(GnuLikeDynamicLinkerMixin, PosixDynamicLinkerMixin, Dyna id = 'ld.lld' def __init__(self, exelist: T.List[str], env: Environment, - for_machine: mesonlib.MachineChoice, prefix_arg: T.Union[str, T.List[str]], + for_machine: mesonlib.MachineChoice, prefix_arg: T.Optional[LinkerOptionStyle], always_args: T.List[str], *, system: str = 'unknown system', version: str = 'unknown version'): super().__init__(exelist, env, for_machine, prefix_arg, always_args, system=system, version=version) @@ -1055,7 +1053,7 @@ def no_undefined_args(self) -> T.List[str]: return [] def get_thinlto_cache_args(self, path: str) -> T.List[str]: - return ['-Wl,--thinlto-cache-dir=' + path] + return self._apply_prefix(['--thinlto-cache-dir=' + path]) def get_win_subsystem_args(self, value: str) -> T.List[str]: # lld does not support a numeric subsystem value @@ -1065,7 +1063,7 @@ def get_win_subsystem_args(self, value: str) -> T.List[str]: if value in self._SUBSYSTEMS: if version is not None: value += f':{version}' - return self._apply_prefix([f'--subsystem,{value}']) + return self._apply_prefix(['--subsystem', value]) else: raise mesonlib.MesonBugException(f'win_subsystem: {value} not handled in lld linker. This should not be possible.') @@ -1111,7 +1109,7 @@ class CcrxDynamicLinker(DynamicLinker): def __init__(self, env: Environment, for_machine: mesonlib.MachineChoice, *, version: str = 'unknown version'): - super().__init__(['rlink.exe'], env, for_machine, '', [], + super().__init__(['rlink.exe'], env, for_machine, None, [], version=version) def get_accepts_rsp(self) -> bool: @@ -1146,7 +1144,7 @@ class Xc16DynamicLinker(DynamicLinker): def __init__(self, env: Environment, for_machine: mesonlib.MachineChoice, *, version: str = 'unknown version'): - super().__init__(['xc16-gcc'], env, for_machine, '', [], + super().__init__(['xc16-gcc'], env, for_machine, None, [], version=version) def get_link_whole_for(self, args: T.List[str]) -> T.List[str]: @@ -1210,7 +1208,7 @@ class CompCertDynamicLinker(DynamicLinker): def __init__(self, env: Environment, for_machine: mesonlib.MachineChoice, *, version: str = 'unknown version'): - super().__init__(['ccomp'], env, for_machine, '', [], + super().__init__(['ccomp'], env, for_machine, None, [], version=version) def get_link_whole_for(self, args: T.List[str]) -> T.List[str]: @@ -1249,7 +1247,7 @@ class TIDynamicLinker(DynamicLinker): def __init__(self, exelist: T.List[str], env: Environment, for_machine: mesonlib.MachineChoice, *, version: str = 'unknown version'): - super().__init__(exelist, env, for_machine, '', [], + super().__init__(exelist, env, for_machine, None, [], version=version) def get_link_whole_for(self, args: T.List[str]) -> T.List[str]: @@ -1295,7 +1293,7 @@ class ArmDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker): def __init__(self, env: Environment, for_machine: mesonlib.MachineChoice, *, version: str = 'unknown version'): - super().__init__(['armlink'], env, for_machine, '', [], + super().__init__(['armlink'], env, for_machine, None, [], version=version) def get_accepts_rsp(self) -> bool: @@ -1429,7 +1427,7 @@ class VisualStudioLikeLinkerMixin(DynamicLinkerBase): if T.TYPE_CHECKING: for_machine = MachineChoice.HOST - def _apply_prefix(self, arg: T.Union[str, T.List[str]]) -> T.List[str]: ... + def _apply_prefix(self, *args: T.Union[str, T.List[str]]) -> T.List[str]: ... _OPTIMIZATION_ARGS: T.Dict[str, T.List[str]] = { 'plain': [], @@ -1444,7 +1442,7 @@ def _apply_prefix(self, arg: T.Union[str, T.List[str]]) -> T.List[str]: ... } def __init__(self, exelist: T.List[str], env: Environment, - for_machine: mesonlib.MachineChoice, prefix_arg: T.Union[str, T.List[str]], + for_machine: mesonlib.MachineChoice, prefix_arg: T.Optional[LinkerOptionStyle], always_args: T.List[str], *, version: str = 'unknown version', direct: bool = True, machine: str = 'x86', rsp_syntax: RSPFileSyntax = RSPFileSyntax.MSVC): @@ -1516,7 +1514,7 @@ class MSVCDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker): def __init__(self, env: Environment, for_machine: mesonlib.MachineChoice, always_args: T.List[str], *, exelist: T.Optional[T.List[str]] = None, - prefix: T.Union[str, T.List[str]] = '', + prefix: T.Optional[LinkerOptionStyle] = None, machine: str = 'x86', version: str = 'unknown version', direct: bool = True, rsp_syntax: RSPFileSyntax = RSPFileSyntax.MSVC): super().__init__(exelist or ['link.exe'], env, for_machine, @@ -1545,7 +1543,7 @@ class ClangClDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker): def __init__(self, env: Environment, for_machine: mesonlib.MachineChoice, always_args: T.List[str], *, exelist: T.Optional[T.List[str]] = None, - prefix: T.Union[str, T.List[str]] = '', + prefix: T.Optional[LinkerOptionStyle] = None, machine: str = 'x86', version: str = 'unknown version', direct: bool = True, rsp_syntax: RSPFileSyntax = RSPFileSyntax.MSVC): super().__init__(exelist or ['lld-link.exe'], env, for_machine, @@ -1579,10 +1577,10 @@ class XilinkDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker): def __init__(self, env: Environment, for_machine: mesonlib.MachineChoice, always_args: T.List[str], *, exelist: T.Optional[T.List[str]] = None, - prefix: T.Union[str, T.List[str]] = '', + prefix: T.Optional[LinkerOptionStyle] = None, machine: str = 'x86', version: str = 'unknown version', direct: bool = True): - super().__init__(['xilink.exe'], env, for_machine, '', always_args, version=version) + super().__init__(['xilink.exe'], env, for_machine, None, always_args, version=version) def get_win_subsystem_args(self, value: str) -> T.List[str]: return self._apply_prefix([f'/SUBSYSTEM:{value.upper()}']) @@ -1650,13 +1648,13 @@ def build_rpath_args(self, build_dir: str, from_dir: str, target: BuildTarget, paths = padding else: paths = paths + ':' + padding - return (self._apply_prefix(f'-rpath,{paths}'), rpath_dirs_to_remove) + return (self._apply_prefix(['-rpath', paths]), rpath_dirs_to_remove) def get_soname_args(self, prefix: str, shlib_name: str, suffix: str, soversion: str, darwin_versions: T.Tuple[str, str] ) -> T.List[str]: sostr = '' if soversion is None else '.' + soversion - return self._apply_prefix(f'-soname,{prefix}{shlib_name}.{suffix}{sostr}') + return self._apply_prefix(['-soname', f'{prefix}{shlib_name}.{suffix}{sostr}']) class AIXDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker): @@ -1746,7 +1744,7 @@ def __init__(self, exelist: T.List[str], env: Environment, for_machine: mesonlib *, version: str = 'unknown version'): # Use optlink instead of link so we don't interfere with other link.exe # implementations. - super().__init__(exelist, env, for_machine, '', [], version=version) + super().__init__(exelist, env, for_machine, None, [], version=version) def get_allow_undefined_args(self) -> T.List[str]: return [] @@ -1798,7 +1796,7 @@ def get_lib_prefix(self) -> str: # nvcc fatal : Don't know what to do with 'subprojects/foo/libbar.so.0.1.2' # from ..compilers.cuda import CudaCompiler - return CudaCompiler.LINKER_PREFIX + return CudaCompiler.LINKER_PREFIX.prefix def fatal_warnings(self) -> T.List[str]: return ['--warning-as-error'] @@ -1816,7 +1814,7 @@ class MetrowerksLinker(DynamicLinker): def __init__(self, exelist: T.List[str], env: Environment, for_machine: mesonlib.MachineChoice, *, version: str = 'unknown version'): - super().__init__(exelist, env, for_machine, '', [], + super().__init__(exelist, env, for_machine, None, [], version=version) def fatal_warnings(self) -> T.List[str]: @@ -1868,7 +1866,7 @@ class TaskingLinker(DynamicLinker): def __init__(self, exelist: T.List[str], env: Environment, for_machine: mesonlib.MachineChoice, *, version: str = 'unknown version'): - super().__init__(exelist, env, for_machine, '', [], + super().__init__(exelist, env, for_machine, None, [], version=version) def get_accepts_rsp(self) -> bool: diff --git a/unittests/internaltests.py b/unittests/internaltests.py index c5ea3b06f992..af71cbf54af2 100644 --- a/unittests/internaltests.py +++ b/unittests/internaltests.py @@ -28,6 +28,7 @@ import mesonbuild.scripts.env2mfile from mesonbuild import coredata from mesonbuild.compilers.c import ClangCCompiler, GnuCCompiler +from mesonbuild.compilers.compilers import ManyInOneLinkerOptionStyle from mesonbuild.compilers.cpp import VisualStudioCPPCompiler from mesonbuild.compilers.d import DmdDCompiler from mesonbuild.compilers.detect import detect_c_compiler @@ -323,7 +324,7 @@ def test_clangcl_unix_args_to_native(self): def test_compiler_args_class_gnuld(self): ## Test --start/end-group env = get_fake_env() - linker = linkers.GnuBFDDynamicLinker([], env, MachineChoice.HOST, '-Wl,', []) + linker = linkers.GnuBFDDynamicLinker([], env, MachineChoice.HOST, ManyInOneLinkerOptionStyle('-Wl,', ','), []) gcc = GnuCCompiler([], [], 'fake', MachineChoice.HOST, env, linker=linker) ## Ensure that the fake compiler is never called by overriding the relevant function gcc.get_default_include_dirs = lambda: ['/usr/include', '/usr/share/include', '/usr/local/include'] @@ -353,7 +354,7 @@ def test_compiler_args_class_gnuld(self): def test_compiler_args_remove_system(self): ## Test --start/end-group env = get_fake_env() - linker = linkers.GnuBFDDynamicLinker([], env, MachineChoice.HOST, '-Wl,', []) + linker = linkers.GnuBFDDynamicLinker([], env, MachineChoice.HOST, ManyInOneLinkerOptionStyle('-Wl,', ','), []) gcc = GnuCCompiler([], [], 'fake', MachineChoice.HOST, env, linker=linker) ## Ensure that the fake compiler is never called by overriding the relevant function gcc.get_default_include_dirs = lambda: ['/usr/include', '/usr/share/include', '/usr/local/include'] From 5fb135644e811db80fd3092c6b7fb8ec3b989d5b Mon Sep 17 00:00:00 2001 From: Katalin Rebhan Date: Sat, 14 Feb 2026 22:55:08 +0100 Subject: [PATCH 103/681] Add fallback for GCC when linker arguments can't be wrapped with -Wl --- mesonbuild/compilers/mixins/gnu.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mesonbuild/compilers/mixins/gnu.py b/mesonbuild/compilers/mixins/gnu.py index 7cb9d9f500be..788769db8f49 100644 --- a/mesonbuild/compilers/mixins/gnu.py +++ b/mesonbuild/compilers/mixins/gnu.py @@ -17,7 +17,7 @@ from ... import mesonlib from ... import mlog from ...options import OptionKey, UserStdOption -from mesonbuild.compilers.compilers import CompileCheckMode, ManyInOneLinkerOptionStyle +from mesonbuild.compilers.compilers import CompileCheckMode, ManyInOneLinkerOptionStyle, PrefixArgumentLinkerOptionStyle if T.TYPE_CHECKING: from ..._typing import ImmutableListProtocol @@ -356,7 +356,8 @@ class GnuLikeCompiler(Compiler, metaclass=mesonlib.SimpleABC): that the actual concrete subclass define their own implementation. """ - LINKER_PREFIX = ManyInOneLinkerOptionStyle('-Wl,', ',') + LINKER_PREFIX = ManyInOneLinkerOptionStyle('-Wl,', ',', + fallback=PrefixArgumentLinkerOptionStyle('-Xlinker')) def __init__(self) -> None: self.base_options = { From 48bb41b125db469953820af66ac9e7b20824063d Mon Sep 17 00:00:00 2001 From: Katalin Rebhan Date: Wed, 11 Mar 2026 00:02:14 +0100 Subject: [PATCH 104/681] Rename LINKER_PREFIX to LINKER_OPTION_STYLE --- mesonbuild/compilers/compilers.py | 2 +- mesonbuild/compilers/cuda.py | 2 +- mesonbuild/compilers/d.py | 10 +++--- mesonbuild/compilers/detect.py | 24 +++++++------- mesonbuild/compilers/fortran.py | 4 +-- mesonbuild/compilers/mixins/gnu.py | 4 +-- mesonbuild/compilers/mixins/tasking.py | 2 +- mesonbuild/compilers/rust.py | 2 +- mesonbuild/compilers/swift.py | 2 +- mesonbuild/linkers/detect.py | 46 +++++++++++++------------- mesonbuild/linkers/linkers.py | 2 +- 11 files changed, 50 insertions(+), 50 deletions(-) diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 2bc2ec25d092..f31e4a108240 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -545,7 +545,7 @@ class Compiler(HoldableObject, metaclass=SimpleABC): # manually searched. internal_libs: T.List[str] = [] - LINKER_PREFIX: T.Optional[LinkerOptionStyle] = None + LINKER_OPTION_STYLE: T.Optional[LinkerOptionStyle] = None # If the compiler is used to fire a separate linking step, environment # variables like CFLAGS have to be passed to the linking step as well. diff --git a/mesonbuild/compilers/cuda.py b/mesonbuild/compilers/cuda.py index 8b4428bfa550..3c8687669be1 100644 --- a/mesonbuild/compilers/cuda.py +++ b/mesonbuild/compilers/cuda.py @@ -45,7 +45,7 @@ class Phase(enum.Enum): class CudaCompiler(Compiler): - LINKER_PREFIX = SimplePrefixLinkerOptionStyle('-Xlinker=') + LINKER_OPTION_STYLE = SimplePrefixLinkerOptionStyle('-Xlinker=') language = 'cuda' # NVCC flags taking no arguments. diff --git a/mesonbuild/compilers/d.py b/mesonbuild/compilers/d.py index 002db7912e9c..367355828738 100644 --- a/mesonbuild/compilers/d.py +++ b/mesonbuild/compilers/d.py @@ -109,7 +109,7 @@ def __init__(self, dmd_frontend_version: T.Optional[str]): def _get_target_arch_args(self) -> T.List[str]: ... - LINKER_PREFIX = SimplePrefixLinkerOptionStyle('-L=') + LINKER_OPTION_STYLE = SimplePrefixLinkerOptionStyle('-L=') def get_output_args(self, outputname: str) -> T.List[str]: return ['-of=' + outputname] @@ -355,8 +355,8 @@ def get_soname_args(self, prefix: str, shlib_name: str, suffix: str, soversion: darwin_versions: T.Tuple[str, str]) -> T.List[str]: sargs = super().get_soname_args(prefix, shlib_name, suffix, soversion, darwin_versions) - if not all(arg.startswith(self.LINKER_PREFIX.prefix) for arg in sargs): - raise MesonBugException(f'Not all soname arguments for the D compiler start with {repr(self.LINKER_PREFIX.prefix)}: {sargs}') + if not all(arg.startswith(self.LINKER_OPTION_STYLE.prefix) for arg in sargs): + raise MesonBugException(f'Not all soname arguments for the D compiler start with {repr(self.LINKER_OPTION_STYLE.prefix)}: {sargs}') return sargs @@ -569,8 +569,8 @@ def has_header(self, hname: str, prefix: str, *, class GnuDCompiler(GnuCompiler, DCompiler): - # we mostly want DCompiler, but that gives us the Compiler.LINKER_PREFIX instead - LINKER_PREFIX = GnuCompiler.LINKER_PREFIX + # we mostly want DCompiler, but that gives us the Compiler.LINKER_OPTION_STYLE instead + LINKER_OPTION_STYLE = GnuCompiler.LINKER_OPTION_STYLE id = 'gcc' def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, diff --git a/mesonbuild/compilers/detect.py b/mesonbuild/compilers/detect.py index dabe6f6df0d6..040a16cf921a 100644 --- a/mesonbuild/compilers/detect.py +++ b/mesonbuild/compilers/detect.py @@ -385,11 +385,11 @@ def sanitize(p: T.Optional[str]) -> T.Optional[str]: # linker. It'll exit with an error code, but still print the # linker version. with tempfile.NamedTemporaryFile(suffix='.c') as f: - cmd = [*compiler, *cls.LINKER_PREFIX.wrap(['--version']), f.name] + cmd = [*compiler, *cls.LINKER_OPTION_STYLE.wrap(['--version']), f.name] _, o, _ = Popen_safe(cmd) linker = linkers.WASMDynamicLinker( - compiler, env, for_machine, cls.LINKER_PREFIX, + compiler, env, for_machine, cls.LINKER_OPTION_STYLE, [], version=search_version(o)) return cls( ccache, compiler, version, for_machine, env, @@ -541,14 +541,14 @@ def sanitize(p: T.Optional[str]) -> T.Optional[str]: if 'PGI Compilers' in out: cls = c.PGICCompiler if lang == 'c' else cpp.PGICPPCompiler env.add_lang_args(cls.language, cls, for_machine) - linker = linkers.PGIDynamicLinker(compiler, env, for_machine, cls.LINKER_PREFIX, [], version=version) + linker = linkers.PGIDynamicLinker(compiler, env, for_machine, cls.LINKER_OPTION_STYLE, [], version=version) return cls( ccache, compiler, version, for_machine, env, linker=linker) if 'NVIDIA Compilers and Tools' in out: cls = c.NvidiaHPC_CCompiler if lang == 'c' else cpp.NvidiaHPC_CPPCompiler env.add_lang_args(cls.language, cls, for_machine) - linker = linkers.NvidiaHPC_DynamicLinker(compiler, env, for_machine, cls.LINKER_PREFIX, [], version=version) + linker = linkers.NvidiaHPC_DynamicLinker(compiler, env, for_machine, cls.LINKER_OPTION_STYLE, [], version=version) return cls( ccache, compiler, version, for_machine, env, linker=linker) @@ -593,7 +593,7 @@ def sanitize(p: T.Optional[str]) -> T.Optional[str]: cls.gcc_version = _get_gnu_version_from_defines(defines) env.add_lang_args(cls.language, cls, for_machine) - linker = linkers.Xc32DynamicLinker(compiler, env, for_machine, cls.LINKER_PREFIX, [], version=version) + linker = linkers.Xc32DynamicLinker(compiler, env, for_machine, cls.LINKER_OPTION_STYLE, [], version=version) return cls( ccache, compiler, version, for_machine, @@ -718,7 +718,7 @@ def detect_cuda_compiler(env: 'Environment', for_machine: MachineChoice) -> Comp val = env.options[key] assert isinstance(val, list) env.coredata.optstore.set_option(key, cls.to_host_flags_base(val, Phase.LINKER)) - linker = CudaLinker(compiler, env, for_machine, CudaCompiler.LINKER_PREFIX, [], version=CudaLinker.parse_version()) + linker = CudaLinker(compiler, env, for_machine, CudaCompiler.LINKER_OPTION_STYLE, [], version=CudaLinker.parse_version()) return cls(ccache, compiler, version, for_machine, cpp_compiler, env, linker=linker) _handle_exceptions(popen_exceptions, compilers) @@ -843,7 +843,7 @@ def detect_fortran_compiler(env: 'Environment', for_machine: MachineChoice) -> C cls = fortran.PGIFortranCompiler env.add_lang_args(cls.language, cls, for_machine) linker = linkers.PGIDynamicLinker(compiler, env, for_machine, - cls.LINKER_PREFIX, [], version=version) + cls.LINKER_OPTION_STYLE, [], version=version) return cls( compiler, version, for_machine, env, full_version=full_version, linker=linker) @@ -852,7 +852,7 @@ def detect_fortran_compiler(env: 'Environment', for_machine: MachineChoice) -> C cls = fortran.NvidiaHPC_FortranCompiler env.add_lang_args(cls.language, cls, for_machine) linker = linkers.PGIDynamicLinker(compiler, env, for_machine, - cls.LINKER_PREFIX, [], version=version) + cls.LINKER_OPTION_STYLE, [], version=version) return cls( compiler, version, for_machine, env, full_version=full_version, linker=linker) @@ -903,7 +903,7 @@ def _get_linker_try_windows(cls: T.Type['Compiler']) -> T.Optional['DynamicLinke cls = fortran.NAGFortranCompiler env.add_lang_args(cls.language, cls, for_machine) linker = linkers.NAGDynamicLinker( - compiler, env, for_machine, cls.LINKER_PREFIX, [], + compiler, env, for_machine, cls.LINKER_OPTION_STYLE, [], version=version) return cls( compiler, version, for_machine, env, @@ -1163,13 +1163,13 @@ def detect_rust_compiler(env: 'Environment', for_machine: MachineChoice) -> Rust exelist=exelist, version=cc.linker.version, direct=True, machine=cc.linker.machine) else: - linker = type(cc.linker)(exelist, env, for_machine, cc.LINKER_PREFIX, + linker = type(cc.linker)(exelist, env, for_machine, cc.LINKER_OPTION_STYLE, always_args=[], system=cc.linker.system, version=cc.linker.version) exelist += cc.linker.get_always_args() elif 'link' in override[0]: linker = guess_win_linker(env, - override, cls, version, for_machine, use_linker_prefix=False) + override, cls, version, for_machine, wrap_linker_args=False) # rustc takes linker arguments without a prefix, and # inserts the correct prefix itself. assert isinstance(linker, linkers.VisualStudioLikeLinkerMixin) @@ -1254,7 +1254,7 @@ def detect_d_compiler(env: 'Environment', for_machine: MachineChoice) -> Compile linker = guess_win_linker(env, exelist, cls, full_version, for_machine, - use_linker_prefix=True, invoked_directly=False, + wrap_linker_args=True, invoked_directly=False, extra_args=extra_args) else: # LDC writes an object file to the current working directory. diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py index eef4f298ed0b..c099facb81ba 100644 --- a/mesonbuild/compilers/fortran.py +++ b/mesonbuild/compilers/fortran.py @@ -347,7 +347,7 @@ def get_module_outdir_args(self, path: str) -> T.List[str]: class G95FortranCompiler(FortranCompiler): - LINKER_PREFIX = ManyInOneLinkerOptionStyle('-Wl,', ',') + LINKER_OPTION_STYLE = ManyInOneLinkerOptionStyle('-Wl,', ',') id = 'g95' def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, @@ -368,7 +368,7 @@ def get_module_outdir_args(self, path: str) -> T.List[str]: class SunFortranCompiler(FortranCompiler): - LINKER_PREFIX = ManyInOneLinkerOptionStyle('-Wl,', ',') + LINKER_OPTION_STYLE = ManyInOneLinkerOptionStyle('-Wl,', ',') id = 'sun' def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]: diff --git a/mesonbuild/compilers/mixins/gnu.py b/mesonbuild/compilers/mixins/gnu.py index 788769db8f49..25208796ea30 100644 --- a/mesonbuild/compilers/mixins/gnu.py +++ b/mesonbuild/compilers/mixins/gnu.py @@ -356,8 +356,8 @@ class GnuLikeCompiler(Compiler, metaclass=mesonlib.SimpleABC): that the actual concrete subclass define their own implementation. """ - LINKER_PREFIX = ManyInOneLinkerOptionStyle('-Wl,', ',', - fallback=PrefixArgumentLinkerOptionStyle('-Xlinker')) + LINKER_OPTION_STYLE = ManyInOneLinkerOptionStyle('-Wl,', ',', + fallback=PrefixArgumentLinkerOptionStyle('-Xlinker')) def __init__(self) -> None: self.base_options = { diff --git a/mesonbuild/compilers/mixins/tasking.py b/mesonbuild/compilers/mixins/tasking.py index c2974929a41d..5a251984e1de 100644 --- a/mesonbuild/compilers/mixins/tasking.py +++ b/mesonbuild/compilers/mixins/tasking.py @@ -49,7 +49,7 @@ class TaskingCompiler(Compiler): Functionality that is common to all TASKING family compilers. ''' - LINKER_PREFIX = SimplePrefixLinkerOptionStyle('-Wl') + LINKER_OPTION_STYLE = SimplePrefixLinkerOptionStyle('-Wl') def __init__(self) -> None: if not self.is_cross: diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index 20b3cff1f37f..dcfdfbb092c4 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -114,7 +114,7 @@ class RustSystemDependency(InternalDependency): class RustCompiler(Compiler): - # rustc doesn't invoke the compiler itself, it doesn't need a LINKER_PREFIX + # rustc doesn't invoke the compiler itself, it doesn't need a LINKER_OPTION_STYLE language = 'rust' id = 'rustc' diff --git a/mesonbuild/compilers/swift.py b/mesonbuild/compilers/swift.py index a2e0e5056cfc..105258027e30 100644 --- a/mesonbuild/compilers/swift.py +++ b/mesonbuild/compilers/swift.py @@ -32,7 +32,7 @@ class SwiftCompiler(Compiler): - LINKER_PREFIX = PrefixArgumentLinkerOptionStyle('-Xlinker') + LINKER_OPTION_STYLE = PrefixArgumentLinkerOptionStyle('-Xlinker') language = 'swift' id = 'llvm' diff --git a/mesonbuild/linkers/detect.py b/mesonbuild/linkers/detect.py index 69188bde109b..c19f66cb2fe0 100644 --- a/mesonbuild/linkers/detect.py +++ b/mesonbuild/linkers/detect.py @@ -37,7 +37,7 @@ def __failed_to_detect_linker(compiler: T.List[str], args: T.List[str], stdout: def guess_win_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Type['Compiler'], comp_version: str, for_machine: MachineChoice, *, - use_linker_prefix: bool = True, invoked_directly: bool = True, + wrap_linker_args: bool = True, invoked_directly: bool = True, extra_args: T.Optional[T.List[str]] = None) -> 'DynamicLinker': from . import linkers env.add_lang_args(comp_class.language, comp_class, for_machine) @@ -48,10 +48,10 @@ def guess_win_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty rsp_syntax = RSPFileSyntax.GCC # Explicitly pass logo here so that we can get the version of link.exe - if not use_linker_prefix or comp_class.LINKER_PREFIX is None: + if not wrap_linker_args or comp_class.LINKER_OPTION_STYLE is None: check_args = ['/logo', '--version'] else: - check_args = comp_class.LINKER_PREFIX.wrap(['/logo', '--version']) + check_args = comp_class.LINKER_OPTION_STYLE.wrap(['/logo', '--version']) check_args += env.coredata.get_external_link_args(for_machine, comp_class.language) @@ -74,16 +74,16 @@ def guess_win_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty if 'LLD' in o.split('\n', maxsplit=1)[0]: if 'compatible with GNU linkers' in o: return linkers.LLVMDynamicLinker( - compiler, env, for_machine, comp_class.LINKER_PREFIX, + compiler, env, for_machine, comp_class.LINKER_OPTION_STYLE, override, version=search_version(o)) if not invoked_directly: return linkers.ClangClDynamicLinker( - env, for_machine, override, exelist=compiler, prefix=comp_class.LINKER_PREFIX, + env, for_machine, override, exelist=compiler, prefix=comp_class.LINKER_OPTION_STYLE, version=search_version(o), direct=False, machine=None, rsp_syntax=rsp_syntax) return linkers.ClangClDynamicLinker( env, for_machine, [], - prefix=comp_class.LINKER_PREFIX if use_linker_prefix else None, + prefix=comp_class.LINKER_OPTION_STYLE if wrap_linker_args else None, exelist=compiler, version=search_version(o), direct=invoked_directly, rsp_syntax=rsp_syntax) elif 'OPTLINK' in o: @@ -99,7 +99,7 @@ def guess_win_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty return linkers.MSVCDynamicLinker( env, for_machine, [], machine=target, exelist=compiler, - prefix=comp_class.LINKER_PREFIX if use_linker_prefix else None, + prefix=comp_class.LINKER_OPTION_STYLE if wrap_linker_args else None, version=search_version(out), direct=invoked_directly, rsp_syntax=rsp_syntax) elif 'GNU coreutils' in o: @@ -130,7 +130,7 @@ def guess_nix_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty system = env.machines[for_machine].system ldflags = env.coredata.get_external_link_args(for_machine, comp_class.language) extra_args += comp_class._unix_args_to_native(ldflags, env.machines[for_machine]) - check_args = comp_class.LINKER_PREFIX.wrap(['--version']) + extra_args + check_args = comp_class.LINKER_OPTION_STYLE.wrap(['--version']) + extra_args override: T.List[str] = [] value = env.lookup_binary_entry(for_machine, comp_class.language + '_ld') @@ -147,7 +147,7 @@ def guess_nix_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty v = search_version(o + e) linker: DynamicLinker if 'LLD' in o.split('\n', maxsplit=1)[0] or 'tiarmlnk' in e: - cmd = compiler + override + comp_class.LINKER_PREFIX.wrap(['-v']) + extra_args + cmd = compiler + override + comp_class.LINKER_OPTION_STYLE.wrap(['-v']) + extra_args _, newo, newerr = Popen_safe_logged(cmd, msg='Detecting LLD linker via') lld_cls: T.Type[DynamicLinker] @@ -157,13 +157,13 @@ def guess_nix_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty lld_cls = linkers.LLVMDynamicLinker linker = lld_cls( - compiler, env, for_machine, comp_class.LINKER_PREFIX, override, system=system, version=v) + compiler, env, for_machine, comp_class.LINKER_OPTION_STYLE, override, system=system, version=v) elif o.startswith("eld"): linker = linkers.ELDDynamicLinker( - compiler, env, for_machine, comp_class.LINKER_PREFIX, override, version=v) + compiler, env, for_machine, comp_class.LINKER_OPTION_STYLE, override, version=v) elif 'Snapdragon' in e and 'LLVM' in e: linker = linkers.QualcommLLVMDynamicLinker( - compiler, env, for_machine, comp_class.LINKER_PREFIX, override, version=v) + compiler, env, for_machine, comp_class.LINKER_OPTION_STYLE, override, version=v) elif e.startswith('lld-link: '): # The LLD MinGW frontend didn't respond to --version before version 9.0.0, # and produced an error message about failing to link (when no object @@ -181,7 +181,7 @@ def guess_nix_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty _, o, e = Popen_safe([linker_cmd, '--version']) v = search_version(o) - linker = linkers.LLVMDynamicLinker(compiler, env, for_machine, comp_class.LINKER_PREFIX, override, version=v) + linker = linkers.LLVMDynamicLinker(compiler, env, for_machine, comp_class.LINKER_OPTION_STYLE, override, version=v) elif 'GNU' in o or 'GNU' in e: gnu_cls: T.Type[GnuDynamicLinker] # this is always the only thing on stdout, except for swift @@ -192,7 +192,7 @@ def guess_nix_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty gnu_cls = linkers.MoldDynamicLinker else: gnu_cls = linkers.GnuBFDDynamicLinker - linker = gnu_cls(compiler, env, for_machine, comp_class.LINKER_PREFIX, override, version=v) + linker = gnu_cls(compiler, env, for_machine, comp_class.LINKER_OPTION_STYLE, override, version=v) elif 'Solaris' in e or 'Solaris' in o: for line in (o+e).split('\n'): if 'ld: Software Generation Utilities' in line: @@ -201,22 +201,22 @@ def guess_nix_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty else: v = 'unknown version' linker = linkers.SolarisDynamicLinker( - compiler, env, for_machine, comp_class.LINKER_PREFIX, override, + compiler, env, for_machine, comp_class.LINKER_OPTION_STYLE, override, version=v) elif 'ld: 0706-012 The -- flag is not recognized' in e: - _, _, e = Popen_safe(compiler + comp_class.LINKER_PREFIX.wrap(['-V']) + extra_args) + _, _, e = Popen_safe(compiler + comp_class.LINKER_OPTION_STYLE.wrap(['-V']) + extra_args) linker = linkers.AIXDynamicLinker( - compiler, env, for_machine, comp_class.LINKER_PREFIX, override, + compiler, env, for_machine, comp_class.LINKER_OPTION_STYLE, override, version=search_version(e)) elif o.startswith('zig ld'): linker = linkers.ZigCCDynamicLinker( - compiler, env, for_machine, comp_class.LINKER_PREFIX, override, version=v) + compiler, env, for_machine, comp_class.LINKER_OPTION_STYLE, override, version=v) # detect xtools first, bug #10805 elif 'xtools-' in o.split('\n', maxsplit=1)[0]: xtools = o.split(' ', maxsplit=1)[0] v = xtools.split('-', maxsplit=2)[1] linker = linkers.AppleDynamicLinker( - compiler, env, for_machine, comp_class.LINKER_PREFIX, override, + compiler, env, for_machine, comp_class.LINKER_OPTION_STYLE, override, system=system, version=v ) # detect linker on MacOS - must be after other platforms because the @@ -225,7 +225,7 @@ def guess_nix_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty # First might be apple clang, second is for real gcc, the third is icc. # Note that "ld: unknown option: " sometimes instead is "ld: unknown options:". elif e.endswith('(use -v to see invocation)\n') or 'macosx_version' in e or 'ld: unknown option' in e: - cmd = compiler + comp_class.LINKER_PREFIX.wrap(['-v']) + extra_args + cmd = compiler + comp_class.LINKER_OPTION_STYLE.wrap(['-v']) + extra_args _, newo, newerr = Popen_safe_logged(cmd, msg='Detecting Apple linker via') for line in newerr.split('\n'): @@ -235,16 +235,16 @@ def guess_nix_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty else: __failed_to_detect_linker(compiler, check_args, o, e) linker = linkers.AppleDynamicLinker( - compiler, env, for_machine, comp_class.LINKER_PREFIX, override, + compiler, env, for_machine, comp_class.LINKER_OPTION_STYLE, override, system=system, version=v ) elif 'ld.exe: unrecognized option' in e or 'ld: unrecognized option' in e: linker = linkers.OS2AoutDynamicLinker( - compiler, env, for_machine, comp_class.LINKER_PREFIX, override, + compiler, env, for_machine, comp_class.LINKER_OPTION_STYLE, override, version='none') elif 'emxomfld: invalid option' in e: linker = linkers.OS2OmfDynamicLinker( - compiler, env, for_machine, comp_class.LINKER_PREFIX, override, + compiler, env, for_machine, comp_class.LINKER_OPTION_STYLE, override, version='none') else: __failed_to_detect_linker(compiler, check_args, o, e) diff --git a/mesonbuild/linkers/linkers.py b/mesonbuild/linkers/linkers.py index bb2c0e546f5f..19c419c4a05c 100644 --- a/mesonbuild/linkers/linkers.py +++ b/mesonbuild/linkers/linkers.py @@ -1796,7 +1796,7 @@ def get_lib_prefix(self) -> str: # nvcc fatal : Don't know what to do with 'subprojects/foo/libbar.so.0.1.2' # from ..compilers.cuda import CudaCompiler - return CudaCompiler.LINKER_PREFIX.prefix + return CudaCompiler.LINKER_OPTION_STYLE.prefix def fatal_warnings(self) -> T.List[str]: return ['--warning-as-error'] From ea995d748c19d1a672a5e7bdc8e15d7a4c048dbc Mon Sep 17 00:00:00 2001 From: Sam James Date: Sun, 3 May 2026 10:16:01 +0100 Subject: [PATCH 105/681] unittests: add another test for mtest --exclude Test the 'main:name' case. --- unittests/allplatformstests.py | 1 + 1 file changed, 1 insertion(+) diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 4fdc9c04d6d1..1b140547b8c0 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -910,6 +910,7 @@ def test_suite_selection(self): self.assertFailedTestCount(4, self.mtest_command + ['--exclude', 'mainprj-failing_test', '--exclude', 'subprjfail:subprjfail-failing_test_no_suite']) self.assertFailedTestCount(5, self.mtest_command + ['--exclude', 'buggy']) + self.assertFailedTestCount(5, self.mtest_command + ['--exclude', 'mainprj:buggy']) self.assertFailedTestCount(0, self.mtest_command + ['--suite', ':success']) self.assertFailedTestCount(5, self.mtest_command + ['--suite', ':fail']) From 7b6c3a0f7aa19ab113a66f897b7ac4644a41dc19 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 24 Apr 2026 10:42:05 -0700 Subject: [PATCH 106/681] docs: Add a release snippet for Python 3.10 requirement --- docs/markdown/snippets/python-310-required.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 docs/markdown/snippets/python-310-required.md diff --git a/docs/markdown/snippets/python-310-required.md b/docs/markdown/snippets/python-310-required.md new file mode 100644 index 000000000000..31a1de69ce4d --- /dev/null +++ b/docs/markdown/snippets/python-310-required.md @@ -0,0 +1,8 @@ +## Support for Python 3.7, 3.8, and 3.9 dropped + +Meson 1.12 is the first version to require Python version 3.10 or greater. + +Support for older versions of Python is maintained with bug fixes only for +some LTS releases. See the [FAQ +entry](FAQ.md#Do-you-at-least-support-my-ancient-python-install) for more +information. From 867083c1a9c70293bc5af451704e5e6075115210 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 28 Jan 2026 11:06:39 -0800 Subject: [PATCH 107/681] 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 5a93e489589b..0b9a477e8134 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 @@ -3530,7 +3530,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 c6f44e397063675080f89cf1e16b67b0cb21b5ac Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 28 Jan 2026 11:14:27 -0800 Subject: [PATCH 108/681] interpreter: fix sources typing in `build_target` Also, delete `check_sources_exist`, which does nothing because the str -> File conversion is already doing this. --- mesonbuild/interpreter/interpreter.py | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 0b9a477e8134..52cdc83f0e93 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -3224,6 +3224,10 @@ def source_strings_to_files(self, sources: T.List[T.Union[mesonlib.FileOrString, @T.overload def source_strings_to_files(self, sources: T.List[kwtypes.CustomTargetInputs]) -> T.List['CustomTargetSources']: ... # 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 @@ -3513,26 +3517,24 @@ 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 # 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) + 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) @@ -3693,14 +3695,6 @@ def add_stdlib_info(self, target): if dep: target.add_deps([dep]) - def check_sources_exist(self, subdir, sources): - for s in sources: - if not isinstance(s, str): - continue # This means a generated source and they always exist. - fname = os.path.join(subdir, s) - if not os.path.isfile(fname): - raise InterpreterException(f'Tried to add non-existing source file {s}.') - def check_for_jar_sources(self, sources, targetclass): for s in sources: if isinstance(s, (str, mesonlib.File)) and compilers.is_java(s): From 7c2fb85e556354a91f7facf592df5e2f36d4c0fa Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 28 Jan 2026 11:25:53 -0800 Subject: [PATCH 109/681] 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 813e64f28a34..66ed810b64c6 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -393,8 +393,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 b6b8e1492e24fddd7b277fda85b47414d50e4fed Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 28 Jan 2026 11:43:15 -0800 Subject: [PATCH 110/681] build: correct override_options annotations Which should be `dict[str, ElementaryOptionValues]` --- mesonbuild/build.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 97925271543c..7c09c8cccf30 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -49,6 +49,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'] @@ -99,7 +100,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 @@ -776,7 +777,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 6abe8e488f7777c0e8b0e1ac0e3c442632151d01 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 28 Jan 2026 11:46:55 -0800 Subject: [PATCH 111/681] 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 7c09c8cccf30..417b733b4dac 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -115,7 +115,6 @@ class BuildTargetKeywordArguments(TypedDict, total=False): vala_vapi: T.Optional[str] install_vala_vapi: bool install_vala_vapi_dir: T.Optional[str] - win_subsystem: str _allow_no_sources: bool @@ -126,6 +125,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 2f181f8a1fef73362757ca1ba8fb8931f672856f Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 28 Jan 2026 12:42:09 -0800 Subject: [PATCH 112/681] 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 66ed810b64c6..66eaad3c2d69 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -353,7 +353,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 83992887fd710f5565650cdcfce9e6e044984491 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 28 Jan 2026 12:51:45 -0800 Subject: [PATCH 113/681] 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 417b733b4dac..ad2eaf7adb97 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -93,7 +93,7 @@ class BuildTargetKeywordArguments(TypedDict, total=False): language_args: T.DefaultDict[Language, T.List[str]] link_args: T.List[str] link_early_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 66eaad3c2d69..687a739656cb 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -359,7 +359,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 3a442492b1cd6429688b67dea536b22319a859da Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 28 Jan 2026 12:55:46 -0800 Subject: [PATCH 114/681] 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 ad2eaf7adb97..ac16aa5c25ed 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -81,6 +81,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 0ac1c94ee7381dcfecba8e9fce6784d9dc6d4933 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 28 Jan 2026 13:36:45 -0800 Subject: [PATCH 115/681] 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 ac16aa5c25ed..d2c1991dd152 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -144,6 +144,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]] = { @@ -3329,7 +3336,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 bffb3c4583b6daf4ade0e32fcc5823215525b85f Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 28 Jan 2026 15:17:18 -0800 Subject: [PATCH 116/681] 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 d2c1991dd152..7cc3d41ea81f 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -836,7 +836,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: T.Set[dependencies.Dependency] = set() self.name_prefix_set = False From 680545f7bd6d5a7d9daad77e462ada4d87b9ae6b Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 28 Jan 2026 15:20:34 -0800 Subject: [PATCH 117/681] backend/ninja: Add depend_files dependencies to BuildTargets Ironically, the VS backend already does this correctly. --- mesonbuild/backend/ninjabackend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 1b5a0162fadc..989ae7307af8 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -3254,7 +3254,7 @@ 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 = self.get_target_depend_files(target).copy() 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 0d243b309f2ce7451864af5fb590e2aed3d8050b Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 28 Jan 2026 13:41:37 -0800 Subject: [PATCH 118/681] 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 687a739656cb..7951a2f76c87 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -443,6 +443,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 c9f5036d48aec3f6b5e7592e60c1d06437dd5c8e Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 28 Jan 2026 15:59:03 -0800 Subject: [PATCH 119/681] 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 ef6c4cdbb7e4..d414e8ee8f06 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -1016,6 +1016,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 b7d12d2f02dccda6a4a5c6da34865beceeb7fc7e Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 28 Jan 2026 16:02:38 -0800 Subject: [PATCH 120/681] build|interpreter: Add build_subdir to the TypedDicts for kwargs --- mesonbuild/interpreter/kwargs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index 7951a2f76c87..45c2a482cb5b 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -350,6 +350,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 a5799d2880f66feeb5fd7635135ce0ac826f33b6 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 28 Jan 2026 15:14:50 -0800 Subject: [PATCH 121/681] build: Remove known kwargs checking We have the Interpreter layer which does DSL translation and the build layer has type annotations. --- mesonbuild/build.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 7cc3d41ea81f..54c3fc874055 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -896,7 +896,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() @@ -985,23 +984,6 @@ def validate_install(self) -> None: 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 in {'language_args', 'install_vala_header', 'install_vala_vapi', - 'install_vala_gir', 'install_vala_header_dir', - 'install_vala_vapi_dir', 'install_vala_gir_dir'}: - 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: T.List[ObjectTypes]) -> None: assert isinstance(objects, list) deprecated_non_objects = [] From 0ee4d653cdd3c7301390b93ac4f5b47a0cc0dc9c Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 6 Apr 2026 09:39:32 -0700 Subject: [PATCH 122/681] interpreter: Fix check_jar_sources There are two fixes, one is to check generated sources (which have always been accepted and ignored). The second is to fix the typing. I have made the type a union of list[str] | sequence[rest of tyepes] because this covers all of the uses, but minimal duplication. The use of `list[str]` here is to avoid the problems of `sequence[str]` in Python. --- mesonbuild/interpreter/interpreter.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 52cdc83f0e93..b4d19a4306e0 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -3695,12 +3695,15 @@ def add_stdlib_info(self, target): if dep: target.add_deps([dep]) - def check_for_jar_sources(self, sources, targetclass): + def check_for_jar_sources(self, sources: T.Union[T.List[str], T.Sequence[T.Union[mesonlib.File, build.BuildTargetTypes, build.GeneratedList, build.StructuredSources]]], + targetclass: T.Type[build.BuildTarget]) -> None: for s in sources: if isinstance(s, (str, mesonlib.File)) and compilers.is_java(s): raise InvalidArguments(f'Build target of type "{targetclass.typename}" cannot build java source: "{s}". Use "{build.Jar.typename}" instead.') elif isinstance(s, build.StructuredSources): self.check_for_jar_sources(s.as_list(), targetclass) + elif isinstance(s, (build.GeneratedList, build.CustomTarget, build.CustomTargetIndex)): + self.check_for_jar_sources(s.get_outputs(), targetclass) def is_subproject(self) -> bool: return self.subproject != '' From 34d127d26731c78d02a9840275d4ae87f32504a8 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 28 Jan 2026 13:33:22 -0800 Subject: [PATCH 123/681] 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 | 386 ++++++++++++++++++-------- mesonbuild/interpreter/kwargs.py | 15 +- 2 files changed, 277 insertions(+), 124 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index b4d19a4306e0..6e43db876c62 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1934,7 +1934,7 @@ def func_jar(self, node: mparser.BaseNode, @typed_kwargs('build_target', *BUILD_TARGET_KWS) def func_build_target(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargsType], - kwargs: kwtypes.BuildTarget + kwargs: kwtypes.BuildTargetFunc, ) -> T.Union[build.Executable, build.StaticLibrary, build.SharedLibrary, build.SharedModule, build.BothLibraries, build.Jar]: target_type = kwargs['target_type'] @@ -3218,6 +3218,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 @@ -3413,7 +3416,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 @@ -3421,14 +3425,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']], @@ -3474,6 +3478,251 @@ def _exe_to_shlib_kwargs(self, kwargs: kwtypes.Executable) -> kwtypes.SharedLibr nkwargs['rust_crate_type'] = 'cdylib' return nkwargs + 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 exist + """ + # 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_early_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))) + + 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) + + # Rewrite `install_dir : [main, vala_header, vala_vapi, vala_gir]` + # Into split arguments + vala_keys = ('install_vala_header', 'install_vala_vapi', 'install_vala_gir') + if len(kwargs['install_dir']) > 1: + if any(kwargs[k] is not None for k in vala_keys): + raise InvalidArguments('Passing install_vala_* and more than one argument to install_dir are mutually exclusive') + + install_dirs = kwargs['install_dir'].copy() + install_dir = install_dirs.pop(0) + + for key in vala_keys: + action = install_dirs.pop(0) if install_dirs else False + if isinstance(action, bool): + final[key] = action + else: + final[key] = True + final[f'{key}_dir'] = action + final['install_dir'] = [install_dir] + + # This was previously allowed, and tested, so we need to allow it to keep working. + if final['vala_gir'] is None and final['install_vala_gir']: + final['install_vala_gir'] = False + else: + for key in vala_keys: + action = kwargs[key] + if action is None: + action = False + if isinstance(action, bool): + final[key] = action + else: + final[key] = True + final[f'{key}_dir'] = action + final['install_dir'] = [kwargs['install_dir'][0] if kwargs['install_dir'] else True] + + 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' if kwargs['gui_app'] else 'console' + 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 + final['export_dynamic'] = kwargs['export_dynamic'] + + if isinstance(kwargs['implib'], bool): + final['implib'] = None + else: + final['implib'] = kwargs['implib'] + + # 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', '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.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: ... @@ -3513,6 +3762,8 @@ def build_target(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargs if targetclass is build.Executable and kwargs.get('android_exe_type') == 'application': m = self.environment.machines[for_machine] if m.is_android(): + # Mypy can't figure this out for some reason + kwargs = T.cast('kwtypes.Executable', kwargs) return self.build_target(node, args, self._exe_to_shlib_kwargs(kwargs), build.SharedLibrary) # Because who owns this isn't clear @@ -3533,50 +3784,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'. - extra_excludes = {'language_args', 'install_vala_header', 'install_vala_vapi', 'install_vala_gir'} - kwargs = {k: v for k, v in kwargs.items() if k in targetclass.known_kwargs | extra_excludes} srcs: T.List['SourceInputs'] = [] struct: T.Optional[build.StructuredSources] = build.StructuredSources() @@ -3610,76 +3817,21 @@ 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) if targetclass is not build.Jar: - kwargs = T.cast('kwtypes.Executable | kwtypes.StaticLibrary | kwtypes.SharedLibrary | kwtypes.SharedModule', kwargs) - # The order of these keys matters! - vala_keys = ('install_vala_header', 'install_vala_vapi', 'install_vala_gir') - - # Rewrite `install_dir : [main, vala_header, vala_vapi, vala_gir]` - # Into split arguments - if len(kwargs['install_dir']) > 1: - if any(kwargs[k] is not None for k in vala_keys): - raise InvalidArguments('Passing install_vala_* and more than one argument to install_dir are mutually exclusive') - - install_dir = kwargs['install_dir'].pop(0) - - for key in vala_keys: - action = kwargs['install_dir'].pop(0) if kwargs['install_dir'] else False - if isinstance(action, bool): - kwargs[key] = action - else: - kwargs[key] = True - kwargs[f'{key}_dir'] = action - kwargs['install_dir'] = [install_dir] - - # This was previously allowed, and tested, so we need to allow it to keep working. - if kwargs['vala_gir'] is None and kwargs['install_vala_gir']: - kwargs['install_vala_gir'] = False - elif kwargs['install_dir']: - for key in vala_keys: - action = kwargs[key] - if action is None: - action = False - if isinstance(action, bool): - kwargs[key] = action - else: - kwargs[key] = True - kwargs[f'{key}_dir'] = action - else: - kwargs['install_dir'] = [True] - - if kwargs['vala_gir'] is None and kwargs['install_vala_gir']: - raise InvalidArguments('Cannot set `install_vala_gir` without `vala_gir`') + self.check_for_jar_sources(sources, targetclass) 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) if targetclass is build.SharedLibrary: diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index 45c2a482cb5b..21e06bb13c75 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -376,7 +376,7 @@ class _BaseBuildTarget(TypedDict): vala_gir: T.Optional[str] -class _BuildTarget(_BaseBuildTarget): +class BuildTarget(_BaseBuildTarget): """Arguments shared by non-JAR functions""" @@ -394,6 +394,7 @@ class _BuildTarget(_BaseBuildTarget): swift_module_name: str sources: SourcesVarargsType link_args: T.List[str] + link_early_args: 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] @@ -417,7 +418,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] @@ -434,7 +435,7 @@ class _StaticLibMixin(TypedDict): pic: T.Optional[bool] -class StaticLibrary(_BuildTarget, _StaticLibMixin, _LibraryMixin): +class StaticLibrary(BuildTarget, _StaticLibMixin, _LibraryMixin): pass @@ -447,16 +448,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""" @@ -490,7 +491,7 @@ class Library(_BuildTarget, _SharedLibMixin, _StaticLibMixin, _LibraryMixin): masm_shared_args: NotRequired[T.List[str]] -class BuildTarget(Library): +class BuildTargetFunc(Library): target_type: Literal['executable', 'shared_library', 'static_library', 'shared_module', 'both_libraries', 'library', 'jar'] From 9d9951998a224dbf477cf5b55815a8aee0af885c Mon Sep 17 00:00:00 2001 From: Rachel Date: Sun, 3 May 2026 13:24:22 -0700 Subject: [PATCH 124/681] build: refactor add_preserved_path_segment into get_preserved_path_segment --- mesonbuild/build.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 54c3fc874055..52569e37e7af 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2079,7 +2079,7 @@ def get_base_outnames(self, inname: str) -> T.List[str]: bases = [x.replace('@BASENAME@', basename).replace('@PLAINNAME@', plainname) for x in self.outputs] return bases - def get_dep_outname(self, inname: str) -> T.List[str]: + def get_dep_outname(self, inname: str) -> str: if self.depfile is None: raise InvalidArguments('Tried to get dep name for rule that does not have dependency file defined.') plainname = os.path.basename(inname) @@ -2125,7 +2125,7 @@ def process_files(self, files: T.Iterable[T.Union[str, File, GeneratedTypes]], if not is_parent_path(preserve_path_from, abs_f): raise InvalidArguments('generator.process: When using preserve_path_from, all input files must be in a subdirectory of the given dir.') f = FileMaybeInTargetPrivateDir(f) - output.add_file(f, self.environment) + output.add_file(f) return output @@ -2170,21 +2170,18 @@ def __post_init__(self) -> None: # know the absolute path of self.depend_files.append(File.from_absolute_file(path)) - def add_preserved_path_segment(self, infile: FileMaybeInTargetPrivateDir, outfiles: T.List[str], environment: Environment) -> T.List[str]: - result: T.List[str] = [] - in_abs = infile.absolute_path(environment.source_dir, environment.build_dir) + def get_preserved_path_segment(self, infile: FileMaybeInTargetPrivateDir) -> str: + in_abs = infile.absolute_path(self.generator.environment.source_dir, self.generator.environment.build_dir) assert os.path.isabs(self.preserve_path_from) rel = os.path.relpath(in_abs, self.preserve_path_from) - path_segment = os.path.dirname(rel) - for of in outfiles: - result.append(os.path.join(path_segment, of)) - return result + return os.path.dirname(rel) - def add_file(self, newfile: FileMaybeInTargetPrivateDir, environment: Environment) -> None: + def add_file(self, newfile: FileMaybeInTargetPrivateDir) -> None: self.infilelist.append(newfile) outfiles = self.generator.get_base_outnames(newfile.fname) if self.preserve_path_from: - outfiles = self.add_preserved_path_segment(newfile, outfiles, environment) + path_segment = self.get_preserved_path_segment(newfile) + outfiles = [os.path.join(path_segment, of) for of in outfiles] self.outfilelist += outfiles self.outmap[newfile] = outfiles From 3db3531a4cb24ac8b28ffc0c670697228d81f518 Mon Sep 17 00:00:00 2001 From: Rachel Date: Sat, 2 May 2026 14:04:42 -0700 Subject: [PATCH 125/681] ninja: Respect preserve_path_from for depfiles --- mesonbuild/backend/ninjabackend.py | 3 +++ test cases/common/168 preserve gendir/meson.build | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 989ae7307af8..b6ab6b870fbb 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -2839,6 +2839,9 @@ def generate_genlist_for_target(self, genlist: build.GeneratedList, target: buil else: rulename = 'CUSTOM_COMMAND_DEP' depfilename = generator.get_dep_outname(infilename) + if genlist.preserve_path_from: + path_segment = genlist.get_preserved_path_segment(curfile) + depfilename = os.path.join(path_segment, depfilename) depfile = os.path.join(self.get_target_private_dir(target), depfilename) args = [x.replace('@DEPFILE@', depfile) for x in base_args] args = [x.replace("@INPUT@", infilename).replace('@OUTPUT@', sole_output) diff --git a/test cases/common/168 preserve gendir/meson.build b/test cases/common/168 preserve gendir/meson.build index d543b6019d6c..90e729ec9e6a 100644 --- a/test cases/common/168 preserve gendir/meson.build +++ b/test cases/common/168 preserve gendir/meson.build @@ -4,7 +4,8 @@ gprog = find_program('genprog.py') gen = generator(gprog, \ output : ['@BASENAME@.c', '@BASENAME@.h'], - arguments : ['--searchdir=@CURRENT_SOURCE_DIR@', '--outdir=@BUILD_DIR@', '@INPUT@']) + arguments : ['--searchdir=@CURRENT_SOURCE_DIR@', '--outdir=@BUILD_DIR@', '@INPUT@'], + depfile : '@BASENAME@.d') generated = gen.process('base.inp', 'com/mesonbuild/subbie.inp', preserve_path_from : meson.current_source_dir()) From d8ab3fb69631f85e7d6b87c3120621fbb63990e3 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 6 May 2026 08:50:29 +0200 Subject: [PATCH 126/681] interpreter: do not apply CARGO_SUBDIR if subproject has a meson.build Commit 57bf0c857 ("cargo: allow overriding Meson's Cargo interpreter") allow using Cargo subprojects to share the parent's Cargo.lock file, even for subprojects that have their own meson.build file. However, in that case subdir() does not represent delayed generation of the meson.build rules for a workspace member---that would not even work if the subdir() is not for a crate but, say, for documentation. Drop CARGO_SUBDIR from the interpreter relaxations if _do_subproject_cargo() did not generate the subproject's AST. Fixes: 57bf0c857 ("cargo: allow overriding Meson's Cargo interpreter") Signed-off-by: Paolo Bonzini --- mesonbuild/interpreter/interpreter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 6e43db876c62..ea3c737a9248 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1068,7 +1068,7 @@ def _do_subproject_cargo(self, subp_name: SubProject, subdir: str, return self._do_subproject_meson( subp_name, subdir, default_options, kwargs, ast, - relaxations={InterpreterRuleRelaxation.CARGO_SUBDIR}, + relaxations={InterpreterRuleRelaxation.CARGO_SUBDIR} if ast is not None else None, cargo=cargo_int) @typed_pos_args('get_option', str) From 2aaebe3bb26ac2667a64d620294e5181a6fb4594 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Sun, 3 May 2026 11:01:22 +0200 Subject: [PATCH 127/681] tests: skip importstd with gcc on Windows It's failing since we updated from gcc 15.2 to 16.1 in MSYS2. The test was checking for an unreleased 15.3 so the test was never run on Windows to begin with. Just skip it with gcc on Windows. fyi, it fails with: undefined reference to `std::__open_terminal(_iobuf*)' undefined reference to `std::__write_to_terminal(void*, std::span)' --- test cases/common/288 importstd/meson.build | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test cases/common/288 importstd/meson.build b/test cases/common/288 importstd/meson.build index 06e0504a3cc1..fcf96ec8e339 100644 --- a/test cases/common/288 importstd/meson.build +++ b/test cases/common/288 importstd/meson.build @@ -5,8 +5,11 @@ cpp = meson.get_compiler('cpp') cpp_id = cpp.get_id() cpp_version = cpp.version() +is_windows = host_machine.system() == 'windows' -if cpp_id == 'gcc' and cpp_version.version_compare('>=15.3') +# gcc 16 on Windows fails with "undefined reference to 'std::__open_terminal(_iobuf*)'" +# clang 22 + mingw-w64 on Windows fails with "fatal error: module 'std' not found" +if not is_windows and cpp_id == 'gcc' and cpp_version.version_compare('>=15.3') # Versions between 15.0 and 15.2 are too unreliable to test. # The same version number of GCC works in some distros # but fails in others. From 43bab2c51d8f636c03f769a9f2bd80eb167e0a71 Mon Sep 17 00:00:00 2001 From: Joe Tse Date: Mon, 4 May 2026 20:04:57 +0800 Subject: [PATCH 128/681] programs: lower case command extension on Windows In compile_commands.json, clangd require lower case extension "exe" to unwrap distcc/ccache/sccache. --- mesonbuild/programs.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/mesonbuild/programs.py b/mesonbuild/programs.py index 9c6eb965a07f..58bbf7e82bab 100644 --- a/mesonbuild/programs.py +++ b/mesonbuild/programs.py @@ -309,8 +309,14 @@ def _search_windows_special_cases(self, name: str, command: T.Optional[str], exc # sure that it can be run directly if it's not a native executable. # For instance, interpreted scripts sometimes need to be run explicitly # with an interpreter if the file association is not done properly. - name_ext = os.path.splitext(command)[1] - if name_ext[1:].lower() in self.windows_exts: + + # Lower case the command extension, as clangd requires lower case + # "exe" to unwrap compiler wrappers in CDB, e.g. "ccache.exe cl ..." + prefix, ext = os.path.splitext(command) + ext = ext.lower() + command = prefix + ext + + if ext[1:] in self.windows_exts: # Good, it can be directly executed return [command] # Try to extract the interpreter from the shebang From 23056d3e5fe831a22863e317961b72051f40b1b1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 6 May 2026 08:56:09 +0200 Subject: [PATCH 129/681] test cases: add new tests for programs in arguments Cover input of custom_target, arguments of test(), and the "depends" keyword argument. The test passes in 1.10.1, including the expect_error(). --- .../296 program in kwargs/customtarget.py | 18 ++++++++ .../common/296 program in kwargs/foo.py | 1 + .../common/296 program in kwargs/meson.build | 46 +++++++++++++++++++ .../subprojects/sub/meson.build | 4 ++ .../subprojects/sub/trivial.c | 6 +++ .../common/296 program in kwargs/test.py | 13 ++++++ 6 files changed, 88 insertions(+) create mode 100644 test cases/common/296 program in kwargs/customtarget.py create mode 100644 test cases/common/296 program in kwargs/foo.py create mode 100644 test cases/common/296 program in kwargs/meson.build create mode 100644 test cases/common/296 program in kwargs/subprojects/sub/meson.build create mode 100644 test cases/common/296 program in kwargs/subprojects/sub/trivial.c create mode 100644 test cases/common/296 program in kwargs/test.py diff --git a/test cases/common/296 program in kwargs/customtarget.py b/test cases/common/296 program in kwargs/customtarget.py new file mode 100644 index 000000000000..efbd580ab69b --- /dev/null +++ b/test cases/common/296 program in kwargs/customtarget.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 + +import argparse +import os + + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument('sometool') + parser.add_argument('output') + args = parser.parse_args() + + with open(args.output, 'w', encoding='utf-8') as f: + f.write('') + + +if __name__ == "__main__": + main() diff --git a/test cases/common/296 program in kwargs/foo.py b/test cases/common/296 program in kwargs/foo.py new file mode 100644 index 000000000000..21ab5da3a865 --- /dev/null +++ b/test cases/common/296 program in kwargs/foo.py @@ -0,0 +1 @@ +#! /usr/bin/env python3 diff --git a/test cases/common/296 program in kwargs/meson.build b/test cases/common/296 program in kwargs/meson.build new file mode 100644 index 000000000000..5e70a8041e06 --- /dev/null +++ b/test cases/common/296 program in kwargs/meson.build @@ -0,0 +1,46 @@ +project('test') + +subproject('sub') +sometool = find_program('sometool') + +custom_target('something1', + output: 'something1', + input: [sometool], + command: [find_program('customtarget.py'), '@INPUT@', '@OUTPUT@'], + build_by_default: true) + +custom_target('something2', + output: 'something2', + input: files('foo.py'), + command: [find_program('customtarget.py'), '@INPUT@', '@OUTPUT@'], + depends: sometool, + build_by_default: true) + +testcase expect_error('.*ExternalProgram.*', how: 're') + custom_target('something3', + output: 'something3', + input: files('foo.py'), + command: [find_program('customtarget.py'), '@INPUT@', '@OUTPUT@'], + depends: find_program('foo.py'), + build_by_default: true) +endtestcase + +testcase expect_error('.*ExternalProgram.*', how: 're') + custom_target('something4', + output: 'something4', + input: [find_program('foo.py')], + command: [find_program('customtarget.py'), '@INPUT@', '@OUTPUT@'], + build_by_default: true) +endtestcase + +test('mytest1', + find_program('test.py'), + args: [find_program('foo.py')], + depends: [sometool]) + +testcase expect_error('.*ExternalProgram.*', how: 're') + test('mytest2', + find_program('test.py'), + args: [sometool], + depends: [find_program('foo.py')]) +endtestcase diff --git a/test cases/common/296 program in kwargs/subprojects/sub/meson.build b/test cases/common/296 program in kwargs/subprojects/sub/meson.build new file mode 100644 index 000000000000..b36949390c2d --- /dev/null +++ b/test cases/common/296 program in kwargs/subprojects/sub/meson.build @@ -0,0 +1,4 @@ +project('tools', 'c') + +exe = executable('sometool', 'trivial.c') +meson.override_find_program('sometool', exe) diff --git a/test cases/common/296 program in kwargs/subprojects/sub/trivial.c b/test cases/common/296 program in kwargs/subprojects/sub/trivial.c new file mode 100644 index 000000000000..96612d48b4e8 --- /dev/null +++ b/test cases/common/296 program in kwargs/subprojects/sub/trivial.c @@ -0,0 +1,6 @@ +#include + +int main(void) { + printf("Trivial test is working.\n"); + return 0; +} diff --git a/test cases/common/296 program in kwargs/test.py b/test cases/common/296 program in kwargs/test.py new file mode 100644 index 000000000000..72768377d9e3 --- /dev/null +++ b/test cases/common/296 program in kwargs/test.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python3 + +import argparse + + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument('sometool') + parser.parse_args() + + +if __name__ == "__main__": + main() From 942b9d5d0244e94f7959d6b2d927ab7e3c046b1c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 6 May 2026 12:15:09 +0200 Subject: [PATCH 130/681] rust: clean up split of bindgen inputs Call source_strings_to_files twice, so that overloads can do their job and the types are clear for both the header and the dependencies. Correctly handle BothLibraries and ExtractedObjects, which weren't properly handled by the backend. Signed-off-by: Paolo Bonzini --- mesonbuild/interpreter/interpreter.py | 4 ++++ mesonbuild/modules/rust.py | 19 +++++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index ea3c737a9248..aad49cbf4688 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -3227,6 +3227,10 @@ def source_strings_to_files(self, sources: T.List[T.Union[mesonlib.FileOrString, @T.overload def source_strings_to_files(self, sources: T.List[kwtypes.CustomTargetInputs]) -> T.List['CustomTargetSources']: ... # noqa: F811 + @T.overload + def source_strings_to_files(self, sources: T.List[T.Union[mesonlib.FileOrString, build.BuildTargetTypes, build.BothLibraries, build.ExtractedObjects, build.GeneratedTypes]] + ) -> T.List[T.Union[mesonlib.File, build.BuildTargetTypes, build.BothLibraries, build.ExtractedObjects, 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 diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index 4873843c0d9c..a973d59da57a 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -31,7 +31,7 @@ if T.TYPE_CHECKING: from . import ModuleState from .. import cargo - from ..build import BuildTargetTypes, ExecutableKeywordArguments, IncludeDirs, LibTypes + from ..build import BuildTargetTypes, ExecutableKeywordArguments, GeneratedTypes, IncludeDirs, LibTypes from ..cargo.interpreter import RUST_ABI from ..compilers.compilers import Language from ..compilers.rust import RustCompiler @@ -64,7 +64,7 @@ class FuncBindgen(TypedDict): args: T.List[str] c_args: T.List[str] include_directories: T.List[IncludeDirs] - input: T.List[SourceInputs] + input: T.List[T.Union[File, str, GeneratedTypes, BuildTarget, BothLibraries, ExtractedObjects]] output: str output_inline_wrapper: str dependencies: T.List[T.Union[Dependency, ExternalLibrary]] @@ -778,14 +778,23 @@ def bindgen(self, state: ModuleState, args: T.List, kwargs: FuncBindgen) -> Modu The main thing this simplifies is the use of `include_directory` objects, instead of having to pass a plethora of `-I` arguments. """ - header, *_deps = self.interpreter.source_strings_to_files(kwargs['input']) + _header, *_deps = kwargs['input'] + + if isinstance(_header, (BuildTarget, BothLibraries, ExtractedObjects)): + raise InterpreterException('bindgen source file must be a C header, not an object or build target') + header = self.interpreter.source_strings_to_files([_header])[0] # Split File and Target dependencies to add pass to CustomTarget depends: T.List[SourceOutputs] = [] depend_files: T.List[File] = [] - for d in _deps: + for d in self.interpreter.source_strings_to_files(_deps): if isinstance(d, File): depend_files.append(d) + elif isinstance(d, BothLibraries): + depends.append(d.shared) + depends.append(d.static) + elif isinstance(d, ExtractedObjects): + depends.append(d.target) else: depends.append(d) @@ -855,8 +864,6 @@ def bindgen(self, state: ModuleState, args: T.List, kwargs: FuncBindgen) -> Modu name: str if isinstance(header, File): name = header.fname - elif isinstance(header, (BuildTarget, BothLibraries, ExtractedObjects, StructuredSources)): - raise InterpreterException('bindgen source file must be a C header, not an object or build target') else: name = header.get_outputs()[0] From e1ac870b9bf72a24f62f05acc2f39d5aab4dc474 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 6 May 2026 12:42:45 +0200 Subject: [PATCH 131/681] test cases: add testcase for passing dependencies to rust.bindgen Signed-off-by: Paolo Bonzini --- test cases/rust/12 bindgen/meson.build | 18 ++++++++++++++++-- test cases/rust/12 bindgen/src/header2.hpp | 1 + test cases/rust/12 bindgen/src/header3.hpp | 1 + test cases/rust/12 bindgen/src/header4.hpp | 1 + 4 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 test cases/rust/12 bindgen/src/header2.hpp create mode 100644 test cases/rust/12 bindgen/src/header3.hpp create mode 100644 test cases/rust/12 bindgen/src/header4.hpp diff --git a/test cases/rust/12 bindgen/meson.build b/test cases/rust/12 bindgen/meson.build index d3dfba9afad4..ab05f6c8c722 100644 --- a/test cases/rust/12 bindgen/meson.build +++ b/test cases/rust/12 bindgen/meson.build @@ -132,16 +132,30 @@ gp_exe = executable( test('global and project arguments', gp_exe) -cpp_lib = static_library('cpplib', 'src/impl.cpp') +cpp_lib_sources = files('src/impl.cpp') +cpp_lib = both_libraries('cpplib', cpp_lib_sources) +cpp_lib_static = static_library('cpplib-static', cpp_lib_sources) cpp_bind = rust.bindgen( input : 'src/header.hpp', output : 'generated-cpp.rs', ) +# test passing various kinds of objects as dependencies + +rust.bindgen( + input : ['src/header2.hpp', cpp_lib.get_static_lib()], + output : 'generated-cpp-static.rs') +rust.bindgen( + input : ['src/header3.hpp', cpp_lib_static.extract_objects(cpp_lib_sources)], + output : 'generated-cpp-objs.rs') +rust.bindgen( + input : ['src/header4.hpp', cpp_lib], + output : 'generated-cpp-both.rs') + cpp_exe = executable( 'cpp_exe', structured_sources(['src/cpp.rs', cpp_bind]), - link_with : cpp_lib, + link_with : cpp_lib_static, ) test('cpp', cpp_exe) diff --git a/test cases/rust/12 bindgen/src/header2.hpp b/test cases/rust/12 bindgen/src/header2.hpp new file mode 100644 index 000000000000..9e6702678369 --- /dev/null +++ b/test cases/rust/12 bindgen/src/header2.hpp @@ -0,0 +1 @@ +#include "header.hpp" diff --git a/test cases/rust/12 bindgen/src/header3.hpp b/test cases/rust/12 bindgen/src/header3.hpp new file mode 100644 index 000000000000..9e6702678369 --- /dev/null +++ b/test cases/rust/12 bindgen/src/header3.hpp @@ -0,0 +1 @@ +#include "header.hpp" diff --git a/test cases/rust/12 bindgen/src/header4.hpp b/test cases/rust/12 bindgen/src/header4.hpp new file mode 100644 index 000000000000..9e6702678369 --- /dev/null +++ b/test cases/rust/12 bindgen/src/header4.hpp @@ -0,0 +1 @@ +#include "header.hpp" From 488203c49dfc9b0523fe5c7ed4e00c9c0c3f56d7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 6 May 2026 10:31:37 +0200 Subject: [PATCH 132/681] backends: fix type for get_target_depend_files() Both the ninja and vs2010 backends call get_target_depend_files on a GeneratedList. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/backends.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 8161aa4a46df..8cf61eb5e01d 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -1469,7 +1469,7 @@ def get_custom_target_sources(self, target: build.CustomTarget) -> T.List[str]: srcs += fname return srcs - def get_target_depend_files(self, target: T.Union[build.CustomTarget, build.BuildTarget], absolute_paths: bool = False) -> T.List[str]: + def get_target_depend_files(self, target: T.Union[build.CustomTarget, build.BuildTarget, build.GeneratedList], absolute_paths: bool = False) -> T.List[str]: deps: T.List[str] = [] for i in target.depend_files: if isinstance(i, mesonlib.File): From da6760d820c5bb660839612de92aca9599b480c1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 6 May 2026 11:10:34 +0200 Subject: [PATCH 133/681] gnome: make an egregious hack explicit Signed-off-by: Paolo Bonzini --- mesonbuild/modules/gnome.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 02e3034863eb..828248c27511 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -1173,7 +1173,9 @@ def generate_gir(self, state: 'ModuleState', args: T.Tuple[T.List[T.Union[Execut builddir = os.path.join(state.environment.get_build_dir(), state.subdir) depends: T.List[T.Union['FileOrString', 'build.GeneratedTypes', build.BuildTarget, build.StructuredSources]] = [] - depends.extend(gir_dep.sources) + # hack - this cast represents how sources are defined for the + # dependency in gobject-introspection's gir/meson.build + depends.extend(T.cast('T.List[CustomTarget]', gir_dep.sources)) depends.extend(girtargets) langs_compilers = self._get_girtargets_langs_compilers(girtargets) From e5900d006fcd4ef6cdcc2724616086635660d8b9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 6 May 2026 11:45:20 +0200 Subject: [PATCH 134/681] type_checking: introduce unified type for "depends" kwarg This also corrects the type of CustomTarget.extra_depends; it is accessed by the Ninja backend, and get_outputs() is called on each element. So T.Union[str, SourceOutputs] is definitely wrong. Signed-off-by: Paolo Bonzini --- mesonbuild/build.py | 24 ++++++++++++---------- mesonbuild/interpreter/kwargs.py | 9 +++++---- mesonbuild/modules/external_project.py | 8 ++++---- mesonbuild/modules/gnome.py | 28 +++++++++++++------------- mesonbuild/modules/rust.py | 5 +++-- 5 files changed, 39 insertions(+), 35 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 52569e37e7af..bd1006cce62b 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -46,6 +46,7 @@ from .compilers.compilers import Compiler, CompilerDict, Language from .interpreter.interpreter import CustomTargetSources, SourceOutputs, Interpreter from .interpreter.interpreterobjects import Test, Doctest + from .interpreter.kwargs import TargetDepends from .linkers.linkers import StaticLinker from .mesonlib import ExecutableSerialisation, FileMode, FileOrString from .mparser import BaseNode @@ -2055,13 +2056,13 @@ def __init__(self, env: Environment, *, depfile: T.Optional[str] = None, capture: bool = False, - depends: T.Optional[T.List[BuildTargetTypes]] = None, + depends: T.Optional[T.Sequence[TargetDepends]] = None, name: str = 'Generator'): self.environment = env self.exe = exe self.depfile = depfile self.capture = capture - self.depends: T.List[BuildTargetTypes] = depends or [] + self.depends: T.List[TargetDepends] = list(depends or []) self.arglist = arguments self.outputs = output self.name = name @@ -2096,7 +2097,7 @@ def process_files(self, files: T.Iterable[T.Union[str, File, GeneratedTypes]], preserve_path_from: T.Optional[str] = None, extra_args: T.Optional[T.List[str]] = None, env: T.Optional[EnvironmentVariables] = None, - extra_depends: T.Optional[T.List[GeneratedTypes]] = None) -> 'GeneratedList': + extra_depends: T.Optional[T.Sequence[TargetDepends]] = None) -> 'GeneratedList': output = GeneratedList( self, subdir, @@ -2139,7 +2140,7 @@ class GeneratedList(HoldableObject): preserve_path_from: T.Optional[str] extra_args: T.List[str] env: T.Optional[EnvironmentVariables] - extra_depends: T.List[GeneratedTypes] + extra_depends: T.List[TargetDepends] def __post_init__(self) -> None: self.name = self.generator.exe @@ -2156,7 +2157,7 @@ def __post_init__(self) -> None: self.env: EnvironmentVariables = EnvironmentVariables() if self.extra_depends is None: - self.extra_depends: T.List[GeneratedTypes] = [] + self.extra_depends: T.List[TargetDepends] = [] if isinstance(self.generator.exe, programs.Program): if not self.generator.exe.found(): @@ -2991,7 +2992,7 @@ def __init__(self, capture: bool = False, console: bool = False, depend_files: T.Optional[T.Sequence[FileOrString]] = None, - extra_depends: T.Optional[T.Sequence[T.Union[str, SourceOutputs]]] = None, + extra_depends: T.Optional[T.Sequence[TargetDepends]] = None, depfile: T.Optional[str] = None, depfile_type: T.Optional[Literal['gcc', 'msvc']] = None, env: T.Optional[EnvironmentVariables] = None, @@ -3050,8 +3051,8 @@ def __repr__(self): repr_str = "<{0} {1}: {2}>" return repr_str.format(self.__class__.__name__, self.get_id(), self.command) - def get_target_dependencies(self) -> T.List[T.Union[SourceOutputs, str]]: - deps: T.List[T.Union[SourceOutputs, str]] = [] + def get_target_dependencies(self) -> T.List[TargetDepends]: + deps: T.List[TargetDepends] = [] deps.extend(self.dependencies) deps.extend(self.extra_depends) for c in self.sources: @@ -3249,7 +3250,8 @@ class RunTarget(Target, CommandBase): def __init__(self, name: str, command: T.Sequence[T.Union[str, File, BuildTargetTypes, programs.Program]], - dependencies: T.Sequence[AnyTargetType], + # the RunTarget case is used by gnome.yelp() + dependencies: T.Sequence[T.Union[RunTarget, TargetDepends]], subdir: str, subproject: str, environment: Environment, @@ -3257,7 +3259,7 @@ def __init__(self, name: str, default_env: bool = True): # These don't produce output artifacts super().__init__(name, subdir, subproject, False, MachineChoice.BUILD, environment) - self.dependencies = dependencies + self.dependencies = list(dependencies) self.depend_files = [] self.command = self.flatten_command(command) self.absolute_paths = False @@ -3268,7 +3270,7 @@ def __repr__(self) -> str: repr_str = "<{0} {1}: {2}>" return repr_str.format(self.__class__.__name__, self.get_id(), self.command[0]) - def get_dependencies(self) -> T.List[BuildTargetTypes]: + def get_dependencies(self) -> T.List[T.Union[RunTarget, TargetDepends]]: return self.dependencies def get_generated_sources(self) -> T.List[GeneratedTypes]: diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index 21e06bb13c75..a0100b828a7d 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -22,6 +22,7 @@ from .type_checking import PkgConfigDefineType, SourcesVarargsType TestArgs = T.Union[str, File, build.Target, ExternalProgram] +TargetDepends = T.Union[build.CustomTarget, build.CustomTargetIndex, build.BuildTarget, build.GeneratedList] CustomTargetInputs = T.Union[str, build.BuildTarget, build.GeneratedTypes, build.ExtractedObjects, ExternalProgram, File] RustAbi = Literal['rust', 'c'] @@ -55,7 +56,7 @@ class BaseTest(TypedDict): expected_exitcode: T.Optional[int] timeout: int workdir: T.Optional[str] - depends: T.List[T.Union[build.CustomTarget, build.BuildTarget]] + depends: T.List[TargetDepends] priority: int env: EnvironmentVariables suite: T.List[str] @@ -109,7 +110,7 @@ class FuncGenerator(TypedDict): output: T.List[str] depfile: T.Optional[str] capture: bool - depends: T.List[T.Union[build.BuildTarget, build.CustomTarget]] + depends: T.List[TargetDepends] class GeneratorProcess(TypedDict): @@ -185,7 +186,7 @@ class FuncAddLanguages(ExtractRequired): class RunTarget(TypedDict): command: T.List[T.Union[str, build.BuildTargetTypes, ExternalProgram, File]] - depends: T.List[T.Union[build.BuildTargetTypes]] + depends: T.List[TargetDepends] env: EnvironmentVariables @@ -199,7 +200,7 @@ class CustomTarget(TypedDict): command: T.List[T.Union[str, build.BuildTargetTypes, Program, File]] console: bool depend_files: T.List[FileOrString] - depends: T.List[T.Union[build.BuildTarget, build.CustomTarget]] + depends: T.List[TargetDepends] depfile: T.Optional[str] env: EnvironmentVariables feed: bool diff --git a/mesonbuild/modules/external_project.py b/mesonbuild/modules/external_project.py index e8d48ceea0a3..b945066d9533 100644 --- a/mesonbuild/modules/external_project.py +++ b/mesonbuild/modules/external_project.py @@ -28,8 +28,8 @@ from . import ModuleState from .._typing import ImmutableListProtocol - from ..build import BuildTarget, CustomTarget from ..interpreter import Interpreter + from ..interpreter.kwargs import TargetDepends from ..interpreterbase import TYPE_var from ..mesonlib import EnvironmentVariables from ..utils.core import EnvironOrDict @@ -44,7 +44,7 @@ class AddProject(TypedDict): cross_configure_options: T.List[str] verbose: bool env: EnvironmentVariables - depends: T.List[T.Union[BuildTarget, CustomTarget]] + depends: T.List[TargetDepends] class ExternalProject(NewExtensionModule): @@ -58,7 +58,7 @@ def __init__(self, cross_configure_options: T.List[str], env: EnvironmentVariables, verbose: bool, - extra_depends: T.List[T.Union['BuildTarget', 'CustomTarget']]): + extra_depends: T.List[TargetDepends]): super().__init__() self.methods.update({'dependency': self.dependency_method, }) @@ -234,7 +234,7 @@ def _run(self, step: str, command: T.List[str], workdir: Path) -> None: print(contents) raise MesonException(m) - def _create_targets(self, extra_depends: T.List[T.Union['BuildTarget', 'CustomTarget']]) -> T.List['TYPE_var']: + def _create_targets(self, extra_depends: T.List[TargetDepends]) -> T.List['TYPE_var']: cmd = self.env.get_build_command() cmd += ['--internal', 'externalproject', '--name', self.name, diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 828248c27511..e92d3f0e2bb7 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -44,7 +44,7 @@ from ..compilers import Compiler from ..interpreter import Interpreter from ..interpreter.interpreter import CustomTargetSources - from ..interpreter.kwargs import CustomTargetInputs + from ..interpreter.kwargs import CustomTargetInputs, TargetDepends from ..interpreterbase import TYPE_var, TYPE_kwargs from ..mesonlib import EnvironmentVariables, FileOrString from ..programs import Program, CommandList, CommandListEntry @@ -627,10 +627,10 @@ def _get_gresource_dependencies( def _get_link_args(self, state: 'ModuleState', lib: T.Union[build.SharedLibrary, build.StaticLibrary], - depends: T.Sequence[T.Union[build.BuildTarget, 'build.GeneratedTypes', 'FileOrString', build.StructuredSources]], + depends: T.Sequence[TargetDepends], include_rpath: bool = False, use_gir_args: bool = False - ) -> T.Tuple[T.List[str], T.List[T.Union[build.BuildTarget, 'build.GeneratedTypes', 'FileOrString', build.StructuredSources]]]: + ) -> T.Tuple[T.List[str], T.List[TargetDepends]]: link_command: T.List[str] = [] new_depends = list(depends) # Construct link args @@ -658,11 +658,11 @@ def _get_link_args(self, state: 'ModuleState', def _get_dependencies_flags_raw( self, deps: T.Sequence[T.Union['Dependency', build.BuildTargetTypes]], state: 'ModuleState', - depends: T.Sequence[T.Union[build.BuildTarget, 'build.GeneratedTypes', 'FileOrString', build.StructuredSources]], + depends: T.Sequence[TargetDepends], include_rpath: bool, use_gir_args: bool, ) -> T.Tuple[OrderedSet[str], OrderedSet[T.Union[str, T.Tuple[str, str]]], OrderedSet[T.Union[str, T.Tuple[str, str]]], OrderedSet[str], - T.List[T.Union[build.BuildTarget, 'build.GeneratedTypes', 'FileOrString', build.StructuredSources]]]: + T.List[TargetDepends]]: cflags: OrderedSet[str] = OrderedSet() # External linker flags that can't be de-duped reliably because they # require two args in order, such as -framework AVFoundation will be stored as a tuple. @@ -751,13 +751,13 @@ def fix_ldflags(ldflags: T.Iterable[T.Union[str, T.Tuple[str, str]]]) -> Ordered def _get_dependencies_flags( self, deps: T.Sequence[T.Union['Dependency', build.BuildTargetTypes]], state: 'ModuleState', - depends: T.Sequence[T.Union[build.BuildTarget, 'build.GeneratedTypes', 'FileOrString', build.StructuredSources]], + depends: T.Sequence[TargetDepends], include_rpath: bool = False, use_gir_args: bool = False, ) -> T.Tuple[OrderedSet[str], T.List[str], T.List[str], OrderedSet[str], - T.List[T.Union[build.BuildTarget, 'build.GeneratedTypes', 'FileOrString', build.StructuredSources]]]: + T.List[TargetDepends]]: - cflags, internal_ldflags_raw, external_ldflags_raw, gi_includes, depends = self._get_dependencies_flags_raw(deps, state, depends, include_rpath, use_gir_args) + cflags, internal_ldflags_raw, external_ldflags_raw, gi_includes, new_depends = self._get_dependencies_flags_raw(deps, state, depends, include_rpath, use_gir_args) internal_ldflags: T.List[str] = [] external_ldflags: T.List[str] = [] @@ -773,7 +773,7 @@ def _get_dependencies_flags( else: external_ldflags.extend(ldflag) - return cflags, internal_ldflags, external_ldflags, gi_includes, depends + return cflags, internal_ldflags, external_ldflags, gi_includes, new_depends def _unwrap_gir_target(self, girtarget: T.Union[Executable, build.StaticLibrary, build.SharedLibrary], state: 'ModuleState' ) -> T.Union[Executable, build.StaticLibrary, build.SharedLibrary]: @@ -980,7 +980,7 @@ def _make_gir_target( girfile: str, scan_command: T.Sequence[T.Union['FileOrString', Executable, Program]], generated_files: T.Sequence[build.GeneratedTypes], - depends: T.Sequence[T.Union['FileOrString', build.BuildTarget, 'build.GeneratedTypes', build.StructuredSources]], + depends: T.Sequence[TargetDepends], env_flags: T.Sequence[str], kwargs: GenerateGir) -> GirTarget: install = kwargs['install_gir'] @@ -1063,8 +1063,8 @@ def _make_typelib_target(state: 'ModuleState', typelib_output: str, def _gather_typelib_includes_and_update_depends( state: 'ModuleState', deps: T.Sequence[T.Union[Dependency, build.BuildTargetTypes]], - depends: T.Sequence[T.Union[build.BuildTarget, 'build.GeneratedTypes', 'FileOrString', build.StructuredSources]] - ) -> T.Tuple[T.List[str], T.List[T.Union[build.BuildTarget, 'build.GeneratedTypes', 'FileOrString', build.StructuredSources]]]: + depends: T.Sequence[TargetDepends] + ) -> T.Tuple[T.List[str], T.List[TargetDepends]]: # Need to recursively add deps on GirTarget sources from our # dependencies and also find the include directories needed for the # typelib generation custom target below. @@ -1172,7 +1172,7 @@ def generate_gir(self, state: 'ModuleState', args: T.Tuple[T.List[T.Union[Execut srcdir = os.path.join(state.environment.get_source_dir(), state.subdir) builddir = os.path.join(state.environment.get_build_dir(), state.subdir) - depends: T.List[T.Union['FileOrString', 'build.GeneratedTypes', build.BuildTarget, build.StructuredSources]] = [] + depends: T.List[TargetDepends] = [] # hack - this cast represents how sources are defined for the # dependency in gobject-introspection's gir/meson.build depends.extend(T.cast('T.List[CustomTarget]', gir_dep.sources)) @@ -1597,7 +1597,7 @@ def _get_build_args(self, c_args: T.List[str], inc_dirs: T.List[T.Union[str, bui deps: T.List[T.Union[Dependency, build.SharedLibrary, build.StaticLibrary]], state: 'ModuleState', depends: T.Sequence[T.Union[build.BuildTarget, 'build.GeneratedTypes']]) -> T.Tuple[ - T.List[str], T.List[T.Union[build.BuildTarget, 'build.GeneratedTypes', 'FileOrString', build.StructuredSources]]]: + T.List[str], T.List[TargetDepends]]: args: T.List[str] = [] cflags = c_args.copy() deps_cflags, internal_ldflags, external_ldflags, _gi_includes, new_depends = \ diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index a973d59da57a..565bc0191838 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -38,7 +38,8 @@ from ..dependencies import ExternalLibrary from ..interpreter import Interpreter from ..interpreter import kwargs as _kwargs - from ..interpreter.interpreter import SourceInputs, SourceOutputs + from ..interpreter.kwargs import TargetDepends + from ..interpreter.interpreter import SourceOutputs from ..interpreter.interpreterobjects import Test from ..interpreterbase import TYPE_kwargs from ..programs import Program @@ -785,7 +786,7 @@ def bindgen(self, state: ModuleState, args: T.List, kwargs: FuncBindgen) -> Modu header = self.interpreter.source_strings_to_files([_header])[0] # Split File and Target dependencies to add pass to CustomTarget - depends: T.List[SourceOutputs] = [] + depends: T.List[TargetDepends] = [] depend_files: T.List[File] = [] for d in self.interpreter.source_strings_to_files(_deps): if isinstance(d, File): From 74caea284c783ad7ccafe4cdfad037aac0a10c9e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 7 May 2026 19:07:35 +0200 Subject: [PATCH 135/681] build: GeneratedList is not allowed in commands Match the CustomTarget iterator to the interpreter's type checking. Signed-off-by: Paolo Bonzini --- mesonbuild/build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index bd1006cce62b..1b7817303728 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2982,7 +2982,7 @@ def __init__(self, subproject: str, environment: Environment, command: T.Sequence[T.Union[ - str, BuildTargetTypes, GeneratedList, + str, BuildTargetTypes, programs.Program, File]], sources: T.Sequence[CustomTargetSources], outputs: T.List[str], From 5ec7277a550fab8c0df3800658bb683b5c3ac55a Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 6 May 2026 08:49:21 -0700 Subject: [PATCH 136/681] build: Fix custom_target having an ExternalProgram as a source Since 1.11 an ExternalProgram is a valid input, but it would have failed here. Spotted by mypy. Signed-off-by: Paolo Bonzini --- mesonbuild/build.py | 11 +++++++---- mesonbuild/interpreter/interpreter.py | 5 ++--- test cases/common/296 program in kwargs/meson.build | 12 +++++------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 1b7817303728..5da28d8ca753 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -3576,10 +3576,11 @@ class TestSetup: env: EnvironmentVariables exclude_suites: T.List[str] -def get_sources_string_names(sources, backend): + +def get_sources_string_names(sources: T.Sequence[CustomTargetSources], backend: Backend) -> list[str]: ''' - For the specified list of @sources which can be strings, Files, or targets, - get all the output basenames. + For the specified list of @sources which can be strings, Files, + ExternalPrograms, or targets, get all the output basenames. ''' names = [] for s in sources: @@ -3589,8 +3590,10 @@ def get_sources_string_names(sources, backend): names += backend.determine_ext_objs(s) elif isinstance(s, File): names.append(s.fname) + elif isinstance(s, programs.ExternalProgram): + names.append(s.get_path()) else: - raise AssertionError(f'Unknown source type: {s!r}') + raise MesonBugException(f'Unknown source type: {s!r}') return names def load(build_dir: str) -> Build: diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index aad49cbf4688..02def677a461 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -3264,9 +3264,8 @@ def source_strings_to_files(self, sources: T.List['SourceInputs'], strict: bool else: self.validate_within_subproject(self.subdir, s) results.append(mesonlib.File.from_source_file(self.environment.source_dir, self.subdir, s)) - elif isinstance(s, mesonlib.File): - results.append(s) - elif isinstance(s, (build.GeneratedList, build.BuildTarget, + elif isinstance(s, (mesonlib.File, ExternalProgram, + build.GeneratedList, build.BuildTarget, build.CustomTargetIndex, build.CustomTarget, build.ExtractedObjects, build.StructuredSources)): results.append(s) diff --git a/test cases/common/296 program in kwargs/meson.build b/test cases/common/296 program in kwargs/meson.build index 5e70a8041e06..b24177a6951d 100644 --- a/test cases/common/296 program in kwargs/meson.build +++ b/test cases/common/296 program in kwargs/meson.build @@ -25,13 +25,11 @@ testcase expect_error('.*ExternalProgram.*', how: 're') build_by_default: true) endtestcase -testcase expect_error('.*ExternalProgram.*', how: 're') - custom_target('something4', - output: 'something4', - input: [find_program('foo.py')], - command: [find_program('customtarget.py'), '@INPUT@', '@OUTPUT@'], - build_by_default: true) -endtestcase +custom_target('something4', + output: 'something4', + input: [find_program('foo.py')], + command: [find_program('customtarget.py'), '@INPUT@', '@OUTPUT@'], + build_by_default: true) test('mytest1', find_program('test.py'), From 5757c9d5323197feab7041812155fa0c24fa48ce Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 6 May 2026 11:22:33 +0200 Subject: [PATCH 137/681] interpreter, backends: fix types for test commands and arguments Make the type of "exe" consistent with the definition of test(), and allow any program for arguments. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/backends.py | 9 +++++---- mesonbuild/interpreter/interpreter.py | 10 +++------- mesonbuild/interpreter/interpreterobjects.py | 6 +++--- mesonbuild/interpreter/kwargs.py | 2 +- 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 8cf61eb5e01d..6e38fc3bad63 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -1181,11 +1181,12 @@ def create_test_serialisation(self, tests: T.List['Test']) -> T.List[TestSeriali exe = t.get_exe() if isinstance(exe, build.LocalProgram): exe = exe.program - if isinstance(exe, programs.Program): - cmd = exe.get_command() - else: + if isinstance(exe, (build.BuildTarget, build.CustomTarget, build.CustomTargetIndex)): cmd = [os.path.join(self.environment.get_build_dir(), self.get_target_filename(exe))] - if isinstance(exe, (build.BuildTarget, programs.Program)): + else: + cmd = exe.get_command() + + if isinstance(exe, (build.BuildTarget, programs.ExternalProgram)): test_for_machine = exe.for_machine else: # E.g. an external verifier or simulator program run on a generated executable. diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 02def677a461..7a4a48d2a6f0 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -2252,7 +2252,7 @@ def func_generator(self, node: mparser.FunctionNode, @typed_pos_args('benchmark', str, (build.Executable, build.Jar, Program, mesonlib.File, build.CustomTarget, build.CustomTargetIndex)) @typed_kwargs('benchmark', *TEST_KWS) def func_benchmark(self, node: mparser.BaseNode, - args: T.Tuple[str, T.Union[build.Executable, build.Jar, Program, mesonlib.File]], + args: T.Tuple[str, T.Union[build.Executable, build.Jar, Program, mesonlib.File, build.CustomTarget, build.CustomTargetIndex]], kwargs: 'kwtypes.FuncBenchmark') -> None: self.add_test(node, args, kwargs, False) @@ -2282,12 +2282,8 @@ def make_test(self, node: mparser.BaseNode, location=node) name = name.replace(':', '_') exe = args[1] - if isinstance(exe, Program): - if not exe.found(): - raise InvalidArguments('Tried to use not-found external program as test exe') - if isinstance(exe, build.LocalProgram): - # This will add exe to 'depends' below - exe = exe.program + if isinstance(exe, Program) and not exe.found(): + raise InvalidArguments('Tried to use not-found external program as test exe') elif isinstance(exe, mesonlib.File): exe = self.find_program_impl([exe]) if isinstance(exe, (build.Executable, build.CustomTarget)): diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index 0d5b8df68743..dd5c9cfee6de 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -838,10 +838,10 @@ class GeneratedObjectsHolder(ObjectHolder[build.ExtractedObjects]): class Test(MesonInterpreterObject): def __init__(self, name: str, project: str, suite: T.List[str], - exe: T.Union[ExternalProgram, build.Executable, build.CustomTarget, build.CustomTargetIndex], + exe: T.Union[Program, build.Executable, build.Jar, build.CustomTarget, build.CustomTargetIndex], depends: T.List[T.Union[build.CustomTarget, build.BuildTarget]], is_parallel: bool, - cmd_args: T.List[T.Union[str, mesonlib.File, build.Target, ExternalProgram]], + cmd_args: T.List[T.Union[str, mesonlib.File, build.Target, Program]], env: mesonlib.EnvironmentVariables, expected_fail: bool, expected_exitcode: int, timeout: int, workdir: T.Optional[str], protocol: str, priority: int, verbose: bool): @@ -862,7 +862,7 @@ def __init__(self, name: str, project: str, suite: T.List[str], self.priority = priority self.verbose = verbose - def get_exe(self) -> T.Union[ExternalProgram, build.Executable, build.CustomTarget, build.CustomTargetIndex]: + def get_exe(self) -> T.Union[Program, build.Executable, build.Jar, build.CustomTarget, build.CustomTargetIndex]: return self.exe def get_name(self) -> str: diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index a0100b828a7d..f1d33d8e842c 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -21,7 +21,7 @@ from ..programs import Program, ExternalProgram from .type_checking import PkgConfigDefineType, SourcesVarargsType -TestArgs = T.Union[str, File, build.Target, ExternalProgram] +TestArgs = T.Union[str, File, build.Target, Program] TargetDepends = T.Union[build.CustomTarget, build.CustomTargetIndex, build.BuildTarget, build.GeneratedList] CustomTargetInputs = T.Union[str, build.BuildTarget, build.GeneratedTypes, build.ExtractedObjects, ExternalProgram, File] From 0aca94d2b165d7287aa64ea9a5f0bc3f9d0bb0c8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 7 May 2026 16:25:29 +0200 Subject: [PATCH 138/681] interpreter, backends: use TargetDepends for tests Signed-off-by: Paolo Bonzini --- mesonbuild/backend/backends.py | 8 +++----- mesonbuild/interpreter/interpreterobjects.py | 11 ++++++++--- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 6e38fc3bad63..76b4ef309c4e 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -1218,16 +1218,14 @@ def create_test_serialisation(self, tests: T.List['Test']) -> T.List[TestSeriali extra_paths = [] cmd_args: T.List[str] = [] - depends: T.Set[build.Target] = set(t.depends) - if isinstance(exe, build.Target): + depends: T.Set[build.BuildTargetTypes] = set(t.depends) + if isinstance(exe, (build.BuildTarget, build.CustomTarget, build.CustomTargetIndex)): depends.add(exe) for a in t.cmd_args: if isinstance(a, build.LocalProgram): a = a.program - if isinstance(a, build.Target): + if isinstance(a, (build.BuildTarget, build.CustomTarget, build.CustomTargetIndex)): depends.add(a) - elif isinstance(a, build.CustomTargetIndex): - depends.add(a.target) if isinstance(a, mesonlib.File): a = os.path.join(self.environment.get_build_dir(), a.rel_to_builddir(self.build_to_src)) diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index dd5c9cfee6de..4c2dbb295447 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -26,7 +26,7 @@ from ..interpreter.type_checking import NoneType, ENV_KW, ENV_SEPARATOR_KW, PKGCONFIG_DEFINE_KW from ..dependencies import Dependency, ExternalLibrary, InternalDependency from ..programs import ExternalProgram, Program -from ..mesonlib import HoldableObject, listify +from ..mesonlib import HoldableObject, listify, MesonException import typing as T @@ -839,7 +839,7 @@ class GeneratedObjectsHolder(ObjectHolder[build.ExtractedObjects]): class Test(MesonInterpreterObject): def __init__(self, name: str, project: str, suite: T.List[str], exe: T.Union[Program, build.Executable, build.Jar, build.CustomTarget, build.CustomTargetIndex], - depends: T.List[T.Union[build.CustomTarget, build.BuildTarget]], + depends: T.Sequence[kwargs.TargetDepends], is_parallel: bool, cmd_args: T.List[T.Union[str, mesonlib.File, build.Target, Program]], env: mesonlib.EnvironmentVariables, @@ -850,7 +850,6 @@ def __init__(self, name: str, project: str, suite: T.List[str], self.suite = listify(suite) self.project_name = project self.exe = exe - self.depends = depends self.is_parallel = is_parallel self.cmd_args = cmd_args self.env = env @@ -862,6 +861,12 @@ def __init__(self, name: str, project: str, suite: T.List[str], self.priority = priority self.verbose = verbose + self.depends: T.List[build.BuildTargetTypes] = [] + for d in depends: + if isinstance(d, build.GeneratedList): + raise MesonException('generated_list not allowed as a dependency for test') + self.depends.append(d) + def get_exe(self) -> T.Union[Program, build.Executable, build.Jar, build.CustomTarget, build.CustomTargetIndex]: return self.exe From b9f01e810f4c663b63883128a19029e15860a14a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 6 May 2026 09:41:54 +0200 Subject: [PATCH 139/681] build: store the underlying File for a LocalProgram that exists at setup time This information is currently lost, causing the interpreter to have to deal with ExternalPrograms without knowing whether the ExternalProgram is a File (so something in the build or source trees) or *really* external. Signed-off-by: Paolo Bonzini --- mesonbuild/build.py | 19 +++++++++++++++++-- mesonbuild/interpreter/mesonmain.py | 2 +- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 5da28d8ca753..be413617e54e 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2163,7 +2163,11 @@ def __post_init__(self) -> None: if not self.generator.exe.found(): raise InvalidArguments('Tried to use not-found external program as generator') if isinstance(self.generator.exe, LocalProgram): - self.extra_depends.append(self.generator.exe.program) + t = self.generator.exe.get_target() + if isinstance(t, File): + self.depend_files.append(t) + else: + self.extra_depends.append(t) else: path = self.generator.exe.get_path() if os.path.isabs(path): @@ -3499,12 +3503,17 @@ def keys(self) -> T.Iterator[str]: return self.values.keys() class LocalProgram(programs.Program): - def __init__(self, program: T.Union[programs.ExternalProgram, Executable, CustomTarget, CustomTargetIndex], version: str) -> None: + def __init__(self, program: T.Union[programs.ExternalProgram, Executable, CustomTarget, CustomTargetIndex], version: str, + file: T.Optional[File] = None) -> None: super().__init__() if isinstance(program, CustomTarget): if len(program.outputs) != 1: raise InvalidArguments('CustomTarget used as LocalProgram must have exactly one output.') + if isinstance(program, programs.ExternalProgram): + if not file: + raise MesonBugException('ExternalProgram used as LocalProgram must be a file from the project') self.name = program.name + self.file = file self.for_machine = program.for_machine self.program = program self.version = version @@ -3527,6 +3536,12 @@ def get_path(self) -> str: # Only the backend knows the actual path to the build program. raise MesonBugException('Cannot call get_path() on program that is a build target.') + def get_target(self) -> T.Union[File, Executable, CustomTarget, CustomTargetIndex]: + if self.file: + return self.file + assert not isinstance(self.program, programs.ExternalProgram) + return self.program + def description(self) -> str: if isinstance(self.program, programs.ExternalProgram): return self.program.description() diff --git a/mesonbuild/interpreter/mesonmain.py b/mesonbuild/interpreter/mesonmain.py index dafabcbcbca5..cea248e558a9 100644 --- a/mesonbuild/interpreter/mesonmain.py +++ b/mesonbuild/interpreter/mesonmain.py @@ -334,7 +334,7 @@ def override_find_program_method(self, args: T.Tuple[str, T.Union[mesonlib.File, if not os.path.exists(abspath): raise InterpreterException(f'Tried to override {name} with a file that does not exist.') prog = ExternalProgram(name, command=[abspath], silent=True) - exe = build.LocalProgram(prog, self.interpreter.project_version) + exe = build.LocalProgram(prog, self.interpreter.project_version, file=exe) elif isinstance(exe, build.Executable): exe = build.LocalProgram(exe, self.interpreter.project_version) self.interpreter.add_find_program_override(name, exe) From 8035ea6f5cbfe5e243873a1ecb3d73593b606c28 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 7 May 2026 16:03:21 +0200 Subject: [PATCH 140/681] interpreter: allow Program for custom_target inputs Previous version of Meson were returning an Executable instead of the LocalProgram, so allow it again; use a convertor so that the LocalProgram is resolved to the underlying file or target. Add support for ExternalProgram while at it. Fixes: #15774 Signed-off-by: Paolo Bonzini --- docs/yaml/functions/custom_target.yaml | 6 ++++-- mesonbuild/interpreter/type_checking.py | 18 +++++++++++++++--- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/docs/yaml/functions/custom_target.yaml b/docs/yaml/functions/custom_target.yaml index b3252050140e..e9ea288b3cba 100644 --- a/docs/yaml/functions/custom_target.yaml +++ b/docs/yaml/functions/custom_target.yaml @@ -189,8 +189,10 @@ kwargs: are also accepted. input: - type: array[str | file] - description: Array of source files. *(since 0.41.0)* the array is flattened. + type: array[str | file | program] + description: | + Array of source files. *(since 0.41.0)* the array is flattened. + `program` is supported *since 1.11.2*. install: type: bool diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py index d414e8ee8f06..ef83dc41be3b 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -9,7 +9,7 @@ from .. import compilers from ..build import (CustomTarget, BuildTarget, - CustomTargetIndex, ExtractedObjects, GeneratedList, IncludeDirs, + CustomTargetIndex, ExtractedObjects, GeneratedList, IncludeDirs, LocalProgram, BothLibraries, SharedLibrary, StaticLibrary, Jar, Executable, StructuredSources) from ..options import OptionKey from ..dependencies import Dependency, DependencyMethods, InternalDependency @@ -25,6 +25,7 @@ if T.TYPE_CHECKING: from typing_extensions import Literal + from .kwargs import CustomTargetInputs from ..build import ObjectTypes, GeneratedTypes, BuildTargetTypes from ..interpreterbase import TYPE_var from ..options import ElementaryOptionValues @@ -351,11 +352,22 @@ def _output_validator(outputs: T.List[str]) -> T.Optional[str]: validator=lambda x: _output_validator([x]) ) -CT_INPUT_KW: KwargInfo[T.List[T.Union[str, File, ExternalProgram, BuildTarget, GeneratedTypes, ExtractedObjects]]] = KwargInfo( +def _local_program_convertor(raw: T.List[T.Union[str, File, BuildTarget, GeneratedTypes, ExtractedObjects, LocalProgram]]) -> T.List[CustomTargetInputs]: + result: T.List[CustomTargetInputs] = [] + for i in raw: + if isinstance(i, LocalProgram): + result.append(i.get_target()) + else: + result.append(i) + return result + +CT_INPUT_KW: KwargInfo[T.List[T.Union[str, File, BuildTarget, GeneratedTypes, ExtractedObjects, LocalProgram]]] = KwargInfo( 'input', - ContainerTypeInfo(list, (str, File, ExternalProgram, BuildTarget, CustomTarget, CustomTargetIndex, ExtractedObjects, GeneratedList)), + ContainerTypeInfo(list, (str, File, BuildTarget, CustomTarget, CustomTargetIndex, ExtractedObjects, GeneratedList, Program)), listify=True, default=[], + convertor=_local_program_convertor, + since_values={ExternalProgram: '1.11.2'}, ) CT_INSTALL_TAG_KW: KwargInfo[T.List[T.Union[str, bool]]] = KwargInfo( From 0fea57bd9815d5f181be1dca9dcc71bcebb727bb Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 6 May 2026 10:38:30 +0200 Subject: [PATCH 141/681] interpreter: allow Program as an element of "depends" Previous version of Meson were returning an Executable instead of the LocalProgram. While at it, add CustomTargetIndex to the type as well - it is included in the KwargInfo but not in the TypedDict. Fixes: #15774 Signed-off-by: Paolo Bonzini --- docs/markdown/Gnome-module.md | 4 ++-- docs/yaml/functions/benchmark.yaml | 3 ++- docs/yaml/functions/custom_target.yaml | 4 ++-- docs/yaml/functions/generator.yaml | 4 ++-- docs/yaml/functions/run_target.yaml | 5 ++-- mesonbuild/build.py | 18 +++++++++++++- mesonbuild/interpreter/interpreterobjects.py | 12 +++++++--- mesonbuild/interpreter/kwargs.py | 2 +- mesonbuild/interpreter/type_checking.py | 6 ++--- .../common/296 program in kwargs/meson.build | 24 ++++++++----------- .../common/296 program in kwargs/test.py | 2 +- 11 files changed, 52 insertions(+), 32 deletions(-) diff --git a/docs/markdown/Gnome-module.md b/docs/markdown/Gnome-module.md index af759df8c3f2..c89e0e96165c 100644 --- a/docs/markdown/Gnome-module.md +++ b/docs/markdown/Gnome-module.md @@ -131,8 +131,8 @@ typelib_target]` Generates a marshal file using the `glib-genmarshal` tool. The first argument is the basename of the output files. -* `depends` [](BuildTarget | CustomTarget | CustomTargetIndex): - passed directly to CustomTarget (*since 0.61.0*) +* `depends` [](BuildTarget | CustomTarget | CustomTargetIndex | Program): + passed directly to CustomTarget (*since 0.61.0*; `program` *since 1.11.2*) * `depend_files` [](str | File): Passed directly to CustomTarget (*since 0.61.0*) * `extra_args`: (*Added 0.42.0*) additional command line arguments to pass * `install_dir`: directory to install header to diff --git a/docs/yaml/functions/benchmark.yaml b/docs/yaml/functions/benchmark.yaml index 3132eb5974b6..4fd7f743d1e5 100644 --- a/docs/yaml/functions/benchmark.yaml +++ b/docs/yaml/functions/benchmark.yaml @@ -88,7 +88,7 @@ kwargs: for the test depends: - type: array[build_tgt | custom_tgt] + type: array[build_tgt | custom_tgt | custom_idx | program] since: 0.46.0 description: | specifies that this test depends on the specified @@ -96,6 +96,7 @@ kwargs: line argument. This is meant for cases where test finds those targets internally, e.g. plugins or globbing. Those targets are built before test is executed even if they have `build_by_default : false`. + `program` is supported *since 1.11.2*. protocol: type: str diff --git a/docs/yaml/functions/custom_target.yaml b/docs/yaml/functions/custom_target.yaml index e9ea288b3cba..9a26e4d8ff09 100644 --- a/docs/yaml/functions/custom_target.yaml +++ b/docs/yaml/functions/custom_target.yaml @@ -167,7 +167,7 @@ kwargs: argument. Useful for adding regen dependencies. depends: - type: array[build_tgt | custom_tgt | custom_idx] + type: array[build_tgt | custom_tgt | custom_idx | program] description: | Specifies that this target depends on the specified target(s), even though it does not take any of them as a command @@ -175,7 +175,7 @@ kwargs: e.g. does globbing internally. Usually you should just put the generated sources as inputs and Meson will set up all dependencies automatically (custom_idx was unavailable as a type between 0.60 - and 1.4.0). + and 1.4.0). `program` is supported *since 1.11.2*. depfile: type: str diff --git a/docs/yaml/functions/generator.yaml b/docs/yaml/functions/generator.yaml index e45c5a27e3b7..cfb86e0dbbf4 100644 --- a/docs/yaml/functions/generator.yaml +++ b/docs/yaml/functions/generator.yaml @@ -50,13 +50,13 @@ kwargs: depends: # Not sure why this is not just `target` - type: array[build_tgt | custom_tgt | custom_idx] + type: array[build_tgt | custom_tgt | custom_idx | program] since: 0.51.0 description: | An array of build targets that must be built before this generator can be run. This is used if you have a generator that calls a second executable that is built in this project (custom_idx was not - available between 0.60 and 1.4.0). + available between 0.60 and 1.4.0). `program` is supported *since 1.11.2*. depfile: type: str diff --git a/docs/yaml/functions/run_target.yaml b/docs/yaml/functions/run_target.yaml index 0e80944ba014..11f184d769b1 100644 --- a/docs/yaml/functions/run_target.yaml +++ b/docs/yaml/functions/run_target.yaml @@ -40,12 +40,13 @@ kwargs: the first item will find that command in `PATH` and run it. depends: - type: array[build_tgt | custom_tgt | custom_idx] + type: array[build_tgt | custom_tgt | custom_idx | program] description: | An array of targets that this target depends on but which are not listed in the command array (because, for example, the script does file globbing internally, custom_idx was not possible - as a type between 0.60 and 1.4.0). + as a type between 0.60 and 1.4.0). `program` is supported *since + 1.11.2*. env: since: 0.57.0 diff --git a/mesonbuild/build.py b/mesonbuild/build.py index be413617e54e..d6d9f1548bf0 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -3029,7 +3029,6 @@ def __init__(self, self.depfile = depfile self.depfile_type = 'gcc' if depfile else depfile_type self.env = env or EnvironmentVariables() - self.extra_depends = list(extra_depends or []) self.feed = feed self.install_dir = list(install_dir or []) self.has_custom_install_dir = bool(self.install_dir) @@ -3044,6 +3043,23 @@ def __init__(self, # Whether to enable using response files for the underlying tool self.rspable = rspable + self.extra_depends: T.List[T.Union[GeneratedTypes, BuildTarget]] = [] + if extra_depends: + for d in extra_depends: + if isinstance(d, LocalProgram): + d = d.get_target() + elif isinstance(d, programs.Program): + path = d.get_path() + # Can only add a dependency on an external program which we + # know the absolute path of + if not os.path.isabs(path): + continue + d = File.from_absolute_file(path) + if isinstance(d, File): + self.depend_files.append(d) + else: + self.extra_depends.append(d) + def install_dir_names(self) -> T.List[T.Optional[str]]: install_dir_names: T.List[T.Optional[str]] = [] if self.has_custom_install_dir: diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index 4c2dbb295447..565940a69a97 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -25,8 +25,8 @@ InterpreterException, InvalidArguments, InvalidCode) from ..interpreter.type_checking import NoneType, ENV_KW, ENV_SEPARATOR_KW, PKGCONFIG_DEFINE_KW from ..dependencies import Dependency, ExternalLibrary, InternalDependency -from ..programs import ExternalProgram, Program -from ..mesonlib import HoldableObject, listify, MesonException +from ..programs import Program +from ..mesonlib import File, HoldableObject, listify, MesonException import typing as T @@ -838,7 +838,7 @@ class GeneratedObjectsHolder(ObjectHolder[build.ExtractedObjects]): class Test(MesonInterpreterObject): def __init__(self, name: str, project: str, suite: T.List[str], - exe: T.Union[Program, build.Executable, build.Jar, build.CustomTarget, build.CustomTargetIndex], + exe: T.Union[Program, build.Executable, build.CustomTarget, build.CustomTargetIndex], depends: T.Sequence[kwargs.TargetDepends], is_parallel: bool, cmd_args: T.List[T.Union[str, mesonlib.File, build.Target, Program]], @@ -865,6 +865,12 @@ def __init__(self, name: str, project: str, suite: T.List[str], for d in depends: if isinstance(d, build.GeneratedList): raise MesonException('generated_list not allowed as a dependency for test') + if isinstance(d, Program): + if not isinstance(d, build.LocalProgram): + continue + d = d.get_target() + if isinstance(d, File): + continue self.depends.append(d) def get_exe(self) -> T.Union[Program, build.Executable, build.Jar, build.CustomTarget, build.CustomTargetIndex]: diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index f1d33d8e842c..ae6c2f6f56e9 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -22,7 +22,7 @@ from .type_checking import PkgConfigDefineType, SourcesVarargsType TestArgs = T.Union[str, File, build.Target, Program] -TargetDepends = T.Union[build.CustomTarget, build.CustomTargetIndex, build.BuildTarget, build.GeneratedList] +TargetDepends = T.Union[build.CustomTarget, build.CustomTargetIndex, build.BuildTarget, build.GeneratedList, Program] CustomTargetInputs = T.Union[str, build.BuildTarget, build.GeneratedTypes, build.ExtractedObjects, ExternalProgram, File] RustAbi = Literal['rust', 'c'] diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py index ef83dc41be3b..26933410fb33 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -272,12 +272,12 @@ def _env_convertor(value: _FullEnvInitValueType) -> EnvironmentVariables: validator=lambda x: 'Depfile must be a plain filename with a subdirectory' if has_path_sep(x) else None ) -DEPENDS_KW: KwargInfo[T.List[BuildTargetTypes]] = KwargInfo( +DEPENDS_KW: KwargInfo[T.List[T.Union[BuildTarget, CustomTarget, CustomTargetIndex, Program]]] = KwargInfo( 'depends', - ContainerTypeInfo(list, (BuildTarget, CustomTarget, CustomTargetIndex)), + ContainerTypeInfo(list, (BuildTarget, CustomTarget, CustomTargetIndex, Program)), listify=True, default=[], - since_values={CustomTargetIndex: '1.5.0'}, + since_values={CustomTargetIndex: '1.5.0', ExternalProgram: '1.11.2'}, ) DEPEND_FILES_KW: KwargInfo[T.List[T.Union[str, File]]] = KwargInfo( diff --git a/test cases/common/296 program in kwargs/meson.build b/test cases/common/296 program in kwargs/meson.build index b24177a6951d..e3e876e80d36 100644 --- a/test cases/common/296 program in kwargs/meson.build +++ b/test cases/common/296 program in kwargs/meson.build @@ -16,14 +16,12 @@ custom_target('something2', depends: sometool, build_by_default: true) -testcase expect_error('.*ExternalProgram.*', how: 're') - custom_target('something3', - output: 'something3', - input: files('foo.py'), - command: [find_program('customtarget.py'), '@INPUT@', '@OUTPUT@'], - depends: find_program('foo.py'), - build_by_default: true) -endtestcase +custom_target('something3', + output: 'something3', + input: files('foo.py'), + command: [find_program('customtarget.py'), '@INPUT@', '@OUTPUT@'], + depends: find_program('foo.py'), + build_by_default: true) custom_target('something4', output: 'something4', @@ -36,9 +34,7 @@ test('mytest1', args: [find_program('foo.py')], depends: [sometool]) -testcase expect_error('.*ExternalProgram.*', how: 're') - test('mytest2', - find_program('test.py'), - args: [sometool], - depends: [find_program('foo.py')]) -endtestcase +test('mytest2', + find_program('test.py'), + args: [sometool], + depends: [find_program('foo.py')]) diff --git a/test cases/common/296 program in kwargs/test.py b/test cases/common/296 program in kwargs/test.py index 72768377d9e3..8021f04340a2 100644 --- a/test cases/common/296 program in kwargs/test.py +++ b/test cases/common/296 program in kwargs/test.py @@ -5,7 +5,7 @@ def main() -> None: parser = argparse.ArgumentParser() - parser.add_argument('sometool') + parser.add_argument('sometool', nargs='+') parser.parse_args() From 16bf9932bc727c088f94dcd1bd842795e45428d8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 7 May 2026 09:46:08 +0200 Subject: [PATCH 142/681] docs: add release notes for external programs in input and depends Signed-off-by: Paolo Bonzini --- docs/markdown/Release-notes-for-1.11.0.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/markdown/Release-notes-for-1.11.0.md b/docs/markdown/Release-notes-for-1.11.0.md index 75bfa4d40236..ed4bbfce5df0 100644 --- a/docs/markdown/Release-notes-for-1.11.0.md +++ b/docs/markdown/Release-notes-for-1.11.0.md @@ -204,3 +204,14 @@ now has an `implicit_include_directories` keyword argument to automatically add current build and source directories to the included paths when compiling a resource. +## External programs as inputs and dependencies to custom targets + +Custom targets now allow specifying an external program in +the `input` and `depends` keyword arguments. This also applies +to several methods provided by modules, as they are lowered to +custom targets internally. (*Added in 1.11.2*). + +## External programs as dependencies to tests + +Tests now allow specifying an external program in +the `depends` keyword argument. (*Added in 1.11.2*). From 92f0ce787e3317119f8e7f6492c094e3de1ef949 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 7 May 2026 09:44:57 +0200 Subject: [PATCH 143/681] docs: tweak `depends` for features added in 1.12.0 Signed-off-by: Paolo Bonzini --- docs/yaml/objects/generator.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/yaml/objects/generator.yaml b/docs/yaml/objects/generator.yaml index 55bf1ed6f3f9..6e6a0edfaad4 100644 --- a/docs/yaml/objects/generator.yaml +++ b/docs/yaml/objects/generator.yaml @@ -45,7 +45,7 @@ methods: sophisticated environment juggling. depends: - type: array[build_tgt | custom_tgt | custom_idx] + type: array[build_tgt | custom_tgt | custom_idx | program] since: 1.12.0 description: | An array of build targets that must be built before attempting to run this From 169ee76b48a2d8e6510288dfdc8a9fff71f53df2 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 6 May 2026 12:48:27 -0700 Subject: [PATCH 144/681] build: Fix annotations of classify_all_sources Switch from list[Ts...] to sequence[Ts...]. It's a private helper being fed a sequence already, and we need the sequence to handle what the outer function does --- mesonbuild/build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index d6d9f1548bf0..70af4d984272 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -613,7 +613,7 @@ def get_sources(sources: T.Sequence['FileOrString'], generated_sources: T.Sequen # Filter out headers and all non-source files return [s for s in sources if is_source(s)] - def classify_all_sources(self, sources: T.List[FileOrString], generated_sources: T.Sequence['GeneratedTypes']) -> T.Dict['Compiler', T.List['FileOrString']]: + def classify_all_sources(self, sources: T.Sequence[FileOrString], generated_sources: T.Sequence['GeneratedTypes']) -> T.Dict['Compiler', T.List['FileOrString']]: sources_ = self.get_sources(sources, generated_sources) return classify_unity_sources(self.target.compilers.values(), sources_) From 1152370fd67c97a3739ef03de425c6c19ba490c0 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 6 May 2026 12:51:19 -0700 Subject: [PATCH 145/681] build: fix annotations of process_vs_module_defs --- mesonbuild/build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 70af4d984272..d1bbad59bf73 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -1847,7 +1847,7 @@ def check_module_linking(self) -> None: 'use shared_library() with `override_options: [\'b_lundef=false\']` instead.') link_target.force_soname = True - def process_vs_module_defs_kw(self, kwargs: ExecutableKeywordArguments) -> None: + def process_vs_module_defs_kw(self, kwargs: ExecutableKeywordArguments | SharedModuleKeywordArguments) -> None: path = kwargs.get('vs_module_defs') if path is None: return From 10d05d08abb7deb51fcb1cf53ea6eed277741296 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 6 May 2026 12:55:38 -0700 Subject: [PATCH 146/681] build: fix a significant number of optional issues Mostly this stems from assignments like: ```python thing_that_should_be_optional_str = None ``` which needs the `str | None` annotation added --- mesonbuild/build.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index d1bbad59bf73..4a49c3a68d24 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -395,7 +395,7 @@ def __init__(self, environment: Environment): self.dep_manifest_name: T.Optional[str] = None self.dep_manifest: T.Dict[str, DepManifest] = {} self.test_setups: T.Dict[str, TestSetup] = {} - self.test_setup_default_name = None + self.test_setup_default_name: str | None = None self.searched_programs: T.Set[str] = set() # The list of all programs that have been searched for. # If we are doing a cross build we need two caches, if we're doing a @@ -845,7 +845,7 @@ def __init__( self.filename = 'no_name' self.doctests: T.Optional[Doctest] = None # The debugging information file this target will generate - self.debug_filename = None + self.debug_filename: str | None = None # The list of all files outputted by this target. Useful in cases such # as Vala which generates .vapi and .h besides the compiled output. self.outputs = [self.filename] @@ -2285,9 +2285,9 @@ def post_init(self) -> None: self.outputs[0] = self.filename # The import library this target will generate - self.import_filename = None + self.import_filename: str | None = None # The debugging information file this target will generate - self.debug_filename = None + self.debug_filename: str | None = None # If using export_dynamic, set the import library name if self.export_dynamic: @@ -2543,9 +2543,9 @@ def __init__( self.vs_module_defs = None self.shortname: T.Optional[str] = None # The import library this target will generate - self.import_filename = None + self.import_filename: str | None = None # The debugging information file this target will generate - self.debug_filename = None + self.debug_filename: str | None = None # Use by the pkgconfig module self.shared_library_only = False self.rust_crate_type = kwargs.get('rust_crate_type', 'dylib') @@ -2587,6 +2587,11 @@ def get_default_install_dir(self) -> T.Tuple[str, str]: def determine_naming_info(self) -> T.Tuple[str, str, str, str, bool]: scheme = self.environment.coredata.get_option_for_target(self, 'namingscheme') assert isinstance(scheme, str), 'for mypy' + + prefix: str | None + suffix: str | None + import_suffix: str | None + if scheme == 'platform': schemename = self.get_platform_scheme_name() prefix, suffix, import_suffix = DEFAULT_SHARED_LIBRARY_NAMES[schemename] @@ -2598,7 +2603,7 @@ def determine_naming_info(self) -> T.Tuple[str, str, str, str, bool]: create_debug_file = False create_debug_file = False self.filename_tpl = self.basic_filename_tpl - import_filename_tpl = None + import_filename_tpl: str | None = None # NOTE: manual prefix/suffix override is currently only tested for C/C++ # C# and Mono if 'cs' in self.compilers: From 7043dd1ccc297caac57e397b169de49159c26b26 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 6 May 2026 12:57:27 -0700 Subject: [PATCH 147/681] build: Fix various lowhanging missing or incorrect annotations --- mesonbuild/build.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 4a49c3a68d24..23efd94f5069 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -877,7 +877,7 @@ def __init__( self.process_objectlist(objects) self.process_kwargs(kwargs) self.missing_languages = self.process_compilers() - self.single_compile_base_args: T.Dict[Compiler, CompilerArgs] = {} + self.single_compile_base_args: T.Dict[Compiler, ImmutableListProtocol[str]] = {} # self.link_targets and self.link_whole_targets contains libraries from # dependencies (see add_deps()). They have not been processed yet because @@ -2535,7 +2535,7 @@ def __init__( objects: T.List[ObjectTypes], environment: Environment, compilers: CompilerDict, - kwargs): + kwargs: SharedLibraryKeywordArguments): self.soversion: T.Optional[str] = None self.ltversion: T.Optional[str] = None # Max length 2, first element is compatibility_version, second is current_version @@ -2868,7 +2868,7 @@ def __init__( objects: T.List[ObjectTypes], environment: Environment, compilers: CompilerDict, - kwargs): + kwargs: SharedModuleKeywordArguments): if 'version' in kwargs: raise MesonException('Shared modules must not specify the version kwarg.') if 'soversion' in kwargs: @@ -3072,7 +3072,7 @@ def install_dir_names(self) -> T.List[T.Optional[str]]: return install_dir_names - def __repr__(self): + def __repr__(self) -> str: repr_str = "<{0} {1}: {2}>" return repr_str.format(self.__class__.__name__, self.get_id(), self.command) @@ -3105,7 +3105,7 @@ def get_transitive_build_target_deps(self) -> T.Set[T.Union[BuildTarget, 'Custom bdeps.update(d.get_transitive_build_target_deps()) return bdeps - def get_dependencies(self) -> T.List[T.Union[CustomTarget, coredata.BuildTarget]]: + def get_dependencies(self) -> T.List[T.Union[CustomTarget, BuildTarget]]: return self.dependencies def should_install(self) -> bool: @@ -3136,7 +3136,7 @@ def get_generated_lists(self) -> T.List[GeneratedList]: def get_generated_sources(self) -> T.List[GeneratedList]: return self.get_generated_lists() - def get_dep_outname(self, infilenames): + def get_dep_outname(self, infilenames: list[str]) -> str: if self.depfile is None: raise InvalidArguments('Tried to get depfile name for custom_target that does not have depfile defined.') if infilenames: @@ -3197,13 +3197,13 @@ def type_suffix(self) -> str: def __getitem__(self, index: int) -> 'CustomTargetIndex': return CustomTargetIndex(self, self.outputs[index]) - def __setitem__(self, index, value): + def __setitem__(self, index: T.Any, value: T.Any) -> T.Any: raise NotImplementedError - def __delitem__(self, index): + def __delitem__(self, index: T.Any) -> T.Any: raise NotImplementedError - def __iter__(self): + def __iter__(self) -> T.Iterator[CustomTargetIndex]: for i in self.outputs: yield CustomTargetIndex(self, i) @@ -3341,7 +3341,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, + objects: T.List[ObjectTypes], environment: Environment, compilers: CompilerDict, kwargs: JarKeywordArguments): super().__init__(name, subdir, subproject, for_machine, sources, structured_sources, objects, environment, compilers, kwargs) @@ -3431,7 +3431,7 @@ def has_custom_install_dir(self) -> bool: def install_dir(self) -> T.List[T.Union[str, Literal[False]]]: return [self.target.install_dir[self.__index]] - def __repr__(self): + def __repr__(self) -> str: return ''.format(self.target, self.output) def get_outputs(self) -> T.List[str]: @@ -3520,7 +3520,7 @@ def __bool__(self) -> bool: def get(self, name: str) -> T.Tuple[T.Union[str, int, bool], T.Optional[str]]: return self.values[name] # (val, desc) - def keys(self) -> T.Iterator[str]: + def keys(self) -> T.Iterable[str]: return self.values.keys() class LocalProgram(programs.Program): @@ -3618,7 +3618,7 @@ def get_sources_string_names(sources: T.Sequence[CustomTargetSources], backend: For the specified list of @sources which can be strings, Files, ExternalPrograms, or targets, get all the output basenames. ''' - names = [] + names: list[str] = [] for s in sources: if isinstance(s, (BuildTarget, CustomTarget, CustomTargetIndex, GeneratedList)): names += s.get_outputs() From 8bbc8e8c6cd2be4caff344c60edc689a5be98e9a Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 6 May 2026 12:58:30 -0700 Subject: [PATCH 148/681] build: fix BuildTarget.get method to match other classes It needs the `recursive` keyword added. --- mesonbuild/build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 23efd94f5069..37814c059276 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -1883,7 +1883,7 @@ def extract_targets_as_list(self, kwargs: BuildTargetKeywordArguments, key: T.Li lib_list.append(lib) return lib_list - def get(self, lib_type: T.Literal['static', 'shared']) -> LibTypes: + def get(self, lib_type: T.Literal['static', 'shared'], recursive: bool = False) -> LibTypes: """Base case used by BothLibraries""" return self From a25874cab5ea5ca9a61372a2cbed8e0a7b7d7c24 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 6 May 2026 12:59:11 -0700 Subject: [PATCH 149/681] build: Add asserts for option values --- mesonbuild/build.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 37814c059276..062e93c2c30c 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2623,6 +2623,7 @@ def determine_naming_info(self) -> T.Tuple[str, str, str, str, bool]: import_filename_tpl = '{0.prefix}{0.name}.dll.lib' # .pdb file is only created when debug symbols are enabled create_debug_file = self.environment.coredata.optstore.get_value_for(OptionKey("debug")) + assert isinstance(create_debug_file, bool), 'for mypy' elif self.get_using_msvc(): # Shared library is of the form foo.dll prefix = prefix if prefix is not None else '' @@ -2631,6 +2632,7 @@ def determine_naming_info(self) -> T.Tuple[str, str, str, str, bool]: import_filename_tpl = '{0.prefix}{0.name}.' + import_suffix # .pdb file is only created when debug symbols are enabled create_debug_file = self.environment.coredata.optstore.get_value_for(OptionKey("debug")) + assert isinstance(create_debug_file, bool), 'for mypy' # Assume GCC-compatible naming else: # Shared library is of the form libfoo.dll From 73f48986806f9a374e8a963ea591a0b287aef4a2 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 6 May 2026 12:59:28 -0700 Subject: [PATCH 150/681] build: cast between SharedLibrary and SharedModule keywords in inheritance I don't know why mypy can't figure this out, but it can't --- mesonbuild/build.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 062e93c2c30c..1250f7a0e865 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2876,7 +2876,9 @@ def __init__( if 'soversion' in kwargs: raise MesonException('Shared modules must not specify the soversion kwarg.') super().__init__(name, subdir, subproject, for_machine, sources, - structured_sources, objects, environment, compilers, kwargs) + structured_sources, objects, environment, compilers, + # SharedModuleKeywordArguments is a subclass, it's annoying mypy can't figure this out + T.cast('SharedLibraryKeywordArguments', kwargs)) # We need to set the soname in cases where build files link the module # to build targets, see: https://github.com/mesonbuild/meson/issues/9492 self.force_soname = False From f91763da7812813f5c3636ea5ddb6ab25aa2b597 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 6 May 2026 13:01:21 -0700 Subject: [PATCH 151/681] build: use SubProject correctly --- mesonbuild/build.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 1250f7a0e865..4ca3607415c6 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -264,7 +264,7 @@ class Headers(HoldableObject): install_subdir: T.Optional[str] custom_install_dir: T.Optional[str] custom_install_mode: 'FileMode' - subproject: str + subproject: SubProject follow_symlinks: T.Optional[bool] = None install_tag: T.Optional[str] = None @@ -296,7 +296,7 @@ class Man(HoldableObject): sources: T.List[File] custom_install_dir: T.Optional[str] custom_install_mode: 'FileMode' - subproject: str + subproject: SubProject locale: T.Optional[str] install_tag: T.Optional[str] = None @@ -314,7 +314,7 @@ def get_sources(self) -> T.List['File']: class EmptyDir(HoldableObject): path: str install_mode: 'FileMode' - subproject: str + subproject: SubProject install_tag: T.Optional[str] = None @@ -327,7 +327,7 @@ class InstallDir(HoldableObject): install_mode: 'FileMode' exclude: T.Tuple[T.Set[str], T.Set[str]] strip_directory: bool - subproject: str + subproject: SubProject from_source_dir: bool = True install_tag: T.Optional[str] = None follow_symlinks: T.Optional[bool] = None @@ -337,7 +337,7 @@ class DepManifest: version: str license: T.List[str] license_files: T.List[T.Tuple[str, File]] - subproject: str + subproject: SubProject def license_mapping(self) -> T.List[T.Tuple[str, str]]: ret = [] @@ -2921,7 +2921,7 @@ class CommandBase: depend_files: T.List[File] dependencies: T.List[T.Union[BuildTarget, 'CustomTarget']] - subproject: str + subproject: SubProject def flatten_command(self, cmd: T.Sequence[T.Union[str, File, programs.Program, BuildTargetTypes]]) -> \ T.List[T.Union[str, File, BuildTarget, CustomTarget, programs.Program]]: @@ -2992,7 +2992,7 @@ class CustomTarget(Target, CustomTargetBase, CommandBase): def __init__(self, name: T.Optional[str], subdir: str, - subproject: str, + subproject: SubProject, environment: Environment, command: T.Sequence[T.Union[ str, BuildTargetTypes, @@ -3225,7 +3225,7 @@ class CompileTarget(BuildTarget): def __init__(self, name: str, subdir: str, - subproject: str, + subproject: SubProject, environment: Environment, sources: T.List['SourceOutputs'], output_templ: str, @@ -3282,7 +3282,7 @@ def __init__(self, name: str, # the RunTarget case is used by gnome.yelp() dependencies: T.Sequence[T.Union[RunTarget, TargetDepends]], subdir: str, - subproject: str, + subproject: SubProject, environment: Environment, env: T.Optional[EnvironmentVariables] = None, default_env: bool = True): @@ -3330,7 +3330,7 @@ class AliasTarget(RunTarget): typename = 'alias' def __init__(self, name: str, dependencies: T.Sequence[Target], - subdir: str, subproject: str, environment: Environment): + subdir: str, subproject: SubProject, environment: Environment): super().__init__(name, [], dependencies, subdir, subproject, environment) def __repr__(self) -> str: @@ -3343,7 +3343,7 @@ class Jar(BuildTarget): typename = 'jar' rust_crate_type = '' # type: ignore[assignment] - def __init__(self, name: str, subdir: str, subproject: str, for_machine: MachineChoice, + def __init__(self, name: str, subdir: str, subproject: SubProject, for_machine: MachineChoice, sources: T.List[SourceOutputs], structured_sources: T.Optional['StructuredSources'], objects: T.List[ObjectTypes], environment: Environment, compilers: CompilerDict, kwargs: JarKeywordArguments): @@ -3585,7 +3585,7 @@ class Data(HoldableObject): install_dir: str install_dir_name: str install_mode: 'FileMode' - subproject: str + subproject: SubProject rename: T.List[str] = None install_tag: T.Optional[str] = None data_type: str = None @@ -3600,7 +3600,7 @@ class SymlinkData(HoldableObject): target: str name: str install_dir: str - subproject: str + subproject: SubProject install_tag: T.Optional[str] = None def __post_init__(self) -> None: From d86e5aa31226fc6caf91834961ca286224657a65 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 4 May 2026 11:32:16 -0700 Subject: [PATCH 152/681] build: work around mypy's inability to correctly deduce union typedict values --- mesonbuild/build.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 4ca3607415c6..f73516df332b 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -1460,13 +1460,16 @@ def _extract_pic_pie(self, kwargs: T.Union[StaticLibraryKeywordArguments, Execut mlog.warning(f"Use the '{arg}' kwarg instead of passing '-f{arg}' manually to {self.name!r}") return True + if (a := kwargs.get(arg)) is not None: + assert isinstance(a, bool), 'for mypy' + return a + k = OptionKey(option) - if kwargs.get(arg) is not None: - return kwargs[arg] - elif k in self.environment.coredata.optstore: + if k in self.environment.coredata.optstore: val = self.environment.coredata.get_option_for_target(self, k) assert isinstance(val, bool), 'for mypy' return val + return False def install_dir_names(self) -> T.List[T.Optional[str]]: From 63aa6c1cfc8643f1d84752724641963f1f3073ab Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 4 May 2026 11:36:40 -0700 Subject: [PATCH 153/681] build: move `get_override()` to BuildTarget It's only correctly implemented in BuildTarget, and it's only used from BuildTarget --- mesonbuild/build.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index f73516df332b..3267086265ac 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -786,9 +786,6 @@ def id(self) -> str: def get_id(self) -> str: return self.id - def get_override(self, name: str) -> T.Optional[ElementaryOptionValues]: - return self.raw_overrides.get(name, None) - def is_linkable_target(self) -> bool: return False @@ -1366,6 +1363,9 @@ def get_custom_install_dir(self) -> T.List[T.Union[str, Literal[False]]]: def get_custom_install_mode(self) -> T.Optional['FileMode']: return self.install_mode + def get_override(self, name: str) -> T.Optional[ElementaryOptionValues]: + return self.raw_overrides.get(name, None) + def process_kwargs(self, kwargs: BuildTargetKeywordArguments) -> None: self.original_kwargs = kwargs From 387fcf8f8e2159d18f6ec01154384a49631b07d9 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 4 May 2026 12:11:31 -0700 Subject: [PATCH 154/681] compilers: Add stub implementations for some Rust methods --- mesonbuild/compilers/compilers.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index f31e4a108240..3b780074d227 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -1688,3 +1688,11 @@ def _update_language_stds(self, opts: MutableKeyedOptionDictType, value: T.List[ if 'none' not in value: value = ['none'] + value std.choices = value + + def get_crt_static(self) -> bool: + """Is this target using static CRT?""" + raise EnvironmentException(f'{self.get_id()} does not support static CRT') + + def get_target_libdir(self) -> str: + """Where is the libdir for the current machine located""" + raise EnvironmentException(f'{self.get_id()} does not support Rust target libdir') From 370be0a675a63c2696a57dfabed3186693dac860 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 4 May 2026 12:12:41 -0700 Subject: [PATCH 155/681] build: Cast compiler to RustCompiler to fix attribute lookup We know it's a Rust Compiler, so we'll just cast. I've looked into more explicit tping for these dictionaries and it's painful. --- mesonbuild/build.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 3267086265ac..05285fc90ff1 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -44,6 +44,7 @@ from ._typing import ImmutableListProtocol from .backend.backends import Backend from .compilers.compilers import Compiler, CompilerDict, Language + from .compilers.rust import RustCompiler from .interpreter.interpreter import CustomTargetSources, SourceOutputs, Interpreter from .interpreter.interpreterobjects import Test, Doctest from .interpreter.kwargs import TargetDepends @@ -2386,7 +2387,7 @@ def post_init(self) -> None: # In the case of no-std, we are likely in a bare metal case # and thus, machine_info kernel should be set to 'none'. # In that case, native_static_libs list is empty. - rustc = self.compilers['rust'] + rustc = T.cast('RustCompiler', self.compilers['rust']) if rustc.get_crt_static(): # musl targets need self-contained for libc.a, libunwind.a etc. link_args = ['-L' + rustc.get_target_libdir() + '/self-contained'] From 350174527ec33f526874fabce30a441a582b613c Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 4 May 2026 12:43:26 -0700 Subject: [PATCH 156/681] build: add assert to help mypy --- mesonbuild/build.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 05285fc90ff1..1df38f136585 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -1644,6 +1644,7 @@ def get_internal_static_libraries(self) -> OrderedSet[StaticTargetTypes]: def get_internal_static_libraries_recurse(self, result: OrderedSet[StaticTargetTypes]) -> None: for t in self.link_targets: if t.is_internal() and t not in result: + assert isinstance(t, (StaticLibrary, CustomTarget, CustomTargetIndex)), 'for mypy' result.add(t) t.get_internal_static_libraries_recurse(result) for t in self.link_whole_targets: @@ -2495,6 +2496,7 @@ def link(self, targets: T.List[BuildTargetTypes]) -> None: if self.install and t.is_internal(): # When we're a static library and we link_with to an # internal/convenience library, promote to link_whole. + assert isinstance(t, (StaticLibrary, CustomTarget, CustomTargetIndex)), 'for mypy' self.link_whole([t], promoted=True) continue self.check_can_link_together(t) From 2ffa9b55ac4f7006a8cdf76d63e005d99437b192 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 6 May 2026 12:14:40 -0700 Subject: [PATCH 157/681] build: fix CustomTargetBase.get_dependencies_recurse To match the arguments of the BuildTarget version --- mesonbuild/build.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 1df38f136585..959b84acd653 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -1512,7 +1512,7 @@ def get_dependencies(self) -> OrderedSet[BuildTargetTypes]: for t in itertools.chain(self.link_targets, self.link_whole_targets): if t not in result: result.add(t) - if isinstance(t, StaticLibrary): + if isinstance(t, (StaticLibrary, CustomTarget, CustomTargetIndex)): t.get_dependencies_recurse(result, visited, handled_by_rustc=self.uses_rust()) return result @@ -2974,8 +2974,10 @@ class CustomTargetBase: rust_crate_type = '' - def get_dependencies_recurse(self, result: OrderedSet[BuildTargetTypes], visited: T.Set[BuildTargetTypes], - include_internals: bool = True) -> None: + def get_dependencies_recurse(self, result: OrderedSet[BuildTargetTypes], + visited: T.Set[tuple[BuildTargetTypes, bool, bool]], + include_internals: bool = True, + handled_by_rustc: bool = False) -> None: pass def get_internal_static_libraries(self) -> OrderedSet[StaticTargetTypes]: From aa772e12a050c847dada2b87bee48544ef4808c3 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 4 May 2026 12:43:39 -0700 Subject: [PATCH 158/681] build: fix use of object in bool context We need to explicitly cast to bool in this case to avoid returning a Compiler type that resolves to False --- mesonbuild/build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 959b84acd653..cca83f321697 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -1826,7 +1826,7 @@ def get_using_msvc(self) -> bool: else: compiler, _ = self.get_clink_dynamic_linker_and_stdlibs() # Mixing many languages with MSVC is not supported yet so ignore stdlibs. - return compiler and compiler.get_linker_id() in {'link', 'lld-link', 'xilink', 'optlink'} + return bool(compiler) and compiler.get_linker_id() in {'link', 'lld-link', 'xilink', 'optlink'} def check_module_linking(self) -> None: ''' From a6c976da21bb0ca4a6c2a6fb0e5ba246b3b4fee1 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 4 May 2026 13:28:04 -0700 Subject: [PATCH 159/681] utils: overload substitute_values signature We need a signature suitable for inputs (exists) that allows str | Program, and a new one for inputs that only allows str Fixes a significant number of errors in the build module --- mesonbuild/utils/universal.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index 76f260c6aa13..0c71c2033400 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -1865,7 +1865,7 @@ def iter_regexin_iter(regexiter: T.Iterable[str], initer: T.Iterable[str | progr return None -def _substitute_values_check_errors(command: T.List[str | programs.Program], values: T.Dict[str, T.Union[str, T.List[str]]]) -> None: +def _substitute_values_check_errors(command: T.Sequence[str | programs.Program], values: T.Dict[str, T.Union[str, T.List[str]]]) -> None: # Error checking inregex: T.List[str] = ['@INPUT([0-9]+)?@', '@PLAINNAME@', '@BASENAME@'] outregex: T.List[str] = ['@OUTPUT([0-9]+)?@', '@OUTDIR@'] @@ -1905,7 +1905,19 @@ def _substitute_values_check_errors(command: T.List[str | programs.Program], val raise MesonException(m.format(match2.group(), len(values['@OUTPUT@']))) -def substitute_values(command: T.List[str | programs.Program], values: T.Dict[str, T.Union[str, T.List[str]]]) -> T.List[str | programs.Program]: +@T.overload +def substitute_values(command: T.List[str], + values: T.Dict[str, T.Union[str, T.List[str]]] + ) -> T.List[str]: ... + +@T.overload +def substitute_values(command: T.List[str | programs.Program], + values: T.Dict[str, T.Union[str, T.List[str]]] + ) -> T.List[str | programs.Program]: ... + +def substitute_values(command: T.List[str | programs.Program] | T.List[str], + values: T.Dict[str, T.Union[str, T.List[str]]] + ) -> T.List[str | programs.Program] | T.List[str]: ''' Substitute the template strings in the @values dict into the list of strings @command and return a new list. For a full list of the templates, From 989ae6ed6d870a711888445f7a4562a2fbfee796 Mon Sep 17 00:00:00 2001 From: Yelninei Date: Thu, 7 May 2026 09:36:40 +0000 Subject: [PATCH 160/681] generate_dub_file: Don't use deprecated meson variables. --- docs/markdown/Dlang-module.md | 2 +- mesonbuild/templates/dlangtemplates.py | 2 +- test cases/d/11 dub/meson.build | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/markdown/Dlang-module.md b/docs/markdown/Dlang-module.md index 915f3a79e262..ad477611856a 100644 --- a/docs/markdown/Dlang-module.md +++ b/docs/markdown/Dlang-module.md @@ -24,7 +24,7 @@ generate_dub_file("project name", "source/folder", key: "value" ...) **Example** ```meson dlang = import('dlang') -dlang.generate_dub_file(meson.project_name().to_lower(), meson.source_root(), +dlang.generate_dub_file(meson.project_name().to_lower(), meson.project_source_root(), authors: 'Meson Team', description: 'Test executable', copyright: 'Copyright © 2018, Meson Team', diff --git a/mesonbuild/templates/dlangtemplates.py b/mesonbuild/templates/dlangtemplates.py index 727ee209c724..335cbcdd10b2 100644 --- a/mesonbuild/templates/dlangtemplates.py +++ b/mesonbuild/templates/dlangtemplates.py @@ -120,7 +120,7 @@ if find_program('dub', required : false).found() dlang_mod.generate_dub_file( meson.project_name().to_lower(), - meson.source_root(), + meson.project_source_root(), name : meson.project_name(), license : meson.project_license(), sourceFiles : '{source_file}', diff --git a/test cases/d/11 dub/meson.build b/test cases/d/11 dub/meson.build index fe79e046eb78..f0a4b1caad0c 100644 --- a/test cases/d/11 dub/meson.build +++ b/test cases/d/11 dub/meson.build @@ -17,7 +17,7 @@ test('test urld', test_exe) # If you want meson to generate/update a dub.json file dlang = import('dlang') -dlang.generate_dub_file(meson.project_name().to_lower(), meson.build_root(), +dlang.generate_dub_file(meson.project_name().to_lower(), meson.project_build_root(), authors: 'Meson Team', description: 'Test executable', copyright: 'Copyright © 2018, Meson Team', From 445a4325be98e5abbb8bc5bf088576e2a75f7c32 Mon Sep 17 00:00:00 2001 From: Yelninei Date: Thu, 7 May 2026 09:45:28 +0000 Subject: [PATCH 161/681] generate_dub_file: Compatibility with dub >= 1.41.0. --- docs/markdown/Dlang-module.md | 2 +- mesonbuild/templates/dlangtemplates.py | 4 ++-- test cases/d/11 dub/meson.build | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/markdown/Dlang-module.md b/docs/markdown/Dlang-module.md index ad477611856a..1ef3983b8070 100644 --- a/docs/markdown/Dlang-module.md +++ b/docs/markdown/Dlang-module.md @@ -29,7 +29,7 @@ dlang.generate_dub_file(meson.project_name().to_lower(), meson.project_source_ro description: 'Test executable', copyright: 'Copyright © 2018, Meson Team', license: 'MIT', - sourceFiles: 'test.d', + sourceFiles: ['test.d'], targetType: 'executable', dependencies: my_dep ) diff --git a/mesonbuild/templates/dlangtemplates.py b/mesonbuild/templates/dlangtemplates.py index 335cbcdd10b2..8bbeb978b7c4 100644 --- a/mesonbuild/templates/dlangtemplates.py +++ b/mesonbuild/templates/dlangtemplates.py @@ -122,8 +122,8 @@ meson.project_name().to_lower(), meson.project_source_root(), name : meson.project_name(), - license : meson.project_license(), - sourceFiles : '{source_file}', + license : meson.project_license()[0], + sourceFiles : ['{source_file}'], description : 'Meson sample project.', version : '{version}', ) diff --git a/test cases/d/11 dub/meson.build b/test cases/d/11 dub/meson.build index f0a4b1caad0c..0fe1a3948c45 100644 --- a/test cases/d/11 dub/meson.build +++ b/test cases/d/11 dub/meson.build @@ -22,7 +22,7 @@ dlang.generate_dub_file(meson.project_name().to_lower(), meson.project_build_roo description: 'Test executable', copyright: 'Copyright © 2018, Meson Team', license: 'MIT', - sourceFiles: 'test.d', + sourceFiles: ['test.d'], targetType: 'executable', dependencies: urld_dep ) From 85dc9a74fdcfe43bd4d86eeb3d3dbe17b55c5167 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 8 May 2026 11:03:14 +0200 Subject: [PATCH 162/681] ninjabackend: create TypedDict for (subset of) arguments to NinjaRule.__init__ Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index b6ab6b870fbb..136f871830f6 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -38,7 +38,7 @@ from ..build import GeneratedList, InvalidArguments if T.TYPE_CHECKING: - from typing_extensions import Literal + from typing_extensions import Literal, TypedDict from .._typing import ImmutableListProtocol from ..build import ExtractedObjects, LibTypes @@ -54,6 +54,11 @@ CommandArgOrStr = T.List[T.Union['NinjaCommandArg', str]] RUST_EDITIONS = Literal['2015', '2018', '2021'] + class NinjaRuleArgs(TypedDict, total=False): + rspable: bool + rspfile_quote_style: RSPFileSyntax + extra: T.Optional[str] + FORTRAN_INCLUDE_PAT = r"^\s*#?include\s*['\"](\w+\.\w+)['\"]" FORTRAN_MODULE_PAT = r"^\s*\bmodule\b\s+(\w+)\s*(?:!+.*)*$" @@ -2433,13 +2438,13 @@ def generate_swift_target(self, target: build.BuildTarget) -> None: # Introspection information self.create_target_source_introspection(target, swiftc, compile_args + header_imports + module_includes, relsrc, rel_generated) - def _rsp_options(self, tool: T.Union['Compiler', 'StaticLinker', 'DynamicLinker']) -> T.Dict[str, T.Union[bool, RSPFileSyntax]]: + def _rsp_options(self, tool: T.Union['Compiler', 'StaticLinker', 'DynamicLinker']) -> NinjaRuleArgs: """Helper method to get rsp options. rsp_file_syntax() is only guaranteed to be implemented if can_linker_accept_rsp() returns True. """ - options = {'rspable': tool.can_linker_accept_rsp()} + options: NinjaRuleArgs = {'rspable': tool.can_linker_accept_rsp()} if options['rspable']: options['rspfile_quote_style'] = tool.rsp_file_syntax() return options From ff5ed1a271f7e82f659a84cca8515e518656aca7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 8 May 2026 12:17:46 +0200 Subject: [PATCH 163/681] linkers: export StaticLinker from mesonbuild.linkers Signed-off-by: Paolo Bonzini --- mesonbuild/arglist.py | 2 +- mesonbuild/backend/backends.py | 2 +- mesonbuild/build.py | 2 +- mesonbuild/linkers/__init__.py | 4 ++++ test cases/unit/116 empty project/expected_mods.json | 3 ++- 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/mesonbuild/arglist.py b/mesonbuild/arglist.py index 4f4d18c55485..a84c54f1f03e 100644 --- a/mesonbuild/arglist.py +++ b/mesonbuild/arglist.py @@ -12,7 +12,7 @@ import typing as T if T.TYPE_CHECKING: - from .linkers.linkers import StaticLinker + from .linkers import StaticLinker from .compilers import Compiler # execinfo is a compiler lib on BSD diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 76b4ef309c4e..440df274d253 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -41,7 +41,7 @@ from ..compilers.compilers import Compiler, Language from ..environment import Environment from ..interpreter import Test - from ..linkers.linkers import StaticLinker + from ..linkers import StaticLinker from ..mesonlib import FileMode, FileOrString from ..options import ElementaryOptionValues diff --git a/mesonbuild/build.py b/mesonbuild/build.py index cca83f321697..7d6bae6b02e1 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -48,7 +48,7 @@ from .interpreter.interpreter import CustomTargetSources, SourceOutputs, Interpreter from .interpreter.interpreterobjects import Test, Doctest from .interpreter.kwargs import TargetDepends - from .linkers.linkers import StaticLinker + from .linkers import StaticLinker from .mesonlib import ExecutableSerialisation, FileMode, FileOrString from .mparser import BaseNode from .options import ElementaryOptionValues diff --git a/mesonbuild/linkers/__init__.py b/mesonbuild/linkers/__init__.py index 356a43d4cd1f..5f010a2c0a07 100644 --- a/mesonbuild/linkers/__init__.py +++ b/mesonbuild/linkers/__init__.py @@ -7,6 +7,7 @@ guess_win_linker, guess_nix_linker, ) +from .linkers import StaticLinker __all__ = [ # base.py @@ -17,4 +18,7 @@ 'defaults', 'guess_win_linker', 'guess_nix_linker', + + # linkers.py + 'StaticLinker', ] diff --git a/test cases/unit/116 empty project/expected_mods.json b/test cases/unit/116 empty project/expected_mods.json index e479bcbb75dd..678fea94a9f8 100644 --- a/test cases/unit/116 empty project/expected_mods.json +++ b/test cases/unit/116 empty project/expected_mods.json @@ -218,6 +218,7 @@ "mesonbuild.linkers", "mesonbuild.linkers.base", "mesonbuild.linkers.detect", + "mesonbuild.linkers.linkers", "mesonbuild.machinefile", "mesonbuild.mesonlib", "mesonbuild.mesonmain", @@ -240,6 +241,6 @@ "mesonbuild.wrap", "mesonbuild.wrap.wrap" ], - "count": 71 + "count": 72 } } From 5eddad31abe1e66c96cc1dfce62d0508e639ae83 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 8 May 2026 11:09:34 +0200 Subject: [PATCH 164/681] ninjabackend: differentiate between StaticLinker and Compiler Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 63 ++++++++++++++++-------------- mesonbuild/linkers/linkers.py | 3 +- 2 files changed, 35 insertions(+), 31 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 136f871830f6..4de591337527 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -27,7 +27,7 @@ from .. import tooldetect from ..arglist import CompilerArgs from ..compilers import Compiler, is_library -from ..linkers import ArLikeLinker, RSPFileSyntax +from ..linkers import ArLikeLinker, RSPFileSyntax, StaticLinker from ..mesonlib import ( File, LibType, MachineChoice, MesonBugException, MesonException, OrderedSet, PerMachine, ProgressBar, quote_arg, unique_list @@ -42,7 +42,6 @@ from .._typing import ImmutableListProtocol from ..build import ExtractedObjects, LibTypes - from ..linkers.linkers import DynamicLinker, StaticLinker from ..compilers.compilers import Language from ..compilers.cs import CsCompiler from ..compilers.fortran import FortranCompiler @@ -2438,7 +2437,7 @@ def generate_swift_target(self, target: build.BuildTarget) -> None: # Introspection information self.create_target_source_introspection(target, swiftc, compile_args + header_imports + module_includes, relsrc, rel_generated) - def _rsp_options(self, tool: T.Union['Compiler', 'StaticLinker', 'DynamicLinker']) -> NinjaRuleArgs: + def _rsp_options(self, tool: T.Union['Compiler', 'StaticLinker']) -> NinjaRuleArgs: """Helper method to get rsp options. rsp_file_syntax() is only guaranteed to be implemented if @@ -3550,7 +3549,12 @@ def generate_shsym(self, target) -> None: def get_import_filename(self, target) -> str: return os.path.join(self.get_target_dir(target), target.import_filename) - def get_target_type_link_args(self, target: build.BuildTarget, linker: Compiler): + def get_target_type_link_args(self, target: build.BuildTarget, linker: T.Union[StaticLinker, Compiler]) -> T.List[str]: + if isinstance(target, build.StaticLibrary): + produce_thin_archive = self.allow_thin_archives[target.for_machine] and not target.should_install() + return linker.get_std_link_args(self.environment, produce_thin_archive) + + assert isinstance(linker, Compiler) commands: T.List[str] = [] if isinstance(target, build.Executable): # Currently only used with the Swift compiler to add '-emit-executable' @@ -3582,9 +3586,6 @@ def get_target_type_link_args(self, target: build.BuildTarget, linker: Compiler) # This is only visited when building for Windows using either GCC or Visual Studio if target.import_filename: commands += linker.gen_import_library_args(self.get_import_filename(target)) - elif isinstance(target, build.StaticLibrary): - produce_thin_archive = self.allow_thin_archives[target.for_machine] and not target.should_install() - commands += linker.get_std_link_args(self.environment, produce_thin_archive) else: raise RuntimeError('Unknown build target type.') return commands @@ -3601,7 +3602,7 @@ def get_target_type_link_args_post_dependencies(self, target, linker): commands += linker.get_win_subsystem_args(target.win_subsystem) return commands - def get_link_whole_args(self, linker: DynamicLinker, target): + def get_link_whole_args(self, linker: Compiler, target): use_custom = False if linker.id == 'msvc': # Expand our object lists manually if we are on pre-Visual Studio 2015 Update 2 @@ -3744,6 +3745,7 @@ def generate_link(self, target: build.BuildTarget, outname, obj_list, linker: T. if isinstance(target, build.StaticLibrary): linker_base = 'STATIC' else: + assert isinstance(linker, Compiler) linker_base = linker.get_language() # Fixme. if isinstance(target, build.SharedLibrary) and self.environment.machines[target.for_machine].is_os2(): target_file = self.get_target_filename(target) @@ -3765,8 +3767,10 @@ def generate_link(self, target: build.BuildTarget, outname, obj_list, linker: T. # options passed on the command-line, in default_options, etc. # These have the lowest priority. if isinstance(target, build.StaticLibrary): + assert isinstance(linker, StaticLinker) base_link_args = linker.get_base_link_args(target, linker, self.environment) else: + assert isinstance(linker, Compiler) base_link_args = compilers.get_base_link_args(target, linker, self.environment) @@ -3786,12 +3790,11 @@ def generate_link(self, target: build.BuildTarget, outname, obj_list, linker: T. # Add link args specific to this BuildTarget type, such as soname args, # PIC, import library generation, etc. commands += self.get_target_type_link_args(target, linker) - # Archives that are copied wholesale in the result. Must be before any - # other link targets so missing symbols from whole archives are found in those. if not isinstance(target, build.StaticLibrary): + assert isinstance(linker, Compiler) + # Archives that are copied wholesale in the result. Must be before any + # other link targets so missing symbols from whole archives are found in those. commands += self.get_link_whole_args(linker, target) - - if not isinstance(target, build.StaticLibrary): commands += linker.get_build_link_args(target, self.build) # Now we will add libraries and library paths from various sources @@ -3802,20 +3805,22 @@ def generate_link(self, target: build.BuildTarget, outname, obj_list, linker: T. # Add link args to link to all internal libraries (link_with:) and # internal dependencies needed by this target. - if linker_base == 'STATIC': + dep_targets = [] + if isinstance(target, build.StaticLibrary): # Link arguments of static libraries are not put in the command # line of the library. They are instead appended to the command # line where the static library is used. dependencies = [] else: + # Only non-static built targets need link args and link dependencies + assert isinstance(linker, Compiler) + dependencies = target.get_dependencies() - internal = self.build_target_link_arguments(linker, dependencies) - commands += internal - # Only non-static built targets need link args and link dependencies - if not isinstance(target, build.StaticLibrary): + internal = self.build_target_link_arguments(linker, dependencies) + commands += internal + # For 'automagic' deps: Boost and GTest. Also dependency('threads'). # pkg-config puts the thread flags itself via `Cflags:` - commands += linker.get_target_link_args(target) # External deps must be last because target link libraries may depend on them. for dep in target.get_external_deps(): @@ -3835,15 +3840,15 @@ def generate_link(self, target: build.BuildTarget, outname, obj_list, linker: T. link_args = VisualStudioLikeLinker.native_args_to_unix(link_args) commands.extend_preserving_lflags(link_args) - # Add link args specific to this BuildTarget type that must not be overridden by dependencies - commands += self.get_target_type_link_args_post_dependencies(target, linker) - - # Add link args for c_* or cpp_* build options. Currently this only - # adds c_winlibs and cpp_winlibs when building for Windows. This needs - # to be after all internal and external libraries so that unresolved - # symbols from those can be found here. This is needed when the - # *_winlibs that we want to link to are static mingw64 libraries. - if isinstance(linker, Compiler): + # Add link args specific to this BuildTarget type that must not be overridden by dependencies + commands += self.get_target_type_link_args_post_dependencies(target, linker) + + # Add link args for c_* or cpp_* build options. Currently this only + # adds c_winlibs and cpp_winlibs when building for Windows. This needs + # to be after all internal and external libraries so that unresolved + # symbols from those can be found here. This is needed when the + # *_winlibs that we want to link to are static mingw64 libraries. + # # The static linker doesn't know what language it is building, so we # don't know what option. Fortunately, it doesn't care to see the # language-specific options either. @@ -3852,8 +3857,7 @@ def generate_link(self, target: build.BuildTarget, outname, obj_list, linker: T. # in the LTO case we do use a real compiler here. commands += linker.get_option_link_args(target) - dep_targets = [] - dep_targets.extend(self.guess_external_link_dependencies(linker, target, commands, internal)) + dep_targets.extend(self.guess_external_link_dependencies(linker, target, commands, internal)) obj_list += self.get_import_std_object(target) @@ -3882,6 +3886,7 @@ def generate_link(self, target: build.BuildTarget, outname, obj_list, linker: T. # Add early arguments before any object files or libraries if not isinstance(target, build.StaticLibrary): + assert isinstance(linker, Compiler) compile_args += linker.get_target_link_early_args(target) if compile_args: elem.add_item('ARGS', compile_args) diff --git a/mesonbuild/linkers/linkers.py b/mesonbuild/linkers/linkers.py index 19c419c4a05c..f6f35bf61a72 100644 --- a/mesonbuild/linkers/linkers.py +++ b/mesonbuild/linkers/linkers.py @@ -18,7 +18,6 @@ from ..environment import Environment from ..mesonlib import MachineChoice from ..build import BuildTarget - from ..compilers import Compiler from ..compilers.compilers import LinkerOptionStyle @@ -47,7 +46,7 @@ def can_linker_accept_rsp(self) -> bool: def get_base_link_args(self, target: 'BuildTarget', - linker: 'Compiler', + linker: StaticLinker, env: 'Environment') -> T.List[str]: """Like compilers.get_base_link_args, but for the static linker.""" return [] From 1ba81064ba5ad16564ac8ede792cbd95ac731611 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 8 May 2026 11:17:02 +0200 Subject: [PATCH 165/681] ninjabackend: fix typing issues with Rust Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 4de591337527..dae55437ba03 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -2052,8 +2052,8 @@ def generate_rust_sources(self, target: build.BuildTarget) -> T.Tuple[T.List[str return orderdeps, main_rust_file - def get_rust_compiler_args(self, target: build.BuildTarget, rustc: Compiler, src_crate_type: str, - depfile: T.Optional[str] = None) -> T.List[str]: + def get_rust_compiler_args(self, target: build.BuildTarget, rustc: RustCompiler, src_crate_type: str, + depfile: T.Optional[str] = None) -> CompilerArgs: # Compiler args for compiling this target args = compilers.get_base_compile_args(target, rustc, self.environment) @@ -2071,16 +2071,16 @@ def get_rust_compiler_args(self, target: build.BuildTarget, rustc: Compiler, src args += rustc.get_build_link_args(target, self.build) args += rustc.get_target_link_args(target) - args += self.generate_basic_compiler_args(target, rustc) - args += ['--crate-name', self._get_rust_crate_name(target.name)] + cargs = args + self.generate_basic_compiler_args(target, rustc) + cargs += ['--crate-name', self._get_rust_crate_name(target.name)] if depfile: - args += rustc.get_dependency_gen_args(target_name, depfile) - args += rustc.get_output_args(target_name) - args += ['-C', 'metadata=' + target.get_id()] - args += target.get_extra_args('rust') - return args + cargs += rustc.get_dependency_gen_args(target_name, depfile) + cargs += rustc.get_output_args(target_name) + cargs += ['-C', 'metadata=' + target.get_id()] + cargs += target.get_extra_args('rust') + return cargs - def get_rust_compiler_deps_and_args(self, target: build.BuildTarget, rustc: Compiler, + def get_rust_compiler_deps_and_args(self, target: build.BuildTarget, rustc: RustCompiler, obj_list: T.List[str]) -> T.Tuple[T.List[str], T.List[RustDep], T.List[str]]: deps: T.List[str] = [] project_deps: T.List[RustDep] = [] @@ -2272,8 +2272,8 @@ def generate_rust_target(self, target: build.BuildTarget, target_name: str, obj_ if target.doctests: assert target.doctests.target is not None rustdoc = rustc.get_rustdoc() - args = rustdoc.get_exe_args() - args += self.get_rust_compiler_args(target.doctests.target, rustdoc, target.rust_crate_type) + args = rustdoc.get_exe_args() + \ + self.get_rust_compiler_args(target.doctests.target, rustdoc, target.rust_crate_type) o, _ = self.flatten_object_list(target.doctests.target) obj_list = unique_list(obj_list + o) # Rustc does not add files in the obj_list to Rust rlibs, From d4ca781eedfa37a1632fc6fcaa12f7608de946a9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 8 May 2026 11:03:58 +0200 Subject: [PATCH 166/681] ninjabackend: fake covariance for CommandArgOrStr Allow passing a T.List[str], but unlike T.Sequence[] do not allow a single string. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index dae55437ba03..c7f39bf1826b 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -50,7 +50,8 @@ from ..mesonlib import FileOrString from .backends import TargetIntrospectionData - CommandArgOrStr = T.List[T.Union['NinjaCommandArg', str]] + CommandArgTypes = T.TypeVar('CommandArgTypes', 'NinjaCommandArg', str, 'NinjaCommandArg | str') + CommandArgs = T.List[CommandArgTypes] RUST_EDITIONS = Literal['2015', '2018', '2021'] class NinjaRuleArgs(TypedDict, total=False): @@ -174,7 +175,7 @@ def write(self, outfile: T.TextIO) -> None: outfile.write('\n') class NinjaRule: - def __init__(self, rule: str, command: CommandArgOrStr, args: CommandArgOrStr, + def __init__(self, rule: str, command: CommandArgs, args: CommandArgs, description: str, rspable: bool = False, deps: T.Optional[str] = None, depfile: T.Optional[str] = None, extra: T.Optional[str] = None, rspfile_quote_style: RSPFileSyntax = RSPFileSyntax.GCC): From d95c4a2541a08f0c0006148cfb83fda3d8a69bee Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 8 May 2026 11:04:29 +0200 Subject: [PATCH 167/681] ninjabackend: add or fix annotations for NinjaBuildElement Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index c7f39bf1826b..727af6dc21de 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -52,6 +52,7 @@ CommandArgTypes = T.TypeVar('CommandArgTypes', 'NinjaCommandArg', str, 'NinjaCommandArg | str') CommandArgs = T.List[CommandArgTypes] + ListifiedStr = str | T.List[str] RUST_EDITIONS = Literal['2015', '2018', '2021'] class NinjaRuleArgs(TypedDict, total=False): @@ -302,7 +303,7 @@ class NinjaBuildElement: rule: NinjaRule - def __init__(self, all_outputs: T.Set[str], outfilenames, rulename, infilenames, implicit_outs=None): + def __init__(self, all_outputs: T.Set[str], outfilenames: ListifiedStr, rulename: str, infilenames: ListifiedStr, implicit_outs: T.Optional[T.List[str]] = None): self.implicit_outfilenames = implicit_outs or [] if isinstance(outfilenames, str): self.outfilenames = [outfilenames] @@ -314,25 +315,25 @@ def __init__(self, all_outputs: T.Set[str], outfilenames, rulename, infilenames, self.infilenames = [infilenames] else: self.infilenames = infilenames - self.deps = set() - self.orderdeps = set() - self.elems = [] + self.deps: T.Set[str] = set() + self.orderdeps: T.Set[str] = set() + self.elems: T.List[T.Tuple[str, T.List[str]]] = [] self.all_outputs = all_outputs self.output_errors = '' - def add_dep(self, dep: T.Union[str, T.List[str]]) -> None: + def add_dep(self, dep: ListifiedStr) -> None: if isinstance(dep, list): self.deps.update(dep) else: self.deps.add(dep) - def add_orderdep(self, dep) -> None: + def add_orderdep(self, dep: ListifiedStr) -> None: if isinstance(dep, list): self.orderdeps.update(dep) else: self.orderdeps.add(dep) - def add_item(self, name: str, elems: T.Union[str, T.List[str], CompilerArgs]) -> None: + def add_item(self, name: str, elems: T.Union[ListifiedStr, CompilerArgs]) -> None: # Always convert from GCC-style argument naming to the naming used by the # current compiler. Also filter system include paths, deduplicate, etc. if isinstance(elems, CompilerArgs): @@ -521,7 +522,7 @@ def __init__(self, build: T.Optional[build.Build]): self.allow_thin_archives = PerMachine[bool](True, True) self.import_std: T.Optional[ImportStdInfo] = None - def create_phony_target(self, dummy_outfile: str, rulename: str, phony_infilename: str) -> NinjaBuildElement: + def create_phony_target(self, dummy_outfile: str, rulename: str, phony_infilenames: ListifiedStr) -> NinjaBuildElement: ''' We need to use aliases for targets that might be used as directory names to workaround a Ninja bug that breaks `ninja -t clean`. @@ -536,7 +537,7 @@ def create_phony_target(self, dummy_outfile: str, rulename: str, phony_infilenam elem = NinjaBuildElement(self.all_outputs, dummy_outfile, 'phony', to_name) self.add_build(elem) - return NinjaBuildElement(self.all_outputs, to_name, rulename, phony_infilename) + return NinjaBuildElement(self.all_outputs, to_name, rulename, phony_infilenames) def detect_vs_dep_prefix(self, tempfilename: str) -> T.TextIO: '''VS writes its dependency in a locale dependent format. From 614c14db3eb0653fcdd65621bc1b9465caf55de6 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 8 May 2026 11:31:15 +0200 Subject: [PATCH 168/681] ninjabackend: cast to compiler subclasses Do not use assertions to avoid failures in test_setup_loaded_modules. --- mesonbuild/backend/ninjabackend.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 727af6dc21de..39003c235465 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -2692,7 +2692,7 @@ def generate_compile_rule_for(self, langname: str, compiler: Compiler) -> None: return if langname == 'cs': if self.environment.machines.matches_build_machine(compiler.for_machine): - self.generate_cs_compile_rule(compiler) + self.generate_cs_compile_rule(T.cast('CsCompiler', compiler)) return if langname == 'vala': self.generate_vala_compile_rules(compiler) @@ -3271,7 +3271,8 @@ def generate_single_compile(self, target: build.BuildTarget, src, # https://github.com/mesonbuild/meson/issues/1348 if not is_generated: abs_src = Path(build_dir) / rel_src - extra_deps += self.get_fortran_deps(compiler, abs_src, target) + extra_deps += self.get_fortran_deps(T.cast('FortranCompiler', compiler), + abs_src, target) if not self.use_dyndeps_for_fortran(): # Dependency hack. Remove once multiple outputs in Ninja is fixed: # https://groups.google.com/forum/#!topic/ninja-build/j-2RfBIOd_8 From e959c09247066aec39ff16821f2ec468aeedb8f3 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 8 May 2026 11:37:20 +0200 Subject: [PATCH 169/681] ninjabackend: fix missing return statements from mypy Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 39003c235465..38a596fd86d8 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -613,7 +613,8 @@ def detect_prefix(out: bytes) -> T.TextIO: raise MesonException(f'Could not determine vs dep dependency prefix string. output: {stderr} {stdout}') - def generate(self, capture: bool = False, vslite_ctx: T.Optional[T.Dict] = None) -> T.Optional[T.Dict]: + def generate(self, capture: bool = False, vslite_ctx: T.Optional[T.Dict] = None) -> T.Optional[T.Dict[str, T.Dict[Language, T.List[str]]]]: + captured_compile_args_per_target: T.Dict[str, T.Dict[Language, T.List[str]]] = {} if vslite_ctx: # We don't yet have a use case where we'd expect to make use of this, # so no harm in catching and reporting something unexpected. @@ -666,7 +667,6 @@ def generate(self, capture: bool = False, vslite_ctx: T.Optional[T.Dict] = None) # Optionally capture compile args per target, for later use (i.e. VisStudio project's NMake intellisense include dirs, defines, and compile options). if capture: - captured_compile_args_per_target = {} for target in self.build.get_targets().values(): if isinstance(target, build.BuildTarget): captured_compile_args_per_target[target.get_id()] = self.generate_common_compile_args_per_src_type(target) @@ -719,8 +719,7 @@ def generate(self, capture: bool = False, vslite_ctx: T.Optional[T.Dict] = None) self.generate_compdb() self.generate_rust_project_json() - if capture: - return captured_compile_args_per_target + return captured_compile_args_per_target def generate_rust_project_json(self) -> None: """Generate a rust-analyzer compatible rust-project.json file.""" @@ -3349,7 +3348,8 @@ def target_uses_import_std(self, target: build.BuildTarget) -> bool: if self.environment.coredata.get_option_for_target(target, 'cpp_importstd') == 'true': return True except KeyError: - return False + pass + return False def handle_cpp_import_std(self, target: build.BuildTarget, compiler): istd_args = [] @@ -3626,7 +3626,7 @@ def get_link_whole_args(self, linker: Compiler, target): return linker.get_link_whole_for(target_args) if target_args else [] @lru_cache(maxsize=None) - def guess_library_absolute_path(self, linker, libname, search_dirs, patterns) -> Path: + def guess_library_absolute_path(self, linker, libname, search_dirs, patterns) -> T.Optional[Path]: from ..compilers.c import CCompiler for d in search_dirs: for p in patterns: @@ -3638,6 +3638,7 @@ def guess_library_absolute_path(self, linker, libname, search_dirs, patterns) -> continue # Return the first result return trial + return None def guess_external_link_dependencies(self, linker, target, commands, internal): # Ideally the linker would generate dependency information that could be used. From 28c4822134f3f06b27db5eb32395cc095f934b76 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 4 May 2026 12:02:11 -0700 Subject: [PATCH 170/681] build: Convert CommandBase into free function This makes the typing clearer, and avoids the need for a base class which adds effectively a private method. --- mesonbuild/build.py | 96 +++++++++++++++++++++++++-------------------- 1 file changed, 53 insertions(+), 43 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 7d6bae6b02e1..888ef27153c2 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2923,46 +2923,53 @@ def is_linkable_target(self) -> bool: # For polymorphism with build targets return True -class CommandBase: - depend_files: T.List[File] - dependencies: T.List[T.Union[BuildTarget, 'CustomTarget']] - subproject: SubProject +def flatten_command(cmd: T.Sequence[str | File | programs.Program | BuildTargetTypes], + subproject: SubProject) -> tuple[list[str | File | BuildTarget | CustomTarget | programs.Program], + list[File], list[BuildTarget | CustomTarget]]: + final_cmd: list[str | File | programs.Program | BuildTarget | CustomTarget] = [] + depend_files: list[File] = [] + dependencies: list[BuildTarget | CustomTarget] = [] + for c in cmd: + if isinstance(c, LocalProgram): + c = c.program + if isinstance(c, str): + final_cmd.append(c) + elif isinstance(c, File): + depend_files.append(c) + final_cmd.append(c) + elif isinstance(c, programs.Program): + if not c.found(): + raise InvalidArguments('Tried to use not-found external program in "command"') + path = c.get_path() + # We know path is not non if c.found() + assert path is not None, 'for mypy' + if os.path.isabs(path): + # Can only add a dependency on an external program which we + # know the absolute path of + depend_files.append(File.from_absolute_file(path)) + # Do NOT flatten -- it is needed for later parsing + final_cmd.append(c) + elif isinstance(c, (BuildTarget, CustomTarget)): + dependencies.append(c) + final_cmd.append(c) + elif isinstance(c, CustomTargetIndex): + FeatureNew.single_use('CustomTargetIndex for command argument', '0.60', subproject) + dependencies.append(c.target) + c, df, d = flatten_command([File.from_built_file(c.get_subdir(), c.get_filename())], subproject) + final_cmd.extend(c) + depend_files.extend(df) + dependencies.extend(d) + elif isinstance(c, list): + # TODO: is this case even reachable? + c, df, d = flatten_command(c, subproject) + final_cmd.extend(c) + depend_files.extend(df) + dependencies.extend(d) + else: + raise InvalidArguments(f'Argument {c!r} in "command" is invalid') + return final_cmd, depend_files, dependencies - def flatten_command(self, cmd: T.Sequence[T.Union[str, File, programs.Program, BuildTargetTypes]]) -> \ - T.List[T.Union[str, File, BuildTarget, CustomTarget, programs.Program]]: - cmd = listify(cmd) - final_cmd: T.List[T.Union[str, File, BuildTarget, 'CustomTarget']] = [] - for c in cmd: - if isinstance(c, LocalProgram): - c = c.program - if isinstance(c, str): - final_cmd.append(c) - elif isinstance(c, File): - self.depend_files.append(c) - final_cmd.append(c) - elif isinstance(c, programs.Program): - if not c.found(): - raise InvalidArguments('Tried to use not-found external program in "command"') - path = c.get_path() - if os.path.isabs(path): - # Can only add a dependency on an external program which we - # know the absolute path of - self.depend_files.append(File.from_absolute_file(path)) - # Do NOT flatten -- it is needed for later parsing - final_cmd.append(c) - elif isinstance(c, (BuildTarget, CustomTarget)): - self.dependencies.append(c) - final_cmd.append(c) - elif isinstance(c, CustomTargetIndex): - FeatureNew.single_use('CustomTargetIndex for command argument', '0.60', self.subproject) - self.dependencies.append(c.target) - final_cmd += self.flatten_command(File.from_built_file(c.get_subdir(), c.get_filename())) - elif isinstance(c, list): - final_cmd += self.flatten_command(c) - else: - raise InvalidArguments(f'Argument {c!r} in "command" is invalid') - return final_cmd class CustomTargetBase: ''' Base class for CustomTarget and CustomTargetIndex @@ -2993,7 +3000,7 @@ def get(self, lib_type: T.Literal['static', 'shared'], recursive: bool = False) """Base case used by BothLibraries""" return self -class CustomTarget(Target, CustomTargetBase, CommandBase): +class CustomTarget(Target, CustomTargetBase): typename = 'custom' @@ -3042,7 +3049,10 @@ def __init__(self, self.depend_files = list(depend_files or []) self.dependencies: T.List[T.Union[CustomTarget, BuildTarget]] = [] # must be after depend_files and dependencies - self.command = self.flatten_command(command) + c, df, d = flatten_command(command, self.subproject) + self.command = c + self.depend_files.extend(df) + self.dependencies.extend(d) self.depfile = depfile self.depfile_type = 'gcc' if depfile else depfile_type self.env = env or EnvironmentVariables() @@ -3281,7 +3291,7 @@ def get_generated_headers(self) -> T.List[File]: gen_headers += [File(True, dep.subdir, o) for o in dep.get_outputs()] return gen_headers -class RunTarget(Target, CommandBase): +class RunTarget(Target): typename = 'run' @@ -3297,8 +3307,8 @@ def __init__(self, name: str, # These don't produce output artifacts super().__init__(name, subdir, subproject, False, MachineChoice.BUILD, environment) self.dependencies = list(dependencies) - self.depend_files = [] - self.command = self.flatten_command(command) + self.command, self.depend_files, d = flatten_command(command, subproject) + self.dependencies.extend(d) self.absolute_paths = False self.env = env self.default_env = default_env From 5c38e730e5154d0ca6986081b6641e737e4ac636 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 4 May 2026 12:17:49 -0700 Subject: [PATCH 171/681] universal: implement `__copy__` protocol for PerMachine classes This makes copying with types much easier than open coding this elsewhere. --- mesonbuild/utils/universal.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index 0c71c2033400..0b1ddc7d53c8 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -8,6 +8,7 @@ from pathlib import Path import argparse import ast +import copy import enum import sys import stat @@ -573,6 +574,14 @@ def assign(self, build: _T, host: _T) -> None: self.build = build self.host = host + def __copy__(self) -> PerMachine[_T]: + build = copy.copy(self.build) + if self.host is self.build: + host = build + else: + host = copy.copy(self.host) + return PerMachine(build, host) + @dataclasses.dataclass(eq=False, order=False) class PerThreeMachine(PerMachine[_T]): @@ -605,6 +614,20 @@ def miss_defaulting(self) -> "PerThreeMachineDefaultable[T.Optional[_T]]": def matches_build_machine(self, machine: MachineChoice) -> bool: return self.build == self[machine] + def __copy__(self) -> PerMachine[_T]: + build = copy.copy(self.build) + if self.host is self.build: + host = build + else: + host = copy.copy(self.host) + + if self.target is self.host: + target = host + else: + target = copy.copy(self.target) + + return PerThreeMachine(build, host, target) + @dataclasses.dataclass(eq=False, order=False) class PerMachineDefaultable(PerMachine[T.Optional[_T]]): From d30694bda34f218f947bd50683b5bb8588d52e77 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 4 May 2026 12:19:14 -0700 Subject: [PATCH 172/681] build: use copy.copy instead of open coding This fixes some typing issues, as well as being more generically correct. --- mesonbuild/build.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 888ef27153c2..c4a2cd6b15cf 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -440,18 +440,9 @@ def get_custom_targets(self) -> OrderedDict[str, CustomTarget]: return custom_targets def copy(self) -> Build: - def copy_value(v: _T) -> _T: - if isinstance(v, PerMachine): - build = copy_value(v.build) - host = build if v.build is v.host else copy_value(v.host) - return PerMachine(build=build, host=host) - if isinstance(v, (list, dict, set, OrderedDict)): - return v.copy() - return v - other = Build.__new__(Build) for k, v in self.__dict__.items(): - other.__dict__[k] = copy_value(v) + other.__dict__[k] = copy.copy(v) return other def merge(self, other: Build) -> None: From d0d4f9baedac11253c1ff9b7aa13329538aec225 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 4 May 2026 12:35:15 -0700 Subject: [PATCH 173/681] build: use dict() to copy and convert mapping in one step --- mesonbuild/build.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index c4a2cd6b15cf..485466d76b05 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -1314,8 +1314,9 @@ def get_transitive_link_deps_mapping(self, prefix: str) -> T.Mapping[str, str]: result: T.Dict[str, str] = {} for i in itertools.chain(self.link_targets, self.link_whole_targets): mapping = i.get_link_deps_mapping(prefix) - #we are merging two dictionaries, while keeping the earlier one dominant - result_tmp = mapping.copy() + # we are merging two dictionaries, while keeping the earlier one dominant + # use dict() both to copy and change type + result_tmp = dict(mapping) result_tmp.update(result) result = result_tmp return result From 5c878ec85493a7c56abf99128398e5eb52c40182 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 4 May 2026 12:40:24 -0700 Subject: [PATCH 174/681] build: Make CustomTarget.depend_files a `list[File]` This makes it match BuildTarget, and removes another use of strings as sources below the Interpreter. --- mesonbuild/build.py | 2 +- mesonbuild/modules/gnome.py | 8 ++++---- mesonbuild/modules/windows.py | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 485466d76b05..85f7e3540a14 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -3011,7 +3011,7 @@ def __init__(self, build_by_default: T.Optional[bool] = None, capture: bool = False, console: bool = False, - depend_files: T.Optional[T.Sequence[FileOrString]] = None, + depend_files: list[File] | None = None, extra_depends: T.Optional[T.Sequence[TargetDepends]] = None, depfile: T.Optional[str] = None, depfile_type: T.Optional[Literal['gcc', 'msvc']] = None, diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index e92d3f0e2bb7..56294495bcbd 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -555,7 +555,7 @@ def compile_resources(self, state: 'ModuleState', args: T.Tuple[str, CustomTarge def _get_gresource_dependencies( state: 'ModuleState', input_file: str, source_dirs: T.List[str], dependencies: T.Sequence[T.Union[mesonlib.File, CustomTarget, CustomTargetIndex]] - ) -> T.Tuple[T.List[mesonlib.FileOrString], T.List[T.Union[CustomTarget, CustomTargetIndex]], T.List[str]]: + ) -> T.Tuple[T.List[mesonlib.File], T.List[T.Union[CustomTarget, CustomTargetIndex]], T.List[str]]: cmd = ['glib-compile-resources', input_file, @@ -579,7 +579,7 @@ def _get_gresource_dependencies( depends: T.List[T.Union[CustomTarget, CustomTargetIndex]] = [] subdirs: T.List[str] = [] - dep_files: T.List[mesonlib.FileOrString] = [] + dep_files: T.List[mesonlib.File] = [] for resfile in raw_dep_files.copy(): resbasename = os.path.basename(resfile) for dep in dependencies: @@ -622,7 +622,7 @@ def _get_gresource_dependencies( 'keyword argument.') raw_dep_files.remove(resfile) dep_files.append(f) - dep_files.extend(raw_dep_files) + dep_files.extend(mesonlib.File.from_absolute_file(r) for r in raw_dep_files) return dep_files, depends, subdirs def _get_link_args(self, state: 'ModuleState', @@ -1296,7 +1296,7 @@ def compile_schemas(self, state: 'ModuleState', args: T.List['TYPE_var'], kwargs [], ['gschemas.compiled'], build_by_default=kwargs['build_by_default'], - depend_files=kwargs['depend_files'], + depend_files=state._interpreter.source_strings_to_files(kwargs['depend_files']), description='Compiling gschemas {}', ) self._devenv_prepend('GSETTINGS_SCHEMA_DIR', os.path.join(state.environment.get_build_dir(), state.subdir)) diff --git a/mesonbuild/modules/windows.py b/mesonbuild/modules/windows.py index 198cbebd828e..d53ebe4fa6b5 100644 --- a/mesonbuild/modules/windows.py +++ b/mesonbuild/modules/windows.py @@ -119,7 +119,7 @@ def compile_resources(self, state: 'ModuleState', args: T.Tuple[T.List[T.Union[str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex]]], kwargs: 'CompileResources') -> ModuleReturnValue: extra_args = kwargs['args'].copy() - wrc_depend_files = kwargs['depend_files'] + wrc_depend_files = state._interpreter.source_strings_to_files(kwargs['depend_files']) wrc_depends = kwargs['depends'] for d in wrc_depends: if isinstance(d, build.CustomTarget): From 2fc0840de97c73499fec8a8cadad09faa10ce6b7 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 4 May 2026 12:46:55 -0700 Subject: [PATCH 175/681] build: Prevent mutation of cached mapping which should be immutable Mypy catches this for us. --- mesonbuild/build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 85f7e3540a14..fe24b31ab7a8 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2568,7 +2568,7 @@ def post_init(self) -> None: def get_link_deps_mapping(self, prefix: str) -> T.Mapping[str, str]: result: T.Dict[str, str] = {} - mappings = self.get_transitive_link_deps_mapping(prefix) + mappings = dict(self.get_transitive_link_deps_mapping(prefix)) old = get_target_macos_dylib_install_name(self) if old not in mappings: fname = self.get_filename() From d23d4e453f96bf3f9723172fe8b5dfeae63bed3c Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 5 May 2026 09:31:31 -0700 Subject: [PATCH 176/681] build: Remove unreachable input validation Mypy should cover internal use cases, and the interpreter is fully typed now. --- mesonbuild/build.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index fe24b31ab7a8..a945032947e9 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2869,10 +2869,6 @@ def __init__( environment: Environment, compilers: CompilerDict, kwargs: SharedModuleKeywordArguments): - if 'version' in kwargs: - raise MesonException('Shared modules must not specify the version kwarg.') - if 'soversion' in kwargs: - raise MesonException('Shared modules must not specify the soversion kwarg.') super().__init__(name, subdir, subproject, for_machine, sources, structured_sources, objects, environment, compilers, # SharedModuleKeywordArguments is a subclass, it's annoying mypy can't figure this out From 866898fa78a60ee349f33f128498e9323187272d Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 4 May 2026 13:12:26 -0700 Subject: [PATCH 177/681] build: cast a list return to do with invariance --- mesonbuild/build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index a945032947e9..94e0c3608aef 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -3199,7 +3199,7 @@ def is_internal(self) -> bool: return CustomTargetIndex(self, self.outputs[0]).is_internal() def extract_all_objects(self) -> T.List[T.Union[str, 'ExtractedObjects']]: - return self.get_outputs() + return T.cast('list[str | ExtractedObjects]', self.get_outputs()) def type_suffix(self) -> str: return "@cus" From 688ace136bdff7352a73f8b9b8b0d14fe7d8cbdd Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 5 May 2026 09:42:18 -0700 Subject: [PATCH 178/681] build: simplify process_kwargs This removes some dead or should-be-dead code, mostly validation. --- mesonbuild/build.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 94e0c3608aef..19572a5a64d7 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -813,7 +813,10 @@ def __init__( environment: Environment, compilers: CompilerDict, kwargs: BuildTargetKeywordArguments): - super().__init__(name, subdir, subproject, True, for_machine, environment, install=kwargs.get('install', False), build_subdir=kwargs.get('build_subdir', '')) + super().__init__(name, subdir, subproject, kwargs.get('build_by_default', True), + for_machine, environment, + install=kwargs.get('install', False), + build_subdir=kwargs.get('build_subdir', '')) # all_compilers is a reference to Interpreter.compilers, as such we # cannot mutate it inside build. Use a Mapping to get help from the # static type checker @@ -1362,9 +1365,6 @@ def get_override(self, name: str) -> T.Optional[ElementaryOptionValues]: def process_kwargs(self, kwargs: BuildTargetKeywordArguments) -> None: self.original_kwargs = kwargs - if 'build_by_default' in kwargs: - self.build_by_default = kwargs['build_by_default'] - if not self.build_by_default and kwargs.get('install', False): # For backward compatibility, if build_by_default is not explicitly # set, use the value of 'install' if it's enabled. @@ -1384,9 +1384,6 @@ def process_kwargs(self, kwargs: BuildTargetKeywordArguments) -> None: This will become a hard error in a future Meson release. ''')) self.link_early_args = kwargs.get('link_early_args', []) - for i in self.link_early_args: - if not isinstance(i, str): - raise InvalidArguments('link_early_args values must be strings.') self.process_link_depends(kwargs.get('link_depends', [])) # Target-specific include dirs must be added BEFORE include dirs from # internal deps (added inside self.add_deps()) to override them. From 3ed65ab0c342fd7192872b9c971c6f5d07eefb7f Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 5 May 2026 09:49:03 -0700 Subject: [PATCH 179/681] build: Fold SharedLibrary.process_kwargs into the initializer --- mesonbuild/build.py | 60 ++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 19572a5a64d7..bc893942e49a 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2531,21 +2531,45 @@ def __init__( environment: Environment, compilers: CompilerDict, kwargs: SharedLibraryKeywordArguments): - self.soversion: T.Optional[str] = None - self.ltversion: T.Optional[str] = None + super().__init__(name, subdir, subproject, for_machine, sources, structured_sources, objects, + environment, compilers, kwargs) + # Max length 2, first element is compatibility_version, second is current_version self.darwin_versions: T.Optional[T.Tuple[str, str]] = None - self.vs_module_defs = None - self.shortname: T.Optional[str] = None + self.soversion: T.Optional[str] = None + self.ltversion: T.Optional[str] = None + if not self.environment.machines[self.for_machine].is_android(): + # Shared library version + self.ltversion = kwargs.get('version') + self.soversion = kwargs.get('soversion') + if self.soversion is None and self.ltversion is not None: + # library version is defined, get the soversion from that + # We replicate what Autotools does here and take the first + # number of the version by default. + self.soversion = self.ltversion.split('.')[0] + # macOS, iOS and tvOS dylib compatibility_version and current_version + self.darwin_versions = kwargs.get('darwin_versions') + if self.darwin_versions is None and self.soversion is not None: + # If unspecified, pick the soversion + self.darwin_versions = (self.soversion, self.soversion) + + self.vs_module_defs: File | None = None + # Visual Studio module-definitions file + self.process_vs_module_defs_kw(kwargs) + + # OS/2 uses a 8.3 name for a DLL + self.shortname = kwargs.get('shortname') + # The import library this target will generate self.import_filename: str | None = None + # The debugging information file this target will generate self.debug_filename: str | None = None + # Use by the pkgconfig module self.shared_library_only = False + self.rust_crate_type = kwargs.get('rust_crate_type', 'dylib') - super().__init__(name, subdir, subproject, for_machine, sources, structured_sources, objects, - environment, compilers, kwargs) def post_init(self) -> None: super().post_init() @@ -2733,30 +2757,6 @@ def determine_filenames(self) -> None: if create_debug_file: self.debug_filename = os.path.splitext(self.filename)[0] + '.pdb' - def process_kwargs(self, kwargs: SharedLibraryKeywordArguments) -> None: - super().process_kwargs(kwargs) - - if not self.environment.machines[self.for_machine].is_android(): - # Shared library version - self.ltversion = kwargs.get('version') - self.soversion = kwargs.get('soversion') - if self.soversion is None and self.ltversion is not None: - # library version is defined, get the soversion from that - # We replicate what Autotools does here and take the first - # number of the version by default. - self.soversion = self.ltversion.split('.')[0] - # macOS, iOS and tvOS dylib compatibility_version and current_version - self.darwin_versions = kwargs.get('darwin_versions') - if self.darwin_versions is None and self.soversion is not None: - # If unspecified, pick the soversion - self.darwin_versions = (self.soversion, self.soversion) - - # Visual Studio module-definitions file - self.process_vs_module_defs_kw(kwargs) - - # OS/2 uses a 8.3 name for a DLL - self.shortname = kwargs.get('shortname') - def get_import_filename(self) -> T.Optional[str]: """ The name of the import library that will be outputted by the compiler From b1a770b6bd5cafd3304a813ce4a4666483bdafe2 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 5 May 2026 09:54:49 -0700 Subject: [PATCH 180/681] build: fold BuildTarget.process_kwargs into BuildTarget initializer --- mesonbuild/build.py | 121 +++++++++++++++++++++----------------------- 1 file changed, 58 insertions(+), 63 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index bc893942e49a..a7fa1503e17e 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -817,6 +817,7 @@ def __init__( for_machine, environment, install=kwargs.get('install', False), build_subdir=kwargs.get('build_subdir', '')) + self.original_kwargs = kwargs # all_compilers is a reference to Interpreter.compilers, as such we # cannot mutate it inside build. Use a Mapping to get help from the # static type checker @@ -867,7 +868,62 @@ def __init__( # 1. Preexisting objects provided by the user with the `objects:` kwarg # 2. Compiled objects created by and extracted from another target self.process_objectlist(objects) - self.process_kwargs(kwargs) + + if not self.build_by_default and kwargs.get('install', False): + # For backward compatibility, if build_by_default is not explicitly + # set, use the value of 'install' if it's enabled. + self.build_by_default = True + + self.raw_overrides = kwargs.get('override_options', {}) + + self.pch['c'] = kwargs.get('c_pch') + self.pch['cpp'] = kwargs.get('cpp_pch') + + self.link_args = kwargs.get('link_args', []) + for l in self.link_args: + if '-Wl,-rpath' in l or l.startswith('-rpath'): + mlog.warning(textwrap.dedent('''\ + Please do not define rpath with a linker argument, use install_rpath + or build_rpath properties instead. + This will become a hard error in a future Meson release. + ''')) + self.link_early_args = kwargs.get('link_early_args', []) + self.process_link_depends(kwargs.get('link_depends', [])) + # Target-specific include dirs must be added BEFORE include dirs from + # internal deps (added inside self.add_deps()) to override them. + self.add_include_dirs(kwargs.get('include_directories', [])) + # Add dependencies (which also have include_directories) + self.add_deps(kwargs.get('dependencies', [])) + + self.has_custom_install_dir = False + i = kwargs.get('install_dir', []) + install_dir = i[0] if i else True + default_install_dir = self.get_default_install_dir()[0] + if install_dir is True: + install_dir = default_install_dir + elif install_dir != default_install_dir: + self.has_custom_install_dir = True + self.install_dir: T.List[T.Union[str, T.Literal[False]]] = [install_dir] + self.install_mode = kwargs.get('install_mode', None) + self.install_tag: T.List[T.Optional[str]] = kwargs.get('install_tag') or [None] + self.extra_files = kwargs.get('extra_files', []) + self.install_rpath: str = kwargs.get('install_rpath', '') + self.build_rpath = kwargs.get('build_rpath', '') + self.resources = kwargs.get('resources', []) + name_prefix = kwargs.get('name_prefix') + if name_prefix is not None: + self.prefix = name_prefix + self.name_prefix_set = True + name_suffix = kwargs.get('name_suffix') + if name_suffix is not None: + self.suffix = name_suffix + self.name_suffix_set = True + self.implicit_include_directories = kwargs.get('implicit_include_directories', True) + self.gnu_symbol_visibility = kwargs.get('gnu_symbol_visibility', '') + self.rust_dependency_map = kwargs.get('rust_dependency_map', {}) + + self.swift_interoperability_mode = kwargs.get('swift_interoperability_mode', 'c') + self.swift_module_name = kwargs.get('swift_module_name') or self.name self.missing_languages = self.process_compilers() self.single_compile_base_args: T.Dict[Compiler, ImmutableListProtocol[str]] = {} @@ -1362,66 +1418,6 @@ def get_custom_install_mode(self) -> T.Optional['FileMode']: def get_override(self, name: str) -> T.Optional[ElementaryOptionValues]: return self.raw_overrides.get(name, None) - def process_kwargs(self, kwargs: BuildTargetKeywordArguments) -> None: - self.original_kwargs = kwargs - - if not self.build_by_default and kwargs.get('install', False): - # For backward compatibility, if build_by_default is not explicitly - # set, use the value of 'install' if it's enabled. - self.build_by_default = True - - self.raw_overrides = kwargs.get('override_options', {}) - - self.pch['c'] = kwargs.get('c_pch') - self.pch['cpp'] = kwargs.get('cpp_pch') - - self.link_args = kwargs.get('link_args', []) - for l in self.link_args: - if '-Wl,-rpath' in l or l.startswith('-rpath'): - mlog.warning(textwrap.dedent('''\ - Please do not define rpath with a linker argument, use install_rpath - or build_rpath properties instead. - This will become a hard error in a future Meson release. - ''')) - self.link_early_args = kwargs.get('link_early_args', []) - self.process_link_depends(kwargs.get('link_depends', [])) - # Target-specific include dirs must be added BEFORE include dirs from - # internal deps (added inside self.add_deps()) to override them. - self.add_include_dirs(kwargs.get('include_directories', [])) - # Add dependencies (which also have include_directories) - self.add_deps(kwargs.get('dependencies', [])) - - self.has_custom_install_dir = False - i = kwargs.get('install_dir', []) - install_dir = i[0] if i else True - default_install_dir = self.get_default_install_dir()[0] - if install_dir is True: - install_dir = default_install_dir - elif install_dir != default_install_dir: - self.has_custom_install_dir = True - self.install_dir: T.List[T.Union[str, T.Literal[False]]] = [install_dir] - - self.install_mode = kwargs.get('install_mode', None) - self.install_tag: T.List[T.Optional[str]] = kwargs.get('install_tag') or [None] - self.extra_files = kwargs.get('extra_files', []) - self.install_rpath: str = kwargs.get('install_rpath', '') - self.build_rpath = kwargs.get('build_rpath', '') - self.resources = kwargs.get('resources', []) - name_prefix = kwargs.get('name_prefix') - if name_prefix is not None: - self.prefix = name_prefix - self.name_prefix_set = True - name_suffix = kwargs.get('name_suffix') - if name_suffix is not None: - self.suffix = name_suffix - self.name_suffix_set = True - self.implicit_include_directories = kwargs.get('implicit_include_directories', True) - self.gnu_symbol_visibility = kwargs.get('gnu_symbol_visibility', '') - self.rust_dependency_map = kwargs.get('rust_dependency_map', {}) - - self.swift_interoperability_mode = kwargs.get('swift_interoperability_mode', 'c') - self.swift_module_name = kwargs.get('swift_module_name') or self.name - @T.overload def _extract_pic_pie(self, kwargs: StaticLibraryKeywordArguments, arg: Literal['pic'], option: Literal['b_staticpic']) -> bool: ... @@ -2533,7 +2529,6 @@ def __init__( kwargs: SharedLibraryKeywordArguments): super().__init__(name, subdir, subproject, for_machine, sources, structured_sources, objects, environment, compilers, kwargs) - # Max length 2, first element is compatibility_version, second is current_version self.darwin_versions: T.Optional[T.Tuple[str, str]] = None self.soversion: T.Optional[str] = None @@ -3130,7 +3125,7 @@ def get_outputs(self) -> T.List[str]: def get_filename(self) -> str: return self.outputs[0] - def get_sources(self) -> T.List[T.Union[str, File, BuildTarget, GeneratedTypes, ExtractedObjects, programs.Program]]: + def get_sources(self) -> T.List[CustomTargetSources]: return self.sources def get_generated_lists(self) -> T.List[GeneratedList]: From 855546371c82a12520b49564331f781aa3101503 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 6 May 2026 08:41:12 -0700 Subject: [PATCH 181/681] build: Fix typing issue in Generator.process_files mypy can't tell that the `if preserve_path_from:` in the final inner loop will not be true for the GeneratedList case. We can avoid that case anyway, just add the files early and continue. --- mesonbuild/build.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index a7fa1503e17e..e9fc9cd92658 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2104,7 +2104,9 @@ def process_files(self, files: T.Iterable[T.Union[str, File, GeneratedTypes]], if preserve_path_from: raise InvalidArguments("generator.process: 'preserve_path_from' is not allowed if one input is a 'generated_list'.") output.depends.add(e) - fs = [FileInTargetPrivateDir(f) for f in e.get_outputs()] + output.add_files(FileMaybeInTargetPrivateDir(FileInTargetPrivateDir(f)) + for f in e.get_outputs()) + continue elif isinstance(e, str): fs = [File.from_source_file(self.environment.source_dir, subdir, e)] else: @@ -2171,6 +2173,10 @@ def get_preserved_path_segment(self, infile: FileMaybeInTargetPrivateDir) -> str rel = os.path.relpath(in_abs, self.preserve_path_from) return os.path.dirname(rel) + def add_files(self, newfiles: T.Iterable[FileMaybeInTargetPrivateDir]) -> None: + for f in newfiles: + self.add_file(f) + def add_file(self, newfile: FileMaybeInTargetPrivateDir) -> None: self.infilelist.append(newfile) outfiles = self.generator.get_base_outnames(newfile.fname) From ecfc3b0d9741c36daf194e48cccb081da1576301 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 6 May 2026 09:18:55 -0700 Subject: [PATCH 182/681] build: add `get_basename()` to `GeneratedList` So that it can fulfill interface contracts. --- mesonbuild/build.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index e9fc9cd92658..5d296695a5a8 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2204,6 +2204,9 @@ def get_extra_args(self) -> T.List[str]: def get_subdir(self) -> str: return self.subdir + def get_basename(self) -> str: + return self.generator.name + class Executable(BuildTarget): known_kwargs = known_exe_kwargs From f6802b20ae6c1301d687f6edb74ae235376a9941 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 7 May 2026 09:32:47 -0700 Subject: [PATCH 183/681] build: Use a cast instead of annotation for BuildTarget.extra_args fallback I don't understand why the one works but not the other with mypy and pylance, but it does --- mesonbuild/build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 5d296695a5a8..dd5597ddf2c2 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -843,7 +843,7 @@ def __init__( # as Vala which generates .vapi and .h besides the compiled output. self.outputs = [self.filename] self.pch: T.Dict[Language, T.Optional[T.Tuple[str, T.Optional[str]]]] = {} - self.extra_args: T.DefaultDict[Language, T.List[str]] = kwargs.get('language_args', defaultdict(list)) + self.extra_args = kwargs.get('language_args', T.cast('T.DefaultDict[Language, list[str]]', defaultdict(list))) self.sources: T.List[File] = [] # If the same source is defined multiple times, use it only once. self.seen_sources: T.Set[File] = set() From 0da18403bf6e5b65ad98b14f2d77ffa1ba0b415c Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 7 May 2026 09:43:23 -0700 Subject: [PATCH 184/681] build: Use Iterable for the process_sourcelist helper Again, we don't have strings, so it's perfectly safe to use iterable here, and we avoid the constant need to cast --- mesonbuild/build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index dd5597ddf2c2..5f5d3ed88b08 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -1052,7 +1052,7 @@ def process_objectlist(self, objects: T.List[ObjectTypes]) -> None: FeatureDeprecated.single_use(f'Source file {deprecated_non_objects[0]} in the \'objects\' kwarg is not an object.', '1.3.0', self.subproject) - def process_sourcelist(self, sources: T.List['SourceOutputs']) -> None: + def process_sourcelist(self, sources: T.Iterable['SourceOutputs']) -> None: """Split sources into generated and static sources. Sources can be: From 10f981321ab1ca59b92bcbd7327bc5494f8d24e8 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 7 May 2026 12:05:08 -0700 Subject: [PATCH 185/681] build: Convert GeneratedList(extra_depends) to list explicitly --- mesonbuild/build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 5f5d3ed88b08..b6ca70135cff 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2094,7 +2094,7 @@ def process_files(self, files: T.Iterable[T.Union[str, File, GeneratedTypes]], preserve_path_from, extra_args=extra_args if extra_args is not None else [], env=env if env is not None else EnvironmentVariables(), - extra_depends=extra_depends if extra_depends is not None else []) + extra_depends=list(extra_depends) if extra_depends is not None else []) for e in files: if isinstance(e, (CustomTarget, CustomTargetIndex)): From eafdbd392ba0943fcf843cbf3438670fbcd81629 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 7 May 2026 09:03:11 -0700 Subject: [PATCH 186/681] docs: Update docs on `include_directories` and `d_import_dirs` Hopefully this will help make it clearer for D that an import_dir is just another use of include_directories --- docs/yaml/functions/_build_target_base.yaml | 8 +++++--- docs/yaml/functions/include_directories.yaml | 11 +++++------ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/docs/yaml/functions/_build_target_base.yaml b/docs/yaml/functions/_build_target_base.yaml index 286a33e2b769..146c35231a48 100644 --- a/docs/yaml/functions/_build_target_base.yaml +++ b/docs/yaml/functions/_build_target_base.yaml @@ -169,8 +169,9 @@ kwargs: include_directories: type: array[inc | str] description: | - one or more objects created with the [[include_directories]] function, - or *(since 0.50.0)* strings, which will be transparently expanded to include directory objects + one or more objects created with the [[include_directories]] function, or + *(since 0.50.0)* strings, which will be implicitly convert to [[@inc]] + objects install: type: bool @@ -282,7 +283,8 @@ kwargs: since: 0.62.0 description: | the directories to add to the string search path (i.e. `-J` switch for DMD). - Must be [[@inc]] objects or plain strings. + Must be objects created with the [[include_directories]] function or plain + strings, which will be implicitly converted into [[@inc]] objects. d_unittest: type: bool diff --git a/docs/yaml/functions/include_directories.yaml b/docs/yaml/functions/include_directories.yaml index 8ca5cb9ed25c..ebe65364f608 100644 --- a/docs/yaml/functions/include_directories.yaml +++ b/docs/yaml/functions/include_directories.yaml @@ -1,12 +1,11 @@ name: include_directories returns: inc description: | - Returns an opaque object which contains the directories (relative to - the current directory) given in the positional arguments. The result - can then be passed to the `include_directories:` keyword argument when - building executables or libraries. You can use the returned object in - any subdirectory you want, Meson will make the paths work - automatically. + Returns an opaque object which contains the directories (relative to the + current directory) given in the positional arguments. The result can then be + passed to the `include_directories:` and `d_import_dirs:` keyword arguments + when building executables or libraries. You can use the returned object in any + subdirectory you want, Meson will make the paths work automatically. Note that this function call itself does not add the directories into the search path, since there is no global search path. For something From db141d45b4f15e3888ca02ce90088268cfbc7537 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 8 May 2026 08:23:12 -0700 Subject: [PATCH 187/681] modules/simd: delete use of permittedKwargs Everything it was handling is now handled by typed_kwargs, since STATIC_LIB_KWS is now complete --- mesonbuild/modules/simd.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mesonbuild/modules/simd.py b/mesonbuild/modules/simd.py index 55788e0c627b..22f080a18a12 100644 --- a/mesonbuild/modules/simd.py +++ b/mesonbuild/modules/simd.py @@ -9,7 +9,7 @@ from .. import build from ..compilers import Compiler from ..interpreter.type_checking import BT_SOURCES_KW, STATIC_LIB_KWS -from ..interpreterbase.decorators import KwargInfo, permittedKwargs, typed_pos_args, typed_kwargs +from ..interpreterbase.decorators import KwargInfo, typed_pos_args, typed_kwargs from . import ExtensionModule, ModuleInfo @@ -65,7 +65,6 @@ def __init__(self, interpreter: Interpreter): KwargInfo('compiler', Compiler, required=True), *[BT_SOURCES_KW.evolve(name=iset, default=None) for iset in ISETS], *[a for a in STATIC_LIB_KWS if a.name != 'sources']) - @permittedKwargs({'compiler', *ISETS, *build.known_stlib_kwargs}) # Also remove this, per above comment def check(self, state: ModuleState, args: T.Tuple[str], kwargs: CheckKw) -> T.List[T.Union[T.List[build.StaticLibrary], build.ConfigurationData]]: result: T.List[build.StaticLibrary] = [] From 82df95db0e80040e3ee6ec7918295a432ebc8370 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 8 May 2026 08:30:46 -0700 Subject: [PATCH 188/681] modules/rust: replace use of permittedKwargs This is the last legitimate user, as such let's just re-implement what it was doing and delete the use of the function. --- mesonbuild/modules/rust.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index 565bc0191838..a2605d9f3a22 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -23,7 +23,7 @@ OUTPUT_KW, INCLUDE_DIRECTORIES, SOURCES_VARARGS, NATIVE_KW, NoneType, in_set_validator, EXECUTABLE_KWS, LIBRARY_KWS, SHARED_MOD_KWS, _BASE_LANG_KW ) -from ..interpreterbase import ContainerTypeInfo, InterpreterException, KwargInfo, typed_kwargs, typed_pos_args, noKwargs, noPosargs, permittedKwargs +from ..interpreterbase import ContainerTypeInfo, InterpreterException, KwargInfo, typed_kwargs, typed_pos_args, noKwargs, noPosargs from ..interpreter.interpreterobjects import Doctest from ..mesonlib import (is_parent_path, File, MachineChoice, MesonException, PerMachine) from ..programs import ExternalProgram, NonExistingExternalProgram @@ -107,6 +107,12 @@ class RustPackageLibrary(_kwargs.Library): SHARED_LIB_KWS_NO_NATIVE = [kwi for kwi in SHARED_LIB_KWS if kwi.name != 'native'] SHARED_MOD_KWS_NO_NATIVE = [kwi for kwi in SHARED_MOD_KWS if kwi.name != 'native'] +_ALLOWED_PROC_MACRO_KWS = { + 'rust_args', 'rust_dependency_map', 'sources', 'dependencies', + 'extra_files', 'link_args', 'link_depends', 'link_with', 'override_options', +} +_PROC_MACRO_KWS = [s for s in SHARED_LIB_KWS if s.name in _ALLOWED_PROC_MACRO_KWS] + def no_spaces_validator(arg: T.Optional[T.Union[str, T.List]]) -> T.Optional[str]: if any(bool(re.search(r'\s', x)) for x in arg): @@ -972,17 +978,20 @@ def compiler_target(self, state: ModuleState, args: T.List, kwargs: '_kwargs.Nat else: raise MesonException(f'No Rust compiler was requested for the {for_machine} machine') - # Allow a limited set of kwargs, but still use the full set of typed_kwargs() - # because it could be setting required default values. @FeatureNew('rust.proc_macro', '1.3.0') - @permittedKwargs({'rust_args', 'rust_dependency_map', 'sources', 'dependencies', 'extra_files', - 'link_args', 'link_depends', 'link_with', 'override_options'}) @typed_pos_args('rust.proc_macro', str, varargs=SOURCES_VARARGS) - @typed_kwargs('rust.proc_macro', *SHARED_LIB_KWS) + @typed_kwargs('rust.proc_macro', *_PROC_MACRO_KWS) def proc_macro(self, state: ModuleState, args: T.Tuple[str, SourcesVarargsType], kwargs: _kwargs.SharedLibrary) -> SharedLibrary: kwargs['native'] = MachineChoice.BUILD kwargs['rust_crate_type'] = 'proc-macro' kwargs['rust_args'] = kwargs['rust_args'] + ['--extern', 'proc_macro'] + + # Set any kwargs to default that haven't already been set + for s in SHARED_LIB_KWS: + if s.name in kwargs: + continue + kwargs[s.name] = s.default # type: ignore[literal-required] + target = state._interpreter.build_target(state.current_node, args, kwargs, SharedLibrary) return target From 6f325a24fe920637b871b4302f2153757889b1b8 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 8 May 2026 08:31:41 -0700 Subject: [PATCH 189/681] build: Remove use of permittedKwargs typed_kwargs is covering all of the cases, so it's just duplicate type checking --- mesonbuild/interpreter/interpreter.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 7a4a48d2a6f0..d3cb5c783f18 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -25,7 +25,7 @@ from ..dependencies import Dependency from ..depfile import DepFile from ..interpreterbase import ContainerTypeInfo, InterpreterBase, KwargInfo, typed_kwargs, typed_pos_args -from ..interpreterbase import noPosargs, noKwargs, permittedKwargs, noArgsFlattening, noSecondLevelHolderResolving, unholder_return +from ..interpreterbase import noPosargs, noKwargs, noArgsFlattening, noSecondLevelHolderResolving, unholder_return from ..interpreterbase import InterpreterException, InvalidArguments, InvalidCode, SubdirDoneRequest from ..interpreterbase import Disabler, disablerIfNotFound from ..interpreterbase import FeatureNew, FeatureDeprecated, FeatureBroken, FeatureNewKwargs @@ -1869,7 +1869,6 @@ def func_dependency(self, node: mparser.BaseNode, args: T.Tuple[T.List[str]], kw def func_disabler(self, node, args, kwargs): return Disabler() - @permittedKwargs(build.known_exe_kwargs) @typed_pos_args('executable', str, varargs=SOURCES_VARARGS) @typed_kwargs('executable', *EXECUTABLE_KWS) def func_executable(self, node: mparser.BaseNode, @@ -1877,7 +1876,6 @@ def func_executable(self, node: mparser.BaseNode, kwargs: kwtypes.Executable) -> T.Union[build.Executable, build.SharedLibrary]: return self.build_target(node, args, kwargs, build.Executable) - @permittedKwargs(build.known_stlib_kwargs) @typed_pos_args('static_library', str, varargs=SOURCES_VARARGS) @typed_kwargs('static_library', *STATIC_LIB_KWS) def func_static_lib(self, node: mparser.BaseNode, @@ -1885,7 +1883,6 @@ def func_static_lib(self, node: mparser.BaseNode, kwargs: kwtypes.StaticLibrary) -> build.StaticLibrary: return self.build_target(node, args, kwargs, build.StaticLibrary) - @permittedKwargs(build.known_shlib_kwargs) @typed_pos_args('shared_library', str, varargs=SOURCES_VARARGS) @typed_kwargs('shared_library', *SHARED_LIB_KWS) def func_shared_lib(self, node: mparser.BaseNode, @@ -1893,7 +1890,6 @@ def func_shared_lib(self, node: mparser.BaseNode, kwargs: kwtypes.SharedLibrary) -> build.SharedLibrary: return self.build_target(node, args, kwargs, build.SharedLibrary) - @permittedKwargs(known_library_kwargs) @typed_pos_args('both_libraries', str, varargs=SOURCES_VARARGS) @typed_kwargs('both_libraries', *LIBRARY_KWS) @noSecondLevelHolderResolving @@ -1903,7 +1899,6 @@ def func_both_lib(self, node: mparser.BaseNode, return self.build_both_libraries(node, args, kwargs) @FeatureNew('shared_module', '0.37.0') - @permittedKwargs(build.known_shmod_kwargs) @typed_pos_args('shared_module', str, varargs=SOURCES_VARARGS) @typed_kwargs('shared_module', *SHARED_MOD_KWS) def func_shared_module(self, node: mparser.BaseNode, @@ -1911,7 +1906,6 @@ def func_shared_module(self, node: mparser.BaseNode, kwargs: kwtypes.SharedModule) -> build.SharedModule: return self.build_target(node, args, kwargs, build.SharedModule) - @permittedKwargs(known_library_kwargs) @typed_pos_args('library', str, varargs=SOURCES_VARARGS) @typed_kwargs('library', *LIBRARY_KWS) @noSecondLevelHolderResolving @@ -1920,7 +1914,6 @@ def func_library(self, node: mparser.BaseNode, kwargs: kwtypes.Library) -> build.Executable: return self.build_library(node, args, kwargs) - @permittedKwargs(build.known_jar_kwargs) @typed_pos_args('jar', str, varargs=(str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList, build.ExtractedObjects, build.BuildTarget)) @typed_kwargs('jar', *JAR_KWS) def func_jar(self, node: mparser.BaseNode, @@ -1929,7 +1922,6 @@ def func_jar(self, node: mparser.BaseNode, return self.build_target(node, args, kwargs, build.Jar) @FeatureNewKwargs('build_target', '0.40.0', ['link_whole', 'override_options']) - @permittedKwargs(known_build_target_kwargs) @typed_pos_args('build_target', str, varargs=SOURCES_VARARGS) @typed_kwargs('build_target', *BUILD_TARGET_KWS) def func_build_target(self, node: mparser.BaseNode, From 7ed7d6cbdda93a4eb90a5b1cd98bfca62243dc24 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 8 May 2026 08:32:30 -0700 Subject: [PATCH 190/681] interpreterbase: delete permittedKwargs! Finally have typed_kwargs complete! yay! --- mesonbuild/interpreterbase/__init__.py | 2 -- mesonbuild/interpreterbase/decorators.py | 16 ---------------- 2 files changed, 18 deletions(-) diff --git a/mesonbuild/interpreterbase/__init__.py b/mesonbuild/interpreterbase/__init__.py index 88999d8b74df..887b93476687 100644 --- a/mesonbuild/interpreterbase/__init__.py +++ b/mesonbuild/interpreterbase/__init__.py @@ -34,7 +34,6 @@ 'noSecondLevelHolderResolving', 'unholder_return', 'disablerIfNotFound', - 'permittedKwargs', 'typed_operator', 'typed_pos_args', 'ContainerTypeInfo', @@ -98,7 +97,6 @@ noSecondLevelHolderResolving, unholder_return, disablerIfNotFound, - permittedKwargs, typed_pos_args, ContainerTypeInfo, KwargInfo, diff --git a/mesonbuild/interpreterbase/decorators.py b/mesonbuild/interpreterbase/decorators.py index f4cac4a57977..38b115431136 100644 --- a/mesonbuild/interpreterbase/decorators.py +++ b/mesonbuild/interpreterbase/decorators.py @@ -8,7 +8,6 @@ from .exceptions import InterpreterException, InvalidArguments from ._unholder import _unholder -from dataclasses import dataclass from functools import wraps import abc import itertools @@ -125,21 +124,6 @@ def wrapped(*wrapped_args: T.Any, **wrapped_kwargs: T.Any) -> T.Any: return ret return T.cast('TV_func', wrapped) -@dataclass(repr=False, eq=False) -class permittedKwargs: - permitted: T.Set[str] - - def __call__(self, f: TV_func) -> TV_func: - @wraps(f) - def wrapped(*wrapped_args: T.Any, **wrapped_kwargs: T.Any) -> T.Any: - kwargs = get_callee_args(wrapped_args)[2] - unknowns = set(kwargs).difference(self.permitted) - if unknowns: - ustr = ', '.join([f'"{u}"' for u in sorted(unknowns)]) - raise InvalidArguments(f'Got unknown keyword arguments {ustr}') - return f(*wrapped_args, **wrapped_kwargs) - return T.cast('TV_func', wrapped) - def typed_operator(operator: MesonOperator, types: T.Union[T.Type, T.Tuple[T.Type, ...]]) -> T.Callable[['_TV_FN_Operator'], '_TV_FN_Operator']: """Decorator that does type checking for operator calls. From d8ea413b0630a6f16ac45293a07e15607ebd8f5e Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 8 May 2026 08:43:41 -0700 Subject: [PATCH 191/681] ast/introspection: Use type_checking instead of known_kwargs We want to drop the latter, as it's just duplication at this point. --- mesonbuild/ast/introspection.py | 13 ++++++++++++- mesonbuild/build.py | 8 ++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py index 6f27ac7ea1aa..da277721ec9a 100644 --- a/mesonbuild/ast/introspection.py +++ b/mesonbuild/ast/introspection.py @@ -13,6 +13,7 @@ from ..build import Executable, Jar, SharedLibrary, SharedModule, StaticLibrary from ..compilers import detect_compiler_for from ..interpreterbase import InvalidArguments, SubProject, UnknownValue, Feature +from ..interpreter import type_checking from ..mesonlib import MachineChoice from ..options import OptionKey from ..mparser import BaseNode, ArrayNode, ElementaryNode, IdNode, FunctionNode, StringNode @@ -25,6 +26,16 @@ from .visitor import AstVisitor +_TARGET_KWARGS: T.Mapping[str, set[str]] = { + 'executable': {s.name for s in type_checking.EXECUTABLE_KWS}, + 'jar': {s.name for s in type_checking.JAR_KWS}, + 'library': {s.name for s in type_checking.LIBRARY_KWS}, + 'shared library': {s.name for s in type_checking.SHARED_LIB_KWS}, + 'shared module': {s.name for s in type_checking.SHARED_MOD_KWS}, + 'static library': {s.name for s in type_checking.STATIC_LIB_KWS}, + 'both libraries': {s.name for s in type_checking.LIBRARY_KWS}, +} + # TODO: it would be nice to not have to duplicate this BUILD_TARGET_FUNCTIONS = [ 'executable', 'jar', 'library', 'shared_library', 'shared_module', @@ -243,7 +254,7 @@ def build_target(self, node: BaseNode, args: T.List[TYPE_var], kwargs_raw: T.Dic extraf_nodes = v # Make sure nothing can crash when creating the build class - _kwargs_reduced = {k: v for k, v in kwargs.items() if k in targetclass.known_kwargs and k in {'install', 'build_by_default', 'build_always', 'name_prefix'}} + _kwargs_reduced = {k: v for k, v in kwargs.items() if k in _TARGET_KWARGS[targetclass.typename] and k in {'install', 'build_by_default', 'build_always', 'name_prefix'}} _kwargs_reduced = {k: v.value if isinstance(v, ElementaryNode) else v for k, v in _kwargs_reduced.items()} _kwargs_reduced = {k: v for k, v in _kwargs_reduced.items() if not isinstance(v, (BaseNode, UnknownValue))} kwargs_reduced = T.cast('BuildTargetKeywordArguments', _kwargs_reduced) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index b6ca70135cff..8447300cfcde 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -671,6 +671,8 @@ def needs_copy(self) -> bool: @dataclass(eq=False) class Target(HoldableObject, metaclass=SimpleABC): + typename: T.ClassVar[str] + name: str subdir: str subproject: 'SubProject' @@ -682,10 +684,6 @@ class Target(HoldableObject, metaclass=SimpleABC): extra_files: T.List[File] = field(default_factory=list) build_subdir: str = '' - @abc.abstractproperty - def typename(self) -> str: - pass - @abc.abstractmethod def type_suffix(self) -> str: pass @@ -2882,6 +2880,8 @@ def get_default_install_dir(self) -> T.Tuple[str, str]: return self.environment.get_shared_module_dir(), '{moduledir_shared}' class BothLibraries(SecondLevelHolder): + typename: T.ClassVar[str] = 'both libraries' + def __init__(self, shared: SharedLibrary, static: StaticLibrary, preferred_library: Literal['shared', 'static']) -> None: self._preferred_library = preferred_library self.shared = shared From d7ba76a8703e4d0b9b0af9d95e0db91b8950098b Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 8 May 2026 08:34:34 -0700 Subject: [PATCH 192/681] build|interpreter: Delete the known_*_target_kwargs They're now unused --- mesonbuild/build.py | 27 --------------------------- mesonbuild/interpreter/interpreter.py | 14 -------------- 2 files changed, 41 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 8447300cfcde..c9de9320a083 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -214,21 +214,6 @@ class JarKeywordArguments(BuildTargetKeywordArguments, total=False): 'win_subsystem', } -known_build_target_kwargs = ( - buildtarget_kwargs | - lang_arg_kwargs | - pch_kwargs | - vala_kwargs | - rust_kwargs | - cs_kwargs | - swift_kwargs) - -known_exe_kwargs = known_build_target_kwargs | {'implib', 'export_dynamic', 'pie', 'vs_module_defs', 'android_exe_type'} -known_shlib_kwargs = known_build_target_kwargs | {'version', 'soversion', 'vs_module_defs', 'darwin_versions', 'rust_abi', 'shortname'} -known_shmod_kwargs = known_build_target_kwargs | {'vs_module_defs', 'rust_abi'} -known_stlib_kwargs = known_build_target_kwargs | {'pic', 'prelink', 'rust_abi'} -known_jar_kwargs = known_exe_kwargs | {'main_class', 'java_resources'} - def _process_install_tag(install_tag: T.Optional[T.List[T.Optional[str]]], num_outputs: int) -> T.List[T.Optional[str]]: _install_tag: T.List[T.Optional[str]] @@ -786,8 +771,6 @@ def should_install(self) -> bool: return False class BuildTarget(Target): - known_kwargs = known_build_target_kwargs - rust_crate_type: RustCrateType # This set contains all the languages a linker can link natively @@ -2207,8 +2190,6 @@ def get_basename(self) -> str: class Executable(BuildTarget): - known_kwargs = known_exe_kwargs - typename = 'executable' def __init__( @@ -2338,8 +2319,6 @@ def is_linkable_target(self) -> bool: class StaticLibrary(BuildTarget): - known_kwargs = known_stlib_kwargs - typename = 'static library' def __init__( @@ -2515,8 +2494,6 @@ def _bundle_static_library(self, t: StaticTargetTypes, promoted: bool = False) - self.objects.append(t.extract_all_objects()) class SharedLibrary(BuildTarget): - known_kwargs = known_shlib_kwargs - typename = 'shared library' # Used by AIX to decide whether to archive shared library or not. @@ -2849,8 +2826,6 @@ def link(self, targets: T.List[BuildTargetTypes]) -> None: # A shared library that is meant to be used with dlopen rather than linking # into something else. class SharedModule(SharedLibrary): - known_kwargs = known_shmod_kwargs - typename = 'shared module' # Used by AIX to not archive shared library for dlopen mechanism @@ -3345,8 +3320,6 @@ def __repr__(self) -> str: return repr_str.format(self.__class__.__name__, self.get_id()) class Jar(BuildTarget): - known_kwargs = known_jar_kwargs - typename = 'jar' rust_crate_type = '' # type: ignore[assignment] diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index d3cb5c783f18..956eb0f69613 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -216,20 +216,6 @@ def dump_value(self, arr, list_sep, indent): line_len += v_len mlog.log(*line, sep=list_sep, display_timestamp=False) -known_library_kwargs = ( - build.known_shlib_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 = ( - known_library_kwargs | - build.known_exe_kwargs | - build.known_jar_kwargs | - {'target_type'} -) - class InterpreterRuleRelaxation(Enum): ''' Defines specific relaxations of the Meson rules. From e540b7ab7a68607b07a39fdd5fef2a4003c37ea2 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 8 May 2026 07:48:09 -0700 Subject: [PATCH 193/681] compilers: Use Language in a few more places --- mesonbuild/compilers/compilers.py | 6 +++--- mesonbuild/scripts/env2mfile.py | 5 ++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 3b780074d227..83d2c0a31f25 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -101,7 +101,7 @@ # This must be sorted, see sort_clink(). clink_langs = ('rust', 'd', 'cuda') + clib_langs -SUFFIX_TO_LANG = dict(itertools.chain(*( +SUFFIX_TO_LANG: T.Mapping[str, Language] = dict(itertools.chain(*( [(suffix, lang) for suffix in v] for lang, v in lang_suffixes.items()))) # Languages that should use LDFLAGS arguments when linking. @@ -111,7 +111,7 @@ soregex = re.compile(r'.*\.so(\.[0-9]+)?(\.[0-9]+)?(\.[0-9]+)?$') # Environment variables that each lang uses. -CFLAGS_MAPPING: T.Mapping[str, str] = { +CFLAGS_MAPPING: T.Mapping[Language, str] = { 'c': 'CFLAGS', 'cpp': 'CXXFLAGS', 'cuda': 'CUFLAGS', @@ -127,7 +127,7 @@ # All these are only for C-linkable languages; see `clink_langs` above. -def sort_clink(lang: str) -> int: +def sort_clink(lang: Language) -> int: ''' Sorting function to sort the list of languages according to reversed(compilers.clink_langs) and append the unknown langs in the end. diff --git a/mesonbuild/scripts/env2mfile.py b/mesonbuild/scripts/env2mfile.py index 3f85ab08a0b2..ee973a923902 100755 --- a/mesonbuild/scripts/env2mfile.py +++ b/mesonbuild/scripts/env2mfile.py @@ -17,6 +17,8 @@ if T.TYPE_CHECKING: import argparse + from ..compilers.compilers import Language + # Note: when adding arguments, please also add them to the completion # scripts in $MESONSRC/data/shell-completions/ def add_arguments(parser: 'argparse.ArgumentParser') -> None: @@ -326,7 +328,8 @@ def write_machine_file(infos: MachineInfo, ofilename: str, write_system_info: bo def detect_language_args_from_envvars(langname: str, envvar_suffix: str = '') -> T.Tuple[T.List[str], T.List[str]]: compile_args = [] if langname in compilers.CFLAGS_MAPPING: - compile_args = shlex.split(os.environ.get(compilers.CFLAGS_MAPPING[langname] + envvar_suffix, '')) + compile_args = shlex.split(os.environ.get( + compilers.CFLAGS_MAPPING[T.cast('Language', langname)] + envvar_suffix, '')) if langname in compilers.LANGUAGES_USING_CPPFLAGS: cppflags = tuple(shlex.split(os.environ.get('CPPFLAGS' + envvar_suffix, ''))) lang_compile_args = list(cppflags) + compile_args From a647420964ada5fdb5097d2592358e30a298eada Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 6 May 2026 12:20:34 -0700 Subject: [PATCH 194/681] build: Fix some of the typing issues with Target.get There's still some additional issues, but, this reduces some of the low hanging issues. Also, uses a TypeAlias for the Literal to avoid duplication --- mesonbuild/build.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index c9de9320a083..9fa22046eebe 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -21,7 +21,7 @@ from . import programs from .mesonlib import ( HoldableObject, SecondLevelHolder, SimpleABC, - File, MesonException, MachineChoice, PerMachine, OrderedSet, listify, + File, MesonException, MachineChoice, PerMachine, OrderedSet, classify_unity_sources, get_filenames_templates_dict, substitute_values, has_path_sep, is_parent_path, relpath, PerMachineDefaultable, @@ -60,6 +60,7 @@ ObjectTypes: TypeAlias = T.Union[str, 'File', 'ExtractedObjects', 'GeneratedTypes'] AnyTargetType: TypeAlias = T.Union['Target', 'CustomTargetIndex'] RustCrateType: TypeAlias = Literal['bin', 'lib', 'rlib', 'dylib', 'cdylib', 'staticlib', 'proc-macro'] + _LibraryType: TypeAlias = Literal['auto', 'shared', 'static'] class DFeatures(TypedDict): @@ -1838,23 +1839,25 @@ def process_vs_module_defs_kw(self, kwargs: ExecutableKeywordArguments | SharedM def extract_targets_as_list(self, kwargs: BuildTargetKeywordArguments, key: T.Literal['link_with', 'link_whole']) -> T.List[LibTypes]: bl_type = self.environment.coredata.optstore.get_value_for(OptionKey('default_both_libraries')) + assert isinstance(bl_type, str), 'for mypy' if bl_type == 'auto': if isinstance(self, StaticLibrary): bl_type = 'static' elif isinstance(self, SharedLibrary): bl_type = 'shared' + bl_type = T.cast('LibraryType', bl_type) self_libs: T.List[LibTypes] = self.link_targets if key == 'link_with' else self.link_whole_targets lib_list = [] - for lib in listify(kwargs.get(key, [])) + self_libs: - if isinstance(lib, (Target, BothLibraries)): + for lib in itertools.chain(kwargs.get(key, []), self_libs): + if isinstance(lib, (BuildTarget, BothLibraries)): lib_list.append(lib.get(bl_type)) else: lib_list.append(lib) return lib_list - def get(self, lib_type: T.Literal['static', 'shared'], recursive: bool = False) -> LibTypes: + def get(self, lib_type: _LibraryType, recursive: bool = False) -> LibTypes: """Base case used by BothLibraries""" return self @@ -2436,7 +2439,7 @@ def set_shared(self, shared_library: SharedLibrary) -> None: self.both_lib = copy.copy(shared_library) self.both_lib.both_lib = None - def get(self, lib_type: T.Literal['static', 'shared'], recursive: bool = False) -> LibTypes: + def get(self, lib_type: _LibraryType, recursive: bool = False) -> LibTypes: result = self if lib_type == 'shared': result = self.both_lib or self @@ -2794,7 +2797,7 @@ def set_static(self, static_library: StaticLibrary) -> None: self.both_lib = copy.copy(static_library) self.both_lib.both_lib = None - def get(self, lib_type: T.Literal['static', 'shared'], recursive: bool = False) -> LibTypes: + def get(self, lib_type: _LibraryType, recursive: bool = False) -> LibTypes: result = self if lib_type == 'static': result = self.both_lib or self @@ -2866,7 +2869,7 @@ def __init__(self, shared: SharedLibrary, static: StaticLibrary, preferred_libra def __repr__(self) -> str: return f'' - def get(self, lib_type: T.Literal['static', 'shared']) -> T.Union[StaticLibrary, SharedLibrary]: + def get(self, lib_type: _LibraryType) -> T.Union[StaticLibrary, SharedLibrary]: if lib_type == 'static': return self.static if lib_type == 'shared': @@ -2960,7 +2963,7 @@ def get_internal_static_libraries_recurse(self, result: OrderedSet[StaticTargetT def get_all_linked_targets(self) -> ImmutableListProtocol[BuildTargetTypes]: return [] - def get(self, lib_type: T.Literal['static', 'shared'], recursive: bool = False) -> LibTypes: + def get(self, lib_type: _LibraryType, recursive: bool = False) -> LibTypes: """Base case used by BothLibraries""" return self From 19767901da2aa59db01e625fc72110569c35ce33 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 6 May 2026 12:27:20 -0700 Subject: [PATCH 195/681] build: Use a overloadable helper for getting the _LibraryType This allows each child instance to override 'auto' for itself, without the parent knowing about them --- mesonbuild/build.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 9fa22046eebe..d94ccf1ac5b9 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -1837,15 +1837,13 @@ def process_vs_module_defs_kw(self, kwargs: ExecutableKeywordArguments | SharedM self.vs_module_defs = File.from_built_file(path.get_builddir(), path.get_filename()) self.process_link_depends([path]) - def extract_targets_as_list(self, kwargs: BuildTargetKeywordArguments, key: T.Literal['link_with', 'link_whole']) -> T.List[LibTypes]: + def _default_library_type(self) -> _LibraryType: bl_type = self.environment.coredata.optstore.get_value_for(OptionKey('default_both_libraries')) assert isinstance(bl_type, str), 'for mypy' - if bl_type == 'auto': - if isinstance(self, StaticLibrary): - bl_type = 'static' - elif isinstance(self, SharedLibrary): - bl_type = 'shared' - bl_type = T.cast('LibraryType', bl_type) + return T.cast('_LibraryType', bl_type) + + def extract_targets_as_list(self, kwargs: BuildTargetKeywordArguments, key: T.Literal['link_with', 'link_whole']) -> T.List[LibTypes]: + bl_type = self._default_library_type() self_libs: T.List[LibTypes] = self.link_targets if key == 'link_with' else self.link_whole_targets @@ -2383,6 +2381,10 @@ def post_init(self) -> None: self.filename = self.prefix + self.name + '.' + self.suffix self.outputs[0] = self.filename + def _default_library_type(self) -> _LibraryType: + bl = super()._default_library_type() + return 'static' if bl == 'auto' else bl + def determine_default_prefix_and_suffix(self) -> T.Tuple[str, str]: scheme = self.environment.coredata.get_option_for_target(self, 'namingscheme') assert isinstance(scheme, str), 'for mypy' @@ -2569,6 +2571,10 @@ def post_init(self) -> None: self.basic_filename_tpl = '{0.prefix}{0.name}.{0.suffix}' self.determine_filenames() + def _default_library_type(self) -> _LibraryType: + bl = super()._default_library_type() + return 'shared' if bl == 'auto' else bl + def get_link_deps_mapping(self, prefix: str) -> T.Mapping[str, str]: result: T.Dict[str, str] = {} mappings = dict(self.get_transitive_link_deps_mapping(prefix)) From dabf73216a3defc390bb56db8f4b50b4dcf7ad9c Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 6 May 2026 12:34:20 -0700 Subject: [PATCH 196/681] build: Sort out typing issues with BuildTarget helpers for linking These extract BothLibraries essentially, but need different logic for link_with and link_whole, as the latter cannot have shared_libraries or executables (generally). --- mesonbuild/build.py | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index d94ccf1ac5b9..4755bb5e44ce 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -914,8 +914,8 @@ def __init__( # we have to call process_compilers() first and we need to process libraries # from link_with and link_whole first. # See https://github.com/mesonbuild/meson/pull/11957#issuecomment-1629243208. - link_targets = self.extract_targets_as_list(kwargs, 'link_with') - link_whole_targets = self.extract_targets_as_list(kwargs, 'link_whole') + link_targets = self._extract_link_with(kwargs) + link_whole_targets = self._extract_link_whole(kwargs) self.link_targets.clear() self.link_whole_targets.clear() self.link(link_targets) @@ -1842,19 +1842,29 @@ def _default_library_type(self) -> _LibraryType: assert isinstance(bl_type, str), 'for mypy' return T.cast('_LibraryType', bl_type) - def extract_targets_as_list(self, kwargs: BuildTargetKeywordArguments, key: T.Literal['link_with', 'link_whole']) -> T.List[LibTypes]: + def _extract_link_with(self, kwargs: BuildTargetKeywordArguments) -> list[LibTypes]: bl_type = self._default_library_type() - self_libs: T.List[LibTypes] = self.link_targets if key == 'link_with' else self.link_whole_targets - - lib_list = [] - for lib in itertools.chain(kwargs.get(key, []), self_libs): + lib_list: list[LibTypes] = [] + for lib in itertools.chain(kwargs.get('link_with', []), self.link_targets): if isinstance(lib, (BuildTarget, BothLibraries)): lib_list.append(lib.get(bl_type)) else: lib_list.append(lib) return lib_list + def _extract_link_whole(self, kwargs: BuildTargetKeywordArguments) -> list[StaticTargetTypes]: + lib_list: list[StaticTargetTypes] = [] + for lib in itertools.chain(kwargs.get('link_whole', []), self.link_whole_targets): + if isinstance(lib, BothLibraries): + lib = lib.get('static') + if not isinstance(lib, StaticLibrary): + raise MesonBugException('Tried to statically link a non-static library. How did we get here?') + lib_list.append(lib) + else: + lib_list.append(lib) + return lib_list + def get(self, lib_type: _LibraryType, recursive: bool = False) -> LibTypes: """Base case used by BothLibraries""" return self @@ -3320,7 +3330,7 @@ class AliasTarget(RunTarget): typename = 'alias' - def __init__(self, name: str, dependencies: T.Sequence[Target], + def __init__(self, name: str, dependencies: T.Sequence[TargetDepends | RunTarget], subdir: str, subproject: SubProject, environment: Environment): super().__init__(name, [], dependencies, subdir, subproject, environment) From 521871a466f570495db1f7eee7993f3ee2b15ac5 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 6 May 2026 12:43:01 -0700 Subject: [PATCH 197/681] build: Use ABC and `Self` to mark CustomTargetBase This allows mypy to realize that `get` will not in fact return an invalid type. --- mesonbuild/build.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 4755bb5e44ce..ea6c4e050865 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -37,7 +37,7 @@ from .interpreterbase import FeatureNew, FeatureDeprecated, SubProject if T.TYPE_CHECKING: - from typing_extensions import Literal, TypeAlias, TypedDict + from typing_extensions import Literal, Self, TypeAlias, TypedDict from .arglist import CompilerArgs from .environment import Environment @@ -2954,7 +2954,7 @@ def flatten_command(cmd: T.Sequence[str | File | programs.Program | BuildTargetT return final_cmd, depend_files, dependencies -class CustomTargetBase: +class CustomTargetBase(metaclass=SimpleABC): ''' Base class for CustomTarget and CustomTargetIndex This base class can be used to provide a dummy implementation of some @@ -2979,10 +2979,11 @@ def get_internal_static_libraries_recurse(self, result: OrderedSet[StaticTargetT def get_all_linked_targets(self) -> ImmutableListProtocol[BuildTargetTypes]: return [] - def get(self, lib_type: _LibraryType, recursive: bool = False) -> LibTypes: + def get(self, lib_type: _LibraryType, recursive: bool = False) -> Self: """Base case used by BothLibraries""" return self + class CustomTarget(Target, CustomTargetBase): typename = 'custom' From 06b467dbd470470ad0cb0898cded25d62e93b3a4 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 7 May 2026 09:24:47 -0700 Subject: [PATCH 198/681] build: BuildTarget.link and .link_whole need to take iterables The problem with list is invariance. These also don't suffer from the string problem, so we don't have to deal with the str == Iterable[str] problem. --- mesonbuild/build.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index ea6c4e050865..2d426b11c46f 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -1589,14 +1589,14 @@ def get_external_deps(self) -> T.List[dependencies.Dependency]: def is_internal(self) -> bool: return False - def link(self, targets: T.List[BuildTargetTypes]) -> None: + def link(self, targets: T.Iterable[BuildTargetTypes]) -> None: for t in targets: self.check_can_link_together(t) self.link_targets.append(t) def link_whole( self, - targets: T.List[StaticTargetTypes], + targets: T.Iterable[StaticTargetTypes], promoted: bool = False) -> None: for t in targets: self.check_can_link_together(t) @@ -2461,7 +2461,7 @@ def get(self, lib_type: _LibraryType, recursive: bool = False) -> LibTypes: def link_whole( self, - targets: T.List[StaticTargetTypes], + targets: T.Iterable[StaticTargetTypes], promoted: bool = False) -> None: for t in targets: self.check_can_link_together(t) @@ -2477,7 +2477,7 @@ def link_whole( self._bundle_static_library(lib, True) self.link_whole_targets.append(t) - def link(self, targets: T.List[BuildTargetTypes]) -> None: + def link(self, targets: T.Iterable[BuildTargetTypes]) -> None: for t in targets: if self.install and t.is_internal(): # When we're a static library and we link_with to an @@ -2823,7 +2823,7 @@ def get(self, lib_type: _LibraryType, recursive: bool = False) -> LibTypes: def link_whole( self, - targets: T.List[StaticTargetTypes], + targets: T.Iterable[StaticTargetTypes], promoted: bool = False) -> None: for t in targets: self.check_can_link_together(t) @@ -2833,7 +2833,7 @@ def link_whole( raise InvalidArguments(msg) self.link_whole_targets.append(t) - def link(self, targets: T.List[BuildTargetTypes]) -> None: + def link(self, targets: T.Iterable[BuildTargetTypes]) -> None: for t in targets: if isinstance(t, StaticLibrary) and not t.pic: msg = f"Can't link non-PIC static library {t.name!r} into shared library {self.name!r}. " From ad8be1903d8f6fdd6345e7151c1c4246efb86cca Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 7 May 2026 12:08:28 -0700 Subject: [PATCH 199/681] build: fix ignored types in CustomTarget.get_target_dependencies Files and ExtractedObjects are also not valid. --- mesonbuild/build.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 2d426b11c46f..f81a5e98c81d 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -3082,8 +3082,8 @@ def __repr__(self) -> str: repr_str = "<{0} {1}: {2}>" return repr_str.format(self.__class__.__name__, self.get_id(), self.command) - def get_target_dependencies(self) -> T.List[TargetDepends]: - deps: T.List[TargetDepends] = [] + def get_target_dependencies(self) -> T.List[TargetDepends | File | ExtractedObjects]: + deps: T.List[TargetDepends | File | ExtractedObjects] = [] deps.extend(self.dependencies) deps.extend(self.extra_depends) for c in self.sources: From 62e76713ddefe8ad9b7cf0d6d7a833a1cedb8f75 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 7 May 2026 12:10:11 -0700 Subject: [PATCH 200/681] build: Correctly use the ObjectTypes annotations for objects --- mesonbuild/backend/backends.py | 2 +- mesonbuild/build.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 440df274d253..45c324f7486a 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -492,7 +492,7 @@ def determine_ext_objs(self, objects: build.ExtractedObjects) -> T.List[str]: return unique_list(obj_list) def _flatten_object_list(self, target: build.BuildTarget, - objects: T.Sequence[T.Union[str, 'File', build.ExtractedObjects]], + objects: T.Sequence[build.ObjectTypes], proj_dir_to_build_root: str) -> T.Tuple[T.List[str], T.List[build.BuildTargetTypes]]: obj_list: T.List[str] = [] deps: T.List[build.BuildTargetTypes] = [] diff --git a/mesonbuild/build.py b/mesonbuild/build.py index f81a5e98c81d..708731c163bd 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -569,7 +569,7 @@ class ExtractedObjects(HoldableObject): target: 'BuildTarget' srclist: T.List[File] = field(default_factory=list) genlist: T.List['GeneratedTypes'] = field(default_factory=list) - objlist: T.List[T.Union[str, 'File', 'ExtractedObjects']] = field(default_factory=list) + objlist: T.List[ObjectTypes] = field(default_factory=list) recursive: bool = True pch: bool = False @@ -1528,7 +1528,7 @@ def get_dependencies_recurse(self, result: OrderedSet[BuildTargetTypes], def get_sources(self) -> T.List[File]: return self.sources - def get_objects(self) -> T.List[T.Union[str, 'File', 'ExtractedObjects']]: + def get_objects(self) -> T.List[ObjectTypes]: return self.objects def get_generated_sources(self) -> T.List['GeneratedTypes']: From 264a0a34cd5016035ce7d0f33501aac2204c7a92 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 7 May 2026 12:20:14 -0700 Subject: [PATCH 201/681] build: Fix CustomTargetIndex.is_linkable_target when holding CompileTarget --- mesonbuild/build.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 708731c163bd..f402c0b2c33b 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -3275,6 +3275,9 @@ def get_generated_headers(self) -> T.List[File]: gen_headers += [File(True, dep.subdir, o) for o in dep.get_outputs()] return gen_headers + def is_linkable_output(self, output: str) -> bool: + return False + class RunTarget(Target): typename = 'run' From df683e65cb93fddb2f814f498c4fce08ff2681f2 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 7 May 2026 12:23:04 -0700 Subject: [PATCH 202/681] build: CustomTargetIndex.extract_all_objects should only return that index's output --- mesonbuild/build.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index f402c0b2c33b..4869fe62b34d 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -3491,8 +3491,8 @@ def is_internal(self) -> bool: suf = os.path.splitext(self.output)[-1] return suf in {'.a', '.lib'} and not self.should_install() - def extract_all_objects(self) -> T.List[T.Union[str, 'ExtractedObjects']]: - return self.target.extract_all_objects() + def extract_all_objects(self) -> T.Union[str, 'ExtractedObjects']: + return self.output def get_custom_install_dir(self) -> T.List[T.Union[str, Literal[False]]]: return self.target.get_custom_install_dir() From 38c7d85ade9e1ee86f36e9ff304fc3a30e5a0ba8 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 7 May 2026 12:24:32 -0700 Subject: [PATCH 203/681] build: Fix CompileTarget's kwarg construction for BuildTarget --- mesonbuild/build.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 4869fe62b34d..65c716814854 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -3238,9 +3238,9 @@ def __init__(self, dependencies: T.List[dependencies.Dependency], depends: T.List[BuildTargetTypes]): compilers = {compiler.get_language(): compiler} - kwargs = { + kwargs: BuildTargetKeywordArguments = { 'build_by_default': False, - 'language_args': {compiler.language: compile_args}, + 'language_args': defaultdict(list, [(compiler.language, compile_args)]), 'include_directories': include_directories, 'dependencies': dependencies, } From 8ea9001dbd23ea03b18440b8c190cec2a81c757b Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 7 May 2026 12:39:10 -0700 Subject: [PATCH 204/681] build: sort out remaining problems with link The typing is quite complex, but seems to work. This also helps resolve the fact that Executable's are actually linkable on Windows (and required in some cases) at this level. --- mesonbuild/build.py | 36 ++++++++++++++++++-------------- mesonbuild/dependencies/base.py | 4 ++-- mesonbuild/interpreter/kwargs.py | 2 +- mesonbuild/modules/gnome.py | 2 +- mesonbuild/modules/rust.py | 6 +++--- 5 files changed, 27 insertions(+), 23 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 65c716814854..32b2079c6ace 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -55,6 +55,7 @@ GeneratedTypes: TypeAlias = T.Union['CustomTarget', 'CustomTargetIndex', 'GeneratedList'] LibTypes: TypeAlias = T.Union['SharedLibrary', 'StaticLibrary', 'CustomTarget', 'CustomTargetIndex'] + LinkableTargetTypes: TypeAlias = T.Union['SharedLibrary', 'StaticLibrary', 'CustomTarget', 'CustomTargetIndex', 'Executable'] BuildTargetTypes: TypeAlias = T.Union['BuildTarget', 'CustomTarget', 'CustomTargetIndex'] StaticTargetTypes: TypeAlias = T.Union['StaticLibrary', 'CustomTarget', 'CustomTargetIndex'] ObjectTypes: TypeAlias = T.Union[str, 'File', 'ExtractedObjects', 'GeneratedTypes'] @@ -100,7 +101,7 @@ class BuildTargetKeywordArguments(TypedDict, total=False): link_depends: T.List[T.Union[File, BuildTargetTypes]] link_language: Language link_whole: T.List[StaticTargetTypes] - link_with: T.List[BuildTargetTypes] + link_with: T.List[LinkableTargetTypes] name_prefix: T.Optional[str] name_suffix: T.Optional[str] native: MachineChoice @@ -810,7 +811,7 @@ def __init__( self.external_deps: T.List[dependencies.Dependency] = [] self.include_dirs: T.List['IncludeDirs'] = [] self.link_language: T.Optional[Language] = kwargs.get('link_language') - self.link_targets: T.List[BuildTargetTypes] = [] + self.link_targets: T.List[LinkableTargetTypes] = [] self.link_whole_targets: T.List[StaticTargetTypes] = [] self.depend_files = kwargs.get('depend_files', []) self.link_depends: T.List[T.Union[File, BuildTargetTypes]] = [] @@ -1589,7 +1590,7 @@ def get_external_deps(self) -> T.List[dependencies.Dependency]: def is_internal(self) -> bool: return False - def link(self, targets: T.Iterable[BuildTargetTypes]) -> None: + def link(self, targets: T.Iterable[LinkableTargetTypes]) -> None: for t in targets: self.check_can_link_together(t) self.link_targets.append(t) @@ -1842,15 +1843,15 @@ def _default_library_type(self) -> _LibraryType: assert isinstance(bl_type, str), 'for mypy' return T.cast('_LibraryType', bl_type) - def _extract_link_with(self, kwargs: BuildTargetKeywordArguments) -> list[LibTypes]: + def _extract_link_with(self, kwargs: BuildTargetKeywordArguments) -> list[LinkableTargetTypes]: bl_type = self._default_library_type() - lib_list: list[LibTypes] = [] + lib_list: list[LinkableTargetTypes] = [] for lib in itertools.chain(kwargs.get('link_with', []), self.link_targets): - if isinstance(lib, (BuildTarget, BothLibraries)): - lib_list.append(lib.get(bl_type)) - else: + if isinstance(lib, (CustomTarget, CustomTargetIndex)): lib_list.append(lib) + else: + lib_list.append(lib.get(bl_type)) return lib_list def _extract_link_whole(self, kwargs: BuildTargetKeywordArguments) -> list[StaticTargetTypes]: @@ -1865,10 +1866,6 @@ def _extract_link_whole(self, kwargs: BuildTargetKeywordArguments) -> list[Stati lib_list.append(lib) return lib_list - def get(self, lib_type: _LibraryType, recursive: bool = False) -> LibTypes: - """Base case used by BothLibraries""" - return self - def determine_rpath_dirs(self) -> T.Tuple[str, ...]: result: OrderedSet[str] if self.environment.coredata.optstore.get_value_for(OptionKey('layout')) == 'mirror': @@ -2303,6 +2300,10 @@ def post_init(self) -> None: name += '_' + self.suffix self.debug_filename = name + '.pdb' + def get(self, lib_type: _LibraryType, recursive: bool = False) -> LinkableTargetTypes: + """Base case used by BothLibraries""" + return self + def get_default_install_dir(self) -> T.Tuple[str, str]: return self.environment.get_bindir(), '{bindir}' @@ -2451,7 +2452,7 @@ def set_shared(self, shared_library: SharedLibrary) -> None: self.both_lib = copy.copy(shared_library) self.both_lib.both_lib = None - def get(self, lib_type: _LibraryType, recursive: bool = False) -> LibTypes: + def get(self, lib_type: _LibraryType, recursive: bool = False) -> LinkableTargetTypes: result = self if lib_type == 'shared': result = self.both_lib or self @@ -2477,7 +2478,7 @@ def link_whole( self._bundle_static_library(lib, True) self.link_whole_targets.append(t) - def link(self, targets: T.Iterable[BuildTargetTypes]) -> None: + def link(self, targets: T.Iterable[LinkableTargetTypes]) -> None: for t in targets: if self.install and t.is_internal(): # When we're a static library and we link_with to an @@ -2813,7 +2814,7 @@ def set_static(self, static_library: StaticLibrary) -> None: self.both_lib = copy.copy(static_library) self.both_lib.both_lib = None - def get(self, lib_type: _LibraryType, recursive: bool = False) -> LibTypes: + def get(self, lib_type: _LibraryType, recursive: bool = False) -> LinkableTargetTypes: result = self if lib_type == 'static': result = self.both_lib or self @@ -2833,7 +2834,7 @@ def link_whole( raise InvalidArguments(msg) self.link_whole_targets.append(t) - def link(self, targets: T.Iterable[BuildTargetTypes]) -> None: + def link(self, targets: T.Iterable[LinkableTargetTypes]) -> None: for t in targets: if isinstance(t, StaticLibrary) and not t.pic: msg = f"Can't link non-PIC static library {t.name!r} into shared library {self.name!r}. " @@ -3366,6 +3367,9 @@ def __init__(self, name: str, subdir: str, subproject: SubProject, for_machine: self.main_class = kwargs.get('main_class', '') self.java_resources: T.Optional[StructuredSources] = kwargs.get('java_resources', None) + def _extract_link_with(self, kwargs: BuildTargetKeywordArguments) -> list[LinkableTargetTypes]: + return kwargs['link_with'] + def get_main_class(self) -> str: return self.main_class diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index 1c1d91108fed..51f5822233ca 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -28,7 +28,7 @@ from ..environment import Environment from ..interpreterbase import FeatureCheckBase from ..build import ( - CustomTarget, IncludeDirs, CustomTargetIndex, LibTypes, + CustomTarget, IncludeDirs, CustomTargetIndex, LinkableTargetTypes, StaticLibrary, StructuredSources, ExtractedObjects, GeneratedTypes ) from ..interpreter.type_checking import PkgConfigDefineType @@ -309,7 +309,7 @@ class InternalDependency(Dependency): def __init__(self, version: str, incdirs: T.Optional[T.List['IncludeDirs']] = None, compile_args: T.Optional[T.List[str]] = None, link_args: T.Optional[T.List[str]] = None, - libraries: T.Optional[T.List[LibTypes]] = None, + libraries: T.Optional[T.List[LinkableTargetTypes]] = None, whole_libraries: T.Optional[T.List[T.Union[StaticLibrary, CustomTarget, CustomTargetIndex]]] = None, sources: T.Optional[T.Sequence[T.Union[mesonlib.File, GeneratedTypes, StructuredSources]]] = None, extra_files: T.Optional[T.Sequence[mesonlib.File]] = None, diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index ae6c2f6f56e9..0ae9d79e9d1a 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -516,7 +516,7 @@ class FuncDeclareDependency(TypedDict): include_directories: T.List[T.Union[build.IncludeDirs, str]] link_args: T.List[str] link_whole: T.List[build.StaticTargetTypes] - link_with: T.List[build.BuildTargetTypes] + link_with: T.List[build.LinkableTargetTypes] objects: T.List[build.ExtractedObjects] sources: T.List[T.Union[FileOrString, build.GeneratedTypes]] variables: T.Dict[str, str] diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 56294495bcbd..e24831ce3695 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -2271,7 +2271,7 @@ def generate_vapi(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'Gener cmd += ['--metadatadir=' + source_dir] inputs: T.List[T.Union[mesonlib.File, GirTarget]] = [] - link_with: T.List[build.LibTypes] = [] + link_with: T.List[build.LinkableTargetTypes] = [] i: CustomTargetInputs for i in kwargs['sources']: if isinstance(i, str): diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index a2605d9f3a22..7f623385ff2b 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -31,7 +31,7 @@ if T.TYPE_CHECKING: from . import ModuleState from .. import cargo - from ..build import BuildTargetTypes, ExecutableKeywordArguments, GeneratedTypes, IncludeDirs, LibTypes + from ..build import ExecutableKeywordArguments, GeneratedTypes, IncludeDirs, LinkableTargetTypes from ..cargo.interpreter import RUST_ABI from ..compilers.compilers import Language from ..compilers.rust import RustCompiler @@ -53,7 +53,7 @@ class FuncRustTest(_kwargs.BaseTest, T.Generic[ArgsType]): args: T.List[ArgsType] dependencies: T.List[T.Union[Dependency, ExternalLibrary]] is_parallel: bool - link_with: T.List[LibTypes] + link_with: T.List[LinkableTargetTypes] link_whole: T.List[T.Union[StaticLibrary, CustomTarget, CustomTargetIndex]] rust_args: T.List[str] @@ -649,7 +649,7 @@ def test_common(self, funcname: str, state: ModuleState, args: T.Tuple[str, Buil 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', []) + T.cast('T.List[BuildTargetTypes]', kwargs['link_with']) + new_target_kwargs['link_with'] = new_target_kwargs.get('link_with', []) + kwargs['link_with'] new_target_kwargs['link_whole'] = new_target_kwargs.get('link_whole', []) + kwargs['link_whole'] lang_args = base_target.extra_args.copy() From bf3b2f32ecfe4c0b20ca1649cb015c1a3de878ab Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 7 May 2026 12:45:54 -0700 Subject: [PATCH 205/681] run_mypy: Add build module! --- run_mypy.py | 1 + 1 file changed, 1 insertion(+) diff --git a/run_mypy.py b/run_mypy.py index 524b4f85bbdf..7a6d29c699ce 100755 --- a/run_mypy.py +++ b/run_mypy.py @@ -29,6 +29,7 @@ # specific files 'mesonbuild/arglist.py', + 'mesonbuild/build.py', 'mesonbuild/backend/backends.py', 'mesonbuild/backend/nonebackend.py', 'mesonbuild/cmdline.py', From 8b3d11f4b4dd1c0ecff4f94b2d5f5b97d39b57d0 Mon Sep 17 00:00:00 2001 From: Marcel Telka Date: Sun, 10 May 2026 19:28:31 +0200 Subject: [PATCH 206/681] test cases: illumos does not support -fsanitize=address --- unittests/linuxliketests.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/unittests/linuxliketests.py b/unittests/linuxliketests.py index a79bd6d767ee..72c958f150d5 100644 --- a/unittests/linuxliketests.py +++ b/unittests/linuxliketests.py @@ -472,6 +472,8 @@ def test_c_link_args_and_env(self): raise SkipTest('asan not available on Cygwin') if is_openbsd(): raise SkipTest('-fsanitize=address is not supported on OpenBSD') + if is_sunos(): + raise SkipTest('-fsanitize=address is not supported on illumos') testdir = os.path.join(self.common_test_dir, '1 trivial') env = {'CFLAGS': '-fsanitize=address'} @@ -1024,6 +1026,8 @@ def test_env_cflags_ldflags(self): raise SkipTest('asan not available on Cygwin') if is_openbsd(): raise SkipTest('-fsanitize=address is not supported on OpenBSD') + if is_sunos(): + raise SkipTest('-fsanitize=address is not supported on illumos') testdir = os.path.join(self.common_test_dir, '1 trivial') env = {'CFLAGS': '-fsanitize=address', 'LDFLAGS': '-I.'} From 90a444e6a4fce75ce7ad2d07af933f57d1640a83 Mon Sep 17 00:00:00 2001 From: Marcel Telka Date: Sun, 10 May 2026 21:18:14 +0200 Subject: [PATCH 207/681] codegen: make yacc args compatible with byacc --- mesonbuild/modules/codegen.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mesonbuild/modules/codegen.py b/mesonbuild/modules/codegen.py index c3fdff279417..d90a12dd592e 100644 --- a/mesonbuild/modules/codegen.py +++ b/mesonbuild/modules/codegen.py @@ -411,7 +411,7 @@ def yacc_method(self, state: ModuleState, args: T.Tuple, kwargs: FindYaccKwargs) node=state.current_node) return YaccGenerator(name, bin) - yacc_args: T.List[str] = ['@INPUT@', '-o', '@OUTPUT0@'] + yacc_args: T.List[str] = [] impl = T.cast('YaccImpls', bin.name) if impl == 'yacc' and isinstance(bin, ExternalProgram): @@ -433,6 +433,7 @@ def yacc_method(self, state: ModuleState, args: T.Tuple, kwargs: FindYaccKwargs) 'output location be configurable, and may not work.', fatal=False) yacc_args.append('-H') + yacc_args.extend(['-o', '@OUTPUT0@', '@INPUT@']) return YaccGenerator(name, bin, T.cast('ImmutableListProtocol[str]', yacc_args)) From 8b7ee37be38238191670bc521f0f9ccd957ae7bd Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 8 May 2026 18:37:15 +0200 Subject: [PATCH 208/681] ninjabackend: fix incorrect calls to Compiler Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 38a596fd86d8..01f682f7975d 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -3365,8 +3365,8 @@ def handle_cpp_import_std(self, target: build.BuildTarget, compiler): mod_file = 'gcm.cache/std.gcm' mod_obj_file = 'std.o' elem = NinjaBuildElement(self.all_outputs, [mod_file, mod_obj_file], 'CUSTOM_COMMAND', []) - compile_args = compiler.get_option_compile_args(target, self.environment) - compile_args += compiler.get_option_std_args(target, self.environment) + compile_args = compiler.get_option_compile_args(target, target.subproject) + compile_args += compiler.get_option_std_args(target, target.subproject) compile_args += ['-c', '-fmodules', '-fsearch-include-path', 'bits/std.cc'] elem.add_item('COMMAND', compiler.exelist + compile_args) self.add_build(elem) @@ -3383,8 +3383,8 @@ def handle_cpp_import_std(self, target: build.BuildTarget, compiler): raise SystemExit('VS std import header could not be located.') in_file_str = str(in_file) elem = NinjaBuildElement(self.all_outputs, [mod_file, mod_obj_file], 'CUSTOM_COMMAND', [in_file_str]) - compile_args = compiler.get_option_compile_args(target, self.environment) - compile_args += compiler.get_option_std_args(target, self.environment) + compile_args = compiler.get_option_compile_args(target, target.subproject) + compile_args += compiler.get_option_std_args(target, target.subproject) compile_args += ['/nologo', '/c', '/O2', in_file_str] elem.add_item('COMMAND', compiler.exelist + compile_args) self.add_build(elem) From 64a6505e4f3304252790a29fd9a4c65b33d076c5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 8 May 2026 16:53:43 +0200 Subject: [PATCH 209/681] ninjabackend: move restat from extra to specific argument This fixes some complaints from mypy about extra being (possibly) passed via both **options and the explicit keyword arguments. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 01f682f7975d..86c0ce4c3f87 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -58,7 +58,6 @@ class NinjaRuleArgs(TypedDict, total=False): rspable: bool rspfile_quote_style: RSPFileSyntax - extra: T.Optional[str] FORTRAN_INCLUDE_PAT = r"^\s*#?include\s*['\"](\w+\.\w+)['\"]" @@ -179,7 +178,8 @@ class NinjaRule: def __init__(self, rule: str, command: CommandArgs, args: CommandArgs, description: str, rspable: bool = False, deps: T.Optional[str] = None, depfile: T.Optional[str] = None, extra: T.Optional[str] = None, - rspfile_quote_style: RSPFileSyntax = RSPFileSyntax.GCC): + rspfile_quote_style: RSPFileSyntax = RSPFileSyntax.GCC, + restat: bool = False): def strToCommandArg(c: T.Union[NinjaCommandArg, str]) -> NinjaCommandArg: if isinstance(c, NinjaCommandArg): @@ -212,6 +212,7 @@ def strToCommandArg(c: T.Union[NinjaCommandArg, str]) -> NinjaCommandArg: self.deps = deps # depstyle 'gcc' or 'msvc' self.depfile = depfile self.extra = extra + self.restat = restat self.rspable = rspable # if a rspfile can be used self.refcount = 0 self.rsprefcount = 0 @@ -264,6 +265,8 @@ def rule_iter() -> T.Iterable[str]: if self.depfile: outfile.write(f' depfile = {self.depfile}\n') outfile.write(f' description = {self.description}\n') + if self.restat: + outfile.write(' restat = 1\n') if self.extra: for l in self.extra.split('\n'): outfile.write(' ') @@ -1423,13 +1426,13 @@ def generate_rules(self) -> None: # Ninja errors out if you have deps = gcc but no depfile, so we must # have two rules for custom commands. self.add_rule(NinjaRule('CUSTOM_COMMAND', ['$COMMAND'], [], '$DESC', - extra='restat = 1')) + restat=True)) self.add_rule(NinjaRule('CUSTOM_COMMAND_DEP', ['$COMMAND'], [], '$DESC', deps='gcc', depfile='$DEPFILE', - extra='restat = 1')) + restat=True)) self.add_rule(NinjaRule('CUSTOM_COMMAND_MSVC_DEP', ['$COMMAND'], [], '$DESC', deps='msvc', - extra='restat = 1')) + restat=True)) self.add_rule(NinjaRule('COPY_FILE', self.environment.get_build_command() + ['--internal', 'copy'], ['$in', '$out'], 'Copying $in to $out')) @@ -2540,8 +2543,7 @@ def generate_dynamic_link_rules(self) -> None: symrule = 'SHSYM' symcmd = args + ['$CROSS'] syndesc = 'Generating symbol file $out' - synstat = 'restat = 1' - self.add_rule(NinjaRule(symrule, symcmd, [], syndesc, extra=synstat)) + self.add_rule(NinjaRule(symrule, symcmd, [], syndesc, restat=True)) def generate_java_compile_rule(self, compiler) -> None: rule = self.compiler_to_rule_name(compiler) @@ -2572,7 +2574,7 @@ def generate_vala_compile_rules(self, compiler) -> None: self.add_rule(NinjaRule(rule, command + args, [], description, depfile=depfile, deps=depstyle, - extra='restat = 1')) + restat=True)) def generate_cython_compile_rules(self, compiler: 'Compiler') -> None: rule = self.compiler_to_rule_name(compiler) @@ -2587,7 +2589,7 @@ def generate_cython_compile_rules(self, compiler: 'Compiler') -> None: self.add_rule(NinjaRule(rule, command + args, [], description, depfile=depfile, - extra='restat = 1')) + restat=True)) def generate_rust_compile_rules(self, compiler) -> None: rule = self.compiler_to_rule_name(compiler) @@ -2614,7 +2616,7 @@ def generate_swift_compile_rules(self, compiler) -> None: command = invoc + ['$ARGS', '$in'] description = 'Compiling Swift source $in' - self.add_rule(NinjaRule(rule, command, [], description, extra='restat = 1')) + self.add_rule(NinjaRule(rule, command, [], description, restat=True)) def use_dyndeps_for_fortran(self) -> bool: '''Use the new Ninja feature for scanning dependencies during build, @@ -2643,7 +2645,7 @@ def generate_fortran_dep_hack(self, crstr: str) -> None: self.add_rule_comment(NinjaComment('''Workaround for these issues: https://groups.google.com/forum/#!topic/ninja-build/j-2RfBIOd_8 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) - self.add_rule(NinjaRule(rule, cmd, [], 'Dep hack', extra='restat = 1')) + self.add_rule(NinjaRule(rule, cmd, [], 'Dep hack', restat=True)) def generate_llvm_ir_compile_rule(self, compiler) -> None: if self.created_llvm_ir_rule[compiler.for_machine]: @@ -2708,11 +2710,12 @@ def generate_compile_rule_for(self, langname: str, compiler: Compiler) -> None: return crstr = self.get_rule_suffix(compiler.for_machine) options = self._rsp_options(compiler) + restat = False if langname == 'fortran': self.generate_fortran_dep_hack(crstr) # gfortran does not update the modification time of *.mod files, therefore restat is needed. # See also: https://github.com/ninja-build/ninja/pull/2275 - options['extra'] = 'restat = 1' + restat = True rule = self.compiler_to_rule_name(compiler) if langname == 'cuda': # for cuda, we manually escape target name ($out) as $CUDA_ESCAPED_TARGET because nvcc doesn't support `-MQ` flag @@ -2729,7 +2732,7 @@ def generate_compile_rule_for(self, langname: str, compiler: Compiler) -> None: deps = 'gcc' depfile = '$DEPFILE' self.add_rule(NinjaRule(rule, command, args, description, **options, - deps=deps, depfile=depfile)) + deps=deps, depfile=depfile, restat=restat)) def generate_pch_rule_for(self, langname: str, compiler: Compiler) -> None: if langname not in {'c', 'cpp'}: From e3b13d66aec8288d30ef6755ee53630ad9130ffe Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 8 May 2026 16:57:28 +0200 Subject: [PATCH 210/681] ninjabackend: remove some rust-related mypy errors Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 10 ++++++---- mesonbuild/build.py | 3 +++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 86c0ce4c3f87..83276ea81edd 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -41,7 +41,7 @@ from typing_extensions import Literal, TypedDict from .._typing import ImmutableListProtocol - from ..build import ExtractedObjects, LibTypes + from ..build import ExtractedObjects from ..compilers.compilers import Language from ..compilers.cs import CsCompiler from ..compilers.fortran import FortranCompiler @@ -730,7 +730,8 @@ def generate_rust_project_json(self) -> None: return with open(os.path.join(self.environment.get_build_dir(), 'rust-project.json'), 'w', encoding='utf-8') as f: - sysroot = self.environment.coredata.compilers.host['rust'].get_sysroot() + compiler = T.cast('RustCompiler', self.environment.coredata.compilers.host['rust']) + sysroot = compiler.get_sysroot() json.dump( { "sysroot": sysroot, @@ -1989,7 +1990,7 @@ def _get_rust_crate_name(target_name: str) -> str: return crate_name.split('+', 1)[0] @staticmethod - def _get_rust_dependency_name(target: build.BuildTarget, dependency: LibTypes) -> str: + def _get_rust_dependency_name(target: build.BuildTarget, dependency: build.BuildTarget) -> str: crate_name_raw = target.rust_dependency_map.get(dependency.name, None) if crate_name_raw is None: dependency_crate_name = NinjaBackend._get_rust_crate_name(dependency.name) @@ -2134,6 +2135,7 @@ def _link_library(libname: str, static: bool, bundle: bool = False) -> None: if d not in itertools.chain(target.link_targets, target.link_whole_targets): # Indirect Rust ABI dependency, we only need its path in linkdirs. continue + assert isinstance(d, build.BuildTarget) # specify `extern CRATE_NAME=OUTPUT_FILE` for each Rust # dependency, so that collisions with libraries in rustc's # sysroot don't cause ambiguity @@ -2201,7 +2203,7 @@ def _link_library(libname: str, static: bool, bundle: bool = False) -> None: args.extend(f'-Clink-arg={a}' for a in target.get_used_stdlib_args('rust')) has_shared_deps = any(isinstance(dep, build.SharedLibrary) for dep in target_deps) - has_rust_shared_deps = any(dep.uses_rust() + has_rust_shared_deps = any(isinstance(dep, build.SharedLibrary) and dep.uses_rust() and dep.rust_crate_type == 'dylib' for dep in target_deps) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 32b2079c6ace..7cb081458322 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2984,6 +2984,9 @@ def get(self, lib_type: _LibraryType, recursive: bool = False) -> Self: """Base case used by BothLibraries""" return self + def uses_rust_abi(self) -> bool: + return False + class CustomTarget(Target, CustomTargetBase): From 30445fbd186547fceb7ce450aa0fb81e8bc989bf Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 8 May 2026 17:18:32 +0200 Subject: [PATCH 211/681] ninjabackend: do not use hasattr Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 83276ea81edd..7086be5ecdcf 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1332,7 +1332,7 @@ def generate_coverage_command(self, elem: NinjaBuildElement, outputs: T.List[str exe_args += ['--llvm-cov', llvm_cov_exe] for target in targets: - if not hasattr(target, 'compilers'): + if not isinstance(target, build.BuildTarget): continue for compiler in target.compilers.values(): if compiler.get_id() == 'clang' and not compiler.info.is_darwin(): From 15d5d657b587f1afcc7e461d62c95c85fcafc1e8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 8 May 2026 17:19:21 +0200 Subject: [PATCH 212/681] ninjabackend: refine covariance Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 7086be5ecdcf..f0da1c6a8d24 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -2463,7 +2463,7 @@ def generate_static_link_rules(self) -> None: if static_linker is None: continue rule = 'STATIC_LINKER{}'.format(self.get_rule_suffix(for_machine)) - cmdlist: T.List[T.Union[str, NinjaCommandArg]] = [] + cmdlist: CommandArgs = [] args = ['$in'] # FIXME: Must normalize file names with pathlib.Path before writing # them out to fix this properly on Windows. See: @@ -2586,7 +2586,7 @@ def generate_cython_compile_rules(self, compiler: 'Compiler') -> None: depargs = compiler.get_dependency_gen_args('$out', '$DEPFILE') depfile = '$out.dep' if depargs else None - args = depargs + ['$ARGS', '$in'] + args: CommandArgs = depargs + ['$ARGS', '$in'] args += NinjaCommandArg.list(compiler.get_output_args('$out'), Quoting.none) self.add_rule(NinjaRule(rule, command + args, [], description, From b1034cf019feef4111d40357cd07774f56d32534 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 8 May 2026 18:07:23 +0200 Subject: [PATCH 213/681] ninjabackend: replace concatenation with unpacking Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index f0da1c6a8d24..4d6f7058d7a2 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1096,7 +1096,7 @@ def generate_target(self, target: T.Union[build.BuildTarget, build.CustomTarget, unity_src.append(abs_src) else: o, s = self.generate_single_compile(target, src, False, [], - header_deps + d_generated_deps + fortran_order_deps, + [*header_deps, *d_generated_deps, *fortran_order_deps], fortran_inc_args) obj_list.append(o) compiled_sources.append(s) @@ -1104,7 +1104,7 @@ def generate_target(self, target: T.Union[build.BuildTarget, build.CustomTarget, if is_unity: for src in self.generate_unity_files(target, unity_src): - o, s = self.generate_single_compile(target, src, True, unity_deps + header_deps + d_generated_deps, + o, s = self.generate_single_compile(target, src, True, [*unity_deps, *header_deps, *d_generated_deps], fortran_order_deps, fortran_inc_args, unity_src) obj_list.append(o) compiled_sources.append(s) @@ -1513,7 +1513,7 @@ def generate_jar_target(self, target: build.Jar) -> None: gen_src_list.append(raw_src) compile_args = self.determine_java_compile_args(target, compiler) - class_list = self.generate_java_compile(src_list + gen_src_list, target, compiler, compile_args) + class_list = self.generate_java_compile([*src_list, *gen_src_list], target, compiler, compile_args) class_dep_list = [os.path.join(self.get_target_private_dir(target), i) for i in class_list] manifest_path = os.path.join(self.get_target_private_dir(target), 'META-INF', 'MANIFEST.MF') manifest_fullpath = os.path.join(self.environment.get_build_dir(), manifest_path) @@ -3879,9 +3879,10 @@ def generate_link(self, target: build.BuildTarget, outname, obj_list, linker: T. dep_targets.extend([self.get_dependency_filename(t) for t in target.link_depends]) elem = NinjaBuildElement(self.all_outputs, outname, linker_rule, obj_list, implicit_outs=implicit_outs) - elem.add_dep(dep_targets + custom_target_libraries) + all_deps = [*dep_targets, *custom_target_libraries] + elem.add_dep(all_deps) if linker.get_id() == 'tasking': - if len([x for x in dep_targets + custom_target_libraries if x.endswith('.ma')]) > 0 and not self.get_target_option(target, OptionKey('b_lto', target.subproject, target.for_machine)): + if any(x.endswith('.ma') for x in all_deps) and not self.get_target_option(target, OptionKey('b_lto', target.subproject, target.for_machine)): raise MesonException(f'Tried to link the target named \'{target.name}\' with a MIL archive without LTO enabled! This causes the compiler to ignore the archive.') # Compiler args must be included in TI C28x linker commands. From 5e256ca3b215ba5ba9cbf8799efa0568f6967004 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 8 May 2026 18:53:49 +0200 Subject: [PATCH 214/681] backends: fix annotation for generate_unity_files Signed-off-by: Paolo Bonzini --- mesonbuild/backend/backends.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 45c324f7486a..be04a8dbfcba 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -435,7 +435,7 @@ def get_unity_source_file(self, target: build.BuildTargetTypes, osrc = f'{target.name}-unity{number}.{suffix}' return mesonlib.File.from_built_file(self.get_target_private_dir(target), osrc) - def generate_unity_files(self, target: build.BuildTarget, unity_src: str) -> T.List[mesonlib.File]: + def generate_unity_files(self, target: build.BuildTarget, unity_src: T.List[str]) -> T.List[mesonlib.File]: abs_files: T.List[str] = [] result: T.List[mesonlib.File] = [] compsrcs = classify_unity_sources(target.compilers.values(), unity_src) From c578d546ee2f176d389afdba246ea0d6f56d6340 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 8 May 2026 17:29:07 +0200 Subject: [PATCH 215/681] backends: make extra_bdeps type consistent with determine_windows_extra_rpaths Signed-off-by: Paolo Bonzini --- mesonbuild/backend/backends.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index be04a8dbfcba..cf4a09607c67 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -542,7 +542,7 @@ def determine_swift_dep_dirs(self, target: build.BuildTarget) -> T.List[str]: def get_executable_serialisation( self, cmd: T.Sequence[T.Union[programs.Program, build.BuildTargetTypes, File, str]], workdir: T.Optional[str] = None, - extra_bdeps: T.Optional[T.List[build.BuildTarget]] = None, + extra_bdeps: T.Optional[T.Sequence[build.BuildTargetTypes]] = None, capture: T.Optional[str] = None, feed: T.Optional[str] = None, env: T.Optional[mesonlib.EnvironmentVariables] = None, @@ -632,7 +632,7 @@ def get_executable_serialisation( def as_meson_exe_cmdline(self, exe: T.Union[str, mesonlib.File, build.BuildTargetTypes, programs.Program], cmd_args: T.Sequence[T.Union[str, mesonlib.File, build.BuildTargetTypes, programs.Program]], workdir: T.Optional[str] = None, - extra_bdeps: T.Optional[T.List[build.BuildTarget]] = None, + extra_bdeps: T.Optional[T.Sequence[build.BuildTargetTypes]] = None, capture: T.Optional[str] = None, feed: T.Optional[str] = None, force_serialize: bool = False, From 83350b1f7a47c906ed8eff75313d3221e10da01f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 8 May 2026 17:37:36 +0200 Subject: [PATCH 216/681] ninjabackend: fix type of generate_java_compile Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 4d6f7058d7a2..393d60b2c08b 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1622,7 +1622,7 @@ def determine_java_compile_args(self, target: build.Jar, compiler: Compiler) -> args += ['-sourcepath', os.pathsep.join(sourcepaths)] return list(args) - def generate_java_compile(self, srcs, target, compiler, args) -> str: + def generate_java_compile(self, srcs, target, compiler, args) -> T.List[str]: deps = [os.path.join(self.get_target_dir(l), l.get_filename()) for l in target.link_targets] generated_sources = self.get_target_generated_sources(target) for rel_src in generated_sources.keys(): From 57a2d5a1259a2fbb507992f3647b80e30ed511ee Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 8 May 2026 18:42:57 +0200 Subject: [PATCH 217/681] ninjabackend: fix types for split_vala_sources Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 393d60b2c08b..5c53d4789c38 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1676,8 +1676,8 @@ def determine_dep_vapis(self, target: build.BuildTarget) -> T.List[str]: return list(result) def split_vala_sources(self, t: build.BuildTarget) -> \ - T.Tuple[T.MutableMapping[str, File], T.MutableMapping[str, File], - T.Tuple[T.MutableMapping[str, File], T.MutableMapping]]: + T.Tuple[T.MutableMapping[str, File | build.GeneratedTypes], T.MutableMapping[str, File | build.GeneratedTypes], + T.Tuple[T.MutableMapping[str, File | build.GeneratedTypes], T.MutableMapping[str, File | build.GeneratedTypes]]]: """ Splits the target's sources into .vala, .gs, .vapi, and other sources. Handles both preexisting and generated sources. @@ -1686,10 +1686,10 @@ def split_vala_sources(self, t: build.BuildTarget) -> \ the keys being the path to the file (relative to the build directory) and the value being the object that generated or represents the file. """ - vala: T.MutableMapping[str, File] = OrderedDict() - vapi: T.MutableMapping[str, File] = OrderedDict() - others: T.MutableMapping[str, File] = OrderedDict() - othersgen: T.MutableMapping[str, File] = OrderedDict() + vala: T.MutableMapping[str, File | build.GeneratedTypes] = OrderedDict() + vapi: T.MutableMapping[str, File | build.GeneratedTypes] = OrderedDict() + others: T.MutableMapping[str, File | build.GeneratedTypes] = OrderedDict() + othersgen: T.MutableMapping[str, File | build.GeneratedTypes] = OrderedDict() # Split preexisting sources for s in t.get_sources(): # BuildTarget sources are always mesonlib.File files which are @@ -1718,11 +1718,11 @@ def split_vala_sources(self, t: build.BuildTarget) -> \ else: srctype = othersgen # Duplicate outputs are disastrous - if f in srctype and srctype[f] is not gensrc: + if f in srctype and srctype[f] != gensrc: msg = 'Duplicate output {0!r} from {1!r} {2!r}; ' \ 'conflicts with {0!r} from {4!r} {3!r}' \ ''.format(f, type(gensrc).__name__, gensrc.name, - srctype[f].name, type(srctype[f]).__name__) + srctype[f], type(srctype[f]).__name__) raise InvalidArguments(msg) # Store 'somefile.vala': GeneratedList (or CustomTarget) srctype[f] = gensrc From 878d166adecb776ccc625113d21ad13cef8199bb Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 8 May 2026 20:10:04 +0200 Subject: [PATCH 218/681] ninjabackend: fix type of get_import_std_object Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 5c53d4789c38..e6a5fb8ec13d 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -3904,7 +3904,7 @@ def generate_link(self, target: build.BuildTarget, outname, obj_list, linker: T. self.create_target_linker_introspection(target, linker, commands) return elem - def get_import_std_object(self, target: build.BuildTarget) -> T.List[File]: + def get_import_std_object(self, target: build.BuildTarget) -> T.List[str]: if not self.target_uses_import_std(target): return [] return self.import_std.gen_objects From 5ee1e054a46a339f660cda7c3165e112a8b70136 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 8 May 2026 17:18:46 +0200 Subject: [PATCH 219/681] ninjabackend: more compiler special casing Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index e6a5fb8ec13d..eb36214e3efd 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1569,7 +1569,7 @@ def generate_cs_target(self, target: build.BuildTarget) -> None: fname = target.get_filename() outname_rel = os.path.join(self.get_target_dir(target), fname) src_list = target.get_sources() - compiler = target.compilers['cs'] + compiler = T.cast('CsCompiler', target.compilers['cs']) rel_srcs = [os.path.normpath(s.rel_to_builddir(self.build_to_src)) for s in src_list] deps = [] commands = self.generate_basic_compiler_args(target, compiler) @@ -2547,7 +2547,7 @@ def generate_dynamic_link_rules(self) -> None: syndesc = 'Generating symbol file $out' self.add_rule(NinjaRule(symrule, symcmd, [], syndesc, restat=True)) - def generate_java_compile_rule(self, compiler) -> None: + def generate_java_compile_rule(self, compiler: Compiler) -> None: rule = self.compiler_to_rule_name(compiler) command = compiler.get_exelist() + ['$ARGS', '$in'] description = 'Compiling Java sources for $FOR_JAR' @@ -2562,7 +2562,7 @@ def generate_cs_compile_rule(self, compiler: 'CsCompiler') -> None: rspable=mesonlib.is_windows(), rspfile_quote_style=compiler.rsp_file_syntax())) - def generate_vala_compile_rules(self, compiler) -> None: + def generate_vala_compile_rules(self, compiler: Compiler) -> None: rule = self.compiler_to_rule_name(compiler) command = compiler.get_exelist() description = 'Compiling Vala source $in' @@ -2593,7 +2593,7 @@ def generate_cython_compile_rules(self, compiler: 'Compiler') -> None: depfile=depfile, restat=True)) - def generate_rust_compile_rules(self, compiler) -> None: + def generate_rust_compile_rules(self, compiler: RustCompiler) -> None: rule = self.compiler_to_rule_name(compiler) command = compiler.get_exelist() + ['$ARGS', '$in'] description = 'Compiling Rust source $in' @@ -2602,7 +2602,7 @@ def generate_rust_compile_rules(self, compiler) -> None: self.add_rule(NinjaRule(rule, command, [], description, deps=depstyle, depfile=depfile)) - def generate_swift_compile_rules(self, compiler) -> None: + def generate_swift_compile_rules(self, compiler: SwiftCompiler) -> None: rule = self.compiler_to_rule_name(compiler) wd_args = compiler.get_working_directory_args('$RUNDIR') @@ -2649,7 +2649,7 @@ def generate_fortran_dep_hack(self, crstr: str) -> None: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) self.add_rule(NinjaRule(rule, cmd, [], 'Dep hack', restat=True)) - def generate_llvm_ir_compile_rule(self, compiler) -> None: + def generate_llvm_ir_compile_rule(self, compiler: Compiler) -> None: if self.created_llvm_ir_rule[compiler.for_machine]: return rule = self.get_compiler_rule_name('llvm_ir', compiler.for_machine) @@ -2701,11 +2701,11 @@ def generate_compile_rule_for(self, langname: str, compiler: Compiler) -> None: self.generate_vala_compile_rules(compiler) return if langname == 'rust': - self.generate_rust_compile_rules(compiler) + self.generate_rust_compile_rules(T.cast('RustCompiler', compiler)) return if langname == 'swift': if self.environment.machines.matches_build_machine(compiler.for_machine): - self.generate_swift_compile_rules(compiler) + self.generate_swift_compile_rules(T.cast('SwiftCompiler', compiler)) return if langname == 'cython': self.generate_cython_compile_rules(compiler) From 253765d09b127b0c777800b531573ecc926b516d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 8 May 2026 17:37:27 +0200 Subject: [PATCH 220/681] ninjabackend: add some types to partially typed functions Just enough to stop mypy from reporting errors about Any. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index eb36214e3efd..dcb36486a1d6 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -2952,12 +2952,12 @@ def get_fortran_deps(self, compiler: FortranCompiler, src: Path, target) -> T.Li mod_files = _scan_fortran_file_deps(src, srcdir, dirname, tdeps, compiler) return mod_files - def get_no_stdlib_link_args(self, target, linker) -> T.List[str]: + def get_no_stdlib_link_args(self, target: build.BuildTarget, linker: Compiler | StaticLinker) -> T.List[str]: if hasattr(linker, 'language') and linker.language in self.build.stdlibs[target.for_machine]: return linker.get_no_stdlib_link_args() return [] - def get_compile_debugfile_args(self, compiler, target, objfile) -> T.List[str]: + def get_compile_debugfile_args(self, compiler: Compiler, target: build.BuildTarget, objfile: str) -> T.List[str]: # The way MSVC uses PDB files is documented exactly nowhere so # the following is what we have been able to decipher via # reverse engineering. @@ -3711,7 +3711,7 @@ def guess_external_link_dependencies(self, linker, target, commands, internal): return guessed_dependencies + absolute_libs - def generate_prelink(self, target, obj_list) -> T.List[str]: + def generate_prelink(self, target: build.BuildTarget, obj_list: T.List[str]) -> T.List[str]: assert isinstance(target, build.StaticLibrary) prelink_name = os.path.join(self.get_target_private_dir(target), target.name + '-prelink.o') elem = NinjaBuildElement(self.all_outputs, [prelink_name], 'CUSTOM_COMMAND', obj_list) From ee37aee9900eb20e3e5a48fdeb90bfe1ecb4a91b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 8 May 2026 18:50:14 +0200 Subject: [PATCH 221/681] ninjabackend: use CompilerArgs instead of List[str] for pch Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index dcb36486a1d6..df6465ef9e08 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -3154,7 +3154,7 @@ def _generate_single_compile_target_args(self, target: build.BuildTarget, compil # Finally add the private dir for the target to the include path. This # must override everything else and must be the final path added. commands += compiler.get_include_args(self.get_target_private_dir(target), False) - return commands + return list(commands) # Returns a dictionary, mapping from each compiler src type (e.g. 'c', 'cpp', etc.) to a list of compiler arg strings # used for that respective src type. @@ -3476,7 +3476,7 @@ def generate_msvc_pch_command(self, target, compiler, pch: T.Tuple[str, T.Option return commands, dep, dst, link_objects, source - def generate_gcc_pch_command(self, target, compiler, pch: str): + def generate_gcc_pch_command(self, target: build.BuildTarget, compiler: Compiler, pch: str) -> T.Tuple[CompilerArgs, str, str, T.List[str]]: commands = self._generate_single_compile(target, compiler) if pch.split('.')[-1] == 'h' and compiler.language == 'cpp': # Explicitly compile pch headers as C++. If Clang is invoked in C++ mode, it actually warns if @@ -3487,7 +3487,7 @@ def generate_gcc_pch_command(self, target, compiler, pch: str): dep = dst + '.' + compiler.get_depfile_suffix() return commands, dep, dst, [] # Gcc does not create an object file during pch generation. - def generate_mwcc_pch_command(self, target, compiler, pch: str): + def generate_mwcc_pch_command(self, target: build.BuildTarget, compiler: Compiler, pch: str) -> T.Tuple[CompilerArgs, str, str, T.List[str]]: commands = self._generate_single_compile(target, compiler) dst = os.path.join(self.get_target_private_dir(target), os.path.basename(pch) + '.' + compiler.get_pch_suffix()) @@ -3645,7 +3645,7 @@ def guess_library_absolute_path(self, linker, libname, search_dirs, patterns) -> return trial return None - def guess_external_link_dependencies(self, linker, target, commands, internal): + def guess_external_link_dependencies(self, linker: Compiler, target: build.BuildTarget, commands: CompilerArgs, internal: T.List[str]) -> T.List[str]: # Ideally the linker would generate dependency information that could be used. # But that has 2 problems: # * currently ld cannot create dependency information in a way that ninja can use: @@ -3658,7 +3658,7 @@ def guess_external_link_dependencies(self, linker, target, commands, internal): build_dir = self.environment.get_build_dir() # the following loop sometimes consumes two items from command in one pass - it = iter(linker.native_args_to_unix(commands)) + it = iter(linker.native_args_to_unix(list(commands))) for item in it: if item in internal and not item.startswith('-'): continue From 92d9493a318635a8f8bd36f9619c583159683ae4 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 8 May 2026 18:32:24 +0200 Subject: [PATCH 222/681] ninjabackend: add simple annotations --- mesonbuild/backend/backends.py | 2 +- mesonbuild/backend/ninjabackend.py | 97 ++++++++++++++++-------------- 2 files changed, 54 insertions(+), 45 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index cf4a09607c67..50ea1dd1ac4f 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -62,7 +62,7 @@ class TargetIntrospectionData(TypedDict): # Languages that can mix with C or C++ but don't support unity builds yet # because the syntax we use for unity builds is specific to C/++/ObjC/++. # Assembly files cannot be unitified and neither can LLVM IR files -LANGS_CANT_UNITY = ('d', 'fortran', 'vala', 'rust') +LANGS_CANT_UNITY: T.FrozenSet[Language] = frozenset({'d', 'fortran', 'vala', 'rust'}) @dataclass(eq=False) class RegenInfo: diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index df6465ef9e08..9b112c324249 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -664,7 +664,7 @@ def generate(self, capture: bool = False, vslite_ctx: T.Optional[T.Dict] = None) with self.detect_vs_dep_prefix(tempfilename) as outfile: self.generate_rules() - self.build_elements = [] + self.build_elements: T.List[T.Union[NinjaBuildElement, NinjaComment]] = [] self.generate_phony() self.add_build_comment(NinjaComment('Build rules for targets')) @@ -817,7 +817,7 @@ def get_target_sources(self, target: build.BuildTarget) -> T.MutableMapping[str, srcs[f] = s return srcs - def get_target_source_can_unity(self, target, source: FileOrString) -> bool: + def get_target_source_can_unity(self, target: build.BuildTarget, source: FileOrString) -> bool: if isinstance(source, File): source = source.fname if compilers.is_llvm_ir(source) or \ @@ -831,7 +831,10 @@ def get_target_source_can_unity(self, target, source: FileOrString) -> bool: return False return True - def create_target_source_introspection(self, target: build.Target, comp: compilers.Compiler, parameters, sources, generated_sources, + def create_target_source_introspection(self, target: build.Target, comp: compilers.Compiler, + parameters: CompilerArgs | T.List[str], + sources: T.List[FileOrString], + generated_sources: T.List[FileOrString], unity_sources: T.Optional[T.List[FileOrString]] = None) -> None: ''' Adds the source file introspection information for a language of a target @@ -1225,12 +1228,12 @@ def select_sources_to_scan(self, compiled_sources: T.List[str], elif ext.lower() in compilers.lang_suffixes['fortran']: yield source, 'fortran' - def process_target_dependencies(self, target) -> None: + def process_target_dependencies(self, target: build.Target) -> None: for t in target.get_dependencies(): if t.get_id() not in self.processed_targets: self.generate_target(t) - def custom_target_generator_inputs(self, target) -> None: + def custom_target_generator_inputs(self, target: build.CustomTarget) -> None: for s in target.sources: if isinstance(s, build.GeneratedList): self.generate_genlist_for_target(s, target) @@ -1412,8 +1415,8 @@ def generate_tests(self) -> None: self.add_build(elem) def generate_rules(self) -> None: - self.rules = [] - self.ruledict = {} + self.rules: T.List[T.Union[NinjaRule, NinjaComment]] = [] + self.ruledict: T.Dict[str, NinjaRule] = {} self.add_rule_comment(NinjaComment('Rules for module scanning.')) self.generate_scanner_rules() @@ -1544,7 +1547,7 @@ def generate_jar_target(self, target: build.Jar) -> None: # Create introspection information self.create_target_source_introspection(target, compiler, compile_args, src_list, gen_src_list) - def generate_cs_resource_tasks(self, target) -> T.Tuple[T.List[str], T.List[str]]: + def generate_cs_resource_tasks(self, target: build.BuildTarget) -> T.Tuple[T.List[str], T.List[str]]: args = [] deps = [] for r in target.resources: @@ -1622,7 +1625,7 @@ def determine_java_compile_args(self, target: build.Jar, compiler: Compiler) -> args += ['-sourcepath', os.pathsep.join(sourcepaths)] return list(args) - def generate_java_compile(self, srcs, target, compiler, args) -> T.List[str]: + def generate_java_compile(self, srcs: T.List[File], target: build.BuildTarget, compiler: Compiler, args: T.List[str]) -> T.List[str]: deps = [os.path.join(self.get_target_dir(l), l.get_filename()) for l in target.link_targets] generated_sources = self.get_target_generated_sources(target) for rel_src in generated_sources.keys(): @@ -2117,7 +2120,7 @@ def _link_library(libname: str, static: bool, bundle: bool = False) -> None: deps.extend([self.get_dependency_filename(t) for t in target.link_depends]) - linkdirs = mesonlib.OrderedSet() + linkdirs: OrderedSet[str] = OrderedSet() external_deps = target.external_deps.copy() target_deps = target.get_dependencies() for d in target_deps: @@ -2304,18 +2307,18 @@ def compiler_to_rule_name(cls, compiler: Compiler) -> str: def compiler_to_pch_rule_name(cls, compiler: Compiler) -> str: return cls.get_compiler_rule_name(compiler.get_language(), compiler.for_machine, 'PCH') - def swift_module_file_name(self, target) -> str: + def swift_module_file_name(self, target: build.BuildTarget) -> str: return os.path.join(self.get_target_private_dir(target), target.swift_module_name + '.swiftmodule') - def determine_swift_dep_modules(self, target) -> T.List[str]: + def determine_swift_dep_modules(self, target: build.BuildTarget) -> T.List[str]: result = [] for l in target.link_targets: if self.is_swift_target(l): result.append(self.swift_module_file_name(l)) return result - def get_swift_link_deps(self, target) -> T.List[str]: + def get_swift_link_deps(self, target: build.BuildTarget) -> T.List[str]: result = [] for l in target.link_targets: result.append(self.get_target_filename(l)) @@ -2796,7 +2799,7 @@ def generate_compile_rules(self) -> None: for mode in compiler.get_modes(): self.generate_compile_rule_for(langname, mode) - def generate_generator_list_rules(self, target) -> None: + def generate_generator_list_rules(self, target: build.BuildTarget) -> None: # CustomTargets have already written their rules and # CustomTargetIndexes don't actually get generated, so write rules for # GeneratedLists here @@ -2805,7 +2808,7 @@ def generate_generator_list_rules(self, target) -> None: continue self.generate_genlist_for_target(genlist, target) - def replace_paths(self, target, args: T.List[str], override_subdir=None) -> T.List[str]: + def replace_paths(self, target: build.BuildTarget, args: T.List[str], override_subdir: T.Optional[str] = None) -> T.List[str]: if override_subdir: source_target_dir = os.path.join(self.build_to_src, override_subdir) else: @@ -2884,7 +2887,7 @@ def generate_genlist_for_target(self, genlist: build.GeneratedList, target: buil elem.add_item('COMMAND', cmdlist) self.add_build(elem) - def scan_fortran_module_outputs(self, target) -> None: + def scan_fortran_module_outputs(self, target: build.BuildTarget) -> None: """ Find all module and submodule made available in a Fortran code file. """ @@ -2902,8 +2905,8 @@ def scan_fortran_module_outputs(self, target) -> None: modre = re.compile(FORTRAN_MODULE_PAT, re.IGNORECASE) submodre = re.compile(FORTRAN_SUBMOD_PAT, re.IGNORECASE) - module_files = {} - submodule_files = {} + module_files: T.Dict[str, File] = {} + submodule_files: T.Dict[str, File] = {} for s in target.get_sources(): # FIXME, does not work for Fortran sources generated by # custom_target() and generator() as those are run after @@ -2938,7 +2941,7 @@ def scan_fortran_module_outputs(self, target) -> None: self.fortran_deps[target.get_basename()] = {**module_files, **submodule_files} - def get_fortran_deps(self, compiler: FortranCompiler, src: Path, target) -> T.List[str]: + def get_fortran_deps(self, compiler: FortranCompiler, src: Path, target: build.BuildTarget) -> T.List[str]: """ Find all module and submodule needed by a Fortran target """ @@ -3026,7 +3029,7 @@ def get_link_debugfile_args(self, linker: T.Union[Compiler, StaticLinker], targe return linker.get_link_debugfile_args(filename) return [] - def generate_llvm_ir_compile(self, target, src: FileOrString) -> T.Tuple[str, str]: + def generate_llvm_ir_compile(self, target: build.BuildTarget, src: FileOrString) -> T.Tuple[str, str]: compiler = get_compiler_for_source(target.compilers.values(), src) commands = compiler.compiler_args() # Compiler args for compiling this target @@ -3184,8 +3187,9 @@ def generate_common_compile_args_per_src_type(self, target: build.BuildTarget) - src_type_to_args[src_type_str] = commands.to_native() return src_type_to_args - def generate_single_compile(self, target: build.BuildTarget, src, - is_generated: bool = False, header_deps=None, + def generate_single_compile(self, target: build.BuildTarget, src: FileOrString, + is_generated: bool = False, + header_deps: T.Optional[T.List[FileOrString]] = None, order_deps: T.Optional[T.List[FileOrString]] = None, extra_args: T.Optional[T.List[str]] = None, unity_sources: T.Optional[T.List[FileOrString]] = None, @@ -3247,6 +3251,7 @@ def generate_single_compile(self, target: build.BuildTarget, src, pchlist = target.pch[compiler.language] else: pchlist = None + pch_dep: T.List[str] if not pchlist: pch_dep = [] elif compiler.id == 'intel': @@ -3356,9 +3361,9 @@ def target_uses_import_std(self, target: build.BuildTarget) -> bool: pass return False - def handle_cpp_import_std(self, target: build.BuildTarget, compiler): - istd_args = [] - istd_dep = [] + def handle_cpp_import_std(self, target: build.BuildTarget, compiler: Compiler) -> T.Tuple[T.List[str], T.List[File]]: + istd_args: T.List[str] = [] + istd_dep: T.List[File] = [] if not self.target_uses_import_std(target): return istd_args, istd_dep mlog.warning('Import std support is experimental and might break compatibility in the future.') @@ -3399,7 +3404,7 @@ def handle_cpp_import_std(self, target: build.BuildTarget, compiler): else: raise MesonException(f'Import std not supported on compiler {compiler.id} yet.') - def add_dependency_scanner_entries_to_element(self, target: build.BuildTarget, compiler, element, src) -> None: + def add_dependency_scanner_entries_to_element(self, target: build.BuildTarget, compiler: Compiler, element: NinjaBuildElement, src: File) -> None: if not self.should_use_dyndeps_for_target(target): return if isinstance(target, build.CompileTarget): @@ -3417,7 +3422,7 @@ def get_dep_scan_file_for(self, target: build.BuildTarget) -> T.Tuple[str, str]: priv = self.get_target_private_dir(target) return os.path.join(priv, 'depscan.json'), os.path.join(priv, 'depscan.dd') - def add_header_deps(self, target, ninja_element, header_deps) -> None: + def add_header_deps(self, target: build.BuildTarget, ninja_element: NinjaBuildElement, header_deps: T.List[FileOrString]) -> None: for d in header_deps: if isinstance(d, File): d = d.rel_to_builddir(self.build_to_src) @@ -3449,12 +3454,12 @@ def get_fortran_module_deps(self, target: build.BuildTarget, compiler: Compiler) for lt in itertools.chain(target.link_targets, target.link_whole_targets) ] - def generate_msvc_pch_command(self, target, compiler, pch: T.Tuple[str, T.Optional[str]]): + def generate_msvc_pch_command(self, target: build.BuildTarget, compiler: Compiler, pch: T.Tuple[str, T.Optional[str]]) -> T.Tuple[T.List[str], str, str, T.List[str], str]: header = pch[0] pchname = compiler.get_pch_name(header) dst = os.path.join(self.get_target_private_dir(target), pchname) - commands = [] + commands: T.List[str] = [] commands += self.generate_basic_compiler_args(target, compiler) if pch[1] is None: @@ -3494,7 +3499,7 @@ def generate_mwcc_pch_command(self, target: build.BuildTarget, compiler: Compile dep = os.path.splitext(dst)[0] + '.' + compiler.get_depfile_suffix() return commands, dep, dst, [] # mwcc compilers do not create an object file during pch generation. - def generate_pch(self, target: build.BuildTarget, header_deps=None): + def generate_pch(self, target: build.BuildTarget, header_deps: T.Optional[T.List[FileOrString]] = None) -> T.List[str]: header_deps = header_deps if header_deps is not None else [] pch_objects = [] for lang in T.cast('T.Tuple[Language, ...]', ('c', 'cpp')): @@ -3528,12 +3533,12 @@ def generate_pch(self, target: build.BuildTarget, header_deps=None): self.all_pch[compiler.id].update(objs + [dst]) return pch_objects - def get_target_shsym_filename(self, target) -> str: + def get_target_shsym_filename(self, target: build.BuildTarget) -> str: # Always name the .symbols file after the primary build output because it always exists targetdir = self.get_target_private_dir(target) return os.path.join(targetdir, target.get_filename() + '.symbols') - def generate_shsym(self, target) -> None: + def generate_shsym(self, target: build.BuildTarget) -> None: # On OS/2, an import library is generated after linking a DLL, so # if a DLL is used as a target, import library is not generated. if self.environment.machines[target.for_machine].is_os2(): @@ -3554,7 +3559,7 @@ def generate_shsym(self, target) -> None: elem.add_item('CROSS', '--cross-host=' + self.environment.machines[target.for_machine].system) self.add_build(elem) - def get_import_filename(self, target) -> str: + def get_import_filename(self, target: build.BuildTarget) -> str: return os.path.join(self.get_target_dir(target), target.import_filename) def get_target_type_link_args(self, target: build.BuildTarget, linker: T.Union[StaticLinker, Compiler]) -> T.List[str]: @@ -3598,8 +3603,8 @@ def get_target_type_link_args(self, target: build.BuildTarget, linker: T.Union[S raise RuntimeError('Unknown build target type.') return commands - def get_target_type_link_args_post_dependencies(self, target, linker): - commands = [] + def get_target_type_link_args_post_dependencies(self, target: build.BuildTarget, linker: T.Union[Compiler, StaticLinker]) -> T.List[str]: + commands: T.List[str] = [] if isinstance(target, build.Executable): # If win_subsystem is significant on this platform, add the appropriate linker arguments. # Unfortunately this can't be done in get_target_type_link_args, because some misguided @@ -3610,7 +3615,7 @@ def get_target_type_link_args_post_dependencies(self, target, linker): commands += linker.get_win_subsystem_args(target.win_subsystem) return commands - def get_link_whole_args(self, linker: Compiler, target): + def get_link_whole_args(self, linker: Compiler, target: build.BuildTarget) -> T.List[str]: use_custom = False if linker.id == 'msvc': # Expand our object lists manually if we are on pre-Visual Studio 2015 Update 2 @@ -3631,7 +3636,7 @@ def get_link_whole_args(self, linker: Compiler, target): return linker.get_link_whole_for(target_args) if target_args else [] @lru_cache(maxsize=None) - def guess_library_absolute_path(self, linker, libname, search_dirs, patterns) -> T.Optional[Path]: + def guess_library_absolute_path(self, linker: Compiler, libname: str, search_dirs: T.Tuple[str, ...], patterns: T.Tuple[str, ...]) -> T.Optional[Path]: from ..compilers.c import CCompiler for d in search_dirs: for p in patterns: @@ -3652,8 +3657,8 @@ def guess_external_link_dependencies(self, linker: Compiler, target: build.Build # https://sourceware.org/bugzilla/show_bug.cgi?id=22843 # * Meson optimizes libraries from the same build using the symbol extractor. # Just letting ninja use ld generated dependencies would undo this optimization. - search_dirs = OrderedSet() - libs = OrderedSet() + search_dirs: OrderedSet[str] = OrderedSet() + libs: OrderedSet[str] = OrderedSet() absolute_libs = [] build_dir = self.environment.get_build_dir() @@ -3747,7 +3752,10 @@ def get_build_rpath_args(self, target: build.BuildTarget, linker: T.Union[Compil target)) return rpath_args - def generate_link(self, target: build.BuildTarget, outname, obj_list, linker: T.Union['Compiler', 'StaticLinker'], extra_args=None, stdlib_args=None): + def generate_link(self, target: build.BuildTarget, outname: str, obj_list: T.List[str], + linker: T.Union[Compiler, StaticLinker], + extra_args: T.Optional[T.List[str]] = None, + stdlib_args: T.Optional[T.List[str]] = None) -> NinjaBuildElement: extra_args = extra_args if extra_args is not None else [] stdlib_args = stdlib_args if stdlib_args is not None else [] implicit_outs = [] @@ -3814,7 +3822,8 @@ def generate_link(self, target: build.BuildTarget, outname, obj_list, linker: T. # Add link args to link to all internal libraries (link_with:) and # internal dependencies needed by this target. - dep_targets = [] + dep_targets: T.List[str] = [] + dependencies: T.List[build.BuildTargetTypes] if isinstance(target, build.StaticLibrary): # Link arguments of static libraries are not put in the command # line of the library. They are instead appended to the command @@ -3886,7 +3895,7 @@ def generate_link(self, target: build.BuildTarget, outname, obj_list, linker: T. raise MesonException(f'Tried to link the target named \'{target.name}\' with a MIL archive without LTO enabled! This causes the compiler to ignore the archive.') # Compiler args must be included in TI C28x linker commands. - compile_args = [] + compile_args: T.List[str] = [] if linker.get_id() in {'c2000', 'c6000', 'ti'}: for for_machine in MachineChoice: clist = self.environment.coredata.compilers[for_machine] @@ -3909,7 +3918,7 @@ def get_import_std_object(self, target: build.BuildTarget) -> T.List[str]: return [] return self.import_std.gen_objects - def get_dependency_filename(self, t) -> str: + def get_dependency_filename(self, t: T.Union[File, build.BuildTargetTypes]) -> str: if isinstance(t, build.SharedLibrary): if t.uses_rust() and t.rust_crate_type == 'proc-macro': return self.get_target_filename(t) @@ -3923,7 +3932,7 @@ def get_dependency_filename(self, t) -> str: self.environment.get_build_dir()) return self.get_target_filename(t) - def generate_shlib_aliases(self, target, outdir) -> None: + def generate_shlib_aliases(self, target: build.BuildTarget, outdir: str) -> None: for alias, to, tag in target.get_aliases(): aliasfile = os.path.join(outdir, alias) abs_aliasfile = os.path.join(self.environment.get_build_dir(), outdir, alias) From 4ac3c69671c7c7fb45fa1c9ef7b3c3723fd22288 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 8 May 2026 18:52:36 +0200 Subject: [PATCH 223/681] ninjabackend: assert type of options Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 9b112c324249..1aa28b74e712 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -655,6 +655,7 @@ def generate(self, capture: bool = False, vslite_ctx: T.Optional[T.Dict] = None) outfile.write('ninja_required_version = 1.8.2\n\n') num_pools = self.environment.coredata.optstore.get_value_for('backend_max_links') + assert isinstance(num_pools, int) if num_pools > 0: outfile.write(f'''pool link_pool depth = {num_pools} @@ -1784,7 +1785,9 @@ def generate_vala_compile(self, target: build.BuildTarget) -> \ valac_outputs.append(vala_c_file) args = self.generate_basic_compiler_args(target, valac) - args += valac.get_colorout_args(self.get_target_option(target, 'b_colorout')) + b_colorout = self.get_target_option(target, 'b_colorout') + assert isinstance(b_colorout, str) + args += valac.get_colorout_args(b_colorout) # Tell Valac to output everything in our private directory. Sadly this # means it will also preserve the directory components of Vala sources # found inside the build tree (generated sources). @@ -1861,8 +1864,12 @@ def generate_cython_transpile(self, target: build.BuildTarget) -> \ args: T.List[str] = [] args += cython.get_always_args() - args += cython.get_debug_args(self.get_target_option(target, 'debug')) - args += cython.get_optimization_args(self.get_target_option(target, 'optimization')) + debug = self.get_target_option(target, 'debug') + assert isinstance(debug, bool) + args += cython.get_debug_args(debug) + optimization = self.get_target_option(target, 'optimization') + assert isinstance(optimization, str) + args += cython.get_optimization_args(optimization) args += cython.get_option_compile_args(target, target.subproject) args += cython.get_option_std_args(target, target.subproject) args += self.build.get_global_args(cython, target.for_machine) @@ -2459,6 +2466,7 @@ def _rsp_options(self, tool: T.Union['Compiler', 'StaticLinker']) -> NinjaRuleAr def generate_static_link_rules(self) -> None: num_pools = self.environment.coredata.optstore.get_value_for('backend_max_links') + assert isinstance(num_pools, int) if 'java' in self.environment.coredata.compilers.host: self.generate_java_link() for for_machine in MachineChoice: @@ -2507,6 +2515,7 @@ def generate_static_link_rules(self) -> None: def generate_dynamic_link_rules(self) -> None: num_pools = self.environment.coredata.optstore.get_value_for('backend_max_links') + assert isinstance(num_pools, int) for for_machine in MachineChoice: complist = self.environment.coredata.compilers[for_machine] for langname, compiler in complist.items(): @@ -3797,7 +3806,9 @@ def generate_link(self, target: build.BuildTarget, outname: str, obj_list: T.Lis # Add things like /NOLOGO; usually can't be overridden commands += linker.get_linker_always_args() # Add buildtype linker args: optimization level, etc. - commands += linker.get_optimization_link_args(self.get_target_option(target, 'optimization')) + optimization = self.get_target_option(target, 'optimization') + assert isinstance(optimization, str) + commands += linker.get_optimization_link_args(optimization) # Add /DEBUG and the pdb filename when using MSVC if self.get_target_option(target, 'debug'): commands += self.get_link_debugfile_args(linker, target) From 53373110e3aaa51d08e06b0075483abb659ec94c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 8 May 2026 19:06:29 +0200 Subject: [PATCH 224/681] ninjabackend: another Compiler vs. StaticLinker check --- 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 1aa28b74e712..8a7ff04b1744 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -3615,11 +3615,12 @@ def get_target_type_link_args(self, target: build.BuildTarget, linker: T.Union[S def get_target_type_link_args_post_dependencies(self, target: build.BuildTarget, linker: T.Union[Compiler, StaticLinker]) -> T.List[str]: commands: T.List[str] = [] if isinstance(target, build.Executable): + assert isinstance(linker, Compiler) + # If win_subsystem is significant on this platform, add the appropriate linker arguments. # Unfortunately this can't be done in get_target_type_link_args, because some misguided # libraries (such as SDL2) add -mwindows to their link flags. m = self.environment.machines[target.for_machine] - if m.is_windows() or m.is_cygwin(): commands += linker.get_win_subsystem_args(target.win_subsystem) return commands From 9d64800c5157de95619e91aabfecbf68c0b40b9b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 8 May 2026 17:32:03 +0200 Subject: [PATCH 225/681] utils: genericize ProgressBarFallback Signed-off-by: Paolo Bonzini --- mesonbuild/utils/universal.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index 0b1ddc7d53c8..10a588d41403 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -2263,7 +2263,7 @@ class LibType(enum.IntEnum): PREFER_STATIC = 3 -class ProgressBarFallback: # lgtm [py/iter-returns-non-self] +class ProgressBarFallback(T.Generic[_T]): ''' Fallback progress bar implementation when tqdm is not foundclass OptionType(enum.IntEnum): @@ -2323,7 +2323,7 @@ class ProgressBarFallback: # lgtm [py/iter-returns-non-self] fallback, it is safe to ignore the 'Iterator does not return self from __iter__ method' warning. ''' - def __init__(self, iterable: T.Optional[T.Iterable[str]] = None, total: T.Optional[int] = None, + def __init__(self, iterable: T.Optional[T.Iterable[_T]] = None, total: T.Optional[int] = None, bar_type: T.Optional[str] = None, desc: T.Optional[str] = None, disable: T.Optional[bool] = None): if iterable is not None: @@ -2341,10 +2341,10 @@ def __init__(self, iterable: T.Optional[T.Iterable[str]] = None, total: T.Option # Pretend to be an iterator when called as one and don't print any # progress - def __iter__(self) -> T.Iterator[str]: + def __iter__(self) -> T.Iterator[_T]: return self.iterable - def __next__(self) -> str: + def __next__(self) -> _T: return next(self.iterable) def print_dot(self) -> None: From cb4cf85030192795faa886ee541daf143a5217ff Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 8 May 2026 17:12:09 +0200 Subject: [PATCH 226/681] build, compilers: add some missing methods Signed-off-by: Paolo Bonzini --- mesonbuild/build.py | 6 ++++++ mesonbuild/compilers/compilers.py | 4 ++++ mesonbuild/linkers/linkers.py | 8 ++++++++ 3 files changed, 18 insertions(+) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 7cb081458322..dbe2226150e7 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2987,6 +2987,12 @@ def get(self, lib_type: _LibraryType, recursive: bool = False) -> Self: def uses_rust_abi(self) -> bool: return False + def uses_fortran(self) -> bool: + return False + + def uses_swift_cpp_interop(self) -> bool: + return False + class CustomTarget(Target, CustomTargetBase): diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 83d2c0a31f25..eb0a5364c8b8 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -1512,6 +1512,10 @@ def get_no_stdinc_args(self) -> T.List[str]: """Arguments to turn off default inclusion of standard libraries.""" return [] + def get_no_stdlib_link_args(self) -> T.List[str]: + """Arguments to turn off default linking to standard libraries.""" + return [] + def get_warn_args(self, level: str) -> T.List[str]: return [] diff --git a/mesonbuild/linkers/linkers.py b/mesonbuild/linkers/linkers.py index f6f35bf61a72..f12475dbaf8a 100644 --- a/mesonbuild/linkers/linkers.py +++ b/mesonbuild/linkers/linkers.py @@ -54,6 +54,10 @@ def get_base_link_args(self, def get_exelist(self) -> T.List[str]: return self.exelist.copy() + def get_archive_name(self, filename: str) -> str: + # Only used for shared libraries on AIX, so return empty. + return str() + def get_std_link_args(self, env: 'Environment', is_thin: bool) -> T.List[str]: return [] @@ -63,6 +67,10 @@ def get_optimization_link_args(self, optimization_level: str) -> T.List[str]: def get_output_args(self, target: str) -> T.List[str]: return [] + def get_no_stdlib_link_args(self) -> T.List[str]: + """Arguments to turn off default linking to standard libraries.""" + return [] + def get_coverage_link_args(self) -> T.List[str]: return [] From d9c0cb17424bc767dbd5d7ed853a52badda5552c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 8 May 2026 20:00:44 +0200 Subject: [PATCH 227/681] ninjabackend: fix typing for rust editions --- mesonbuild/backend/ninjabackend.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 8a7ff04b1744..596c3044570a 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -53,7 +53,7 @@ CommandArgTypes = T.TypeVar('CommandArgTypes', 'NinjaCommandArg', str, 'NinjaCommandArg | str') CommandArgs = T.List[CommandArgTypes] ListifiedStr = str | T.List[str] - RUST_EDITIONS = Literal['2015', '2018', '2021'] + RUST_EDITIONS = Literal['2015', '2018', '2021', '2024'] class NinjaRuleArgs(TypedDict, total=False): rspable: bool @@ -1966,7 +1966,7 @@ def _add_rust_project_entry(self, name: str, main_rust_file: str, args: Compiler from_subproject: bool, proc_macro_dylib_path: T.Optional[str], deps: T.List[RustDep]) -> None: raw_edition: T.Optional[str] = mesonlib.first(reversed(args), lambda x: x.startswith('--edition')) - edition: RUST_EDITIONS = '2015' if not raw_edition else raw_edition.split('=', 1)[-1] + edition = '2015' if not raw_edition else raw_edition.split('=', 1)[-1] cfg: T.List[str] = [] arg_itr: T.Iterator[str] = iter(args) @@ -1982,7 +1982,7 @@ def _add_rust_project_entry(self, name: str, main_rust_file: str, args: Compiler main_rust_file, crate_type, target_name, - edition, + T.cast('RUST_EDITIONS', edition), deps, cfg, is_workspace_member=not from_subproject, From e8b5e639f9ed795f9ca4e1ff2092bc803050eea9 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 8 May 2026 08:53:29 -0700 Subject: [PATCH 228/681] modules/hotdoc: use SubProject instead of str --- mesonbuild/modules/hotdoc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/modules/hotdoc.py b/mesonbuild/modules/hotdoc.py index a45fd57ecafd..9641678ac8df 100644 --- a/mesonbuild/modules/hotdoc.py +++ b/mesonbuild/modules/hotdoc.py @@ -28,7 +28,7 @@ from . import ModuleState from ..environment import Environment from ..interpreter import Interpreter - from ..interpreterbase import TYPE_kwargs, TYPE_var + from ..interpreterbase import TYPE_kwargs, TYPE_var, SubProject _T = T.TypeVar('_T') @@ -365,7 +365,7 @@ def config_path_method(self, *args: T.Any, **kwargs: T.Any) -> str: class HotdocTarget(CustomTarget): - def __init__(self, name: str, subdir: str, subproject: str, hotdoc_conf: File, + def __init__(self, name: str, subdir: str, subproject: SubProject, hotdoc_conf: File, extra_extension_paths: T.Set[str], extra_assets: T.List[str], subprojects: T.List['HotdocTarget'], environment: Environment, **kwargs: T.Any): super().__init__(name, subdir, subproject, environment, **kwargs, absolute_paths=True) From 112a8ba6f0f4e9d9129e7cc37c7c88868ded6d26 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 8 May 2026 08:53:49 -0700 Subject: [PATCH 229/681] modules/hotdoc: Add assert for option values --- mesonbuild/modules/hotdoc.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mesonbuild/modules/hotdoc.py b/mesonbuild/modules/hotdoc.py index 9641678ac8df..7992e111c4cf 100644 --- a/mesonbuild/modules/hotdoc.py +++ b/mesonbuild/modules/hotdoc.py @@ -329,7 +329,11 @@ def make_targets(self) -> T.Tuple[HotdocTarget, mesonlib.ExecutableSerialisation install_script = None if install: - datadir = os.path.join(self.state.get_option('prefix'), self.state.get_option('datadir')) + prefix = self.state.get_option('prefix') + assert isinstance(prefix, str), 'for mypy' + datadir = self.state.get_option('datadir') + assert isinstance(datadir, str), 'for mypy' + datadir = os.path.join(prefix, datadir) devhelp = self.kwargs.get('devhelp_activate', False) if not isinstance(devhelp, bool): FeatureDeprecated.single_use('hotdoc.generate_doc() devhelp_activate must be boolean', '1.1.0', self.state.subproject) From d75dc8527960806a70695e73e0a8bdf2f347f82f Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 8 May 2026 08:58:32 -0700 Subject: [PATCH 230/681] modules/hotdoc: Fix missing and incorrect annotations Also, remove the unused HotdocTargetBuilder.extra_assets --- mesonbuild/modules/hotdoc.py | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/mesonbuild/modules/hotdoc.py b/mesonbuild/modules/hotdoc.py index 7992e111c4cf..99097d5d3499 100644 --- a/mesonbuild/modules/hotdoc.py +++ b/mesonbuild/modules/hotdoc.py @@ -28,6 +28,7 @@ from . import ModuleState from ..environment import Environment from ..interpreter import Interpreter + from ..interpreter.kwargs import TargetDepends from ..interpreterbase import TYPE_kwargs, TYPE_var, SubProject _T = T.TypeVar('_T') @@ -64,7 +65,7 @@ def run_hotdoc(self, cmd: T.List[str]) -> int: class HotdocTargetBuilder: - def __init__(self, name: str, state: ModuleState, hotdoc: HotdocExternalProgram, interpreter: Interpreter, kwargs): + def __init__(self, name: str, state: ModuleState, hotdoc: HotdocExternalProgram, interpreter: Interpreter, kwargs: GenerateDocKwargs): self.hotdoc = hotdoc self.build_by_default = kwargs.pop('build_by_default', False) self.kwargs = kwargs @@ -81,10 +82,9 @@ def __init__(self, name: str, state: ModuleState, hotdoc: HotdocExternalProgram, self.cmd: T.List[TYPE_var] = ['conf', '--project-name', name, "--disable-incremental-build", '--output', os.path.join(self.builddir, self.subdir, self.name + '-doc')] - self._extra_extension_paths = set() - self.extra_assets = set() - self.extra_depends = [] - self._subprojects = [] + self._extra_extension_paths: set[str] = set() + self.extra_depends: list[build.BuildTargetTypes] = [] + self._subprojects: list[HotdocTarget] = [] def process_known_arg(self, option: str, argname: T.Optional[str] = None, value_processor: T.Optional[T.Callable] = None) -> None: if not argname: @@ -169,7 +169,9 @@ def process_gi_c_source_roots(self) -> None: self.cmd += ['--gi-c-source-roots'] + value - def process_dependencies(self, deps: T.List[T.Union[Dependency, build.StaticLibrary, build.SharedLibrary, CustomTarget, CustomTargetIndex]]) -> T.List[str]: + def process_dependencies(self, deps: T.Sequence[TargetDepends | Dependency | File | build.ExtractedObjects | build.StructuredSources]) -> T.List[str]: + # build.StructuredSources and build.ExtractedObjects shouldn't actually + # happen here, but we get them from Dependency. cflags = set() for dep in mesonlib.listify(ensure_list(deps)): if isinstance(dep, InternalDependency): @@ -241,9 +243,18 @@ def generate_hotdoc_config(self) -> None: raise MesonException('hotdoc failed to configure') os.chdir(cwd) - def ensure_file(self, value: T.Union[str, File, CustomTarget, CustomTargetIndex]) -> T.Union[File, CustomTarget, CustomTargetIndex]: + @T.overload + def ensure_file(self, value: list[str | File | CustomTarget | CustomTargetIndex] + ) -> list[File | CustomTarget | CustomTargetIndex]: ... + + @T.overload + def ensure_file(self, value: str | File | CustomTarget | CustomTargetIndex + ) -> File | CustomTarget | CustomTargetIndex: ... + + def ensure_file(self, value: str | File | CustomTarget | CustomTargetIndex | list[str | File | CustomTarget | CustomTargetIndex] + ) -> File | CustomTarget | CustomTargetIndex | list[File | CustomTarget | CustomTargetIndex]: if isinstance(value, list): - res = [] + res: list[File | CustomTarget | CustomTargetIndex] = [] for val in value: res.append(self.ensure_file(val)) return res From 158b9167c87d424eb8db6bec72476d92efc034aa Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 8 May 2026 09:17:15 -0700 Subject: [PATCH 231/681] modules/hotdoc: fix issues with the kwargs the TypedDict Mainly this is due to deleting fields (requires Total=False), and the fact that Python cannot (yet) represent arbitrary keys. I've just type: ignored those issues for now and we can revisit when PEP 728 is implemented and Python can represent that. Also, make the build_by_default parameter explicit --- mesonbuild/modules/hotdoc.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/mesonbuild/modules/hotdoc.py b/mesonbuild/modules/hotdoc.py index 99097d5d3499..5a2715b9adb3 100644 --- a/mesonbuild/modules/hotdoc.py +++ b/mesonbuild/modules/hotdoc.py @@ -33,7 +33,12 @@ _T = T.TypeVar('_T') - class GenerateDocKwargs(TypedDict): + # there is currently no way to represent the arbitrary key/value paris that + # the hotdoc module allows. + # + # PEP-728, which is due in Python 3.15, will fix this, we can then add a new + # extra_items=str keyword argument. + class GenerateDocKwargs(TypedDict, total=False): sitemap: T.Union[str, File, CustomTarget, CustomTargetIndex] index: T.Union[str, File, CustomTarget, CustomTargetIndex] project_version: str @@ -46,6 +51,8 @@ class GenerateDocKwargs(TypedDict): extra_extension_paths: T.List[str] subprojects: T.List['HotdocTarget'] install: bool + build_by_default: bool + def ensure_list(value: T.Union[_T, T.List[_T]]) -> T.List[_T]: if not isinstance(value, list): @@ -90,7 +97,7 @@ def process_known_arg(self, option: str, argname: T.Optional[str] = None, value_ if not argname: argname = option.strip("-").replace("-", "_") - value = self.kwargs.pop(argname) + value = self.kwargs.pop(argname) # type: ignore[misc] if value is not None and value_processor: value = value_processor(value) @@ -143,8 +150,8 @@ def check_extra_arg_type(self, arg: str, value: TYPE_var) -> None: def process_extra_args(self) -> None: for arg, value in self.kwargs.items(): option = "--" + arg.replace("_", "-") - self.check_extra_arg_type(arg, value) - self.set_arg_value(option, value) + self.check_extra_arg_type(arg, value) # type: ignore[arg-type] + self.set_arg_value(option, value) # type: ignore[arg-type] def add_extension_paths(self, paths: T.Union[T.List[str], T.Set[str]]) -> None: for path in paths: @@ -448,6 +455,7 @@ def has_extensions(self, state: ModuleState, args: T.Tuple[T.List[str]], kwargs: KwargInfo('extra_extension_paths', ContainerTypeInfo(list, str), listify=True, default=[]), KwargInfo('subprojects', ContainerTypeInfo(list, HotdocTarget), listify=True, default=[]), KwargInfo('install', bool, default=False), + KwargInfo('build_by_default', bool, default=False), allow_unknown=True ) def generate_doc(self, state: ModuleState, args: T.Tuple[str], kwargs: GenerateDocKwargs) -> ModuleReturnValue: From 821063d1423df331374949b61bfae075e1fce737 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 8 May 2026 09:22:39 -0700 Subject: [PATCH 232/681] run_mypy: all modules are now type safe! --- run_mypy.py | 27 +-------------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/run_mypy.py b/run_mypy.py index 7a6d29c699ce..224c3119a9ca 100755 --- a/run_mypy.py +++ b/run_mypy.py @@ -22,6 +22,7 @@ 'mesonbuild/interpreter/primitives/', 'mesonbuild/interpreterbase/', 'mesonbuild/linkers/', + 'mesonbuild/modules/', 'mesonbuild/scripts/', 'mesonbuild/templates/', 'mesonbuild/utils/', @@ -54,32 +55,6 @@ 'mesonbuild/mintro.py', 'mesonbuild/mlog.py', 'mesonbuild/msubprojects.py', - 'mesonbuild/modules/__init__.py', - 'mesonbuild/modules/cmake.py', - 'mesonbuild/modules/codegen.py', - 'mesonbuild/modules/cuda.py', - 'mesonbuild/modules/dlang.py', - 'mesonbuild/modules/external_project.py', - 'mesonbuild/modules/fs.py', - 'mesonbuild/modules/gnome.py', - 'mesonbuild/modules/i18n.py', - 'mesonbuild/modules/icestorm.py', - 'mesonbuild/modules/java.py', - 'mesonbuild/modules/keyval.py', - 'mesonbuild/modules/modtest.py', - 'mesonbuild/modules/pkgconfig.py', - 'mesonbuild/modules/python.py', - 'mesonbuild/modules/python3.py', - 'mesonbuild/modules/_qt.py', - 'mesonbuild/modules/qt4.py', - 'mesonbuild/modules/qt5.py', - 'mesonbuild/modules/qt6.py', - 'mesonbuild/modules/rust.py', - 'mesonbuild/modules/simd.py', - 'mesonbuild/modules/snippets.py', - 'mesonbuild/modules/sourceset.py', - 'mesonbuild/modules/wayland.py', - 'mesonbuild/modules/windows.py', 'mesonbuild/mparser.py', 'mesonbuild/msetup.py', 'mesonbuild/mtest.py', From 542232a59811094e61fc72fbc915f75f5d23d64f Mon Sep 17 00:00:00 2001 From: Jon Turney Date: Sun, 8 Feb 2026 13:15:56 +0000 Subject: [PATCH 233/681] meson-test-run.xml: Factor out creating testcase element --- run_project_tests.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/run_project_tests.py b/run_project_tests.py index 2d4e4e7f4890..b83339ad8b96 100755 --- a/run_project_tests.py +++ b/run_project_tests.py @@ -1287,11 +1287,18 @@ def tqdm_print(*args: mlog.TV_Loggable, sep: str = ' ') -> None: current_suite = junit_root.find(f"./testsuite[@name='{t.category}']") + current_test = ET.SubElement(current_suite, + 'testcase', + {'name': testname, 'classname': t.category}) + + if result: + testcase_time = result.conftime + result.buildtime + result.testtime + current_test.set('time', '%.3f' % testcase_time) + if is_skipped and skip_as_expected: f.update_log(TestStatus.SKIP) if not t.skip_category: safe_print(bold('Reason:'), skip_reason) - current_test = ET.SubElement(current_suite, 'testcase', {'name': testname, 'classname': t.category}) ET.SubElement(current_test, 'skipped', {}) continue @@ -1307,7 +1314,6 @@ def tqdm_print(*args: mlog.TV_Loggable, sep: str = ' ') -> None: f.update_log(status) safe_print(bold('Reason:'), result.msg) - current_test = ET.SubElement(current_suite, 'testcase', {'name': testname, 'classname': t.category}) ET.SubElement(current_test, 'failure', {'message': result.msg}) continue @@ -1364,13 +1370,7 @@ def tqdm_print(*args: mlog.TV_Loggable, sep: str = ' ') -> None: conf_time += result.conftime build_time += result.buildtime test_time += result.testtime - testcase_time = result.conftime + result.buildtime + result.testtime log_text_file(logfile, t.path, result) - current_test = ET.SubElement( - current_suite, - 'testcase', - {'name': testname, 'classname': t.category, 'time': '%.3f' % testcase_time} - ) if result.msg != '': ET.SubElement(current_test, 'failure', {'message': result.msg}) stdoel = ET.SubElement(current_test, 'system-out') From d26bbb64b8d29436151dc61f0bac8f6bf0474325 Mon Sep 17 00:00:00 2001 From: Jon Turney Date: Sun, 8 Feb 2026 15:25:50 +0000 Subject: [PATCH 234/681] meson-test-run.xml: Always attach output to testcase node This means that we capture that information even for SKIP and UNEXRUN cases. v2: Match the schema's ordering of testcase node's children. The schema uses 'sequence' for testcase, so child nodes should appear in a specific order. Restructure _run_tests a bit, so it's easy to do that. --- run_project_tests.py | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/run_project_tests.py b/run_project_tests.py index b83339ad8b96..c483956261bc 100755 --- a/run_project_tests.py +++ b/run_project_tests.py @@ -1295,14 +1295,15 @@ def tqdm_print(*args: mlog.TV_Loggable, sep: str = ' ') -> None: testcase_time = result.conftime + result.buildtime + result.testtime current_test.set('time', '%.3f' % testcase_time) + # skip if is_skipped and skip_as_expected: f.update_log(TestStatus.SKIP) if not t.skip_category: safe_print(bold('Reason:'), skip_reason) ET.SubElement(current_test, 'skipped', {}) - continue - if not skip_as_expected: + # unexrun/unexskip + elif not skip_as_expected: failing_tests += 1 if is_skipped: skip_msg = f'Test asked to be skipped ({skip_reason}), but was not expected to' @@ -1315,10 +1316,9 @@ def tqdm_print(*args: mlog.TV_Loggable, sep: str = ' ') -> None: f.update_log(status) safe_print(bold('Reason:'), result.msg) ET.SubElement(current_test, 'failure', {'message': result.msg}) - continue - # Handle Failed tests - if result.msg != '': + # failed + elif result.msg != '': f.update_log(TestStatus.ERROR) safe_print(bold('During:'), result.step.name) safe_print(bold('Reason:'), result.msg) @@ -1356,6 +1356,10 @@ def tqdm_print(*args: mlog.TV_Loggable, sep: str = ' ') -> None: safe_print("Cancelling the rest of the tests") for f2 in futures: f2.cancel() + + ET.SubElement(current_test, 'failure', {'message': result.msg}) + + # success else: f.update_log(TestStatus.OK) passing_tests += 1 @@ -1367,16 +1371,18 @@ def tqdm_print(*args: mlog.TV_Loggable, sep: str = ' ') -> None: else: mesonlib.windows_proof_rmtree(abspath) - conf_time += result.conftime - build_time += result.buildtime - test_time += result.testtime - log_text_file(logfile, t.path, result) - if result.msg != '': - ET.SubElement(current_test, 'failure', {'message': result.msg}) - stdoel = ET.SubElement(current_test, 'system-out') - stdoel.text = mtest.replace_unencodable_xml_chars(result.stdo) - stdeel = ET.SubElement(current_test, 'system-err') - stdeel.text = mtest.replace_unencodable_xml_chars(result.stde) + if result: + # track total runtime + conf_time += result.conftime + build_time += result.buildtime + test_time += result.testtime + + # attach stdout and stderr child nodes to 'testcase' node + ET.SubElement(current_test, 'system-out').text = mtest.replace_unencodable_xml_chars(result.stdo) + ET.SubElement(current_test, 'system-err').text = mtest.replace_unencodable_xml_chars(result.stde) + + # write stdout/stderr to log file (and terminal) + log_text_file(logfile, t.path, result) # Reset, just in case safe_print = default_print From 75d88bbfd5c6a017928fabb99d0d99504e33fe36 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 25 Feb 2026 11:40:25 +0100 Subject: [PATCH 235/681] cmake: fix case where NINJA is set in the environment For the --system-information case try multiple generators as done in dependencies/cmake.py. Then, when actually running cmake pass -DCMAKE_MAKE_PROGRAM so that cmake and meson's ninja are consistent. Fixes: #11236 Reviewed-by: Dylan Baker Signed-off-by: Paolo Bonzini --- mesonbuild/cmake/common.py | 17 +++++++++++++++-- mesonbuild/modules/cmake.py | 13 +++++++++++-- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/mesonbuild/cmake/common.py b/mesonbuild/cmake/common.py index 3fd20a31972d..eddbd6678abc 100644 --- a/mesonbuild/cmake/common.py +++ b/mesonbuild/cmake/common.py @@ -5,14 +5,17 @@ from ..mesonlib import MesonException from ..options import OptionKey +from ..tooldetect import detect_ninja from .. import mlog from pathlib import Path +import functools import typing as T if T.TYPE_CHECKING: from ..compilers.compilers import Language from ..environment import Environment from ..interpreterbase import TYPE_var + from .._typing import ImmutableListProtocol language_map: T.Mapping[Language, str] = { 'c': 'C', @@ -107,11 +110,21 @@ def _flags_to_list(raw: str) -> T.List[str]: res = [r for r in res if len(r) > 0] return res +@functools.lru_cache(maxsize=None) +def _cmake_get_generator_args(backend_name: str) -> ImmutableListProtocol[str]: + assert backend_name in backend_generator_map + args = ['-G', backend_generator_map[backend_name]] + if backend_name == 'ninja': + ninja = detect_ninja() + if ninja: + assert len(ninja) == 1 + args += [f'-DCMAKE_MAKE_PROGRAM={ninja[0]}'] + return args + def cmake_get_generator_args(env: 'Environment') -> T.List[str]: backend_name = env.coredata.optstore.get_value_for(OptionKey('backend')) assert isinstance(backend_name, str) - assert backend_name in backend_generator_map - return ['-G', backend_generator_map[backend_name]] + return list(_cmake_get_generator_args(backend_name)) def cmake_defines_to_args(raw: T.List[T.Dict[str, TYPE_var]], permissive: bool = False) -> T.List[str]: res: T.List[str] = [] diff --git a/mesonbuild/modules/cmake.py b/mesonbuild/modules/cmake.py index 2091499c30a3..0bfdacc99498 100644 --- a/mesonbuild/modules/cmake.py +++ b/mesonbuild/modules/cmake.py @@ -12,6 +12,7 @@ from .. import build, mesonlib, mlog, dependencies from ..options import OptionKey from ..cmake import TargetOptions, cmake_defines_to_args +from ..dependencies.cmake import CMakeDependency from ..interpreter import SubprojectHolder from ..interpreter.type_checking import REQUIRED_KW, INSTALL_DIR_KW, INCLUDE_TYPE, NoneType, in_set_validator from ..interpreterbase import ( @@ -272,8 +273,16 @@ def detect_cmake(self, state: ModuleState) -> bool: if not cmakebin.found(): return False - p, stdout, stderr = mesonlib.Popen_safe(cmakebin.get_command() + ['--system-information', '-G', 'Ninja'])[0:3] - if p.returncode != 0: + # Try different CMake generators since specifying no generator may fail + # in cygwin for some reason + for gen in CMakeDependency.class_cmake_generators: + cmd = cmakebin.get_command() + ['--system-information'] + if gen: + cmd += ['-G', gen] + p, stdout, stderr = mesonlib.Popen_safe(cmd)[0:3] + if p.returncode == 0: + break + else: mlog.log(f'error retrieving cmake information: returnCode={p.returncode} stdout={stdout} stderr={stderr}') return False From 4393d7cb07b31202f61c3819c256f64bf470ba3a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 25 Feb 2026 14:37:44 +0100 Subject: [PATCH 236/681] interpreter: allow files for *_args The type check fix in _BASE_LANG_KW is all that's needed since __convert_file_args already handles File objects correctly (converts to path string + adds to depend_files for rebuild tracking). In fact, the vala_args case 569 already had (str, File). This just brings the other languages in line. Fixes: #1633 Reviewed-by: Dylan Baker Signed-off-by: Paolo Bonzini --- docs/yaml/functions/_build_target_base.yaml | 8 +------- mesonbuild/interpreter/type_checking.py | 4 ++-- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/docs/yaml/functions/_build_target_base.yaml b/docs/yaml/functions/_build_target_base.yaml index 146c35231a48..c2c4552e9fa1 100644 --- a/docs/yaml/functions/_build_target_base.yaml +++ b/docs/yaml/functions/_build_target_base.yaml @@ -43,15 +43,9 @@ kwargs: description: precompiled header file to use for the given language _args: - type: array[str] - description: | - compiler flags to use for the given language; - eg: `cpp_args` for C++ - - vala_args: type: array[str | file] description: | - Compiler flags for Vala. Unlike other languages this may contain Files + compiler flags to use for the given language; eg: `cpp_args` for C++ sources: type: str | file | custom_tgt | custom_idx | generated_list | structured_src diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py index 26933410fb33..b36886975f9c 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -569,9 +569,9 @@ def suite_convertor(suite: T.List[str]) -> T.List[str]: since_values={CustomTargetIndex: '1.3.0'} ) -_BASE_LANG_KW: KwargInfo[T.List[str]] = KwargInfo( +_BASE_LANG_KW: KwargInfo[T.List[T.Union[str, File]]] = KwargInfo( 'UNKNOWN', - ContainerTypeInfo(list, (str)), + ContainerTypeInfo(list, (str, File)), listify=True, default=[], ) From 1695fbc7e89df6489c851fb0b1c65a9c8f0d0b51 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 25 Feb 2026 11:11:22 +0100 Subject: [PATCH 237/681] apply -werror=true when linking Fixes: #11318 Reviewed-by: Dylan Baker Signed-off-by: Paolo Bonzini --- docs/markdown/snippets/werror-linker.md | 6 ++++++ mesonbuild/backend/vs2010backend.py | 2 ++ mesonbuild/compilers/compilers.py | 5 +++++ mesonbuild/compilers/cuda.py | 3 +++ mesonbuild/compilers/mixins/islinker.py | 3 +++ mesonbuild/compilers/rust.py | 4 ++++ 6 files changed, 23 insertions(+) create mode 100644 docs/markdown/snippets/werror-linker.md diff --git a/docs/markdown/snippets/werror-linker.md b/docs/markdown/snippets/werror-linker.md new file mode 100644 index 000000000000..3976c26c544e --- /dev/null +++ b/docs/markdown/snippets/werror-linker.md @@ -0,0 +1,6 @@ +## `werror=true` now applies to the linker as well + +When `werror=true` is set, Meson now passes the appropriate +fatal-warnings flag to the linker (for example `--fatal-warnings` +for GNU ld, `-fatal_warnings` for Apple ld, `/WX` for MSVC link). +Previously, `werror=true` only affected compiler warnings. diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index cb3bf78fb184..a360d0681046 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -1449,6 +1449,8 @@ def add_non_makefile_vcxproj_elements( link = ET.SubElement(compiles, 'Link') extra_link_args = compiler.compiler_args() extra_link_args += compiler.get_optimization_link_args(self.optimization) + if self.get_target_option(target, 'werror'): + extra_link_args += compiler.get_linker_fatal_warnings() # Generate Debug info if self.debug: self.generate_debug_information(link) diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index eb0a5364c8b8..0ba0adbca4e3 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -356,6 +356,8 @@ def get_base_link_args(target: 'BuildTarget', env: 'Environment') -> T.List[str]: args: T.List[str] = [] build_dir = env.get_build_dir() + if env.coredata.get_option_for_target(target, 'werror'): + args.extend(linker.get_linker_fatal_warnings()) try: if env.coredata.get_option_for_target(target, 'b_lto'): if env.coredata.get_option_for_target(target, 'werror'): @@ -1180,6 +1182,9 @@ def bitcode_args(self) -> T.List[str]: def get_optimization_link_args(self, optimization_level: str) -> T.List[str]: return self.linker.get_optimization_link_args(optimization_level) + def get_linker_fatal_warnings(self) -> T.List[str]: + return self.linker.fatal_warnings() + def get_soname_args(self, prefix: str, shlib_name: str, suffix: str, soversion: str, darwin_versions: T.Tuple[str, str]) -> T.List[str]: return self.linker.get_soname_args( diff --git a/mesonbuild/compilers/cuda.py b/mesonbuild/compilers/cuda.py index 3c8687669be1..4f29498a3987 100644 --- a/mesonbuild/compilers/cuda.py +++ b/mesonbuild/compilers/cuda.py @@ -688,6 +688,9 @@ def get_depfile_suffix(self) -> str: def get_optimization_link_args(self, optimization_level: str) -> T.List[str]: return self._to_host_flags(self.host_compiler.get_optimization_link_args(optimization_level), Phase.LINKER) + def get_linker_fatal_warnings(self) -> T.List[str]: + return self._to_host_flags(self.host_compiler.get_linker_fatal_warnings(), Phase.LINKER) + def build_rpath_args(self, build_dir: str, from_dir: str, target: BuildTarget, extra_paths: T.Optional[T.List[str]] = None ) -> T.Tuple[T.List[str], T.Set[bytes]]: diff --git a/mesonbuild/compilers/mixins/islinker.py b/mesonbuild/compilers/mixins/islinker.py index 1c0361918991..0fec9be54287 100644 --- a/mesonbuild/compilers/mixins/islinker.py +++ b/mesonbuild/compilers/mixins/islinker.py @@ -111,6 +111,9 @@ def get_asneeded_args(self) -> T.List[str]: def get_optimization_link_args(self, optimization_level: str) -> T.List[str]: return [] + def get_linker_fatal_warnings(self) -> T.List[str]: + return [] + def get_link_debugfile_name(self, targetfile: str) -> T.Optional[str]: return None diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index dcfdfbb092c4..cc491d1b0a88 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -470,6 +470,10 @@ def bitcode_args(self) -> T.List[str]: def headerpad_args(self) -> T.List[str]: return rustc_link_args(super().headerpad_args()) + @functools.lru_cache(maxsize=None) + def get_linker_fatal_warnings(self) -> T.List[str]: + return rustc_link_args(super().get_linker_fatal_warnings()) + @functools.lru_cache(maxsize=None) def get_allow_undefined_link_args(self) -> T.List[str]: return rustc_link_args(super().get_allow_undefined_link_args()) From 9f3bf076bb9d05421fb6aabe7011a42d957adb0d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 25 Feb 2026 14:14:50 +0100 Subject: [PATCH 238/681] backends: fix vala object filename for sources in subdirectories When both_libraries() reuses object files from the shared library for the static library via ExtractedObjects, the object filenames must match those produced for the shared library. For non-built vala source files in subdirectories, object_filename_from_source() was using os.path.basename() which dropped the subdirectory component, while the actual compilation preserved it. Use os.path.relpath() against the target's source directory instead, matching the logic in generate_vala_compile(). Fixes: #6960 Reviewed-by: Dylan Baker Signed-off-by: Paolo Bonzini --- mesonbuild/backend/backends.py | 2 +- .../lib/meson.build | 10 ++++++++++ .../lib/subdir/mylib.vala | 5 +++++ .../33 subdir sources and both_libraries/meson.build | 5 +++++ 4 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 test cases/vala/33 subdir sources and both_libraries/lib/meson.build create mode 100644 test cases/vala/33 subdir sources and both_libraries/lib/subdir/mylib.vala create mode 100644 test cases/vala/33 subdir sources and both_libraries/meson.build diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 50ea1dd1ac4f..5fc3b8336be6 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -783,7 +783,7 @@ def object_filename_from_source(self, target: build.BuildTarget, compiler: Compi rel_src = rel_src[len(build_dir) + 1:] rel_src = os.path.relpath(rel_src, self.get_target_private_dir(target)) else: - rel_src = os.path.basename(rel_src) + rel_src = os.path.relpath(rel_src, os.path.join(self.build_to_src, target.get_subdir())) # A meson- prefixed directory is reserved; hopefully no-one creates a file name with such a weird prefix. gen_source = 'meson-generated_' + rel_src[:-5] + '.c' elif source.is_built: diff --git a/test cases/vala/33 subdir sources and both_libraries/lib/meson.build b/test cases/vala/33 subdir sources and both_libraries/lib/meson.build new file mode 100644 index 000000000000..ee4aeb80834b --- /dev/null +++ b/test cases/vala/33 subdir sources and both_libraries/lib/meson.build @@ -0,0 +1,10 @@ +args = [] +# https://github.com/mesonbuild/meson/issues/1969 +if get_option('unity') == 'on' + vala_args = ['-H', 'mylib.h'] +endif + +l = both_libraries('valalib', 'subdir/mylib.vala', + vala_args : args, + dependencies : valadeps, + install_dir : [false, get_option('includedir'), false]) diff --git a/test cases/vala/33 subdir sources and both_libraries/lib/subdir/mylib.vala b/test cases/vala/33 subdir sources and both_libraries/lib/subdir/mylib.vala new file mode 100644 index 000000000000..5cc903bd33e4 --- /dev/null +++ b/test cases/vala/33 subdir sources and both_libraries/lib/subdir/mylib.vala @@ -0,0 +1,5 @@ +public class LibraryObject : Object { + public void func() { + stdout.printf("Method in library called."); + } +} diff --git a/test cases/vala/33 subdir sources and both_libraries/meson.build b/test cases/vala/33 subdir sources and both_libraries/meson.build new file mode 100644 index 000000000000..29dab4b63d91 --- /dev/null +++ b/test cases/vala/33 subdir sources and both_libraries/meson.build @@ -0,0 +1,5 @@ +project('shared library', 'vala', 'c') + +valadeps = [dependency('glib-2.0'), dependency('gobject-2.0')] + +subdir('lib') From 1f28fe631841c6f01a263b9efabe4ddb623cc127 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 25 Feb 2026 12:00:44 +0100 Subject: [PATCH 239/681] compilers: override -nostdinc/-nostdlib flags for MSVC Fixes: #8412 Reviewed-by: Dylan Baker Signed-off-by: Paolo Bonzini --- mesonbuild/compilers/mixins/visualstudio.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mesonbuild/compilers/mixins/visualstudio.py b/mesonbuild/compilers/mixins/visualstudio.py index 3608d706e475..87173fb6bd57 100644 --- a/mesonbuild/compilers/mixins/visualstudio.py +++ b/mesonbuild/compilers/mixins/visualstudio.py @@ -129,6 +129,12 @@ def get_always_args(self) -> T.List[str]: # TODO: use ImmutableListProtocol[str] here instead return self.always_args.copy() + def get_no_stdinc_args(self) -> T.List[str]: + return ['/X'] + + def get_no_stdlib_link_args(self) -> T.List[str]: + return ['/NODEFAULTLIB'] + def get_pch_suffix(self) -> str: return 'pch' From bd21cd2c0602a35d548634b1f10ff4f1d466656f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 25 Feb 2026 12:07:12 +0100 Subject: [PATCH 240/681] compilers: override -nostdinc/-nostdlib for Objective C++ Use -nostdinc++ and -nostdlib++ just like in CPPCompiler. Reviewed-by: Dylan Baker Signed-off-by: Paolo Bonzini --- mesonbuild/compilers/objcpp.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mesonbuild/compilers/objcpp.py b/mesonbuild/compilers/objcpp.py index ad047d78626b..ff040855c540 100644 --- a/mesonbuild/compilers/objcpp.py +++ b/mesonbuild/compilers/objcpp.py @@ -34,6 +34,12 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ full_version=full_version, linker=linker) CLikeCompiler.__init__(self) + def get_no_stdinc_args(self) -> T.List[str]: + return ['-nostdinc++'] + + def get_no_stdlib_link_args(self) -> T.List[str]: + return ['-nostdlib++'] + def form_compileropt_key(self, basename: str) -> OptionKey: if basename == 'std': return OptionKey('cpp_std', machine=self.for_machine) From 25682fcbf7979d9c95e40024bdce473228531e69 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 25 Feb 2026 12:38:53 +0100 Subject: [PATCH 241/681] scan-build: fix program lookup from SCANBUILD environment variable When I pass an executable on PATH to SCANBUILD environment variable it fails: Could not execute scan-build "" Using shutil.which looks up bare names like scan-build in PATH and, on Windows, automatically checks executable extensions. Fixes: #9744 Reviewed-by: Dylan Baker Signed-off-by: Paolo Bonzini --- mesonbuild/tooldetect.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mesonbuild/tooldetect.py b/mesonbuild/tooldetect.py index 0d9f5c043b8c..44ffba9fde13 100644 --- a/mesonbuild/tooldetect.py +++ b/mesonbuild/tooldetect.py @@ -202,9 +202,9 @@ def detect_scanbuild() -> T.List[str]: break if exelist: - tool = exelist[0] - if os.path.isfile(tool) and os.access(tool, os.X_OK): - return [tool] + tool = shutil.which(exelist[0]) + if tool is not None: + return [tool] + exelist[1:] return [] def detect_clangformat() -> T.List[str]: From daa443188fd980cf7344e3c03cb3cb2455cef29b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 27 Feb 2026 08:43:24 +0100 Subject: [PATCH 242/681] mtest: tap: accept nonzero return code for failed tests The TAP 14 spec says that "A command-line test runner might exit with a non-zero status code" to "indicate failure to the user in whatever means are appropriate". Only force the ERROR test case if the result was OK or EXPECTEDFAIL. Fixes: #7811 Reviewed-by: Dylan Baker Signed-off-by: Paolo Bonzini --- mesonbuild/mtest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index 72621946088c..d18c151a4c40 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -1149,7 +1149,7 @@ def needs_parsing(self) -> bool: return True def complete(self) -> None: - if self.returncode != 0 and not self.res.was_killed(): + if self.returncode != 0 and self.res.is_ok(): self.res = TestResult.ERROR self.stde = self.stde or '' self.stde += f'\n(test program exited with status code {self.returncode})' From 6b748d744df2085e7f67f2f9a7bbf4afbf5d92ec Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 27 Feb 2026 12:43:33 +0100 Subject: [PATCH 243/681] configure_file: fix checks and logging with build_dir Meson gives an erroneous warning about the output file for configure_file() being overwritten if multiple configure_file() calls are being made from the same directory with the same output file name, but different build_subdir settings. In this case, the output files are not being overwritten because they are being created in different subdirectories. The output also does not include the build_subdir: Configuring file using configuration WARNING: Output file "file" for configure_file() at meson.build:4 overwrites configure_file() output at meson.build:4 Configuring file using configuration Now: Configuring hello/file using configuration Configuring world/file using configuration Fixes: #15500 Reviewed-by: Dylan Baker Signed-off-by: Paolo Bonzini --- mesonbuild/interpreter/interpreter.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 956eb0f69613..a152105383a2 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -2710,7 +2710,12 @@ def func_configure_file(self, node: mparser.BaseNode, args: T.List[TYPE_var], output = outputs[0] if depfile: depfile = mesonlib.substitute_values([depfile], values)[0] - ofile_rpath = os.path.join(self.subdir, output) + + # Validate build_subdir + build_subdir = kwargs['build_subdir'] + self.validate_build_subdir(build_subdir, output) + + ofile_rpath = os.path.join(self.subdir, build_subdir, output) if ofile_rpath in self.configure_file_outputs: mesonbuildfile = os.path.join(self.subdir, 'meson.build') current_call = f"{mesonbuildfile}:{self.current_node.lineno}" @@ -2719,12 +2724,8 @@ def func_configure_file(self, node: mparser.BaseNode, args: T.List[TYPE_var], else: self.configure_file_outputs[ofile_rpath] = self.current_node.lineno - # Validate build_subdir - build_subdir = kwargs['build_subdir'] - self.validate_build_subdir(build_subdir, output) - - (ofile_path, ofile_fname) = os.path.split(os.path.join(self.subdir, build_subdir, output)) - ofile_abs = os.path.join(self.environment.build_dir, ofile_path, ofile_fname) + ofile_path, ofile_fname = os.path.split(ofile_rpath) + ofile_abs = os.path.join(self.environment.build_dir, ofile_rpath) os.makedirs(os.path.split(ofile_abs)[0], exist_ok=True) # Perform the appropriate action @@ -2737,7 +2738,7 @@ def func_configure_file(self, node: mparser.BaseNode, args: T.List[TYPE_var], raise InvalidArguments( f'"configuration_data": initial value dictionary key "{k!r}"" must be "str | int | bool", not "{v!r}"') conf = build.ConfigurationData(conf) - mlog.log('Configuring', mlog.bold(output), 'using configuration') + mlog.log('Configuring', mlog.bold(os.path.join(build_subdir, output)), 'using configuration') if len(inputs) > 1: raise InterpreterException('At most one input file can given in configuration mode') if inputs: From b3dc010e0f858be9177b901c4093592c085a969d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 27 Feb 2026 12:57:53 +0100 Subject: [PATCH 244/681] mtest: do not show output of tests interrupted by --max-fail After the maximum number of failures is reached, any running test is canceled. However in this case the partial output of the test might not be a symptom of a hung test in the same way as it is after CTRL-C, so hide it. Fixes: #13322 Reviewed-by: Dylan Baker Signed-off-by: Paolo Bonzini --- mesonbuild/mtest.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index d18c151a4c40..0c7e69bbb072 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -738,7 +738,7 @@ def log(self, harness: 'TestHarness', result: 'TestRun') -> None: else: print(harness.format(result, mlog.colorize_console(), max_left_width=self.max_left_width), flush=True) - if result.verbose or result.res.is_bad(): + if result.verbose or harness.is_bad_result(result): self.print_log(harness, result) if result.warnings: print(flush=True) @@ -1673,6 +1673,7 @@ class TestHarness: def __init__(self, options: argparse.Namespace): self.options = options self.collected_failures: T.List[TestRun] = [] + self.maxfail_reached = False self.fail_count = 0 self.expectedfail_count = 0 self.unexpectedpass_count = 0 @@ -1835,11 +1836,14 @@ def process_test_result(self, result: TestRun) -> None: else: sys.exit(f'Unknown test result encountered: {result.res}') - if result.res.is_bad(): + if self.is_bad_result(result): self.collected_failures.append(result) for l in self.loggers: l.log(self, result) + def is_bad_result(self, result: TestRun) -> bool: + return result.res.is_bad() and not (result.res is TestResult.INTERRUPT and self.maxfail_reached) + @property def numlen(self) -> int: return len(str(self.test_count)) @@ -2154,6 +2158,7 @@ async def run_test(test: SingleTestRunner) -> None: self.process_test_result(res) maxfail = self.options.maxfail if maxfail and self.fail_count >= maxfail and res.res.is_bad(): + self.maxfail_reached = True cancel_all_tests() def test_done(f: asyncio.Future) -> None: From 9b60304a9b2090d8932495bca75fc7bb8c655200 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 24 Apr 2026 15:16:47 +0200 Subject: [PATCH 245/681] gettext, gnome: use meson_exe to invoke msgmerge meson is instructing msgmerge to output to the same file as the input, but this means that interrupting the execution of msgmerge results in an empty .po file. Instead, go through scripts.meson_exe and make msgmerge print to stdout. This is more effective because it also skips the update if the contents did not change. Fixes: #4683 Reviewed-by: Dylan Baker Signed-off-by: Paolo Bonzini --- mesonbuild/modules/gnome.py | 9 ++++----- mesonbuild/scripts/gettext.py | 7 ++++++- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index e24831ce3695..b4c46102b7e5 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -1393,11 +1393,10 @@ def yelp(self, state: 'ModuleState', args: T.Tuple[str, T.List[str]], kwargs: 'Y mesonlib.FileMode(), state.subproject, install_tag='doc') targets.append(l_data) - po_fname = l + '.po' - po_args: CommandList = [ - msgmerge, '-q', '-o', - os.path.join('@SOURCE_ROOT@', l_subdir, po_fname), - os.path.join('@SOURCE_ROOT@', l_subdir, po_fname), pot_file] + po_path = os.path.join('@SOURCE_ROOT@', l_subdir, l + '.po') + po_args: CommandList = [*state.environment.get_build_command(), + '--internal', 'exe', '--capture', po_path, '--', + msgmerge, '-q', po_path, pot_file] potarget = build.RunTarget(f'help-{project_id}-{l}-update-po', po_args, [pottarget], l_subdir, state.subproject, state.environment) diff --git a/mesonbuild/scripts/gettext.py b/mesonbuild/scripts/gettext.py index 94e002da592b..a0a368c077a8 100644 --- a/mesonbuild/scripts/gettext.py +++ b/mesonbuild/scripts/gettext.py @@ -8,6 +8,9 @@ import subprocess import typing as T +from .meson_exe import run_exe +from ..utils.core import ExecutableSerialisation + parser = argparse.ArgumentParser() parser.add_argument('command') parser.add_argument('--pkgname', default='') @@ -59,7 +62,9 @@ def update_po(src_sub: str, msgmerge: str, msginit: str, pkgname: str, langs: T. for l in langs: pofile = os.path.join(src_sub, l + '.po') if os.path.exists(pofile): - subprocess.check_call([msgmerge, '-q', '-o', pofile, pofile, potfile]) + exe = ExecutableSerialisation([msgmerge, '-q', pofile, potfile], capture=pofile) + if run_exe(exe) != 0: + return 1 else: subprocess.check_call([msginit, '--input', potfile, '--output-file', pofile, '--locale', l, '--no-translator']) return 0 From 119b36dceeeeabd6b7053d05f2e2cae843e818d8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 17 Nov 2025 18:44:02 +0100 Subject: [PATCH 246/681] interpreter: accept "forbidden" targets in a subdirectory An executable in a subdirectory does not result in a forbidden target name, allow it if it's not named "meson-*" or "meson-internal__*". Signed-off-by: Paolo Bonzini --- mesonbuild/interpreter/interpreter.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index a152105383a2..e39e85205715 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -2124,8 +2124,6 @@ def func_custom_target(self, node: mparser.FunctionNode, args: T.Tuple[str], f'(there are {len(kwargs["install_tag"])} install_tags, ' f'and {len(kwargs["output"])} outputs)') - for t in kwargs['output']: - self.validate_forbidden_targets(t) self._validate_custom_target_outputs(len(inputs) > 1, kwargs['output'], "custom_target") tg = build.CustomTarget( @@ -2151,6 +2149,10 @@ def func_custom_target(self, node: mparser.FunctionNode, args: T.Tuple[str], install_tag=kwargs['install_tag'], backend=self.backend, build_subdir=kwargs['build_subdir']) + + subdir = tg.get_builddir() + for t in tg.get_outputs(): + self.validate_forbidden_targets(t, not subdir) self.add_target(tg.name, tg) return tg @@ -3249,8 +3251,7 @@ def source_strings_to_files(self, sources: T.List['SourceInputs'], strict: bool 'string or File-type object') return results - @staticmethod - def validate_forbidden_targets(name: str) -> None: + def validate_forbidden_targets(self, name: str, in_root: bool) -> None: if name.startswith('meson-internal__'): raise InvalidArguments("Target names starting with 'meson-internal__' are reserved " "for Meson's internal use. Please rename.") @@ -3258,8 +3259,12 @@ def validate_forbidden_targets(name: str) -> None: raise InvalidArguments("Target names starting with 'meson-' and without a file extension " "are reserved for Meson's internal use. Please rename.") if name in coredata.FORBIDDEN_TARGET_NAMES: - raise InvalidArguments(f"Target name '{name}' is reserved for Meson's " - "internal use. Please rename.") + if in_root: + raise InvalidArguments(f"Target name '{name}' is reserved for Meson's " + "internal use. Please rename.") + else: + FeatureNew.single_use(f"Target name '{name}' reserved in the root build directory, but allowed in subdirectories", + '1.12.0', self.subproject, location=self.current_node) def add_target(self, name: str, tobj: build.Target) -> None: if self.backend.name == 'none': @@ -3282,11 +3287,12 @@ def add_target(self, name: str, tobj: build.Target) -> None: build_subdir = tobj.get_build_subdir() self.validate_build_subdir(build_subdir, name) - self.validate_forbidden_targets(name) + subdir = tobj.get_builddir() + self.validate_forbidden_targets(name, not subdir) + # To permit an executable and a shared library to have the # same name, such as "foo.exe" and "libfoo.a". idname = tobj.get_id() - subdir = tobj.get_builddir() namedir = (name, subdir) if idname in self.build.targets: From de6bbc6820f694a041da65550a80f6b046617a04 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 11 May 2026 10:13:35 -0700 Subject: [PATCH 247/681] interpreter: fix typed_kwargs name of add_project_link_arguments Which was reporting global instead of project --- mesonbuild/interpreter/interpreter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index e39e85205715..ecb3ab6c29bc 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -2974,7 +2974,7 @@ def func_add_project_arguments(self, node: mparser.FunctionNode, args: T.Tuple[T args[0], kwargs) @typed_pos_args('add_project_link_arguments', varargs=str) - @typed_kwargs('add_global_arguments', NATIVE_KW, LANGUAGE_KW) + @typed_kwargs('add_project_arguments', NATIVE_KW, LANGUAGE_KW) def func_add_project_link_arguments(self, node: mparser.FunctionNode, args: T.Tuple[T.List[str]], kwargs: 'kwtypes.FuncAddProjectArgs') -> None: self._add_project_arguments(node, self.current_build_project().project_link_args[kwargs['native']], args[0], kwargs) From aaab1802eb6e63a6b55fdd1acc6db4067ad84d47 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 14 May 2026 18:50:09 +0200 Subject: [PATCH 248/681] cargo: allow git dependencies with slash in branch name If you have something like this in Cargo.toml: [dependencies] gtk = { git = "https://github.com/valpackett/gtk4-rs", branch = "val/xsqkvtwpkqkz", features = ["gnome_49"], package = "gtk4" } you can get errors like ../meson.build:12:23: ERROR: Neither a subproject directory nor a gtk4-rs-val/xsqkvtwpkqkz.wrap file was found. or ../meson.build:1:0: ERROR: Directory key must be a name and not a path The first case shows clearly what's going on, the branch contains a slash but subprojects must be directly under subprojects/ with no intermediate directories. Fixes: #15780 Signed-off-by: Paolo Bonzini --- mesonbuild/cargo/interpreter.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index 6ffae639bf14..83bc8e10d551 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -884,7 +884,8 @@ def _parse_git_url(url: str, branch: T.Optional[str] = None) -> T.Tuple[str, str if directory.endswith('.git'): directory = directory[:-4] if branch: - directory += f'-{branch}' + branch_encoded = branch.replace('/', '-') + directory += f'-{branch_encoded}' url = urllib.parse.urlunparse(parts._replace(params='', query='', fragment='')) return url, revision, directory From c938127320342f4c69e8a730ca694cae4fae1909 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 14 May 2026 18:19:46 +0200 Subject: [PATCH 249/681] ninjabackend: add PCH objects as a dependency PCH objects for the Microsoft compiler were not listed in build.ninja, leading to rules like build a.dll.p/meson_pch-c.obj a.dll.p/f_pch.pch: c_PCH a.dll.p/meson_pch-c.c | ../test$ cases/common/296$ pch$ link_language/pch/f_pch.h .... build a.dll | a.pdb: c_LINKER a.dll.p/f.cc.obj LINK_ARGS = "/release" "/nologo" "/DEBUG" "/PDB:a.pdb" "/DLL" "/IMPLIB:a.lib" "-APPCONTAINER" "WindowsApp.lib" "kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "comdlg32.lib" "advapi32.lib" "a.dll.p\meson_pch-c.obj" If the link_language for a.dll is not used for any of the object files, nothing will have built a.dll.p/meson_pch-c.obj prior to the link and the build will fail. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 596c3044570a..a965963deb97 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -3764,9 +3764,9 @@ def get_build_rpath_args(self, target: build.BuildTarget, linker: T.Union[Compil def generate_link(self, target: build.BuildTarget, outname: str, obj_list: T.List[str], linker: T.Union[Compiler, StaticLinker], - extra_args: T.Optional[T.List[str]] = None, + extra_objs: T.Optional[T.List[str]] = None, stdlib_args: T.Optional[T.List[str]] = None) -> NinjaBuildElement: - extra_args = extra_args if extra_args is not None else [] + extra_objs = extra_objs if extra_objs is not None else [] stdlib_args = stdlib_args if stdlib_args is not None else [] implicit_outs = [] if isinstance(target, build.StaticLibrary): @@ -3893,14 +3893,14 @@ def generate_link(self, target: build.BuildTarget, outname: str, obj_list: T.Lis # Add libraries generated by custom targets custom_target_libraries = self.get_custom_target_provided_libraries(target) - commands += extra_args + commands += extra_objs commands += custom_target_libraries commands += stdlib_args # Standard library arguments go last, because they never depend on anything. dep_targets.extend([self.get_dependency_filename(t) for t in dependencies]) dep_targets.extend([self.get_dependency_filename(t) for t in target.link_depends]) elem = NinjaBuildElement(self.all_outputs, outname, linker_rule, obj_list, implicit_outs=implicit_outs) - all_deps = [*dep_targets, *custom_target_libraries] + all_deps = [*dep_targets, *extra_objs, *custom_target_libraries] elem.add_dep(all_deps) if linker.get_id() == 'tasking': if any(x.endswith('.ma') for x in all_deps) and not self.get_target_option(target, OptionKey('b_lto', target.subproject, target.for_machine)): From ac6b76610d7083e50833246f6557cdea557d0a3a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 27 Feb 2026 11:08:56 +0100 Subject: [PATCH 250/681] build, ninjabackend: limit PCH to exactly source and link languages If the link_language is not one of the languages used to compile the sources, looking it up in target.compilers results in a KeyError. Add it manually, and also skip languages that are not part of the target; this avoids inconsistent behavior for targets that conditionally have sources from one language: srcs = ['a.c'] if host_machine.system() == 'windows' srcs.append('windows.cpp') endif build_target( 'target', srcs, c_args : ['-DDEFINE'], cpp_args : ['-DDEFINE'], # ignored for !windows automatically c_pch : 'c_pch.c', cpp_pch : 'cpp_pch.c', # always ends up doing work even on !windows ) Fixes: #14113 Suggested-by: Dylan Baker Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 2 ++ mesonbuild/build.py | 2 ++ test cases/common/296 pch link_language/f.cc | 2 ++ test cases/common/296 pch link_language/f.h | 2 ++ test cases/common/296 pch link_language/meson.build | 2 ++ test cases/common/296 pch link_language/pch/f_pch.h | 1 + 6 files changed, 11 insertions(+) create mode 100644 test cases/common/296 pch link_language/f.cc create mode 100644 test cases/common/296 pch link_language/f.h create mode 100644 test cases/common/296 pch link_language/meson.build create mode 100644 test cases/common/296 pch link_language/pch/f_pch.h diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index a965963deb97..054e15f7ebbc 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -3515,6 +3515,8 @@ def generate_pch(self, target: build.BuildTarget, header_deps: T.Optional[T.List pch = target.pch[lang] if not pch: continue + if lang not in target.compilers: + continue compiler: Compiler = target.compilers[lang] if compiler.get_argument_syntax() == 'msvc': (commands, dep, dst, objs, src) = self.generate_msvc_pch_command(target, compiler, pch) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index dbe2226150e7..d2542412aa21 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -1100,6 +1100,8 @@ def process_compilers_late(self) -> None: if self.link_language not in self.all_compilers: m = f'Target {self.name} requires {self.link_language} compiler not part of the project' raise MesonException(m) + if self.link_language not in self.compilers and self.pch.get(self.link_language, None): + self.compilers[self.link_language] = self.all_compilers[self.link_language] # If this library is linked against another library we need to consider # the languages of those libraries as well. diff --git a/test cases/common/296 pch link_language/f.cc b/test cases/common/296 pch link_language/f.cc new file mode 100644 index 000000000000..2968c941d962 --- /dev/null +++ b/test cases/common/296 pch link_language/f.cc @@ -0,0 +1,2 @@ +#include "f.h" +int f() { return 42; } diff --git a/test cases/common/296 pch link_language/f.h b/test cases/common/296 pch link_language/f.h new file mode 100644 index 000000000000..69788c847df9 --- /dev/null +++ b/test cases/common/296 pch link_language/f.h @@ -0,0 +1,2 @@ +#pragma once +int f(); diff --git a/test cases/common/296 pch link_language/meson.build b/test cases/common/296 pch link_language/meson.build new file mode 100644 index 000000000000..ee2bda90b5ee --- /dev/null +++ b/test cases/common/296 pch link_language/meson.build @@ -0,0 +1,2 @@ +project('a', 'c', 'cpp') +library('a', 'f.cc', c_pch: 'pch/f_pch.h', link_language: 'c') diff --git a/test cases/common/296 pch link_language/pch/f_pch.h b/test cases/common/296 pch link_language/pch/f_pch.h new file mode 100644 index 000000000000..acd5e08c3b4e --- /dev/null +++ b/test cases/common/296 pch link_language/pch/f_pch.h @@ -0,0 +1 @@ +#include "f.h" From 2e953c1d239d8de2f0f6f86dd846b2a21c0bf3c9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 15 May 2026 17:52:53 +0200 Subject: [PATCH 251/681] interpreter: fix typos in typed_kwargs Both add_project_link_arguments and add_global_link_arguments had a cut-and-paste mistake in the @typed_kwargs() decorator, fix it (again). Signed-off-by: Paolo Bonzini --- mesonbuild/interpreter/interpreter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index ecb3ab6c29bc..07f0a6e546c1 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -2963,7 +2963,7 @@ def func_add_global_arguments(self, node: mparser.FunctionNode, args: T.Tuple[T. self._add_global_arguments(node, self.build.global_args[kwargs['native']], args[0], kwargs) @typed_pos_args('add_global_link_arguments', varargs=str) - @typed_kwargs('add_global_arguments', NATIVE_KW, LANGUAGE_KW) + @typed_kwargs('add_global_link_arguments', NATIVE_KW, LANGUAGE_KW) def func_add_global_link_arguments(self, node: mparser.FunctionNode, args: T.Tuple[T.List[str]], kwargs: 'kwtypes.FuncAddProjectArgs') -> None: self._add_global_arguments(node, self.build.global_link_args[kwargs['native']], args[0], kwargs) @@ -2974,7 +2974,7 @@ def func_add_project_arguments(self, node: mparser.FunctionNode, args: T.Tuple[T args[0], kwargs) @typed_pos_args('add_project_link_arguments', varargs=str) - @typed_kwargs('add_project_arguments', NATIVE_KW, LANGUAGE_KW) + @typed_kwargs('add_project_link_arguments', NATIVE_KW, LANGUAGE_KW) def func_add_project_link_arguments(self, node: mparser.FunctionNode, args: T.Tuple[T.List[str]], kwargs: 'kwtypes.FuncAddProjectArgs') -> None: self._add_project_arguments(node, self.current_build_project().project_link_args[kwargs['native']], args[0], kwargs) From cff370d3499c0a88a4770a7eaa680556b0833b97 Mon Sep 17 00:00:00 2001 From: Ludovic Rousseau Date: Sun, 17 May 2026 15:21:16 +0200 Subject: [PATCH 252/681] Documentation: remove duplicated text and fix a grammatical error. --- docs/yaml/objects/compiler.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/yaml/objects/compiler.yaml b/docs/yaml/objects/compiler.yaml index c6761806df96..bdb2032c3ce7 100644 --- a/docs/yaml/objects/compiler.yaml +++ b/docs/yaml/objects/compiler.yaml @@ -195,7 +195,7 @@ methods: returns: int description: | Returns the alignment of the specified type. For C-like languages, - For C-like languages, the header `stddef.h` and `stdio.h` are included + the headers `stddef.h` and `stdio.h` are included implicitly for native compilation, only `stddef.h` is included when cross-compiling. From ba92b2527150570ee3ed698a36aa896cfad59bb2 Mon Sep 17 00:00:00 2001 From: Aleksandr Mezin Date: Mon, 18 May 2026 01:59:32 +0300 Subject: [PATCH 253/681] mtest: fix handling of empty lines in TAP YAML block YAML blocks can contain lines that consist of whitespace only. `.rstrip()` makes such lines empty. Move `.rstrip()` past YAML block handling. --- mesonbuild/mtest.py | 3 ++- unittests/taptests.py | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index 0c7e69bbb072..6ccbe5cb2b69 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -398,7 +398,6 @@ def parse(self, io: T.Iterator[str]) -> T.Iterator[TYPE_TAPResult]: def parse_line(self, line: T.Optional[str]) -> T.Iterator[TYPE_TAPResult]: if line is not None: self.lineno += 1 - line = line.rstrip() # YAML blocks are only accepted after a test if self.state == self._AFTER_TEST: @@ -420,6 +419,8 @@ def parse_line(self, line: T.Optional[str]) -> T.Iterator[TYPE_TAPResult]: yield self.Error(f'YAML block not terminated (started on line {self.yaml_lineno})') self.state = self._MAIN + line = line.rstrip() + assert self.state == self._MAIN if not line or line.startswith('#'): return diff --git a/unittests/taptests.py b/unittests/taptests.py index e91194cb564c..d3707edcc74d 100644 --- a/unittests/taptests.py +++ b/unittests/taptests.py @@ -331,3 +331,10 @@ def test_yaml(self): self.assert_error(events) self.assert_test(events, number=2, name='', result=TestResult.FAIL) self.assert_last(events) + + # Whitespace-only line in YAML block + events = self.parse_tap_v13('ok 1\n ---\n foo: abc\n \n bar: def\nnot ok 2') + self.assert_test(events, number=1, name='', result=TestResult.OK) + self.assert_error(events) + self.assert_test(events, number=2, name='', result=TestResult.FAIL) + self.assert_last(events) From a72f7b7389409aea61da1b530ebe62980555fa96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20=22sp1rit=22=E2=80=8B?= Date: Sun, 17 May 2026 21:46:44 +0200 Subject: [PATCH 254/681] cargo: add docsrs to unconditionally added --check-cfg cfg's In order to suppress compiler warnings that'll occur otherwise if the crate in question is relying on it. --- mesonbuild/cargo/interpreter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index 83bc8e10d551..15fc5c64c48d 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -155,8 +155,8 @@ def get_lint_args(self, rustc: RustCompiler) -> T.List[str]: args.extend(lint.to_arguments(has_check_cfg)) if has_check_cfg: - args.append('--check-cfg') - args.append('cfg(test)') + args.extend(['--check-cfg', 'cfg(docsrs)', + '--check-cfg', 'cfg(test)']) for feature in self.manifest.features: if feature != 'default': args.append('--check-cfg') From 86b4e9eac979d671e4895548f55a2db143ded5a1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 17 May 2026 15:22:52 +0200 Subject: [PATCH 255/681] introspect: do not print umask as octal in JSON Signed-off-by: Paolo Bonzini --- mesonbuild/mintro.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 42bd13dea9f4..424c77537e73 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -246,7 +246,14 @@ def add_keys(opts: T.Union[options.MutableKeyedOptionDictType, options.OptionSto elif isinstance(opt, options.UserComboOption): optdict['choices'] = opt.printable_choices() typestr = 'combo' - elif isinstance(opt, (options.UserIntegerOption, options.UserUmaskOption)): + elif isinstance(opt, options.UserUmaskOption): + # do not print 0o22 + if isinstance(optdict['value'], int): + typestr = 'integer' + optdict['value'] = int(optdict['value']) + else: + typestr = 'string' + elif isinstance(opt, options.UserIntegerOption): typestr = 'integer' elif isinstance(opt, options.UserStringArrayOption): typestr = 'array' From d8cb326b5fd04d83cc87b296974be75b58095e82 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 8 May 2026 08:21:53 -0700 Subject: [PATCH 256/681] Interpreter: fix the typing of the Summary class This still leaves the problem of the Summary object using Any, but we'll get there. --- mesonbuild/interpreter/interpreter.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 07f0a6e546c1..5dd690cb9e0b 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -150,15 +150,15 @@ class Summary: def __init__(self, project_name: str, project_version: str): self.project_name = project_name self.project_version = project_version - self.sections = collections.defaultdict(dict) + self.sections: collections.defaultdict[str, dict[str, tuple[mlog.TV_LoggableList, str | None]]] = collections.defaultdict(dict) self.max_key_len = 0 def add_section(self, section: str, values: T.Dict[str, T.Any], bool_yn: bool, - list_sep: T.Optional[str], subproject: str) -> None: + list_sep: T.Optional[str], subproject: SubProject) -> None: for k, v in values.items(): if k in self.sections[section]: raise InterpreterException(f'Summary section {section!r} already have key {k!r}') - formatted_values = [] + formatted_values: mlog.TV_LoggableList = [] for i in listify(v): if isinstance(i, bool): if bool_yn: @@ -182,7 +182,7 @@ def add_section(self, section: str, values: T.Dict[str, T.Any], bool_yn: bool, self.sections[section][k] = (formatted_values, list_sep) self.max_key_len = max(self.max_key_len, len(k)) - def dump(self): + def dump(self) -> None: mlog.log(self.project_name, mlog.normal_cyan(self.project_version)) for section, values in self.sections.items(): mlog.log('') # newline @@ -197,17 +197,17 @@ def dump(self): self.dump_value(v, list_sep, indent) mlog.log('') # newline - def dump_value(self, arr, list_sep, indent): + def dump_value(self, arr: mlog.TV_LoggableList, list_sep: str | None, indent: int) -> None: lines_sep = '\n' + ' ' * indent if list_sep is None: mlog.log(*arr, sep=lines_sep, display_timestamp=False) return max_len = shutil.get_terminal_size().columns - line = [] + line: mlog.TV_LoggableList = [] line_len = indent lines_sep = list_sep.rstrip() + lines_sep for v in arr: - v_len = len(v) + len(list_sep) + v_len = len(str(v)) + len(list_sep) if line and line_len + v_len > max_len: mlog.log(*line, sep=list_sep, end=lines_sep) line_len = indent @@ -216,6 +216,7 @@ def dump_value(self, arr, list_sep, indent): line_len += v_len mlog.log(*line, sep=list_sep, display_timestamp=False) + class InterpreterRuleRelaxation(Enum): ''' Defines specific relaxations of the Meson rules. From b9ad8a0604a49881ca72fb5a3e534c772e4e00f5 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 14 May 2026 10:51:26 -0700 Subject: [PATCH 257/681] utils: Move SubProject definition to utils.universal Since we use it at ever layer of the codebase, it doesn't make sense for it to live in the interpreter --- mesonbuild/ast/interpreter.py | 3 ++- mesonbuild/ast/introspection.py | 4 ++-- mesonbuild/build.py | 4 ++-- mesonbuild/cargo/interpreter.py | 4 ++-- mesonbuild/coredata.py | 3 +-- mesonbuild/interpreter/dependencyfallbacks.py | 4 ++-- mesonbuild/interpreter/interpreter.py | 3 ++- mesonbuild/interpreter/interpreterobjects.py | 3 ++- mesonbuild/interpreter/primitives/range.py | 2 +- mesonbuild/interpreterbase/__init__.py | 4 ---- mesonbuild/interpreterbase/baseobjects.py | 5 +---- mesonbuild/interpreterbase/decorators.py | 3 ++- mesonbuild/interpreterbase/helpers.py | 4 +++- mesonbuild/interpreterbase/interpreterbase.py | 3 ++- mesonbuild/mdist.py | 3 +-- mesonbuild/modules/cmake.py | 3 +-- mesonbuild/modules/hotdoc.py | 4 ++-- mesonbuild/optinterpreter.py | 2 +- mesonbuild/options.py | 2 +- mesonbuild/utils/core.py | 2 +- mesonbuild/utils/universal.py | 3 ++- mesonbuild/wrap/wrap.py | 3 +-- 22 files changed, 34 insertions(+), 37 deletions(-) diff --git a/mesonbuild/ast/interpreter.py b/mesonbuild/ast/interpreter.py index 3ec6c85a070b..035ee2af2f1b 100644 --- a/mesonbuild/ast/interpreter.py +++ b/mesonbuild/ast/interpreter.py @@ -60,7 +60,7 @@ if T.TYPE_CHECKING: from .visitor import AstVisitor from ..interpreter import Interpreter - from ..interpreterbase import SubProject, TYPE_var, TYPE_nvar + from ..interpreterbase import TYPE_var, TYPE_nvar from ..mparser import ( AndNode, ComparisonNode, @@ -71,6 +71,7 @@ TestCaseClauseNode, UMinusNode, ) + from ..mesonlib import SubProject _T = T.TypeVar('_T') _V = T.TypeVar('_V') diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py index da277721ec9a..3e15dd271873 100644 --- a/mesonbuild/ast/introspection.py +++ b/mesonbuild/ast/introspection.py @@ -12,9 +12,9 @@ from .. import compilers, environment, mesonlib from ..build import Executable, Jar, SharedLibrary, SharedModule, StaticLibrary from ..compilers import detect_compiler_for -from ..interpreterbase import InvalidArguments, SubProject, UnknownValue, Feature +from ..interpreterbase import InvalidArguments, UnknownValue, Feature from ..interpreter import type_checking -from ..mesonlib import MachineChoice +from ..mesonlib import MachineChoice, SubProject from ..options import OptionKey from ..mparser import BaseNode, ArrayNode, ElementaryNode, IdNode, FunctionNode, StringNode from .interpreter import AstInterpreter, IntrospectionBuildTarget, IntrospectionDependency diff --git a/mesonbuild/build.py b/mesonbuild/build.py index d2542412aa21..1aecdc8009f0 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -20,7 +20,7 @@ from . import mlog from . import programs from .mesonlib import ( - HoldableObject, SecondLevelHolder, SimpleABC, + HoldableObject, SecondLevelHolder, SimpleABC, SubProject, File, MesonException, MachineChoice, PerMachine, OrderedSet, classify_unity_sources, get_filenames_templates_dict, substitute_values, has_path_sep, @@ -34,7 +34,7 @@ is_known_suffix, detect_static_linker, LANGUAGES_USING_LDFLAGS, get_base_compile_args ) -from .interpreterbase import FeatureNew, FeatureDeprecated, SubProject +from .interpreterbase import FeatureNew, FeatureDeprecated if T.TYPE_CHECKING: from typing_extensions import Literal, Self, TypeAlias, TypedDict diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index 15fc5c64c48d..2602ffb0ae4b 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -24,10 +24,10 @@ from .cfg import eval_cfg from .toml import load_toml from .manifest import Manifest, CargoLock, CargoLockPackage, Workspace, fixup_meson_varname -from ..interpreterbase import SubProject from ..mesonlib import ( is_parent_path, lazy_property, MesonException, MachineChoice, - unique_list, version_compare) + unique_list, version_compare, SubProject, +) from .. import coredata, mlog from ..wrap.wrap import PackageDefinition diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index fb19d59b6ac8..87a1fe6c2aa1 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -28,9 +28,8 @@ from . import dependencies from .compilers.compilers import Compiler, CompilerDict, CompileResult, RunResult, CompileCheckMode, Language from .dependencies.detect import TV_DepID - from .mesonlib import FileOrString + from .mesonlib import FileOrString, SubProject from .cmake.traceparser import CMakeCacheEntry - from .interpreterbase import SubProject from .options import ElementaryOptionValues, MutableKeyedOptionDictType from .build import BuildTarget from .cmdline import SharedCMDOptions diff --git a/mesonbuild/interpreter/dependencyfallbacks.py b/mesonbuild/interpreter/dependencyfallbacks.py index 1685cc9de70a..c6027b0f047d 100644 --- a/mesonbuild/interpreter/dependencyfallbacks.py +++ b/mesonbuild/interpreter/dependencyfallbacks.py @@ -8,11 +8,11 @@ from .. import dependencies from .. import build from ..wrap import WrapMode -from ..mesonlib import stringlistify, version_compare_many +from ..mesonlib import stringlistify, version_compare_many, SubProject from ..options import OptionKey from ..dependencies import Dependency, DependencyException, NotFoundDependency from ..interpreterbase import (MesonInterpreterObject, FeatureNew, - InterpreterException, InvalidArguments, SubProject) + InterpreterException, InvalidArguments) import typing as T if T.TYPE_CHECKING: diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 5dd690cb9e0b..3a23deb1baf0 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -118,8 +118,9 @@ from . import kwargs as kwtypes from ..backend.backends import Backend from ..compilers.compilers import CompilerDict, Language - from ..interpreterbase.baseobjects import InterpreterObject, TYPE_var, TYPE_kwargs, SubProject + from ..interpreterbase.baseobjects import InterpreterObject, TYPE_var, TYPE_kwargs from ..options import OptionDict + from ..mesonlib import SubProject from .type_checking import SourcesVarargsType # Input source types passed to Targets diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index 565940a69a97..c9c49bbd54d6 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -35,7 +35,8 @@ from ..cmake.interpreter import CMakeInterpreter from ..dependencies.base import IncludeType from ..envconfig import MachineInfo - from ..interpreterbase import FeatureCheckBase, SubProject, TYPE_var, TYPE_kwargs, TYPE_nvar, TYPE_nkwargs + from ..interpreterbase import FeatureCheckBase, TYPE_var, TYPE_kwargs, TYPE_nvar, TYPE_nkwargs + from ..mesonlib import SubProject from .interpreter import Interpreter from typing_extensions import Literal, TypedDict diff --git a/mesonbuild/interpreter/primitives/range.py b/mesonbuild/interpreter/primitives/range.py index 1aceb684801e..4420b90cfea1 100644 --- a/mesonbuild/interpreter/primitives/range.py +++ b/mesonbuild/interpreter/primitives/range.py @@ -13,7 +13,7 @@ ) if T.TYPE_CHECKING: - from ...interpreterbase import SubProject + from ...mesonlib import SubProject class RangeHolder(MesonInterpreterObject, IterableObject): def __init__(self, start: int, stop: int, step: int, *, subproject: 'SubProject') -> None: diff --git a/mesonbuild/interpreterbase/__init__.py b/mesonbuild/interpreterbase/__init__.py index 887b93476687..dddb2027f3da 100644 --- a/mesonbuild/interpreterbase/__init__.py +++ b/mesonbuild/interpreterbase/__init__.py @@ -48,8 +48,6 @@ 'InterpreterBase', - 'SubProject', - 'TV_func', 'TYPE_elementary', 'TYPE_var', @@ -82,8 +80,6 @@ TYPE_key_resolver, TYPE_HoldableTypes, - SubProject, - HoldableTypes, UnknownValue, diff --git a/mesonbuild/interpreterbase/baseobjects.py b/mesonbuild/interpreterbase/baseobjects.py index 20867d6416d8..b9d2edb06273 100644 --- a/mesonbuild/interpreterbase/baseobjects.py +++ b/mesonbuild/interpreterbase/baseobjects.py @@ -7,7 +7,7 @@ from .exceptions import InvalidCode, InvalidArguments from .helpers import flatten, resolve_second_level_holders from .operator import MesonOperator -from ..mesonlib import HoldableObject, MesonBugException, SimpleABC +from ..mesonlib import HoldableObject, MesonBugException, SimpleABC, SubProject import textwrap import typing as T @@ -32,9 +32,6 @@ TYPE_op_func = T.Callable[[TYPE_op_arg, TYPE_op_arg], TYPE_var] TYPE_method_func = T.Callable[['InterpreterObject', T.List[TYPE_var], TYPE_kwargs], TYPE_var] - -SubProject = T.NewType('SubProject', str) - class InterpreterObject: TRIVIAL_OPERATORS: T.Dict[ MesonOperator, diff --git a/mesonbuild/interpreterbase/decorators.py b/mesonbuild/interpreterbase/decorators.py index 38b115431136..2df5487d19e5 100644 --- a/mesonbuild/interpreterbase/decorators.py +++ b/mesonbuild/interpreterbase/decorators.py @@ -18,10 +18,11 @@ from typing_extensions import Protocol, TypeAlias, TypeIs from .. import mparser + from ..mesonlib import SubProject from ..modules import ModuleObject, ModuleState from ..mparser import FunctionNode from ..optinterpreter import OptionInterpreter - from .baseobjects import InterpreterObject, ObjectHolder, SubProject, TV_func, TYPE_var, TYPE_kwargs + from .baseobjects import InterpreterObject, ObjectHolder, TV_func, TYPE_var, TYPE_kwargs from .interpreterbase import InterpreterBase from .operator import MesonOperator diff --git a/mesonbuild/interpreterbase/helpers.py b/mesonbuild/interpreterbase/helpers.py index 6ac2e1ce36b3..195c68115131 100644 --- a/mesonbuild/interpreterbase/helpers.py +++ b/mesonbuild/interpreterbase/helpers.py @@ -14,7 +14,9 @@ import typing as T if T.TYPE_CHECKING: - from .baseobjects import TYPE_var, TYPE_kwargs, SubProject + from .baseobjects import TYPE_var, TYPE_kwargs + from ..mesonlib import SubProject + def flatten(args: T.Union['TYPE_var', T.List['TYPE_var']]) -> T.List['TYPE_var']: if isinstance(args, mparser.StringNode): diff --git a/mesonbuild/interpreterbase/interpreterbase.py b/mesonbuild/interpreterbase/interpreterbase.py index a601e5c6d408..13de57039325 100644 --- a/mesonbuild/interpreterbase/interpreterbase.py +++ b/mesonbuild/interpreterbase/interpreterbase.py @@ -40,9 +40,10 @@ import textwrap if T.TYPE_CHECKING: - from .baseobjects import InterpreterObjectTypeVar, SubProject, TYPE_kwargs, TYPE_var + from .baseobjects import InterpreterObjectTypeVar, TYPE_kwargs, TYPE_var from ..ast import AstVisitor from ..interpreter import Interpreter + from ..mesonlib import SubProject HolderMapType = T.Dict[ T.Union[ diff --git a/mesonbuild/mdist.py b/mesonbuild/mdist.py index 59e416511334..df1d9144dbac 100644 --- a/mesonbuild/mdist.py +++ b/mesonbuild/mdist.py @@ -32,8 +32,7 @@ if T.TYPE_CHECKING: from ._typing import ImmutableListProtocol - from .interpreterbase.baseobjects import SubProject - from .mesonlib import ExecutableSerialisation + from .mesonlib import ExecutableSerialisation, SubProject archive_choices = ['bztar', 'gztar', 'xztar', 'zip'] diff --git a/mesonbuild/modules/cmake.py b/mesonbuild/modules/cmake.py index 0bfdacc99498..682b5dd3727a 100644 --- a/mesonbuild/modules/cmake.py +++ b/mesonbuild/modules/cmake.py @@ -23,7 +23,6 @@ InvalidArguments, InterpreterException, - SubProject, typed_pos_args, typed_kwargs, @@ -440,7 +439,7 @@ def configure_package_config_file(self, state: ModuleState, args: TYPE_var, kwar def subproject(self, state: ModuleState, args: T.Tuple[str], kwargs_: Subproject) -> T.Union[SubprojectHolder, CMakeSubproject]: if kwargs_['cmake_options'] and kwargs_['options'] is not None: raise InterpreterException('"options" cannot be used together with "cmake_options"') - subp_name = SubProject(args[0]) + subp_name = mesonlib.SubProject(args[0]) kw: kwargs.DoSubproject = { 'required': kwargs_['required'], 'options': kwargs_['options'], diff --git a/mesonbuild/modules/hotdoc.py b/mesonbuild/modules/hotdoc.py index 5a2715b9adb3..4a3ebdd34c13 100644 --- a/mesonbuild/modules/hotdoc.py +++ b/mesonbuild/modules/hotdoc.py @@ -29,7 +29,7 @@ from ..environment import Environment from ..interpreter import Interpreter from ..interpreter.kwargs import TargetDepends - from ..interpreterbase import TYPE_kwargs, TYPE_var, SubProject + from ..interpreterbase import TYPE_kwargs, TYPE_var _T = T.TypeVar('_T') @@ -387,7 +387,7 @@ def config_path_method(self, *args: T.Any, **kwargs: T.Any) -> str: class HotdocTarget(CustomTarget): - def __init__(self, name: str, subdir: str, subproject: SubProject, hotdoc_conf: File, + def __init__(self, name: str, subdir: str, subproject: mesonlib.SubProject, hotdoc_conf: File, extra_extension_paths: T.Set[str], extra_assets: T.List[str], subprojects: T.List['HotdocTarget'], environment: Environment, **kwargs: T.Any): super().__init__(name, subdir, subproject, environment, **kwargs, absolute_paths=True) diff --git a/mesonbuild/optinterpreter.py b/mesonbuild/optinterpreter.py index 1127cc936e4f..e6f897b359e0 100644 --- a/mesonbuild/optinterpreter.py +++ b/mesonbuild/optinterpreter.py @@ -16,7 +16,7 @@ if T.TYPE_CHECKING: from .interpreterbase import TYPE_var, TYPE_kwargs - from .interpreterbase import SubProject + from .mesonlib import SubProject from typing_extensions import TypedDict, Literal from .options import OptionStore diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 5e63b4ac275f..a2a0195ff91e 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -36,7 +36,7 @@ from typing_extensions import Literal, Final, TypeAlias from .envconfig import MachineInfo - from .interpreterbase import SubProject + from .mesonlib import SubProject from .compilers.compilers import Language DeprecatedType: TypeAlias = T.Union[bool, str, T.Dict[str, str], T.List[str]] diff --git a/mesonbuild/utils/core.py b/mesonbuild/utils/core.py index 095e173a4a9e..0073040633a3 100644 --- a/mesonbuild/utils/core.py +++ b/mesonbuild/utils/core.py @@ -18,8 +18,8 @@ from hashlib import _Hash from typing_extensions import Literal from ..mparser import BaseNode - from ..interpreterbase import SubProject from .. import programs + from .universal import SubProject EnvironOrDict = T.Union[T.Dict[str, str], os._Environ[str]] diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index 10a588d41403..daed4db11741 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -38,7 +38,6 @@ from ..cmdline import StrOrBytesPath from ..environment import Environment from ..compilers.compilers import Compiler - from ..interpreterbase.baseobjects import SubProject from .. import programs class _EnvPickleLoadable(Protocol): @@ -65,6 +64,7 @@ class _VerPickleLoadable(Protocol): 'NoProjectVersion', 'project_meson_versions', 'SecondLevelHolder', + 'SubProject', 'File', 'FileMode', 'GitException', @@ -173,6 +173,7 @@ class _VerPickleLoadable(Protocol): 'windows_proof_rmtree', ] +SubProject = T.NewType('SubProject', str) class NoProjectVersion: pass diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py index 1f0c7a1453b5..9af23ea89a09 100644 --- a/mesonbuild/wrap/wrap.py +++ b/mesonbuild/wrap/wrap.py @@ -32,10 +32,9 @@ from .. import coredata from ..mesonlib import ( DirectoryLock, DirectoryLockAction, quiet_git, GIT, ProgressBar, MesonException, - windows_proof_rmtree, Popen_safe + windows_proof_rmtree, Popen_safe, SubProject, ) from ..interpreterbase import FeatureNew -from ..interpreterbase import SubProject from .. import mesonlib if T.TYPE_CHECKING: From 75b9a3287eb83ad3efd2952c61b3a94a39ae8824 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 14 May 2026 10:56:40 -0700 Subject: [PATCH 258/681] mesonlib: Add a constant for the root SubProject To avoid needing to cast or convert throughout the codebase. --- mesonbuild/ast/introspection.py | 2 +- mesonbuild/build.py | 4 ++-- mesonbuild/interpreterbase/baseobjects.py | 4 ++-- mesonbuild/utils/universal.py | 3 +++ mesonbuild/wrap/wrap.py | 4 ++-- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py index 3e15dd271873..75e942beaedb 100644 --- a/mesonbuild/ast/introspection.py +++ b/mesonbuild/ast/introspection.py @@ -67,7 +67,7 @@ def __init__(self, backend: str, visitors: T.Optional[T.List[AstVisitor]] = None, cross_file: T.Optional[str] = None, - subproject: SubProject = SubProject(''), + subproject: SubProject = mesonlib.ROOT_SUBPROJECT, subproject_dir: str = 'subprojects', env: T.Optional[environment.Environment] = None): options = IntrospectionHelper(cross_file) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 1aecdc8009f0..ac80e2251563 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -22,7 +22,7 @@ from .mesonlib import ( HoldableObject, SecondLevelHolder, SimpleABC, SubProject, File, MesonException, MachineChoice, PerMachine, OrderedSet, - classify_unity_sources, + classify_unity_sources, ROOT_SUBPROJECT, get_filenames_templates_dict, substitute_values, has_path_sep, is_parent_path, relpath, PerMachineDefaultable, MesonBugException, EnvironmentVariables, pickle_load, lazy_property, @@ -453,7 +453,7 @@ def ensure_static_linker(self, compiler: Compiler) -> None: self.static_linker[MachineChoice.BUILD] = self.static_linker[MachineChoice.HOST] def get_project(self) -> str: - return self.projects[SubProject('')].name + return self.projects[ROOT_SUBPROJECT].name def get_subproject_dir(self) -> str: return self.subproject_dir diff --git a/mesonbuild/interpreterbase/baseobjects.py b/mesonbuild/interpreterbase/baseobjects.py index b9d2edb06273..4f29aa038843 100644 --- a/mesonbuild/interpreterbase/baseobjects.py +++ b/mesonbuild/interpreterbase/baseobjects.py @@ -7,7 +7,7 @@ from .exceptions import InvalidCode, InvalidArguments from .helpers import flatten, resolve_second_level_holders from .operator import MesonOperator -from ..mesonlib import HoldableObject, MesonBugException, SimpleABC, SubProject +from ..mesonlib import HoldableObject, MesonBugException, SimpleABC, SubProject, ROOT_SUBPROJECT import textwrap import typing as T @@ -102,7 +102,7 @@ def __init__(self, *, subproject: T.Optional['SubProject'] = None) -> None: # Current node set during a method call. This can be used as location # when printing a warning message during a method call. self.current_node: mparser.BaseNode = None - self.subproject = subproject or SubProject('') + self.subproject = subproject or ROOT_SUBPROJECT # The type of the object that can be printed to the user def display_name(self) -> str: diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index daed4db11741..9a26d28b1dce 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -59,6 +59,7 @@ class _VerPickleLoadable(Protocol): __all__ = [ 'GIT', + 'ROOT_SUBPROJECT', 'SimpleABC', 'python_command', 'NoProjectVersion', @@ -175,6 +176,8 @@ class _VerPickleLoadable(Protocol): SubProject = T.NewType('SubProject', str) +ROOT_SUBPROJECT = SubProject('') + class NoProjectVersion: pass diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py index 9af23ea89a09..6e5cc5b3fbcc 100644 --- a/mesonbuild/wrap/wrap.py +++ b/mesonbuild/wrap/wrap.py @@ -233,7 +233,7 @@ def from_directory(filename: str) -> PackageDefinition: return PackageDefinition(name, subprojects_dir) @staticmethod - def from_wrap_file(filename: str, subproject: SubProject = SubProject('')) -> PackageDefinition: + def from_wrap_file(filename: str, subproject: SubProject = mesonlib.ROOT_SUBPROJECT) -> PackageDefinition: config, type_, values = PackageDefinition._parse_wrap(filename) if 'diff_files' in values: FeatureNew('Wrap files with diff_files', '0.63.0').use(subproject) @@ -366,7 +366,7 @@ def verbose_git(cmd: T.List[str], workingdir: str, check: bool = False) -> bool: class Resolver: source_dir: str subdir: str - subproject: SubProject = SubProject('') + subproject: SubProject = mesonlib.ROOT_SUBPROJECT wrap_mode: WrapMode = WrapMode.default wrap_frontend: bool = False allow_insecure: bool = False From 5938cb0f6c7a3a29aabe85b48f6daed3b7ecb03c Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 11 May 2026 09:04:55 -0700 Subject: [PATCH 259/681] program: Add `get_name` to base class And implement for LocalProgram. This is needed to fulfill it's shape inside the Interpreter --- mesonbuild/build.py | 3 +++ mesonbuild/programs.py | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index ac80e2251563..ba61e4ddc35a 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -3595,6 +3595,9 @@ def description(self) -> str: def runnable(self) -> bool: return isinstance(self.program, programs.ExternalProgram) + def get_name(self) -> str: + return self.name + # A bit poorly named, but this represents plain data files to copy # during install. @dataclass(eq=False) diff --git a/mesonbuild/programs.py b/mesonbuild/programs.py index 58bbf7e82bab..faa03306a9cd 100644 --- a/mesonbuild/programs.py +++ b/mesonbuild/programs.py @@ -50,6 +50,10 @@ def get_command(self) -> T.List[str]: def get_path(self) -> T.Optional[str]: pass + @abstractmethod + def get_name(self) -> T.Optional[str]: + pass + @abstractmethod def runnable(self) -> bool: pass From 94f2c2ee85d583018e4b49751faa221a9044502f Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 11 May 2026 09:05:56 -0700 Subject: [PATCH 260/681] interpreter: Delete unused old validation code --- mesonbuild/interpreter/interpreter.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 3a23deb1baf0..fd3484651fc8 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -721,15 +721,6 @@ def func_assert(self, node: mparser.FunctionNode, args: T.Tuple[bool, T.Optional message = printer.result raise InterpreterException('Assert failed: ' + message) - def validate_arguments(self, args, argcount, arg_types): - if argcount is not None: - if argcount != len(args): - raise InvalidArguments(f'Expected {argcount} arguments, got {len(args)}.') - for actual, wanted in zip(args, arg_types): - if wanted is not None: - if not isinstance(actual, wanted): - raise InvalidArguments('Incorrect argument type.') - # Executables aren't actually accepted, but we allow them here to allow for # better error messages when overridden @typed_pos_args( From 880d3162763d1dd1546bec63b4ff9bc506d2610b Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 11 May 2026 09:10:30 -0700 Subject: [PATCH 261/681] Interpreter: Fix more str that should be SubProject The never ending battle to use SubProject --- mesonbuild/cargo/interpreter.py | 4 ++-- mesonbuild/interpreter/interpreter.py | 10 +++++----- mesonbuild/wrap/wrap.py | 12 ++++++------ 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index 2602ffb0ae4b..e1e30e35dcca 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -913,7 +913,7 @@ def load_cargo_lock(filename: str, subproject_dir: str) -> T.Optional[CargoLock] checksum = cargolock.metadata[f'checksum {package.name} {package.version} ({package.source})'] url = f'https://crates.io/api/v1/crates/{package.name}/{package.version}/download' directory = f'{package.name}-{package.version}' - name = meson_depname + name = SubProject(meson_depname) wrap_type = 'file' cfg = { 'directory': directory, @@ -924,7 +924,7 @@ def load_cargo_lock(filename: str, subproject_dir: str) -> T.Optional[CargoLock] } elif package.source.startswith('git+'): url, revision, directory = _parse_git_url(package.source) - name = directory + name = SubProject(directory) wrap_type = 'git' cfg = { 'url': url, diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index fd3484651fc8..903ea1ceb70c 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -239,7 +239,7 @@ def __init__( self, _build: build.Build, backend: T.Optional[Backend] = None, - subproject: SubProject = '', + subproject: SubProject = mesonlib.ROOT_SUBPROJECT, subdir: str = '', subproject_dir: str = 'subprojects', invoker_method_default_options: T.Optional[OptionDict] = None, @@ -828,7 +828,7 @@ def func_option(self, nodes, args, kwargs): DEFAULT_OPTIONS.evolve(since='0.38.0'), KwargInfo('version', ContainerTypeInfo(list, str), default=[], listify=True), ) - def func_subproject(self, nodes: mparser.BaseNode, args: T.Tuple[str], kwargs: kwtypes.Subproject) -> SubprojectHolder: + def func_subproject(self, nodes: mparser.BaseNode, args: T.Tuple[SubProject], kwargs: kwtypes.Subproject) -> SubprojectHolder: kw: kwtypes.DoSubproject = { 'required': kwargs['required'], 'default_options': kwargs['default_options'], @@ -1447,7 +1447,7 @@ def func_exception(self, node, args, kwargs): ) def func_expect_error(self, node: mparser.BaseNode, args: T.Tuple[str], kwargs: TYPE_kwargs) -> ContextManagerObject: class ExpectErrorObject(ContextManagerObject): - def __init__(self, msg: str, how: str, subproject: str) -> None: + def __init__(self, msg: str, how: str, subproject: SubProject) -> None: super().__init__(subproject) self.old_stdout = sys.stdout sys.stdout = self.new_stdout = io.StringIO() @@ -1698,7 +1698,7 @@ def program_lookup(self, args: T.List[mesonlib.FileOrString], for_machine: Machi # Override find_program('meson') to return what we were invoked with return ExternalProgram('meson', self.environment.get_build_command(), silent=True) - fallback = None + fallback: SubProject | None = None wrap_mode = WrapMode.from_string(self.coredata.optstore.get_value_for(OptionKey('wrap_mode'))) if wrap_mode != WrapMode.nofallback and self.environment.wrap_resolver: fallback = self.environment.wrap_resolver.find_program_provider(args) @@ -1749,7 +1749,7 @@ def check_program_version(self, progobj: Program, extra_info.insert(0, mlog.normal_cyan(version)) return True - def find_program_fallback(self, fallback: str, args: T.List[mesonlib.FileOrString], + def find_program_fallback(self, fallback: SubProject, args: T.List[mesonlib.FileOrString], default_options: OptionDict, required: bool, extra_info: T.List[mlog.TV_Loggable] ) -> T.Optional[Program]: diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py index 6e5cc5b3fbcc..ddbb7eea1d82 100644 --- a/mesonbuild/wrap/wrap.py +++ b/mesonbuild/wrap/wrap.py @@ -196,7 +196,7 @@ class WrapNotFoundException(WrapException): pass class PackageDefinition: - def __init__(self, name: str, subprojects_dir: str, type_: T.Optional[str] = None, values: T.Optional[T.Dict[str, str]] = None): + def __init__(self, name: SubProject, subprojects_dir: str, type_: T.Optional[str] = None, values: T.Optional[T.Dict[str, str]] = None): self.name = name self.subprojects_dir = subprojects_dir self.type = type_ @@ -223,12 +223,12 @@ def __init__(self, name: str, subprojects_dir: str, type_: T.Optional[str] = Non self.provided_deps[self.name.lower()] = None @staticmethod - def from_values(name: str, subprojects_dir: str, type_: str, values: T.Dict[str, str]) -> PackageDefinition: + def from_values(name: SubProject, subprojects_dir: str, type_: str, values: T.Dict[str, str]) -> PackageDefinition: return PackageDefinition(name, subprojects_dir, type_, values) @staticmethod def from_directory(filename: str) -> PackageDefinition: - name = os.path.basename(filename) + name = SubProject(os.path.basename(filename)) subprojects_dir = os.path.dirname(filename) return PackageDefinition(name, subprojects_dir) @@ -267,7 +267,7 @@ def from_wrap_file(filename: str, subproject: SubProject = mesonlib.ROOT_SUBPROJ wrap.redirected = True return wrap - name = os.path.basename(filename)[:-5] + name = SubProject(os.path.basename(filename)[:-5]) wrap = PackageDefinition.from_values(name, subprojects_dir, type_, values) wrap.original_filename = filename wrap.parse_provide_section(config) @@ -381,7 +381,7 @@ def __post_init__(self) -> None: self.provided_programs: T.Dict[str, PackageDefinition] = {} self.wrapdb: T.Dict[str, T.Any] = {} self.wrapdb_provided_deps: T.Dict[str, str] = {} - self.wrapdb_provided_programs: T.Dict[str, str] = {} + self.wrapdb_provided_programs: T.Dict[str, SubProject] = {} self.loaded_dirs: T.Set[str] = set() self.load_wraps() self.load_netrc() @@ -502,7 +502,7 @@ def get_varname(self, subp_name: str, depname: str) -> T.Optional[str]: wrap = self.wraps.get(subp_name) return wrap.provided_deps.get(depname) if wrap else None - def find_program_provider(self, names: T.List[str]) -> T.Optional[str]: + def find_program_provider(self, names: T.List[str]) -> T.Optional[SubProject]: for name in names: wrap = self.provided_programs.get(name) if wrap: From 80db2c79e40bb6b9d2bd32bef7b38c4e60667caf Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 11 May 2026 09:13:48 -0700 Subject: [PATCH 262/681] interpreter: assert types of options where possible There is the `get_option` function where we can't know, but otherwise, assert --- mesonbuild/interpreter/interpreter.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 903ea1ceb70c..a856f0395e97 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1118,9 +1118,11 @@ def set_backend(self) -> None: # Use of the '--genvslite vsxxxx' option ultimately overrides any '--backend xxx' # option the user may specify. backend_name = self.coredata.optstore.get_value_for(OptionKey('genvslite')) + assert isinstance(backend_name, str), 'for mypy' self.backend = backends.get_genvslite_backend(backend_name, self.build) else: backend_name = self.coredata.optstore.get_value_for(OptionKey('backend')) + assert isinstance(backend_name, str), 'for mypy' self.backend = backends.get_backend_from_name(backend_name, self.build) if self.backend is None: @@ -1274,7 +1276,9 @@ def func_project(self, node: mparser.FunctionNode, args: T.Tuple[str, T.List[str # Load wrap files from this (sub)project. subprojects_dir = os.path.join(self.subdir, spdirname) if not self.is_subproject(): - wrap_mode = WrapMode.from_string(self.coredata.optstore.get_value_for(OptionKey('wrap_mode'))) + wrap_mode_s = self.coredata.optstore.get_value_for(OptionKey('wrap_mode')) + assert isinstance(wrap_mode_s, str), 'for mypy' + wrap_mode = WrapMode.from_string(wrap_mode_s) self.environment.wrap_resolver = wrap.Resolver(self.environment.get_source_dir(), subprojects_dir, self.subproject, wrap_mode) else: assert self.environment.wrap_resolver is not None, 'for mypy' @@ -1699,7 +1703,9 @@ def program_lookup(self, args: T.List[mesonlib.FileOrString], for_machine: Machi return ExternalProgram('meson', self.environment.get_build_command(), silent=True) fallback: SubProject | None = None - wrap_mode = WrapMode.from_string(self.coredata.optstore.get_value_for(OptionKey('wrap_mode'))) + wrap_mode_s = self.coredata.optstore.get_value_for(OptionKey('wrap_mode')) + assert isinstance(wrap_mode_s, str), 'for mypy' + wrap_mode = WrapMode.from_string(wrap_mode_s) if wrap_mode != WrapMode.nofallback and self.environment.wrap_resolver: fallback = self.environment.wrap_resolver.find_program_provider(args) if fallback and wrap_mode == WrapMode.forcefallback: @@ -3110,6 +3116,7 @@ def check_clang_asan_lundef(self) -> None: if (self.coredata.optstore.get_value_for('b_lundef') and self.coredata.optstore.get_value_for('b_sanitize')): value = self.coredata.optstore.get_value_for('b_sanitize') + assert isinstance(value, list), 'for mypy' mlog.warning(textwrap.dedent(f'''\ Trying to use {value} sanitizer on Clang with b_lundef. This will probably not work. @@ -3313,8 +3320,10 @@ def build_both_libraries(self, node: mparser.BaseNode, args: T.Tuple[str, Source shared_lib = self.build_target(node, args, kwargs, build.SharedLibrary, shared_library_only=False) static_lib = self.build_target(node, args, kwargs, build.StaticLibrary) preferred_library = self.coredata.optstore.get_value_for(OptionKey('default_both_libraries', subproject=self.subproject)) + assert isinstance(preferred_library, str), 'for mypy' if preferred_library == 'auto': preferred_library = self.coredata.optstore.get_value_for(OptionKey('default_library', subproject=self.subproject)) + assert isinstance(preferred_library, str), 'for mypy' if preferred_library == 'both': preferred_library = 'shared' From 3697ae283c46caae59d2377605e79f500f1fb822 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 11 May 2026 09:26:49 -0700 Subject: [PATCH 263/681] interpreter: Fix most cases of missing function argument annotations There's still a couple of tricky cases left, but this covers most things --- mesonbuild/interpreter/interpreter.py | 40 ++++++++++++++------------- mesonbuild/interpreter/kwargs.py | 13 +++++++++ 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index a856f0395e97..02c890067809 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -818,7 +818,7 @@ def run_command_impl(self, in_builddir=in_builddir, check=check, capture=kwargs['capture'], console=kwargs['console']) - def func_option(self, nodes, args, kwargs): + def func_option(self, nodes: mparser.BaseNode, args: list[TYPE_var], kwargs: TYPE_kwargs) -> T.NoReturn: raise InterpreterException('Tried to call option() in build description file. All options must be in the option file.') @typed_pos_args('subproject', str) @@ -1324,7 +1324,7 @@ def func_add_languages(self, node: mparser.FunctionNode, args: T.Tuple[T.List[st success &= self.add_languages(langs, False, MachineChoice.BUILD) return success - def _stringify_user_arguments(self, args: T.List[TYPE_var], func_name: str) -> T.List[str]: + def _stringify_user_arguments(self, args: T.List[TYPE_var], func_name: str) -> mlog.TV_LoggableList: try: return [stringifyUserArguments(i, self.subproject) for i in args] except InvalidArguments as e: @@ -1332,13 +1332,13 @@ def _stringify_user_arguments(self, args: T.List[TYPE_var], func_name: str) -> T @noArgsFlattening @noKwargs - def func_message(self, node: mparser.BaseNode, args, kwargs): + def func_message(self, node: mparser.BaseNode, args: list[TYPE_var], kwargs: TYPE_kwargs) -> None: if len(args) > 1: FeatureNew.single_use('message with more than one argument', '0.54.0', self.subproject, location=node) args_str = self._stringify_user_arguments(args, 'message') self.message_impl(args_str) - def message_impl(self, args): + def message_impl(self, args: mlog.TV_LoggableList) -> None: mlog.log(mlog.bold('Message:'), *args) @noArgsFlattening @@ -1418,7 +1418,7 @@ def compatibility_sort_helper(s): @noArgsFlattening @FeatureNew('warning', '0.44.0') @noKwargs - def func_warning(self, node, args, kwargs): + def func_warning(self, node: mparser.BaseNode, args: list[TYPE_var], kwargs: TYPE_kwargs) -> None: if len(args) > 1: FeatureNew.single_use('warning with more than one argument', '0.54.0', self.subproject, location=node) args_str = self._stringify_user_arguments(args, 'warning') @@ -1426,7 +1426,7 @@ def func_warning(self, node, args, kwargs): @noArgsFlattening @noKwargs - def func_error(self, node, args, kwargs): + def func_error(self, node: mparser.BaseNode, args: list[TYPE_var], kwargs: TYPE_kwargs) -> T.NoReturn: if len(args) > 1: FeatureNew.single_use('error with more than one argument', '0.58.0', self.subproject, location=node) args_str = self._stringify_user_arguments(args, 'error') @@ -1435,13 +1435,13 @@ def func_error(self, node, args, kwargs): @noArgsFlattening @FeatureNew('debug', '0.63.0') @noKwargs - def func_debug(self, node, args, kwargs): + def func_debug(self, node: mparser.BaseNode, args: list[TYPE_var], kwargs: TYPE_kwargs) -> None: args_str = self._stringify_user_arguments(args, 'debug') mlog.debug('Debug:', *args_str) @noKwargs @noPosargs - def func_exception(self, node, args, kwargs): + def func_exception(self, node: mparser.BaseNode, args: list[TYPE_var], kwargs: TYPE_kwargs) -> T.NoReturn: raise RuntimeError('unit test traceback :)') @typed_pos_args('expect_error', str) @@ -1851,7 +1851,7 @@ def func_dependency(self, node: mparser.BaseNode, args: T.Tuple[T.List[str]], kw @FeatureNew('disabler', '0.44.0') @noKwargs @noPosargs - def func_disabler(self, node, args, kwargs): + def func_disabler(self, node: mparser.BaseNode, args: list[TYPE_var], kwargs: TYPE_kwargs) -> Disabler: return Disabler() @typed_pos_args('executable', str, varargs=SOURCES_VARARGS) @@ -2309,7 +2309,7 @@ def make_test(self, node: mparser.BaseNode, def add_test(self, node: mparser.BaseNode, args: T.Tuple[str, T.Union[build.Executable, build.Jar, ExternalProgram, mesonlib.File, build.CustomTarget, build.CustomTargetIndex]], - kwargs: T.Dict[str, T.Any], is_base_test: bool): + kwargs: T.Dict[str, T.Any], is_base_test: bool) -> None: if isinstance(args[1], (build.CustomTarget, build.CustomTargetIndex)): FeatureNew.single_use('test with CustomTarget as command', '1.4.0', self.subproject) if any(isinstance(i, ExternalProgram) for i in kwargs['args']): @@ -2401,7 +2401,8 @@ def func_install_man(self, node: mparser.BaseNode, INSTALL_MODE_KW, KwargInfo('install_tag', (str, NoneType), since='0.62.0') ) - def func_install_emptydir(self, node: mparser.BaseNode, args: T.Tuple[str], kwargs) -> None: + def func_install_emptydir(self, node: mparser.BaseNode, args: T.Tuple[str], + kwargs: kwtypes.FuncInstallEmptyDir) -> build.EmptyDir: d = build.EmptyDir(args[0], kwargs['install_mode'], self.subproject, kwargs['install_tag']) self.build.emptydir.append(d) @@ -2416,8 +2417,8 @@ def func_install_emptydir(self, node: mparser.BaseNode, args: T.Tuple[str], kwar INSTALL_TAG_KW, ) def func_install_symlink(self, node: mparser.BaseNode, - args: T.Tuple[T.List[str]], - kwargs) -> build.SymlinkData: + args: T.Tuple[str], + kwargs: kwtypes.FuncInstallSymlink) -> build.SymlinkData: name = args[0] # Validation while creating the SymlinkData object target = kwargs['pointing_to'] l = build.SymlinkData(target, name, kwargs['install_dir'], @@ -2620,7 +2621,7 @@ def func_install_subdir(self, node: mparser.BaseNode, args: T.Tuple[str], self.build.install_dirs.append(idir) return idir - def validate_build_subdir(self, build_subdir: str, target: str): + def validate_build_subdir(self, build_subdir: str, target: str) -> None: if build_subdir and build_subdir != '.': if os.path.exists(os.path.join(self.source_root, self.subdir, build_subdir)): raise InvalidArguments(f'Build subdir "{build_subdir}" in "{target}" exists in source tree.') @@ -2666,7 +2667,7 @@ def validate_build_subdir(self, build_subdir: str, target: str): KwargInfo('build_subdir', str, default='', since='1.10.0'), ) def func_configure_file(self, node: mparser.BaseNode, args: T.List[TYPE_var], - kwargs: kwtypes.ConfigureFile): + kwargs: kwtypes.ConfigureFile) -> build.File: actions = sorted(x for x in ['configuration', 'command', 'copy'] if kwargs[x] not in [None, False]) num_actions = len(actions) @@ -3133,7 +3134,7 @@ def check_clang_asan_lundef(self) -> None: # object is generated. The result can be used in a different # subproject than it is defined in (due to e.g. a # declare_dependency). - def validate_within_subproject(self, subdir, fname): + def validate_within_subproject(self, subdir: str, fname: str) -> None: srcdir = self.environment.source_dir builddir = self.environment.build_dir if isinstance(fname, P_OBJ.DependencyVariableString): @@ -3363,7 +3364,7 @@ def build_both_libraries(self, node: mparser.BaseNode, args: T.Tuple[str, Source return build.BothLibraries(shared_lib, static_lib, preferred_library) - def build_library(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargsType], kwargs: kwtypes.Library): + def build_library(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargsType], kwargs: kwtypes.Library) -> build.SharedLibrary | build.BothLibraries | build.StaticLibrary: default_library = self.coredata.optstore.get_value_for(OptionKey('default_library', subproject=self.subproject)) assert isinstance(default_library, str), 'for mypy' if default_library == 'shared': @@ -3827,7 +3828,7 @@ def build_target(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargs self.project_args_frozen = True return target - def add_stdlib_info(self, target): + def add_stdlib_info(self, target: build.BuildTarget) -> None: for l in target.compilers.keys(): dep = self.build.stdlibs[target.for_machine].get(l, None) if dep: @@ -3904,7 +3905,8 @@ def func_is_disabler(self, node: mparser.BaseNode, args: T.Tuple[object], kwargs @noKwargs @FeatureNew('range', '0.58.0') @typed_pos_args('range', int, optargs=[int, int]) - def func_range(self, node, args: T.Tuple[int, T.Optional[int], T.Optional[int]], kwargs: T.Dict[str, T.Any]) -> P_OBJ.RangeHolder: + def func_range(self, node: mparser.BaseNode, args: T.Tuple[int, T.Optional[int], T.Optional[int]], + kwargs: TYPE_kwargs) -> P_OBJ.RangeHolder: start, stop, step = args # Just like Python's range, we allow range(stop), range(start, stop), or # range(start, stop, step) diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index 0ae9d79e9d1a..e716cafc172d 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -154,6 +154,12 @@ class FuncInstallData(TypedDict): follow_symlinks: T.Optional[bool] +class FuncInstallEmptyDir(TypedDict): + + install_mode: FileMode + install_tag: T.Optional[str] + + class FuncInstallHeaders(TypedDict): install_dir: T.Optional[str] @@ -170,6 +176,13 @@ class FuncInstallMan(TypedDict): locale: T.Optional[str] +class FuncInstallSymlink(TypedDict): + + install_dir: T.Optional[str] + install_tag: T.Optional[str] + pointing_to: str + + class FuncImportModule(ExtractRequired): disabler: bool From 21f82726be926ff5671c0d5ffb750484f1ca7e64 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 11 May 2026 09:38:45 -0700 Subject: [PATCH 264/681] interpreter: fix import source for cli arguments types --- mesonbuild/interpreter/interpreter.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 02c890067809..3912ef8c76e6 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -121,6 +121,7 @@ from ..interpreterbase.baseobjects import InterpreterObject, TYPE_var, TYPE_kwargs from ..options import OptionDict from ..mesonlib import SubProject + from ..cmdline import SharedCMDOptions from .type_checking import SourcesVarargsType # Input source types passed to Targets @@ -245,7 +246,7 @@ def __init__( invoker_method_default_options: T.Optional[OptionDict] = None, ast: T.Optional[mparser.CodeBlockNode] = None, relaxations: T.Optional[T.Set[InterpreterRuleRelaxation]] = None, - user_defined_options: T.Optional[coredata.SharedCMDOptions] = None, + user_defined_options: T.Optional[SharedCMDOptions] = None, cargo: T.Optional[cargo.Interpreter] = None, ) -> None: super().__init__(_build.environment.get_source_dir(), subdir, subproject, subproject_dir, _build.environment) From fc7eabdb3abc5bbe1301c26ced6e3c410587222b Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 11 May 2026 09:39:18 -0700 Subject: [PATCH 265/681] interpreter: pass summary_impl arguments as keywords not as dict The TypedDict doesn't work because sections is required, and it needs to be required. There's also zero cases where we don't pass these arguments, so just pass them explicitly --- mesonbuild/interpreter/interpreter.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 3912ef8c76e6..62dd7532ca9a 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1361,13 +1361,13 @@ def func_summary(self, node: mparser.BaseNode, args: T.Tuple[T.Union[str, T.Dict if not isinstance(args[0], str): raise InterpreterException('Summary first argument must be string.') values = {args[0]: args[1]} - self.summary_impl(kwargs['section'], values, kwargs) + self.summary_impl(values=values, **kwargs) - def summary_impl(self, section: str, values, kwargs: 'kwtypes.Summary') -> None: + def summary_impl(self, section: str, values: dict[str, T.Any], bool_yn: bool, list_sep: str | None) -> None: if self.subproject not in self.summary: self.summary[self.subproject] = Summary(self.active_projectname, self.project_version) self.summary[self.subproject].add_section( - section, values, kwargs['bool_yn'], kwargs['list_sep'], self.subproject) + section, values, bool_yn, list_sep, self.subproject) def _print_summary(self) -> None: # Add automatic 'Subprojects' section in main project. @@ -1385,10 +1385,7 @@ def _print_summary(self) -> None: value += [f'(from {stack})'] all_subprojects[name] = value if all_subprojects: - self.summary_impl('Subprojects', all_subprojects, - {'bool_yn': True, - 'list_sep': ' ', - }) + self.summary_impl('Subprojects', all_subprojects, bool_yn=True, list_sep=' ') # Add automatic section with all user defined options if self.user_defined_options: values = collections.OrderedDict() @@ -1406,7 +1403,7 @@ def compatibility_sort_helper(s): sorted_options = sorted(self.user_defined_options.cmd_line_options.items(), key=compatibility_sort_helper) values.update({str(k): v for k, v in sorted_options}) if values: - self.summary_impl('User defined options', values, {'bool_yn': False, 'list_sep': None}) + self.summary_impl('User defined options', values, bool_yn=False, list_sep=None) # Print all summaries, main project last. mlog.log('') # newline main_summary = self.summary.pop('', None) From a061c63391ca8952a3dd98eebb969ab97a0cbcfb Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 11 May 2026 09:40:07 -0700 Subject: [PATCH 266/681] interpreter: fix return annotations for func_install_headers --- mesonbuild/interpreter/interpreter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 62dd7532ca9a..1b15a50f8f34 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -2333,7 +2333,7 @@ def add_test(self, node: mparser.BaseNode, ) def func_install_headers(self, node: mparser.BaseNode, args: T.Tuple[T.List['mesonlib.FileOrString']], - kwargs: 'kwtypes.FuncInstallHeaders') -> build.Headers: + kwargs: 'kwtypes.FuncInstallHeaders') -> list[build.Headers]: install_mode = self._warn_kwarg_install_mode_sticky(kwargs['install_mode']) source_files = self.source_strings_to_files(args[0]) install_subdir = kwargs['subdir'] @@ -2346,7 +2346,7 @@ def func_install_headers(self, node: mparser.BaseNode, install_subdir = '' dirs = collections.defaultdict(list) - ret_headers = [] + ret_headers: list[build.Headers] = [] if kwargs['preserve_path']: for file in source_files: dirname = os.path.dirname(file.fname) From 1af6907d73322fd8ea8ffe2f57251c1486e5ec75 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 11 May 2026 09:54:30 -0700 Subject: [PATCH 267/681] interpreter: Add missing TypedDict values --- mesonbuild/interpreter/interpreter.py | 4 ++-- mesonbuild/interpreter/kwargs.py | 20 ++++++++++++-------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 1b15a50f8f34..5c97bda9a950 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -2251,7 +2251,7 @@ def unpack_env_kwarg(self, kwargs: T.Union[EnvironmentVariables, T.Dict[str, 'TY def make_test(self, node: mparser.BaseNode, args: T.Tuple[str, T.Union[build.Executable, build.Jar, Program, mesonlib.File, build.CustomTarget, build.CustomTargetIndex]], - kwargs: 'kwtypes.BaseTest', + kwargs: kwtypes.FuncTest | kwtypes.FuncBenchmark, klass: T.Type[TestClass] = Test) -> TestClass: name = args[0] if ':' in name: @@ -2307,7 +2307,7 @@ def make_test(self, node: mparser.BaseNode, def add_test(self, node: mparser.BaseNode, args: T.Tuple[str, T.Union[build.Executable, build.Jar, ExternalProgram, mesonlib.File, build.CustomTarget, build.CustomTargetIndex]], - kwargs: T.Dict[str, T.Any], is_base_test: bool) -> None: + kwargs: kwtypes.FuncTest | kwtypes.FuncBenchmark, is_base_test: bool) -> None: if isinstance(args[1], (build.CustomTarget, build.CustomTargetIndex)): FeatureNew.single_use('test with CustomTarget as command', '1.4.0', self.subproject) if any(isinstance(i, ExternalProgram) for i in kwargs['args']): diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index e716cafc172d..54731b738025 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -56,27 +56,24 @@ class BaseTest(TypedDict): expected_exitcode: T.Optional[int] timeout: int workdir: T.Optional[str] - depends: T.List[TargetDepends] priority: int env: EnvironmentVariables + depends: T.List[TargetDepends] suite: T.List[str] + verbose: bool class FuncBenchmark(BaseTest): - """Keyword Arguments shared between `test` and `benchmark`.""" + """Keyword arguments used by `benchmark` and `test`, but not Rust""" + # Needs complex refactor for rust test to go in BaseTest args: T.List[TestArgs] protocol: Literal['exitcode', 'tap', 'gtest', 'rust'] - class FuncTest(FuncBenchmark): - """Keyword Arguments for `test` - - `test` only adds the `is_parallel` argument over benchmark, so inheritance - is helpful here. - """ + """Keyword Arguments for `test` but not `benchmark`""" is_parallel: bool @@ -138,6 +135,7 @@ class BuildTargeMethodExtractAllObjects(TypedDict): class FuncInstallSubdir(TypedDict): install_dir: str + install_tag: T.Optional[str] strip_directory: bool exclude_files: T.List[str] exclude_directories: T.List[str] @@ -151,7 +149,9 @@ class FuncInstallData(TypedDict): sources: T.List[FileOrString] rename: T.List[str] install_mode: FileMode + install_tag: T.Optional[str] follow_symlinks: T.Optional[bool] + preserve_path: bool class FuncInstallEmptyDir(TypedDict): @@ -167,12 +167,14 @@ class FuncInstallHeaders(TypedDict): subdir: T.Optional[str] follow_symlinks: T.Optional[bool] install_tag: T.Optional[str] + preserve_path: bool class FuncInstallMan(TypedDict): install_dir: T.Optional[str] install_mode: FileMode + install_tag: T.Optional[str] locale: T.Optional[str] @@ -271,6 +273,7 @@ class FindProgram(ExtractRequired, ExtractSearchDirs): default_options: T.Dict[OptionKey, options.ElementaryOptionValues] native: MachineChoice version: T.List[str] + version_argument: str class RunCommand(TypedDict): @@ -338,6 +341,7 @@ class ConfigureFile(TypedDict): configuration: T.Optional[T.Union[T.Dict[str, T.Union[str, int, bool]], build.ConfigurationData]] macro_name: T.Optional[str] build_subdir: str + copy: bool class Subproject(ExtractRequired): From 6c40c6ca5e7660cfabb7b842e70a4f2271d222de Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 11 May 2026 09:58:29 -0700 Subject: [PATCH 268/681] interpreter: delete unpack_env_kwargs It doesn't do anything useful anymore --- mesonbuild/interpreter/interpreter.py | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 5c97bda9a950..bb574842cd30 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -2240,15 +2240,6 @@ def func_test(self, node: mparser.BaseNode, kwargs: 'kwtypes.FuncTest') -> None: self.add_test(node, args, kwargs, True) - def unpack_env_kwarg(self, kwargs: T.Union[EnvironmentVariables, T.Dict[str, 'TYPE_var'], T.List['TYPE_var'], str]) -> EnvironmentVariables: - envlist = kwargs.get('env') - if envlist is None: - return EnvironmentVariables() - msg = ENV_KW.validator(envlist) - if msg: - raise InvalidArguments(f'"env": {msg}') - return ENV_KW.convertor(envlist) - def make_test(self, node: mparser.BaseNode, args: T.Tuple[str, T.Union[build.Executable, build.Jar, Program, mesonlib.File, build.CustomTarget, build.CustomTargetIndex]], kwargs: kwtypes.FuncTest | kwtypes.FuncBenchmark, @@ -2268,8 +2259,6 @@ def make_test(self, node: mparser.BaseNode, elif isinstance(exe, build.CustomTargetIndex): kwargs.setdefault('depends', []).append(exe.target) - env = self.unpack_env_kwarg(kwargs) - if kwargs['timeout'] <= 0: FeatureNew.single_use('test() timeout <= 0', '0.57.0', self.subproject, location=node) @@ -2296,7 +2285,7 @@ def make_test(self, node: mparser.BaseNode, kwargs['depends'], kwargs.get('is_parallel', False), kwargs['args'], - env, + kwargs['env'], expected_fail, kwargs['expected_exitcode'], kwargs['timeout'], From f8e94d5d45d20bb94d0ac2913ea8367cac66f4f2 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 11 May 2026 10:12:27 -0700 Subject: [PATCH 269/681] interpreter: fix return type of func_install_data --- mesonbuild/interpreter/interpreter.py | 4 ++-- mesonbuild/modules/python.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index bb574842cd30..3450a67aa8d0 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -2544,7 +2544,7 @@ def install_data_impl(self, sources: T.List[mesonlib.File], install_dir: str, tag: T.Optional[str], install_data_type: T.Optional[str] = None, preserve_path: bool = False, - follow_symlinks: T.Optional[bool] = None) -> build.Data: + follow_symlinks: T.Optional[bool] = None) -> list[build.Data]: install_dir_name = install_dir.optname if isinstance(install_dir, P_OBJ.OptionString) else install_dir dirs = collections.defaultdict(list) if preserve_path: @@ -2554,7 +2554,7 @@ def install_data_impl(self, sources: T.List[mesonlib.File], install_dir: str, else: dirs[''].extend(sources) - ret_data = [] + ret_data: list[build.Data] = [] for childdir, files in dirs.items(): d = build.Data(files, os.path.join(install_dir, childdir), os.path.join(install_dir_name, childdir), install_mode, self.subproject, rename, tag, install_data_type, diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py index a4861688a1b0..59388686a2bc 100644 --- a/mesonbuild/modules/python.py +++ b/mesonbuild/modules/python.py @@ -318,7 +318,7 @@ def dependency_method(self, args: T.List['TYPE_var'], kwargs: FuncDependency) -> ) @InterpreterObject.method('install_sources') def install_sources_method(self, args: T.Tuple[T.List[T.Union[str, mesonlib.File]]], - kwargs: 'PyInstallKw') -> 'Data': + kwargs: 'PyInstallKw') -> list[Data]: self.held_object.run_bytecompile[self.version] = True tag = kwargs['install_tag'] or 'python-runtime' pure = kwargs['pure'] if kwargs['pure'] is not None else self.pure From 0b3c1f940139700ef03b2c0de0a6c4189134665a Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 11 May 2026 10:15:09 -0700 Subject: [PATCH 270/681] interpreter: fix annotation of _add_project_arguments Which had too many levels of dictionaries --- mesonbuild/interpreter/interpreter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 3450a67aa8d0..ce601083c350 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -3033,7 +3033,7 @@ def _add_global_arguments(self, node: mparser.FunctionNode, argsdict: T.Dict[str frozen = self.project_args_frozen or self.global_args_frozen self._add_arguments(node, argsdict, frozen, args, kwargs) - def _add_project_arguments(self, node: mparser.FunctionNode, argsdict: T.Dict[str, T.Dict[str, T.List[str]]], + def _add_project_arguments(self, node: mparser.FunctionNode, argsdict: T.Dict[str, T.List[str]], args: T.List[str], kwargs: 'kwtypes.FuncAddProjectArgs') -> None: self._add_arguments(node, argsdict, self.project_args_frozen, args, kwargs) From 71be21cb957c42bd7e4203db8d0733aa7cae89d2 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 11 May 2026 10:21:37 -0700 Subject: [PATCH 271/681] interpreter: help mypy understand what's happening in `build_target` This function is still wildly too complex, and should be split more, but this gets us to the point that mypy doesn't throw a billion warnings --- mesonbuild/interpreter/interpreter.py | 40 ++++++++++++++++++--------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index ce601083c350..2683dbace130 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -3759,7 +3759,7 @@ def build_target(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargs s for s in raw_sources if not isinstance(s, (build.BuildTarget, build.ExtractedObjects))]) objs = kwargs['objects'] - srcs: T.List['SourceInputs'] = [] + srcs: T.List[SourceOutputs] = [] struct: T.Optional[build.StructuredSources] = build.StructuredSources() for s in sources: if isinstance(s, build.StructuredSources): @@ -3790,26 +3790,40 @@ def build_target(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargs node=node) outputs.update(o) + if targetclass is not build.Jar: + self.check_for_jar_sources(sources, targetclass) + + target: build.BuildTarget if targetclass is build.Executable: - nkwargs = self.__convert_executable_kwargs(node, kwargs) + nkwargs = self.__convert_executable_kwargs( + node, T.cast('kwtypes.Executable', kwargs)) + target = build.Executable(name, self.subdir, self.subproject, for_machine, srcs, struct, objs, + self.environment, self.compilers[for_machine], nkwargs) elif targetclass is build.StaticLibrary: - nkwargs = self.__convert_static_library_kwargs(node, kwargs) + nkwargs = self.__convert_static_library_kwargs( + node, T.cast('kwtypes.StaticLibrary', kwargs)) + target = build.StaticLibrary(name, self.subdir, self.subproject, for_machine, srcs, struct, objs, + self.environment, self.compilers[for_machine], nkwargs) elif targetclass is build.SharedLibrary: - nkwargs = self.__convert_shared_library_kwargs(node, kwargs) + nkwargs = self.__convert_shared_library_kwargs( + node, T.cast('kwtypes.SharedLibrary', kwargs)) + target = build.SharedLibrary(name, self.subdir, self.subproject, for_machine, srcs, struct, objs, + self.environment, self.compilers[for_machine], nkwargs) + target.shared_library_only = shared_library_only elif targetclass is build.SharedModule: - nkwargs = self.__convert_shared_module_kwargs(node, kwargs) + nkwargs = self.__convert_shared_module_kwargs( + node, T.cast('kwtypes.SharedModule', kwargs)) + target = build.SharedModule(name, self.subdir, self.subproject, for_machine, srcs, struct, objs, + self.environment, self.compilers[for_machine], nkwargs) + target.shared_library_only = shared_library_only else: - nkwargs = self.__convert_jar_kwargs(node, kwargs) + nkwargs = self.__convert_jar_kwargs( + node, T.cast('kwtypes.Jar', kwargs)) + target = build.Jar(name, self.subdir, self.subproject, for_machine, srcs, struct, objs, + self.environment, self.compilers[for_machine], nkwargs) - if targetclass is not build.Jar: - self.check_for_jar_sources(sources, targetclass) - - target = targetclass(name, self.subdir, self.subproject, for_machine, srcs, struct, objs, - 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) - if targetclass is build.SharedLibrary: - target.shared_library_only = shared_library_only self.add_target(name, target) self.project_args_frozen = True From 91e106eb4c6ec60464f60bd3367f02db9885679b Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 11 May 2026 10:23:22 -0700 Subject: [PATCH 272/681] interpreter: ignore a bunch of typedict literal-required warnings That frankly mypy should be able to figure out, but can't. --- mesonbuild/interpreter/interpreter.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 2683dbace130..17331c562418 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -3504,7 +3504,7 @@ def __convert_build_target_base_kwargs(self, kwargs: kwtypes.BuildTarget, final: # Into split arguments vala_keys = ('install_vala_header', 'install_vala_vapi', 'install_vala_gir') if len(kwargs['install_dir']) > 1: - if any(kwargs[k] is not None for k in vala_keys): + if any(kwargs[k] is not None for k in vala_keys): # type: ignore[literal-required] raise InvalidArguments('Passing install_vala_* and more than one argument to install_dir are mutually exclusive') install_dirs = kwargs['install_dir'].copy() @@ -3513,10 +3513,10 @@ def __convert_build_target_base_kwargs(self, kwargs: kwtypes.BuildTarget, final: for key in vala_keys: action = install_dirs.pop(0) if install_dirs else False if isinstance(action, bool): - final[key] = action + final[key] = action # type: ignore[literal-required] else: - final[key] = True - final[f'{key}_dir'] = action + final[key] = True # type: ignore[literal-required] + final[f'{key}_dir'] = action # type: ignore[literal-required] final['install_dir'] = [install_dir] # This was previously allowed, and tested, so we need to allow it to keep working. @@ -3524,14 +3524,14 @@ def __convert_build_target_base_kwargs(self, kwargs: kwtypes.BuildTarget, final: final['install_vala_gir'] = False else: for key in vala_keys: - action = kwargs[key] + action = kwargs[key] # type: ignore[literal-required] if action is None: action = False if isinstance(action, bool): - final[key] = action + final[key] = action # type: ignore[literal-required] else: - final[key] = True - final[f'{key}_dir'] = action + final[key] = True # type: ignore[literal-required] + final[f'{key}_dir'] = action # type: ignore[literal-required] final['install_dir'] = [kwargs['install_dir'][0] if kwargs['install_dir'] else True] def __convert_executable_kwargs(self, node: mparser.BaseNode, kwargs: kwtypes.Executable) -> build.ExecutableKeywordArguments: From d8af51917cbadc3c9bd53f42b72e9486e3e9d6b0 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 11 May 2026 10:24:29 -0700 Subject: [PATCH 273/681] interpreter: fix depend_files type from func_custom_target --- mesonbuild/interpreter/interpreter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 17331c562418..e145b3541281 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -2135,7 +2135,7 @@ def func_custom_target(self, node: mparser.FunctionNode, args: T.Tuple[str], build_by_default=build_by_default, capture=kwargs['capture'], console=kwargs['console'], - depend_files=kwargs['depend_files'], + depend_files=self.source_strings_to_files(kwargs['depend_files']), depfile=kwargs['depfile'], extra_depends=kwargs['depends'], env=kwargs['env'], From 5c4de933dd0fbd69e1d1755da52b1d01cab2aece Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 18 May 2026 13:07:51 -0700 Subject: [PATCH 274/681] Add `encoding` to Path().write_text() calls To help with more Visual Studio backend failures. --- test cases/common/182 find override/subdir/converter.py | 2 +- test cases/common/182 find override/subdir/gencodegen.py.in | 2 +- test cases/common/208 link custom/custom_stlib.py | 2 +- .../generate_conflicting_stlibs.py | 2 +- .../generate_stlibs.py | 2 +- test cases/common/227 very long command line/codegen.py | 2 +- test cases/common/262 generator chain/stage1.py | 2 +- test cases/common/262 generator chain/stage2.py | 2 +- test cases/unit/132 custom target index test/meson.build | 4 ++-- test cases/unit/35 dist script/replacer.py | 2 +- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/test cases/common/182 find override/subdir/converter.py b/test cases/common/182 find override/subdir/converter.py index efe04649d942..690c723b4d34 100755 --- a/test cases/common/182 find override/subdir/converter.py +++ b/test cases/common/182 find override/subdir/converter.py @@ -12,4 +12,4 @@ d = pathlib.Path(ifilename).read_text().split('\n')[0].strip() -pathlib.Path(ofilename).write_text(ftempl % d) +pathlib.Path(ofilename).write_text(ftempl % d, encoding='utf-8') diff --git a/test cases/common/182 find override/subdir/gencodegen.py.in b/test cases/common/182 find override/subdir/gencodegen.py.in index 78ebb2f4e566..9bdb874b46d8 100755 --- a/test cases/common/182 find override/subdir/gencodegen.py.in +++ b/test cases/common/182 find override/subdir/gencodegen.py.in @@ -12,4 +12,4 @@ ftempl = '''int %s(void) { d = pathlib.Path(ifilename).read_text().split('\n')[0].strip() -pathlib.Path(ofilename).write_text(ftempl % d) +pathlib.Path(ofilename).write_text(ftempl % d, encoding='utf-8') diff --git a/test cases/common/208 link custom/custom_stlib.py b/test cases/common/208 link custom/custom_stlib.py index 6a090f3db92d..13cf5b3fdc99 100755 --- a/test cases/common/208 link custom/custom_stlib.py +++ b/test cases/common/208 link custom/custom_stlib.py @@ -70,7 +70,7 @@ def generate_lib(outfile, private_dir, compiler_array): if not private_dir.exists(): private_dir.mkdir() c_file = private_dir / 'flob.c' - c_file.write_text(contents) + c_file.write_text(contents, encoding='utf-8') for i in compiler_array: if (i.endswith('cl') or i.endswith('cl.exe')) and 'clang-cl' not in i: return generate_lib_msvc(outfile, c_file, private_dir, compiler_array) diff --git a/test cases/common/209 link custom_i single from multiple/generate_conflicting_stlibs.py b/test cases/common/209 link custom_i single from multiple/generate_conflicting_stlibs.py index 42d6631ddf2e..30bf60c8fff5 100644 --- a/test cases/common/209 link custom_i single from multiple/generate_conflicting_stlibs.py +++ b/test cases/common/209 link custom_i single from multiple/generate_conflicting_stlibs.py @@ -67,7 +67,7 @@ def generate_lib(outfiles, private_dir, compiler_array): for i, content in enumerate(contents): c_file = private_dir / ('flob_' + str(i + 1) + '.c') - c_file.write_text(content) + c_file.write_text(content, encoding='utf-8') outfile = outfiles[i] cl_found = False diff --git a/test cases/common/210 link custom_i multiple from multiple/generate_stlibs.py b/test cases/common/210 link custom_i multiple from multiple/generate_stlibs.py index 5292006c597d..ae612790ad30 100644 --- a/test cases/common/210 link custom_i multiple from multiple/generate_stlibs.py +++ b/test cases/common/210 link custom_i multiple from multiple/generate_stlibs.py @@ -69,7 +69,7 @@ def generate_lib(outfiles, private_dir, compiler_array): for i, content in enumerate(contents): c_file = private_dir / ('flob_' + str(i + 1) + '.c') - c_file.write_text(content) + c_file.write_text(content, encoding='utf-8') outfile = outfiles[i] cl_found = False diff --git a/test cases/common/227 very long command line/codegen.py b/test cases/common/227 very long command line/codegen.py index b1de6073e756..54a0cbe66c60 100755 --- a/test cases/common/227 very long command line/codegen.py +++ b/test cases/common/227 very long command line/codegen.py @@ -4,4 +4,4 @@ from pathlib import Path Path(sys.argv[2]).write_text( - 'int func{n}(void) {{ return {n}; }}'.format(n=sys.argv[1])) + 'int func{n}(void) {{ return {n}; }}'.format(n=sys.argv[1]), encoding='utf-8') diff --git a/test cases/common/262 generator chain/stage1.py b/test cases/common/262 generator chain/stage1.py index 73e02fccf7fd..d15e0d055545 100644 --- a/test cases/common/262 generator chain/stage1.py +++ b/test cases/common/262 generator chain/stage1.py @@ -3,4 +3,4 @@ from pathlib import Path assert(Path(sys.argv[1]).read_text() == 'stage1\n') -Path(sys.argv[2]).write_text('stage2\n') +Path(sys.argv[2]).write_text('stage2\n', encoding='utf-8') diff --git a/test cases/common/262 generator chain/stage2.py b/test cases/common/262 generator chain/stage2.py index 7f82592a1efe..44517762f601 100644 --- a/test cases/common/262 generator chain/stage2.py +++ b/test cases/common/262 generator chain/stage2.py @@ -3,4 +3,4 @@ from pathlib import Path assert(Path(sys.argv[1]).read_text() == 'stage2\n') -Path(sys.argv[2]).write_text('int main(void){}\n') +Path(sys.argv[2]).write_text('int main(void){}\n', encoding='utf-8') diff --git a/test cases/unit/132 custom target index test/meson.build b/test cases/unit/132 custom target index test/meson.build index adbbb4df46b4..a00d1d420fd6 100644 --- a/test cases/unit/132 custom target index test/meson.build +++ b/test cases/unit/132 custom target index test/meson.build @@ -4,8 +4,8 @@ python3 = find_program('python3') gen_code = ''' import sys, pathlib -pathlib.Path(sys.argv[1]).write_text("foo") -pathlib.Path(sys.argv[2]).write_text("bar")''' +pathlib.Path(sys.argv[1]).write_text("foo", encoding='utf-8') +pathlib.Path(sys.argv[2]).write_text("bar", encoding='utf-8')''' foobar_txt = custom_target(command: [python3, '-c', gen_code, '@OUTPUT@'], output: ['foo.txt', 'bar.txt']) test_code = ''' diff --git a/test cases/unit/35 dist script/replacer.py b/test cases/unit/35 dist script/replacer.py index 96ccdcc8bf2c..4b0c30570358 100755 --- a/test cases/unit/35 dist script/replacer.py +++ b/test cases/unit/35 dist script/replacer.py @@ -13,4 +13,4 @@ contents = modfile.read_text() contents = contents.replace(sys.argv[1], sys.argv[2]) -modfile.write_text(contents) +modfile.write_text(contents, encoding='utf-8') From ebeccc8793141c71119da64add2ab32c0a96780e Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 18 May 2026 10:59:23 -0700 Subject: [PATCH 275/681] pathlib: Update the typing to reflect Python 3.12 changes In python 3.12 `pathlib.Path` became directly subclassable. This makes our tricks with `type()` return something dynamic and incorrect, while before 3.12 `Path` is a factory, and using `type()` is correct. It also means we need to do a little trickery to get the types of `Path` the same on both paths. --- mesonbuild/_pathlib.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/mesonbuild/_pathlib.py b/mesonbuild/_pathlib.py index b0ae2e7f3171..ce8db37615ce 100644 --- a/mesonbuild/_pathlib.py +++ b/mesonbuild/_pathlib.py @@ -23,6 +23,7 @@ import pathlib import os import platform +import sys __all__ = [ 'PurePath', @@ -39,8 +40,14 @@ if platform.system().lower() in {'windows'}: # Can not directly inherit from pathlib.Path because the __new__ # operator of pathlib.Path() returns a {Posix,Windows}Path object. - class Path(type(pathlib.Path())): - def resolve(self, strict: bool = False) -> 'Path': + # Until Python 3.12, when this was changed so that Path can be directly inherited. + if sys.version_info >= (3, 12): + _BASE_CLASS = pathlib.Path + else: + _BASE_CLASS = type(pathlib.Path()) + + class _Path(_BASE_CLASS): + def resolve(self, strict: bool = False) -> _Path: ''' Work around a resolve bug on certain Windows systems: @@ -51,7 +58,9 @@ def resolve(self, strict: bool = False) -> 'Path': try: return super().resolve(strict=strict) except OSError: - return Path(os.path.normpath(self)) + return _Path(os.path.normpath(self)) + + Path: type[_BASE_CLASS] = _Path else: Path = pathlib.Path PosixPath = pathlib.PosixPath From 7d4c05a6ee53fb6970edf461bc221184748bc1f5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 16 May 2026 09:14:28 +0200 Subject: [PATCH 276/681] linkers: apple: do not pass -object_path_lto to dyld Until now, Meson did not really need to check specifically whether the linker was Apple ld64 or dyld, because the version numbers are more or less compatible. However, -object_path_lto is not supported by dyld at all. To account for that, save whether ld64 or dyld are in use, and use it to remove the option for dyld. Signed-off-by: Paolo Bonzini --- mesonbuild/linkers/detect.py | 4 +++- mesonbuild/linkers/linkers.py | 9 +++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/mesonbuild/linkers/detect.py b/mesonbuild/linkers/detect.py index c19f66cb2fe0..10ccda159b93 100644 --- a/mesonbuild/linkers/detect.py +++ b/mesonbuild/linkers/detect.py @@ -228,15 +228,17 @@ def guess_nix_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty cmd = compiler + comp_class.LINKER_OPTION_STYLE.wrap(['-v']) + extra_args _, newo, newerr = Popen_safe_logged(cmd, msg='Detecting Apple linker via') + is_dyld: bool for line in newerr.split('\n'): if 'PROJECT:ld' in line or 'PROJECT:dyld' in line: v = line.split('-')[1] + is_dyld = 'PROJECT:dyld' in line break else: __failed_to_detect_linker(compiler, check_args, o, e) linker = linkers.AppleDynamicLinker( compiler, env, for_machine, comp_class.LINKER_OPTION_STYLE, override, - system=system, version=v + system=system, version=v, is_dyld=is_dyld ) elif 'ld.exe: unrecognized option' in e or 'ld: unrecognized option' in e: linker = linkers.OS2AoutDynamicLinker( diff --git a/mesonbuild/linkers/linkers.py b/mesonbuild/linkers/linkers.py index f12475dbaf8a..fd90cddeb964 100644 --- a/mesonbuild/linkers/linkers.py +++ b/mesonbuild/linkers/linkers.py @@ -853,6 +853,13 @@ class AppleDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker): id = 'ld64' + def __init__(self, exelist: T.List[str], env: Environment, + for_machine: mesonlib.MachineChoice, prefix_arg: T.Optional[LinkerOptionStyle], + always_args: T.List[str], *, system: str, + version: str, is_dyld: bool = False): + super().__init__(exelist, env, for_machine, prefix_arg, always_args, system=system, version=version) + self.is_dyld = is_dyld + def get_asneeded_args(self) -> T.List[str]: return self._apply_prefix('-dead_strip_dylibs') @@ -959,6 +966,8 @@ def get_thinlto_cache_args(self, path: str) -> T.List[str]: def get_lto_obj_cache_path(self, path: str) -> T.List[str]: # https://clang.llvm.org/docs/CommandGuide/clang.html#cmdoption-flto + if self.is_dyld: + return [] return ["-Wl,-object_path_lto," + path] def export_dynamic_args(self) -> T.List[str]: From 75ddbed344a20dcc2b83e76cf448e7d259ae706b Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Tue, 19 May 2026 00:33:15 -0400 Subject: [PATCH 277/681] refman: spelling: FetureCheck -> FeatureCheck --- docs/refman/loaderbase.py | 4 ++-- docs/refman/model.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/refman/loaderbase.py b/docs/refman/loaderbase.py index 39c92bf09ce4..d465c03bdf2c 100644 --- a/docs/refman/loaderbase.py +++ b/docs/refman/loaderbase.py @@ -8,7 +8,7 @@ from .model import ( NamedObject, - FetureCheck, + FeatureCheck, ArgBase, PosArg, DataTypeInfo, @@ -36,7 +36,7 @@ def _validate_named_object(self, obj: NamedObject) -> None: assert obj.name.islower(), f'Object names must be lower case ({obj.name})' assert name_regex.match(obj.name) or obj.name == '[index]', f'Invalid name {obj.name}' - def _validate_feature_check(self, obj: FetureCheck) -> None: + def _validate_feature_check(self, obj: FeatureCheck) -> None: meson_version_reg = re.compile(r'[0-9]+\.[0-9]+\.[0-9]+') obj.since = obj.since.strip() obj.deprecated = obj.deprecated.strip() diff --git a/docs/refman/model.py b/docs/refman/model.py index 1360d895914f..dbbfde17e60e 100644 --- a/docs/refman/model.py +++ b/docs/refman/model.py @@ -16,7 +16,7 @@ def hidden(self) -> bool: return self.name.startswith('_') @dataclass -class FetureCheck: +class FeatureCheck: since: str deprecated: str @@ -33,7 +33,7 @@ class Type: # Arguments @dataclass -class ArgBase(NamedObject, FetureCheck): +class ArgBase(NamedObject, FeatureCheck): type: Type @dataclass @@ -53,7 +53,7 @@ class Kwarg(ArgBase): # Function @dataclass -class Function(NamedObject, FetureCheck): +class Function(NamedObject, FeatureCheck): notes: T.List[str] warnings: T.List[str] returns: Type @@ -82,7 +82,7 @@ class ObjectType(Enum): RETURNED = 4 @dataclass -class Object(NamedObject, FetureCheck): +class Object(NamedObject, FeatureCheck): notes: T.List[str] warnings: T.List[str] long_name: str From cf0c0cfbe361e8e728672805f71d9be0fd526954 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Tue, 19 May 2026 04:04:48 -0400 Subject: [PATCH 278/681] refman: Also fix 'feture' spelling in loaderyaml --- docs/refman/loaderyaml.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/refman/loaderyaml.py b/docs/refman/loaderyaml.py index 15ba5909dcfd..ed6e7d837be6 100644 --- a/docs/refman/loaderyaml.py +++ b/docs/refman/loaderyaml.py @@ -37,27 +37,27 @@ def __init__(self) -> None: 'description': Str(), } - d_feture_check = { + d_feature_check = { Optional('since', default=''): Str(), Optional('deprecated', default=''): Str(), } self.s_posarg = Map({ - **d_feture_check, + **d_feature_check, 'description': Str(), 'type': Str(), Optional('default', default=''): Str(), }) self.s_varargs = Map({ - **d_named_object, **d_feture_check, + **d_named_object, **d_feature_check, 'type': Str(), Optional('min_varargs', default=-1): Int(), Optional('max_varargs', default=-1): Int(), }) self.s_kwarg = Map({ - **d_feture_check, + **d_feature_check, 'type': Str(), 'description': Str(), Optional('required', default=False): Bool(), @@ -65,7 +65,7 @@ def __init__(self) -> None: }) self.s_function = Map({ - **d_named_object, **d_feture_check, + **d_named_object, **d_feature_check, 'returns': Str(), Optional('notes', default=[]): OrValidator(Seq(Str()), EmptyList()), Optional('warnings', default=[]): OrValidator(Seq(Str()), EmptyList()), @@ -82,7 +82,7 @@ def __init__(self) -> None: }) self.s_object = Map({ - **d_named_object, **d_feture_check, + **d_named_object, **d_feature_check, 'long_name': Str(), Optional('extends', default=''): Str(), Optional('notes', default=[]): OrValidator(Seq(Str()), EmptyList()), From dbc0af353e9f53ae83997d0b8d8c9b5dca8b78db Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 19 May 2026 20:53:07 +0200 Subject: [PATCH 279/681] backends: accept CustomTargetIndex for build_target_link_arguments Signed-off-by: Paolo Bonzini --- mesonbuild/backend/backends.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 5fc3b8336be6..8651dd0932bd 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -1037,7 +1037,7 @@ def generate_basic_compiler_args(self, target: build.BuildTarget, compiler: 'Com commands += compiler.get_include_args(priv_dir, False) return commands - def build_target_link_arguments(self, compiler: 'Compiler', deps: T.List[build.Target]) -> T.List[str]: + def build_target_link_arguments(self, compiler: Compiler, deps: T.Iterable[build.BuildTargetTypes]) -> T.List[str]: args: T.List[str] = [] for d in deps: if not d.is_linkable_target(): From 775658b13ea7b7df1fb8f5ef4c35beff359e898f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 19 May 2026 21:06:52 +0200 Subject: [PATCH 280/681] backends, ninjabackend: be more lenient if CustomTarget/CustomTargetIndex is found Signed-off-by: Paolo Bonzini --- mesonbuild/backend/backends.py | 6 ++++-- mesonbuild/backend/ninjabackend.py | 6 +++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 8651dd0932bd..45f5159034c5 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -527,8 +527,10 @@ def _flatten_object_list(self, target: build.BuildTarget, return obj_list, deps @staticmethod - def is_swift_target(target: build.BuildTarget) -> bool: - for s in target.sources: + def is_swift_target(target: build.BuildTargetTypes) -> bool: + if not isinstance(target, build.BuildTarget): + return False + for s in target.get_sources(): if s.endswith('swift'): return True return False diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 054e15f7ebbc..871b9d339e25 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1142,9 +1142,11 @@ def generate_target(self, target: T.Union[build.BuildTarget, build.CustomTarget, elem = NinjaBuildElement(self.all_outputs, linker.get_archive_name(outname), 'AIX_LINKER', [outname]) self.add_build(elem) - def should_use_dyndeps_for_target(self, target: 'build.BuildTarget') -> bool: + def should_use_dyndeps_for_target(self, target: build.BuildTargetTypes) -> bool: if not self.ninja_has_dyndeps: return False + if not isinstance(target, build.BuildTarget): + return False if 'fortran' in target.compilers: return True if 'cpp' not in target.compilers: @@ -1207,6 +1209,7 @@ def generate_dependency_scan_target(self, target: build.BuildTarget, infiles: T.Set[str] = set() for t in target.get_all_linked_targets(): if self.should_use_dyndeps_for_target(t): + assert isinstance(t, build.BuildTarget) infiles.add(self.get_dep_scan_file_for(t)[0]) _, od = self.flatten_object_list(target) infiles.update({self.get_dep_scan_file_for(t)[0] for t in od if t.uses_fortran()}) @@ -2322,6 +2325,7 @@ def determine_swift_dep_modules(self, target: build.BuildTarget) -> T.List[str]: result = [] for l in target.link_targets: if self.is_swift_target(l): + assert isinstance(l, build.BuildTarget) # for mypy result.append(self.swift_module_file_name(l)) return result From b8eb0b9ebc86a8b5b00eeb29c23bc8b1ebc43e80 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 19 May 2026 21:08:03 +0200 Subject: [PATCH 281/681] backends: allow RunTarget for get_target_depend_files Signed-off-by: Paolo Bonzini --- mesonbuild/backend/backends.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 45f5159034c5..2e3b589956cd 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -1470,7 +1470,7 @@ def get_custom_target_sources(self, target: build.CustomTarget) -> T.List[str]: srcs += fname return srcs - def get_target_depend_files(self, target: T.Union[build.CustomTarget, build.BuildTarget, build.GeneratedList], absolute_paths: bool = False) -> T.List[str]: + def get_target_depend_files(self, target: T.Union[build.CustomTarget, build.BuildTarget, build.GeneratedList, build.RunTarget], absolute_paths: bool = False) -> T.List[str]: deps: T.List[str] = [] for i in target.depend_files: if isinstance(i, mesonlib.File): From 812cbfab0f99d2db899555cd9d9577040257dabb Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 19 May 2026 21:11:05 +0200 Subject: [PATCH 282/681] backends: allow any iterable for extra_bdeps NinjaBackend passes a set, which is not a sequence. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/backends.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 2e3b589956cd..46e79a33a633 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -544,7 +544,7 @@ def determine_swift_dep_dirs(self, target: build.BuildTarget) -> T.List[str]: def get_executable_serialisation( self, cmd: T.Sequence[T.Union[programs.Program, build.BuildTargetTypes, File, str]], workdir: T.Optional[str] = None, - extra_bdeps: T.Optional[T.Sequence[build.BuildTargetTypes]] = None, + extra_bdeps: T.Optional[T.Iterable[build.BuildTargetTypes]] = None, capture: T.Optional[str] = None, feed: T.Optional[str] = None, env: T.Optional[mesonlib.EnvironmentVariables] = None, @@ -634,7 +634,7 @@ def get_executable_serialisation( def as_meson_exe_cmdline(self, exe: T.Union[str, mesonlib.File, build.BuildTargetTypes, programs.Program], cmd_args: T.Sequence[T.Union[str, mesonlib.File, build.BuildTargetTypes, programs.Program]], workdir: T.Optional[str] = None, - extra_bdeps: T.Optional[T.Sequence[build.BuildTargetTypes]] = None, + extra_bdeps: T.Optional[T.Iterable[build.BuildTargetTypes]] = None, capture: T.Optional[str] = None, feed: T.Optional[str] = None, force_serialize: bool = False, @@ -1138,7 +1138,7 @@ def extract_dll_paths(cls, target: build.BuildTarget) -> T.Set[str]: def determine_windows_extra_paths( self, target: T.Union[build.BuildTargetTypes, programs.Program, mesonlib.File, str], - extra_bdeps: T.Sequence[build.BuildTargetTypes]) -> T.List[str]: + extra_bdeps: T.Iterable[build.BuildTargetTypes]) -> T.List[str]: """On Windows there is no such thing as an rpath. We must determine all locations of DLLs that this exe From f1cef6ce593adbb703c6375fe61453809fd82f7c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 19 May 2026 21:17:14 +0200 Subject: [PATCH 283/681] backends: allow RunTarget for eval_custom_target_command Signed-off-by: Paolo Bonzini --- mesonbuild/backend/backends.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 46e79a33a633..28a75adf00c7 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -1439,7 +1439,7 @@ def get_custom_target_provided_libraries(self, target: T.Union[build.BuildTarget libs.extend(self.get_custom_target_provided_by_generated_source(t)) return libs - def get_custom_target_sources(self, target: build.CustomTarget) -> T.List[str]: + def get_custom_target_sources(self, target: build.RunTarget | build.CustomTarget) -> T.List[str]: ''' Custom target sources can be of various object types; strings, File, BuildTarget, even other CustomTargets. @@ -1456,6 +1456,7 @@ def get_custom_target_sources(self, target: build.CustomTarget) -> T.List[str]: elif isinstance(i, (build.CustomTarget, build.CustomTargetIndex)): fname = [os.path.join(self.get_custom_target_output_dir(i), p) for p in i.get_outputs()] elif isinstance(i, build.GeneratedList): + assert not isinstance(target, build.RunTarget) fname = [os.path.join(self.get_target_private_dir(target), p) for p in i.get_outputs()] elif isinstance(i, build.ExtractedObjects): fname = self.determine_ext_objs(i) @@ -1527,7 +1528,7 @@ def get_custom_target_dir_include_args( return incs def eval_custom_target_command( - self, target: build.CustomTarget, absolute_outputs: bool = False) -> \ + self, target: build.RunTarget | build.CustomTarget, absolute_outputs: bool = False) -> \ T.Tuple[T.List[str], T.List[str], T.List[str | programs.Program]]: # We want the outputs to be absolute only when using the VS backend # XXX: Maybe allow the vs backend to use relative paths too? @@ -1565,6 +1566,7 @@ def eval_custom_target_command( if '@CURRENT_SOURCE_DIR@' in i: i = i.replace('@CURRENT_SOURCE_DIR@', os.path.join(source_root, target.get_subdir())) if '@DEPFILE@' in i: + assert not isinstance(target, build.RunTarget) if target.depfile is None: msg = f'Custom target {target.name!r} has @DEPFILE@ but no depfile ' \ 'keyword argument.' @@ -1572,6 +1574,7 @@ def eval_custom_target_command( dfilename = os.path.join(outdir, target.depfile) i = i.replace('@DEPFILE@', dfilename) if '@PRIVATE_DIR@' in i: + assert not isinstance(target, build.RunTarget) pdir = self.get_target_private_dir_abs(target) os.makedirs(pdir, exist_ok=True) if not target.absolute_paths: From 29e5c0b56669eedfd6eb91c71b562cca55d7d420 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 19 May 2026 21:25:25 +0200 Subject: [PATCH 284/681] backends: tighten return type of flatten_object_list --- mesonbuild/backend/backends.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 28a75adf00c7..c78a1d4cdbc6 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -483,7 +483,7 @@ def relpath(todir: str, fromdir: str) -> str: os.path.join('dummyprefixdir', fromdir)) def flatten_object_list(self, target: build.BuildTarget, proj_dir_to_build_root: str = '' - ) -> T.Tuple[T.List[str], T.List[build.BuildTargetTypes]]: + ) -> T.Tuple[T.List[str], T.List[build.BuildTarget]]: obj_list, deps = self._flatten_object_list(target, target.get_objects(), proj_dir_to_build_root) return unique_list(obj_list), deps @@ -493,9 +493,9 @@ def determine_ext_objs(self, objects: build.ExtractedObjects) -> T.List[str]: def _flatten_object_list(self, target: build.BuildTarget, objects: T.Sequence[build.ObjectTypes], - proj_dir_to_build_root: str) -> T.Tuple[T.List[str], T.List[build.BuildTargetTypes]]: + proj_dir_to_build_root: str) -> T.Tuple[T.List[str], T.List[build.BuildTarget]]: obj_list: T.List[str] = [] - deps: T.List[build.BuildTargetTypes] = [] + deps: T.List[build.BuildTarget] = [] for obj in objects: if isinstance(obj, str): o = os.path.join(proj_dir_to_build_root, From ef20eefef598d34cd5770ae0d123c53cb567362e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 19 May 2026 21:42:04 +0200 Subject: [PATCH 285/681] backends: allow CustomTarget for get_custom_target_dir_include_args Signed-off-by: Paolo Bonzini --- mesonbuild/backend/backends.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index c78a1d4cdbc6..0f44b91847cc 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -1501,7 +1501,7 @@ def get_custom_target_output_dir(self, target: build.AnyTargetType) -> str: def get_normpath_target(self, source: str) -> str: return os.path.normpath(source) - def get_custom_target_dirs(self, target: build.CustomTarget, compiler: 'Compiler', *, + def get_custom_target_dirs(self, target: build.BuildTarget | build.CustomTarget, compiler: 'Compiler', *, absolute_path: bool = False) -> T.List[str]: custom_target_include_dirs: T.List[str] = [] for i in target.get_generated_sources(): @@ -1520,7 +1520,7 @@ def get_custom_target_dirs(self, target: build.CustomTarget, compiler: 'Compiler return custom_target_include_dirs def get_custom_target_dir_include_args( - self, target: build.CustomTarget, compiler: 'Compiler', *, + self, target: build.BuildTarget | build.CustomTarget, compiler: 'Compiler', *, absolute_path: bool = False) -> T.List[str]: incs: T.List[str] = [] for i in self.get_custom_target_dirs(target, compiler, absolute_path=absolute_path): From d63dc94d5f72de09bacbc347d2c603752d97e143 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 19 May 2026 20:58:20 +0200 Subject: [PATCH 286/681] ninjabackend: fix typing errors in assignemnts Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 871b9d339e25..2cc91eadafed 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -3714,14 +3714,14 @@ def guess_external_link_dependencies(self, linker: Compiler, target: build.Build try: static_patterns = linker.get_library_naming(LibType.STATIC, strict=True) shared_patterns = linker.get_library_naming(LibType.SHARED, strict=True) - search_dirs = tuple(search_dirs) + tuple(linker.get_library_dirs()) + search_dirs_tuple = tuple(search_dirs) + tuple(linker.get_library_dirs()) for libname in libs: # be conservative and record most likely shared and static resolution, because we don't know exactly # which one the linker will prefer staticlibs = self.guess_library_absolute_path(linker, libname, - search_dirs, static_patterns) + search_dirs_tuple, static_patterns) sharedlibs = self.guess_library_absolute_path(linker, libname, - search_dirs, shared_patterns) + search_dirs_tuple, shared_patterns) if staticlibs: guessed_dependencies.append(staticlibs.resolve().as_posix()) if sharedlibs: @@ -3841,7 +3841,7 @@ def generate_link(self, target: build.BuildTarget, outname: str, obj_list: T.Lis # Add link args to link to all internal libraries (link_with:) and # internal dependencies needed by this target. dep_targets: T.List[str] = [] - dependencies: T.List[build.BuildTargetTypes] + dependencies: T.Iterable[build.BuildTargetTypes] if isinstance(target, build.StaticLibrary): # Link arguments of static libraries are not put in the command # line of the library. They are instead appended to the command From a4aa215d51b4f551d6bd43f6373ae922fa670f7a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 19 May 2026 21:03:41 +0200 Subject: [PATCH 287/681] ninjabackend: remove chain that confuses mypy Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 2cc91eadafed..b28bfc05dc74 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -2145,7 +2145,7 @@ def _link_library(libname: str, static: bool, bundle: bool = False) -> None: if isinstance(d, build.StaticLibrary): external_deps.extend(d.external_deps) if d.uses_rust_abi(): - if d not in itertools.chain(target.link_targets, target.link_whole_targets): + if d not in target.link_targets and d not in target.link_whole_targets: # Indirect Rust ABI dependency, we only need its path in linkdirs. continue assert isinstance(d, build.BuildTarget) From 12b689343e5960785b2f196c74bd43f035eacc73 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 19 May 2026 21:31:34 +0200 Subject: [PATCH 288/681] ninjabackend: tighten type on get_import_filename Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index b28bfc05dc74..078d6489f8e9 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -3574,7 +3574,7 @@ def generate_shsym(self, target: build.BuildTarget) -> None: elem.add_item('CROSS', '--cross-host=' + self.environment.machines[target.for_machine].system) self.add_build(elem) - def get_import_filename(self, target: build.BuildTarget) -> str: + def get_import_filename(self, target: build.Executable | build.SharedLibrary) -> str: return os.path.join(self.get_target_dir(target), target.import_filename) def get_target_type_link_args(self, target: build.BuildTarget, linker: T.Union[StaticLinker, Compiler]) -> T.List[str]: From e5a19a4c27927cb890ab16460412683ee769608c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 19 May 2026 21:37:35 +0200 Subject: [PATCH 289/681] ninjabackend: treat CustomTargetIndex as built Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 078d6489f8e9..3cb522c9a11d 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1768,7 +1768,7 @@ def generate_vala_compile(self, target: build.BuildTarget) -> \ # If the Vala file is outside the build directory, the paths from # the --basedir till the subdir will be duplicated inside the # private builddir. - if isinstance(gensrc, (build.CustomTarget, build.GeneratedList)) or gensrc.is_built: + if isinstance(gensrc, (build.CustomTarget, build.CustomTargetIndex, build.GeneratedList)) or gensrc.is_built: vala_c_file = os.path.splitext(os.path.basename(vala_file))[0] + '.c' # Check if the vala file is in a subdir of --basedir abs_srcbasedir = os.path.join(self.environment.get_source_dir(), target.get_subdir()) From c8d60f4a531c40eec7ee52efdaa7de2ac5102f10 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 19 May 2026 21:39:37 +0200 Subject: [PATCH 290/681] ninjabackend: generators can belong to CustomTargets Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 3cb522c9a11d..662edf075300 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -2821,7 +2821,7 @@ def generate_generator_list_rules(self, target: build.BuildTarget) -> None: continue self.generate_genlist_for_target(genlist, target) - def replace_paths(self, target: build.BuildTarget, args: T.List[str], override_subdir: T.Optional[str] = None) -> T.List[str]: + def replace_paths(self, target: build.BuildTarget | build.CustomTarget, args: T.List[str], override_subdir: T.Optional[str] = None) -> T.List[str]: if override_subdir: source_target_dir = os.path.join(self.build_to_src, override_subdir) else: @@ -2835,7 +2835,7 @@ def replace_paths(self, target: build.BuildTarget, args: T.List[str], override_s args = [x.replace('\\', '/') for x in args] return args - def generate_genlist_for_target(self, genlist: build.GeneratedList, target: build.BuildTarget) -> None: + def generate_genlist_for_target(self, genlist: build.GeneratedList, target: build.BuildTarget | build.CustomTarget) -> None: for x in genlist.depends: if isinstance(x, build.GeneratedList): self.generate_genlist_for_target(x, target) From 937143729790e98bcc0f450f1511d7fc2d54ae9a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 19 May 2026 21:33:44 +0200 Subject: [PATCH 291/681] ninjabackend: tweak File vs str vs File|str Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 662edf075300..6d19aeedaa93 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -52,6 +52,7 @@ CommandArgTypes = T.TypeVar('CommandArgTypes', 'NinjaCommandArg', str, 'NinjaCommandArg | str') CommandArgs = T.List[CommandArgTypes] + FileList = T.List[File] | T.List[str] | T.List[File | str] ListifiedStr = str | T.List[str] RUST_EDITIONS = Literal['2015', '2018', '2021', '2024'] @@ -834,9 +835,9 @@ def get_target_source_can_unity(self, target: build.BuildTarget, source: FileOrS def create_target_source_introspection(self, target: build.Target, comp: compilers.Compiler, parameters: CompilerArgs | T.List[str], - sources: T.List[FileOrString], - generated_sources: T.List[FileOrString], - unity_sources: T.Optional[T.List[FileOrString]] = None) -> None: + sources: FileList, + generated_sources: FileList, + unity_sources: T.Optional[T.List[str]] = None) -> None: ''' Adds the source file introspection information for a language of a target @@ -1172,7 +1173,7 @@ def should_use_dyndeps_for_target(self, target: build.BuildTargetTypes) -> bool: def generate_dependency_scan_target(self, target: build.BuildTarget, compiled_sources: T.List[str], source2object: T.Dict[str, str], - object_deps: T.List[FileOrString]) -> None: + object_deps: T.List[File]) -> None: if not self.should_use_dyndeps_for_target(target): return self._uses_dyndeps = True @@ -2249,7 +2250,7 @@ def _link_library(libname: str, static: bool, bundle: bool = False) -> None: return deps, project_deps, args def generate_rust_target(self, target: build.BuildTarget, target_name: str, obj_list: T.List[str], - fortran_order_deps: T.List[str]) -> None: + fortran_order_deps: T.List[File]) -> None: orderdeps, main_rust_file = self.generate_rust_sources(target) if main_rust_file is None: raise RuntimeError('A Rust target has no Rust sources. This is weird. Also a bug. Please report') @@ -3203,9 +3204,9 @@ def generate_common_compile_args_per_src_type(self, target: build.BuildTarget) - def generate_single_compile(self, target: build.BuildTarget, src: FileOrString, is_generated: bool = False, header_deps: T.Optional[T.List[FileOrString]] = None, - order_deps: T.Optional[T.List[FileOrString]] = None, + order_deps: T.Optional[T.List[File] | T.List[FileOrString]] = None, extra_args: T.Optional[T.List[str]] = None, - unity_sources: T.Optional[T.List[FileOrString]] = None, + unity_sources: T.Optional[T.List[str]] = None, ) -> T.Tuple[str, str]: """ Compiles C/C++, ObjC/ObjC++, Fortran, and D sources From df274a6215f421676c65ddadbcf053eb890017bf Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 19 May 2026 21:48:53 +0200 Subject: [PATCH 292/681] ninjabackend: reuse handling of fortran_order_deps from generate_single_compile Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 6d19aeedaa93..804bd98d3c56 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1203,7 +1203,7 @@ def generate_dependency_scan_target(self, target: build.BuildTarget, # they use or export. for s in scan_sources: elem.deps.add(s[0]) - elem.orderdeps.update(object_deps) + elem.add_orderdep(self.order_deps_to_strings(target, object_deps)) elem.add_item('name', target.name) self.add_build(elem) @@ -2279,8 +2279,7 @@ def generate_rust_target(self, target: build.BuildTarget, target_name: str, obj_ element = NinjaBuildElement(self.all_outputs, target_name, compiler_name, main_rust_file) if orderdeps: element.add_orderdep(orderdeps) - if fortran_order_deps: - element.add_orderdep(fortran_order_deps) + element.add_orderdep(self.order_deps_to_strings(target, fortran_order_deps)) if deps: # dependencies need to cause a relink, they're not just for ordering element.add_dep(deps) @@ -3201,6 +3200,16 @@ def generate_common_compile_args_per_src_type(self, target: build.BuildTarget) - src_type_to_args[src_type_str] = commands.to_native() return src_type_to_args + def order_deps_to_strings(self, target: build.BuildTarget, order_deps: T.List[File] | T.List[FileOrString]) -> T.List[str]: + result: T.List[str] = [] + for d in order_deps: + if isinstance(d, File): + d = d.rel_to_builddir(self.build_to_src) + elif not self.has_dir_part(d): + d = os.path.join(self.get_target_private_dir(target), d) + result.append(d) + return result + def generate_single_compile(self, target: build.BuildTarget, src: FileOrString, is_generated: bool = False, header_deps: T.Optional[T.List[FileOrString]] = None, @@ -3323,12 +3332,7 @@ def generate_single_compile(self, target: build.BuildTarget, src: FileOrString, self.add_header_deps(target, element, header_deps) for d in extra_deps: element.add_dep(d) - for d in order_deps: - if isinstance(d, File): - d = d.rel_to_builddir(self.build_to_src) - elif not self.has_dir_part(d): - d = os.path.join(self.get_target_private_dir(target), d) - element.add_orderdep(d) + element.add_orderdep(self.order_deps_to_strings(target, order_deps)) element.add_dep(pch_dep) if not self.use_dyndeps_for_fortran(): for i in self.get_fortran_module_deps(target, compiler): From 6b1845ac4052c211cd162c377edeb1255635d695 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 20 May 2026 23:44:55 +0200 Subject: [PATCH 293/681] ninjabackend: add assertion to clarify gen_pch_args is MSVC-only Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 804bd98d3c56..8c2e5087877d 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -3473,6 +3473,8 @@ def get_fortran_module_deps(self, target: build.BuildTarget, compiler: Compiler) ] def generate_msvc_pch_command(self, target: build.BuildTarget, compiler: Compiler, pch: T.Tuple[str, T.Optional[str]]) -> T.Tuple[T.List[str], str, str, T.List[str], str]: + from ..compilers.mixins.visualstudio import VisualStudioLikeCompiler + assert isinstance(compiler, VisualStudioLikeCompiler) # for mypy header = pch[0] pchname = compiler.get_pch_name(header) dst = os.path.join(self.get_target_private_dir(target), pchname) From 88285d41fdec0c26072f78e865785c6002813371 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Mon, 18 May 2026 19:47:08 +0200 Subject: [PATCH 294/681] ci: Replace removed `wine-stable` with `wine` on `ubuntu-rolling` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Górny --- ci/ciimage/ubuntu-rolling/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/ciimage/ubuntu-rolling/install.sh b/ci/ciimage/ubuntu-rolling/install.sh index f3cef5fe30c3..6551107e0cb9 100755 --- a/ci/ciimage/ubuntu-rolling/install.sh +++ b/ci/ciimage/ubuntu-rolling/install.sh @@ -47,7 +47,7 @@ eatmydata apt-get -y build-dep meson # packages eatmydata apt-get -y install "${pkgs[@]}" "${transitivepkgs[@]}" -eatmydata apt-get -y install --no-install-recommends wine-stable # Wine is special +eatmydata apt-get -y install --no-install-recommends wine # Wine is special # HACK: build hotdoc from git repo since current sdist is broken on modern compilers # change back to 'hotdoc' once it's fixed From 250f0c6b6e708b65cfcef86cd128d304a3912205 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 17 May 2026 12:17:08 +0200 Subject: [PATCH 295/681] cargo: raise error early if using an unconfigured package Instead of raising an exception for accessing a field of a NoneType, report a Meson error and omit the entry from packages(). Signed-off-by: Paolo Bonzini --- docs/markdown/Rust-module.md | 3 ++- mesonbuild/modules/rust.py | 6 +++++- test cases/rust/34 rust.workspace workspace/Cargo.toml | 3 ++- .../rust/34 rust.workspace workspace/another/Cargo.toml | 7 +++++++ .../rust/34 rust.workspace workspace/another/src/lib.rs | 3 +++ test cases/rust/34 rust.workspace workspace/meson.build | 3 +++ 6 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 test cases/rust/34 rust.workspace workspace/another/Cargo.toml create mode 100644 test cases/rust/34 rust.workspace workspace/another/src/lib.rs diff --git a/docs/markdown/Rust-module.md b/docs/markdown/Rust-module.md index a1039f4759df..e39122bd5a02 100644 --- a/docs/markdown/Rust-module.md +++ b/docs/markdown/Rust-module.md @@ -271,7 +271,8 @@ say "require this specific configuration," which may conflict with the parent pr packages = ws.packages() ``` -Returns a list of package names in the workspace. +Returns a list of configured package names in the workspace. Non-default +workspace members are not included. ### workspace.package() diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index 7f623385ff2b..8c8dc1ca337f 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -152,7 +152,9 @@ def subdir(self) -> str: @noKwargs def packages_method(self, state: ModuleState, args: T.List, kwargs: TYPE_kwargs) -> T.List[str]: """Returns list of package names in workspace.""" - package_names = [pkg.manifest.package.name for pkg in self.ws.packages.values()] + package_names = [pkg.manifest.package.name + for pkg in self.ws.packages.values() + if pkg.cfg] return sorted(package_names) @typed_pos_args('workspace.package', optargs=[str]) @@ -260,6 +262,8 @@ class RustPackage(RustCrate): def __init__(self, rust_ws: RustWorkspace, package: cargo.PackageState) -> None: super().__init__(rust_ws, package) + if not package.cfg: + raise MesonException(f"package {self.package.manifest.package.name}-{self.package.manifest.package.version} not configured") self.methods.update({ 'dependencies': self.dependencies_method, 'library': self.library_method, diff --git a/test cases/rust/34 rust.workspace workspace/Cargo.toml b/test cases/rust/34 rust.workspace workspace/Cargo.toml index ae2ae4aa796f..0c5a8fb2cee9 100644 --- a/test cases/rust/34 rust.workspace workspace/Cargo.toml +++ b/test cases/rust/34 rust.workspace workspace/Cargo.toml @@ -1,5 +1,6 @@ [workspace] -members = [".", "more"] +members = [".", "more", "another"] +default_members = [".", "more"] [package] name = "workspace_test" diff --git a/test cases/rust/34 rust.workspace workspace/another/Cargo.toml b/test cases/rust/34 rust.workspace workspace/another/Cargo.toml new file mode 100644 index 000000000000..b4404963133c --- /dev/null +++ b/test cases/rust/34 rust.workspace workspace/another/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "another" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["rlib"] diff --git a/test cases/rust/34 rust.workspace workspace/another/src/lib.rs b/test cases/rust/34 rust.workspace workspace/another/src/lib.rs new file mode 100644 index 000000000000..33dd8fa57ed6 --- /dev/null +++ b/test cases/rust/34 rust.workspace workspace/another/src/lib.rs @@ -0,0 +1,3 @@ +pub fn do_something() { + MIGHT_AS_WELL_FAIL_COMPILATION!(); +} diff --git a/test cases/rust/34 rust.workspace workspace/meson.build b/test cases/rust/34 rust.workspace workspace/meson.build index be971c6e7bf8..fbafc4a1d9f0 100644 --- a/test cases/rust/34 rust.workspace workspace/meson.build +++ b/test cases/rust/34 rust.workspace workspace/meson.build @@ -37,6 +37,9 @@ endtestcase testcase expect_error('workspace member "nonexistent" not found') cargo_ws.package('nonexistent') endtestcase +testcase expect_error('package another-0.1.0 not configured') + cargo_ws.package('another') +endtestcase # failure test cases for dependency() testcase expect_error('package.dependency.*must be one of c, proc-macro, rust.*', how: 're') From 068d755a612d9a8e16c80bf307d657ea7c7b3a9f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 18 May 2026 12:35:11 +0200 Subject: [PATCH 296/681] cargo: canonicalize all workspace members to POSIX paths Signed-off-by: Paolo Bonzini --- mesonbuild/cargo/manifest.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/mesonbuild/cargo/manifest.py b/mesonbuild/cargo/manifest.py index 0d1c0f279ff7..6fba31c2be12 100644 --- a/mesonbuild/cargo/manifest.py +++ b/mesonbuild/cargo/manifest.py @@ -9,6 +9,7 @@ import dataclasses import os import typing as T +from pathlib import PurePath from . import version @@ -617,8 +618,12 @@ def from_raw(cls, raw: raw.Manifest, path: str) -> Self: ws = _raw_to_dataclass(raw['workspace'], cls, 'Workspace') if 'package' in raw: ws.root_package = Manifest.from_raw(raw, path, ws, '.') - if not ws.default_members: - ws.default_members = ['.'] if ws.root_package else ws.members + ws.members = list(PurePath(m).as_posix() for m in ws.members) + if ws.default_members: + ws.default_members = list(PurePath(m).as_posix() for m in ws.default_members) + else: + ws.default_members = ['.'] if ws.root_package else list(ws.members) + return ws From c003c1fa93c09afffb838ec76d4138d6f61bfb7f Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Wed, 20 May 2026 22:26:20 +0300 Subject: [PATCH 297/681] Update default Qt version to 6. --- test cases/frameworks/4 qt/meson_options.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test cases/frameworks/4 qt/meson_options.txt b/test cases/frameworks/4 qt/meson_options.txt index 9a7513434012..4e52ac545c9a 100644 --- a/test cases/frameworks/4 qt/meson_options.txt +++ b/test cases/frameworks/4 qt/meson_options.txt @@ -1,3 +1,3 @@ option('method', type : 'string', value : 'auto', description : 'The method to use to find Qt') -option('required', type : 'string', value : 'qt5', description : 'The version of Qt which is required to be present') +option('required', type : 'string', value : 'qt6', description : 'The version of Qt which is required to be present') option('expect_lrelease', type: 'boolean', value: true) From ca27e8cf00ca04a6a30bb92e8bb3764ee0092119 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Wed, 20 May 2026 23:16:00 +0300 Subject: [PATCH 298/681] Update used Qt version in macOS. --- .github/workflows/macos.yml | 12 ++++++------ test cases/frameworks/4 qt/main.cpp | 5 +++++ test cases/frameworks/40 qt qml/test.json | 2 +- test cases/frameworks/5 protocol buffers/test.json | 2 +- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index daf8d378a852..55a112c69ad3 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -46,7 +46,7 @@ jobs: - name: Install Python dependencies run: | python3 -m pip install pytest pytest-xdist pytest-subtests fastjsonschema - - run: brew install pkg-config ninja llvm qt@5 + - run: brew install pkg-config ninja llvm qt@6 - env: CPPFLAGS: "-I/opt/homebrew/include" LDFLAGS: "-L/opt/homebrew/lib" @@ -56,8 +56,8 @@ jobs: # These cannot evaluate anything, so we cannot set PATH or SDKROOT here run: | export SDKROOT="$(xcodebuild -version -sdk macosx Path)" - export PATH="$HOME/tools:/opt/homebrew/opt/qt@5/bin:/opt/homebrew/opt/llvm/bin:$PATH" - export PKG_CONFIG_PATH="/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/Current/lib/pkgconfig:/opt/homebrew/opt/qt@5/lib/pkgconfig:$PKG_CONFIG_PATH" + export PATH="$HOME/tools:/opt/homebrew/opt/qt@6/bin:/opt/homebrew/opt/llvm/bin:$PATH" + export PKG_CONFIG_PATH="/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/Current/lib/pkgconfig:/opt/homebrew/opt/qt@6/lib/pkgconfig:$PKG_CONFIG_PATH" python3 ./run_unittests.py @@ -93,7 +93,7 @@ jobs: find /opt/homebrew/Cellar/python* -name EXTERNALLY-MANAGED -print0 | xargs -0 rm -vf # use python3 from homebrew because it is a valid framework, unlike the actions one: # https://github.com/actions/setup-python/issues/58 - - run: brew install pkg-config ninja llvm qt@5 boost ldc hdf5 openmpi lapack scalapack sdl2 boost-python3 gtk-doc zstd ncurses objfw libomp + - run: brew install pkg-config ninja llvm qt@6 boost ldc hdf5 openmpi lapack scalapack sdl2 boost-python3 gtk-doc zstd ncurses objfw libomp - run: | python3 -m pip install cython - env: @@ -107,6 +107,6 @@ jobs: export SDKROOT="$(xcodebuild -version -sdk macosx Path)" # Append LLVM's bin directory to PATH to prioritise Apple Clang over Homebrew Clang. # We need this to avoid objfw test failures. - export PATH="$HOME/tools:/opt/homebrew/opt/qt@5/bin:/opt/homebrew/opt/ncurses/bin:$PATH:/opt/homebrew/opt/llvm/bin" - export PKG_CONFIG_PATH="/opt/homebrew/opt/qt@5/lib/pkgconfig:/opt/homebrew/opt/lapack/lib/pkgconfig:/opt/homebrew/opt/ncurses/lib/pkgconfig:$PKG_CONFIG_PATH" + export PATH="$HOME/tools:/opt/homebrew/opt/qt@6/bin:/opt/homebrew/opt/ncurses/bin:$PATH:/opt/homebrew/opt/llvm/bin" + export PKG_CONFIG_PATH="/opt/homebrew/opt/qt@6/lib/pkgconfig:/opt/homebrew/opt/lapack/lib/pkgconfig:/opt/homebrew/opt/ncurses/lib/pkgconfig:$PKG_CONFIG_PATH" ./run_project_tests.py --backend=ninja -- $MESON_ARGS diff --git a/test cases/frameworks/4 qt/main.cpp b/test cases/frameworks/4 qt/main.cpp index 3c141b9d2e26..1b7969074057 100644 --- a/test cases/frameworks/4 qt/main.cpp +++ b/test cases/frameworks/4 qt/main.cpp @@ -3,6 +3,10 @@ #include #include "mainWindow.h" +// Our CI gets its Qt via Homebrew. Starting with +// Qt 6 they stopped shipping private headers. +// If the situation changes, remove this ifndef. +#ifndef __APPLE__ #if QT_VERSION > 0x050000 // include some random private headers // As you're not supposed to use it, your system may miss @@ -11,6 +15,7 @@ // - apt-file search qobject_p.h #include #endif +#endif int main(int argc, char **argv) { #ifndef UNITY_BUILD diff --git a/test cases/frameworks/40 qt qml/test.json b/test cases/frameworks/40 qt qml/test.json index 0f6a2c3fcac3..ae9e42c9c544 100644 --- a/test cases/frameworks/40 qt qml/test.json +++ b/test cases/frameworks/40 qt qml/test.json @@ -18,5 +18,5 @@ {"type": "file", "file": "usr/qml/My/Module5/qmldir"}, {"type": "file", "file": "usr/qml/My/Module5/My_Module5.qmltypes"} ], - "expect_skip_on_jobname": ["cygwin", "msys2", "azure", "macos", "windows"] + "expect_skip_on_jobname": ["cygwin", "msys2", "azure", "windows"] } diff --git a/test cases/frameworks/5 protocol buffers/test.json b/test cases/frameworks/5 protocol buffers/test.json index 078ef98e1260..7d58c31adc29 100644 --- a/test cases/frameworks/5 protocol buffers/test.json +++ b/test cases/frameworks/5 protocol buffers/test.json @@ -1,3 +1,3 @@ { - "expect_skip_on_jobname": ["azure", "cygwin", "macos", "msys2", "windows"] + "expect_skip_on_jobname": ["azure", "cygwin", "msys2", "windows"] } From 995ee8e5c628dff5e27a413f36239261cb6c03b6 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Thu, 21 May 2026 15:33:10 +0300 Subject: [PATCH 299/681] MacOS Qt 6 private dir fix from Carlo Cabrera. https://github.com/mesonbuild/meson/pull/15824#issuecomment-4490893769 --- mesonbuild/dependencies/qt.py | 23 +++++++++++++++++------ test cases/frameworks/4 qt/main.cpp | 5 ----- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/mesonbuild/dependencies/qt.py b/mesonbuild/dependencies/qt.py index e090215789cc..ae5596a20b02 100644 --- a/mesonbuild/dependencies/qt.py +++ b/mesonbuild/dependencies/qt.py @@ -196,12 +196,23 @@ def __init__(self, name: str, env: 'Environment', kwargs: DependencyObjectKWs): qt_inc_dir = mod.get_variable(pkgconfig='includedir') mod_private_dir = os.path.join(qt_inc_dir, 'Qt' + m) if not os.path.isdir(mod_private_dir): - # At least some versions of homebrew don't seem to set this - # up correctly. /usr/local/opt/qt/include/Qt + m_name is a - # symlink to /usr/local/opt/qt/include, but the pkg-config - # file points to /usr/local/Cellar/qt/x.y.z/Headers/, and - # the Qt + m_name there is not a symlink, it's a file - mod_private_dir = qt_inc_dir + if self.env.machines[self.for_machine].is_darwin(): + # On macOS Qt is conventionally shipped as a framework + # (e.g. Homebrew's qt@6). pkg-config 'includedir' is + # just the prefix include dir and contains no Qt + # headers; both public and private headers live under + # /Qt.framework/Headers. + libdir = mod.get_variable(pkgconfig='libdir', default_value='') + framework_inc = os.path.join(libdir, f'Qt{m}.framework', 'Headers') + if libdir and os.path.isdir(framework_inc): + mod_private_dir = framework_inc + if not os.path.isdir(mod_private_dir): + # At least some versions of homebrew don't seem to set this + # up correctly. /usr/local/opt/qt/include/Qt + m_name is a + # symlink to /usr/local/opt/qt/include, but the pkg-config + # file points to /usr/local/Cellar/qt/x.y.z/Headers/, and + # the Qt + m_name there is not a symlink, it's a file + mod_private_dir = qt_inc_dir mod_private_inc = _qt_get_private_includes(mod_private_dir, m, mod.version) for directory in mod_private_inc: mod.compile_args.append('-I' + directory) diff --git a/test cases/frameworks/4 qt/main.cpp b/test cases/frameworks/4 qt/main.cpp index 1b7969074057..3c141b9d2e26 100644 --- a/test cases/frameworks/4 qt/main.cpp +++ b/test cases/frameworks/4 qt/main.cpp @@ -3,10 +3,6 @@ #include #include "mainWindow.h" -// Our CI gets its Qt via Homebrew. Starting with -// Qt 6 they stopped shipping private headers. -// If the situation changes, remove this ifndef. -#ifndef __APPLE__ #if QT_VERSION > 0x050000 // include some random private headers // As you're not supposed to use it, your system may miss @@ -15,7 +11,6 @@ // - apt-file search qobject_p.h #include #endif -#endif int main(int argc, char **argv) { #ifndef UNITY_BUILD From d1ac05c9a9c21284cbffb16625dd06733c6ff3e4 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 17 May 2026 13:41:34 +0200 Subject: [PATCH 300/681] rust: add extra_members kwarg for workspace() Cargo allows building non-default workspace members via the -p option. Within Meson, we still want to do a single configuration process for the workspace, but we need to add any non-default members (typically chosen via machine objects and/or Meson options) at the time workspace() is invoked. Add a new argument to workspace() for this. Co-authored-by: Florian sp1rit Signed-off-by: Paolo Bonzini --- docs/markdown/Rust-module.md | 5 ++++- docs/markdown/Rust.md | 16 ++++++++++++++++ .../snippets/rust-workspace-non-default.md | 6 ++++++ mesonbuild/cargo/interpreter.py | 19 ++++++++++++++----- mesonbuild/modules/rust.py | 9 ++++++++- 5 files changed, 48 insertions(+), 7 deletions(-) create mode 100644 docs/markdown/snippets/rust-workspace-non-default.md diff --git a/docs/markdown/Rust-module.md b/docs/markdown/Rust-module.md index e39122bd5a02..2475a72dddbc 100644 --- a/docs/markdown/Rust-module.md +++ b/docs/markdown/Rust-module.md @@ -228,6 +228,8 @@ workspace. Keyword arguments: - `default_features`: (`bool`, optional) Whether to enable default features. - `features`: (`array[str]`, optional) List of additional features to enable globally. +- `extra_members`: (`array[str]`, optional) *Since 1.12.0* list of non-default + workspace members to configure. A project that wishes to use Cargo subprojects should have `Cargo.lock` and `Cargo.toml` files in the root source directory, and should call this function before using @@ -272,7 +274,8 @@ packages = ws.packages() ``` Returns a list of configured package names in the workspace. Non-default -workspace members are not included. +workspace members are not included unless they were passed as `extra_members` +when the workspace was created. ### workspace.package() diff --git a/docs/markdown/Rust.md b/docs/markdown/Rust.md index 3bbf966be3ca..d9ab8315a82c 100644 --- a/docs/markdown/Rust.md +++ b/docs/markdown/Rust.md @@ -175,3 +175,19 @@ Sources are automatically discovered, but can be specified as a It is still possible to use keyword arguments to link non-Rust build targets, or even to use the usual Meson functions such as [[static_library]] or [[executable]]. + +### Non-default workspace members + +With Cargo, workspace members that are not included in +`workspace.default-members` can be accessed through the `-p` option. +In Meson, non-default members are not part of dependency resolution +by default; the set of optional packages that has to be configured +is specified *since 1.12.0* in the `extra_members` argument of the +`workspace()` method, while up to Meson 1.11.x non-default members would +never be built. + +Cargo subprojects will only build default members unless they have a custom +(handwritten) `meson.build` that sets `extra_members` appropriately; +typically the choice of which members to build will come from a Meson +ption. Note that crates that come from `crates.io` are never workspaces, +and therefore they are not subject to this limitation. diff --git a/docs/markdown/snippets/rust-workspace-non-default.md b/docs/markdown/snippets/rust-workspace-non-default.md new file mode 100644 index 000000000000..deb74f73863a --- /dev/null +++ b/docs/markdown/snippets/rust-workspace-non-default.md @@ -0,0 +1,6 @@ +## Non-default members of Cargo workspaces can now be built + +The new keyword argument `extra_members` to the `workspace()` method +allows configuring non-default members of a Cargo workspace. Previously, +non-default members were never used for dependency resolution and +could not be built. diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index e1e30e35dcca..4a45edfbee8d 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -316,11 +316,11 @@ def features(self, value: T.List[str]) -> None: def get_build_def_files(self) -> T.List[str]: return self.build_def_files - def load_workspace(self, subdir: str) -> WorkspaceState: + def load_workspace(self, subdir: str, extra_members: T.Optional[T.List[str]]) -> WorkspaceState: """Load the root Cargo.toml package and prepare it with features and dependencies.""" subdir = os.path.normpath(subdir) manifest, cached = self._load_manifest(subdir) - ws = self._get_workspace(manifest, subdir, False) + ws = self._get_workspace(manifest, subdir, extra_members, False) if not cached: self._prepare_entry_point(ws) return ws @@ -356,7 +356,7 @@ def interpret(self, subdir: str, project_root: T.Optional[str] = None) -> mparse assert isinstance(manifest, Manifest) return self.interpret_package(manifest, build, subdir, project_root) else: - ws = self.load_workspace(subdir) + ws = self.load_workspace(subdir, None) return self.interpret_workspace(ws, build, subdir) def interpret_package(self, manifest: Manifest, build: builder.Builder, subdir: str, project_root: str) -> mparser.CodeBlockNode: @@ -447,7 +447,7 @@ def _add_workspace_member(self, manifest_: Manifest, ws: WorkspaceState, m: str) else: ws.packages[m] = PackageState(manifest_, ws_subdir=ws.subdir, ws_member=m, downloaded=ws.downloaded) - def _get_workspace(self, manifest: T.Union[Workspace, Manifest], subdir: str, downloaded: bool) -> WorkspaceState: + def _get_workspace(self, manifest: T.Union[Workspace, Manifest], subdir: str, extra_members: T.Optional[T.List[str]], downloaded: bool) -> WorkspaceState: ws = self.workspaces.get(subdir) if ws: return ws @@ -456,6 +456,15 @@ def _get_workspace(self, manifest: T.Union[Workspace, Manifest], subdir: str, do ws = WorkspaceState(workspace, subdir, downloaded=downloaded) if workspace.root_package: self._add_workspace_member(workspace.root_package, ws, '.') + + if extra_members is not None: + for m in extra_members: + m = PurePath(m).as_posix() + if m not in workspace.members: + l = ', '.join(sorted(list(workspace.members))) + raise MesonException(f'{m} is not a workspace member for {subdir}/Cargo.toml (valid members are {l})') + if m not in workspace.default_members: + workspace.default_members.append(m) for m in workspace.members: self._load_workspace_member(ws, m) self.workspaces[subdir] = ws @@ -530,7 +539,7 @@ def _fetch_package_from_subproject(self, package_name: str, subp_name: str) -> P subp_name in self.environment.wrap_resolver.wraps and \ self.environment.wrap_resolver.wraps[subp_name].type is not None - ws = self._get_workspace(manifest, subdir, downloaded=downloaded) + ws = self._get_workspace(manifest, subdir, None, downloaded=downloaded) member = ws.packages_to_member[package_name] pkg = self._require_workspace_member(ws, member) pkg.subproject_name = subp_name diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index 8c8dc1ca337f..d07f7fcdb3f4 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -75,6 +75,7 @@ class FuncBindgen(TypedDict): class FuncWorkspace(TypedDict): default_features: T.Optional[bool] features: T.List[str] + extra_members: T.List[str] class FuncDependency(TypedDict): rust_abi: T.Optional[RUST_ABI] @@ -1017,6 +1018,12 @@ def to_system_dependency(self, state: ModuleState, args: T.Tuple[Dependency, T.O default=None, listify=True, ), + KwargInfo( + 'extra_members', + (ContainerTypeInfo(list, str), NoneType), + default=None, + listify=True, + ), ) def workspace(self, state: ModuleState, args: T.List, kwargs: FuncWorkspace) -> RustWorkspace: """Creates a Rust workspace object, controlling the build of @@ -1039,7 +1046,7 @@ def workspace(self, state: ModuleState, args: T.List, kwargs: FuncWorkspace) -> cargo_features.extend(features) self.interpreter.cargo.features = cargo_features - ws = self.interpreter.cargo.load_workspace(state.root_subdir) + ws = self.interpreter.cargo.load_workspace(state.root_subdir, kwargs['extra_members']) # Cargo projects may not have a subprojects directory, because # dependencies are declared in Cargo.toml rather than .wrap files. From 89a5cc031bf26077cd1ad512b840daf46a2c2c75 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 17 May 2026 13:06:06 +0200 Subject: [PATCH 301/681] cargo: glob-expand workspace members Cargo.toml workspace members are glob expressions, which we'd rather not rely on. Treat them always as non-default members, so that they have to be listed explicitly in extra_members but are nevertheless available. Co-authored-by: Florian sp1rit Signed-off-by: Paolo Bonzini --- docs/markdown/Rust.md | 13 +++++-- mesonbuild/cargo/manifest.py | 36 +++++++++++++++++++ test cases/rust/43 extra_members/Cargo.lock | 15 ++++++++ test cases/rust/43 extra_members/Cargo.toml | 6 ++++ .../rust/43 extra_members/gAmma/Cargo.toml | 4 +++ .../rust/43 extra_members/gAmma/src/main.rs | 1 + test cases/rust/43 extra_members/meson.build | 12 +++++++ .../43 extra_members/subdir/alpha/Cargo.toml | 4 +++ .../43 extra_members/subdir/alpha/src/main.rs | 1 + .../43 extra_members/subdir/beta/Cargo.toml | 4 +++ .../43 extra_members/subdir/beta/src/main.rs | 3 ++ 11 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 test cases/rust/43 extra_members/Cargo.lock create mode 100644 test cases/rust/43 extra_members/Cargo.toml create mode 100644 test cases/rust/43 extra_members/gAmma/Cargo.toml create mode 100644 test cases/rust/43 extra_members/gAmma/src/main.rs create mode 100644 test cases/rust/43 extra_members/meson.build create mode 100644 test cases/rust/43 extra_members/subdir/alpha/Cargo.toml create mode 100644 test cases/rust/43 extra_members/subdir/alpha/src/main.rs create mode 100644 test cases/rust/43 extra_members/subdir/beta/Cargo.toml create mode 100644 test cases/rust/43 extra_members/subdir/beta/src/main.rs diff --git a/docs/markdown/Rust.md b/docs/markdown/Rust.md index d9ab8315a82c..d3616b9642bc 100644 --- a/docs/markdown/Rust.md +++ b/docs/markdown/Rust.md @@ -176,7 +176,7 @@ It is still possible to use keyword arguments to link non-Rust build targets, or even to use the usual Meson functions such as [[static_library]] or [[executable]]. -### Non-default workspace members +### Non-default and wildcard workspace members With Cargo, workspace members that are not included in `workspace.default-members` can be accessed through the `-p` option. @@ -189,5 +189,12 @@ never be built. Cargo subprojects will only build default members unless they have a custom (handwritten) `meson.build` that sets `extra_members` appropriately; typically the choice of which members to build will come from a Meson -ption. Note that crates that come from `crates.io` are never workspaces, -and therefore they are not subject to this limitation. +option. + +Furthermore, while Cargo accepts glob patterns for the `workspace.members` +field, Meson (*since 1.12.0*) expands such patterns but never treats +globbed patterns as default members. Therefore, they will only be built +if included in `extra_members`. + +Note that crates that come from `crates.io` are never workspaces, and +therefore they are not subject to these differences. diff --git a/mesonbuild/cargo/manifest.py b/mesonbuild/cargo/manifest.py index 6fba31c2be12..ef2bc1e0e15a 100644 --- a/mesonbuild/cargo/manifest.py +++ b/mesonbuild/cargo/manifest.py @@ -7,7 +7,9 @@ import collections import dataclasses +import glob import os +import re import typing as T from pathlib import PurePath @@ -47,6 +49,23 @@ def fixup_meson_varname(name: str) -> str: return name.replace('-', '_') +_BRACKET_ESCAPE_RE = re.compile(r'\[(.)\]') + + +def _glob_has_wildcard(s: str) -> bool: + """Strip single-character bracket expressions (``[X]``) from *s*, so + that the result can be fed to glob.has_magic() without those + escapes being mistaken for wildcards.""" + return glob.has_magic(_BRACKET_ESCAPE_RE.sub('', s)) + + +def _remove_simple_globs(s: str) -> str: + """Strip single-character bracket expressions (``[X]``) from *s*, so + that the result can be fed to glob.has_magic() without those + escapes being mistaken for wildcards.""" + return _BRACKET_ESCAPE_RE.sub(r'\1', s) + + class DefaultValue: """Base class to converts a raw value from cargo manifest to a meson value @@ -624,6 +643,23 @@ def from_raw(cls, raw: raw.Manifest, path: str) -> Self: else: ws.default_members = ['.'] if ws.root_package else list(ws.members) + def expand(entries: T.List[str], keep_glob_results: bool) -> T.List[str]: + result: T.List[str] = [] + for entry in entries: + if not _glob_has_wildcard(entry): + result.append(_remove_simple_globs(entry)) + continue + + if keep_glob_results: + result.extend(PurePath(exp).as_posix() + for exp in glob.glob(entry, root_dir=path) + if os.path.isdir(os.path.join(path, exp))) + return result + + # meson-specific behavior for glob members: they are allowed as + # arguments to cargo.package(), but never built by default + ws.members = expand(ws.members, keep_glob_results=True) + ws.default_members = expand(ws.default_members, keep_glob_results=False) return ws diff --git a/test cases/rust/43 extra_members/Cargo.lock b/test cases/rust/43 extra_members/Cargo.lock new file mode 100644 index 000000000000..a432bf6f8b6f --- /dev/null +++ b/test cases/rust/43 extra_members/Cargo.lock @@ -0,0 +1,15 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "alpha" +version = "0.0.0" + +[[package]] +name = "beta" +version = "0.0.0" + +[[package]] +name = "gamma" +version = "0.0.0" diff --git a/test cases/rust/43 extra_members/Cargo.toml b/test cases/rust/43 extra_members/Cargo.toml new file mode 100644 index 000000000000..de2774702c4d --- /dev/null +++ b/test cases/rust/43 extra_members/Cargo.toml @@ -0,0 +1,6 @@ +[workspace] +resolver = "3" +members = [ + "subdir/*", + "g[A]mma" +] diff --git a/test cases/rust/43 extra_members/gAmma/Cargo.toml b/test cases/rust/43 extra_members/gAmma/Cargo.toml new file mode 100644 index 000000000000..9b6d3d506006 --- /dev/null +++ b/test cases/rust/43 extra_members/gAmma/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "gamma" +version = "0.0.0" +edition = "2021" diff --git a/test cases/rust/43 extra_members/gAmma/src/main.rs b/test cases/rust/43 extra_members/gAmma/src/main.rs new file mode 100644 index 000000000000..f328e4d9d04c --- /dev/null +++ b/test cases/rust/43 extra_members/gAmma/src/main.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/test cases/rust/43 extra_members/meson.build b/test cases/rust/43 extra_members/meson.build new file mode 100644 index 000000000000..fd3d54677a0b --- /dev/null +++ b/test cases/rust/43 extra_members/meson.build @@ -0,0 +1,12 @@ +project('rust ws extra_members', 'rust', + meson_version: '>= 1.11' +) + +cargo = import('rust').workspace(extra_members: ['subdir/alpha']) + +if 'beta' in cargo.packages() + error('Unexpectedly resolved glob member') +endif + +cargo.package('alpha').executable() +cargo.package('gamma').executable() diff --git a/test cases/rust/43 extra_members/subdir/alpha/Cargo.toml b/test cases/rust/43 extra_members/subdir/alpha/Cargo.toml new file mode 100644 index 000000000000..1b69e2f3fa7a --- /dev/null +++ b/test cases/rust/43 extra_members/subdir/alpha/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "alpha" +version = "0.0.0" +edition = "2021" diff --git a/test cases/rust/43 extra_members/subdir/alpha/src/main.rs b/test cases/rust/43 extra_members/subdir/alpha/src/main.rs new file mode 100644 index 000000000000..f328e4d9d04c --- /dev/null +++ b/test cases/rust/43 extra_members/subdir/alpha/src/main.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/test cases/rust/43 extra_members/subdir/beta/Cargo.toml b/test cases/rust/43 extra_members/subdir/beta/Cargo.toml new file mode 100644 index 000000000000..346c1475c8c4 --- /dev/null +++ b/test cases/rust/43 extra_members/subdir/beta/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "beta" +version = "0.0.0" +edition = "2021" diff --git a/test cases/rust/43 extra_members/subdir/beta/src/main.rs b/test cases/rust/43 extra_members/subdir/beta/src/main.rs new file mode 100644 index 000000000000..5efd50c40dbc --- /dev/null +++ b/test cases/rust/43 extra_members/subdir/beta/src/main.rs @@ -0,0 +1,3 @@ +fn main() { +#error "Intentionally not building" +} From e594c1cb0001bf47a57137b9dfa82de1bfcd10f6 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 25 May 2026 12:51:49 +0200 Subject: [PATCH 302/681] build, backends: remove broken extract_all_objects code for custom targets CustomTarget.extract_all_objects and CustomTargetIndex.extract_all_objects are effectively dead. No path consumes their str/list[str] return value: - the extract_all_objects() DSL method is defined on BuildTargetHolder only. - _bundle_static_library() runs t.extract_all_objects() only for a StaticLibrary, whereas custom targets raise InvalidArguments - vs2010backend.py and ninjabackend.py's link_whole workaround call it but immediately treat the result as an ExtractedObjects The custom target here would be a .a file, therefore there is no way to extract objects from it. Remove the code and raise an exception instead. This fixes 7 mypy issues in the ninja backend and 8 in the vs2010 backend. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 8 ++++++-- mesonbuild/backend/vs2010backend.py | 5 +++++ mesonbuild/build.py | 6 ------ 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 8c2e5087877d..c6286220e07d 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -41,7 +41,6 @@ from typing_extensions import Literal, TypedDict from .._typing import ImmutableListProtocol - from ..build import ExtractedObjects from ..compilers.compilers import Language from ..compilers.cs import CsCompiler from ..compilers.fortran import FortranCompiler @@ -3647,8 +3646,13 @@ def get_link_whole_args(self, linker: Compiler, target: build.BuildTarget) -> T. use_custom = True if use_custom: - objects_from_static_libs: T.List[ExtractedObjects] = [] + objects_from_static_libs: T.List[str] = [] for dep in target.link_whole_targets: + if not isinstance(dep, build.BuildTarget): + raise MesonException( + f'Cannot extract objects from custom target {dep.name!r} to ' + f'link_whole it into {target.name!r}: this is not supported ' + 'with versions of MSVC older than Visual Studio 2015 Update 2.') l = dep.extract_all_objects(False) objects_from_static_libs += self.determine_ext_objs(l) objects_from_static_libs.extend(self.flatten_object_list(dep)[0]) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index a360d0681046..73bf4684e161 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -1496,6 +1496,11 @@ def add_non_makefile_vcxproj_elements( if t in target.link_whole_targets: if compiler.id == 'msvc' and version_compare(compiler.version, '<19.00.23918'): # Expand our object lists manually if we are on pre-Visual Studio 2015 Update 2 + if not isinstance(t, build.BuildTarget): + raise MesonException( + f'Cannot extract objects from custom target {t.name!r} to ' + f'link_whole it into {target.name!r}: this is not supported ' + 'with versions of MSVC older than Visual Studio 2015 Update 2.') l = t.extract_all_objects(False) # Unfortunately, we can't use self.object_filename_from_source() diff --git a/mesonbuild/build.py b/mesonbuild/build.py index ba61e4ddc35a..f34db6489d96 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -3206,9 +3206,6 @@ def is_internal(self) -> bool: return False return CustomTargetIndex(self, self.outputs[0]).is_internal() - def extract_all_objects(self) -> T.List[T.Union[str, 'ExtractedObjects']]: - return T.cast('list[str | ExtractedObjects]', self.get_outputs()) - def type_suffix(self) -> str: return "@cus" @@ -3506,9 +3503,6 @@ def is_internal(self) -> bool: suf = os.path.splitext(self.output)[-1] return suf in {'.a', '.lib'} and not self.should_install() - def extract_all_objects(self) -> T.Union[str, 'ExtractedObjects']: - return self.output - def get_custom_install_dir(self) -> T.List[T.Union[str, Literal[False]]]: return self.target.get_custom_install_dir() From 603958b786876cc9187579ae6aad627264f8b824 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 25 May 2026 12:51:49 +0200 Subject: [PATCH 303/681] vs2010backend: fix broken use of flatten_object_list result The second element, the order deps, was added in commit c02d7fe11 ("backend/ninja: properly track objects extracted from fortran sources", 2022-08-24) but the visual studio backend was not adjusted. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/vs2010backend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 73bf4684e161..8a5ea95725e2 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -1518,7 +1518,7 @@ def add_non_makefile_vcxproj_elements( rel_obj = self.object_filename_from_source(t, compiler, src, target_private_dir) extra_link_args.append(rel_obj) - extra_link_args.extend(self.flatten_object_list(t)) + extra_link_args.extend(self.flatten_object_list(t)[0]) else: # /WHOLEARCHIVE:foo must go into AdditionalOptions extra_link_args += compiler.get_link_whole_for(linkname) From 26f902714dd29e7db2c114194a29592f944bbb19 Mon Sep 17 00:00:00 2001 From: Chris Down Date: Sun, 16 Nov 2025 18:17:14 +0800 Subject: [PATCH 304/681] interpreter: Add hex/octal/binary support to str and int primitives Add support for parsing and formatting integers in hexadecimal, octal, and binary representations to complement the existing decimal support. str.to_int() now accepts strings with 0x/0o/0b prefixes and optional leading signs (+/-), enabling direct parsing of hex, octal, and binary literals while maintaining full backward compatibility with decimal strings including those with leading zeros. int.to_string() gains a new format keyword argument that accepts 'dec', 'hex', 'oct', or 'bin', allowing integers to be formatted with appropriate prefixes (0x, 0o, 0b). The format parameter works correctly with the existing fill parameter and handles negative numbers properly. Round-trip conversions are fully supported: values formatted with int.to_string(format: 'hex') can be parsed back with str.to_int(). Fixes: #2047 Fixes: #15201 --- docs/markdown/Syntax.md | 18 +++ .../markdown/snippets/int-base-conversions.md | 10 ++ mesonbuild/interpreter/primitives/integer.py | 18 ++- mesonbuild/interpreter/primitives/string.py | 9 +- .../286 number base conversions/meson.build | 138 ++++++++++++++++++ .../common/35 string operations/meson.build | 3 + 6 files changed, 192 insertions(+), 4 deletions(-) create mode 100644 docs/markdown/snippets/int-base-conversions.md create mode 100644 test cases/common/286 number base conversions/meson.build diff --git a/docs/markdown/Syntax.md b/docs/markdown/Syntax.md index f4a2e1772a2a..892dbc6d19b1 100644 --- a/docs/markdown/Syntax.md +++ b/docs/markdown/Syntax.md @@ -80,6 +80,15 @@ string_var = '42' num = string_var.to_int() ``` +Hexadecimal, octal, and binary strings can be converted to numbers since +1.12.0: + +```meson +hex_var = '0xFF'.to_int() # 255 +oct_var = '0o755'.to_int() # 493 +bin_var = '0b1010'.to_int() # 10 +``` + Numbers can be converted to a string: ```meson @@ -87,6 +96,15 @@ int_var = 42 string_var = int_var.to_string() ``` +Numbers can be formatted as hexadecimal, octal, or binary strings since 1.12.0: + +```meson +int_var = 255 +hex_str = int_var.to_string(format: 'hex') # '0xff' +oct_str = int_var.to_string(format: 'oct') # '0o377' +bin_str = int_var.to_string(format: 'bin') # '0b11111111' +``` + ## Booleans A boolean is either `true` or `false`. diff --git a/docs/markdown/snippets/int-base-conversions.md b/docs/markdown/snippets/int-base-conversions.md new file mode 100644 index 000000000000..4b1cbf2b968f --- /dev/null +++ b/docs/markdown/snippets/int-base-conversions.md @@ -0,0 +1,10 @@ +## Added integer base conversions to `str.to_int()` and `int.to_string()` + +Meson strings can now be converted from hexadecimal, octal, and binary +integer literals with `str.to_int()`. Integers can also be formatted back to +strings in those bases with `int.to_string(format:)`. + +```meson +assert('0xff'.to_int() == 255) +assert(255.to_string(format: 'hex') == '0xff') +``` diff --git a/mesonbuild/interpreter/primitives/integer.py b/mesonbuild/interpreter/primitives/integer.py index c59ea6e229a3..443458c92eaa 100644 --- a/mesonbuild/interpreter/primitives/integer.py +++ b/mesonbuild/interpreter/primitives/integer.py @@ -7,12 +7,19 @@ FeatureBroken, InvalidArguments, KwargInfo, noKwargs, noPosargs, typed_operator, typed_kwargs ) +from ..type_checking import in_set_validator import typing as T if T.TYPE_CHECKING: + from typing_extensions import Literal, TypedDict + from ...interpreterbase import TYPE_var, TYPE_kwargs + class ToStringKw(TypedDict): + fill: int + format: Literal['dec', 'hex', 'oct', 'bin'] + class IntegerHolder(ObjectHolder[int]): # Operators that only require type checks TRIVIAL_OPERATORS = { @@ -55,12 +62,17 @@ def is_odd_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool: @typed_kwargs( 'to_string', - KwargInfo('fill', int, default=0, since='1.3.0') + KwargInfo('fill', int, default=0, since='1.3.0'), + KwargInfo('format', str, default='dec', since='1.12.0', + validator=in_set_validator({'dec', 'hex', 'oct', 'bin'})), ) @noPosargs @InterpreterObject.method('to_string') - def to_string_method(self, args: T.List[TYPE_var], kwargs: T.Dict[str, T.Any]) -> str: - return str(self.held_object).zfill(kwargs['fill']) + def to_string_method(self, args: T.List[TYPE_var], kwargs: ToStringKw) -> str: + format_codes = {'hex': 'x', 'oct': 'o', 'bin': 'b', 'dec': 'd'} + return '{:#0{fill}{format}}'.format(self.held_object, + fill=max(0, kwargs['fill']), + format=format_codes[kwargs['format']]) @typed_operator(MesonOperator.DIV, int) @InterpreterObject.operator(MesonOperator.DIV) diff --git a/mesonbuild/interpreter/primitives/string.py b/mesonbuild/interpreter/primitives/string.py index 2adc58d34019..4092d056e565 100644 --- a/mesonbuild/interpreter/primitives/string.py +++ b/mesonbuild/interpreter/primitives/string.py @@ -135,7 +135,14 @@ def substring_method(self, args: T.Tuple[T.Optional[int], T.Optional[int]], kwar @InterpreterObject.method('to_int') def to_int_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> int: try: - return int(self.held_object) + s = self.held_object.strip() + try: + # For backward compatibility, try to parse the string as a decimal + # integer first. This is to allow leading zeros which are disallowed + # when determining the integer base from the string prefix. + return int(s) + except ValueError: + return int(s, base=0) except ValueError: raise InvalidArguments(f'String {self.held_object!r} cannot be converted to int') diff --git a/test cases/common/286 number base conversions/meson.build b/test cases/common/286 number base conversions/meson.build new file mode 100644 index 000000000000..404c53a2ef95 --- /dev/null +++ b/test cases/common/286 number base conversions/meson.build @@ -0,0 +1,138 @@ +project('test number base conversions') + +### .to_int() + +# hexadecimal strings +assert('0x10'.to_int() == 16, 'Hex string conversion failed') +assert('0X10'.to_int() == 16, 'Hex string conversion (uppercase X) failed') +assert('0xff'.to_int() == 255, 'Hex string conversion (lowercase) failed') +assert('0xFF'.to_int() == 255, 'Hex string conversion (uppercase) failed') +assert('0xDEADBEEF'.to_int() == 3735928559, 'Large hex conversion failed') +assert('0x1'.to_int() == 1, 'Small hex conversion failed') +assert('0x0'.to_int() == 0, 'Zero hex conversion failed') + +# signed hexadecimal strings +assert('-0xf'.to_int() == -15, 'Negative hex string conversion failed') +assert('+0x10'.to_int() == 16, 'Positive hex string conversion failed') +assert('-0xFF'.to_int() == -255, 'Negative hex string (uppercase) conversion failed') + +# octal strings +assert('0o10'.to_int() == 8, 'Octal string conversion failed') +assert('0O10'.to_int() == 8, 'Octal string conversion (uppercase O) failed') +assert('0o77'.to_int() == 63, 'Octal string conversion failed') +assert('0o755'.to_int() == 493, 'Octal permission-like conversion failed') +assert('0o0'.to_int() == 0, 'Zero octal conversion failed') + +# signed octal strings +assert('-0o10'.to_int() == -8, 'Negative octal string conversion failed') +assert('+0o77'.to_int() == 63, 'Positive octal string conversion failed') + +# binary strings +assert('0b10'.to_int() == 2, 'Binary string conversion failed') +assert('0B10'.to_int() == 2, 'Binary string conversion (uppercase B) failed') +assert('0b1111'.to_int() == 15, 'Binary string conversion failed') +assert('0b11111111'.to_int() == 255, 'Binary byte conversion failed') +assert('0b0'.to_int() == 0, 'Zero binary conversion failed') + +# signed binary strings +assert('-0b101'.to_int() == -5, 'Negative binary string conversion failed') +assert('+0b1111'.to_int() == 15, 'Positive binary string conversion failed') + +# decimal strings (backwards compat) +assert('10'.to_int() == 10, 'Decimal string conversion failed') +assert('255'.to_int() == 255, 'Decimal string conversion failed') +assert('0'.to_int() == 0, 'Zero decimal conversion failed') +assert('12345'.to_int() == 12345, 'Large decimal conversion failed') +assert('1_234'.to_int() == 1234, 'Decimal string conversion with underscores failed') +assert('0_100'.to_int() == 100, 'Zero-prefixed decimal string with underscores failed') + +# leading zeroes are decimal (backwards compat) +assert('010'.to_int() == 10, 'Decimal with leading zero broke backward compatibility') +assert('0123'.to_int() == 123, 'Decimal with leading zeros broke backward compatibility') +assert('007'.to_int() == 7, 'Decimal with leading zeros broke backward compatibility') +assert('0x_FF'.to_int() == 255, 'Hex string conversion with underscores failed') +assert('0o_755'.to_int() == 493, 'Octal string conversion with underscores failed') +assert('0b_1010'.to_int() == 10, 'Binary string conversion with underscores failed') + +### .to_string() + +# hex format +assert(16.to_string(format: 'hex') == '0x10', 'Int to hex string failed') +assert(255.to_string(format: 'hex') == '0xff', 'Int to hex string failed') +assert(0.to_string(format: 'hex') == '0x0', 'Zero to hex string failed') +assert(1.to_string(format: 'hex') == '0x1', 'One to hex string failed') +assert(3735928559.to_string(format: 'hex') == '0xdeadbeef', 'Large hex string failed') + +# octal format +assert(8.to_string(format: 'oct') == '0o10', 'Int to octal string failed') +assert(63.to_string(format: 'oct') == '0o77', 'Int to octal string failed') +assert(493.to_string(format: 'oct') == '0o755', 'Permission to octal string failed') +assert(0.to_string(format: 'oct') == '0o0', 'Zero to octal string failed') + +# binary format +assert(2.to_string(format: 'bin') == '0b10', 'Int to binary string failed') +assert(15.to_string(format: 'bin') == '0b1111', 'Int to binary string failed') +assert(255.to_string(format: 'bin') == '0b11111111', 'Byte to binary string failed') +assert(0.to_string(format: 'bin') == '0b0', 'Zero to binary string failed') + +# decimal format (explicit) +assert(10.to_string(format: 'dec') == '10', 'Int to decimal string failed') +assert(255.to_string(format: 'dec') == '255', 'Int to decimal string failed') + +# default +assert(42.to_string() == '42', 'Default int to string failed') + +# fill and hex format +assert(255.to_string(format: 'hex', fill: 8) == '0x0000ff', 'Hex with fill failed') +assert(1.to_string(format: 'hex', fill: 6) == '0x0001', 'Hex with fill failed') +assert(255.to_string(format: 'hex', fill: 4) == '0xff', 'Hex with fill (no padding needed) failed') + +# fill and other formats +assert(8.to_string(format: 'oct', fill: 6) == '0o0010', 'Octal with fill failed') +assert(2.to_string(format: 'bin', fill: 10) == '0b00000010', 'Binary with fill failed') + +# negative numbers +assert((-15).to_string(format: 'hex') == '-0xf', 'Negative hex conversion failed') +assert((-8).to_string(format: 'oct') == '-0o10', 'Negative octal conversion failed') +assert((-5).to_string(format: 'bin') == '-0b101', 'Negative binary conversion failed') + +# negative numbers and fill +assert((-15).to_string(format: 'hex', fill: 6) == '-0x00f', 'Negative hex with fill failed') +assert((-8).to_string(format: 'oct', fill: 7) == '-0o0010', 'Negative octal with fill failed') +assert((-5).to_string(format: 'bin', fill: 8) == '-0b00101', 'Negative binary with fill failed') +assert((-4).to_string(fill: 3) == '-04', 'Negative decimal with fill failed') + +# fill and decimal +assert(4.to_string(fill: 3) == '004', 'Decimal with fill failed') +assert(4.to_string(fill: -1) == '4', 'Negative decimal fill should clamp to zero') +assert(255.to_string(format: 'hex', fill: -1) == '0xff', 'Negative hex fill should clamp to zero') + +### Round trip conversions + +# positive + +hex_val = 0x200 +hex_str = hex_val.to_string(format: 'hex') +assert(hex_str.to_int() == hex_val, 'Hex round-trip failed') + +oct_val = 0o755 +oct_str = oct_val.to_string(format: 'oct') +assert(oct_str.to_int() == oct_val, 'Octal round-trip failed') + +bin_val = 0b11010 +bin_str = bin_val.to_string(format: 'bin') +assert(bin_str.to_int() == bin_val, 'Binary round-trip failed') + +# negative + +neg_hex = -255 +neg_hex_str = neg_hex.to_string(format: 'hex') +assert(neg_hex_str.to_int() == neg_hex, 'Negative hex round-trip failed') + +neg_oct = -63 +neg_oct_str = neg_oct.to_string(format: 'oct') +assert(neg_oct_str.to_int() == neg_oct, 'Negative octal round-trip failed') + +neg_bin = -15 +neg_bin_str = neg_bin.to_string(format: 'bin') +assert(neg_bin_str.to_int() == neg_bin, 'Negative binary round-trip failed') diff --git a/test cases/common/35 string operations/meson.build b/test cases/common/35 string operations/meson.build index 8cc5b0c08f61..6d3993bfce51 100644 --- a/test cases/common/35 string operations/meson.build +++ b/test cases/common/35 string operations/meson.build @@ -54,6 +54,9 @@ assert('#include '.underscorify() == '_include__foo_bar_h_', 'Broken assert('Do SomeThing 09'.underscorify() == 'Do_SomeThing_09', 'Broken underscorify') assert('3'.to_int() == 3, 'String int conversion does not work.') +assert('0x10'.to_int() == 16, 'Hex string conversion does not work.') +assert('0o10'.to_int() == 8, 'Octal string conversion does not work.') +assert('0b10'.to_int() == 2, 'Binary string conversion does not work.') assert(true.to_string() == 'true', 'bool string conversion failed') assert(false.to_string() == 'false', 'bool string conversion failed') From 0d3326610fedb4ae6f005141fdd6aaaed042fad8 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Sat, 2 May 2026 22:02:13 -0700 Subject: [PATCH 305/681] Re-evaluate host machine arch on Xcode backend generate Fixes: #14650 If not set explicitly via a host machine file, Meson's Interpreter will _redetect_machines() at the end of initialization. However, the backend is created before this and therefore the host machine arch may be set to None when the Xcode backend is initialized. Co-authored-by: Shukant Pal Signed-off-by: Christopher Snowhill --- mesonbuild/backend/xcodebackend.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index bc66baf9d3ce..80d8ac5d7605 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -254,9 +254,7 @@ def __init__(self, build: T.Optional[build.Build]): self.regen_dependency_id = self.gen_id() self.top_level_dict = PbxDict() self.generator_outputs = {} - self.arch = self.build.environment.machines.host.cpu - if self.arch == 'aarch64': - self.arch = 'arm64' + self.set_arch() self.xcodeversion, self.objversion = autodetect_xcode_version() # In Xcode files are not accessed via their file names, but rather every one of them # gets an unique id. More precisely they get one unique id per target they are used @@ -310,7 +308,14 @@ def determine_swift_dep_dirs(self, target: build.BuildTarget) -> T.List[str]: result.append(os.path.join(self.environment.get_build_dir(), self.get_target_dir(l))) return result + def set_arch(self): + self.arch = self.build.environment.machines.host.cpu + if self.arch == 'aarch64': + self.arch = 'arm64' + def generate(self, capture: bool = False, vslite_ctx: T.Optional[T.Dict] = None) -> None: + if self.arch is None: # The host machine arch may not be set when backend is created + self.set_arch() # Check for (currently) unexpected capture arg use cases - if capture: raise MesonBugException('We do not expect the xcode backend to generate with \'capture = True\'') From 8fb36fb1fc976af39e94b8e7469f7bfc0f8e8678 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 19 May 2026 12:19:25 -0700 Subject: [PATCH 306/681] backend/vs2010: Add lots of missing annotations This is the really low hanging fruit, just simple and obvious types taht are missing and types that where incorrect. --- mesonbuild/backend/vs2010backend.py | 72 ++++++++++++++--------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 8a5ea95725e2..369770d71dec 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -92,13 +92,13 @@ def split_o_flags_args(args: T.List[str]) -> T.List[str]: o_flags += ['/O' + f for f in flags] return o_flags -def generate_guid_from_path(path, path_type) -> str: +def generate_guid_from_path(path: str | os.PathLike[str], path_type: str) -> str: return str(uuid.uuid5(uuid.NAMESPACE_URL, 'meson-vs-' + path_type + ':' + str(path))).upper() def detect_microsoft_gdk(platform: str) -> bool: - return re.match(r'Gaming\.(Desktop|Xbox.XboxOne|Xbox.Scarlett)\.x64', platform, re.IGNORECASE) + return re.match(r'Gaming\.(Desktop|Xbox.XboxOne|Xbox.Scarlett)\.x64', platform, re.IGNORECASE) is not None -def filtered_src_langs_generator(sources: T.List[str]): +def filtered_src_langs_generator(sources: T.List[str]) -> T.Iterable[Language]: for src in sources: ext = src.split('.')[-1] if compilers.compilers.is_source_suffix(ext): @@ -110,8 +110,8 @@ def filtered_src_langs_generator(sources: T.List[str]): # simply refer to the project's shared intellisense define and include fields, rather than have to fill out their # own duplicate full set of defines/includes/opts intellisense fields. All of which helps keep the vcxproj file # size down. -def get_primary_source_lang(target_sources: T.List[File], custom_sources: T.List[str]) -> T.Optional[str]: - lang_counts = Counter([compilers.compilers.SUFFIX_TO_LANG[src.suffix] for src in target_sources if compilers.compilers.is_source_suffix(src.suffix)]) +def get_primary_source_lang(target_sources: T.List[File], custom_sources: T.List[str]) -> T.Optional[Language]: + lang_counts: Counter[Language] = Counter([compilers.compilers.SUFFIX_TO_LANG[src.suffix] for src in target_sources if compilers.compilers.is_source_suffix(src.suffix)]) lang_counts += Counter(filtered_src_langs_generator(custom_sources)) most_common_lang_list = lang_counts.most_common(1) # It may be possible that we have a target with no actual src files of interest (e.g. a generator target), @@ -147,14 +147,14 @@ def __init__(self, build: T.Optional[build.Build], gen_lite: bool = False): self.platform_toolset = None self.vs_version = '2010' self.windows_target_platform_version = None - self.subdirs = {} + self.subdirs: dict[PurePath, tuple[str, str | None]] = {} self.handled_target_deps = {} self.gen_lite = gen_lite # Synonymous with generating the simpler makefile-style multi-config projects that invoke 'meson compile' builds, avoiding native MSBuild complications def detect_toolset(self) -> None: pass - def get_target_private_dir(self, target): + def get_target_private_dir(self, target: build.BuildTargetTypes) -> str: return os.path.join(self.get_target_dir(target), target.get_id()) def generate_genlist_for_target(self, genlist: build.GeneratedTypes, target: build.BuildTarget, parent_node: ET.Element, generator_output_files: T.List[str], custom_target_include_dirs: T.List[str], custom_target_output_files: T.List[str]) -> None: @@ -234,7 +234,7 @@ def generate_custom_generator_commands(self, target, parent_node): def generate(self, capture: bool = False, - vslite_ctx: T.Optional[T.Dict] = None) -> T.Optional[T.Dict]: + vslite_ctx: T.Optional[T.Dict] = None) -> None: # Check for (currently) unexpected capture arg use cases - if capture: raise MesonBugException('We do not expect any vs backend to generate with \'capture = True\'') @@ -296,7 +296,7 @@ def generate(self, Vs2010Backend.touch_regen_timestamp(self.environment.get_build_dir()) @staticmethod - def get_regen_stampfile(build_dir: str) -> None: + def get_regen_stampfile(build_dir: str) -> str: return os.path.join(os.path.join(build_dir, Environment.private_dir), 'regen.stamp') @staticmethod @@ -304,7 +304,7 @@ def touch_regen_timestamp(build_dir: str) -> None: with open(Vs2010Backend.get_regen_stampfile(build_dir), 'w', encoding='utf-8'): pass - def get_vcvars_command(self): + def get_vcvars_command(self) -> str: has_arch_values = 'VSCMD_ARG_TGT_ARCH' in os.environ and 'VSCMD_ARG_HOST_ARCH' in os.environ # Use vcvarsall.bat if we found it. @@ -592,14 +592,14 @@ def split_sources(self, srclist): headers.append(i) return sources, headers, objects, languages - def target_to_build_root(self, target): + def target_to_build_root(self, target: build.BuildTargetTypes) -> str: if self.get_target_dir(target) == '': return '' directories = os.path.normpath(self.get_target_dir(target)).split(os.sep) return os.sep.join(['..'] * len(directories)) - def quote_arguments(self, arr): + def quote_arguments(self, arr: list[str]) -> list[str]: return ['"%s"' % i for i in arr] def add_project_reference(self, root: ET.Element, include: str, projid: str, link_outputs: bool = False) -> None: @@ -816,7 +816,7 @@ def gen_compile_target_vcxproj(self, target: build.CompileTarget, ofname: str, g self._prettyprint_vcxproj_xml(ET.ElementTree(root), ofname) @classmethod - def lang_from_source_file(cls, src): + def lang_from_source_file(cls, src: mesonlib.FileOrString) -> Language: ext = src.split('.')[-1] if ext in compilers.c_suffixes: return 'c' @@ -959,16 +959,16 @@ def escape_additional_option(option: str) -> str: return f'"{option}"' @staticmethod - def split_link_args(args): + def split_link_args(args: list[str]) -> tuple[list[str], list[str], list[str]]: """ Split a list of link arguments into three lists: * library search paths * library filenames (or paths) * other link arguments """ - lpaths = [] - libs = [] - other = [] + lpaths: list[str] = [] + libs: list[str] = [] + other: list[str] = [] for arg in args: if arg.startswith('/LIBPATH:'): lpath = arg[9:] @@ -991,7 +991,7 @@ def split_link_args(args): other.append(arg) return lpaths, libs, other - def _get_cl_compiler(self, target: build.BuildTarget): + def _get_cl_compiler(self, target: build.BuildTarget) -> compilers.Compiler: for lang, c in target.compilers.items(): if lang in {'c', 'cpp'}: return c @@ -1016,17 +1016,17 @@ def _prettyprint_vcxproj_xml(self, tree: ET.ElementTree, ofname: str) -> None: # Returns: (target_args,file_args), (target_defines,file_defines), (target_inc_dirs,file_inc_dirs) def get_args_defines_and_inc_dirs(self, target: build.BuildTarget, compiler: compilers.Compiler, generated_files_include_dirs, proj_to_src_root, proj_to_src_dir, build_args): # Arguments, include dirs, defines for all files in the current target - target_args = [] - target_defines = [] - target_inc_dirs = [] + target_args: list[str] = [] + target_defines: list[str] = [] + target_inc_dirs: list[str] = [] # Arguments, include dirs, defines passed to individual files in # a target; perhaps because the args are language-specific # # file_args is also later split out into defines and include_dirs in # case someone passed those in there file_args: T.Dict[Language, CompilerArgs] = {l: c.compiler_args() for l, c in target.compilers.items()} - file_defines = {l: [] for l in target.compilers} - file_inc_dirs = {l: [] for l in target.compilers} + file_defines: dict[str, list[str]] = {l: [] for l in target.compilers} + file_inc_dirs: dict[str, list[str]] = {l: [] for l in target.compilers} # The order in which these compile args are added must match # generate_single_compile() and generate_basic_compiler_args() for l, comp in target.compilers.items(): @@ -1155,7 +1155,7 @@ def get_args_defines_and_inc_dirs(self, target: build.BuildTarget, compiler: com return (target_args, file_args), (target_defines, file_defines), (target_inc_dirs, file_inc_dirs) - def get_build_args(self, target: build.BuildTarget, compiler, optimization_level: str, debug: bool, sanitize: str) -> T.List[str]: + def get_build_args(self, target: build.BuildTarget, compiler: compilers.Compiler, optimization_level: str, debug: bool, sanitize: str) -> T.List[str]: build_args = compiler.get_optimization_args(optimization_level) build_args += compiler.get_debug_args(debug) build_args += compiler.sanitizer_compile_args(target, sanitize) @@ -1707,7 +1707,7 @@ def gen_vcxproj(self, target: build.BuildTarget, ofname: str, guid: str, vslite_ # Visual Studio can't load projects that present duplicated items. Filter them out # by keeping track of already added paths. - def path_normalize_add(path, lis): + def path_normalize_add(path: str, lis: T.List[str]) -> bool: normalized = os.path.normcase(os.path.normpath(path)) if normalized not in lis: lis.append(normalized) @@ -1735,7 +1735,7 @@ def path_normalize_add(path, lis): # used with a vs backend pch_sources[lang] = (pch[0], None, lang, None) - previous_includes = [] + previous_includes: list[str] = [] if len(headers) + len(gen_hdrs) + len(target.extra_files) + len(pch_sources) > 0: if self.gen_lite and gen_hdrs: # Although we're constructing our .vcxproj under our '..._vs' directory, we want to reference generated files @@ -1759,7 +1759,7 @@ def path_normalize_add(path, lis): if path_normalize_add(path, previous_includes): ET.SubElement(inc_hdrs, 'CLInclude', Include=path) - previous_sources = [] + previous_sources: list[str] = [] if len(sources) + len(gen_src) + len(pch_sources) > 0: if self.gen_lite: # Get data to fill in intellisense fields for sources that can't reference the project-wide values @@ -1841,7 +1841,7 @@ def path_normalize_add(path, lis): # but the others need to be included explicitly explicit_link_gen_objs = [obj for obj in gen_objs if not obj.endswith(('.obj', '.res'))] - previous_objects = [] + previous_objects: list[str] = [] if len(objects) + len(additional_objects) + len(explicit_link_gen_objs) > 0: inc_objs = ET.SubElement(root, 'ItemGroup') for s in objects: @@ -1867,7 +1867,7 @@ def path_normalize_add(path, lis): self.gen_vcxproj_filters(target, ofname) return True - def gen_vcxproj_filters(self, target, ofname): + def gen_vcxproj_filters(self, target, ofname) -> None: # Generate pitchfork of filters based on directory structure. root = ET.Element('Project', {'ToolsVersion': '4.0', 'xmlns': 'http://schemas.microsoft.com/developer/msbuild/2003'}) @@ -1875,7 +1875,7 @@ def gen_vcxproj_filters(self, target, ofname): filter_items = ET.SubElement(root, 'ItemGroup') mlog.debug(f'Generating vcxproj filters {target.name}.') - def relative_to_defined_in(file): + def relative_to_defined_in(file) -> str: # Get the relative path to file's directory from the location of the meson.build that defines this target. return os.path.dirname(self.relpath(PureWindowsPath(file.subdir, file.fname), self.get_target_dir(target))) @@ -1916,7 +1916,7 @@ def relative_to_defined_in(file): sources, headers, objects, _ = self.split_sources(all_files) down = self.target_to_build_root(target) - def add_element(type_name, elements): + def add_element(type_name: str, elements: list[ET.Element]) -> None: for i in elements: if not os.path.isabs(i.fname): dirname = relative_to_defined_in(i) @@ -1933,7 +1933,7 @@ def add_element(type_name, elements): self._prettyprint_vcxproj_xml(ET.ElementTree(root), ofname + '.filters') - def gen_regenproj(self): + def gen_regenproj(self) -> None: # To fully adapt the REGEN work for a 'genvslite' solution, to check timestamps, settings, and regenerate the # '[builddir]_vs' solution/vcxprojs, as well as regenerating the accompanying buildtype-suffixed ninja build # directories (from which we need to first collect correct, updated preprocessor defs and compiler options in @@ -2013,7 +2013,7 @@ def gen_regenproj(self): ET.SubElement(root, 'ImportGroup', Label='ExtensionTargets') self._prettyprint_vcxproj_xml(ET.ElementTree(root), ofname) - def gen_testproj(self): + def gen_testproj(self) -> None: project_name = 'RUN_TESTS' ofname = os.path.join(self.environment.get_build_dir(), f'{project_name}.vcxproj') guid = self.environment.coredata.test_guid @@ -2067,7 +2067,7 @@ def gen_testproj(self): self.add_regen_dependency(root) self._prettyprint_vcxproj_xml(ET.ElementTree(root), ofname) - def gen_installproj(self): + def gen_installproj(self) -> None: project_name = 'RUN_INSTALL' ofname = os.path.join(self.environment.get_build_dir(), f'{project_name}.vcxproj') guid = self.environment.coredata.install_guid @@ -2167,7 +2167,7 @@ def generate_lang_standard_info(self, file_args: T.Dict[str, CompilerArgs], clco # Returns if a target generates a manifest or not. # Returns 'embed' if the generated manifest is embedded. - def get_gen_manifest(self, target: T.Optional[build.BuildTarget]): + def get_gen_manifest(self, target: T.Optional[build.BuildTarget]) -> bool | T.Literal['embed']: if not isinstance(target, build.BuildTarget): return True @@ -2191,7 +2191,7 @@ def get_gen_manifest(self, target: T.Optional[build.BuildTarget]): # and arm64ec+masm (written in x64 assembly). # # For now, assume it's the native ones. (same behavior as ninja backend) - def get_masm_type(self, target: build.BuildTarget): + def get_masm_type(self, target: build.BuildTarget) -> T.Literal['marmasm', 'masm'] | None: if not isinstance(target, build.BuildTarget): return None From c1c980397893619c6a93162ba650d3e4602576f4 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 21 May 2026 10:52:40 -0700 Subject: [PATCH 307/681] interpreter: Use a specific sublcass of InterpreterException for Sandbox violations This will be used to catch them later, as well as simpifying raising them --- mesonbuild/interpreter/interpreter.py | 28 +++++++++++++++++-- .../failing/56 subproj filegrab/test.json | 2 +- test cases/failing/58 grab sibling/test.json | 2 +- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index e145b3541281..935201cba1ae 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -5,6 +5,7 @@ from __future__ import annotations import io, sys, traceback +import dataclasses from .. import mparser from .. import environment @@ -148,6 +149,29 @@ def _project_version_validator(value: T.Union[T.List, str, mesonlib.File, None]) return 'when passed as array must contain a File' return None + +@dataclasses.dataclass +class SandboxViolationError(InterpreterException): + + """Exception raised when trying to use files outside of this project + + :param inputtype: The kind of thing, usually "directory" or "file" + :param name: The name of the thing being used + :param subproject: Is this an attempt to use a file from inside a subproject? + """ + + inputtype: str + name: str + subproject: bool = dataclasses.field(default=True, kw_only=True) + + def __str__(self) -> str: + if self.subproject: + msg = 'a nested subproject' + else: + msg = 'outside current (sub)project' + return f'Sandbox violation: Tried to grab {self.inputtype} {self.name} from {msg}.' + + class Summary: def __init__(self, project_name: str, project_version: str): self.project_name = project_name @@ -3162,12 +3186,12 @@ def do_validate_within_subproject(norm: str) -> None: project_root = os.path.join(srcdir, self.root_subdir) if not is_parent_path(project_root, norm): name = os.path.basename(norm) - raise InterpreterException(f'Sandbox violation: Tried to grab {inputtype} {name} outside current (sub)project.') + raise SandboxViolationError(inputtype, name, subproject=False) subproject_dir = os.path.join(project_root, self.subproject_dir) if is_parent_path(subproject_dir, norm): name = os.path.basename(norm) - raise InterpreterException(f'Sandbox violation: Tried to grab {inputtype} {name} from a nested subproject.') + raise SandboxViolationError(inputtype, name) fname = os.path.join(subdir, fname) if fname in self.validated_cache: diff --git a/test cases/failing/56 subproj filegrab/test.json b/test cases/failing/56 subproj filegrab/test.json index cdd102955edd..f911fc317949 100644 --- a/test cases/failing/56 subproj filegrab/test.json +++ b/test cases/failing/56 subproj filegrab/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/56 subproj filegrab/subprojects/a/meson.build:3:0: ERROR: Sandbox violation: Tried to grab file prog.c outside current (sub)project." + "line": "test cases/failing/56 subproj filegrab/subprojects/a/meson.build:3:0: ERROR: Sandbox violation: Tried to grab file prog.c from outside current (sub)project." } ] } diff --git a/test cases/failing/58 grab sibling/test.json b/test cases/failing/58 grab sibling/test.json index cbf8b2ddcad0..f677ccc3f235 100644 --- a/test cases/failing/58 grab sibling/test.json +++ b/test cases/failing/58 grab sibling/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/58 grab sibling/subprojects/a/meson.build:3:0: ERROR: Sandbox violation: Tried to grab file sneaky.c outside current (sub)project." + "line": "test cases/failing/58 grab sibling/subprojects/a/meson.build:3:0: ERROR: Sandbox violation: Tried to grab file sneaky.c from outside current (sub)project." } ] } From 7851563c0b6a1351d999e873618e0bd8e366fe53 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 21 May 2026 11:37:56 -0700 Subject: [PATCH 308/681] interpreter: remove source_strings_to_files strict argument It turns out that the only thing that was actually happening when the strict argument was set to true was that a warning wasn't being emitted, but was still added to the sources list. This can be seen by removing the relaxation introduced in this patch and running the "14 configure_file" test, which will error when the output of a configure file is passed by string name instead of File reference. I've made the decision to not play wack-a-mole with this, and just remove strict. Every file in the build directory will now raise a warning about using string names. This way, when we get to Meson 2.0, we can just delete the code that allows this completely and wack all the moles at the same time. --- mesonbuild/interpreter/interpreter.py | 50 ++++++++++++++++++--------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 935201cba1ae..6e79542579c7 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -172,6 +172,16 @@ def __str__(self) -> str: return f'Sandbox violation: Tried to grab {self.inputtype} {self.name} from {msg}.' +@dataclasses.dataclass +class BuiltFileByNameError(InterpreterException): + + name: str + + def __str__(self) -> str: + return (f'{self.name} is a generated file, and should be passed by reference instead of string name. ' + 'This will become a hard error in Meson 2.0') + + class Summary: def __init__(self, project_name: str, project_version: str): self.project_name = project_name @@ -2119,7 +2129,7 @@ def func_custom_target(self, node: mparser.FunctionNode, args: T.Tuple[str], # string in the meantime. FeatureNew.single_use('custom_target() with no name argument', '0.60.0', self.subproject, location=node) name = '' - inputs = self.source_strings_to_files(kwargs['input'], strict=False) + inputs = self.source_strings_to_files(kwargs['input']) command = kwargs['command'] if command and isinstance(command[0], str): command[0] = self.find_program_impl([command[0]]) @@ -3173,8 +3183,8 @@ def do_validate_within_subproject(norm: str) -> None: inputtype = 'directory' else: inputtype = 'file' - if InterpreterRuleRelaxation.ALLOW_BUILD_DIR_FILE_REFERENCES in self.relaxations and is_parent_path(builddir, norm): - return + if is_parent_path(builddir, norm): + raise BuiltFileByNameError(fname) if not is_parent_path(srcdir, norm): # Grabbing files outside the source tree is ok. @@ -3202,10 +3212,7 @@ def do_validate_within_subproject(norm: str) -> None: self.validated_cache.add(fname) @T.overload - def source_strings_to_files(self, sources: T.List['mesonlib.FileOrString'], strict: bool = True) -> T.List['mesonlib.File']: ... - - @T.overload - def source_strings_to_files(self, sources: T.List['mesonlib.FileOrString'], strict: bool = False) -> T.List['mesonlib.FileOrString']: ... # noqa: F811 + def source_strings_to_files(self, sources: T.List['mesonlib.FileOrString']) -> T.List['mesonlib.File']: ... @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]]: ... @@ -3217,20 +3224,22 @@ def source_strings_to_files(self, sources: T.List[T.Union[mesonlib.FileOrString, def source_strings_to_files(self, sources: T.List[kwtypes.CustomTargetInputs]) -> T.List['CustomTargetSources']: ... # noqa: F811 @T.overload - def source_strings_to_files(self, sources: T.List[T.Union[mesonlib.FileOrString, build.BuildTargetTypes, build.BothLibraries, build.ExtractedObjects, build.GeneratedTypes]] + def source_strings_to_files(self, sources: T.List[T.Union[mesonlib.FileOrString, build.BuildTargetTypes, build.BothLibraries, build.ExtractedObjects, build.GeneratedTypes]], + strict: bool = True ) -> T.List[T.Union[mesonlib.File, build.BuildTargetTypes, build.BothLibraries, build.ExtractedObjects, build.GeneratedTypes]]: ... # noqa: F811 @T.overload - def source_strings_to_files(self, sources: T.List[T.Union[mesonlib.FileOrString, build.GeneratedTypes, build.StructuredSources]] + def source_strings_to_files(self, sources: T.List[T.Union[mesonlib.FileOrString, build.GeneratedTypes, build.StructuredSources]], + strict: bool = True, ) -> 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 + def source_strings_to_files(self, sources: T.List['SourceInputs']) -> T.List['SourceOutputs']: ... # noqa: F811 @T.overload - def source_strings_to_files(self, sources: T.List[SourcesVarargsType], strict: bool = True) -> T.List['SourceOutputs']: ... # noqa: F811 + def source_strings_to_files(self, sources: T.List[SourcesVarargsType]) -> T.List['SourceOutputs']: ... # noqa: F811 - def source_strings_to_files(self, sources: T.List['SourceInputs'], strict: bool = True) -> T.List['SourceOutputs']: # noqa: F811 + def source_strings_to_files(self, sources: T.List['SourceInputs']) -> T.List['SourceOutputs']: # noqa: F811 """Lower inputs to a list of Targets and Files, replacing any strings. :param sources: A raw (Meson DSL) list of inputs (targets, files, and @@ -3242,16 +3251,23 @@ def source_strings_to_files(self, sources: T.List['SourceInputs'], strict: bool if not isinstance(sources, list): sources = [sources] results: T.List['SourceOutputs'] = [] + + # In Meson 2.0 this shoul not add ALLOW_BUILD_DIR_FILE_REFERENCES + # universally, and we should just use self.relaxations + relaxations: set[InterpreterRuleRelaxation] = self.relaxations | {InterpreterRuleRelaxation.ALLOW_BUILD_DIR_FILE_REFERENCES} + for s in sources: if isinstance(s, str): if s.endswith(' '): raise MesonException(f'{s!r} ends with a space. This is probably an error.') - if not strict and s.startswith(self.environment.get_build_dir()): - results.append(s) - mlog.warning(f'Source item {s!r} cannot be converted to File object, because it is a generated file. ' - 'This will become a hard error in meson 2.0.', location=self.current_node) - else: + try: self.validate_within_subproject(self.subdir, s) + except BuiltFileByNameError as e: + if InterpreterRuleRelaxation.ALLOW_BUILD_DIR_FILE_REFERENCES not in relaxations: + raise + mlog.warning(str(e), location=self.current_node) + results.append(mesonlib.File.from_built_file(self.subdir, s)) + else: results.append(mesonlib.File.from_source_file(self.environment.source_dir, self.subdir, s)) elif isinstance(s, (mesonlib.File, ExternalProgram, build.GeneratedList, build.BuildTarget, From 74ce1f5adb6919dd9562f8e43f7b6557c14b2f7b Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 19 May 2026 09:06:30 -0700 Subject: [PATCH 309/681] interpreter: don't use hasattr for windows only path Explicitly check for windows make the type checker able to understand this code better. --- mesonbuild/interpreter/interpreter.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 6e79542579c7..6ed3db6c3c26 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -559,7 +559,9 @@ def add_build_def_file(self, f: mesonlib.FileOrString) -> None: except OSError: f_ = Path(f) s = f_.stat() - if (hasattr(s, 'st_file_attributes') and + # Mypy needs the `sys.platform` check to understand that this is + # a Windows only path + if (sys.platform == 'win32' and s.st_file_attributes & stat.FILE_ATTRIBUTE_REPARSE_POINT != 0 and s.st_reparse_tag == stat.IO_REPARSE_TAG_APPEXECLINK): # This is a Windows Store link which we can't From 327a137bbb7378411c23ab7f35babd9574827f47 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 19 May 2026 09:07:58 -0700 Subject: [PATCH 310/681] interpreterbase: Fix signature of handle_meson_version_from_ast It has an unused strict parameter that the Interpreter doesn't --- mesonbuild/interpreterbase/interpreterbase.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/interpreterbase/interpreterbase.py b/mesonbuild/interpreterbase/interpreterbase.py index 13de57039325..293c6b5fdebe 100644 --- a/mesonbuild/interpreterbase/interpreterbase.py +++ b/mesonbuild/interpreterbase/interpreterbase.py @@ -98,7 +98,7 @@ def __init__(self, source_root: str, subdir: str, subproject: SubProject, subpro # current meson version target within that if-block. self.tmp_meson_version: T.Optional[str] = None - def handle_meson_version_from_ast(self, strict: bool = True) -> None: + def handle_meson_version_from_ast(self) -> None: # do nothing in an AST interpreter return From 54653c92fcff4e961667ba8c8326cc0bdb6b949b Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 19 May 2026 09:11:02 -0700 Subject: [PATCH 311/681] interpreter: add more asserts for option values --- mesonbuild/interpreter/interpreter.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 6ed3db6c3c26..a4a960ba769d 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1126,6 +1126,7 @@ def func_get_option(self, node: mparser.BaseNode, args: T.Tuple[str], # likely good enough for most usecases. if not value: return 'none' + assert isinstance(value, list), 'for mypy' return ','.join(sorted(value)) if isinstance(value, str): From a8bc7427e052144442e9c737da3b57621fbcbe63 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 19 May 2026 09:18:57 -0700 Subject: [PATCH 312/681] interpreter/kwargs: Fix type annotations for project.default_options --- 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 54731b738025..d66637a7ff97 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -240,7 +240,7 @@ class Project(TypedDict): version: T.Optional[FileOrString] meson_version: T.Optional[str] - default_options: T.List[str] + default_options: options.OptionDict license: T.List[str] license_files: T.List[str] subproject_dir: str From cf984a085f49a256e204276504a166445a7f1e19 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 19 May 2026 09:32:53 -0700 Subject: [PATCH 313/681] options: Fix annotations of options from command lines Using generics helps us to deal with the fact that you can get two different types. --- mesonbuild/options.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index a2a0195ff91e..c16e28c61967 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -1231,9 +1231,9 @@ def is_compiler_option(self, key: OptionKey) -> bool: def is_module_option(self, key: OptionKey) -> bool: return key in self.module_options - def prefix_split_options(self, coll: OptionDict) -> T.Tuple[T.Optional[str], OptionDict]: + def prefix_split_options(self, coll: dict[OptionKey, _T]) -> T.Tuple[str | None, dict[OptionKey, _T]]: prefix = None - others_d: OptionDict = {} + others_d: dict[OptionKey, _T] = {} for k, v in coll.items(): if k.name == 'prefix': if not isinstance(v, str): @@ -1245,9 +1245,9 @@ def prefix_split_options(self, coll: OptionDict) -> T.Tuple[T.Optional[str], Opt def first_handle_prefix(self, project_default_options: OptionDict, - cmd_line_options: OptionDict, + cmd_line_options: dict[OptionKey, str | None], machine_file_options: OptionDict) \ - -> T.Tuple[OptionDict, OptionDict, OptionDict]: + -> T.Tuple[OptionDict, dict[OptionKey, str | None], OptionDict]: # Copy to avoid later mutation nopref_machine_file_options = copy.copy(machine_file_options) @@ -1281,7 +1281,7 @@ def hard_reset_from_prefix(self, prefix: str) -> None: def initialize_from_top_level_project_call(self, project_default_options_in: OptionDict, - cmd_line_options_in: OptionDict, + cmd_line_options_in: dict[OptionKey, str | None], machine_file_options_in: OptionDict) -> None: (project_default_options, cmd_line_options, machine_file_options) = self.first_handle_prefix(project_default_options_in, cmd_line_options_in, @@ -1325,7 +1325,7 @@ def initialize_from_subproject_call(self, subproject: str, spcall_default_options: OptionDict, project_default_options: OptionDict, - cmd_line_options: OptionDict, + cmd_line_options: dict[OptionKey, str | None], machine_file_options: OptionDict) -> None: options: OptionDict = {} From 485221e537de081358aebca5e57c51a373e0bb20 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 19 May 2026 09:36:58 -0700 Subject: [PATCH 314/681] interpreter: convert 'native' for add_langauges in typed_kwargs This eliminates an untyped helper method --- mesonbuild/interpreter/interpreter.py | 20 +++++++++++--------- mesonbuild/interpreter/kwargs.py | 2 +- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index a4a960ba769d..48a398d65b59 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1335,7 +1335,16 @@ def func_project(self, node: mparser.FunctionNode, args: T.Tuple[str, T.List[str if not self.is_subproject(): self.check_stdlibs() - @typed_kwargs('add_languages', KwargInfo('native', (bool, NoneType), since='0.54.0'), REQUIRED_KW) + @typed_kwargs( + 'add_languages', + KwargInfo( + 'native', + (bool, NoneType), + since='0.54.0', + convertor=lambda x: {None: None, True: MachineChoice.BUILD, False: MachineChoice.HOST}[x], + ), + REQUIRED_KW, + ) @typed_pos_args('add_languages', varargs=str) def func_add_languages(self, node: mparser.FunctionNode, args: T.Tuple[T.List[str]], kwargs: 'kwtypes.FuncAddLanguages') -> bool: disabled, required, feature = extract_required_kwarg(kwargs, self.subproject) @@ -1348,7 +1357,7 @@ def func_add_languages(self, node: mparser.FunctionNode, args: T.Tuple[T.List[st mlog.log('Compiler for language', mlog.bold(lang), 'skipped: feature', mlog.bold(feature), 'disabled') return False if native is not None: - return self.add_languages(langs, required, self.machine_from_native_kwarg(kwargs)) + return self.add_languages(langs, required, native) else: # absent 'native' means 'both' for backwards compatibility tv = FeatureNew.get_target_version(self.subproject) @@ -3933,13 +3942,6 @@ def func_unset_variable(self, node: mparser.BaseNode, args: T.Tuple[str], kwargs except KeyError: raise InterpreterException(f'Tried to unset unknown variable "{varname}".') - @staticmethod - def machine_from_native_kwarg(kwargs: T.Dict[str, T.Any]) -> MachineChoice: - native = kwargs.get('native', False) - if not isinstance(native, bool): - raise InvalidArguments('Argument to "native" must be a boolean.') - return MachineChoice.BUILD if native else MachineChoice.HOST - @FeatureNew('is_disabler', '0.52.0') @typed_pos_args('is_disabler', object) @noKwargs diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index d66637a7ff97..a9260fe2257b 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -196,7 +196,7 @@ class FuncIncludeDirectories(TypedDict): class FuncAddLanguages(ExtractRequired): - native: T.Optional[bool] + native: MachineChoice | None class RunTarget(TypedDict): From a82c56c167eb9a5f7954312c3313a7d5cc2126f5 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 19 May 2026 09:44:37 -0700 Subject: [PATCH 315/681] interpreter: fix typing issues with func_summary and helpers --- mesonbuild/interpreter/interpreter.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 48a398d65b59..d535d49b52b9 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -189,7 +189,7 @@ def __init__(self, project_name: str, project_version: str): self.sections: collections.defaultdict[str, dict[str, tuple[mlog.TV_LoggableList, str | None]]] = collections.defaultdict(dict) self.max_key_len = 0 - def add_section(self, section: str, values: T.Dict[str, T.Any], bool_yn: bool, + def add_section(self, section: str, values: T.Dict[str, mlog.TV_Loggable | mlog.TV_LoggableList], bool_yn: bool, list_sep: T.Optional[str], subproject: SubProject) -> None: for k, v in values.items(): if k in self.sections[section]: @@ -1409,7 +1409,7 @@ def func_summary(self, node: mparser.BaseNode, args: T.Tuple[T.Union[str, T.Dict values = {args[0]: args[1]} self.summary_impl(values=values, **kwargs) - def summary_impl(self, section: str, values: dict[str, T.Any], bool_yn: bool, list_sep: str | None) -> None: + def summary_impl(self, section: str, values: dict[str, mlog.TV_Loggable | mlog.TV_LoggableList], bool_yn: bool, list_sep: str | None) -> None: if self.subproject not in self.summary: self.summary[self.subproject] = Summary(self.active_projectname, self.project_version) self.summary[self.subproject].add_section( @@ -1417,9 +1417,9 @@ def summary_impl(self, section: str, values: dict[str, T.Any], bool_yn: bool, li def _print_summary(self) -> None: # Add automatic 'Subprojects' section in main project. - all_subprojects = collections.OrderedDict() + all_subprojects: dict[str, mlog.TV_Loggable | mlog.TV_LoggableList] = {} for name, subp in sorted(self.subprojects.items()): - value = [subp.found()] + value: mlog.TV_LoggableList = [subp.found()] if subp.disabled_feature: value += [f'Feature {subp.disabled_feature!r} disabled'] elif subp.exception: @@ -1434,19 +1434,13 @@ def _print_summary(self) -> None: self.summary_impl('Subprojects', all_subprojects, bool_yn=True, list_sep=' ') # Add automatic section with all user defined options if self.user_defined_options: - values = collections.OrderedDict() + values: dict[str, mlog.TV_Loggable | mlog.TV_LoggableList] = {} if self.user_defined_options.cross_file: values['Cross files'] = self.user_defined_options.cross_file if self.user_defined_options.native_file: values['Native files'] = self.user_defined_options.native_file - def compatibility_sort_helper(s): - if isinstance(s, tuple): - s = s[0] - if isinstance(s, str): - return s - return s.name - sorted_options = sorted(self.user_defined_options.cmd_line_options.items(), key=compatibility_sort_helper) + sorted_options = sorted(self.user_defined_options.cmd_line_options.items(), key=lambda x: x[0].name) values.update({str(k): v for k, v in sorted_options}) if values: self.summary_impl('User defined options', values, bool_yn=False, list_sep=None) From e2c9d8cf334957deea56dea70b05783c57502857 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 19 May 2026 09:47:50 -0700 Subject: [PATCH 316/681] interpreter: fix _stingifiy_user_arguments type It was convenient to return TV_LoggableList, but it's not correct. We just have to have more unions to avoid the Sequence[str] problem. --- mesonbuild/interpreter/interpreter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index d535d49b52b9..81f61be89bc8 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1371,7 +1371,7 @@ def func_add_languages(self, node: mparser.FunctionNode, args: T.Tuple[T.List[st success &= self.add_languages(langs, False, MachineChoice.BUILD) return success - def _stringify_user_arguments(self, args: T.List[TYPE_var], func_name: str) -> mlog.TV_LoggableList: + def _stringify_user_arguments(self, args: T.List[TYPE_var], func_name: str) -> list[str]: try: return [stringifyUserArguments(i, self.subproject) for i in args] except InvalidArguments as e: @@ -1385,7 +1385,7 @@ def func_message(self, node: mparser.BaseNode, args: list[TYPE_var], kwargs: TYP args_str = self._stringify_user_arguments(args, 'message') self.message_impl(args_str) - def message_impl(self, args: mlog.TV_LoggableList) -> None: + def message_impl(self, args: mlog.TV_LoggableList | list[str]) -> None: mlog.log(mlog.bold('Message:'), *args) @noArgsFlattening From 78226ebbdd1283f1cbcce893bd48a03b9bf2c5e8 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 19 May 2026 09:54:09 -0700 Subject: [PATCH 317/681] interpreter: fix all type issues with func_expect_error --- mesonbuild/interpreter/interpreter.py | 8 +++++--- mesonbuild/interpreter/kwargs.py | 5 +++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 81f61be89bc8..27cd27cc9c43 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -114,6 +114,7 @@ if T.TYPE_CHECKING: from typing_extensions import Literal + import types from .. import cargo from . import kwargs as kwtypes @@ -1487,17 +1488,17 @@ def func_exception(self, node: mparser.BaseNode, args: list[TYPE_var], kwargs: T 'expect_error', KwargInfo('how', str, default='literal', validator=in_set_validator({'literal', 're'})), ) - def func_expect_error(self, node: mparser.BaseNode, args: T.Tuple[str], kwargs: TYPE_kwargs) -> ContextManagerObject: + def func_expect_error(self, node: mparser.BaseNode, args: T.Tuple[str], kwargs: kwtypes.FuncExpectError) -> ContextManagerObject: class ExpectErrorObject(ContextManagerObject): def __init__(self, msg: str, how: str, subproject: SubProject) -> None: super().__init__(subproject) self.old_stdout = sys.stdout sys.stdout = self.new_stdout = io.StringIO() - sys.stdout.colorize_console = getattr(self.old_stdout, 'colorize_console', None) + sys.stdout.colorize_console = getattr(self.old_stdout, 'colorize_console', None) # type: ignore[attr-defined] self.msg = msg self.how = how - def __exit__(self, exc_type, exc_val, exc_tb): + def __exit__(self, exc_type: type[BaseException], exc_val: BaseException, exc_tb: types.TracebackType) -> bool: sys.stdout = self.old_stdout for l in self.new_stdout.getvalue().splitlines(): if 'ERROR:' in l: @@ -1512,6 +1513,7 @@ def __exit__(self, exc_type, exc_val, exc_tb): (self.how == 're' and not re.match(self.msg, msg)): raise InterpreterException(f'Expecting error {self.msg!r} but got {msg!r}') return True + return False return ExpectErrorObject(args[0], kwargs['how'], self.subproject) def add_languages(self, args: T.List[Language], required: bool, for_machine: MachineChoice) -> bool: diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index a9260fe2257b..89c7ca9f581a 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -560,3 +560,8 @@ class FuncDependency(ExtractRequired): private_headers: bool static: T.Optional[bool] version: T.List[str] + + +class FuncExpectError(TypedDict): + + how: str From c5634dc34e927d2ef883c488af233203871e4fbb Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 19 May 2026 09:55:09 -0700 Subject: [PATCH 318/681] interpreter: fix return type of func_configure_file File is not in build, it's in mesonlib --- mesonbuild/interpreter/interpreter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 27cd27cc9c43..14e79d34d609 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -2696,7 +2696,7 @@ def validate_build_subdir(self, build_subdir: str, target: str) -> None: KwargInfo('build_subdir', str, default='', since='1.10.0'), ) def func_configure_file(self, node: mparser.BaseNode, args: T.List[TYPE_var], - kwargs: kwtypes.ConfigureFile) -> build.File: + kwargs: kwtypes.ConfigureFile) -> mesonlib.File: actions = sorted(x for x in ['configuration', 'command', 'copy'] if kwargs[x] not in [None, False]) num_actions = len(actions) From 8cab0d686326a132d8683a49b06951c83caf2f16 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 19 May 2026 10:12:22 -0700 Subject: [PATCH 319/681] interpreter: fix annotations of some of the build_target dispatch functions --- mesonbuild/interpreter/interpreter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 14e79d34d609..56e52dc10243 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1936,7 +1936,7 @@ def func_shared_module(self, node: mparser.BaseNode, @noSecondLevelHolderResolving def func_library(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargsType], - kwargs: kwtypes.Library) -> build.Executable: + kwargs: kwtypes.Library) -> build.StaticLibrary | build.SharedLibrary | build.BothLibraries: return self.build_library(node, args, kwargs) @typed_pos_args('jar', str, varargs=(str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList, build.ExtractedObjects, build.BuildTarget)) @@ -1944,7 +1944,7 @@ def func_library(self, node: mparser.BaseNode, def func_jar(self, node: mparser.BaseNode, args: T.Tuple[str, T.List[T.Union[str, mesonlib.File, build.GeneratedTypes]]], kwargs: kwtypes.Jar) -> build.Jar: - return self.build_target(node, args, kwargs, build.Jar) + return self.build_target(node, T.cast('tuple[str, SourcesVarargsType]', args), kwargs, build.Jar) @FeatureNewKwargs('build_target', '0.40.0', ['link_whole', 'override_options']) @typed_pos_args('build_target', str, varargs=SOURCES_VARARGS) From f0e1bec44fe23800daaa3f56ed90e471878f34a9 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 19 May 2026 10:14:20 -0700 Subject: [PATCH 320/681] interpreter/kwargs: fix command annotations for VcsTag --- 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 89c7ca9f581a..79c656c79be9 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -312,7 +312,7 @@ class ConfigurationDataSet(TypedDict): class VcsTag(TypedDict): - command: T.List[T.Union[str, build.GeneratedTypes, Program, File]] + command: T.List[T.Union[str, build.BuildTargetTypes, Program, File]] fallback: T.Optional[str] input: T.List[T.Union[str, build.BuildTarget, build.GeneratedTypes, build.ExtractedObjects, Program, File]] From bf8d012639fef1d963d60776739ad63ea4bf4c9a Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 19 May 2026 10:15:42 -0700 Subject: [PATCH 321/681] interpreter: fix typing issues in func_build_target By adding Executable arguments to to the BuildTarget TypedDict, we can get the executable one working. Unfortunately, we can't fix Jar because Jar isn't really a BuildTarget, and using this path is deprecated anyway because of that. --- mesonbuild/interpreter/interpreter.py | 4 +++- mesonbuild/interpreter/kwargs.py | 28 +++++++++++++++++---------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 56e52dc10243..64d9092f154f 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1968,7 +1968,9 @@ def func_build_target(self, node: mparser.BaseNode, return self.build_both_libraries(node, args, kwargs) elif target_type == 'library': return self.build_library(node, args, kwargs) - return self.build_target(node, args, kwargs, build.Jar) + # We can't avoid the cast here because of the mis-matched sources types + # between Jar and BuildTarget types + return self.build_target(node, args, T.cast('kwtypes.Jar', kwargs), build.Jar) @noPosargs @typed_kwargs( diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index 79c656c79be9..595b2c11554b 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -436,7 +436,7 @@ class _LibraryMixin(TypedDict): rust_abi: T.Optional[RustAbi] -class Executable(BuildTarget): +class _ExecutableMixin(TypedDict): export_dynamic: T.Optional[bool] gui_app: T.Optional[bool] @@ -447,6 +447,10 @@ class Executable(BuildTarget): android_exe_type: T.Optional[Literal['application', 'executable']] +class Executable(BuildTarget, _ExecutableMixin): + pass + + class _StaticLibMixin(TypedDict): prelink: bool @@ -462,12 +466,12 @@ class _SharedLibMixin(TypedDict): darwin_versions: T.Optional[T.Tuple[str, str]] 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): - pass + + vs_module_defs: T.Optional[T.Union[str, File, build.CustomTarget, build.CustomTargetIndex]] class SharedModule(BuildTarget, _LibraryMixin): @@ -509,18 +513,22 @@ class Library(BuildTarget, _SharedLibMixin, _StaticLibMixin, _LibraryMixin): masm_shared_args: NotRequired[T.List[str]] -class BuildTargetFunc(Library): +class _JarMixin(TypedDict): - target_type: Literal['executable', 'shared_library', 'static_library', - 'shared_module', 'both_libraries', 'library', 'jar'] + main_class: str + java_resources: T.Optional[build.StructuredSources] + java_args: T.List[str] -class Jar(_BaseBuildTarget): +class Jar(_BaseBuildTarget, _JarMixin): - main_class: str - java_resources: T.Optional[build.StructuredSources] sources: T.Union[str, File, build.GeneratedTypes, build.ExtractedObjects, build.BuildTarget] - java_args: T.List[str] + + +class BuildTargetFunc(Library, _ExecutableMixin, _JarMixin): + + target_type: Literal['executable', 'shared_library', 'static_library', + 'shared_module', 'both_libraries', 'library', 'jar'] class FuncDeclareDependency(TypedDict): From b2cc152724bd25069b8a6d2267ae9a05b03ba879 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 19 May 2026 10:19:11 -0700 Subject: [PATCH 322/681] interpreter: fix type assignment of func_structured_sources --- mesonbuild/interpreter/interpreter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 64d9092f154f..72a21dfb8f64 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -2466,7 +2466,7 @@ def func_structured_sources( args: T.Tuple[object, T.Optional[T.Dict[str, object]]], kwargs: 'TYPE_kwargs') -> build.StructuredSources: valid_types = (str, mesonlib.File, build.GeneratedList, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList) - sources: T.Dict[str, T.List[T.Union[mesonlib.File, 'build.GeneratedTypes']]] = collections.defaultdict(list) + sources: T.DefaultDict[str, T.List[T.Union[mesonlib.File, 'build.GeneratedTypes']]] = collections.defaultdict(list) for arg in mesonlib.listify(args[0]): if not isinstance(arg, valid_types): From 6928387346aa81e1a8ec2e2d820c530c29b5b08a Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 19 May 2026 10:20:25 -0700 Subject: [PATCH 323/681] interpreter/kwargs: fix RunTarget.command annotations --- 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 595b2c11554b..ec03b12b079a 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -200,7 +200,7 @@ class FuncAddLanguages(ExtractRequired): class RunTarget(TypedDict): - command: T.List[T.Union[str, build.BuildTargetTypes, ExternalProgram, File]] + command: T.List[T.Union[str, build.BuildTargetTypes, Program, File]] depends: T.List[TargetDepends] env: EnvironmentVariables From 4542902608bc0bc11a09e479c8e0348f22d3211c Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 19 May 2026 10:23:12 -0700 Subject: [PATCH 324/681] build: Fix AliasTarget dependency type An alias can depend on any Target, not just built targets --- mesonbuild/build.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index f34db6489d96..49ba737ae0b5 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -3294,7 +3294,7 @@ class RunTarget(Target): def __init__(self, name: str, command: T.Sequence[T.Union[str, File, BuildTargetTypes, programs.Program]], # the RunTarget case is used by gnome.yelp() - dependencies: T.Sequence[T.Union[RunTarget, TargetDepends]], + dependencies: T.Sequence[Target | CustomTargetIndex | GeneratedList | programs.Program], subdir: str, subproject: SubProject, environment: Environment, @@ -3313,7 +3313,7 @@ def __repr__(self) -> str: repr_str = "<{0} {1}: {2}>" return repr_str.format(self.__class__.__name__, self.get_id(), self.command[0]) - def get_dependencies(self) -> T.List[T.Union[RunTarget, TargetDepends]]: + def get_dependencies(self) -> T.List[Target | CustomTargetIndex | GeneratedList | programs.Program]: return self.dependencies def get_generated_sources(self) -> T.List[GeneratedTypes]: @@ -3343,7 +3343,7 @@ class AliasTarget(RunTarget): typename = 'alias' - def __init__(self, name: str, dependencies: T.Sequence[TargetDepends | RunTarget], + def __init__(self, name: str, dependencies: T.Sequence[Target], subdir: str, subproject: SubProject, environment: Environment): super().__init__(name, [], dependencies, subdir, subproject, environment) From b4d3bee6fecdb614b5717633d5b2a9931d2206a2 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 19 May 2026 11:04:13 -0700 Subject: [PATCH 325/681] interpreter: Fix more ExternalProgram -> Program issues --- mesonbuild/interpreter/interpreter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 72a21dfb8f64..a87743df4c65 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -2339,7 +2339,7 @@ def make_test(self, node: mparser.BaseNode, kwargs['verbose']) def add_test(self, node: mparser.BaseNode, - args: T.Tuple[str, T.Union[build.Executable, build.Jar, ExternalProgram, mesonlib.File, build.CustomTarget, build.CustomTargetIndex]], + args: T.Tuple[str, T.Union[build.Executable, build.Jar, Program, mesonlib.File, build.CustomTarget, build.CustomTargetIndex]], kwargs: kwtypes.FuncTest | kwtypes.FuncBenchmark, is_base_test: bool) -> None: if isinstance(args[1], (build.CustomTarget, build.CustomTargetIndex)): FeatureNew.single_use('test with CustomTarget as command', '1.4.0', self.subproject) From 78b5c6887f56922b5a505a7c34af511356e851a0 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 19 May 2026 11:14:19 -0700 Subject: [PATCH 326/681] interpreter: Fix annotation of add_target Which in turn points out that we have several levels of incorrect target annotations below that. --- mesonbuild/backend/backends.py | 15 +++++++-------- mesonbuild/build.py | 4 ++-- mesonbuild/interpreter/interpreter.py | 2 +- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 0f44b91847cc..8f9fe793ef7b 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -3,7 +3,6 @@ from __future__ import annotations -from collections import OrderedDict from dataclasses import dataclass, InitVar from functools import lru_cache from itertools import chain @@ -1394,13 +1393,8 @@ def replace_outputs(self, args: T.List[str], private_dir: str, output_list: T.Li newargs.append(arg) return newargs - def get_build_by_default_targets(self) -> 'T.OrderedDict[str, T.Union[build.BuildTarget, build.CustomTarget]]': - result: 'T.OrderedDict[str, T.Union[build.BuildTarget, build.CustomTarget]]' = OrderedDict() - # Get all build and custom targets that must be built by default - for name, b in self.build.get_targets().items(): - if b.build_by_default: - result[name] = b - return result + def get_build_by_default_targets(self) -> dict[str, build.BuildTarget | build.CustomTarget | build.RunTarget]: + return {k: v for k, v in self.build.targets.items() if v.build_by_default} def get_testlike_targets(self, benchmark: bool = False) -> T.Iterable[T.Union[build.BuildTarget, build.CustomTarget]]: targets = self.build.get_benchmarks() if benchmark else self.build.get_tests() @@ -1712,6 +1706,8 @@ def generate_target_install(self, d: InstallData) -> None: for t in self.build.get_targets().values(): if not t.should_install(): continue + # Mypy doesn't understand that AliasTarget and RunTarget always return False above + assert isinstance(t, (build.BuildTarget, build.CustomTarget)), 'for mypy' outdirs = t.install_dir install_dir_names = t.install_dir_names() # Sanity-check the outputs and install_dirs @@ -2006,6 +2002,9 @@ def get_devenv(self) -> mesonlib.EnvironmentVariables: if t.for_machine is not MachineChoice.HOST or not t.should_install(): continue + # Mypy doesn't understand that AliasTarget and RunTarget always return False above + assert isinstance(t, (build.BuildTarget, build.CustomTarget)), 'for mypy' + if (host_machine.is_windows() or host_machine.is_cygwin()) and isinstance(t, (build.Executable, build.SharedModule)): # On windows we cannot rely on rpath to run executables from build # directory. We have to add in PATH the location of every DLL needed. diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 49ba737ae0b5..669393ff576c 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -363,7 +363,7 @@ def __init__(self, environment: Environment): self.project_version: T.Optional[str] = None self.environment = environment self.projects: T.Dict[SubProject, BuildProject] = {} - self.targets: 'T.OrderedDict[str, T.Union[CustomTarget, BuildTarget]]' = OrderedDict() + self.targets: dict[str, CustomTarget | BuildTarget | RunTarget | AliasTarget] = {} self.targetnames: T.Set[T.Tuple[str, str]] = set() # Set of executable names and their subdir self.global_args: PerMachine[T.Dict[str, T.List[str]]] = PerMachine({}, {}) self.global_link_args: PerMachine[T.Dict[str, T.List[str]]] = PerMachine({}, {}) @@ -458,7 +458,7 @@ def get_project(self) -> str: def get_subproject_dir(self) -> str: return self.subproject_dir - def get_targets(self) -> 'T.OrderedDict[str, T.Union[CustomTarget, BuildTarget]]': + def get_targets(self) -> dict[str, CustomTarget | BuildTarget | RunTarget | AliasTarget]: return self.targets def get_tests(self) -> T.List['Test']: diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index a87743df4c65..d9760ba8a61d 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -3304,7 +3304,7 @@ def validate_forbidden_targets(self, name: str, in_root: bool) -> None: FeatureNew.single_use(f"Target name '{name}' reserved in the root build directory, but allowed in subdirectories", '1.12.0', self.subproject, location=self.current_node) - def add_target(self, name: str, tobj: build.Target) -> None: + def add_target(self, name: str, tobj: build.BuildTarget | build.CustomTarget | build.RunTarget | build.AliasTarget) -> None: if self.backend.name == 'none': raise InterpreterException('Install-only backend cannot generate target rules, try using `--backend=ninja`.') if name == '': From 215ca701f982d1df380e7d12a2b39499c022d733 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 19 May 2026 11:30:45 -0700 Subject: [PATCH 327/681] interpreter: ensure that positional arguments have literal values --- mesonbuild/interpreter/interpreter.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index d9760ba8a61d..2b539d7e3a87 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -3364,6 +3364,8 @@ def build_both_libraries(self, node: mparser.BaseNode, args: T.Tuple[str, Source assert isinstance(preferred_library, str), 'for mypy' if preferred_library == 'both': preferred_library = 'shared' + assert preferred_library in {'shared', 'static'} + preferred_library = T.cast('Literal["static", "shared"]', preferred_library) if self.backend.name == 'xcode': # Xcode is a bit special in that you can't (at least for the moment) From 4fb70e0b8c1bc63a290890740a492adc84f962e0 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 19 May 2026 11:39:45 -0700 Subject: [PATCH 328/681] interpreter/kwargs: Update _BaseBuildTarget.link_with The build layer was updated to LInkableTargetTypes, but this wasn't --- 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 ec03b12b079a..e94c1428fee7 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -381,7 +381,7 @@ class _BaseBuildTarget(TypedDict): 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] + link_with: T.List[build.LinkableTargetTypes] name_prefix: T.Optional[str] name_suffix: T.Optional[str] native: MachineChoice From 581a8dcdf0ff454ccb0fbce2cc32abdd07d7bdb5 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 19 May 2026 11:40:51 -0700 Subject: [PATCH 329/681] interpreter: __process_language_args accepts Jar kwargs as well --- mesonbuild/interpreter/interpreter.py | 2 +- mesonbuild/interpreter/kwargs.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 2b539d7e3a87..1e2b775bc166 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -3442,7 +3442,7 @@ def __convert_file_args(self, raw: T.List[mesonlib.FileOrString]) -> T.Tuple[T.L return depend_files, args - def __process_language_args(self, kwargs: kwtypes.BuildTarget + def __process_language_args(self, kwargs: kwtypes.BaseBuildTarget, ) -> T.Tuple[T.DefaultDict[Language, T.List[str]], T.List[mesonlib.File]]: """Convert split language args into a combined dictionary. diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index e94c1428fee7..1a4779975518 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -358,7 +358,7 @@ class DoSubproject(ExtractRequired): options: T.Optional[CMakeSubprojectOptions] -class _BaseBuildTarget(TypedDict): +class BaseBuildTarget(TypedDict): """Arguments used by all BuildTarget like functions. @@ -394,7 +394,7 @@ class _BaseBuildTarget(TypedDict): vala_gir: T.Optional[str] -class BuildTarget(_BaseBuildTarget): +class BuildTarget(BaseBuildTarget): """Arguments shared by non-JAR functions""" @@ -520,7 +520,7 @@ class _JarMixin(TypedDict): java_args: T.List[str] -class Jar(_BaseBuildTarget, _JarMixin): +class Jar(BaseBuildTarget, _JarMixin): sources: T.Union[str, File, build.GeneratedTypes, build.ExtractedObjects, build.BuildTarget] From 27b2264527da7968012c821067e818f3c4674463 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 27 May 2026 09:35:31 -0700 Subject: [PATCH 330/681] tests: set read encoding to help the VS backend Not setting this seems to cause intermittent test failures in the VS backend. The following VSCode substitutions where used to generate this patch - `open\((.*), 'r'\)` -> `open($1, 'r', encoding='utf-8')` - `read_text()` -> `read_text(encoding='utf-8')` --- test cases/common/182 find override/subdir/converter.py | 2 +- test cases/common/182 find override/subdir/gencodegen.py.in | 2 +- .../216 custom target input extracted objects/libdir/gen.py | 2 +- test cases/common/262 generator chain/stage1.py | 2 +- test cases/common/262 generator chain/stage2.py | 2 +- .../common/271 env in generator.process/generate_main.py | 2 +- .../common/295 depends in generator.process/generate_main.py | 2 +- test cases/format/5 transform/file_compare.py | 4 ++-- test cases/unit/132 custom target index test/meson.build | 2 +- test cases/unit/35 dist script/replacer.py | 2 +- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/test cases/common/182 find override/subdir/converter.py b/test cases/common/182 find override/subdir/converter.py index 690c723b4d34..ea0f9488ac44 100755 --- a/test cases/common/182 find override/subdir/converter.py +++ b/test cases/common/182 find override/subdir/converter.py @@ -10,6 +10,6 @@ } ''' -d = pathlib.Path(ifilename).read_text().split('\n')[0].strip() +d = pathlib.Path(ifilename).read_text(encoding='utf-8').split('\n')[0].strip() pathlib.Path(ofilename).write_text(ftempl % d, encoding='utf-8') diff --git a/test cases/common/182 find override/subdir/gencodegen.py.in b/test cases/common/182 find override/subdir/gencodegen.py.in index 9bdb874b46d8..d37d549bc8e1 100755 --- a/test cases/common/182 find override/subdir/gencodegen.py.in +++ b/test cases/common/182 find override/subdir/gencodegen.py.in @@ -10,6 +10,6 @@ ftempl = '''int %s(void) { } ''' -d = pathlib.Path(ifilename).read_text().split('\n')[0].strip() +d = pathlib.Path(ifilename).read_text(encoding='utf-8').split('\n')[0].strip() pathlib.Path(ofilename).write_text(ftempl % d, encoding='utf-8') diff --git a/test cases/common/216 custom target input extracted objects/libdir/gen.py b/test cases/common/216 custom target input extracted objects/libdir/gen.py index 095fffd929ef..ddbafa95b714 100644 --- a/test cases/common/216 custom target input extracted objects/libdir/gen.py +++ b/test cases/common/216 custom target input extracted objects/libdir/gen.py @@ -1,6 +1,6 @@ #! /usr/bin/env python3 import sys -with open(sys.argv[1], 'r') as f: +with open(sys.argv[1], 'r', encoding='utf-8') as f: for l in f: l = l.rstrip() print(l.replace(sys.argv[2], sys.argv[3])) diff --git a/test cases/common/262 generator chain/stage1.py b/test cases/common/262 generator chain/stage1.py index d15e0d055545..aed7812e6bc1 100644 --- a/test cases/common/262 generator chain/stage1.py +++ b/test cases/common/262 generator chain/stage1.py @@ -2,5 +2,5 @@ import sys from pathlib import Path -assert(Path(sys.argv[1]).read_text() == 'stage1\n') +assert(Path(sys.argv[1]).read_text(encoding='utf-8') == 'stage1\n') Path(sys.argv[2]).write_text('stage2\n', encoding='utf-8') diff --git a/test cases/common/262 generator chain/stage2.py b/test cases/common/262 generator chain/stage2.py index 44517762f601..0630a31735b9 100644 --- a/test cases/common/262 generator chain/stage2.py +++ b/test cases/common/262 generator chain/stage2.py @@ -2,5 +2,5 @@ import sys from pathlib import Path -assert(Path(sys.argv[1]).read_text() == 'stage2\n') +assert(Path(sys.argv[1]).read_text(encoding='utf-8') == 'stage2\n') Path(sys.argv[2]).write_text('int main(void){}\n', encoding='utf-8') diff --git a/test cases/common/271 env in generator.process/generate_main.py b/test cases/common/271 env in generator.process/generate_main.py index 12b98c4ae054..ec08fc1c4cdb 100644 --- a/test cases/common/271 env in generator.process/generate_main.py +++ b/test cases/common/271 env in generator.process/generate_main.py @@ -5,7 +5,7 @@ ENV_VAR_VALUE = os.environ.get('ENV_VAR_VALUE') assert ENV_VAR_VALUE is not None -with open(sys.argv[1], 'r') as infile, \ +with open(sys.argv[1], 'r', encoding='utf-8') as infile, \ open(sys.argv[2], 'w', encoding='utf-8') as outfile: outfile.write(infile.read().replace('ENV_VAR_VALUE', ENV_VAR_VALUE)) diff --git a/test cases/common/295 depends in generator.process/generate_main.py b/test cases/common/295 depends in generator.process/generate_main.py index 8de78694c57f..4b7d5d27a993 100644 --- a/test cases/common/295 depends in generator.process/generate_main.py +++ b/test cases/common/295 depends in generator.process/generate_main.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 import sys -with open(sys.argv[1], 'r') as infile, \ +with open(sys.argv[1], 'r', encoding='utf-8') as infile, \ open(sys.argv[2], 'w', encoding='utf-8') as outfile: outfile.write(infile.read()) diff --git a/test cases/format/5 transform/file_compare.py b/test cases/format/5 transform/file_compare.py index fd3ce10560a6..e6fb29bab603 100644 --- a/test cases/format/5 transform/file_compare.py +++ b/test cases/format/5 transform/file_compare.py @@ -13,9 +13,9 @@ def main() -> int: parser.add_argument('expected', help='the contents we expected') args = parser.parse_args() - with open(args.actual, 'r') as f: + with open(args.actual, 'r', encoding='utf-8') as f: actual = f.readlines() - with open(args.expected, 'r') as f: + with open(args.expected, 'r', encoding='utf-8') as f: expected = f.readlines() if actual == expected: diff --git a/test cases/unit/132 custom target index test/meson.build b/test cases/unit/132 custom target index test/meson.build index a00d1d420fd6..aaed21b91a1a 100644 --- a/test cases/unit/132 custom target index test/meson.build +++ b/test cases/unit/132 custom target index test/meson.build @@ -10,7 +10,7 @@ foobar_txt = custom_target(command: [python3, '-c', gen_code, '@OUTPUT@'], outpu test_code = ''' import sys, pathlib -sys.exit(0 if pathlib.Path(sys.argv[1]).read_text() == sys.argv[2] else 4)''' +sys.exit(0 if pathlib.Path(sys.argv[1]).read_text(encoding='utf-8') == sys.argv[2] else 4)''' test('foo.txt', python3, args: ['-c', test_code, foobar_txt[0], 'foo']) test('bar.txt', python3, args: ['-c', test_code, foobar_txt[1], 'bar']) diff --git a/test cases/unit/35 dist script/replacer.py b/test cases/unit/35 dist script/replacer.py index 4b0c30570358..5713a28d3365 100755 --- a/test cases/unit/35 dist script/replacer.py +++ b/test cases/unit/35 dist script/replacer.py @@ -11,6 +11,6 @@ modfile = source_root / 'prog.c' -contents = modfile.read_text() +contents = modfile.read_text(encoding='utf-8') contents = contents.replace(sys.argv[1], sys.argv[2]) modfile.write_text(contents, encoding='utf-8') From cf2c9d13c4dd1ef5a91ca4cd651cfe4451b57710 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 27 May 2026 17:27:11 +0200 Subject: [PATCH 331/681] mformat: allow --recursive --check-diff Signed-off-by: Paolo Bonzini --- .../snippets/mformat-recursive-check-diff.md | 5 +++++ mesonbuild/mformat.py | 13 ++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) create mode 100644 docs/markdown/snippets/mformat-recursive-check-diff.md diff --git a/docs/markdown/snippets/mformat-recursive-check-diff.md b/docs/markdown/snippets/mformat-recursive-check-diff.md new file mode 100644 index 000000000000..0752098fca6a --- /dev/null +++ b/docs/markdown/snippets/mformat-recursive-check-diff.md @@ -0,0 +1,5 @@ +## `meson format` allows `--recursive` with `--check-diff` + +Previously, the `--recursive` option to `meson format` required +either `--inplace` or `--output`. With this version, `--check-diff` +is allowed to. diff --git a/mesonbuild/mformat.py b/mesonbuild/mformat.py index 35f0f948ac23..67e4dbcdc814 100644 --- a/mesonbuild/mformat.py +++ b/mesonbuild/mformat.py @@ -1014,7 +1014,7 @@ def add_arguments(parser: argparse.ArgumentParser) -> None: parser.add_argument( '-r', '--recursive', action='store_true', - help='recurse subdirs (requires --check-only or --inplace option)', + help='recurse subdirs (requires --check-only, --check-diff or --inplace option)', ) parser.add_argument( '-c', '--configuration', @@ -1056,8 +1056,8 @@ def get_meson_format(sources: T.List[Path]) -> T.Optional[Path]: def run(options: argparse.Namespace) -> int: if options.output and len(options.sources) != 1: raise MesonException('--output argument implies having exactly one source file') - if options.recursive and not (options.inplace or options.check_only): - raise MesonException('--recursive argument requires either --inplace or --check-only option') + if options.recursive and not (options.inplace or options.check_only or options.check_diff): + raise MesonException('--recursive argument requires one of --inplace, --check-diff or --check-only') from_stdin = len(options.sources) == 1 and options.sources[0].name == '-' and options.sources[0].parent == Path() if options.recursive and from_stdin: @@ -1075,6 +1075,7 @@ def run(options: argparse.Namespace) -> int: options.configuration = get_meson_format(sources) formatter = Formatter(options.configuration, options.editor_config, options.recursive) + err = 0 while sources: src_file = sources.pop(0) @@ -1102,13 +1103,15 @@ def run(options: argparse.Namespace) -> int: raise MesonException(f'Unable to write to {src_file}') from e elif options.check_only or options.check_diff: if code != formatted: + err = 1 if options.check_diff: diff = difflib.unified_diff(code.splitlines(), formatted.splitlines(), str(src_file), str(src_file), '(original)', '(reformatted)', lineterm='') print('\n'.join(diff)) - return 1 + else: + break elif options.output: try: with options.output.open('w', encoding='utf-8', newline=formatter.current_config.newline) as of: @@ -1118,7 +1121,7 @@ def run(options: argparse.Namespace) -> int: else: print(formatted, end='') - return 0 + return err # TODO: remove empty newlines when more than N (2...) # TODO: magic comment to prevent formatting From b916f7bd4452d9419cfe44dea9d473f42b6dd873 Mon Sep 17 00:00:00 2001 From: Zephyr Lykos Date: Tue, 9 Dec 2025 22:23:08 +0800 Subject: [PATCH 332/681] dependencies: support custom stdatomic lookup for MSVC Closes: https://github.com/mesonbuild/meson/issues/14233 Fixes: f070670e77fe36ece1a89ce00c13419de354b9ce --- docs/markdown/snippets/atomic-now-supports-msvc.md | 4 ++++ mesonbuild/dependencies/misc.py | 14 ++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 docs/markdown/snippets/atomic-now-supports-msvc.md diff --git a/docs/markdown/snippets/atomic-now-supports-msvc.md b/docs/markdown/snippets/atomic-now-supports-msvc.md new file mode 100644 index 000000000000..8d3d30044b5b --- /dev/null +++ b/docs/markdown/snippets/atomic-now-supports-msvc.md @@ -0,0 +1,4 @@ +## Custom dependency for atomic now works on MSVC + +`dependency('atomic')` now works on MSVC >=19.35.32124. +It requires `c_std=c11` or later, otherwise the dependency will return not found. diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py index 1f9f06e97241..c9f46d782e8e 100644 --- a/mesonbuild/dependencies/misc.py +++ b/mesonbuild/dependencies/misc.py @@ -61,6 +61,20 @@ def __init__(self, name: str, env: Environment, kwargs: DependencyObjectKWs): if self.clib_compiler.has_function('atomic_flag_clear', '#include ')[0]: self.is_found = True + elif self.clib_compiler.get_id() == 'msvc': + feature_flag = '/experimental:c11atomics' + std_args = self.clib_compiler.get_option_std_args(None, None) + # atomic_flag_clear forwards to __c11_atomic_store in msvc, + # which behaves like __builtin_* in gcc, and we're unable to detect it with .has_function(). + if self.clib_compiler.has_function("_Atomic_thread_fence", + "#include ", + extra_args=std_args)[0]: + self.is_found = True + elif self.clib_compiler.has_function("_Atomic_thread_fence", + "#include ", + extra_args=std_args + [feature_flag])[0]: + self.compile_args = [feature_flag] + self.is_found = True class AtomicSystemDependency(SystemDependency): From 1af53710cbb25acdef9ad0e333a0c1909b1c33d0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 24 May 2026 16:50:20 +0200 Subject: [PATCH 333/681] unittests: add test for cargo.version.api Signed-off-by: Paolo Bonzini --- unittests/cargotests.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/unittests/cargotests.py b/unittests/cargotests.py index fc571ee786ee..56b47a7b6266 100644 --- a/unittests/cargotests.py +++ b/unittests/cargotests.py @@ -13,7 +13,8 @@ from mesonbuild.cargo.interpreter import load_cargo_lock from mesonbuild.cargo.manifest import Dependency, Lint, Manifest, Package, Workspace from mesonbuild.cargo.toml import load_toml -from mesonbuild.cargo.version import convert +from mesonbuild.cargo.version import api, convert +from mesonbuild.mesonlib import MesonException class CargoVersionTest(unittest.TestCase): @@ -70,6 +71,20 @@ def test_cargo_to_meson(self) -> None: with self.subTest(): self.assertListEqual(convert(data), expected) + def test_api(self) -> None: + cases: T.List[T.Tuple[str, str]] = [ + # Plain versions + ('1.2.3', '1'), + ('0.4.5', '0.4'), + ('0.0.3', '0'), + + # Explicit operators + ('1.*', '1'), + ] + for (data, expected) in cases: + with self.subTest(data=data): + self.assertEqual(api(data), expected) + class CargoCfgTest(unittest.TestCase): From 7bc4b718f75ffa7f7048eca7d4cf2465ae334dc7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 24 May 2026 17:40:44 +0200 Subject: [PATCH 334/681] unittests: add test for updating version of a cargo package Signed-off-by: Paolo Bonzini --- unittests/cargotests.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/unittests/cargotests.py b/unittests/cargotests.py index 56b47a7b6266..b939ca4bf972 100644 --- a/unittests/cargotests.py +++ b/unittests/cargotests.py @@ -331,7 +331,7 @@ class CargoTomlTest(unittest.TestCase): glib = { path = "glib" } gtk = { package = "gtk4", version = "0.9" } once_cell = "1.0" - syn = { version = "2", features = ["parse"] } + syn = { version = ">=1, <3", features = ["parse"] } [workspace.lints.rust] warnings = "deny" @@ -369,6 +369,19 @@ def test_cargo_toml_ws_package(self) -> None: self.assertEqual(pkg.edition, '2015') self.assertEqual(pkg.repository, None) + def test_update_dependency(self) -> None: + with tempfile.TemporaryDirectory() as tmpdir: + fname = os.path.join(tmpdir, 'Cargo.toml') + with open(fname, 'w', encoding='utf-8') as f: + f.write(self.CARGO_TOML_WS) + workspace_toml = load_toml(fname) + + workspace = Workspace.from_raw(workspace_toml, tmpdir) + dep = Dependency.from_raw('syn', {'workspace': True, 'features': ['full']}, 'member', workspace) + self.assertEqual(dep.api, '1') + dep.update_version('=2.0.113') + self.assertEqual(dep.api, '2') + def test_cargo_toml_ws_dependency(self) -> None: with tempfile.TemporaryDirectory() as tmpdir: fname = os.path.join(tmpdir, 'Cargo.toml') @@ -401,9 +414,9 @@ def test_cargo_toml_ws_dependency(self) -> None: dep = Dependency.from_raw('syn', {'workspace': True, 'features': ['full']}, 'member', workspace) self.assertEqual(dep.package, 'syn') - self.assertEqual(dep.version, '2') - self.assertEqual(dep.meson_version, ['>= 2', '< 3']) - self.assertEqual(dep.api, '2') + self.assertEqual(dep.version, '>=1, <3') + self.assertEqual(dep.meson_version, ['>=1', '<3']) + self.assertEqual(dep.api, '1') self.assertEqual(sorted(set(dep.features)), ['full', 'parse']) def test_cargo_toml_package(self) -> None: From ec51939c929498df947e1c9485e40949cd287300 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 24 May 2026 17:46:38 +0200 Subject: [PATCH 335/681] unittests: add some currently-failing tests Signed-off-by: Paolo Bonzini --- unittests/cargotests.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/unittests/cargotests.py b/unittests/cargotests.py index b939ca4bf972..ed82117c3f81 100644 --- a/unittests/cargotests.py +++ b/unittests/cargotests.py @@ -26,6 +26,14 @@ def test_cargo_to_meson(self) -> None: ('> 1', ['> 1']), ('= 1', ['= 1']), ('< 1', ['< 1']), + # Trailing zeros in the bound: Meson's Version treats a shorter + # prefix as < the same prefix with trailing zeros, so the >= bound + # must be canonicalized to behave like Cargo (where 1 == 1.0 == 1.0.0). +# ('>= 1.0', ['>= 1']), +# ('>= 1.0.0', ['>= 1']), +# ('> 1.0', ['> 1.0']), +# ('> 1.0.0', ['> 1.0.0']), + # Cargo's <= must accept x.y.z, which Meson's <= would not ('<= 1', ['< 2']), ('<= 1.1', ['< 1.2']), ('<= 1.1.1', ['<= 1.1.1']), @@ -44,6 +52,8 @@ def test_cargo_to_meson(self) -> None: ('2', ['>= 2', '< 3']), ('2.4', ['>= 2.4', '< 3']), ('2.4.5', ['>= 2.4.5', '< 3']), +# ('1.0', ['>= 1', '< 2']), +# ('1.0.0', ['>= 1', '< 2']), ('0.0.0', ['< 1']), ('0.0', ['< 1']), ('0', ['< 1']), @@ -56,6 +66,8 @@ def test_cargo_to_meson(self) -> None: ('^2', ['>= 2', '< 3']), ('^2.4', ['>= 2.4', '< 3']), ('^2.4.5', ['>= 2.4.5', '< 3']), +# ('^1.0', ['>= 1', '< 2']), +# ('^1.0.0', ['>= 1', '< 2']), ('^0.0.0', ['< 1']), ('^0.0', ['< 1']), ('^0', ['< 1']), From 8a360f9fe0564c2cfe88ea01665e7d1fa3af8179 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 25 May 2026 10:41:11 +0200 Subject: [PATCH 336/681] unittests: do not assume cargo versions are parsed into meson versions Check the acceptance directly against a set of versions, instead of checking the result of the conversion. Signed-off-by: Paolo Bonzini --- unittests/cargotests.py | 112 ++++++++++++++++++++++------------------ 1 file changed, 61 insertions(+), 51 deletions(-) diff --git a/unittests/cargotests.py b/unittests/cargotests.py index ed82117c3f81..7385d6fd68b2 100644 --- a/unittests/cargotests.py +++ b/unittests/cargotests.py @@ -14,74 +14,84 @@ from mesonbuild.cargo.manifest import Dependency, Lint, Manifest, Package, Workspace from mesonbuild.cargo.toml import load_toml from mesonbuild.cargo.version import api, convert -from mesonbuild.mesonlib import MesonException +from mesonbuild.mesonlib import MesonException, version_compare class CargoVersionTest(unittest.TestCase): - def test_cargo_to_meson(self) -> None: - cases: T.List[T.Tuple[str, T.List[str]]] = [ - # Basic requirements - ('>= 1', ['>= 1']), - ('> 1', ['> 1']), - ('= 1', ['= 1']), - ('< 1', ['< 1']), + def test_cargo_parse(self) -> None: + # Each case is (cargo_requirement, accepted_versions, rejected_versions). + # The conversion from Cargo to Meson constraints is opaque, so probe the + # resulting predicate on versions around the boundaries. + cases: T.List[T.Tuple[str, T.List[str], T.List[str]]] = [ + # Basic comparison requirements + ('>= 1', ['1', '1.0', '1.5', '2'], ['0.9']), + ('> 1', ['1.0.1', '1.5', '2'], ['0.9', '1']), + ('= 1', ['1'], ['0.9', '1.0.1', '2']), +# ('= 1', ['1.0', '1.0.0'], []), + ('< 1', ['0.9'], ['1', '1.0', '2']), # Trailing zeros in the bound: Meson's Version treats a shorter # prefix as < the same prefix with trailing zeros, so the >= bound # must be canonicalized to behave like Cargo (where 1 == 1.0 == 1.0.0). -# ('>= 1.0', ['>= 1']), -# ('>= 1.0.0', ['>= 1']), -# ('> 1.0', ['> 1.0']), -# ('> 1.0.0', ['> 1.0.0']), +# ('>= 1.0', ['1', '1.0', '1.0.0', '1.5'], ['0.9']), +# ('>= 1.0.0', ['1', '1.0', '1.0.0', '1.5'], ['0.9']), +# ('> 1.0', ['1.0.1', '1.5', '2'], ['0.9', '1', '1.0']), +# ('> 1.0.0', ['1.0.1', '1.5', '2'], ['0.9', '1', '1.0', '1.0.0']), # Cargo's <= must accept x.y.z, which Meson's <= would not - ('<= 1', ['< 2']), - ('<= 1.1', ['< 1.2']), - ('<= 1.1.1', ['<= 1.1.1']), + ('<= 1', ['0.9', '1', '1.0', '1.5', '1.99'], ['2', '2.0']), + ('<= 1.1', ['1.0', '1.1', '1.1.5'], ['1.2', '2']), + ('<= 1.1.1', ['1.0', '1.1', '1.1.1'], ['1.1.2', '1.2']), - # tilde tests - ('~1', ['>= 1', '< 2']), - ('~1.1', ['>= 1.1', '< 1.2']), - ('~1.1.2', ['>= 1.1.2', '< 1.2.0']), + # Tilde requirements + ('~1', ['1', '1.5', '1.99'], ['0.9', '2']), + ('~1.1', ['1.1', '1.1.5'], ['1.0', '1.2', '2']), + ('~1.1.2', ['1.1.2', '1.1.5'], ['1.1.1', '1.2.0']), # Wildcards - ('*', []), - ('1.*', ['>= 1', '< 2']), - ('2.3.*', ['>= 2.3', '< 2.4']), + ('*', ['0.1', '1', '99.99'], []), + ('1.*', ['1', '1.5'], ['0.9', '2']), + ('2.3.*', ['2.3', '2.3.5'], ['2.2', '2.4']), # Unqualified - ('2', ['>= 2', '< 3']), - ('2.4', ['>= 2.4', '< 3']), - ('2.4.5', ['>= 2.4.5', '< 3']), -# ('1.0', ['>= 1', '< 2']), -# ('1.0.0', ['>= 1', '< 2']), - ('0.0.0', ['< 1']), - ('0.0', ['< 1']), - ('0', ['< 1']), - ('0.0.5', ['>= 0.0.5', '< 0.0.6']), - ('0.5.0', ['>= 0.5.0', '< 0.6']), - ('0.5', ['>= 0.5', '< 0.6']), - ('1.0.45', ['>= 1.0.45', '< 2']), - - # Caret (Which is the same as unqualified) - ('^2', ['>= 2', '< 3']), - ('^2.4', ['>= 2.4', '< 3']), - ('^2.4.5', ['>= 2.4.5', '< 3']), -# ('^1.0', ['>= 1', '< 2']), -# ('^1.0.0', ['>= 1', '< 2']), - ('^0.0.0', ['< 1']), - ('^0.0', ['< 1']), - ('^0', ['< 1']), - ('^0.0.5', ['>= 0.0.5', '< 0.0.6']), - ('^0.5.0', ['>= 0.5.0', '< 0.6']), - ('^0.5', ['>= 0.5', '< 0.6']), + ('2', ['2', '2.5'], ['1', '3']), + ('2.4', ['2.4', '2.5'], ['2.3', '3']), + ('2.4.5', ['2.4.5', '2.6'], ['2.4.4', '3']), + ('0.0.0', ['0', '0.0.0', '0.0.5', '0.5'], ['1']), + ('0.0', ['0', '0.5', '0.999'], ['1']), + ('0', ['0', '0.5'], ['1']), + ('0.0.5', ['0.0.5'], ['0.0.4', '0.0.6']), + ('0.5.0', ['0.5.0', '0.5.5'], ['0.4.0', '0.6']), + ('0.5', ['0.5', '0.5.5'], ['0.4', '0.6']), + ('1.0.45', ['1.0.45', '1.5'], ['1.0.44', '2']), + + # Caret (equivalent to unqualified) + ('^2', ['2', '2.5'], ['1', '3']), + ('^2.4', ['2.4', '2.5'], ['2.3', '3']), + ('^2.4.5', ['2.4.5', '2.6'], ['2.4.4', '3']), +# ('^1.0', ['1', '1.0', '1.0.0', '1.5'], ['0.9', '2']), +# ('^1.0.0', ['1', '1.0', '1.0.0', '1.5'], ['0.9', '2']), + ('^0.0.0', ['0', '0.0.5'], ['1']), + ('^0.0', ['0', '0.5'], ['1']), + ('^0', ['0', '0.5'], ['1']), + ('^0.0.5', ['0.0.5'], ['0.0.4', '0.0.6']), + ('^0.5.0', ['0.5.0', '0.5.5'], ['0.4.0', '0.6']), + ('^0.5', ['0.5', '0.5.5'], ['0.4', '0.6']), # Multiple requirements - ('>= 1.2.3, < 1.4.7', ['>= 1.2.3', '< 1.4.7']), + ('>= 1.2.3, < 1.4.7', ['1.2.3', '1.3.0'], ['1.2.2', '1.4.7', '1.5']), ] - for (data, expected) in cases: - with self.subTest(): - self.assertListEqual(convert(data), expected) + for (cargo_req, accepted, rejected) in cases: + translated = convert(cargo_req) + def check(ver): + return all(version_compare(ver, constraint) for constraint in translated) + + for ver in accepted: + with self.subTest(req=cargo_req, ver=ver): + self.assertTrue(check(ver), f'{cargo_req!r} should accept {ver!r}') + for ver in rejected: + with self.subTest(req=cargo_req, ver=ver): + self.assertFalse(check(ver), f'{cargo_req!r} should reject {ver!r}') def test_api(self) -> None: cases: T.List[T.Tuple[str, str]] = [ From 4310a4f2944377a1d136c8857c2aaac2647556c7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 25 May 2026 09:51:20 +0200 Subject: [PATCH 337/681] cargo: remove feature check The set of required features may be different for host and build versions of the same crate. Since there is no way to find whether a subproject is being invoked as a native one, remove the code. Signed-off-by: Paolo Bonzini --- mesonbuild/cargo/interpreter.py | 82 --------------------------------- 1 file changed, 82 deletions(-) diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index 4a45edfbee8d..e791dbaea80c 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -377,7 +377,6 @@ def _create_package(self, pkg: PackageState, build: builder.Builder, subdir: str build.identifier('features'), ]), ] - ast += self._create_feature_checks(pkg, build) ast += self._create_meson_subdir(build) if pkg.manifest.lib: @@ -737,87 +736,6 @@ def _create_project(self, name: str, pkg: T.Optional[PackageState], build: build 'cargo_ws') ] - def _create_feature_checks(self, pkg: PackageState, build: builder.Builder) -> T.List[mparser.BaseNode]: - cfg = pkg.cfg - ast: T.List[mparser.BaseNode] = [] - for depname in cfg.required_deps: - dep = pkg.manifest.dependencies[depname] - dep_pkg = self._dep_package(pkg, dep) - if dep_pkg.manifest.lib: - ast += self._create_feature_check(dep_pkg, dep, build) - return ast - - def _create_feature_check(self, pkg: PackageState, dep: Dependency, build: builder.Builder) -> T.List[mparser.BaseNode]: - cfg = pkg.cfg - feat_obj: mparser.BaseNode - feat_pkg = self.cargolock and self.resolve_package(dep.package, dep.api) - if feat_pkg: - if feat_pkg.ws_subdir == pkg.ws_subdir: - return [] - - feat_obj = build.method( - 'features', - build.method( - 'subproject', - build.identifier('cargo_ws'), - [build.string(dep.package), build.string(dep.api)])) - else: - version_ = dep.meson_version or [pkg.manifest.package.version] - kw = { - 'version': build.array([build.string(s) for s in version_]), - } - # actual_features = dependency(...).get_variable('features', default_value : '').split(',') - dep_obj = build.function( - 'dependency', - [build.string(_dependency_name(dep.package, dep.api))], - kw) - feat_obj = build.method( - 'split', - build.method( - 'get_variable', - dep_obj, - [build.string('features')], - {'default_value': build.string('')} - ), - [build.string(',')], - ) - - # However, this subproject could have been previously configured with a - # different set of features. Cargo collects the set of features globally - # but Meson can only use features enabled by the first call that triggered - # the configuration of that subproject. - # - # Verify all features that we need are actually enabled for that dependency, - # otherwise abort with an error message. The user has to set the corresponding - # option manually with -Dxxx-rs:feature-yyy=true, or the main project can do - # that in its project(..., default_options: ['xxx-rs:feature-yyy=true']). - return [ - # actual_features = dependency(...).get_variable('features', default_value : '').split(',') - build.assign( - feat_obj, - 'actual_features' - ), - # needed_features = [f1, f2, ...] - # foreach f : needed_features - # if f not in actual_features - # error() - # endif - # endforeach - build.assign(build.array([build.string(f) for f in cfg.features]), 'needed_features'), - build.foreach(['f'], build.identifier('needed_features'), build.block([ - build.if_(build.not_in(build.identifier('f'), build.identifier('actual_features')), build.block([ - build.function('error', [ - build.string('Dependency'), - build.string(_dependency_name(dep.package, dep.api)), - build.string('previously configured with features'), - build.identifier('actual_features'), - build.string('but need'), - build.identifier('needed_features'), - ]) - ])) - ])), - ] - def _create_meson_subdir(self, build: builder.Builder) -> T.List[mparser.BaseNode]: # Allow Cargo subprojects to add extra Rust args in meson/meson.build file. # This is used to replace build.rs logic. From 61f62eb747b13f304d0b5941f3c0f17c37997e65 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 24 May 2026 15:43:31 +0200 Subject: [PATCH 338/681] cargo: simplify _resolve_package Raising the exception is unnecessary in all cases: - resolve_package called from _create_feature_check: Meson emits a runtime dependency() call, which will cause an error if needed - subproject_method (mesonbuild/modules/rust.py): already raises an exception on None, with a better message than the current one - _dep_package (interpreter.py:592-595): leaves dep.version unset, and _fetch_package calls _fetch_package_from_provider which raises a better diagnostic ("not found in any wrap files") if needed. Signed-off-by: Paolo Bonzini --- mesonbuild/cargo/interpreter.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index e791dbaea80c..2cef60af30cc 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -492,16 +492,12 @@ def _fetch_package(self, package_name: str, api: str) -> PackageState: def _resolve_package(self, package_name: str, version_constraints: T.List[str]) -> T.Optional[CargoLockPackage]: """From all available versions from Cargo.lock, pick the most recent satisfying the constraints and return it.""" - if self.cargolock: - cargo_lock_pkgs = self.cargolock.named(package_name) - else: - cargo_lock_pkgs = [] - for cargo_pkg in cargo_lock_pkgs: + if not self.cargolock: + return None + + for cargo_pkg in self.cargolock.named(package_name): if all(version_compare(cargo_pkg.version, v) for v in version_constraints): return cargo_pkg - - if not version_constraints: - raise MesonException(f'Cannot determine version of cargo package {package_name}') return None def resolve_package(self, package_name: str, api: str) -> T.Optional[PackageState]: From 4a63956e2018d0cc1da7412f21ffde604d2a85b9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 24 May 2026 11:14:53 +0200 Subject: [PATCH 339/681] cargo: add a "tokenizer" for version requirement strings Eliminate duplicate code for x.y.* and ~x.y, and generally speaking simplify version.convert(), by canonicalizing each of the requirements. Signed-off-by: Paolo Bonzini --- mesonbuild/cargo/version.py | 75 ++++++++++++++++++------------------- unittests/cargotests.py | 2 +- 2 files changed, 38 insertions(+), 39 deletions(-) diff --git a/mesonbuild/cargo/version.py b/mesonbuild/cargo/version.py index f92be582f5b1..cbe27c8616a5 100644 --- a/mesonbuild/cargo/version.py +++ b/mesonbuild/cargo/version.py @@ -19,43 +19,55 @@ def api(version: str) -> str: return '0' +def split(cargo_ver: str) -> T.Iterable[tuple[str, str]]: + """Canonicalization of Cargo version requirements""" + cargo_ver = cargo_ver.strip() + if not cargo_ver: + return + for ver in cargo_ver.split(','): + ver = ver.strip() + if ver == '*': + continue + + if ver.startswith(('>=', '<=', '!=')): + yield ver[0:2], ver[2:].lstrip() + elif ver.startswith(('~', '=', '^', '>', '<')): + yield ver[0], ver[1:].lstrip() + elif ver.endswith('.*'): + # asterisk requirements are same as tilde: 1.* == ~1 + # https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#wildcard-requirements + yield '~', ver[:-2].lstrip() + else: + # caret requirement is the default strategy + yield '^', ver + + def convert(cargo_ver: str) -> T.List[str]: - """Convert a Cargo compatible version into a Meson compatible one. + """Return a function that checks a Version against a Cargo version + requirement. :param cargo_ver: The version, as Cargo specifies :return: A list of version constraints, as Meson understands them """ - # Cleanup, just for safety - cargo_ver = cargo_ver.strip() - if not cargo_ver: - return [] - cargo_vers = [c.strip() for c in cargo_ver.split(',')] - out: T.List[str] = [] - - for ver in cargo_vers: + for op, ver in split(cargo_ver): # https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#comparison-requirements # <= 3 allows 3.0.0 where meson version compare does not # So change <= into < with a bumped version - if ver.startswith('<='): - v = ver[2:].strip().split('.') + if op == '<=': + v = ver.split('.') if len(v) == 1: out.append(f'< {int(v[0]) + 1}') elif len(v) == 2: out.append(f'< {v[0]}.{int(v[1]) + 1}') else: - out.append(ver) + out.append(f'{op} {ver}') - # This covers >= as well - elif ver.startswith(('>', '<', '=')): - out.append(ver) - - elif ver.startswith('~'): - # Rust has these tilde requirements, which means that it is >= to - # the version, but less than the next version + elif op == '~': + # Tilde requirements are the same as asterisk, so 1.* == ~1 # https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#tilde-requirements # we convert those into a pair of constraints - v = ver[1:].split('.') + v = ver.split('.') out.append(f'>= {".".join(v)}') if len(v) == 3: out.append(f'< {v[0]}.{int(v[1]) + 1}.0') @@ -64,24 +76,8 @@ def convert(cargo_ver: str) -> T.List[str]: else: out.append(f'< {int(v[0]) + 1}') - elif '*' in ver: - # Rust has astrisk requirements,, which are like 1.* == ~1 - # https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#wildcard-requirements - v = ver.split('.')[:-1] - if v: - out.append(f'>= {".".join(v)}') - if len(v) == 2: - out.append(f'< {v[0]}.{int(v[1]) + 1}') - elif len(v) == 1: - out.append(f'< {int(v[0]) + 1}') - - else: - # a Caret version is equivalent to the default strategy - # https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#caret-requirements - if ver.startswith('^'): - ver = ver[1:] - - # If there is no qualifier, then it means this or the next non-zero version + elif op == '^': + # Allow changes after the first non-zero version # That means that if this is `1.1.0``, then we need `>= 1.1.0` && `< 2.0.0` # Or if we have `0.1.0`, then we need `>= 0.1.0` && `< 0.2.0` # Or if we have `0.1`, then we need `>= 0.1.0` && `< 0.2.0` @@ -117,4 +113,7 @@ def convert(cargo_ver: str) -> T.List[str]: else: out.append('< 1') + else: + out.append(f'{op} {ver}') + return out diff --git a/unittests/cargotests.py b/unittests/cargotests.py index 7385d6fd68b2..ba7bc818ef6a 100644 --- a/unittests/cargotests.py +++ b/unittests/cargotests.py @@ -437,7 +437,7 @@ def test_cargo_toml_ws_dependency(self) -> None: dep = Dependency.from_raw('syn', {'workspace': True, 'features': ['full']}, 'member', workspace) self.assertEqual(dep.package, 'syn') self.assertEqual(dep.version, '>=1, <3') - self.assertEqual(dep.meson_version, ['>=1', '<3']) + self.assertEqual(dep.meson_version, ['>= 1', '< 3']) self.assertEqual(dep.api, '1') self.assertEqual(sorted(set(dep.features)), ['full', 'parse']) From 7476c79c3ea1a579a48c106e62bf6779261f1dec Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 24 May 2026 17:56:06 +0200 Subject: [PATCH 340/681] cargo: generalize version.api Now that "split" does more of the parsing, move the conversion of dependency requirements to API numbers out of the Dependency class. While at it, add comments. Signed-off-by: Paolo Bonzini --- mesonbuild/cargo/manifest.py | 14 +------------- mesonbuild/cargo/version.py | 23 ++++++++++++++++++++++- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/mesonbuild/cargo/manifest.py b/mesonbuild/cargo/manifest.py index ef2bc1e0e15a..7a2eaff54205 100644 --- a/mesonbuild/cargo/manifest.py +++ b/mesonbuild/cargo/manifest.py @@ -312,19 +312,7 @@ def meson_version(self) -> T.List[str]: @lazy_property def api(self) -> str: - # Extract wanted API version from version constraints. - api = set() - for v in self.meson_version: - if v.startswith(('>=', '==')): - api.add(version.api(v[2:].strip())) - elif v.startswith('='): - api.add(version.api(v[1:].strip())) - if not api: - return '' - elif len(api) == 1: - return api.pop() - else: - raise MesonException(f'Cannot determine minimum API version from {self.version}.') + return version.api(self.version) def update_version(self, v: str) -> None: self.version = v diff --git a/mesonbuild/cargo/version.py b/mesonbuild/cargo/version.py index cbe27c8616a5..3fbdf3355856 100644 --- a/mesonbuild/cargo/version.py +++ b/mesonbuild/cargo/version.py @@ -6,8 +6,9 @@ from __future__ import annotations import typing as T +from ..mesonlib import MesonException -def api(version: str) -> str: +def _api_of(version: str) -> str: # x.y.z -> x # 0.x.y -> 0.x # 0.0.x -> 0 @@ -42,6 +43,26 @@ def split(cargo_ver: str) -> T.Iterable[tuple[str, str]]: yield '^', ver +def api(cargo_ver: str) -> str: + """Determine the API version implied by a Cargo version requirement. + + :param cargo_ver: A Cargo version string. + :return: empty string if no lower bound establishes an API, otherwise the + major version (or ``"0.x"`` / ``"0"`` for 0.x.y / 0.0.x versions). + Raise exception if constraints disagree on the API. + """ + apis: T.Set[str] = set() + for op, ver in split(cargo_ver): + if op in {'>=', '=', '^', '~'}: + apis.add(_api_of(ver)) + if not apis: + return '' + elif len(apis) == 1: + return apis.pop() + else: + raise MesonException(f'Cannot determine API version from {cargo_ver!r}.') + + def convert(cargo_ver: str) -> T.List[str]: """Return a function that checks a Version against a Cargo version requirement. From 83e038303fe2c3ac778d36526a3919fc6e94a366 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 24 May 2026 16:50:47 +0200 Subject: [PATCH 341/681] cargo: "compile" version checks to a callable Signed-off-by: Paolo Bonzini --- mesonbuild/cargo/interpreter.py | 11 +++---- mesonbuild/cargo/manifest.py | 8 ++++-- mesonbuild/cargo/version.py | 16 ++++++++--- unittests/cargotests.py | 51 +++++++++++++++++++++------------ 4 files changed, 55 insertions(+), 31 deletions(-) diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index 2cef60af30cc..7e940573d310 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -26,7 +26,7 @@ from .manifest import Manifest, CargoLock, CargoLockPackage, Workspace, fixup_meson_varname from ..mesonlib import ( is_parent_path, lazy_property, MesonException, MachineChoice, - unique_list, version_compare, SubProject, + unique_list, SubProject, ) from .. import coredata, mlog from ..wrap.wrap import PackageDefinition @@ -489,19 +489,20 @@ def _fetch_package(self, package_name: str, api: str) -> PackageState: return pkg return self._fetch_package_from_provider(package_name, api) - def _resolve_package(self, package_name: str, version_constraints: T.List[str]) -> T.Optional[CargoLockPackage]: + def _resolve_package(self, package_name: str, accepts_version: T.Callable[[str], bool]) -> \ + T.Optional[CargoLockPackage]: """From all available versions from Cargo.lock, pick the most recent satisfying the constraints and return it.""" if not self.cargolock: return None for cargo_pkg in self.cargolock.named(package_name): - if all(version_compare(cargo_pkg.version, v) for v in version_constraints): + if accepts_version(cargo_pkg.version): return cargo_pkg return None def resolve_package(self, package_name: str, api: str) -> T.Optional[PackageState]: - cargo_pkg = self._resolve_package(package_name, version.convert(api)) + cargo_pkg = self._resolve_package(package_name, version.cargo_parse(api)) if not cargo_pkg: return None api = version.api(cargo_pkg.version) @@ -584,7 +585,7 @@ def _dep_package(self, pkg: PackageState, dep: Dependency) -> PackageState: _, _, directory = _parse_git_url(dep.git, dep.branch) dep_pkg = self._fetch_package_from_subproject(dep.package, directory) else: - cargo_pkg = self._resolve_package(dep.package, dep.meson_version) + cargo_pkg = self._resolve_package(dep.package, dep.accepts_version) if cargo_pkg: dep.update_version(f'={cargo_pkg.version}') dep_pkg = self._fetch_package(dep.package, dep.api) diff --git a/mesonbuild/cargo/manifest.py b/mesonbuild/cargo/manifest.py index 7a2eaff54205..6c7f7b51ba08 100644 --- a/mesonbuild/cargo/manifest.py +++ b/mesonbuild/cargo/manifest.py @@ -307,8 +307,10 @@ class Dependency: features: T.List[str] = dataclasses.field(default_factory=list) @lazy_property - def meson_version(self) -> T.List[str]: - return version.convert(self.version) + def accepts_version(self) -> T.Callable[[str], bool]: + # The value of the property is the function that checks the validity + # of a given package version, so dep.accepts_version(v) works. + return version.cargo_parse(self.version) @lazy_property def api(self) -> str: @@ -321,7 +323,7 @@ def update_version(self, v: str) -> None: except AttributeError: pass try: - delattr(self, 'meson_version') + delattr(self, 'accepts_version') except AttributeError: pass diff --git a/mesonbuild/cargo/version.py b/mesonbuild/cargo/version.py index 3fbdf3355856..ad1e5bb4e1cc 100644 --- a/mesonbuild/cargo/version.py +++ b/mesonbuild/cargo/version.py @@ -4,6 +4,8 @@ """Convert Cargo versions into Meson compatible ones.""" from __future__ import annotations +from functools import lru_cache +from ..mesonlib import version_compare import typing as T from ..mesonlib import MesonException @@ -13,7 +15,7 @@ def _api_of(version: str) -> str: # 0.x.y -> 0.x # 0.0.x -> 0 vers = version.split('.') - if int(vers[0]) != 0: + if not vers[0] or int(vers[0]) != 0: return vers[0] elif len(vers) >= 2 and int(vers[1]) != 0: return f'0.{vers[1]}' @@ -63,12 +65,13 @@ def api(cargo_ver: str) -> str: raise MesonException(f'Cannot determine API version from {cargo_ver!r}.') -def convert(cargo_ver: str) -> T.List[str]: +@lru_cache(maxsize=None) +def cargo_parse(cargo_ver: str) -> T.Callable[[str], bool]: """Return a function that checks a Version against a Cargo version requirement. :param cargo_ver: The version, as Cargo specifies - :return: A list of version constraints, as Meson understands them + :return: A function returning true if the version is accepted. """ out: T.List[str] = [] for op, ver in split(cargo_ver): @@ -137,4 +140,9 @@ def convert(cargo_ver: str) -> T.List[str]: else: out.append(f'{op} {ver}') - return out + def compare(ver: str) -> bool: + return all(version_compare(ver, comparison) for comparison in out) + + if not out: + return lambda v: True + return compare diff --git a/unittests/cargotests.py b/unittests/cargotests.py index ba7bc818ef6a..d7e822b33e4b 100644 --- a/unittests/cargotests.py +++ b/unittests/cargotests.py @@ -13,8 +13,8 @@ from mesonbuild.cargo.interpreter import load_cargo_lock from mesonbuild.cargo.manifest import Dependency, Lint, Manifest, Package, Workspace from mesonbuild.cargo.toml import load_toml -from mesonbuild.cargo.version import api, convert -from mesonbuild.mesonlib import MesonException, version_compare +from mesonbuild.cargo.version import api, cargo_parse +from mesonbuild.mesonlib import MesonException class CargoVersionTest(unittest.TestCase): @@ -82,10 +82,7 @@ def test_cargo_parse(self) -> None: ] for (cargo_req, accepted, rejected) in cases: - translated = convert(cargo_req) - def check(ver): - return all(version_compare(ver, constraint) for constraint in translated) - + check = cargo_parse(cargo_req) for ver in accepted: with self.subTest(req=cargo_req, ver=ver): self.assertTrue(check(ver), f'{cargo_req!r} should accept {ver!r}') @@ -282,7 +279,7 @@ class CargoTomlTest(unittest.TestCase): num-complex = "0.4" rayon = "1.0" once_cell = "1" - async-channel = "2.0" + async-channel = "2.1" zerocopy = { version = "0.7", features = ["derive"] } [lints.rust] @@ -415,21 +412,24 @@ def test_cargo_toml_ws_dependency(self) -> None: dep = Dependency.from_raw('glib', {'workspace': True}, 'member', workspace) self.assertEqual(dep.package, 'glib') self.assertEqual(dep.version, '') - self.assertEqual(dep.meson_version, []) + self.assertTrue(dep.accepts_version('9999')) self.assertEqual(dep.path, os.path.join('..', 'glib')) self.assertEqual(dep.features, []) dep = Dependency.from_raw('gtk', {'workspace': True}, 'member', workspace) self.assertEqual(dep.package, 'gtk4') self.assertEqual(dep.version, '0.9') - self.assertEqual(dep.meson_version, ['>= 0.9', '< 0.10']) + self.assertTrue(dep.accepts_version('0.9')) + self.assertFalse(dep.accepts_version('0.10')) self.assertEqual(dep.api, '0.9') self.assertEqual(dep.features, []) dep = Dependency.from_raw('once_cell', {'workspace': True, 'optional': True}, 'member', workspace) self.assertEqual(dep.package, 'once_cell') self.assertEqual(dep.version, '1.0') - self.assertEqual(dep.meson_version, ['>= 1.0', '< 2']) + self.assertTrue(dep.accepts_version('1.0')) + self.assertTrue(dep.accepts_version('1.23')) + self.assertFalse(dep.accepts_version('2.0')) self.assertEqual(dep.api, '1') self.assertEqual(dep.features, []) self.assertTrue(dep.optional) @@ -437,7 +437,9 @@ def test_cargo_toml_ws_dependency(self) -> None: dep = Dependency.from_raw('syn', {'workspace': True, 'features': ['full']}, 'member', workspace) self.assertEqual(dep.package, 'syn') self.assertEqual(dep.version, '>=1, <3') - self.assertEqual(dep.meson_version, ['>= 1', '< 3']) + self.assertTrue(dep.accepts_version('1.0')) + self.assertTrue(dep.accepts_version('2.0')) + self.assertFalse(dep.accepts_version('3.0')) self.assertEqual(dep.api, '1') self.assertEqual(sorted(set(dep.features)), ['full', 'parse']) @@ -509,33 +511,44 @@ def test_cargo_toml_dependencies(self) -> None: self.assertEqual(len(manifest.dependencies), 6) self.assertEqual(manifest.dependencies['gtk'].package, 'gtk4') self.assertEqual(manifest.dependencies['gtk'].version, '0.9') - self.assertEqual(manifest.dependencies['gtk'].meson_version, ['>= 0.9', '< 0.10']) + self.assertTrue(manifest.dependencies['gtk'].accepts_version('0.9')) + self.assertFalse(manifest.dependencies['gtk'].accepts_version('0.10')) self.assertEqual(manifest.dependencies['gtk'].api, '0.9') self.assertEqual(manifest.dependencies['num-complex'].package, 'num-complex') self.assertEqual(manifest.dependencies['num-complex'].version, '0.4') - self.assertEqual(manifest.dependencies['num-complex'].meson_version, ['>= 0.4', '< 0.5']) + self.assertTrue(manifest.dependencies['num-complex'].accepts_version('0.4')) + self.assertFalse(manifest.dependencies['num-complex'].accepts_version('0.5')) self.assertEqual(manifest.dependencies['rayon'].package, 'rayon') self.assertEqual(manifest.dependencies['rayon'].version, '1.0') - self.assertEqual(manifest.dependencies['rayon'].meson_version, ['>= 1.0', '< 2']) + self.assertTrue(manifest.dependencies['rayon'].accepts_version('1.0')) + self.assertTrue(manifest.dependencies['rayon'].accepts_version('1.23')) + self.assertFalse(manifest.dependencies['rayon'].accepts_version('2.0')) self.assertEqual(manifest.dependencies['rayon'].api, '1') self.assertEqual(manifest.dependencies['once_cell'].package, 'once_cell') self.assertEqual(manifest.dependencies['once_cell'].version, '1') - self.assertEqual(manifest.dependencies['once_cell'].meson_version, ['>= 1', '< 2']) + self.assertTrue(manifest.dependencies['once_cell'].accepts_version('1.0')) + self.assertTrue(manifest.dependencies['once_cell'].accepts_version('1.23')) + self.assertFalse(manifest.dependencies['once_cell'].accepts_version('2.0')) self.assertEqual(manifest.dependencies['once_cell'].api, '1') self.assertEqual(manifest.dependencies['async-channel'].package, 'async-channel') - self.assertEqual(manifest.dependencies['async-channel'].version, '2.0') - self.assertEqual(manifest.dependencies['async-channel'].meson_version, ['>= 2.0', '< 3']) + self.assertEqual(manifest.dependencies['async-channel'].version, '2.1') + self.assertFalse(manifest.dependencies['async-channel'].accepts_version('2.0')) + self.assertTrue(manifest.dependencies['async-channel'].accepts_version('2.1')) + self.assertTrue(manifest.dependencies['async-channel'].accepts_version('2.23')) + self.assertFalse(manifest.dependencies['async-channel'].accepts_version('2.0')) self.assertEqual(manifest.dependencies['async-channel'].api, '2') self.assertEqual(manifest.dependencies['zerocopy'].package, 'zerocopy') self.assertEqual(manifest.dependencies['zerocopy'].version, '0.7') - self.assertEqual(manifest.dependencies['zerocopy'].meson_version, ['>= 0.7', '< 0.8']) + self.assertTrue(manifest.dependencies['zerocopy'].accepts_version('0.7')) + self.assertFalse(manifest.dependencies['zerocopy'].accepts_version('0.8')) self.assertEqual(manifest.dependencies['zerocopy'].features, ['derive']) self.assertEqual(manifest.dependencies['zerocopy'].api, '0.7') self.assertEqual(len(manifest.dev_dependencies), 1) self.assertEqual(manifest.dev_dependencies['gir-format-check'].package, 'gir-format-check') self.assertEqual(manifest.dev_dependencies['gir-format-check'].version, '^0.1') - self.assertEqual(manifest.dev_dependencies['gir-format-check'].meson_version, ['>= 0.1', '< 0.2']) + self.assertTrue(manifest.dev_dependencies['gir-format-check'].accepts_version('0.1')) + self.assertFalse(manifest.dev_dependencies['gir-format-check'].accepts_version('0.2')) self.assertEqual(manifest.dev_dependencies['gir-format-check'].api, '0.1') def test_cargo_toml_proc_macro(self) -> None: From 9fb5c6b4664b82fef57f9447ee0c005d95187efb Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 25 May 2026 10:11:09 +0200 Subject: [PATCH 342/681] cargo: add a SemVer class Semantic versions are quite different from RPM versions implemented in Meson's Version class. Just implement them with a completely different class. Signed-off-by: Paolo Bonzini --- mesonbuild/cargo/version.py | 128 ++++++++++++++++++++++++++++++++++++ unittests/cargotests.py | 26 +++++++- 2 files changed, 153 insertions(+), 1 deletion(-) diff --git a/mesonbuild/cargo/version.py b/mesonbuild/cargo/version.py index ad1e5bb4e1cc..7dc134af7803 100644 --- a/mesonbuild/cargo/version.py +++ b/mesonbuild/cargo/version.py @@ -6,6 +6,8 @@ from __future__ import annotations from functools import lru_cache from ..mesonlib import version_compare +import operator +import re import typing as T from ..mesonlib import MesonException @@ -65,6 +67,132 @@ def api(cargo_ver: str) -> str: raise MesonException(f'Cannot determine API version from {cargo_ver!r}.') +# Tokens: a digit run, an alphanumeric-with-hyphens identifier (covers the +# leading ``-`` of the pre-release section and identifiers within it), or the +# ``+`` that introduces build metadata. +_SEMVER_TOK_RE = re.compile(r'(\d+)|([A-Za-z-][0-9A-Za-z-]*)|(\+.*)') + + +class SemVer: + """A SemVer 2.0.0 version, suitable for ordering. + + Adapted from ``mesonlib.Version`` but with the int/str precedence + reversed: SemVer says numeric pre-release identifiers sort *below* + alphanumeric ones, whereas Meson's ``Version`` does the opposite. + + Versions are represented as a flat list: + [major, minor, patch, 0] for normal versions + [major, minor, patch, -1, *pre_idents] for pre-releases + + The fourth slot (0 for normal, -1 for pre-release) makes a normal + version sort *above* the corresponding pre-release. + Missing minor/patch components default to 0. + """ + + __slots__ = ('_v', 'specified_count') + + def __init__(self, in_: str | list[int | str] = None) -> None: + vec: list[int | str] + if isinstance(in_, str): + vec = [] + pre = False + specified_count = 0 + for m in _SEMVER_TOK_RE.finditer(in_): + if m.group(1): + if pre or specified_count < 3: + vec.append(int(m.group(1))) + if not pre: + specified_count += 1 + elif m.group(2): + ident = m.group(2) + if not pre: + # The leading ``-`` is just a section marker. + if ident.startswith('-'): + ident = ident[1:] + if not ident: + continue + while len(vec) < 3: + vec.append(0) + vec.append(-1) + pre = True + vec.append(ident) + else: + break # +build metadata: discard the rest + else: + # Direct construction from a pre-built component list. + vec = list(in_) + specified_count = min(3, len(in_)) + + while len(vec) < 4: + vec.append(0) + self._v = vec + self.specified_count = specified_count + + def __repr__(self) -> str: + s = '.'.join(str(c) for c in self._v[:self.specified_count]) + if len(self._v) > 4: + s += '-' + '.'.join(str(c) for c in self._v[4:]) + return f'' + + @property + def has_prerelease(self) -> bool: + return self._v[3] == -1 + + def __lt__(self, other: object) -> bool: + if isinstance(other, SemVer): + return self.__cmp(other._v, operator.lt) + return NotImplemented + + def __gt__(self, other: object) -> bool: + if isinstance(other, SemVer): + return self.__cmp(other._v, operator.gt) + return NotImplemented + + def __le__(self, other: object) -> bool: + if isinstance(other, SemVer): + return self.__cmp(other._v, operator.le) + return NotImplemented + + def __ge__(self, other: object) -> bool: + if isinstance(other, SemVer): + return self.__cmp(other._v, operator.ge) + return NotImplemented + + def __eq__(self, other: object) -> bool: + if isinstance(other, SemVer): + return self._v == other._v + return NotImplemented + + def __ne__(self, other: object) -> bool: + if isinstance(other, SemVer): + return self._v != other._v + return NotImplemented + + def __cmp(self, other: list[int | str], comparator: T.Callable[[T.Any, T.Any], bool]) -> bool: + for ours, theirs in zip(self._v, other): + ours_is_int = isinstance(ours, int) + theirs_is_int = isinstance(theirs, int) + if ours_is_int != theirs_is_int: + # SemVer: int (numeric) < str (alphanumeric). + return comparator(theirs_is_int, ours_is_int) + if ours != theirs: + return comparator(ours, theirs) + # "A larger set of pre-release fields has a higher precedence." + return comparator(len(self._v), len(other)) + + def next_ver(self, bump_idx: int) -> SemVer: + """Return a new SemVer with the component at ``bump_idx`` (0=major, + 1=minor, 2=patch) bumped, following normal-version components zeroed, + and any pre-release dropped.""" + v = list(self._v[:3]) + last = v[bump_idx] + assert isinstance(last, int) + v[bump_idx] = last + 1 + for i in range(bump_idx + 1, 3): + v[i] = 0 + return SemVer(v) + + @lru_cache(maxsize=None) def cargo_parse(cargo_ver: str) -> T.Callable[[str], bool]: """Return a function that checks a Version against a Cargo version diff --git a/unittests/cargotests.py b/unittests/cargotests.py index d7e822b33e4b..ea76955a483d 100644 --- a/unittests/cargotests.py +++ b/unittests/cargotests.py @@ -13,7 +13,7 @@ from mesonbuild.cargo.interpreter import load_cargo_lock from mesonbuild.cargo.manifest import Dependency, Lint, Manifest, Package, Workspace from mesonbuild.cargo.toml import load_toml -from mesonbuild.cargo.version import api, cargo_parse +from mesonbuild.cargo.version import api, cargo_parse, SemVer from mesonbuild.mesonlib import MesonException @@ -90,6 +90,30 @@ def test_cargo_parse(self) -> None: with self.subTest(req=cargo_req, ver=ver): self.assertFalse(check(ver), f'{cargo_req!r} should reject {ver!r}') + def test_semver_parse(self) -> None: + # Pre-release components parse into the version vector after a -1 + # sentinel; build metadata (after '+') is discarded. + self.assertEqual(SemVer('1.2.3')._v, [1, 2, 3, 0]) + self.assertEqual(SemVer('1.0.0-alpha.1')._v, [1, 0, 0, -1, 'alpha', 1]) + self.assertEqual(SemVer('1.2.3-rc.1+exp.sha')._v, [1, 2, 3, -1, 'rc', 1]) + self.assertEqual(SemVer('1.0.0+build.5')._v, [1, 0, 0, 0]) + + # numeric identifiers sort below alphanumeric ones, a larger set of fields + # wins, and a release sorts above all of its pre-releases. + # https://semver.org/#spec-item-11 + ordered = [ + '1.0.0-alpha', '1.0.0-alpha.1', '1.0.0-alpha.beta', '1.0.0-beta', + '1.0.0-beta.2', '1.0.0-beta.11', '1.0.0-rc.1', '1.0.0', + ] + for lo, hi in zip(ordered, ordered[1:]): + with self.subTest(lo=lo, hi=hi): + self.assertLess(SemVer(lo), SemVer(hi)) + self.assertGreater(SemVer(hi), SemVer(lo)) + + # Build metadata does not affect precedence. + self.assertEqual(SemVer('1.0.0+build.5'), SemVer('1.0.0')) + self.assertEqual(SemVer('1.2.3-rc.1+exp.sha'), SemVer('1.2.3-rc.1')) + def test_api(self) -> None: cases: T.List[T.Tuple[str, str]] = [ # Plain versions From 855dd901d22f82bf9c039363ed4e8c983b501db7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 25 May 2026 09:33:34 +0200 Subject: [PATCH 343/681] cargo: change version comparisons to use semver Fixes: #15636 Signed-off-by: Paolo Bonzini --- mesonbuild/cargo/manifest.py | 4 +- mesonbuild/cargo/version.py | 93 ++++++++++++++---------------------- unittests/cargotests.py | 15 +++--- 3 files changed, 46 insertions(+), 66 deletions(-) diff --git a/mesonbuild/cargo/manifest.py b/mesonbuild/cargo/manifest.py index 6c7f7b51ba08..a568e0e04288 100644 --- a/mesonbuild/cargo/manifest.py +++ b/mesonbuild/cargo/manifest.py @@ -15,7 +15,7 @@ from . import version -from ..mesonlib import MesonException, lazy_property, Version +from ..mesonlib import MesonException, lazy_property from .. import mlog if T.TYPE_CHECKING: @@ -696,7 +696,7 @@ def _versions(self) -> T.Dict[str, T.List[CargoLockPackage]]: for pkg in self.package: versions[pkg.name].append(pkg) for pkg_versions in versions.values(): - pkg_versions.sort(reverse=True, key=lambda pkg: Version(pkg.version)) + pkg_versions.sort(reverse=True, key=lambda pkg: version.SemVer(pkg.version)) return versions @classmethod diff --git a/mesonbuild/cargo/version.py b/mesonbuild/cargo/version.py index 7dc134af7803..2c0580b8b5be 100644 --- a/mesonbuild/cargo/version.py +++ b/mesonbuild/cargo/version.py @@ -5,7 +5,6 @@ from __future__ import annotations from functools import lru_cache -from ..mesonlib import version_compare import operator import re import typing as T @@ -201,75 +200,57 @@ def cargo_parse(cargo_ver: str) -> T.Callable[[str], bool]: :param cargo_ver: The version, as Cargo specifies :return: A function returning true if the version is accepted. """ - out: T.List[str] = [] + out: list[tuple[T.Callable[[T.Any, T.Any], bool], SemVer]] = [] for op, ver in split(cargo_ver): + semver = SemVer(ver) + # https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#comparison-requirements - # <= 3 allows 3.0.0 where meson version compare does not - # So change <= into < with a bumped version if op == '<=': - v = ver.split('.') - if len(v) == 1: - out.append(f'< {int(v[0]) + 1}') - elif len(v) == 2: - out.append(f'< {v[0]}.{int(v[1]) + 1}') - else: - out.append(f'{op} {ver}') + # Bump the last *specified* component and convert to `<`. + nextver = semver.next_ver(semver.specified_count - 1) + out.append((operator.lt, nextver)) elif op == '~': # Tilde requirements are the same as asterisk, so 1.* == ~1 # https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#tilde-requirements # we convert those into a pair of constraints - v = ver.split('.') - out.append(f'>= {".".join(v)}') - if len(v) == 3: - out.append(f'< {v[0]}.{int(v[1]) + 1}.0') - elif len(v) == 2: - out.append(f'< {v[0]}.{int(v[1]) + 1}') + out.append((operator.ge, semver)) + if semver.specified_count >= 2: + nextver = semver.next_ver(1) else: - out.append(f'< {int(v[0]) + 1}') + nextver = semver.next_ver(0) + out.append((operator.lt, nextver)) elif op == '^': - # Allow changes after the first non-zero version - # That means that if this is `1.1.0``, then we need `>= 1.1.0` && `< 2.0.0` - # Or if we have `0.1.0`, then we need `>= 0.1.0` && `< 0.2.0` - # Or if we have `0.1`, then we need `>= 0.1.0` && `< 0.2.0` - # Or if we have `0.0.0`, then we need `< 1.0.0` - # Or if we have `0.0`, then we need `< 1.0.0` - # Or if we have `0`, then we need `< 1.0.0` - # Or if we have `0.0.3`, then we need `>= 0.0.3` && `< 0.0.4` - # https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#specifying-dependencies-from-cratesio - # - # this works much like the ~ versions, but in reverse. Tilde starts - # at the patch version and works up, to the major version, while - # bare numbers start at the major version and work down to the patch - # version - vers = ver.split('.') - min_: T.List[str] = [] - max_: T.List[str] = [] - bumped = False - for v_ in vers: - if v_ != '0' and not bumped: - min_.append(v_) - max_.append(str(int(v_) + 1)) - bumped = True - else: - min_.append(v_) - if not bumped: - max_.append('0') - - # If there is no minimum, don't emit one - if set(min_) != {'0'}: - out.append('>= {}'.format('.'.join(min_))) - if set(max_) != {'0'}: - out.append('< {}'.format('.'.join(max_))) + # https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#caret-requirements + # Bump the leftmost non-zero major/minor/patch component + out.append((operator.ge, semver)) + for bump_idx in range(3): + if semver._v[bump_idx] != 0: + break else: - out.append('< 1') - - else: - out.append(f'{op} {ver}') + # All zeros: ``^0.0.0`` means ``< 1.0.0``, so bump the major. + bump_idx = 0 + nextver = semver.next_ver(bump_idx) + out.append((operator.lt, nextver)) + + elif op == '>=': + out.append((operator.ge, semver)) + elif op == '!=': + out.append((operator.ne, semver)) + elif op == '=': + out.append((operator.eq, semver)) + elif op == '>': + out.append((operator.gt, semver)) + elif op == '<': + out.append((operator.lt, semver)) def compare(ver: str) -> bool: - return all(version_compare(ver, comparison) for comparison in out) + lhs = SemVer(ver) + for op, rhs in out: + if not op(lhs, rhs): + return False + return True if not out: return lambda v: True diff --git a/unittests/cargotests.py b/unittests/cargotests.py index ea76955a483d..a89a8bace697 100644 --- a/unittests/cargotests.py +++ b/unittests/cargotests.py @@ -27,16 +27,15 @@ def test_cargo_parse(self) -> None: # Basic comparison requirements ('>= 1', ['1', '1.0', '1.5', '2'], ['0.9']), ('> 1', ['1.0.1', '1.5', '2'], ['0.9', '1']), - ('= 1', ['1'], ['0.9', '1.0.1', '2']), -# ('= 1', ['1.0', '1.0.0'], []), + ('= 1', ['1', '1.0', '1.0.0'], ['0.9', '1.0.1', '2']), ('< 1', ['0.9'], ['1', '1.0', '2']), # Trailing zeros in the bound: Meson's Version treats a shorter # prefix as < the same prefix with trailing zeros, so the >= bound # must be canonicalized to behave like Cargo (where 1 == 1.0 == 1.0.0). -# ('>= 1.0', ['1', '1.0', '1.0.0', '1.5'], ['0.9']), -# ('>= 1.0.0', ['1', '1.0', '1.0.0', '1.5'], ['0.9']), -# ('> 1.0', ['1.0.1', '1.5', '2'], ['0.9', '1', '1.0']), -# ('> 1.0.0', ['1.0.1', '1.5', '2'], ['0.9', '1', '1.0', '1.0.0']), + ('>= 1.0', ['1', '1.0', '1.0.0', '1.5'], ['0.9']), + ('>= 1.0.0', ['1', '1.0', '1.0.0', '1.5'], ['0.9']), + ('> 1.0', ['1.0.1', '1.5', '2'], ['0.9', '1', '1.0']), + ('> 1.0.0', ['1.0.1', '1.5', '2'], ['0.9', '1', '1.0', '1.0.0']), # Cargo's <= must accept x.y.z, which Meson's <= would not ('<= 1', ['0.9', '1', '1.0', '1.5', '1.99'], ['2', '2.0']), ('<= 1.1', ['1.0', '1.1', '1.1.5'], ['1.2', '2']), @@ -68,8 +67,8 @@ def test_cargo_parse(self) -> None: ('^2', ['2', '2.5'], ['1', '3']), ('^2.4', ['2.4', '2.5'], ['2.3', '3']), ('^2.4.5', ['2.4.5', '2.6'], ['2.4.4', '3']), -# ('^1.0', ['1', '1.0', '1.0.0', '1.5'], ['0.9', '2']), -# ('^1.0.0', ['1', '1.0', '1.0.0', '1.5'], ['0.9', '2']), + ('^1.0', ['1', '1.0', '1.0.0', '1.5'], ['0.9', '2']), + ('^1.0.0', ['1', '1.0', '1.0.0', '1.5'], ['0.9', '2']), ('^0.0.0', ['0', '0.0.5'], ['1']), ('^0.0', ['0', '0.5'], ['1']), ('^0', ['0', '0.5'], ['1']), From f516566a55797a7ed2b56ac50f02a4010852a2a2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 25 May 2026 10:04:05 +0200 Subject: [PATCH 344/681] cargo: implement prerelease filtering This worked by chance at the edges ('<2.0' did not match '<2.0-pre') just because RPM sorting order placed '2.0-pre' *after* '2.0'. With SemVer it has to be implemented fully. Signed-off-by: Paolo Bonzini --- mesonbuild/cargo/version.py | 6 ++++++ unittests/cargotests.py | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/mesonbuild/cargo/version.py b/mesonbuild/cargo/version.py index 2c0580b8b5be..d22bdd0ba4f0 100644 --- a/mesonbuild/cargo/version.py +++ b/mesonbuild/cargo/version.py @@ -201,8 +201,12 @@ def cargo_parse(cargo_ver: str) -> T.Callable[[str], bool]: :return: A function returning true if the version is accepted. """ out: list[tuple[T.Callable[[T.Any, T.Any], bool], SemVer]] = [] + # Pre-release versions only match when at least one constraint + # names a pre-release. + accept_prerelease = False for op, ver in split(cargo_ver): semver = SemVer(ver) + accept_prerelease = accept_prerelease or semver.has_prerelease # https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#comparison-requirements if op == '<=': @@ -247,6 +251,8 @@ def cargo_parse(cargo_ver: str) -> T.Callable[[str], bool]: def compare(ver: str) -> bool: lhs = SemVer(ver) + if lhs.has_prerelease and not accept_prerelease: + return False for op, rhs in out: if not op(lhs, rhs): return False diff --git a/unittests/cargotests.py b/unittests/cargotests.py index a89a8bace697..360c0b604a80 100644 --- a/unittests/cargotests.py +++ b/unittests/cargotests.py @@ -78,6 +78,18 @@ def test_cargo_parse(self) -> None: # Multiple requirements ('>= 1.2.3, < 1.4.7', ['1.2.3', '1.3.0'], ['1.2.2', '1.4.7', '1.5']), + + # Pre-releases are excluded unless a constraint itself names a + # pre-release, even when otherwise in range. + ('>= 1.0', ['1.0', '1.5', '2'], ['2.0-pre1', '1.5-pre1']), + ('^1', ['1', '1.5'], ['1.5.0-pre', '2.0-pre']), + # A pre-release bound enables pre-release matching and orders them. + ('>= 1.0.0-alpha', + ['1.0.0-alpha', '1.0.0-alpha.1', '1.0.0-beta', '1.0.0', '1.5'], + ['1.0.0-alph', '0.9']), + ('>= 1.0.0-alpha, < 1.0.0', + ['1.0.0-alpha', '1.0.0-beta', '1.0.0-rc.1'], + ['1.0.0', '0.9']), ] for (cargo_req, accepted, rejected) in cases: @@ -89,6 +101,11 @@ def test_cargo_parse(self) -> None: with self.subTest(req=cargo_req, ver=ver): self.assertFalse(check(ver), f'{cargo_req!r} should reject {ver!r}') + def test_semver_has_prerelease(self) -> None: + self.assertTrue(SemVer('1.0.0-alpha').has_prerelease) + self.assertFalse(SemVer('1.0.0').has_prerelease) + self.assertFalse(SemVer('1.0.0+build.5').has_prerelease) + def test_semver_parse(self) -> None: # Pre-release components parse into the version vector after a -1 # sentinel; build metadata (after '+') is discarded. @@ -453,6 +470,7 @@ def test_cargo_toml_ws_dependency(self) -> None: self.assertTrue(dep.accepts_version('1.0')) self.assertTrue(dep.accepts_version('1.23')) self.assertFalse(dep.accepts_version('2.0')) + self.assertFalse(dep.accepts_version('2.0-pre1')) self.assertEqual(dep.api, '1') self.assertEqual(dep.features, []) self.assertTrue(dep.optional) @@ -546,12 +564,14 @@ def test_cargo_toml_dependencies(self) -> None: self.assertTrue(manifest.dependencies['rayon'].accepts_version('1.0')) self.assertTrue(manifest.dependencies['rayon'].accepts_version('1.23')) self.assertFalse(manifest.dependencies['rayon'].accepts_version('2.0')) + self.assertFalse(manifest.dependencies['rayon'].accepts_version('2.0-pre1')) self.assertEqual(manifest.dependencies['rayon'].api, '1') self.assertEqual(manifest.dependencies['once_cell'].package, 'once_cell') self.assertEqual(manifest.dependencies['once_cell'].version, '1') self.assertTrue(manifest.dependencies['once_cell'].accepts_version('1.0')) self.assertTrue(manifest.dependencies['once_cell'].accepts_version('1.23')) self.assertFalse(manifest.dependencies['once_cell'].accepts_version('2.0')) + self.assertFalse(manifest.dependencies['once_cell'].accepts_version('2.0-pre1')) self.assertEqual(manifest.dependencies['once_cell'].api, '1') self.assertEqual(manifest.dependencies['async-channel'].package, 'async-channel') self.assertEqual(manifest.dependencies['async-channel'].version, '2.1') @@ -559,6 +579,7 @@ def test_cargo_toml_dependencies(self) -> None: self.assertTrue(manifest.dependencies['async-channel'].accepts_version('2.1')) self.assertTrue(manifest.dependencies['async-channel'].accepts_version('2.23')) self.assertFalse(manifest.dependencies['async-channel'].accepts_version('2.0')) + self.assertFalse(manifest.dependencies['async-channel'].accepts_version('2.0-pre1')) self.assertEqual(manifest.dependencies['async-channel'].api, '2') self.assertEqual(manifest.dependencies['zerocopy'].package, 'zerocopy') self.assertEqual(manifest.dependencies['zerocopy'].version, '0.7') From 5e8699bed4bac2731409e07c6a7622b02de4dd6b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 28 May 2026 13:23:42 +0200 Subject: [PATCH 345/681] build: remove dead isinstance check self.name must be a str, it is checked by mypy now. Signed-off-by: Paolo Bonzini --- mesonbuild/build.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 669393ff576c..2b80f65da5e0 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -3329,12 +3329,7 @@ def get_filename(self) -> str: return self.name def get_outputs(self) -> T.List[str]: - if isinstance(self.name, str): - return [self.name] - elif isinstance(self.name, list): - return self.name - else: - raise RuntimeError('RunTarget: self.name is neither a list nor a string. This is a bug') + return [self.name] def type_suffix(self) -> str: return "@run" From 4cb8f4079fa5ac7a31e051e0eb826cf9351a9236 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 28 May 2026 13:28:17 +0200 Subject: [PATCH 346/681] ninjabackend: remove dead isinstance check Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index c6286220e07d..899e5f477451 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1245,9 +1245,6 @@ def custom_target_generator_inputs(self, target: build.CustomTarget) -> None: def unwrap_dep_list(self, target: T.Union[build.CustomTarget, build.RunTarget]) -> T.List[str]: deps = [] for i in target.get_dependencies(): - # FIXME, should not grab element at zero but rather expand all. - if isinstance(i, list): - i = i[0] # Add a dependency on all the outputs of this target for output in i.get_outputs(): deps.append(os.path.join(self.get_target_dir(i), output)) From 929c583d806ba7cda2a1fbf62a4eed4014518c99 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 28 May 2026 13:34:05 +0200 Subject: [PATCH 347/681] build: replace union of all Targets with superclass Signed-off-by: Paolo Bonzini --- mesonbuild/backend/backends.py | 4 ++-- mesonbuild/backend/ninjabackend.py | 8 ++++---- mesonbuild/build.py | 5 +++-- mesonbuild/interpreter/interpreter.py | 4 ++-- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 8f9fe793ef7b..c2b0d7c28be7 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -1393,7 +1393,7 @@ def replace_outputs(self, args: T.List[str], private_dir: str, output_list: T.Li newargs.append(arg) return newargs - def get_build_by_default_targets(self) -> dict[str, build.BuildTarget | build.CustomTarget | build.RunTarget]: + def get_build_by_default_targets(self) -> dict[str, build.Target]: return {k: v for k, v in self.build.targets.items() if v.build_by_default} def get_testlike_targets(self, benchmark: bool = False) -> T.Iterable[T.Union[build.BuildTarget, build.CustomTarget]]: @@ -1465,7 +1465,7 @@ def get_custom_target_sources(self, target: build.RunTarget | build.CustomTarget srcs += fname return srcs - def get_target_depend_files(self, target: T.Union[build.CustomTarget, build.BuildTarget, build.GeneratedList, build.RunTarget], absolute_paths: bool = False) -> T.List[str]: + def get_target_depend_files(self, target: T.Union[build.Target, build.GeneratedList], absolute_paths: bool = False) -> T.List[str]: deps: T.List[str] = [] for i in target.depend_files: if isinstance(i, mesonlib.File): diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 899e5f477451..0f61b2800df9 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -906,15 +906,15 @@ def create_target_linker_introspection(self, target: build.Target, linker: T.Uni } tgt[lnk_hash] = lnk_block - def generate_target(self, target: T.Union[build.BuildTarget, build.CustomTarget, build.RunTarget]) -> None: - if isinstance(target, build.BuildTarget): - os.makedirs(self.get_target_private_dir_abs(target), exist_ok=True) + def generate_target(self, target: T.Union[build.Target]) -> None: if isinstance(target, build.CustomTarget): self.generate_custom_target(target) return if isinstance(target, build.RunTarget): self.generate_run_target(target) return + assert isinstance(target, build.BuildTarget) + os.makedirs(self.get_target_private_dir_abs(target), exist_ok=True) compiled_sources: T.List[str] = [] source2object: T.Dict[str, str] = {} name = target.get_id() @@ -1232,7 +1232,7 @@ def select_sources_to_scan(self, compiled_sources: T.List[str], elif ext.lower() in compilers.lang_suffixes['fortran']: yield source, 'fortran' - def process_target_dependencies(self, target: build.Target) -> None: + def process_target_dependencies(self, target: build.BuildTarget) -> None: for t in target.get_dependencies(): if t.get_id() not in self.processed_targets: self.generate_target(t) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 2b80f65da5e0..3cb9a6660332 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -363,7 +363,7 @@ def __init__(self, environment: Environment): self.project_version: T.Optional[str] = None self.environment = environment self.projects: T.Dict[SubProject, BuildProject] = {} - self.targets: dict[str, CustomTarget | BuildTarget | RunTarget | AliasTarget] = {} + self.targets: dict[str, Target] = {} self.targetnames: T.Set[T.Tuple[str, str]] = set() # Set of executable names and their subdir self.global_args: PerMachine[T.Dict[str, T.List[str]]] = PerMachine({}, {}) self.global_link_args: PerMachine[T.Dict[str, T.List[str]]] = PerMachine({}, {}) @@ -458,7 +458,7 @@ def get_project(self) -> str: def get_subproject_dir(self) -> str: return self.subproject_dir - def get_targets(self) -> dict[str, CustomTarget | BuildTarget | RunTarget | AliasTarget]: + def get_targets(self) -> dict[str, Target]: return self.targets def get_tests(self) -> T.List['Test']: @@ -670,6 +670,7 @@ class Target(HoldableObject, metaclass=SimpleABC): build_always_stale: bool = False extra_files: T.List[File] = field(default_factory=list) build_subdir: str = '' + depend_files: T.List[File] = field(default_factory=list) @abc.abstractmethod def type_suffix(self) -> str: diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 1e2b775bc166..1120fd96c260 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -494,7 +494,7 @@ def process_new_values(self, invalues: T.List[T.Union[TYPE_var, ExecutableSerial for v in invalues: if isinstance(v, ObjectHolder): raise InterpreterException('Modules must not return ObjectHolders') - if isinstance(v, (build.BuildTarget, build.CustomTarget, build.RunTarget)): + if isinstance(v, build.Target): self.add_target(v.name, v) elif isinstance(v, list): self.process_new_values(v) @@ -3304,7 +3304,7 @@ def validate_forbidden_targets(self, name: str, in_root: bool) -> None: FeatureNew.single_use(f"Target name '{name}' reserved in the root build directory, but allowed in subdirectories", '1.12.0', self.subproject, location=self.current_node) - def add_target(self, name: str, tobj: build.BuildTarget | build.CustomTarget | build.RunTarget | build.AliasTarget) -> None: + def add_target(self, name: str, tobj: build.Target) -> None: if self.backend.name == 'none': raise InterpreterException('Install-only backend cannot generate target rules, try using `--backend=ninja`.') if name == '': From 08e39401c06e9f6ef8c587c51e9d13555c73f4eb Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 28 May 2026 13:39:01 +0200 Subject: [PATCH 348/681] ninjabackend: handle CustomTargetIndex as dependency Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 0f61b2800df9..8f9bb43a1000 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1235,6 +1235,8 @@ def select_sources_to_scan(self, compiled_sources: T.List[str], def process_target_dependencies(self, target: build.BuildTarget) -> None: for t in target.get_dependencies(): if t.get_id() not in self.processed_targets: + if isinstance(t, build.CustomTargetIndex): + t = t.target self.generate_target(t) def custom_target_generator_inputs(self, target: build.CustomTarget) -> None: From b5f6dbd7f0b10d559dc250c5638c945980f931ba Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 28 May 2026 13:26:40 +0200 Subject: [PATCH 349/681] ninjabackend: handle Program in unwrap_dep_list Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 8f9bb43a1000..616f936e4741 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -24,6 +24,7 @@ from .. import build from .. import mlog from .. import compilers +from .. import programs from .. import tooldetect from ..arglist import CompilerArgs from ..compilers import Compiler, is_library @@ -1248,6 +1249,10 @@ def unwrap_dep_list(self, target: T.Union[build.CustomTarget, build.RunTarget]) deps = [] for i in target.get_dependencies(): # Add a dependency on all the outputs of this target + if isinstance(i, build.LocalProgram): + i = i.program + if isinstance(i, programs.Program): + continue for output in i.get_outputs(): deps.append(os.path.join(self.get_target_dir(i), output)) return deps From f01539f628cfe9a9f244076dae2693deac0127ff Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 28 May 2026 15:47:56 +0200 Subject: [PATCH 350/681] ninjabackend: handle GeneratedList in unwrap_dep_list Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 616f936e4741..7dbc36d6d860 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1254,7 +1254,10 @@ def unwrap_dep_list(self, target: T.Union[build.CustomTarget, build.RunTarget]) if isinstance(i, programs.Program): continue for output in i.get_outputs(): - deps.append(os.path.join(self.get_target_dir(i), output)) + if isinstance(i, GeneratedList): + deps.append(os.path.join(self.get_target_private_dir(target), output)) + else: + deps.append(os.path.join(self.get_target_dir(i), output)) return deps def generate_custom_target(self, target: build.CustomTarget) -> None: From dc7af94da0ffbf108ab3bd45e56c1c368a38d7c1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 28 May 2026 13:58:01 +0200 Subject: [PATCH 351/681] ninjabackend: extend and reuse unwrap_dep_list Fixes Program handling in generate_genlist_for_target(). Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 7dbc36d6d860..803c094bd1af 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1245,9 +1245,9 @@ def custom_target_generator_inputs(self, target: build.CustomTarget) -> None: if isinstance(s, build.GeneratedList): self.generate_genlist_for_target(s, target) - def unwrap_dep_list(self, target: T.Union[build.CustomTarget, build.RunTarget]) -> T.List[str]: + def unwrap_dep_list(self, target: build.Target, dep_targets: T.Iterable[build.Target | build.GeneratedList | build.CustomTargetIndex | programs.Program]) -> T.List[str]: deps = [] - for i in target.get_dependencies(): + for i in dep_targets: # Add a dependency on all the outputs of this target if isinstance(i, build.LocalProgram): i = i.program @@ -1255,6 +1255,7 @@ def unwrap_dep_list(self, target: T.Union[build.CustomTarget, build.RunTarget]) continue for output in i.get_outputs(): if isinstance(i, GeneratedList): + assert isinstance(target, (build.BuildTarget, build.CustomTarget, build.CustomTargetIndex)) deps.append(os.path.join(self.get_target_private_dir(target), output)) else: deps.append(os.path.join(self.get_target_dir(i), output)) @@ -1263,7 +1264,7 @@ def unwrap_dep_list(self, target: T.Union[build.CustomTarget, build.RunTarget]) def generate_custom_target(self, target: build.CustomTarget) -> None: self.custom_target_generator_inputs(target) (srcs, ofilenames, cmd) = self.eval_custom_target_command(target) - deps = self.unwrap_dep_list(target) + deps = self.unwrap_dep_list(target, target.get_dependencies()) deps += self.get_target_depend_files(target) if target.build_always_stale: deps.append('PHONY') @@ -1329,7 +1330,7 @@ def generate_run_target(self, target: build.RunTarget) -> None: elem.add_item('COMMAND', meson_exe_cmd) 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.unwrap_dep_list(target, target.get_dependencies()) deps += self.get_target_depend_files(target) elem.add_dep(deps) self.add_build(elem) @@ -2849,11 +2850,9 @@ def generate_genlist_for_target(self, genlist: build.GeneratedList, target: buil subdir = genlist.subdir exe = generator.get_exe() infilelist = genlist.get_inputs() - extra_dependencies = self.get_target_depend_files(genlist) - for d in genlist.extra_depends: - # Add a dependency on all the outputs of this target - for output in d.get_outputs(): - extra_dependencies.append(os.path.join(self.get_target_dir(d), output)) + dependencies = self.get_target_depend_files(genlist) + dependencies += self.unwrap_dep_list(target, generator.depends) + dependencies += self.unwrap_dep_list(target, genlist.extra_depends) for curfile in infilelist: infilename = curfile.rel_to_builddir(self.build_to_src, self.get_target_private_dir(target)) base_args = generator.get_arglist(infilename) @@ -2888,11 +2887,9 @@ def generate_genlist_for_target(self, genlist: build.GeneratedList, target: buil os.makedirs(abs_pdir, exist_ok=True) elem = NinjaBuildElement(self.all_outputs, outfilespriv, rulename, infilename) - elem.add_dep([self.get_target_filename(x) for x in generator.depends]) + elem.add_dep(dependencies) if generator.depfile is not None: elem.add_item('DEPFILE', depfile) - if len(extra_dependencies) > 0: - elem.add_dep(extra_dependencies) if len(generator.outputs) == 1: what = f'{sole_output!r}' From 8e705ad9fb5811fa824934fbbfa1896b63551a1f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 28 May 2026 13:42:09 +0200 Subject: [PATCH 352/681] ninjabackend: fix cython + generators Extract the earlier choice of directory into its own variable, and reuse it when creating the File. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 803c094bd1af..912c6411b3b5 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1908,11 +1908,12 @@ def generate_cython_transpile(self, target: build.BuildTarget) -> \ header_deps = [] # Keep track of generated headers for those sources for gen in target.get_generated_sources(): + if isinstance(gen, GeneratedList): + builddir = self.get_target_private_dir(target) + else: + builddir = self.get_target_dir(gen) for ssrc in gen.get_outputs(): - if isinstance(gen, GeneratedList): - ssrc = os.path.join(self.get_target_private_dir(target), ssrc) - else: - ssrc = os.path.join(gen.get_builddir(), ssrc) + ssrc = os.path.join(builddir, ssrc) if ssrc.endswith('.pyx'): output = os.path.join(self.get_target_private_dir(target), f'{ssrc}.{ext}') element = NinjaBuildElement( @@ -1925,7 +1926,7 @@ def generate_cython_transpile(self, target: build.BuildTarget) -> \ # TODO: introspection? cython_sources.append(output) else: - generated_sources[ssrc] = mesonlib.File.from_built_file(gen.get_builddir(), ssrc) + generated_sources[ssrc] = mesonlib.File.from_built_file(builddir, ssrc) # Following logic in L883-900 where we determine whether to add generated source # as a header(order-only) dep to the .so compilation rule if not compilers.is_source(ssrc) and \ From 57d74ea542efb56d4aa8faa2aa83b3a747e9b857 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 28 May 2026 13:53:28 +0200 Subject: [PATCH 353/681] ninjabackend: use consistent types for generated sources dict Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 49 +++++++++++++++--------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 912c6411b3b5..81aa5735dde1 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -794,13 +794,13 @@ def get_generated_headers(self, target: build.BuildTarget) -> T.List[FileOrStrin self._generated_header_cache[tid] = header_deps return header_deps - def get_target_generated_sources(self, target: build.BuildTarget) -> T.MutableMapping[str, File]: + def get_target_generated_sources(self, target: build.BuildTarget) -> T.MutableMapping[str, File | build.GeneratedTypes]: """ Returns a dictionary with the keys being the path to the file (relative to the build directory) and the value being the File object representing the same path. """ - srcs: T.MutableMapping[str, File] = OrderedDict() + srcs: T.MutableMapping[str, File | build.GeneratedTypes] = {} for gensrc in target.get_generated_sources(): for s in gensrc.get_outputs(): rel_src = self.get_target_generated_dir(target, gensrc, s) @@ -954,7 +954,7 @@ def generate_target(self, target: T.Union[build.Target]) -> None: # GeneratedList and CustomTarget sources to be built; dict of the full # path to source relative to build root and the generating target/list - generated_sources: T.MutableMapping[str, File] + generated_sources: T.MutableMapping[str, File | build.GeneratedTypes] # List of sources that have been transpiled from a DSL (like Vala) into # a language that is handled below, such as C or C++ @@ -1522,7 +1522,7 @@ def generate_jar_target(self, target: build.Jar) -> None: # Add possible java generated files to src list generated_sources = self.get_target_generated_sources(target) gen_src_list = [] - for rel_src in generated_sources.keys(): + for rel_src in generated_sources: raw_src = File.from_built_relative(rel_src) if rel_src.endswith('.java'): gen_src_list.append(raw_src) @@ -1609,7 +1609,7 @@ def generate_cs_target(self, target: build.BuildTarget) -> None: outputs = [outname_rel] generated_sources = self.get_target_generated_sources(target) generated_rel_srcs = [] - for rel_src in generated_sources.keys(): + for rel_src in generated_sources: if rel_src.lower().endswith('.cs'): generated_rel_srcs.append(os.path.normpath(rel_src)) deps.append(os.path.normpath(rel_src)) @@ -1640,7 +1640,7 @@ def determine_java_compile_args(self, target: build.Jar, compiler: Compiler) -> def generate_java_compile(self, srcs: T.List[File], target: build.BuildTarget, compiler: Compiler, args: T.List[str]) -> T.List[str]: deps = [os.path.join(self.get_target_dir(l), l.get_filename()) for l in target.link_targets] generated_sources = self.get_target_generated_sources(target) - for rel_src in generated_sources.keys(): + for rel_src in generated_sources: if rel_src.endswith('.java'): deps.append(rel_src) @@ -1692,7 +1692,7 @@ def determine_dep_vapis(self, target: build.BuildTarget) -> T.List[str]: def split_vala_sources(self, t: build.BuildTarget) -> \ T.Tuple[T.MutableMapping[str, File | build.GeneratedTypes], T.MutableMapping[str, File | build.GeneratedTypes], - T.Tuple[T.MutableMapping[str, File | build.GeneratedTypes], T.MutableMapping[str, File | build.GeneratedTypes]]]: + T.MutableMapping[str, File], T.MutableMapping[str, File | build.GeneratedTypes]]: """ Splits the target's sources into .vala, .gs, .vapi, and other sources. Handles both preexisting and generated sources. @@ -1703,7 +1703,7 @@ def split_vala_sources(self, t: build.BuildTarget) -> \ """ vala: T.MutableMapping[str, File | build.GeneratedTypes] = OrderedDict() vapi: T.MutableMapping[str, File | build.GeneratedTypes] = OrderedDict() - others: T.MutableMapping[str, File | build.GeneratedTypes] = OrderedDict() + others: T.MutableMapping[str, File] = OrderedDict() othersgen: T.MutableMapping[str, File | build.GeneratedTypes] = OrderedDict() # Split preexisting sources for s in t.get_sources(): @@ -1714,39 +1714,38 @@ def split_vala_sources(self, t: build.BuildTarget) -> \ raise InvalidArguments(f'All sources in target {t!r} must be of type mesonlib.File, not {s!r}') f = s.rel_to_builddir(self.build_to_src) if s.endswith(('.vala', '.gs')): - srctype = vala + vala[f] = s elif s.endswith('.vapi'): - srctype = vapi + vapi[f] = s else: - srctype = others - srctype[f] = s + others[f] = s # Split generated sources for gensrc in t.get_generated_sources(): for s in gensrc.get_outputs(): f = self.get_target_generated_dir(t, gensrc, s) if s.endswith(('.vala', '.gs')): - srctype = vala + gensrctype = vala elif s.endswith('.vapi'): - srctype = vapi + gensrctype = vapi # Generated non-Vala (C/C++) sources. Won't be used for # generating the Vala compile rule below. else: - srctype = othersgen + gensrctype = othersgen # Duplicate outputs are disastrous - if f in srctype and srctype[f] != gensrc: + if f in gensrctype and gensrctype[f] != gensrc: msg = 'Duplicate output {0!r} from {1!r} {2!r}; ' \ 'conflicts with {0!r} from {4!r} {3!r}' \ ''.format(f, type(gensrc).__name__, gensrc.name, - srctype[f], type(srctype[f]).__name__) + gensrctype[f], type(gensrctype[f]).__name__) raise InvalidArguments(msg) # Store 'somefile.vala': GeneratedList (or CustomTarget) - srctype[f] = gensrc - return vala, vapi, (others, othersgen) + gensrctype[f] = gensrc + return vala, vapi, others, othersgen def generate_vala_compile(self, target: build.BuildTarget) -> \ - T.Tuple[T.MutableMapping[str, File], T.MutableMapping[str, File], T.List[str]]: + T.Tuple[T.MutableMapping[str, File], T.MutableMapping[str, File | build.GeneratedTypes], T.List[str]]: """Vala is compiled into C. Set up all necessary build steps here.""" - (vala_src, vapi_src, other_src) = self.split_vala_sources(target) + (vala_src, vapi_src, others, othersgen) = self.split_vala_sources(target) extra_dep_files = [] if not vala_src: raise InvalidArguments(f'Vala library {target.name!r} has no Vala or Genie source files.') @@ -1835,7 +1834,7 @@ def generate_vala_compile(self, target: build.BuildTarget) -> \ args += ['--shared-library', shared_target.get_filename()] # Detect gresources and add --gresources/--gresourcesdir arguments for each gres_dirs = [] - for gensrc in other_src[1].values(): + for gensrc in othersgen.values(): if isinstance(gensrc, modules.GResourceTarget): gres_xml, = self.get_custom_target_sources(gensrc) args += ['--gresources=' + gres_xml] @@ -1861,14 +1860,14 @@ def generate_vala_compile(self, target: build.BuildTarget) -> \ element.add_dep(extra_dep_files) self.add_build(element) self.create_target_source_introspection(target, valac, args, all_files, []) - return other_src[0], other_src[1], vala_c_src + return others, othersgen, vala_c_src def generate_cython_transpile(self, target: build.BuildTarget) -> \ - T.Tuple[T.MutableMapping[str, File], T.MutableMapping[str, File], T.List[str]]: + T.Tuple[T.MutableMapping[str, File], T.MutableMapping[str, File | build.GeneratedTypes], T.List[str]]: """Generate rules for transpiling Cython files to C or C++""" static_sources: T.MutableMapping[str, File] = OrderedDict() - generated_sources: T.MutableMapping[str, File] = OrderedDict() + generated_sources: T.MutableMapping[str, File | build.GeneratedTypes] = OrderedDict() cython_sources: T.List[str] = [] cython = target.compilers['cython'] From e1a99b105152db0f03fb47a7b19cedb91d8e3abb Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 28 May 2026 14:05:13 +0200 Subject: [PATCH 354/681] build: define interface superclass for targets that have get() This allows asserting in ninjabackend.py that it exists. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 1 + mesonbuild/build.py | 22 +++++++++++++++------- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 81aa5735dde1..d3e0849888de 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1804,6 +1804,7 @@ def generate_vala_compile(self, target: build.BuildTarget) -> \ args += ['--directory', c_out_dir] args += ['--basedir', srcbasedir] if target.is_linkable_target(): + assert isinstance(target, build.LinkableTarget) # Library name args += ['--library', target.name] # Outputted header diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 3cb9a6660332..82d1f4dbd2b8 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -37,7 +37,7 @@ from .interpreterbase import FeatureNew, FeatureDeprecated if T.TYPE_CHECKING: - from typing_extensions import Literal, Self, TypeAlias, TypedDict + from typing_extensions import Literal, TypeAlias, TypedDict from .arglist import CompilerArgs from .environment import Environment @@ -1995,6 +1995,13 @@ def get_platform_scheme_name(self) -> str: return 'unix' +class LinkableTarget(metaclass=SimpleABC): + @abc.abstractmethod + def get(self, lib_type: _LibraryType) -> LinkableTargetTypes: + """If applicable, return the shared or static "part" of this target. + Otherwise, just return self.""" + + class FileInTargetPrivateDir: """Represents a file with the path '/path/to/build/target_private_dir/fname'. target_private_dir is the return value of get_target_private_dir which is e.g. 'subdir/target.p'. @@ -2200,7 +2207,7 @@ def get_basename(self) -> str: return self.generator.name -class Executable(BuildTarget): +class Executable(BuildTarget, LinkableTarget): typename = 'executable' def __init__( @@ -2333,7 +2340,7 @@ def is_linkable_target(self) -> bool: return self.is_linkwithable -class StaticLibrary(BuildTarget): +class StaticLibrary(BuildTarget, LinkableTarget): typename = 'static library' def __init__( @@ -2512,7 +2519,7 @@ def _bundle_static_library(self, t: StaticTargetTypes, promoted: bool = False) - else: self.objects.append(t.extract_all_objects()) -class SharedLibrary(BuildTarget): +class SharedLibrary(BuildTarget, LinkableTarget): typename = 'shared library' # Used by AIX to decide whether to archive shared library or not. @@ -2877,7 +2884,7 @@ def __init__( def get_default_install_dir(self) -> T.Tuple[str, str]: return self.environment.get_shared_module_dir(), '{moduledir_shared}' -class BothLibraries(SecondLevelHolder): +class BothLibraries(SecondLevelHolder, LinkableTarget): typename: T.ClassVar[str] = 'both libraries' def __init__(self, shared: SharedLibrary, static: StaticLibrary, preferred_library: Literal['shared', 'static']) -> None: @@ -2958,7 +2965,7 @@ def flatten_command(cmd: T.Sequence[str | File | programs.Program | BuildTargetT return final_cmd, depend_files, dependencies -class CustomTargetBase(metaclass=SimpleABC): +class CustomTargetBase(LinkableTarget, metaclass=SimpleABC): ''' Base class for CustomTarget and CustomTargetIndex This base class can be used to provide a dummy implementation of some @@ -2983,8 +2990,9 @@ def get_internal_static_libraries_recurse(self, result: OrderedSet[StaticTargetT def get_all_linked_targets(self) -> ImmutableListProtocol[BuildTargetTypes]: return [] - def get(self, lib_type: _LibraryType, recursive: bool = False) -> Self: + def get(self, lib_type: _LibraryType, recursive: bool = False) -> LinkableTargetTypes: """Base case used by BothLibraries""" + assert isinstance(self, (CustomTarget, CustomTargetIndex)) return self def uses_rust_abi(self) -> bool: From 55ba56b54f31947a2361f863c8da7722d973e481 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 28 May 2026 14:41:21 +0200 Subject: [PATCH 355/681] backends: add more complete typing of introspection data In particular, include the linker case. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/backends.py | 13 ++++++++++--- mesonbuild/backend/ninjabackend.py | 17 +++++++++++------ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index c2b0d7c28be7..b311a1656dc5 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -44,11 +44,11 @@ from ..mesonlib import FileMode, FileOrString from ..options import ElementaryOptionValues - from typing_extensions import Literal, TypedDict, NotRequired + from typing_extensions import Literal, TypedDict, NotRequired, TypeAlias _ALL_SOURCES_TYPE = T.List[T.Union[File, build.GeneratedTypes]] - class TargetIntrospectionData(TypedDict): + class CompilerIntrospectionData(TypedDict): language: T.Union[Language, Literal['unknown']] machine: NotRequired[str] @@ -56,7 +56,14 @@ class TargetIntrospectionData(TypedDict): parameters: T.List[str] sources: T.List[str] generated_sources: T.List[str] + unity_sources: NotRequired[T.List[str]] + class LinkerIntrospectionData(TypedDict): + + linker: T.List[str] + parameters: T.List[str] + + TargetIntrospectionData: TypeAlias = T.Union['CompilerIntrospectionData', 'LinkerIntrospectionData'] # Languages that can mix with C or C++ but don't support unity builds yet # because the syntax we use for unity builds is specific to C/++/ObjC/++. @@ -1944,7 +1951,7 @@ def generate_subdir_install(self, d: InstallData) -> None: follow_symlinks=sd.follow_symlinks) d.install_subdirs.append(i) - def get_introspection_data(self, target_id: str, target: build.Target) -> T.List['TargetIntrospectionData']: + def get_introspection_data(self, target_id: str, target: build.Target) -> T.List[TargetIntrospectionData]: ''' Returns a list of source dicts with the following format for a given target: [ diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index d3e0849888de..741e0fc8a58f 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -48,7 +48,7 @@ from ..compilers.rust import RustCompiler from ..compilers.swift import SwiftCompiler from ..mesonlib import FileOrString - from .backends import TargetIntrospectionData + from .backends import TargetIntrospectionData, CompilerIntrospectionData, LinkerIntrospectionData CommandArgTypes = T.TypeVar('CommandArgTypes', 'NinjaCommandArg', str, 'NinjaCommandArg | str') CommandArgs = T.List[CommandArgTypes] @@ -511,7 +511,9 @@ def __init__(self, build: T.Optional[build.Build]): self.all_outputs: T.Set[str] = set() self.all_pch: T.Dict[str, T.Set[str]] = defaultdict(set) self.all_structured_sources: T.Set[str] = set() - self.introspection_data = {} + # 1st level: target; 2nd: compiler or linker; 3rd: individual keys + self.introspection_data: dict[str, dict[tuple[str, tuple[str, ...]] | tuple[str, ...], + TargetIntrospectionData]] = {} self.created_llvm_ir_rule = PerMachine(False, False) self.rust_crates: T.Dict[str, RustCrate] = {} self.implicit_meson_outs: T.List[str] = [] @@ -858,8 +860,10 @@ def create_target_source_introspection(self, target: build.Target, comp: compile lang = comp.get_language() tgt = self.introspection_data[tid] # Find an existing entry or create a new one - id_hash = (lang, tuple(parameters)) - src_block = tgt.get(id_hash, None) + id_hash: tuple[str, tuple] = (lang, tuple(parameters)) + + src_block: T.Optional[CompilerIntrospectionData] + src_block = T.cast('T.Optional[CompilerIntrospectionData]', tgt.get(id_hash, None)) if src_block is None: # Convert parameters if isinstance(parameters, CompilerArgs): @@ -891,8 +895,9 @@ def compute_path(file: FileOrString) -> str: def create_target_linker_introspection(self, target: build.Target, linker: T.Union[Compiler, StaticLinker], parameters: CompilerArgs) -> None: tid = target.get_id() tgt = self.introspection_data[tid] - lnk_hash = tuple(parameters) - lnk_block = tgt.get(lnk_hash, None) + lnk_hash: tuple[str, ...] = tuple(parameters) + lnk_block: T.Optional[LinkerIntrospectionData] + lnk_block = T.cast('T.Optional[LinkerIntrospectionData]', tgt.get(lnk_hash, None)) if lnk_block is None: paramlist = parameters.to_native(copy=True) From 47dabb99fe540c90fa390078c49e26a75563d537 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 28 May 2026 14:42:15 +0200 Subject: [PATCH 356/681] ninjabackend: explicitly use repr when including byte string in string Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 741e0fc8a58f..963a7d1f972c 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -617,7 +617,7 @@ def detect_prefix(out: bytes) -> T.TextIO: if result: return result - raise MesonException(f'Could not determine vs dep dependency prefix string. output: {stderr} {stdout}') + raise MesonException(f'Could not determine vs dep dependency prefix string. output: {stderr!r} {stdout!r}') def generate(self, capture: bool = False, vslite_ctx: T.Optional[T.Dict] = None) -> T.Optional[T.Dict[str, T.Dict[Language, T.List[str]]]]: captured_compile_args_per_target: T.Dict[str, T.Dict[Language, T.List[str]]] = {} From 6db82db9fcb1ca15f6efaef6f88a9a7998cc292f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 28 May 2026 14:42:36 +0200 Subject: [PATCH 357/681] ninjabackend is fully typed Signed-off-by: Paolo Bonzini --- run_mypy.py | 1 + 1 file changed, 1 insertion(+) diff --git a/run_mypy.py b/run_mypy.py index 224c3119a9ca..207f77a453e7 100755 --- a/run_mypy.py +++ b/run_mypy.py @@ -32,6 +32,7 @@ 'mesonbuild/arglist.py', 'mesonbuild/build.py', 'mesonbuild/backend/backends.py', + 'mesonbuild/backend/ninjabackend.py', 'mesonbuild/backend/nonebackend.py', 'mesonbuild/cmdline.py', 'mesonbuild/coredata.py', From 4f530d869b3d90e83736b45a9b0951bc26be457f Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 27 May 2026 10:36:09 -0700 Subject: [PATCH 358/681] interpreter: Pass value as a list in process_new_values --- mesonbuild/interpreter/interpreter.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 1120fd96c260..78fa097b7d48 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -489,8 +489,7 @@ def append_holder_map(self, held_type: T.Type[mesonlib.HoldableObject], holder_t held_type: holder_type }) - def process_new_values(self, invalues: T.List[T.Union[TYPE_var, ExecutableSerialisation]]) -> None: - invalues = listify(invalues) + def process_new_values(self, invalues: list[TYPE_var | ExecutableSerialisation] | list[build.GeneratedTypes | mesonlib.File | build.StructuredSources]) -> None: for v in invalues: if isinstance(v, ObjectHolder): raise InterpreterException('Modules must not return ObjectHolders') @@ -508,7 +507,7 @@ def process_new_values(self, invalues: T.List[T.Union[TYPE_var, ExecutableSerial elif isinstance(v, dependencies.InternalDependency): # FIXME: This is special cased and not ideal: # The first source is our new VapiTarget, the rest are deps - self.process_new_values(v.sources[0]) + self.process_new_values([v.sources[0]]) elif isinstance(v, build.InstallDir): self.build.install_dirs.append(v) elif isinstance(v, Test): From 6647ddb8a7ad86a27f062a61f7b182aabda549d6 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 27 May 2026 10:43:04 -0700 Subject: [PATCH 359/681] interpreter: Fix issues with find_program* There's a few related typing issues here, mostly with handling File when we expect a string. --- mesonbuild/interpreter/interpreter.py | 6 +++--- mesonbuild/wrap/wrap.py | 5 ++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 78fa097b7d48..16a7e7266001 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1799,7 +1799,7 @@ def find_program_fallback(self, fallback: SubProject, args: T.List[mesonlib.File required: bool, extra_info: T.List[mlog.TV_Loggable] ) -> T.Optional[Program]: mlog.log('Fallback to subproject', mlog.bold(fallback), 'which provides program', - mlog.bold(' '.join(args))) + mlog.bold(' '.join(str(a) for a in args))) sp_kwargs: kwtypes.DoSubproject = { 'required': required, 'default_options': default_options or {}, @@ -1827,7 +1827,7 @@ def func_find_program(self, node: mparser.BaseNode, args: T.Tuple[T.List[mesonli ) -> Program: disabled, required, feature = extract_required_kwarg(kwargs, self.subproject) if disabled: - mlog.log('Program', mlog.bold(' '.join(args[0])), 'skipped: feature', mlog.bold(feature), 'disabled') + mlog.log('Program', mlog.bold(' '.join(str(a) for a in args[0])), 'skipped: feature', mlog.bold(feature), 'disabled') return self.notfound_program(args[0]) search_dirs = extract_search_dirs(kwargs) @@ -2002,7 +2002,7 @@ def func_vcs_tag(self, node: mparser.BaseNode, args: T.List['TYPE_var'], kwargs: if isinstance(vcs_cmd[0], (str, mesonlib.File)): if isinstance(vcs_cmd[0], mesonlib.File): FeatureNew.single_use('vcs_tag with file as the first argument', '0.62.0', self.subproject, location=node) - maincmd = self.find_program_impl(vcs_cmd[0], required=False) + maincmd = self.find_program_impl([vcs_cmd[0]], required=False) if maincmd.found(): vcs_cmd[0] = maincmd else: diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py index ddbb7eea1d82..008b1efe1f43 100644 --- a/mesonbuild/wrap/wrap.py +++ b/mesonbuild/wrap/wrap.py @@ -502,8 +502,11 @@ def get_varname(self, subp_name: str, depname: str) -> T.Optional[str]: wrap = self.wraps.get(subp_name) return wrap.provided_deps.get(depname) if wrap else None - def find_program_provider(self, names: T.List[str]) -> T.Optional[SubProject]: + def find_program_provider(self, names: list[str | mesonlib.File]) -> T.Optional[SubProject]: for name in names: + # A File is always a local program, i.e., a script file in the source tree + if isinstance(name, mesonlib.File): + continue wrap = self.provided_programs.get(name) if wrap: return wrap.name From ee9d868ebd12f98b0f945494436626fc1046348f Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 27 May 2026 10:57:05 -0700 Subject: [PATCH 360/681] interpreter: ignore mypy error that is wrong This is correct, we require that `klass` is of type[T], where T must be Test or any subclass thereof. Mypy doesn't think that assigning `Test` to `type[T]` is valid in this case, but it absolutely is. pyright accepts this as fine. --- mesonbuild/interpreter/interpreter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 16a7e7266001..1f20250efbf5 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -2286,7 +2286,7 @@ def func_test(self, node: mparser.BaseNode, def make_test(self, node: mparser.BaseNode, args: T.Tuple[str, T.Union[build.Executable, build.Jar, Program, mesonlib.File, build.CustomTarget, build.CustomTargetIndex]], kwargs: kwtypes.FuncTest | kwtypes.FuncBenchmark, - klass: T.Type[TestClass] = Test) -> TestClass: + klass: T.Type[TestClass] = Test) -> TestClass: # type: ignore[assignment] name = args[0] if ':' in name: mlog.deprecation(f'":" is not allowed in test name "{name}", it has been replaced with "_"', From 505fdd384c9127f5569f63cfc50b8c6b5890de04 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 27 May 2026 11:13:18 -0700 Subject: [PATCH 361/681] interpreter/kwargs: Use a Mixin for vs_module_defs Not only does this reduce duplication, but it makes Library have the correct shape for SharedLibrary and SharedModule, which fixes some mypy errors in interpreter.py --- mesonbuild/interpreter/kwargs.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index 1a4779975518..28b410361c5d 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -436,18 +436,22 @@ class _LibraryMixin(TypedDict): rust_abi: T.Optional[RustAbi] +class _VsModuleDefsMixin(TypedDict): + + vs_module_defs: T.Optional[T.Union[str, File, build.CustomTarget, build.CustomTargetIndex]] + + class _ExecutableMixin(TypedDict): export_dynamic: T.Optional[bool] gui_app: T.Optional[bool] implib: T.Optional[T.Union[str, bool]] pie: T.Optional[bool] - vs_module_defs: T.Optional[T.Union[str, File, build.CustomTarget, build.CustomTargetIndex]] win_subsystem: T.Optional[str] android_exe_type: T.Optional[Literal['application', 'executable']] -class Executable(BuildTarget, _ExecutableMixin): +class Executable(BuildTarget, _ExecutableMixin, _VsModuleDefsMixin): pass @@ -469,17 +473,15 @@ class _SharedLibMixin(TypedDict): shortname: str -class SharedLibrary(BuildTarget, _SharedLibMixin, _LibraryMixin): - - vs_module_defs: T.Optional[T.Union[str, File, build.CustomTarget, build.CustomTargetIndex]] - +class SharedLibrary(BuildTarget, _SharedLibMixin, _LibraryMixin, _VsModuleDefsMixin): + pass -class SharedModule(BuildTarget, _LibraryMixin): - vs_module_defs: T.Optional[T.Union[str, File, build.CustomTarget, build.CustomTargetIndex]] +class SharedModule(BuildTarget, _LibraryMixin, _VsModuleDefsMixin): + pass -class Library(BuildTarget, _SharedLibMixin, _StaticLibMixin, _LibraryMixin): +class Library(BuildTarget, _SharedLibMixin, _StaticLibMixin, _LibraryMixin, _VsModuleDefsMixin): """For library, both_library, and as a base for build_target""" From 6aa0fba171a4400f2948e1567a333462bc3a16bd Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 27 May 2026 11:14:48 -0700 Subject: [PATCH 362/681] interpreter: Explicitly set type form make_test This is required because mypy struggles with the `TypeVar`. Pyright understands this. Likely newer versions of mypy do as well --- mesonbuild/interpreter/interpreter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 1f20250efbf5..ab13c51b334c 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -2345,7 +2345,7 @@ def add_test(self, node: mparser.BaseNode, if any(isinstance(i, ExternalProgram) for i in kwargs['args']): FeatureNew.single_use('test with program in args', '1.6.0', self.subproject) - t = self.make_test(node, args, kwargs) + t: Test = self.make_test(node, args, kwargs) if is_base_test: self.build.tests.append(t) mlog.debug('Adding test', mlog.bold(t.name, True)) From cae5f33c7c2780a812e34c8dc54a37447f6681e4 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 27 May 2026 11:17:20 -0700 Subject: [PATCH 363/681] interpreter: fix and ignore typeddict literal-required error Using a tuple should be sufficient here, as mypy (and pyright) infer keys from tuples. However, mypy still doesn't understand so we'll add a type[ignore] comment. When we reach a mypy that understands it will start warning about this. --- mesonbuild/interpreter/interpreter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index ab13c51b334c..3181c58ad31e 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -2698,8 +2698,8 @@ def validate_build_subdir(self, build_subdir: str, target: str) -> None: ) def func_configure_file(self, node: mparser.BaseNode, args: T.List[TYPE_var], kwargs: kwtypes.ConfigureFile) -> mesonlib.File: - actions = sorted(x for x in ['configuration', 'command', 'copy'] - if kwargs[x] not in [None, False]) + actions = sorted(x for x in ('configuration', 'command', 'copy') + if kwargs[x] not in [None, False]) # type: ignore[literal-required] num_actions = len(actions) if num_actions == 0: raise InterpreterException('Must specify an action with one of these ' From 7fb6a42c7ea5f9f70538461eed00d446537d57f5 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 27 May 2026 11:19:52 -0700 Subject: [PATCH 364/681] interpreter: fix return type of func_install_data --- mesonbuild/interpreter/interpreter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 3181c58ad31e..a82ff5d82deb 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -2555,7 +2555,7 @@ def _warn_kwarg_install_mode_sticky(self, mode: FileMode) -> FileMode: ) def func_install_data(self, node: mparser.BaseNode, args: T.Tuple[T.List['mesonlib.FileOrString']], - kwargs: 'kwtypes.FuncInstallData') -> build.Data: + kwargs: 'kwtypes.FuncInstallData') -> list[build.Data]: sources = self.source_strings_to_files(args[0] + kwargs['sources']) rename = kwargs['rename'] or None if rename: From 1d8459ab4ae00fbbcfa380e22b6aae70c0c6d8ae Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 27 May 2026 11:23:46 -0700 Subject: [PATCH 365/681] interpreter: fix typing issues in get and set variable functions --- mesonbuild/interpreter/interpreter.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index a82ff5d82deb..23623dff8c8b 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -3903,7 +3903,7 @@ def is_subproject(self) -> bool: @noKwargs @noArgsFlattening @noSecondLevelHolderResolving - def func_set_variable(self, node: mparser.BaseNode, args: T.Tuple[str, object], kwargs: 'TYPE_kwargs') -> None: + def func_set_variable(self, node: mparser.BaseNode, args: T.Tuple[str, TYPE_var | InterpreterObject], kwargs: 'TYPE_kwargs') -> None: varname, value = args if mparser.IDENT_RE.fullmatch(varname) is None: raise InvalidCode('Invalid variable name: ' + varname) @@ -3913,8 +3913,8 @@ def func_set_variable(self, node: mparser.BaseNode, args: T.Tuple[str, object], @noKwargs @noArgsFlattening @unholder_return - def func_get_variable(self, node: mparser.BaseNode, args: T.Tuple[T.Union[str, Disabler], T.Optional[object]], - kwargs: 'TYPE_kwargs') -> 'TYPE_var': + def func_get_variable(self, node: mparser.BaseNode, args: T.Tuple[T.Union[str, Disabler], T.Optional[TYPE_var | InterpreterObject]], + kwargs: 'TYPE_kwargs') -> TYPE_var | InterpreterObject: varname, fallback = args if isinstance(varname, Disabler): return varname From 4fbdadc82c8921b6a94cc8d64791167f3d81065f Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 27 May 2026 11:41:19 -0700 Subject: [PATCH 366/681] utils/universal: make functions that are generic but don't return take object Both of these functions can take a lot of different types, but they don't return a generic type (one returns `str | None`, one only raises). This fixes a bunch of type issues in the interpreter --- mesonbuild/utils/universal.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index 9a26d28b1dce..bf10f91effdf 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -1876,7 +1876,7 @@ def Popen_safe_logged(args: T.List[str], msg: str = 'Called', **kwargs: T.Any) - return p, o, e -def iter_regexin_iter(regexiter: T.Iterable[str], initer: T.Iterable[str | programs.Program]) -> T.Optional[str]: +def iter_regexin_iter(regexiter: T.Iterable[str], initer: T.Iterable[object]) -> T.Optional[str]: ''' Takes each regular expression in @regexiter and tries to search for it in every item in @initer. If there is a match, returns that match. @@ -1892,7 +1892,7 @@ def iter_regexin_iter(regexiter: T.Iterable[str], initer: T.Iterable[str | progr return None -def _substitute_values_check_errors(command: T.Sequence[str | programs.Program], values: T.Dict[str, T.Union[str, T.List[str]]]) -> None: +def _substitute_values_check_errors(command: T.Sequence[object], values: T.Dict[str, T.Union[str, T.List[str]]]) -> None: # Error checking inregex: T.List[str] = ['@INPUT([0-9]+)?@', '@PLAINNAME@', '@BASENAME@'] outregex: T.List[str] = ['@OUTPUT([0-9]+)?@', '@OUTDIR@'] From 64cbf1a5fa6d23e450c7db68938477fe380f9a75 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 27 May 2026 11:43:23 -0700 Subject: [PATCH 367/681] utils/universal: make substitute values generic To avoid the issue where we'd have a thousand different signatures for this, we can just make it generic a do a tiny bit of casting internally to help mypy when it can't figure out that _T must contain str. --- mesonbuild/utils/universal.py | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index bf10f91effdf..e3d83b4a0fe9 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -38,7 +38,6 @@ from ..cmdline import StrOrBytesPath from ..environment import Environment from ..compilers.compilers import Compiler - from .. import programs class _EnvPickleLoadable(Protocol): @@ -1932,19 +1931,9 @@ def _substitute_values_check_errors(command: T.Sequence[object], values: T.Dict[ raise MesonException(m.format(match2.group(), len(values['@OUTPUT@']))) -@T.overload -def substitute_values(command: T.List[str], +def substitute_values(command: T.List[_T], values: T.Dict[str, T.Union[str, T.List[str]]] - ) -> T.List[str]: ... - -@T.overload -def substitute_values(command: T.List[str | programs.Program], - values: T.Dict[str, T.Union[str, T.List[str]]] - ) -> T.List[str | programs.Program]: ... - -def substitute_values(command: T.List[str | programs.Program] | T.List[str], - values: T.Dict[str, T.Union[str, T.List[str]]] - ) -> T.List[str | programs.Program] | T.List[str]: + ) -> T.List[_T]: ''' Substitute the template strings in the @values dict into the list of strings @command and return a new list. For a full list of the templates, @@ -1983,7 +1972,7 @@ def replace(m: T.Match[str]) -> str: return v[0] return v - outcmd: T.List[str | programs.Program] = [] + outcmd: T.List[_T] = [] for vv in command: if not isinstance(vv, str): outcmd.append(vv) @@ -1991,22 +1980,27 @@ def replace(m: T.Match[str]) -> str: # Append values that are exactly a template string. # This is faster than a string replace, and makes it # possible to special case @INPUT@ and @OUTPUT@ too + + # We have to do a bunch of casting here because mypy doesn't realize + # that if we're adding str to this list then we got string inputs, + # and attempting to set the return type to `_T | str` causes + # problems elswhere, so let's contain the pain here. if vv == '@INPUT@': inputs = values['@INPUT@'] assert isinstance(inputs, list) - outcmd += inputs + outcmd += T.cast('list[_T]', inputs) elif vv == '@OUTPUT@': outputs = values['@OUTPUT@'] assert isinstance(outputs, list) - outcmd += outputs + outcmd += T.cast('list[_T]', outputs) else: o = values[vv] assert isinstance(o, str), 'for mypy' - outcmd.append(o) + outcmd.append(T.cast('_T', o)) else: # Substitute everything else with replacement assert values - outcmd.append(value_rx.sub(replace, vv)) + outcmd.append(T.cast('_T', value_rx.sub(replace, vv))) return outcmd From 97bef5ca61e6e4ca0b7be3781dce05c5cfc90afc Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 27 May 2026 11:52:10 -0700 Subject: [PATCH 368/681] interpreter: fix issues with run_command_impl There's an incorrect annotation, and some re-assignment changing type to fix here. --- mesonbuild/interpreter/interpreter.py | 4 ++-- mesonbuild/interpreter/kwargs.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 23623dff8c8b..03be20bed48e 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -2808,8 +2808,8 @@ def func_configure_file(self, node: mparser.BaseNode, args: T.List[TYPE_var], # Substitute @INPUT@, @OUTPUT@, etc here. _cmd = mesonlib.substitute_values(kwargs['command'], values) mlog.log('Configuring', mlog.bold(output), 'with command') - cmd, *args = _cmd - res = self.run_command_impl((cmd, args), + cmd, *_args = _cmd + res = self.run_command_impl((cmd, _args), {'capture': True, 'console': False, 'check': True, diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index 28b410361c5d..8d902790af65 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -336,7 +336,7 @@ class ConfigureFile(TypedDict): install_mode: FileMode install_tag: T.Optional[str] encoding: str - command: T.Optional[T.List[T.Union[build.Executable, ExternalProgram, Compiler, File, str]]] + command: T.Optional[T.List[T.Union[build.Executable, Program, Compiler, File, str]]] input: T.List[FileOrString] configuration: T.Optional[T.Union[T.Dict[str, T.Union[str, int, bool]], build.ConfigurationData]] macro_name: T.Optional[str] From c76afe2bbd9c0c090c66330d5727dc14f85c76ad Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 27 May 2026 12:03:52 -0700 Subject: [PATCH 369/681] interpreter: fix typing of func_environment It needed it's own kwargs annotations, and then we need to do some casting after using the validator. --- mesonbuild/interpreter/interpreter.py | 7 ++++--- mesonbuild/interpreter/kwargs.py | 6 ++++++ mesonbuild/interpreter/type_checking.py | 6 +++--- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 03be20bed48e..4bdd94ceb244 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -124,7 +124,7 @@ from ..options import OptionDict from ..mesonlib import SubProject from ..cmdline import SharedCMDOptions - from .type_checking import SourcesVarargsType + from .type_checking import SourcesVarargsType, FullEnvInitValueType # Input source types passed to Targets SourceInputs = T.Union[mesonlib.FileOrString, build.GeneratedTypes, build.BuildTarget, @@ -3096,7 +3096,7 @@ def _add_arguments(self, node: mparser.FunctionNode, argsdict: T.Dict[str, T.Lis @typed_pos_args('environment', optargs=[(str, list, dict)]) @typed_kwargs('environment', ENV_METHOD_KW, ENV_SEPARATOR_KW.evolve(since='0.62.0')) def func_environment(self, node: mparser.FunctionNode, args: T.Tuple[T.Union[None, str, T.List['TYPE_var'], T.Dict[str, 'TYPE_var']]], - kwargs: 'TYPE_kwargs') -> EnvironmentVariables: + kwargs: kwtypes.FuncEnvironment) -> EnvironmentVariables: init = args[0] if init is not None: FeatureNew.single_use('environment positional arguments', '0.52.0', self.subproject, location=node) @@ -3105,7 +3105,8 @@ def func_environment(self, node: mparser.FunctionNode, args: T.Tuple[T.Union[Non raise InvalidArguments(f'"environment": {msg}') if isinstance(init, dict) and any(i for i in init.values() if isinstance(i, list)): FeatureNew.single_use('List of string in dictionary value', '0.62.0', self.subproject, location=node) - return env_convertor_with_method(init, kwargs['method'], kwargs['separator']) + # the validator call above ensured that we have the correct type + return env_convertor_with_method(T.cast('FullEnvInitValueType', init), kwargs['method'], kwargs['separator']) return EnvironmentVariables() @typed_pos_args('join_paths', varargs=str, min_varargs=1) diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index 8d902790af65..fedb537c2cd0 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -575,3 +575,9 @@ class FuncDependency(ExtractRequired): class FuncExpectError(TypedDict): how: str + + +class FuncEnvironment(TypedDict): + + method: Literal['set', 'prepend', 'append'] + separator: str diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py index b36886975f9c..217766440764 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -32,7 +32,7 @@ from ..mesonlib import EnvInitValueType from ..interpreterbase.decorators import FeatureCheckBase - _FullEnvInitValueType = T.Union[EnvironmentVariables, T.List[str], T.List[T.List[str]], EnvInitValueType, str, None] + FullEnvInitValueType = T.Union[EnvironmentVariables, T.List[str], T.List[T.List[str]], EnvInitValueType, str, None] PkgConfigDefineType = T.Optional[T.Tuple[T.Tuple[str, str], ...]] SourcesVarargsType = T.List[T.Union[str, File, GeneratedTypes, StructuredSources, ExtractedObjects, BuildTarget]] @@ -243,7 +243,7 @@ def split_equal_string(input: str) -> T.Tuple[str, str]: # Split _env_convertor() and env_convertor_with_method() to make mypy happy. # It does not want extra arguments in KwargInfo convertor callable. -def env_convertor_with_method(value: _FullEnvInitValueType, +def env_convertor_with_method(value: FullEnvInitValueType, init_method: Literal['set', 'prepend', 'append'] = 'set', separator: str = os.pathsep) -> EnvironmentVariables: if isinstance(value, str): @@ -256,7 +256,7 @@ def env_convertor_with_method(value: _FullEnvInitValueType, return EnvironmentVariables() return value -def _env_convertor(value: _FullEnvInitValueType) -> EnvironmentVariables: +def _env_convertor(value: FullEnvInitValueType) -> EnvironmentVariables: return env_convertor_with_method(value) ENV_KW: KwargInfo[T.Union[EnvironmentVariables, T.List, T.Dict, str, None]] = KwargInfo( From fa67f8026dbc05d8ded11c14a882ae70601576fa Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 27 May 2026 12:07:09 -0700 Subject: [PATCH 370/681] interpreter: fix mypy errors in make_test The first one (needing to cast the .get()) makes no sense, it seems like a mypy bug. Probably fixed in newer mypy. Also, `Test` needs to accept Jar as an executable. --- mesonbuild/interpreter/interpreter.py | 2 +- mesonbuild/interpreter/interpreterobjects.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 4bdd94ceb244..060aa2daccc0 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -2326,7 +2326,7 @@ def make_test(self, node: mparser.BaseNode, suite, exe, kwargs['depends'], - kwargs.get('is_parallel', False), + T.cast('bool', kwargs.get('is_parallel', False)), kwargs['args'], kwargs['env'], expected_fail, diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index c9c49bbd54d6..deb347a65d27 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -839,7 +839,7 @@ class GeneratedObjectsHolder(ObjectHolder[build.ExtractedObjects]): class Test(MesonInterpreterObject): def __init__(self, name: str, project: str, suite: T.List[str], - exe: T.Union[Program, build.Executable, build.CustomTarget, build.CustomTargetIndex], + exe: T.Union[Program, build.Executable, build.CustomTarget, build.CustomTargetIndex, build.Jar], depends: T.Sequence[kwargs.TargetDepends], is_parallel: bool, cmd_args: T.List[T.Union[str, mesonlib.File, build.Target, Program]], From 55b2a3aa2eff336b4b2fc50566bd6b15c4c5d6b9 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 27 May 2026 12:14:00 -0700 Subject: [PATCH 371/681] utils/universal: fix typing of check_direntry_issues We currently pass item other than iterables of str | bytes, which is what we expect. Accept any object, and then only go down the warn path if we have str | bytes input. Fixes typing in the interpreter --- mesonbuild/utils/universal.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index e3d83b4a0fe9..05e3ebf4eeea 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -268,7 +268,7 @@ def is_ascii_string(astring: T.Union[str, bytes]) -> bool: return True -def check_direntry_issues(direntry_array: T.Union[T.Iterable[T.Union[str, bytes]], str, bytes]) -> None: +def check_direntry_issues(direntry_array: T.Iterable[object]) -> None: import locale # Warn if the locale is not UTF-8. This can cause various unfixable issues # such as os.stat not being able to decode filenames with unicode in them. @@ -279,13 +279,13 @@ def check_direntry_issues(direntry_array: T.Union[T.Iterable[T.Union[str, bytes] if isinstance(direntry_array, (str, bytes)): direntry_array = [direntry_array] for de in direntry_array: - if is_ascii_string(de): - continue - mlog.warning(textwrap.dedent(f''' - You are using {e!r} which is not a Unicode-compatible - locale but you are trying to access a file system entry called {de!r} which is - not pure ASCII. This may cause problems. - ''')) + if isinstance(de, (str, bytes)) and not is_ascii_string(de): + mlog.warning(textwrap.dedent(f''' + You are using {e!r} which is not a Unicode-compatible + locale but you are trying to access a file system entry called {de!r} which is + not pure ASCII. This may cause problems. + ''')) + class SimpleABC(type): '''Lightweight replacement for ``abc.ABCMeta``. From af825c2e2632cf5a10ba4a586abc794f0aa0b24c Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 27 May 2026 13:07:10 -0700 Subject: [PATCH 372/681] interpreter: fix remaining issues with source_strings_to_files This is not elegant at all, in fact it's ugly as sin. However, since I cannot convince mypy 1.9 to understand an approach with generics, this is the only real option we have. Lots' of type ignores, lots of duplicate code. But it works. --- mesonbuild/interpreter/interpreter.py | 58 ++++++++++++++++--------- mesonbuild/interpreter/kwargs.py | 5 +-- mesonbuild/interpreter/type_checking.py | 4 +- 3 files changed, 41 insertions(+), 26 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 060aa2daccc0..4d71ca5c4c2f 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -134,7 +134,7 @@ build.ExtractedObjects, build.StructuredSources] # Sources for custom targets, which can also include ExternalProgram CustomTargetSources = T.Union[mesonlib.File, build.GeneratedTypes, build.BuildTarget, - build.ExtractedObjects, ExternalProgram] + build.ExtractedObjects, Program] BuildTargetSource = T.Union[mesonlib.FileOrString, build.GeneratedTypes, build.StructuredSources] @@ -3222,34 +3222,53 @@ def do_validate_within_subproject(norm: str) -> None: self.validated_cache.add(fname) @T.overload - def source_strings_to_files(self, sources: T.List['mesonlib.FileOrString']) -> T.List['mesonlib.File']: ... + def source_strings_to_files(self, sources: list[str]) -> list[mesonlib.File]: ... @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]]: ... + def source_strings_to_files(self, sources: list['mesonlib.FileOrString']) -> list['mesonlib.File']: ... @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 + def source_strings_to_files(self, sources: list[T.Union[mesonlib.FileOrString, build.BuildTargetTypes]]) -> list[T.Union[mesonlib.File, build.BuildTargetTypes]]: ... # type: ignore[overload-overlap] @T.overload - def source_strings_to_files(self, sources: T.List[kwtypes.CustomTargetInputs]) -> T.List['CustomTargetSources']: ... # noqa: F811 + def source_strings_to_files(self, sources: list[T.Union[mesonlib.FileOrString, build.GeneratedTypes]]) -> list[T.Union[mesonlib.File, build.GeneratedTypes]]: ... @T.overload - def source_strings_to_files(self, sources: T.List[T.Union[mesonlib.FileOrString, build.BuildTargetTypes, build.BothLibraries, build.ExtractedObjects, build.GeneratedTypes]], - strict: bool = True - ) -> T.List[T.Union[mesonlib.File, build.BuildTargetTypes, build.BothLibraries, build.ExtractedObjects, build.GeneratedTypes]]: ... # noqa: F811 + def source_strings_to_files(self, sources: list[kwtypes.CustomTargetInputs]) -> list['CustomTargetSources']: ... # type: ignore[overload-overlap] @T.overload - def source_strings_to_files(self, sources: T.List[T.Union[mesonlib.FileOrString, build.GeneratedTypes, build.StructuredSources]], - strict: bool = True, - ) -> T.List[T.Union[mesonlib.File, build.GeneratedTypes, build.StructuredSources]]: ... # noqa: F811 + def source_strings_to_files(self, sources: list[T.Union[mesonlib.FileOrString, build.BuildTargetTypes, build.BothLibraries, build.ExtractedObjects, build.GeneratedTypes]], # type: ignore[overload-overlap] + ) -> list[T.Union[mesonlib.File, build.BuildTargetTypes, build.BothLibraries, build.ExtractedObjects, build.GeneratedTypes]]: ... @T.overload - def source_strings_to_files(self, sources: T.List['SourceInputs']) -> T.List['SourceOutputs']: ... # noqa: F811 + def source_strings_to_files(self, sources: list[T.Union[mesonlib.FileOrString, build.GeneratedTypes, build.StructuredSources]], + ) -> list[T.Union[mesonlib.File, build.GeneratedTypes, build.StructuredSources]]: ... # noqa: F811 @T.overload - def source_strings_to_files(self, sources: T.List[SourcesVarargsType]) -> T.List['SourceOutputs']: ... # noqa: F811 + def source_strings_to_files(self, sources: list[SourcesVarargsType]) -> list['SourceOutputs']: ... - def source_strings_to_files(self, sources: T.List['SourceInputs']) -> T.List['SourceOutputs']: # noqa: F811 + @T.overload + def source_strings_to_files(self, sources: list['SourceInputs']) -> list['SourceOutputs']: ... # type: ignore[overload-cannot-match] + + def source_strings_to_files(self, + sources: T.Union[ + list[str], + list[mesonlib.File | str], + list[mesonlib.File | str | build.BuildTargetTypes], + list[mesonlib.File | str | build.GeneratedTypes], + list[kwtypes.CustomTargetInputs], + list[mesonlib.File | str | build.BuildTargetTypes | build.BothLibraries | build.ExtractedObjects | build.GeneratedTypes], + list[mesonlib.File | str | build.GeneratedTypes | build.StructuredSources], + list[SourceInputs], + list[SourcesVarargsType], + ] + ) -> T.Union[list[mesonlib.File], + list[mesonlib.File | build.BuildTargetTypes], + list[mesonlib.File | build.GeneratedTypes], + list[mesonlib.File | build.BuildTargetTypes | build.BothLibraries | build.ExtractedObjects | build.GeneratedTypes], + list[mesonlib.File | build.GeneratedTypes | build.StructuredSources], + list[CustomTargetSources], + list[SourceOutputs]]: """Lower inputs to a list of Targets and Files, replacing any strings. :param sources: A raw (Meson DSL) list of inputs (targets, files, and @@ -3258,9 +3277,7 @@ def source_strings_to_files(self, sources: T.List['SourceInputs']) -> T.List['So :return: A list of Targets and Files """ mesonlib.check_direntry_issues(sources) - if not isinstance(sources, list): - sources = [sources] - results: T.List['SourceOutputs'] = [] + results: list[mesonlib.File | build.GeneratedTypes | build.BuildTarget | build.ExtractedObjects | build.StructuredSources | Program] = [] # In Meson 2.0 this shoul not add ALLOW_BUILD_DIR_FILE_REFERENCES # universally, and we should just use self.relaxations @@ -3279,15 +3296,14 @@ def source_strings_to_files(self, sources: T.List['SourceInputs']) -> T.List['So results.append(mesonlib.File.from_built_file(self.subdir, s)) else: results.append(mesonlib.File.from_source_file(self.environment.source_dir, self.subdir, s)) - elif isinstance(s, (mesonlib.File, ExternalProgram, - build.GeneratedList, build.BuildTarget, - build.CustomTargetIndex, build.CustomTarget, + elif isinstance(s, (mesonlib.File, build.GeneratedList, Program, + build.BuildTarget, build.CustomTargetIndex, build.CustomTarget, build.ExtractedObjects, build.StructuredSources)): results.append(s) else: raise InterpreterException(f'Source item is {s!r} instead of ' 'string or File-type object') - return results + return results # type: ignore[return-value] def validate_forbidden_targets(self, name: str, in_root: bool) -> None: if name.startswith('meson-internal__'): diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index fedb537c2cd0..cfabcd67b46e 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -24,7 +24,7 @@ TestArgs = T.Union[str, File, build.Target, Program] TargetDepends = T.Union[build.CustomTarget, build.CustomTargetIndex, build.BuildTarget, build.GeneratedList, Program] CustomTargetInputs = T.Union[str, build.BuildTarget, build.GeneratedTypes, - build.ExtractedObjects, ExternalProgram, File] + build.ExtractedObjects, Program, File] RustAbi = Literal['rust', 'c'] class NativeKW(TypedDict): @@ -314,8 +314,7 @@ class VcsTag(TypedDict): command: T.List[T.Union[str, build.BuildTargetTypes, Program, File]] fallback: T.Optional[str] - input: T.List[T.Union[str, build.BuildTarget, build.GeneratedTypes, - build.ExtractedObjects, Program, File]] + input: list[CustomTargetInputs] output: T.List[str] replace_string: str install: bool diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py index 217766440764..fdbfc20f05ba 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -352,7 +352,7 @@ def _output_validator(outputs: T.List[str]) -> T.Optional[str]: validator=lambda x: _output_validator([x]) ) -def _local_program_convertor(raw: T.List[T.Union[str, File, BuildTarget, GeneratedTypes, ExtractedObjects, LocalProgram]]) -> T.List[CustomTargetInputs]: +def _local_program_convertor(raw: T.List[T.Union[str, File, BuildTarget, GeneratedTypes, ExtractedObjects, Program]]) -> T.List[CustomTargetInputs]: result: T.List[CustomTargetInputs] = [] for i in raw: if isinstance(i, LocalProgram): @@ -361,7 +361,7 @@ def _local_program_convertor(raw: T.List[T.Union[str, File, BuildTarget, Generat result.append(i) return result -CT_INPUT_KW: KwargInfo[T.List[T.Union[str, File, BuildTarget, GeneratedTypes, ExtractedObjects, LocalProgram]]] = KwargInfo( +CT_INPUT_KW: KwargInfo[T.List[T.Union[str, File, BuildTarget, GeneratedTypes, ExtractedObjects, Program]]] = KwargInfo( 'input', ContainerTypeInfo(list, (str, File, BuildTarget, CustomTarget, CustomTargetIndex, ExtractedObjects, GeneratedList, Program)), listify=True, From 0d7c892b12e89af8dbee3f96134fb218ed6f6f05 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 27 May 2026 13:08:34 -0700 Subject: [PATCH 373/681] run_mypy: The whole interpreter is type safe! --- run_mypy.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/run_mypy.py b/run_mypy.py index 207f77a453e7..be8f5592d655 100755 --- a/run_mypy.py +++ b/run_mypy.py @@ -19,7 +19,7 @@ 'mesonbuild/cmake/', 'mesonbuild/compilers/', 'mesonbuild/dependencies/', - 'mesonbuild/interpreter/primitives/', + 'mesonbuild/interpreter/', 'mesonbuild/interpreterbase/', 'mesonbuild/linkers/', 'mesonbuild/modules/', @@ -39,11 +39,6 @@ 'mesonbuild/depfile.py', 'mesonbuild/envconfig.py', 'mesonbuild/environment.py', - 'mesonbuild/interpreter/compiler.py', - 'mesonbuild/interpreter/dependencyfallbacks.py', - 'mesonbuild/interpreter/mesonmain.py', - 'mesonbuild/interpreter/interpreterobjects.py', - 'mesonbuild/interpreter/type_checking.py', 'mesonbuild/machinefile.py', 'mesonbuild/mesondata.py', 'mesonbuild/mcompile.py', From 261971657df76e5171ed1e518c9afcf964673bac Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 30 May 2026 11:33:59 +0200 Subject: [PATCH 374/681] backends: move and rename unwrap_dep_list Prepare for using it in the VS2010 backend. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/backends.py | 17 +++++++++++++++++ mesonbuild/backend/ninjabackend.py | 25 ++++--------------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index b311a1656dc5..b079ab3cace7 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -1472,6 +1472,23 @@ def get_custom_target_sources(self, target: build.RunTarget | build.CustomTarget srcs += fname return srcs + def get_paths_for_dep_outputs(self, target: build.Target, dep_targets: T.Iterable[build.Target | build.GeneratedList | build.CustomTargetIndex | programs.Program]) -> T.List[str]: + """Return a list of strings with the paths to all the outputs of all targets in + dep_targets.""" + deps = [] + for i in dep_targets: + if isinstance(i, build.LocalProgram): + i = i.program + if isinstance(i, programs.Program): + continue + for output in i.get_outputs(): + if isinstance(i, build.GeneratedList): + assert isinstance(target, (build.BuildTarget, build.CustomTarget, build.CustomTargetIndex)) + deps.append(os.path.join(self.get_target_private_dir(target), output)) + else: + deps.append(os.path.join(self.get_target_dir(i), output)) + return deps + def get_target_depend_files(self, target: T.Union[build.Target, build.GeneratedList], absolute_paths: bool = False) -> T.List[str]: deps: T.List[str] = [] for i in target.depend_files: diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 963a7d1f972c..2531808bae2c 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -24,7 +24,6 @@ from .. import build from .. import mlog from .. import compilers -from .. import programs from .. import tooldetect from ..arglist import CompilerArgs from ..compilers import Compiler, is_library @@ -1250,26 +1249,10 @@ def custom_target_generator_inputs(self, target: build.CustomTarget) -> None: if isinstance(s, build.GeneratedList): self.generate_genlist_for_target(s, target) - def unwrap_dep_list(self, target: build.Target, dep_targets: T.Iterable[build.Target | build.GeneratedList | build.CustomTargetIndex | programs.Program]) -> T.List[str]: - deps = [] - for i in dep_targets: - # Add a dependency on all the outputs of this target - if isinstance(i, build.LocalProgram): - i = i.program - if isinstance(i, programs.Program): - continue - for output in i.get_outputs(): - if isinstance(i, GeneratedList): - assert isinstance(target, (build.BuildTarget, build.CustomTarget, build.CustomTargetIndex)) - deps.append(os.path.join(self.get_target_private_dir(target), output)) - else: - deps.append(os.path.join(self.get_target_dir(i), output)) - return deps - def generate_custom_target(self, target: build.CustomTarget) -> None: self.custom_target_generator_inputs(target) (srcs, ofilenames, cmd) = self.eval_custom_target_command(target) - deps = self.unwrap_dep_list(target, target.get_dependencies()) + deps = self.get_paths_for_dep_outputs(target, target.get_dependencies()) deps += self.get_target_depend_files(target) if target.build_always_stale: deps.append('PHONY') @@ -1335,7 +1318,7 @@ def generate_run_target(self, target: build.RunTarget) -> None: elem.add_item('COMMAND', meson_exe_cmd) elem.add_item('description', f'Running external command {target.name}{cmd_type}') elem.add_item('pool', 'console') - deps = self.unwrap_dep_list(target, target.get_dependencies()) + deps = self.get_paths_for_dep_outputs(target, target.get_dependencies()) deps += self.get_target_depend_files(target) elem.add_dep(deps) self.add_build(elem) @@ -2857,8 +2840,8 @@ def generate_genlist_for_target(self, genlist: build.GeneratedList, target: buil exe = generator.get_exe() infilelist = genlist.get_inputs() dependencies = self.get_target_depend_files(genlist) - dependencies += self.unwrap_dep_list(target, generator.depends) - dependencies += self.unwrap_dep_list(target, genlist.extra_depends) + dependencies += self.get_paths_for_dep_outputs(target, generator.depends) + dependencies += self.get_paths_for_dep_outputs(target, genlist.extra_depends) for curfile in infilelist: infilename = curfile.rel_to_builddir(self.build_to_src, self.get_target_private_dir(target)) base_args = generator.get_arglist(infilename) From 900d012940b4f753e7bcb72c3dbf2da55bc5f3ae Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 30 May 2026 11:34:49 +0200 Subject: [PATCH 375/681] backends: use get_paths_for_dep_outputs() more Replace copied-and-pasted loops with the function that generalizes them. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 5 +---- mesonbuild/backend/vs2010backend.py | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 2531808bae2c..4e3eaf178815 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1254,6 +1254,7 @@ def generate_custom_target(self, target: build.CustomTarget) -> None: (srcs, ofilenames, cmd) = self.eval_custom_target_command(target) deps = self.get_paths_for_dep_outputs(target, target.get_dependencies()) deps += self.get_target_depend_files(target) + deps += self.get_paths_for_dep_outputs(target, target.extra_depends) if target.build_always_stale: deps.append('PHONY') if target.depfile_type == 'gcc': @@ -1264,10 +1265,6 @@ def generate_custom_target(self, target: build.CustomTarget) -> None: rulename = 'CUSTOM_COMMAND' elem = NinjaBuildElement(self.all_outputs, ofilenames, rulename, srcs) elem.add_dep(deps) - for d in target.extra_depends: - # Add a dependency on all the outputs of this target - for output in d.get_outputs(): - elem.add_dep(os.path.join(self.get_target_dir(d), output)) cmd, reason = self.as_meson_exe_cmdline(target.command[0], cmd[1:], extra_bdeps=target.get_transitive_build_target_deps(), diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 369770d71dec..ea9d729ef7a3 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -186,10 +186,7 @@ def generate_genlist_for_target(self, genlist: build.GeneratedTypes, target: bui sole_output = '' infilename = os.path.join(down, curfile.rel_to_builddir(self.build_to_src, self.get_target_private_dir(target))) deps = self.get_target_depend_files(genlist, True) - for d in genlist.extra_depends: - # Add a dependency on all the outputs of this target - for output in d.get_outputs(): - deps.append(os.path.join(self.get_target_dir(d), output)) + deps += self.get_paths_for_dep_outputs(target, genlist.extra_depends) 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] From 12b96bfd9e970bf6d39009f0f32632a98f3ad47c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 30 May 2026 11:42:20 +0200 Subject: [PATCH 376/681] backends: remove dead isinstance check depend_files is always a List[File]. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/backends.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index b079ab3cace7..d14260854f79 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -1492,17 +1492,11 @@ def get_paths_for_dep_outputs(self, target: build.Target, dep_targets: T.Iterabl def get_target_depend_files(self, target: T.Union[build.Target, build.GeneratedList], absolute_paths: bool = False) -> T.List[str]: deps: T.List[str] = [] for i in target.depend_files: - if isinstance(i, mesonlib.File): - if absolute_paths: - deps.append(i.absolute_path(self.environment.get_source_dir(), - self.environment.get_build_dir())) - else: - deps.append(i.rel_to_builddir(self.build_to_src)) + if absolute_paths: + deps.append(i.absolute_path(self.environment.get_source_dir(), + self.environment.get_build_dir())) else: - if absolute_paths: - deps.append(os.path.join(self.environment.get_source_dir(), target.get_subdir(), i)) - else: - deps.append(os.path.join(self.build_to_src, target.get_subdir(), i)) + deps.append(i.rel_to_builddir(self.build_to_src)) return deps def get_custom_target_output_dir(self, target: build.AnyTargetType) -> str: From 20fb48a32684f0d306be446b592687107485fe7f Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 28 May 2026 09:30:22 -0700 Subject: [PATCH 377/681] backend/vs2010: Add typing information to gen_vcxproj_filters --- mesonbuild/backend/backends.py | 2 +- mesonbuild/backend/vs2010backend.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index d14260854f79..eb569594bf3a 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -484,7 +484,7 @@ def init_language_file(suffix: str, unity_file_number: int) -> T.TextIO: return result @staticmethod - def relpath(todir: str, fromdir: str) -> str: + def relpath(todir: str | os.PathLike[str], fromdir: str | os.PathLike[str]) -> str: return os.path.relpath(os.path.join('dummyprefixdir', todir), os.path.join('dummyprefixdir', fromdir)) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index ea9d729ef7a3..87c5420d472b 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -1864,7 +1864,7 @@ def path_normalize_add(path: str, lis: T.List[str]) -> bool: self.gen_vcxproj_filters(target, ofname) return True - def gen_vcxproj_filters(self, target, ofname) -> None: + def gen_vcxproj_filters(self, target: build.BuildTarget, ofname: str) -> None: # Generate pitchfork of filters based on directory structure. root = ET.Element('Project', {'ToolsVersion': '4.0', 'xmlns': 'http://schemas.microsoft.com/developer/msbuild/2003'}) @@ -1872,7 +1872,7 @@ def gen_vcxproj_filters(self, target, ofname) -> None: filter_items = ET.SubElement(root, 'ItemGroup') mlog.debug(f'Generating vcxproj filters {target.name}.') - def relative_to_defined_in(file) -> str: + def relative_to_defined_in(file: File) -> str: # Get the relative path to file's directory from the location of the meson.build that defines this target. return os.path.dirname(self.relpath(PureWindowsPath(file.subdir, file.fname), self.get_target_dir(target))) @@ -1913,7 +1913,7 @@ def relative_to_defined_in(file) -> str: sources, headers, objects, _ = self.split_sources(all_files) down = self.target_to_build_root(target) - def add_element(type_name: str, elements: list[ET.Element]) -> None: + def add_element(type_name: str, elements: list[File]) -> None: for i in elements: if not os.path.isabs(i.fname): dirname = relative_to_defined_in(i) From 874dfa368f9df160b7d48c5654acbea4dad2d522 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 28 May 2026 09:36:24 -0700 Subject: [PATCH 378/681] backend/vs2010: Annotate split_sources Which uses a bound generic to accept list[str] | list[File] | list[File | str] --- mesonbuild/backend/vs2010backend.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 87c5420d472b..7921489ba040 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -31,6 +31,7 @@ from ..compilers.compilers import Language Project = T.Tuple[str, Path, str, MachineChoice] + FileLike = T.TypeVar('FileLike', bound=File | str) def autodetect_vs_version(build: T.Optional[build.Build]) -> backends.Backend: vs_version = os.getenv('VisualStudioVersion', None) @@ -567,11 +568,11 @@ def generate_projects(self, vslite_ctx: dict = None) -> T.List[Project]: return projlist - def split_sources(self, srclist): - sources = [] - headers = [] - objects = [] - languages = [] + def split_sources(self, srclist: list[FileLike]) -> tuple[list[FileLike], list[FileLike], list[FileLike], list[str]]: + sources: list[FileLike] = [] + headers: list[FileLike] = [] + objects: list[FileLike] = [] + languages: list[str] = [] for i in srclist: if compilers.is_header(i): headers.append(i) From 5c1fb341a2eac7260ddcb57c1d3401460452f93e Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 28 May 2026 09:36:54 -0700 Subject: [PATCH 379/681] backend/vs2010: Add annotations to generate_custom_generator_commands --- mesonbuild/backend/vs2010backend.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 7921489ba040..02cc09a6a145 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -222,10 +222,10 @@ def generate_genlist_for_target(self, genlist: build.GeneratedTypes, target: bui ET.SubElement(cbs, 'Outputs').text = ';'.join(outfiles) ET.SubElement(cbs, 'AdditionalInputs').text = ';'.join(deps) - def generate_custom_generator_commands(self, target, parent_node): - generator_output_files = [] - custom_target_include_dirs = [] - custom_target_output_files = [] + def generate_custom_generator_commands(self, target: build.BuildTarget, parent_node: ET.Element) -> tuple[list[str], list[str], list[str]]: + generator_output_files: list[str] = [] + custom_target_include_dirs: list[str] = [] + custom_target_output_files: list[str] = [] for genlist in target.get_generated_sources(): self.generate_genlist_for_target(genlist, target, parent_node, generator_output_files, custom_target_include_dirs, custom_target_output_files) return generator_output_files, custom_target_output_files, custom_target_include_dirs From cdb1920714eb37dfe331c9aa3f299600d58ac194 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 28 May 2026 09:39:52 -0700 Subject: [PATCH 380/681] backend/vs2010: Add assertions to help mypy with option values --- mesonbuild/backend/vs2010backend.py | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 02cc09a6a145..a94dbeb8a440 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -277,13 +277,25 @@ def generate(self, else: raise MesonException('Unsupported Visual Studio platform: ' + build_machine) - self.buildtype = self.environment.coredata.optstore.get_value_for(OptionKey('buildtype')) - self.optimization = self.environment.coredata.optstore.get_value_for(OptionKey('optimization')) - self.debug = self.environment.coredata.optstore.get_value_for(OptionKey('debug')) + buildtype = self.environment.coredata.optstore.get_value_for(OptionKey('buildtype')) + assert isinstance(buildtype, str), 'for mypy' + self.buildtype = buildtype + + optimization = self.environment.coredata.optstore.get_value_for(OptionKey('optimization')) + assert isinstance(optimization, str), 'for mypy' + self.optimization = optimization + + debug = self.environment.coredata.optstore.get_value_for(OptionKey('debug')) + assert isinstance(debug, bool), 'for mypy' + self.debug = debug + try: - self.sanitize = self.environment.coredata.optstore.get_value_for(OptionKey('b_sanitize')) + sanitize = self.environment.coredata.optstore.get_value_for(OptionKey('b_sanitize')) + assert isinstance(sanitize, list), 'for mypy' except KeyError: - self.sanitize = [] + sanitize = [] + self.sanitize = sanitize + sln_filename = os.path.join(self.environment.get_build_dir(), self.build.project_name + '.sln') projlist = self.generate_projects(vslite_ctx) self.gen_testproj() @@ -1049,7 +1061,9 @@ def get_args_defines_and_inc_dirs(self, target: build.BuildTarget, compiler: com # Compile args added from the env or cross file: CFLAGS/CXXFLAGS, etc. We want these # to override all the defaults, but not the per-target compile args. for lang in file_args.keys(): - file_args[lang] += self.get_target_option(target, OptionKey(f'{lang}_args', machine=target.for_machine)) + l_args = self.get_target_option(target, OptionKey(f'{lang}_args', machine=target.for_machine)) + assert isinstance(l_args, list), 'for mypy' + file_args[lang] += l_args for args in file_args.values(): # This is where Visual Studio will insert target_args, target_defines, # etc, which are added later from external deps (see below). @@ -1355,6 +1369,7 @@ def add_non_makefile_vcxproj_elements( ET.SubElement(clconf, 'OpenMPSupport').text = 'true' # CRT type; debug or release vscrt_type = self.get_target_option(target, 'b_vscrt') + assert isinstance(vscrt_type, str), 'for mypy' vscrt_val = compiler.get_crt_val(vscrt_type) if vscrt_val == 'mdd': ET.SubElement(type_config, 'UseDebugLibraries').text = 'true' From 6169918fb49c5a68aaefe025eb54bf31ddf0a478 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 28 May 2026 09:43:09 -0700 Subject: [PATCH 381/681] backend/vs2010: fix annotations to get_build_args They were still expecting a str for sanitize instead of a list[str] --- mesonbuild/backend/vs2010backend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index a94dbeb8a440..e371e8cb85df 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -1167,7 +1167,7 @@ def get_args_defines_and_inc_dirs(self, target: build.BuildTarget, compiler: com return (target_args, file_args), (target_defines, file_defines), (target_inc_dirs, file_inc_dirs) - def get_build_args(self, target: build.BuildTarget, compiler: compilers.Compiler, optimization_level: str, debug: bool, sanitize: str) -> T.List[str]: + def get_build_args(self, target: build.BuildTarget, compiler: compilers.Compiler, optimization_level: str, debug: bool, sanitize: list[str]) -> T.List[str]: build_args = compiler.get_optimization_args(optimization_level) build_args += compiler.get_debug_args(debug) build_args += compiler.sanitizer_compile_args(target, sanitize) From a9143761b1ed6500593fe77cf6672487c6e096c0 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 28 May 2026 11:18:37 -0700 Subject: [PATCH 382/681] backend/vs2010: Fix typing of PCH handling --- mesonbuild/backend/vs2010backend.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index e371e8cb85df..44563e8cae48 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -836,27 +836,27 @@ def lang_from_source_file(cls, src: mesonlib.FileOrString) -> Language: return 'masm' raise MesonException(f'Could not guess language from source file {src}.') - def add_pch(self, pch_sources: T.Dict[str, T.Tuple[str, T.Optional[str], str, T.Optional[str]]], - lang, inc_cl) -> None: + def add_pch(self, pch_sources: T.Dict[Language, T.Tuple[str, T.Optional[str], str, T.Optional[str]]], + lang: Language, inc_cl: ET.Element) -> None: if lang in pch_sources: self.use_pch(pch_sources, lang, inc_cl) - def create_pch(self, pch_sources: T.Dict[str, T.Tuple[str, T.Optional[str], str, T.Optional[str]]], - lang, inc_cl) -> None: + def create_pch(self, pch_sources: T.Dict[Language, T.Tuple[str, T.Optional[str], str, T.Optional[str]]], + lang: Language, inc_cl: ET.Element) -> None: pch = ET.SubElement(inc_cl, 'PrecompiledHeader') pch.text = 'Create' self.add_pch_files(pch_sources, lang, inc_cl) - def use_pch(self, pch_sources: T.Dict[str, T.Tuple[str, T.Optional[str], str, T.Optional[str]]], - lang, inc_cl) -> None: + def use_pch(self, pch_sources: T.Dict[Language, T.Tuple[str, T.Optional[str], str, T.Optional[str]]], + lang: Language, inc_cl: ET.Element) -> None: pch = ET.SubElement(inc_cl, 'PrecompiledHeader') pch.text = 'Use' header = self.add_pch_files(pch_sources, lang, inc_cl) pch_include = ET.SubElement(inc_cl, 'ForcedIncludeFiles') pch_include.text = header + ';%(ForcedIncludeFiles)' - def add_pch_files(self, pch_sources: T.Dict[str, T.Tuple[str, T.Optional[str], str, T.Optional[str]]], - lang, inc_cl) -> str: + def add_pch_files(self, pch_sources: T.Dict[Language, T.Tuple[str, T.Optional[str], str, T.Optional[str]]], + lang: Language, inc_cl: ET.Element) -> str: header = os.path.basename(pch_sources[lang][0]) pch_file = ET.SubElement(inc_cl, 'PrecompiledHeaderFile') # When USING PCHs, MSVC will not do the regular include @@ -1728,7 +1728,7 @@ def path_normalize_add(path: str, lis: T.List[str]) -> bool: else: return False - pch_sources: T.Dict[str, T.Tuple[str, T.Optional[str], str, T.Optional[str]]] = {} + pch_sources: T.Dict[Language, T.Tuple[str, T.Optional[str], str, T.Optional[str]]] = {} if self.target_uses_pch(target): for lang in T.cast('T.Tuple[Language]', ('c', 'cpp')): pch = target.pch[lang] From 9b68f4da3f6877d4a9d65566f274b72a9c32243d Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 28 May 2026 11:21:26 -0700 Subject: [PATCH 383/681] backend/vs2010: fix uses of str -> Language --- mesonbuild/backend/vs2010backend.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 44563e8cae48..b51009ef8135 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -1035,8 +1035,8 @@ def get_args_defines_and_inc_dirs(self, target: build.BuildTarget, compiler: com # file_args is also later split out into defines and include_dirs in # case someone passed those in there file_args: T.Dict[Language, CompilerArgs] = {l: c.compiler_args() for l, c in target.compilers.items()} - file_defines: dict[str, list[str]] = {l: [] for l in target.compilers} - file_inc_dirs: dict[str, list[str]] = {l: [] for l in target.compilers} + file_defines: dict[Language, list[str]] = {l: [] for l in target.compilers} + file_inc_dirs: dict[Language, list[str]] = {l: [] for l in target.compilers} # The order in which these compile args are added must match # generate_single_compile() and generate_basic_compiler_args() for l, comp in target.compilers.items(): From fe328164841124f5985d7e469f576970eba5e558 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 28 May 2026 11:24:25 -0700 Subject: [PATCH 384/681] backend/vs2010: Fix annotations for add_*_(args|options) The annotations are all the same here (or very close) --- mesonbuild/backend/vs2010backend.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index b51009ef8135..46778838df0e 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -889,7 +889,7 @@ def is_argument_with_msbuild_xml_entry(self, entry): return True return entry[1:].startswith('M') - def add_additional_options(self, lang, parent_node, file_args): + def add_additional_options(self, lang: Language, parent_node: ET.Element, file_args: dict[Language, CompilerArgs]) -> None: args = [] for arg in file_args[lang].to_native(): if self.is_argument_with_msbuild_xml_entry(arg): @@ -930,7 +930,7 @@ def add_project_nmake_defs_incs_and_opts(self, parent_node, src: str, defs_paths ET.SubElement(parent_node, 'AdditionalIncludeDirectories').text = '$(NMakeIncludeSearchPath)' ET.SubElement(parent_node, 'AdditionalOptions').text = '$(AdditionalOptions)' - def add_preprocessor_defines(self, lang, parent_node, file_defines): + def add_preprocessor_defines(self, lang: Language, parent_node: ET.Element, file_defines: dict[Language, CompilerArgs]) -> None: defines = [] for define in file_defines[lang]: if define == '%(PreprocessorDefinitions)': @@ -939,7 +939,7 @@ def add_preprocessor_defines(self, lang, parent_node, file_defines): defines.append(self.escape_preprocessor_define(define)) ET.SubElement(parent_node, "PreprocessorDefinitions").text = ';'.join(defines) - def add_include_dirs(self, lang, parent_node, file_inc_dirs): + def add_include_dirs(self, lang: Language, parent_node: ET.Element, file_inc_dirs: dict[Language, list[str]]) -> None: dirs = file_inc_dirs[lang] ET.SubElement(parent_node, "AdditionalIncludeDirectories").text = ';'.join(dirs) From 6aca286a5bec055c04151de15b23f2d481098d00 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 28 May 2026 11:34:11 -0700 Subject: [PATCH 385/681] build: Use more Language --- mesonbuild/build.py | 8 ++++---- mesonbuild/interpreter/interpreter.py | 6 +++--- mesonbuild/modules/gnome.py | 7 ++++--- mesonbuild/modules/rust.py | 3 +++ 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 82d1f4dbd2b8..2f00aa17d441 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -346,8 +346,8 @@ def to_json(self) -> T.Dict[str, T.Union[str, T.List[str]]]: class BuildProject: name: str version: str - project_args: PerMachine[T.Dict[str, T.List[str]]] = field(default_factory=lambda: PerMachine({}, {})) - project_link_args: PerMachine[T.Dict[str, T.List[str]]] = field(default_factory=lambda: PerMachine({}, {})) + project_args: PerMachine[T.Dict[Language, T.List[str]]] = field(default_factory=lambda: PerMachine({}, {})) + project_link_args: PerMachine[T.Dict[Language, T.List[str]]] = field(default_factory=lambda: PerMachine({}, {})) # literally everything isn't dataclass stuff @@ -365,8 +365,8 @@ def __init__(self, environment: Environment): self.projects: T.Dict[SubProject, BuildProject] = {} self.targets: dict[str, Target] = {} self.targetnames: T.Set[T.Tuple[str, str]] = set() # Set of executable names and their subdir - self.global_args: PerMachine[T.Dict[str, T.List[str]]] = PerMachine({}, {}) - self.global_link_args: PerMachine[T.Dict[str, T.List[str]]] = PerMachine({}, {}) + self.global_args: PerMachine[T.Dict[Language, T.List[str]]] = PerMachine({}, {}) + self.global_link_args: PerMachine[T.Dict[Language, T.List[str]]] = PerMachine({}, {}) self.tests: T.List['Test'] = [] self.benchmarks: T.List['Test'] = [] self.headers: T.List[Headers] = [] diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 4d71ca5c4c2f..c728b4f17e44 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -3063,7 +3063,7 @@ def _warn_about_builtin_args(self, args: T.List[str]) -> None: mlog.warning(f'Consider using the built-in option for language standard version instead of using "{arg}".', location=self.current_node) - def _add_global_arguments(self, node: mparser.FunctionNode, argsdict: T.Dict[str, T.List[str]], + def _add_global_arguments(self, node: mparser.FunctionNode, argsdict: T.Dict[Language, T.List[str]], args: T.List[str], kwargs: 'kwtypes.FuncAddProjectArgs') -> None: if self.is_subproject(): msg = f'Function \'{node.func_name.value}\' cannot be used in subprojects because ' \ @@ -3076,11 +3076,11 @@ def _add_global_arguments(self, node: mparser.FunctionNode, argsdict: T.Dict[str frozen = self.project_args_frozen or self.global_args_frozen self._add_arguments(node, argsdict, frozen, args, kwargs) - def _add_project_arguments(self, node: mparser.FunctionNode, argsdict: T.Dict[str, T.List[str]], + def _add_project_arguments(self, node: mparser.FunctionNode, argsdict: T.Dict[Language, T.List[str]], args: T.List[str], kwargs: 'kwtypes.FuncAddProjectArgs') -> None: self._add_arguments(node, argsdict, self.project_args_frozen, args, kwargs) - def _add_arguments(self, node: mparser.FunctionNode, argsdict: T.Dict[str, T.List[str]], + def _add_arguments(self, node: mparser.FunctionNode, argsdict: T.Dict[Language, T.List[str]], args_frozen: bool, args: T.List[str], kwargs: 'kwtypes.FuncAddProjectArgs') -> None: if args_frozen: msg = f'Tried to use \'{node.func_name.value}\' after a build target has been declared.\n' \ diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index b4c46102b7e5..84341244c065 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -42,6 +42,7 @@ from . import ModuleState from ..build import BuildTarget from ..compilers import Compiler + from ..compilers.compilers import Language from ..interpreter import Interpreter from ..interpreter.interpreter import CustomTargetSources from ..interpreter.kwargs import CustomTargetInputs, TargetDepends @@ -889,8 +890,8 @@ def _scan_gir_targets(state: 'ModuleState', girtargets: T.Sequence[build.BuildTa return ret @staticmethod - def _get_girtargets_langs_compilers(girtargets: T.Sequence[build.BuildTarget]) -> T.List[T.Tuple[str, 'Compiler']]: - ret: T.List[T.Tuple[str, 'Compiler']] = [] + def _get_girtargets_langs_compilers(girtargets: T.Sequence[build.BuildTarget]) -> T.List[T.Tuple[Language, 'Compiler']]: + ret: T.List[T.Tuple[Language, 'Compiler']] = [] for girtarget in girtargets: for lang, compiler in girtarget.compilers.items(): # XXX: Can you use g-i with any other language? @@ -917,7 +918,7 @@ def _get_gir_targets_inc_dirs(girtargets: T.Sequence[build.BuildTarget]) -> Orde return ret @staticmethod - def _get_langs_compilers_flags(state: 'ModuleState', langs_compilers: T.List[T.Tuple[str, 'Compiler']] + def _get_langs_compilers_flags(state: 'ModuleState', langs_compilers: T.List[T.Tuple[Language, 'Compiler']] ) -> T.Tuple[T.List[str], T.List[str], T.List[str]]: cflags: T.List[str] = [] internal_ldflags: T.List[str] = [] diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index d07f7fcdb3f4..5e5e8575c0b6 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -891,6 +891,9 @@ def bindgen(self, state: ModuleState, args: T.List, kwargs: FuncBindgen) -> Modu else: raise InterpreterException(f'Unknown file type extension for: {name}') + # Mypy can't figure out that if Literal a is a subset of Literal B it's valid + language = T.cast('Language', language) + # We only want include directories and defines, other things may not be valid cargs = state.get_option(f'{language}_args', state.subproject) assert isinstance(cargs, list), 'for mypy' From 2050a197727d6acc1f74cf8a592e5865032fefe6 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 28 May 2026 11:35:34 -0700 Subject: [PATCH 386/681] backend/vs2010: fix target annotation --- mesonbuild/backend/vs2010backend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 46778838df0e..19347eab69aa 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -602,7 +602,7 @@ def split_sources(self, srclist: list[FileLike]) -> tuple[list[FileLike], list[F headers.append(i) return sources, headers, objects, languages - def target_to_build_root(self, target: build.BuildTargetTypes) -> str: + def target_to_build_root(self, target: build.AnyTargetType) -> str: if self.get_target_dir(target) == '': return '' From 8021f0d76a06f8b76df726c8a649b153c285acdf Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 28 May 2026 12:37:28 -0700 Subject: [PATCH 387/681] backend/vs2010: Fix annotations for add_non_makefile_vcxproj_elements --- mesonbuild/backend/vs2010backend.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 19347eab69aa..8b1d0029443a 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -1338,12 +1338,12 @@ def add_non_makefile_vcxproj_elements( type_config: ET.Element, target: build.BuildTarget, platform: str, - subsystem, - build_args, - target_args, - target_defines, - target_inc_dirs, - file_args + subsystem: str, + build_args: list[str], + target_args: list[str], + target_defines: list[str], + target_inc_dirs: list[str], + file_args: dict[Language, CompilerArgs] ) -> None: compiler = self._get_cl_compiler(target) buildtype_link_args = compiler.get_optimization_link_args(self.optimization) @@ -1505,7 +1505,10 @@ def add_non_makefile_vcxproj_elements( lobj = t else: lobj = self.build.targets[t.get_id()] - linkname = os.path.join(down, self.get_target_filename_for_linking(lobj)) + targetname = self.get_target_filename_for_linking(lobj) + if targetname is None: + continue + linkname = os.path.join(down, targetname) if t in target.link_whole_targets: if compiler.id == 'msvc' and version_compare(compiler.version, '<19.00.23918'): # Expand our object lists manually if we are on pre-Visual Studio 2015 Update 2 @@ -1534,7 +1537,7 @@ def add_non_makefile_vcxproj_elements( extra_link_args.extend(self.flatten_object_list(t)[0]) else: # /WHOLEARCHIVE:foo must go into AdditionalOptions - extra_link_args += compiler.get_link_whole_for(linkname) + extra_link_args += compiler.get_link_whole_for([linkname]) # To force Visual Studio to build this project even though it # has no sources, we include a reference to the vcxproj file # that builds this target. Technically we should add this only @@ -2175,7 +2178,7 @@ def add_regen_dependency(self, root: ET.Element) -> None: regen_vcxproj = os.path.join(self.environment.get_build_dir(), 'REGEN.vcxproj') self.add_project_reference(root, regen_vcxproj, self.environment.coredata.regen_guid) - def generate_lang_standard_info(self, file_args: T.Dict[str, CompilerArgs], clconf: ET.Element) -> None: + def generate_lang_standard_info(self, file_args: T.Dict[Language, CompilerArgs], clconf: ET.Element) -> None: pass # Returns if a target generates a manifest or not. From 8ccc5c237b0e57d90e878dd2f1369e22c2a9a564 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 30 May 2026 12:00:37 +0200 Subject: [PATCH 388/681] backend/vs2010: fix Path/PurePath confusion --- mesonbuild/backend/vs2010backend.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 8b1d0029443a..a510ce3d3854 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -30,7 +30,7 @@ from ..arglist import CompilerArgs from ..compilers.compilers import Language - Project = T.Tuple[str, Path, str, MachineChoice] + Project = T.Tuple[str, PurePath, str, MachineChoice] FileLike = T.TypeVar('FileLike', bound=File | str) def autodetect_vs_version(build: T.Optional[build.Build]) -> backends.Backend: @@ -410,7 +410,7 @@ def get_target_deps(self, t: T.Dict[T.Any, build.AnyTargetType], recursive=False ret.update(all_deps) return ret - def generate_solution_dirs(self, ofile: str, parents: T.Sequence[Path]) -> None: + def generate_solution_dirs(self, ofile: T.TextIO, parents: T.Sequence[PurePath]) -> None: prj_templ = 'Project("{%s}") = "%s", "%s", "{%s}"\n' iterpaths = reversed(parents) # Skip first path From ab962d0391a0d9ce608c7e6d8bfb9b57c813fff9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 30 May 2026 12:01:15 +0200 Subject: [PATCH 389/681] backend/vs2010: add missing annotations Signed-off-by: Paolo Bonzini --- mesonbuild/backend/vs2010backend.py | 32 ++++++++++++++--------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index a510ce3d3854..44082325feda 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -126,7 +126,7 @@ def get_primary_source_lang(target_sources: T.List[File], custom_sources: T.List def get_non_primary_lang_intellisense_fields(vslite_ctx: dict, target_id: str, primary_src_lang: str) -> T.Dict[str, T.Dict[str, T.Tuple[str, str, str]]]: - defs_paths_opts_per_lang_and_buildtype = {} + defs_paths_opts_per_lang_and_buildtype: dict[str, dict[str, tuple[str, str, str]]] = {} for buildtype in coredata.get_genvs_default_buildtype_list(): captured_build_args = vslite_ctx[buildtype][target_id] # Results in a 'Src types to compile args' dict non_primary_build_args_per_src_lang = [(lang, build_args) for lang, build_args in captured_build_args.items() if lang != primary_src_lang] # Only need to individually populate intellisense fields for sources of non-primary types. @@ -149,7 +149,7 @@ def __init__(self, build: T.Optional[build.Build], gen_lite: bool = False): self.vs_version = '2010' self.windows_target_platform_version = None self.subdirs: dict[PurePath, tuple[str, str | None]] = {} - self.handled_target_deps = {} + self.handled_target_deps: dict[str, list[str]] = {} self.gen_lite = gen_lite # Synonymous with generating the simpler makefile-style multi-config projects that invoke 'meson compile' builds, avoiding native MSBuild complications def detect_toolset(self) -> None: @@ -341,7 +341,7 @@ def get_vcvars_command(self) -> str: (script_path, os.environ['VSCMD_ARG_TGT_ARCH'], os.environ['VSCMD_ARG_HOST_ARCH']) return '' - def get_obj_target_deps(self, obj_list): + def get_obj_target_deps(self, obj_list: list[build.ObjectTypes]) -> T.Iterable[tuple[str, build.BuildTarget]]: result = {} for o in obj_list: if isinstance(o, build.ExtractedObjects): @@ -454,7 +454,7 @@ def generate_solution(self, sln_filename: str, projlist: T.List[Project]) -> Non self.environment.coredata.lang_guids[lang], prj[0], prj[1], prj[2]) ofile.write(prj_line) - target_dict = {target.get_id(): target} + target_dict: dict[str, build.AnyTargetType] = {target.get_id(): target} # Get recursive deps recursive_deps = self.get_target_deps( target_dict, recursive=True) @@ -622,8 +622,8 @@ def add_project_reference(self, root: ET.Element, include: str, projid: str, lin # objects and .lib files manually. ET.SubElement(pref, 'LinkLibraryDependencies').text = 'false' - def add_target_deps(self, root: ET.Element, target): - target_dict = {target.get_id(): target} + def add_target_deps(self, root: ET.Element, target: build.AnyTargetType) -> None: + target_dict: T.Dict[str, build.AnyTargetType] = {target.get_id(): target} for dep in self.get_target_deps(target_dict).values(): if dep.get_id() in self.handled_target_deps[target.get_id()]: # This dependency was already handled manually. @@ -633,13 +633,13 @@ def add_target_deps(self, root: ET.Element, target): tid = self.environment.coredata.target_guids[dep.get_id()] self.add_project_reference(root, vcxproj, tid) - def create_basic_project(self, target_name, *, - temp_dir, - guid, - conftype='Utility', - target_ext=None, - target_platform=None, - gen_manifest=True, + def create_basic_project(self, target_name: str, *, + temp_dir: str, + guid: str, + conftype: str = 'Utility', + target_ext: T.Optional[str] = None, + target_platform: T.Optional[str] = None, + gen_manifest: bool | T.Literal['embed'] = True, masm_type: T.Optional[T.Literal['masm', 'marmasm']] = None) -> T.Tuple[ET.Element, ET.Element]: root = ET.Element('Project', {'DefaultTargets': "Build", 'ToolsVersion': '4.0', @@ -879,7 +879,7 @@ def add_pch_files(self, pch_sources: T.Dict[Language, T.Tuple[str, T.Optional[st return header - def is_argument_with_msbuild_xml_entry(self, entry): + def is_argument_with_msbuild_xml_entry(self, entry: str) -> bool: # Remove arguments that have a top level XML entry so # they are not used twice. # FIXME add args as needed. @@ -901,7 +901,7 @@ def add_additional_options(self, lang: Language, parent_node: ET.Element, file_a ET.SubElement(parent_node, "AdditionalOptions").text = ' '.join(args) # Set up each project's source file ('CLCompile') element with appropriate preprocessor, include dir, and compile option values for correct intellisense. - def add_project_nmake_defs_incs_and_opts(self, parent_node, src: str, defs_paths_opts_per_lang_and_buildtype: dict, platform: str): + def add_project_nmake_defs_incs_and_opts(self, parent_node: ET.Element, src: str, defs_paths_opts_per_lang_and_buildtype: dict, platform: str) -> None: # For compactness, sources whose type matches the primary src type (i.e. most frequent in the set of source types used in the target/project, # according to the 'captured_build_args' map), can simply reference the preprocessor definitions, include dirs, and compile option NMake fields of # the project itself. @@ -1271,7 +1271,7 @@ def add_gen_lite_makefile_vcxproj_elements(self, platform: str, target_ext: str, vslite_ctx: dict, - target, + target: build.AnyTargetType, proj_to_build_root: str, primary_src_lang: T.Optional[str]) -> None: ET.SubElement(root, 'ImportGroup', Label='ExtensionSettings') From 85ee7af13dbdbb0271b0d575d26881417d39f775 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 30 May 2026 12:01:45 +0200 Subject: [PATCH 390/681] backend/vs2010: fix variance and subclassing issues Signed-off-by: Paolo Bonzini --- mesonbuild/backend/vs2010backend.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 44082325feda..35e80deca686 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -348,7 +348,7 @@ def get_obj_target_deps(self, obj_list: list[build.ObjectTypes]) -> T.Iterable[t result[o.target.get_id()] = o.target return result.items() - def get_target_deps(self, t: T.Dict[T.Any, build.AnyTargetType], recursive=False): + def get_target_deps(self, t: T.Mapping[str, build.AnyTargetType], recursive: bool = False) -> T.Dict[str, build.Target]: all_deps: T.Dict[str, build.Target] = {} for target in t.values(): if isinstance(target, build.CustomTargetIndex): @@ -2183,7 +2183,7 @@ def generate_lang_standard_info(self, file_args: T.Dict[Language, CompilerArgs], # Returns if a target generates a manifest or not. # Returns 'embed' if the generated manifest is embedded. - def get_gen_manifest(self, target: T.Optional[build.BuildTarget]) -> bool | T.Literal['embed']: + def get_gen_manifest(self, target: T.Optional[build.Target]) -> bool | T.Literal['embed']: if not isinstance(target, build.BuildTarget): return True From 173d7605f266c5e8019dafea1159b4700ef56e5c Mon Sep 17 00:00:00 2001 From: "L. E. Segovia" Date: Mon, 1 Jun 2026 13:37:55 -0300 Subject: [PATCH 391/681] nasm: use the correct flag for response file support Unlike GCC-style compilers, response files for Nasm must be passed with the '-@foo.rsp' flag. This fixes building MPV on Windows with sufficiently long include paths, which are necessary to trigger response file usage. See https://gitlab.freedesktop.org/gstreamer/meson-ports/ffmpeg/-/work_items/75 --- mesonbuild/backend/ninjabackend.py | 2 ++ mesonbuild/compilers/asm.py | 5 +++++ mesonbuild/linkers/base.py | 1 + 3 files changed, 8 insertions(+) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 4e3eaf178815..f668f2a0b29d 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -254,6 +254,8 @@ def rule_iter() -> T.Iterable[str]: if rsp == '_RSP': if self.rspfile_quote_style is RSPFileSyntax.TASKING: outfile.write(' command = {} --option-file=$out.rsp\n'.format(' '.join([self._quoter(x) for x in self.command]))) + elif self.rspfile_quote_style is RSPFileSyntax.NASM: + outfile.write(' command = {} -@$out.rsp\n'.format(' '.join([self._quoter(x) for x in self.command]))) else: outfile.write(' command = {} @$out.rsp\n'.format(' '.join([self._quoter(x) for x in self.command]))) outfile.write(' rspfile = $out.rsp\n') diff --git a/mesonbuild/compilers/asm.py b/mesonbuild/compilers/asm.py index 6902652e8509..de299f9d72bd 100644 --- a/mesonbuild/compilers/asm.py +++ b/mesonbuild/compilers/asm.py @@ -6,6 +6,7 @@ from ..mesonlib import EnvironmentException, get_meson_command from ..options import OptionKey from .compilers import Compiler +from ..linkers import RSPFileSyntax from ..linkers.linkers import VisualStudioLikeLinkerMixin from .mixins.metrowerks import MetrowerksCompiler, mwasmarm_instruction_set_args, mwasmeppc_instruction_set_args from .mixins.ti import TICompiler @@ -159,6 +160,10 @@ def get_crt_link_args(self, crt_val: str) -> T.List[str]: return [] return self.crt_args[self.get_crt_val(crt_val)] + # It uses '-@' instead of the normal '@' for denoting response files. + def rsp_file_syntax(self) -> RSPFileSyntax: + return RSPFileSyntax.NASM + class YasmCompiler(NasmCompiler): id = 'yasm' diff --git a/mesonbuild/linkers/base.py b/mesonbuild/linkers/base.py index 68fdb2ea3a70..d35a8df4c1c9 100644 --- a/mesonbuild/linkers/base.py +++ b/mesonbuild/linkers/base.py @@ -19,6 +19,7 @@ class RSPFileSyntax(enum.Enum): MSVC = enum.auto() GCC = enum.auto() TASKING = enum.auto() + NASM = enum.auto() class ArLikeLinker: From 4e0d4c85816b032aa2cea3ea416fa96a1e6a5260 Mon Sep 17 00:00:00 2001 From: Mayank Basena <0mayankbasena@gmail.com> Date: Tue, 2 Jun 2026 04:13:50 +0000 Subject: [PATCH 392/681] fix: handle empty stdout in sizeof test instead of crashing with ValueError When sanitizer flags (e.g., -fsanitize=address) cause the test binary to produce empty stdout, int(res.stdout) raises a bare ValueError that crashes meson setup. Now catches the ValueError and raises a proper EnvironmentException with a helpful error message. Fixes mesonbuild/meson#14452 --- mesonbuild/compilers/mixins/clike.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py index e54ad7538162..f57221526339 100644 --- a/mesonbuild/compilers/mixins/clike.py +++ b/mesonbuild/compilers/mixins/clike.py @@ -545,7 +545,12 @@ def sizeof(self, typename: str, prefix: str, *, return -1, False if res.returncode != 0: raise mesonlib.EnvironmentException('Could not run sizeof test binary.') - return int(res.stdout), res.cached + try: + return int(res.stdout), res.cached + except ValueError: + raise mesonlib.EnvironmentException( + f'Could not determine size of {typename}: compiler output was empty or invalid.' + ) def _cross_alignment(self, typename: str, prefix: str, *, extra_args: T.Optional[T.List[str]] = None, From 94b3bbc87de1b0525a73f6d877a98dc58134087d Mon Sep 17 00:00:00 2001 From: Gleb Popov <6yearold@gmail.com> Date: Tue, 2 Jun 2026 15:51:21 +0300 Subject: [PATCH 393/681] ElbrusCompiler: mark as not supporting -fuse-ld --- mesonbuild/compilers/mixins/elbrus.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mesonbuild/compilers/mixins/elbrus.py b/mesonbuild/compilers/mixins/elbrus.py index e020123891f0..4eb4d8e012c3 100644 --- a/mesonbuild/compilers/mixins/elbrus.py +++ b/mesonbuild/compilers/mixins/elbrus.py @@ -96,3 +96,7 @@ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = def openmp_flags(self) -> T.List[str]: return ['-fopenmp'] + + @classmethod + def use_linker_args(cls, linker: str, version: str) -> T.List[str]: + return [] From 1b915c31c5a4146683ccb7f594430089ac7a6f77 Mon Sep 17 00:00:00 2001 From: Gleb Popov <6yearold@gmail.com> Date: Tue, 2 Jun 2026 16:01:21 +0300 Subject: [PATCH 394/681] allplatformtests: Fix test_compiler_detection for the elbrus/lcc case --- unittests/allplatformstests.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 1b140547b8c0..cd9c918791e1 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -41,6 +41,7 @@ from mesonbuild.programs import ExternalProgram from mesonbuild.compilers.mixins.clang import ClangCompiler +from mesonbuild.compilers.mixins.elbrus import ElbrusCompiler from mesonbuild.compilers.mixins.gnu import GnuCompiler from mesonbuild.compilers.mixins.intel import IntelGnuLikeCompiler from mesonbuild.compilers.c import VisualStudioCCompiler, ClangClCCompiler @@ -1164,7 +1165,11 @@ def test_compiler_detection(self): # Very rough/strict heuristics. Would never work for actual # compiler detection, but should be ok for the tests. ebase = os.path.basename(evalue) - if ebase.startswith('g') or ebase.endswith(('-gcc', '-g++')): + if ecc.id == 'lcc': + # lcc ships the gcc symlink, but it isn't a GnuCompiler, + # but a GnuLikeCompiler + self.assertIsInstance(ecc, ElbrusCompiler) + elif ebase.startswith('g') or ebase.endswith(('-gcc', '-g++')): self.assertIsInstance(ecc, gnu) self.assertIsInstance(elinker, ar) elif 'clang-cl' in ebase: From 3b4402217a5fb20d64f179e8aa6620074abc6c73 Mon Sep 17 00:00:00 2001 From: Gleb Popov <6yearold@gmail.com> Date: Tue, 2 Jun 2026 16:10:43 +0300 Subject: [PATCH 395/681] linuxliketests: Skip -fuse-ld tests for lcc --- unittests/linuxliketests.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/unittests/linuxliketests.py b/unittests/linuxliketests.py index 72c958f150d5..2288e0d693aa 100644 --- a/unittests/linuxliketests.py +++ b/unittests/linuxliketests.py @@ -31,7 +31,7 @@ from mesonbuild.compilers import ( detect_c_compiler, detect_cpp_compiler, compiler_from_language, ) -from mesonbuild.compilers.c import AppleClangCCompiler +from mesonbuild.compilers.c import AppleClangCCompiler, ElbrusCompiler from mesonbuild.compilers.cpp import AppleClangCPPCompiler from mesonbuild.compilers.objc import AppleClangObjCCompiler from mesonbuild.compilers.objcpp import AppleClangObjCPPCompiler @@ -1660,6 +1660,8 @@ def _check_ld(self, check: str, name: str, lang: str, expected: str) -> None: if isinstance(comp, (AppleClangCCompiler, AppleClangCPPCompiler, AppleClangObjCCompiler, AppleClangObjCPPCompiler)): raise SkipTest('AppleClang is currently only supported with ld64') + if isinstance(comp, ElbrusCompiler): + raise SkipTest('ElbrusCompiler currently cannot override the linker.') if lang != 'rust' and comp.use_linker_args('bfd', '') == []: raise SkipTest( f'Compiler {comp.id} does not support using alternative linkers') From 8946c7eff1fe14342f4943d4febd86cc619cb0ed Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 1 Jun 2026 09:24:48 -0700 Subject: [PATCH 396/681] backend/vs2010: Fix handling of get_target_deps types --- mesonbuild/backend/vs2010backend.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 35e80deca686..92d844c69518 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -19,6 +19,7 @@ from .. import mlog from .. import compilers from .. import mesonlib +from .. import programs from ..mesonlib import ( File, MesonBugException, MesonException, replace_if_different, version_compare, MachineChoice ) @@ -348,8 +349,8 @@ def get_obj_target_deps(self, obj_list: list[build.ObjectTypes]) -> T.Iterable[t result[o.target.get_id()] = o.target return result.items() - def get_target_deps(self, t: T.Mapping[str, build.AnyTargetType], recursive: bool = False) -> T.Dict[str, build.Target]: - all_deps: T.Dict[str, build.Target] = {} + def get_target_deps(self, t: T.Mapping[str, build.AnyTargetType], recursive: bool = False) -> T.Dict[str, build.AnyTargetType]: + all_deps: T.Dict[str, build.AnyTargetType] = {} for target in t.values(): if isinstance(target, build.CustomTargetIndex): # just transfer it to the CustomTarget code @@ -362,6 +363,10 @@ def get_target_deps(self, t: T.Mapping[str, build.AnyTargetType], recursive: boo all_deps[d.get_id()] = d elif isinstance(target, build.RunTarget): for d in target.get_dependencies(): + if isinstance(d, build.LocalProgram): + d = d.program + if isinstance(d, (programs.Program, build.GeneratedList)): + continue all_deps[d.get_id()] = d elif isinstance(target, build.BuildTarget): for ldep in target.link_targets: From 8aa5810bc03995b996ed72d7e123e296761e9411 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 1 Jun 2026 09:29:32 -0700 Subject: [PATCH 397/681] backend/vs2010: Convert CustomTargetIndex to CustomTarget --- mesonbuild/backend/vs2010backend.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 92d844c69518..0ec8524cc087 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -466,6 +466,8 @@ def generate_solution(self, sln_filename: str, projlist: T.List[Project]) -> Non ofile.write('EndProject\n') for dep, target in recursive_deps.items(): if prj[0] in default_projlist: + if isinstance(target, build.CustomTargetIndex): + target = target.target default_projlist[dep] = target test_line = prj_templ % (self.environment.coredata.lang_guids['default'], From a108221ef7b2b64524dfd7d740957a2b87538ae0 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 1 Jun 2026 09:30:40 -0700 Subject: [PATCH 398/681] backend/vs2010: gen_vcxproj takes a Target not a BuildTarget --- mesonbuild/backend/vs2010backend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 0ec8524cc087..2823b320c6f4 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -1638,7 +1638,7 @@ def relocate_generated_file_paths_to_concrete_build_dir(self, gen_files: T.List[ # Returns bool indicating whether the .vcxproj has been generated. # Under some circumstances, it's unnecessary to create some .vcxprojs, so, when generating the .sln, # we need to respect that not all targets will have generated a project. - def gen_vcxproj(self, target: build.BuildTarget, ofname: str, guid: str, vslite_ctx: dict = None) -> bool: + def gen_vcxproj(self, target: build.Target, ofname: str, guid: str, vslite_ctx: dict = None) -> bool: mlog.debug(f'Generating vcxproj {target.name}.') subsystem = 'Windows' self.handled_target_deps[target.get_id()] = [] From c27155a0ddf98f3076ad198744e4c3d3dca11303 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 1 Jun 2026 09:32:43 -0700 Subject: [PATCH 399/681] backend/vs2010: fix type to include CustomTargets in genlist_for_target --- mesonbuild/backend/vs2010backend.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 2823b320c6f4..0f677c47c87e 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -159,7 +159,13 @@ def detect_toolset(self) -> None: def get_target_private_dir(self, target: build.BuildTargetTypes) -> str: return os.path.join(self.get_target_dir(target), target.get_id()) - def generate_genlist_for_target(self, genlist: build.GeneratedTypes, target: build.BuildTarget, parent_node: ET.Element, generator_output_files: T.List[str], custom_target_include_dirs: T.List[str], custom_target_output_files: T.List[str]) -> None: + def generate_genlist_for_target( + self, genlist: build.GeneratedTypes, + target: build.BuildTarget | build.CustomTarget, + parent_node: ET.Element, + generator_output_files: T.List[str], + custom_target_include_dirs: T.List[str], + custom_target_output_files: T.List[str]) -> None: if isinstance(genlist, build.GeneratedList): for x in genlist.depends: self.generate_genlist_for_target(x, target, parent_node, [], [], []) @@ -223,7 +229,9 @@ def generate_genlist_for_target(self, genlist: build.GeneratedTypes, target: bui ET.SubElement(cbs, 'Outputs').text = ';'.join(outfiles) ET.SubElement(cbs, 'AdditionalInputs').text = ';'.join(deps) - def generate_custom_generator_commands(self, target: build.BuildTarget, parent_node: ET.Element) -> tuple[list[str], list[str], list[str]]: + def generate_custom_generator_commands( + self, target: build.BuildTarget | build.CustomTarget, parent_node: ET.Element + ) -> tuple[list[str], list[str], list[str]]: generator_output_files: list[str] = [] custom_target_include_dirs: list[str] = [] custom_target_output_files: list[str] = [] From c1bb7f883a8f057f6b38be2ee6091fcb78e62125 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 2 Jun 2026 09:03:28 -0700 Subject: [PATCH 400/681] backend/ninja: Convert generate_unity_files to use list[File] Instead of converting the File back into a str, just pass the File. This matches the behavior of the vs backend, and resolves some type issues there. --- mesonbuild/backend/backends.py | 4 ++-- mesonbuild/backend/ninjabackend.py | 15 ++++++--------- mesonbuild/utils/universal.py | 6 ++++-- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index eb569594bf3a..a19ad39bba44 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -441,7 +441,7 @@ def get_unity_source_file(self, target: build.BuildTargetTypes, osrc = f'{target.name}-unity{number}.{suffix}' return mesonlib.File.from_built_file(self.get_target_private_dir(target), osrc) - def generate_unity_files(self, target: build.BuildTarget, unity_src: T.List[str]) -> T.List[mesonlib.File]: + def generate_unity_files(self, target: build.BuildTarget, unity_src: list[File]) -> list[mesonlib.File]: abs_files: T.List[str] = [] result: T.List[mesonlib.File] = [] compsrcs = classify_unity_sources(target.compilers.values(), unity_src) @@ -474,7 +474,7 @@ def init_language_file(suffix: str, unity_file_number: int) -> T.TextIO: ofile = init_language_file(comp.get_default_suffix(), unity_file_number) unity_file_number += 1 files_in_current = 0 - ofile.write(f'#include<{src}>\n') + ofile.write(f'#include<{src.absolute_path(self.source_dir, self.build_dir)}>\n') files_in_current += 1 if ofile: ofile.close() diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index f668f2a0b29d..8ab7f324399b 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -840,7 +840,7 @@ def create_target_source_introspection(self, target: build.Target, comp: compile parameters: CompilerArgs | T.List[str], sources: FileList, generated_sources: FileList, - unity_sources: T.Optional[T.List[str]] = None) -> None: + unity_sources: list[File] | None = None) -> None: ''' Adds the source file introspection information for a language of a target @@ -985,7 +985,7 @@ def generate_target(self, target: T.Union[build.Target]) -> None: obj_list = [] is_unity = self.is_unity(target) header_deps = [] - unity_src = [] + unity_src: list[File] = [] unity_deps = [] # Generated sources that must be built before compiling a Unity target. header_deps += self.get_generated_headers(target) @@ -1005,13 +1005,12 @@ def generate_target(self, target: T.Union[build.Target]) -> None: # This will be set as dependencies of all the target's sources. At the # same time, also deal with generated sources that need to be compiled. generated_source_files: T.List[File] = [] - for rel_src in generated_sources.keys(): + for rel_src in generated_sources: raw_src = File.from_built_relative(rel_src) if compilers.is_source(rel_src): if is_unity and self.get_target_source_can_unity(target, rel_src): unity_deps.append(raw_src) - abs_src = os.path.join(self.environment.get_build_dir(), rel_src) - unity_src.append(abs_src) + unity_src.append(raw_src) else: generated_source_files.append(raw_src) elif compilers.is_object(rel_src): @@ -1102,9 +1101,7 @@ def generate_target(self, target: T.Union[build.Target]) -> None: o, s = self.generate_llvm_ir_compile(target, src) obj_list.append(o) elif is_unity and self.get_target_source_can_unity(target, src): - abs_src = os.path.join(self.environment.get_build_dir(), - src.rel_to_builddir(self.build_to_src)) - unity_src.append(abs_src) + unity_src.append(src) else: o, s = self.generate_single_compile(target, src, False, [], [*header_deps, *d_generated_deps, *fortran_order_deps], @@ -3206,7 +3203,7 @@ def generate_single_compile(self, target: build.BuildTarget, src: FileOrString, header_deps: T.Optional[T.List[FileOrString]] = None, order_deps: T.Optional[T.List[File] | T.List[FileOrString]] = None, extra_args: T.Optional[T.List[str]] = None, - unity_sources: T.Optional[T.List[str]] = None, + unity_sources: list[File] | None = None, ) -> T.Tuple[str, str]: """ Compiles C/C++, ObjC/ObjC++, Fortran, and D sources diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index 05e3ebf4eeea..70bb5588dd20 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -51,6 +51,8 @@ class _VerPickleLoadable(Protocol): # .version or a .environment to be passed. _PL = T.TypeVar('_PL', bound=T.Union[_EnvPickleLoadable, _VerPickleLoadable]) + FileLike = T.TypeVar('FileLike', bound='File' | str) + FileOrString = T.Union['File', str] _T = T.TypeVar('_T') @@ -514,8 +516,8 @@ def get_compiler_for_source(compilers: T.Iterable['Compiler'], src: 'FileOrStrin raise MesonException(f'No specified compiler can handle file {src!s}') -def classify_unity_sources(compilers: T.Iterable['Compiler'], sources: T.Sequence['FileOrString']) -> T.Dict['Compiler', T.List['FileOrString']]: - compsrclist: T.Dict['Compiler', T.List['FileOrString']] = {} +def classify_unity_sources(compilers: T.Iterable['Compiler'], sources: T.Sequence[FileLike]) -> T.Dict['Compiler', T.List[FileLike]]: + compsrclist: T.Dict['Compiler', T.List[FileLike]] = {} for src in sources: comp = get_compiler_for_source(compilers, src) if comp not in compsrclist: From e239843b2cb91cea6449ab7256b8d0460ba9657f Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 1 Jun 2026 09:54:13 -0700 Subject: [PATCH 401/681] backend/vs2010: fix type annotations for get_args_defines_and_inc_dirs --- mesonbuild/backend/vs2010backend.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 0f677c47c87e..f655912fcdc4 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -945,7 +945,7 @@ def add_project_nmake_defs_incs_and_opts(self, parent_node: ET.Element, src: str ET.SubElement(parent_node, 'AdditionalIncludeDirectories').text = '$(NMakeIncludeSearchPath)' ET.SubElement(parent_node, 'AdditionalOptions').text = '$(AdditionalOptions)' - def add_preprocessor_defines(self, lang: Language, parent_node: ET.Element, file_defines: dict[Language, CompilerArgs]) -> None: + def add_preprocessor_defines(self, lang: Language, parent_node: ET.Element, file_defines: dict[Language, list[str]]) -> None: defines = [] for define in file_defines[lang]: if define == '%(PreprocessorDefinitions)': @@ -1039,7 +1039,14 @@ def _prettyprint_vcxproj_xml(self, tree: ET.ElementTree, ofname: str) -> None: replace_if_different(ofname, ofname_tmp) # Returns: (target_args,file_args), (target_defines,file_defines), (target_inc_dirs,file_inc_dirs) - def get_args_defines_and_inc_dirs(self, target: build.BuildTarget, compiler: compilers.Compiler, generated_files_include_dirs, proj_to_src_root, proj_to_src_dir, build_args): + def get_args_defines_and_inc_dirs( + self, target: build.BuildTarget, + compiler: compilers.Compiler, + generated_files_include_dirs: list[str], + proj_to_src_root: str, + proj_to_src_dir: str, + build_args: list[str] + ) -> tuple[tuple[list[str], dict[Language, CompilerArgs]], tuple[list[str], dict[Language, list[str]]], tuple[list[str], dict[Language, list[str]]]]: # Arguments, include dirs, defines for all files in the current target target_args: list[str] = [] target_defines: list[str] = [] From 0a2d0d18d51cf6f607f409f81d7d372b09923cbc Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 1 Jun 2026 09:55:55 -0700 Subject: [PATCH 402/681] backend/vs2010: Add annotations to some fields assigned None These are only used in child classes, so they don't show up in mypy yet. --- mesonbuild/backend/vs2010backend.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index f655912fcdc4..e80cde6907a8 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -146,9 +146,9 @@ def __init__(self, build: T.Optional[build.Build], gen_lite: bool = False): self.project_file_version = '10.0.30319.1' self.sln_file_version = '11.00' self.sln_version_comment = '2010' - self.platform_toolset = None + self.platform_toolset: str | None = None self.vs_version = '2010' - self.windows_target_platform_version = None + self.windows_target_platform_version: str | None = None self.subdirs: dict[PurePath, tuple[str, str | None]] = {} self.handled_target_deps: dict[str, list[str]] = {} self.gen_lite = gen_lite # Synonymous with generating the simpler makefile-style multi-config projects that invoke 'meson compile' builds, avoiding native MSBuild complications From 4a077324c7a26a6ca4e30153fddd6facf4328805 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 1 Jun 2026 10:01:51 -0700 Subject: [PATCH 403/681] backend/vs2010: Add full typing for vslite_ctx This currently just has `dict = None`, which violates strict null checking, but also isn't particularly useful. --- mesonbuild/backend/vs2010backend.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index e80cde6907a8..1d72b0eace02 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -28,11 +28,14 @@ from .. import coredata if T.TYPE_CHECKING: + from typing_extensions import TypeAlias + from ..arglist import CompilerArgs from ..compilers.compilers import Language Project = T.Tuple[str, PurePath, str, MachineChoice] FileLike = T.TypeVar('FileLike', bound=File | str) + _VSLITE_CTX: TypeAlias = dict[str, dict[str, dict[Language, list[str]]]] def autodetect_vs_version(build: T.Optional[build.Build]) -> backends.Backend: vs_version = os.getenv('VisualStudioVersion', None) @@ -124,9 +127,10 @@ def get_primary_source_lang(target_sources: T.List[File], custom_sources: T.List # (pre-processor defines, include paths, additional compiler options) # fields to use to fill in the respective intellisense fields of sources that can't simply # reference and re-use the shared 'primary' language intellisense fields of the vcxproj. -def get_non_primary_lang_intellisense_fields(vslite_ctx: dict, +def get_non_primary_lang_intellisense_fields(vslite_ctx: _VSLITE_CTX, target_id: str, - primary_src_lang: str) -> T.Dict[str, T.Dict[str, T.Tuple[str, str, str]]]: + primary_src_lang: Language + ) -> T.Dict[str, T.Dict[str, T.Tuple[str, str, str]]]: defs_paths_opts_per_lang_and_buildtype: dict[str, dict[str, tuple[str, str, str]]] = {} for buildtype in coredata.get_genvs_default_buildtype_list(): captured_build_args = vslite_ctx[buildtype][target_id] # Results in a 'Src types to compile args' dict @@ -241,7 +245,7 @@ def generate_custom_generator_commands( def generate(self, capture: bool = False, - vslite_ctx: T.Optional[T.Dict] = None) -> None: + vslite_ctx: _VSLITE_CTX | None = None) -> None: # Check for (currently) unexpected capture arg use cases - if capture: raise MesonBugException('We do not expect any vs backend to generate with \'capture = True\'') @@ -568,7 +572,7 @@ def generate_solution(self, sln_filename: str, projlist: T.List[Project]) -> Non ofile.write('EndGlobal\n') replace_if_different(sln_filename, sln_filename_tmp) - def generate_projects(self, vslite_ctx: dict = None) -> T.List[Project]: + def generate_projects(self, vslite_ctx: _VSLITE_CTX | None = None) -> T.List[Project]: startup_project = self.environment.coredata.optstore.get_value_for('backend_startup_project') projlist: T.List[Project] = [] startup_idx = 0 @@ -1292,10 +1296,10 @@ def add_gen_lite_makefile_vcxproj_elements(self, root: ET.Element, platform: str, target_ext: str, - vslite_ctx: dict, + vslite_ctx: _VSLITE_CTX, target: build.AnyTargetType, proj_to_build_root: str, - primary_src_lang: T.Optional[str]) -> None: + primary_src_lang: T.Optional[Language]) -> None: ET.SubElement(root, 'ImportGroup', Label='ExtensionSettings') ET.SubElement(root, 'ImportGroup', Label='Shared') prop_sheets_grp = ET.SubElement(root, 'ImportGroup', Label='PropertySheets') @@ -1653,7 +1657,7 @@ def relocate_generated_file_paths_to_concrete_build_dir(self, gen_files: T.List[ # Returns bool indicating whether the .vcxproj has been generated. # Under some circumstances, it's unnecessary to create some .vcxprojs, so, when generating the .sln, # we need to respect that not all targets will have generated a project. - def gen_vcxproj(self, target: build.Target, ofname: str, guid: str, vslite_ctx: dict = None) -> bool: + def gen_vcxproj(self, target: build.Target, ofname: str, guid: str, vslite_ctx: _VSLITE_CTX | None = None) -> bool: mlog.debug(f'Generating vcxproj {target.name}.') subsystem = 'Windows' self.handled_target_deps[target.get_id()] = [] From 261c25d446d77b80a0befcb7720cf6d09a25622e Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 1 Jun 2026 09:55:41 -0700 Subject: [PATCH 404/681] backend: Finish typing the rest of the vs backends --- mesonbuild/backend/vs2017backend.py | 6 ++++-- mesonbuild/backend/vs2019backend.py | 6 ++++-- mesonbuild/backend/vs2022backend.py | 6 ++++-- mesonbuild/backend/vs2026backend.py | 6 ++++-- run_mypy.py | 8 ++++++++ 5 files changed, 24 insertions(+), 8 deletions(-) diff --git a/mesonbuild/backend/vs2017backend.py b/mesonbuild/backend/vs2017backend.py index bf6c33771f16..0f891fec4a63 100644 --- a/mesonbuild/backend/vs2017backend.py +++ b/mesonbuild/backend/vs2017backend.py @@ -11,7 +11,9 @@ from ..mesonlib import MesonException if T.TYPE_CHECKING: + from ..arglist import CompilerArgs from ..build import Build + from ..compilers.compilers import Language class Vs2017Backend(Vs2010Backend): @@ -45,11 +47,11 @@ def detect_toolset(self) -> None: if sdk_version: self.windows_target_platform_version = sdk_version.rstrip('\\') - def generate_debug_information(self, link): + def generate_debug_information(self, link: ET.Element) -> None: # valid values for vs2017 is 'false', 'true', 'DebugFastLink', 'DebugFull' ET.SubElement(link, 'GenerateDebugInformation').text = 'DebugFull' - def generate_lang_standard_info(self, file_args, clconf): + def generate_lang_standard_info(self, file_args: T.Dict[Language, CompilerArgs], clconf: ET.Element) -> None: if 'cpp' in file_args: optargs = [x for x in file_args['cpp'] if x.startswith('/std:c++')] if optargs: diff --git a/mesonbuild/backend/vs2019backend.py b/mesonbuild/backend/vs2019backend.py index bf86a4701730..b6cc0a382101 100644 --- a/mesonbuild/backend/vs2019backend.py +++ b/mesonbuild/backend/vs2019backend.py @@ -10,7 +10,9 @@ from .vs2010backend import Vs2010Backend if T.TYPE_CHECKING: + from ..arglist import CompilerArgs from ..build import Build + from ..compilers.compilers import Language class Vs2019Backend(Vs2010Backend): @@ -40,11 +42,11 @@ def detect_toolset(self) -> None: if sdk_version: self.windows_target_platform_version = sdk_version.rstrip('\\') - def generate_debug_information(self, link): + def generate_debug_information(self, link: ET.Element) -> None: # valid values for vs2019 is 'false', 'true', 'DebugFastLink', 'DebugFull' ET.SubElement(link, 'GenerateDebugInformation').text = 'DebugFull' - def generate_lang_standard_info(self, file_args, clconf): + def generate_lang_standard_info(self, file_args: T.Dict[Language, CompilerArgs], clconf: ET.Element) -> None: if 'cpp' in file_args: optargs = [x for x in file_args['cpp'] if x.startswith('/std:c++')] if optargs: diff --git a/mesonbuild/backend/vs2022backend.py b/mesonbuild/backend/vs2022backend.py index d7de4be32ba9..b6aaf3c70c1f 100644 --- a/mesonbuild/backend/vs2022backend.py +++ b/mesonbuild/backend/vs2022backend.py @@ -10,7 +10,9 @@ from .vs2010backend import Vs2010Backend if T.TYPE_CHECKING: + from ..arglist import CompilerArgs from ..build import Build + from ..compilers.compilers import Language class Vs2022Backend(Vs2010Backend): @@ -40,11 +42,11 @@ def detect_toolset(self) -> None: if sdk_version: self.windows_target_platform_version = sdk_version.rstrip('\\') - def generate_debug_information(self, link): + def generate_debug_information(self, link: ET.Element) -> None: # valid values for vs2022 is 'false', 'true', 'DebugFastLink', 'DebugFull' ET.SubElement(link, 'GenerateDebugInformation').text = 'DebugFull' - def generate_lang_standard_info(self, file_args, clconf): + def generate_lang_standard_info(self, file_args: T.Dict[Language, CompilerArgs], clconf: ET.Element) -> None: if 'cpp' in file_args: optargs = [x for x in file_args['cpp'] if x.startswith('/std:c++')] if optargs: diff --git a/mesonbuild/backend/vs2026backend.py b/mesonbuild/backend/vs2026backend.py index ba24d8ac49d4..5b3eea9ae1f8 100644 --- a/mesonbuild/backend/vs2026backend.py +++ b/mesonbuild/backend/vs2026backend.py @@ -10,7 +10,9 @@ from .vs2010backend import Vs2010Backend if T.TYPE_CHECKING: + from ..arglist import CompilerArgs from ..build import Build + from ..compilers.compilers import Language class Vs2026Backend(Vs2010Backend): @@ -40,11 +42,11 @@ def detect_toolset(self) -> None: if sdk_version: self.windows_target_platform_version = sdk_version.rstrip('\\') - def generate_debug_information(self, link): + def generate_debug_information(self, link: ET.Element) -> None: # valid values for vs2026 is 'false', 'true', 'DebugFastLink', 'DebugFull' ET.SubElement(link, 'GenerateDebugInformation').text = 'DebugFull' - def generate_lang_standard_info(self, file_args, clconf): + def generate_lang_standard_info(self, file_args: T.Dict[Language, CompilerArgs], clconf: ET.Element) -> None: if 'cpp' in file_args: optargs = [x for x in file_args['cpp'] if x.startswith('/std:c++')] if optargs: diff --git a/run_mypy.py b/run_mypy.py index be8f5592d655..bce6775e2928 100755 --- a/run_mypy.py +++ b/run_mypy.py @@ -34,6 +34,14 @@ 'mesonbuild/backend/backends.py', 'mesonbuild/backend/ninjabackend.py', 'mesonbuild/backend/nonebackend.py', + 'mesonbuild/backend/vs2010backend.py', + 'mesonbuild/backend/vs2012backend.py', + 'mesonbuild/backend/vs2013backend.py', + 'mesonbuild/backend/vs2015backend.py', + 'mesonbuild/backend/vs2017backend.py', + 'mesonbuild/backend/vs2019backend.py', + 'mesonbuild/backend/vs2022backend.py', + 'mesonbuild/backend/vs2026backend.py', 'mesonbuild/cmdline.py', 'mesonbuild/coredata.py', 'mesonbuild/depfile.py', From 36dcdca07d12787905b0650b19c03af3cdbb4389 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 1 Jun 2026 11:26:30 -0700 Subject: [PATCH 405/681] mesonmain: fix typing issues with `ensure_stdout_accepts_unicode` Need an additional type check, as well as to change a type ignore --- mesonbuild/mesonmain.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index 1ded0b155264..5aa2bd8bdf51 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -226,8 +226,8 @@ def run_script_command(script_name: str, script_args: T.List[str]) -> int: return 1 def ensure_stdout_accepts_unicode() -> None: - if sys.stdout.encoding and not sys.stdout.encoding.upper().startswith('UTF-'): - sys.stdout.reconfigure(errors='surrogateescape') # type: ignore[attr-defined] + if sys.stdout.encoding and isinstance(sys.stdout.encoding, str) and not sys.stdout.encoding.upper().startswith('UTF-'): + sys.stdout.reconfigure(errors='surrogateescape') # type: ignore[union-attr] def set_meson_command(mainfile: str) -> None: # Set the meson command that will be used to run scripts and so on From d1f600c8ea290b5e9540bb6b19cb136a9e798d12 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 1 Jun 2026 11:28:05 -0700 Subject: [PATCH 406/681] mesonmain: Fix annotations of validate_original_args --- mesonbuild/mesonmain.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index 5aa2bd8bdf51..0fdb2da24f45 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -234,17 +234,16 @@ def set_meson_command(mainfile: str) -> None: from . import mesonlib mesonlib.set_meson_command(mainfile) -def validate_original_args(args): +def validate_original_args(args: list[str]) -> None: import mesonbuild.options import itertools - def has_startswith(coll, target): + def has_startswith(coll: list[str], target: str) -> bool: for entry in coll: if entry.startswith(target + '=') or entry == target: return True return False - #ds = [x for x in args if x.startswith('-D')] - #longs = [x for x in args if x.startswith('--')] + for optionkey in itertools.chain(mesonbuild.options.BUILTIN_DIR_OPTIONS, mesonbuild.options.BUILTIN_CORE_OPTIONS): longarg = mesonbuild.options.argparse_name_to_arg(optionkey.name) shortarg = f'-D{optionkey.name}' From c3163eb6aa36f1898f1de68148850d170f4a88f5 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 1 Jun 2026 11:45:49 -0700 Subject: [PATCH 407/681] mesonmain: Use a Protocol for the command line arguments This get's us really close. --- mesonbuild/mesonmain.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index 0fdb2da24f45..670b856a4189 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -20,6 +20,14 @@ from .utils.core import MesonException, MesonBugException from . import mlog +if T.TYPE_CHECKING: + + class MesonMainCMDOptions(T.Protocol): + + command: str + run_func: T.Callable[['MesonMainCMDOptions'], int] + + def errorhandler(e: Exception, command: str) -> int: import traceback if isinstance(e, MesonException): @@ -178,7 +186,7 @@ def run(self, args: T.List[str]) -> int: from . import mesonlib args = mesonlib.expand_arguments(args) - options = parser.parse_args(args) + options = T.cast('MesonMainCMDOptions', parser.parse_args(args)) if command is None: command = options.command From 6035563c18c8b659e3f3ff12d7036b4b1bb1fdf4 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 2 Jun 2026 08:51:41 -0700 Subject: [PATCH 408/681] mesonmain: Cast importlib to fix final mypy issues --- mesonbuild/mesonmain.py | 7 ++++--- run_mypy.py | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index 670b856a4189..ee2f5c8a8ea9 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -221,13 +221,14 @@ def run_script_command(script_name: str, script_args: T.List[str]) -> int: module_name = script_map.get(script_name, script_name) try: - module = importlib.import_module('mesonbuild.scripts.' + module_name) - except ModuleNotFoundError as e: + func = T.cast('T.Callable[[list[str]], int]', + importlib.import_module('mesonbuild.scripts.' + module_name).run) + except (ModuleNotFoundError, AttributeError) as e: mlog.exception(e) return 1 try: - return module.run(script_args) + return func(script_args) except MesonException as e: mlog.error(f'Error in {script_name} helper script:') mlog.exception(e) diff --git a/run_mypy.py b/run_mypy.py index bce6775e2928..37d79578190c 100755 --- a/run_mypy.py +++ b/run_mypy.py @@ -49,6 +49,7 @@ 'mesonbuild/environment.py', 'mesonbuild/machinefile.py', 'mesonbuild/mesondata.py', + 'mesonbuild/mesonmain.py', 'mesonbuild/mcompile.py', 'mesonbuild/mdevenv.py', 'mesonbuild/mconf.py', From a7e124d43d1000a5d363f1f4a0afa7b948c0935d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 20 Mar 2026 11:51:24 +0100 Subject: [PATCH 409/681] add unit tests for version checks --- unittests/versiontests.py | 169 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 unittests/versiontests.py diff --git a/unittests/versiontests.py b/unittests/versiontests.py new file mode 100644 index 000000000000..cace1e61dc53 --- /dev/null +++ b/unittests/versiontests.py @@ -0,0 +1,169 @@ +# SPDX-License-Identifier: Apache-2.0 + +import unittest + +from mesonbuild.mesonlib import ( + Version, version_compare, version_compare_many, + version_compare_condition_with_min, search_version, +) + + +class VersionComparisonTests(unittest.TestCase): + + def test_version_ordering(self): + LT = -1 + EQ = 0 + #GT = 1 + + # mostly from RPM tests + tests = [ + ("1.0", "1.0", EQ), + ("1_0", "1_0", EQ), + ("1_0", "1.0", EQ), + ("1.0", "2.0", LT), + ("2.0", "2.0.0", LT), + ("2.0", "2.0.1", LT), + ("2.0.1", "2.0.1", EQ), + ("2.0.1", "2.0.1a", LT), + ("2.0.1a", "2.0.1a", EQ), + ("2.10", "3.111", LT), + ("2.456", "2.1000", LT), + ("1.2rc1", "1.2.0", LT), + ("5.5p1", "5.5p1", EQ), + ("5.5p1", "5.5p2", LT), + ("5.5p1", "5.5p10", LT), + ("10xyz", "10.1xyz", LT), + ("xyz10", "xyz10", EQ), + ("xyz10", "xyz10.1", LT), + ("xyz.4", "8", LT), + ("xyz.4", "2", LT), + ("5.5p2", "5.6p1", LT), + ("5.6p1", "6.5p1", LT), + ("6.0", "6.0.rc1", LT), + ("10a2", "10b2", LT), + ("1.0aa", "1.0aa", EQ), + ("1.0a", "1.0aa", LT), + ("10.0001", "10.1", EQ), + ("10.0001", "10.0039", LT), + ("1.05", "1.5", EQ), + ("2a", "2.0", LT), + ] + + for (a, b, result) in tests: + with self.subTest(f'({a!r}, {b!r})'): + ver_a = Version(a) + ver_b = Version(b) + + self.assertEqual(ver_a <= ver_b, result <= 0, f'{ver_a} <= {ver_b}') + self.assertEqual(ver_a < ver_b, result < 0, f'{ver_a} <= {ver_b}') + self.assertEqual(ver_a > ver_b, result > 0, f'{ver_a} <= {ver_b}') + self.assertEqual(ver_a >= ver_b, result >= 0, f'{ver_a} <= {ver_b}') + + self.assertEqual(ver_a == ver_b, result == 0, f'{ver_a} <= {ver_b}') + self.assertEqual(ver_b == ver_a, result == 0, f'{ver_a} <= {ver_b}') + self.assertEqual(ver_a != ver_b, result != 0, f'{ver_a} <= {ver_b}') + self.assertEqual(ver_b != ver_a, result != 0, f'{ver_a} <= {ver_b}') + + self.assertEqual(ver_b <= ver_a, -result <= 0, f'{ver_a} <= {ver_b}') + self.assertEqual(ver_b < ver_a, -result < 0, f'{ver_a} <= {ver_b}') + self.assertEqual(ver_b > ver_a, -result > 0, f'{ver_a} <= {ver_b}') + self.assertEqual(ver_b >= ver_a, -result >= 0, f'{ver_a} <= {ver_b}') + + def test_version_compare(self): + """Test version_compare with operator prefixes.""" + self.assertTrue(version_compare('1.0', '>=1.0')) + self.assertTrue(version_compare('1.1', '>=1.0')) + self.assertFalse(version_compare('0.9', '>=1.0')) + + self.assertTrue(version_compare('1.0', '<=1.0')) + self.assertTrue(version_compare('0.9', '<=1.0')) + self.assertFalse(version_compare('1.1', '<=1.0')) + + self.assertTrue(version_compare('1.0', '>0.9')) + self.assertFalse(version_compare('1.0', '>1.0')) + + self.assertTrue(version_compare('1.0', '<1.1')) + self.assertFalse(version_compare('1.0', '<1.0')) + + self.assertTrue(version_compare('1.0', '==1.0')) + self.assertFalse(version_compare('1.0', '==1.1')) + + self.assertTrue(version_compare('1.0', '!=1.1')) + self.assertFalse(version_compare('1.0', '!=1.0')) + + # bare version and = means == + self.assertTrue(version_compare('1.0', '1.0')) + self.assertFalse(version_compare('1.0', '1.1')) + self.assertTrue(version_compare('1.0', '=1.0')) + self.assertFalse(version_compare('1.0', '=1.1')) + + def test_version_compare_many(self): + result, not_found, found = version_compare_many('1.5', ['>=1.0', '<2.0']) + self.assertTrue(result) + self.assertEqual(not_found, []) + self.assertEqual(found, ['>=1.0', '<2.0']) + + result, not_found, found = version_compare_many('0.5', ['>=1.0', '<2.0']) + self.assertFalse(result) + self.assertEqual(not_found, ['>=1.0']) + self.assertEqual(found, ['<2.0']) + + result, not_found, found = version_compare_many('2.5', ['>=1.0', '<2.0']) + self.assertFalse(result) + self.assertEqual(not_found, ['<2.0']) + self.assertEqual(found, ['>=1.0']) + + # string condition is treated as single-element list + result, not_found, found = version_compare_many('1.0', '>=1.0') + self.assertTrue(result) + self.assertEqual(not_found, []) + self.assertEqual(found, ['>=1.0']) + + def test_version_compare_condition_with_min(self): + # >= condition: minimum must be <= condition version + self.assertTrue(version_compare_condition_with_min('>=0.46.0', '0.46.0')) + self.assertTrue(version_compare_condition_with_min('>=0.50.0', '0.46.0')) + self.assertFalse(version_compare_condition_with_min('>=0.40.0', '0.46.0')) + + # > condition: minimum must be strictly < condition version + self.assertFalse(version_compare_condition_with_min('>0.46.0', '0.46.0')) + self.assertTrue(version_compare_condition_with_min('>0.50.0', '0.46.0')) + self.assertFalse(version_compare_condition_with_min('>0.45.0', '0.46.0')) + + # == condition: minimum must be <= condition version + self.assertTrue(version_compare_condition_with_min('==0.46.0', '0.46.0')) + self.assertTrue(version_compare_condition_with_min('==0.50.0', '0.46.0')) + self.assertFalse(version_compare_condition_with_min('==0.40.0', '0.46.0')) + + # = or bare condition is the same as == + self.assertTrue(version_compare_condition_with_min('=0.46.0', '0.46.0')) + self.assertTrue(version_compare_condition_with_min('0.46.0', '0.46.0')) + self.assertFalse(version_compare_condition_with_min('0.40.0', '0.46.0')) + + # < and <= conditions always return False (includes versions older than minimum) + self.assertFalse(version_compare_condition_with_min('<1.0.0', '0.46.0')) + self.assertFalse(version_compare_condition_with_min('<=1.0.0', '0.46.0')) + self.assertFalse(version_compare_condition_with_min('<0.30.0', '0.46.0')) + self.assertFalse(version_compare_condition_with_min('<=0.30.0', '0.46.0')) + + # != condition always returns False + self.assertFalse(version_compare_condition_with_min('!=0.40.0', '0.46.0')) + + # two-component version in condition is mapped to three-component + # e.g. '>=0.46' is treated as '>=0.46.0' + self.assertTrue(version_compare_condition_with_min('>=0.46', '0.46.0')) + self.assertFalse(version_compare_condition_with_min('>=0.46', '0.46.1')) + self.assertTrue(version_compare_condition_with_min('>=0.46.0', '0.46')) + self.assertTrue(version_compare_condition_with_min('>=0.46.1', '0.46')) + + def test_search_version(self): + self.assertEqual(search_version('foo 1.2.3'), '1.2.3') + self.assertEqual(search_version('1.2.3'), '1.2.3') + self.assertEqual(search_version('foo 2026.03.20 1.2.3'), '1.2.3') + self.assertEqual(search_version('2026.03.20 1.2.3'), '1.2.3') + self.assertEqual(search_version('foo 2026.03.20'), '2026.03.20') + self.assertEqual(search_version('2026.03.20'), '2026.03.20') + self.assertEqual(search_version('2026.03'), '2026.03') + self.assertEqual(search_version('2026.03 1.2.3'), '1.2.3') + self.assertEqual(search_version('foo v1.2.3'), '1.2.3') + self.assertEqual(search_version('2026'), 'unknown version') From 589b1488a6659c688032454bdcadda80166c028e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 20 Mar 2026 14:54:49 +0100 Subject: [PATCH 410/681] utils: fix version_compare_condition_with_min with > condition Right now specifying >1.10.0 gives warnings like meson.build:18: WARNING: Project targets '>1.10.0' but uses feature introduced in '1.10.0': meson.version().version_compare() with multiple arguments. From 1.8.0 - 1.9.* it failed to match str.version_compare This is incorrect. A project targeting 1.10.1 or later can use features introduced by 1.10.0. Fix the bug in version_compare_condition_with_min and adjust the testcases. Signed-off-by: Paolo Bonzini --- mesonbuild/utils/universal.py | 8 +------- unittests/versiontests.py | 4 ++-- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index 70bb5588dd20..94f2f66cf184 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -1020,25 +1020,19 @@ def version_compare_many(vstr1: str, conditions: T.Union[str, T.Iterable[str]]) # the minimum version for a feature |minimum| def version_compare_condition_with_min(condition: str, minimum: str) -> bool: if condition.startswith('>='): - cmpop = operator.le condition = condition[2:] elif condition.startswith('<='): return False elif condition.startswith('!='): return False elif condition.startswith('=='): - cmpop = operator.le condition = condition[2:] elif condition.startswith('='): - cmpop = operator.le condition = condition[1:] elif condition.startswith('>'): - cmpop = operator.lt condition = condition[1:] elif condition.startswith('<'): return False - else: - cmpop = operator.le # Declaring a project(meson_version: '>=0.46') and then using features in # 0.46.0 is valid, because (knowing the meson versioning scheme) '0.46.0' is @@ -1054,7 +1048,7 @@ def version_compare_condition_with_min(condition: str, minimum: str) -> bool: if re.match(r'^\d+.\d+$', condition): condition += '.0' - return T.cast('bool', cmpop(Version(minimum), Version(condition))) + return Version(minimum) <= Version(condition) def search_version(text: str) -> str: # Usually of the type 4.1.4 but compiler output may contain diff --git a/unittests/versiontests.py b/unittests/versiontests.py index cace1e61dc53..a800dcb30d96 100644 --- a/unittests/versiontests.py +++ b/unittests/versiontests.py @@ -125,8 +125,8 @@ def test_version_compare_condition_with_min(self): self.assertTrue(version_compare_condition_with_min('>=0.50.0', '0.46.0')) self.assertFalse(version_compare_condition_with_min('>=0.40.0', '0.46.0')) - # > condition: minimum must be strictly < condition version - self.assertFalse(version_compare_condition_with_min('>0.46.0', '0.46.0')) + # > condition: minimum must be <= condition version + self.assertTrue(version_compare_condition_with_min('>0.46.0', '0.46.0')) self.assertTrue(version_compare_condition_with_min('>0.50.0', '0.46.0')) self.assertFalse(version_compare_condition_with_min('>0.45.0', '0.46.0')) From 2f9c97d591bfb054910bb622c15557b3d6d10992 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 29 May 2026 09:27:24 +0200 Subject: [PATCH 411/681] utils, interpreter: move special handling of .0 versions to feature decorators Signed-off-by: Paolo Bonzini --- mesonbuild/interpreter/interpreter.py | 4 +-- mesonbuild/interpreterbase/decorators.py | 33 +++++++++++++++--------- mesonbuild/utils/universal.py | 14 ---------- unittests/versiontests.py | 7 ++--- 4 files changed, 27 insertions(+), 31 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index c728b4f17e44..e58b6bfde9fa 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1361,7 +1361,7 @@ def func_add_languages(self, node: mparser.FunctionNode, args: T.Tuple[T.List[st else: # absent 'native' means 'both' for backwards compatibility tv = FeatureNew.get_target_version(self.subproject) - if FeatureNew.check_version(tv, '0.54.0'): + if FeatureNew.check_version(tv, '0.54'): mlog.warning('add_languages is missing native:, assuming languages are wanted for both host and build.', location=node) @@ -2786,7 +2786,7 @@ def func_configure_file(self, node: mparser.BaseNode, args: T.List[TYPE_var], if confdata_useless: ifbase = os.path.basename(inputs_abs[0]) tv = FeatureNew.get_target_version(self.subproject) - if FeatureNew.check_version(tv, '0.47.0'): + if FeatureNew.check_version(tv, '0.47'): mlog.warning('Got an empty configuration_data() object and found no ' f'substitutions in the input file {ifbase!r}. If you want to ' 'copy a file to the build dir, use the \'copy:\' keyword ' diff --git a/mesonbuild/interpreterbase/decorators.py b/mesonbuild/interpreterbase/decorators.py index 2df5487d19e5..e1589436f701 100644 --- a/mesonbuild/interpreterbase/decorators.py +++ b/mesonbuild/interpreterbase/decorators.py @@ -635,8 +635,17 @@ class FeatureCheckBase(metaclass=mesonlib.SimpleABC): def __init__(self, feature_name: str, feature_version: str, extra_message: str = ''): self.feature_name = feature_name - self.feature_version = feature_version + self.feature_version_for_msg = feature_version self.extra_message = extra_message + self.feature_version = feature_version + # Map versions in the constraint of the form '0.46.0' to '0.46', to + # ensure that '0.46' in project(meson_version: '>=0.46') allows + # using features in '0.46.0'. Meson versioning is basically + # semver, i.e. '0.46.0' is the lowest version which satisfies the + # constraint '>=0.46', but meson.version_compare() is more like + # rpm versions for historical reasons. + while self.feature_version.endswith('.0'): + self.feature_version = self.feature_version[:-2] @staticmethod def get_target_version(subproject: str) -> T.Union[str, mesonlib.NoProjectVersion]: @@ -660,18 +669,18 @@ def use(self, subproject: 'SubProject', location: T.Optional['mparser.BaseNode'] return # Feature is too new for target version or we want to emit notices, register it if subproject not in self.feature_registry: - self.feature_registry[subproject] = {self.feature_version: set()} + self.feature_registry[subproject] = {self.feature_version_for_msg: set()} register = self.feature_registry[subproject] - if self.feature_version not in register: - register[self.feature_version] = set() + if self.feature_version_for_msg not in register: + register[self.feature_version_for_msg] = set() feature_key = (self.feature_name, location) - if feature_key in register[self.feature_version]: + if feature_key in register[self.feature_version_for_msg]: # Don't warn about the same feature multiple times # FIXME: This is needed to prevent duplicate warnings, but also # means we won't warn about a feature used in multiple places. return - register[self.feature_version].add(feature_key) + register[self.feature_version_for_msg].add(feature_key) # Target version is new enough, don't warn even if it is registered for notice if self.check_version(tv, self.feature_version): return @@ -729,7 +738,7 @@ class FeatureNew(FeatureCheckBase): # Class variable, shared across all instances # - # Format: {subproject: {feature_version: set(feature_names)}} + # Format: {subproject: {feature_version_for_msg: set(feature_names)}} feature_registry = {} @staticmethod @@ -760,7 +769,7 @@ def log_usage_warning(self, tv: T.Union[str, mesonlib.NoProjectVersion], locatio args = [ prefix, 'but uses feature introduced in', - f"'{self.feature_version}':", + f"'{self.feature_version_for_msg}':", f'{self.feature_name}.', ] if self.extra_message: @@ -772,7 +781,7 @@ class FeatureDeprecated(FeatureCheckBase): # Class variable, shared across all instances # - # Format: {subproject: {feature_version: set(feature_names)}} + # Format: {subproject: {feature_version_for_msg: set(feature_names)}} feature_registry = {} emit_notice = True @@ -801,7 +810,7 @@ def log_usage_warning(self, tv: T.Union[str, mesonlib.NoProjectVersion], locatio args = [ prefix, 'but uses feature deprecated since', - f"'{self.feature_version}':", + f"'{self.feature_version_for_msg}':", f'{self.feature_name}.', ] if self.extra_message: @@ -814,7 +823,7 @@ class FeatureBroken(FeatureCheckBase): # Class variable, shared across all instances # - # Format: {subproject: {feature_version: set(feature_names)}} + # Format: {subproject: {feature_version_for_msg: set(feature_names)}} feature_registry = {} unconditional = True @@ -835,7 +844,7 @@ def log_usage_warning(self, tv: T.Union[str, mesonlib.NoProjectVersion], locatio args = [ 'Project uses feature that was always broken,', 'and is now deprecated since', - f"'{self.feature_version}':", + f"'{self.feature_version_for_msg}':", f'{self.feature_name}.', ] if self.extra_message: diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index 94f2f66cf184..8973650ebb45 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -1034,20 +1034,6 @@ def version_compare_condition_with_min(condition: str, minimum: str) -> bool: elif condition.startswith('<'): return False - # Declaring a project(meson_version: '>=0.46') and then using features in - # 0.46.0 is valid, because (knowing the meson versioning scheme) '0.46.0' is - # the lowest version which satisfies the constraint '>=0.46'. - # - # But this will fail here, because the minimum version required by the - # version constraint ('0.46') is strictly less (in our version comparison) - # than the minimum version needed for the feature ('0.46.0'). - # - # Map versions in the constraint of the form '0.46' to '0.46.0', to embed - # this knowledge of the meson versioning scheme. - condition = condition.strip() - if re.match(r'^\d+.\d+$', condition): - condition += '.0' - return Version(minimum) <= Version(condition) def search_version(text: str) -> str: diff --git a/unittests/versiontests.py b/unittests/versiontests.py index a800dcb30d96..05e52f303a69 100644 --- a/unittests/versiontests.py +++ b/unittests/versiontests.py @@ -149,9 +149,10 @@ def test_version_compare_condition_with_min(self): # != condition always returns False self.assertFalse(version_compare_condition_with_min('!=0.40.0', '0.46.0')) - # two-component version in condition is mapped to three-component - # e.g. '>=0.46' is treated as '>=0.46.0' - self.assertTrue(version_compare_condition_with_min('>=0.46', '0.46.0')) + # test behavior of two-component versions + # '0.46.0' is mangled to '0.46' so that '>=0.46.0' works, but this is + # not done by version_compare_condition_with_min + self.assertFalse(version_compare_condition_with_min('>=0.46', '0.46.0')) self.assertFalse(version_compare_condition_with_min('>=0.46', '0.46.1')) self.assertTrue(version_compare_condition_with_min('>=0.46.0', '0.46')) self.assertTrue(version_compare_condition_with_min('>=0.46.1', '0.46')) From 296c555577d6dd250207463e8d8fa78281976643 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 20 Mar 2026 11:55:38 +0100 Subject: [PATCH 412/681] utils: add Range class Add a class that can be used to encode a range of versions. It is useful to express the project's minimum version and to reason on it, for example to detect redundant Meson version checks. --- mesonbuild/utils/universal.py | 85 +++++++++++++++++- unittests/versiontests.py | 159 +++++++++++++++++++++++++++++++++- 2 files changed, 239 insertions(+), 5 deletions(-) diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index 8973650ebb45..ca571f6a0d4a 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -31,7 +31,7 @@ from .core import MesonException, HoldableObject if T.TYPE_CHECKING: - from typing_extensions import Literal, Protocol + from typing_extensions import Literal, Protocol, Self from .._typing import ImmutableListProtocol from ..build import ConfigurationData @@ -47,6 +47,15 @@ class _VerPickleLoadable(Protocol): version: str + class Comparable(Protocol): + """Protocol for annotating comparable types.""" + def __eq__(self, other: object) -> bool: ... + def __ne__(self, other: object) -> bool: ... + def __lt__(self, other: Self) -> bool: ... + def __le__(self, other: Self) -> bool: ... + def __ge__(self, other: Self) -> bool: ... + def __gt__(self, other: Self) -> bool: ... + # A generic type for pickle_load. This allows any type that has either a # .version or a .environment to be passed. _PL = T.TypeVar('_PL', bound=T.Union[_EnvPickleLoadable, _VerPickleLoadable]) @@ -82,6 +91,7 @@ class _VerPickleLoadable(Protocol): 'PerThreeMachine', 'PerThreeMachineDefaultable', 'ProgressBar', + 'Range', 'RealPathAction', 'TemporaryDirectoryWinProof', 'Version', @@ -918,10 +928,10 @@ def __init__(self, s: str) -> None: for m in _VERSION_TOK_RE.finditer(s)] def __str__(self) -> str: - return '{} (V={})'.format(self._s, str(self._v)) + return self._s def __repr__(self) -> str: - return f'' + return f'' def __lt__(self, other: object) -> bool: if isinstance(other, Version): @@ -1016,6 +1026,75 @@ def version_compare_many(vstr1: str, conditions: T.Union[str, T.Iterable[str]]) return not not_found, not_found, found +_V = T.TypeVar('_V', bound='Comparable') + +@dataclasses.dataclass(order=False) +class Range(T.Generic[_V]): + min: T.Optional[_V] = None + min_eq: bool = False + max: T.Optional[_V] = None + max_eq: bool = False + is_empty: bool = False + + def __str__(self) -> str: + if self.is_empty: + return '(empty)' + if self.min is not None and self.max is not None and self.min == self.max: + return f'== {self.min}' + parts = [] + if self.min is not None: + parts.append(f'>{"=" if self.min_eq else ""} {self.min}') + if self.max is not None: + parts.append(f'<{"=" if self.max_eq else ""} {self.max}') + return ', '.join(parts) if parts else '(any)' + + def __contains__(self, x: _V) -> bool: + if self.is_empty: + return False + if self.min is not None and (x < self.min if self.min_eq else x <= self.min): + return False + if self.max is not None and (x > self.max if self.max_eq else x >= self.max): + return False + return True + + def __post_init__(self) -> None: + if self.min is None or self.max is None: + return + self.is_empty = False + if self.min < self.max: + return + if self.min == self.max and self.min_eq and self.max_eq: + return + self.min = None + self.max = None + self.is_empty = True + + def _intersect_min(self, v: _V, eq: bool) -> None: + if self.min is None or v > self.min: + self.min, self.min_eq = v, eq + elif v == self.min: + self.min_eq = eq and self.min_eq + + def _intersect_max(self, v: _V, eq: bool) -> None: + if self.max is None or v < self.max: + self.max, self.max_eq = v, eq + elif v == self.max: + self.max_eq = eq and self.max_eq + + def intersect(self, x: Range[_V]) -> Range[_V]: + if x.is_empty: + return copy.copy(x) + result = copy.copy(self) + if self.is_empty: + return result + if x.min is not None: + result._intersect_min(x.min, x.min_eq) + if x.max is not None: + result._intersect_max(x.max, x.max_eq) + result.__post_init__() + return result + + # determine if the minimum version satisfying the condition |condition| exceeds # the minimum version for a feature |minimum| def version_compare_condition_with_min(condition: str, minimum: str) -> bool: diff --git a/unittests/versiontests.py b/unittests/versiontests.py index 05e52f303a69..2652ef3597d3 100644 --- a/unittests/versiontests.py +++ b/unittests/versiontests.py @@ -3,8 +3,9 @@ import unittest from mesonbuild.mesonlib import ( - Version, version_compare, version_compare_many, - version_compare_condition_with_min, search_version, + Range, Version, version_compare, + version_compare_many, version_compare_condition_with_min, + search_version, ) @@ -157,6 +158,160 @@ def test_version_compare_condition_with_min(self): self.assertTrue(version_compare_condition_with_min('>=0.46.0', '0.46')) self.assertTrue(version_compare_condition_with_min('>=0.46.1', '0.46')) + def test_range_contains(self): + """Test Range.__contains__.""" + # unbounded range contains everything + r = Range() + self.assertIn(5, r) + self.assertIn(0, r) + + # min only, inclusive + r = Range(min=3, min_eq=True) + self.assertIn(3, r) + self.assertIn(5, r) + self.assertNotIn(2, r) + + # min only, exclusive + r = Range(min=3, min_eq=False) + self.assertNotIn(3, r) + self.assertIn(4, r) + self.assertNotIn(2, r) + + # max only, inclusive + r = Range(max=7, max_eq=True) + self.assertIn(7, r) + self.assertIn(5, r) + self.assertNotIn(8, r) + + # max only, exclusive + r = Range(max=7, max_eq=False) + self.assertNotIn(7, r) + self.assertIn(6, r) + self.assertNotIn(8, r) + + # both bounds, inclusive + r = Range(min=3, min_eq=True, max=7, max_eq=True) + self.assertIn(3, r) + self.assertIn(5, r) + self.assertIn(7, r) + self.assertNotIn(2, r) + self.assertNotIn(8, r) + + # both bounds, exclusive + r = Range(min=3, min_eq=False, max=7, max_eq=False) + self.assertNotIn(3, r) + self.assertIn(5, r) + self.assertNotIn(7, r) + + # empty range contains nothing + r = Range(min=5, min_eq=False, max=5, max_eq=False) + self.assertNotIn(5, r) + + # min == max, both inclusive -> single point, not empty + r = Range(min=5, max=5, min_eq=True, max_eq=True) + self.assertIn(5, r) + + def test_range_init_trivial(self): + """Test that __post_init__ detects trivial ranges.""" + r = Range() + self.assertFalse(r.is_empty) + + # min > max + r = Range(min=7, max=3, min_eq=True, max_eq=True) + self.assertTrue(r.is_empty) + + # min == max, not both inclusive -> empty + r = Range(min=5, max=5, min_eq=True, max_eq=False) + self.assertTrue(r.is_empty) + + r = Range(min=5, max=5, min_eq=False, max_eq=True) + self.assertTrue(r.is_empty) + + r = Range(min=5, max=5, min_eq=False, max_eq=False) + self.assertTrue(r.is_empty) + + # min == max, both inclusive -> single point, not empty + r = Range(min=5, max=5, min_eq=True, max_eq=True) + self.assertFalse(r.is_empty) + + def test_range_intersect(self): + """Test Range.intersect.""" + # intersect two overlapping ranges + a = Range(min=1, min_eq=True, max=10, max_eq=True) + b = Range(min=5, min_eq=True, max=15, max_eq=True) + r = a.intersect(b) + self.assertIn(5, r) + self.assertIn(10, r) + self.assertNotIn(4, r) + self.assertNotIn(11, r) + + # intersect does not mutate original + self.assertIn(4, a) + + # intersect narrows unbounded range + a = Range() + b = Range(min=3, min_eq=True, max=7, max_eq=False) + r = a.intersect(b) + self.assertIn(3, r) + self.assertIn(6, r) + self.assertNotIn(2, r) + self.assertNotIn(7, r) + + # intersect non-overlapping ranges produces empty + a = Range(min=1, min_eq=True, max=3, max_eq=True) + b = Range(min=5, min_eq=True, max=7, max_eq=True) + r = a.intersect(b) + self.assertTrue(r.is_empty) + + # intersect with matching boundary tightens eq + a = Range(min=5, min_eq=True) + b = Range(min=5, min_eq=False) + r = a.intersect(b) + self.assertEqual(5, r.min) + self.assertNotIn(5, r) + + a = Range(max=5, max_eq=True) + b = Range(max=5, max_eq=False) + r = a.intersect(b) + self.assertEqual(5, r.max) + self.assertIn(4, r) + + # intersecting empty range stays empty + a = Range(min=7, max=3, min_eq=True, max_eq=True) + self.assertTrue(a.is_empty) + b = Range(min=1, min_eq=True, max=10, max_eq=True) + r = a.intersect(b) + self.assertTrue(r.is_empty) + + b = Range() + r = a.intersect(b) + self.assertTrue(r.is_empty) + + a = Range() + b = Range(min=7, max=3, min_eq=True, max_eq=True) + r = a.intersect(b) + self.assertTrue(r.is_empty) + + def test_range_str(self): + """Test Range.__str__.""" + self.assertEqual(str(Range()), '(any)') + self.assertEqual(str(Range(min=3, min_eq=True)), '>= 3') + self.assertEqual(str(Range(min=3, min_eq=False)), '> 3') + self.assertEqual(str(Range(max=7, max_eq=True)), '<= 7') + self.assertEqual(str(Range(max=7, max_eq=False)), '< 7') + self.assertEqual(str(Range(min=3, min_eq=True, max=7, max_eq=False)), '>= 3, < 7') + self.assertEqual(str(Range(min=5, max=5, min_eq=True, max_eq=True)), '== 5') + # empty range + r = Range(min=7, max=3, min_eq=True, max_eq=True) + self.assertEqual(str(r), '(empty)') + + def test_range_str_version(self): + """Test Range.__str__ with Version objects.""" + r = Range(min=Version('0.46.0'), min_eq=True) + self.assertEqual(str(r), '>= 0.46.0') + r = Range(min=Version('1.0'), min_eq=True, max=Version('2.0'), max_eq=False) + self.assertEqual(str(r), '>= 1.0, < 2.0') + def test_search_version(self): self.assertEqual(search_version('foo 1.2.3'), '1.2.3') self.assertEqual(search_version('1.2.3'), '1.2.3') From 348acc73830e06dcd88e913dbbfef747dc76cede Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 20 Mar 2026 14:55:43 +0100 Subject: [PATCH 413/681] reimplement version_compare_condition_with_min in terms of Range version_compare_condition_with_min includes both parsing and manipulation of the project's minimum version. Simplify it version_compare_condition_with_min by first converting the project(meson_version: ...) to a Range, and then operating (not surprisingly) on its .min field. --- mesonbuild/utils/universal.py | 50 ++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index ca571f6a0d4a..a7193c7b63d7 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -1005,7 +1005,7 @@ def _version_extract_cmpop(vstr2: str) -> T.Tuple[T.Callable[[T.Any, T.Any], boo else: cmpop = operator.eq - return (cmpop, vstr2) + return (cmpop, vstr2.strip()) def version_compare(vstr1: str, vstr2: str) -> bool: @@ -1095,25 +1095,39 @@ def intersect(self, x: Range[_V]) -> Range[_V]: return result +# note that Range is immutable, so no need to have Range() | None +def version_check_to_range(checks: T.List[str], start: Range[Version] = Range()) -> Range[Version]: + for x in checks: + op, v = _version_extract_cmpop(x) + if op is operator.ge: + r = Range(min=Version(v), min_eq=True) + elif op is operator.gt: + r = Range(min=Version(v), min_eq=False) + elif op is operator.le: + r = Range(max=Version(v), max_eq=True) + elif op is operator.lt: + r = Range(max=Version(v), max_eq=False) + elif op is operator.eq: + r = Range(min=Version(v), max=Version(v), min_eq=True, max_eq=True) + elif op is operator.ne: + continue # cop out + start = start.intersect(r) + return start + + # determine if the minimum version satisfying the condition |condition| exceeds # the minimum version for a feature |minimum| -def version_compare_condition_with_min(condition: str, minimum: str) -> bool: - if condition.startswith('>='): - condition = condition[2:] - elif condition.startswith('<='): - return False - elif condition.startswith('!='): - return False - elif condition.startswith('=='): - condition = condition[2:] - elif condition.startswith('='): - condition = condition[1:] - elif condition.startswith('>'): - condition = condition[1:] - elif condition.startswith('<'): - return False - - return Version(minimum) <= Version(condition) +def version_compare_condition_with_min(condition: T.Union[str, Range[Version]], minimum: str) -> bool: + if isinstance(condition, str): + condition = version_check_to_range([condition]) + + if condition.min is None: + # A < constraint on the project version (max is not None) or a full + # range should always include versions older than minimum, return False. + # is_empty=True instead behaves like an absurdly high min and returns True. + return condition.is_empty + else: + return Version(minimum) <= condition.min def search_version(text: str) -> str: # Usually of the type 4.1.4 but compiler output may contain From a36292fe65c37da192a01818ed80f89c874a9160 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 20 Mar 2026 14:16:59 +0100 Subject: [PATCH 414/681] decorators: return None for the target version before project() An empty string does not really have an equivalent with Range; change it to None in preparation for tracking the current project version in a Range[Version] --- mesonbuild/interpreterbase/decorators.py | 40 +++++++++++++----------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/mesonbuild/interpreterbase/decorators.py b/mesonbuild/interpreterbase/decorators.py index e1589436f701..27e33f907bb0 100644 --- a/mesonbuild/interpreterbase/decorators.py +++ b/mesonbuild/interpreterbase/decorators.py @@ -35,6 +35,8 @@ def __call__(s, self: _TV_IntegerObject, other: _TV_ARG1) -> TYPE_var: ... CalleeArgs: TypeAlias = T.Tuple[mparser.BaseNode, T.Optional[T.List[TYPE_var]], T.Optional[TYPE_kwargs], SubProject] + MesonVersionTarget = str | mesonlib.NoProjectVersion | None + def is_module(obj: object) -> TypeIs[ModuleObject]: return not hasattr(obj, 'current_node') @@ -648,21 +650,21 @@ def __init__(self, feature_name: str, feature_version: str, extra_message: str = self.feature_version = self.feature_version[:-2] @staticmethod - def get_target_version(subproject: str) -> T.Union[str, mesonlib.NoProjectVersion]: + def get_target_version(subproject: str) -> MesonVersionTarget: # Don't do any checks if project() has not been parsed yet if subproject not in mesonlib.project_meson_versions: - return '' + return None return mesonlib.project_meson_versions[subproject] @staticmethod @abc.abstractmethod - def check_version(target_version: T.Union[str, mesonlib.NoProjectVersion], feature_version: str) -> bool: + def check_version(target_version: MesonVersionTarget, feature_version: str) -> bool: pass def use(self, subproject: 'SubProject', location: T.Optional['mparser.BaseNode'] = None) -> None: tv = self.get_target_version(subproject) # No target version - if tv == '' and not self.unconditional: + if tv is None and not self.unconditional: return # Target version is new enough, don't warn if self.check_version(tv, self.feature_version) and not self.emit_notice: @@ -705,15 +707,15 @@ def report(cls, subproject: str) -> None: if '\n' in warning_str: mlog.warning(warning_str) - def log_usage_warning(self, tv: T.Union[str, mesonlib.NoProjectVersion], location: T.Optional['mparser.BaseNode']) -> None: + def log_usage_warning(self, tv: MesonVersionTarget, location: T.Optional['mparser.BaseNode']) -> None: raise InterpreterException('log_usage_warning not implemented') @staticmethod - def get_warning_str_prefix(tv: T.Union[str, mesonlib.NoProjectVersion]) -> str: + def get_warning_str_prefix(tv: MesonVersionTarget) -> str: raise InterpreterException('get_warning_str_prefix not implemented') @staticmethod - def get_notice_str_prefix(tv: T.Union[str, mesonlib.NoProjectVersion]) -> str: + def get_notice_str_prefix(tv: MesonVersionTarget) -> str: raise InterpreterException('get_notice_str_prefix not implemented') def __call__(self, f: TV_func) -> TV_func: @@ -742,7 +744,7 @@ class FeatureNew(FeatureCheckBase): feature_registry = {} @staticmethod - def check_version(target_version: T.Union[str, mesonlib.NoProjectVersion], feature_version: str) -> bool: + def check_version(target_version: MesonVersionTarget, feature_version: str) -> bool: if isinstance(target_version, str): return mesonlib.version_compare_condition_with_min(target_version, feature_version) else: @@ -751,17 +753,17 @@ def check_version(target_version: T.Union[str, mesonlib.NoProjectVersion], featu return mesonlib.version_compare(feature_version, f'<{major}.0') @staticmethod - def get_warning_str_prefix(tv: T.Union[str, mesonlib.NoProjectVersion]) -> str: + def get_warning_str_prefix(tv: MesonVersionTarget) -> str: if isinstance(tv, str): return f'Project specifies a minimum meson_version \'{tv}\' but uses features which were added in newer versions:' else: return 'Project specifies no minimum version but uses features which were added in versions:' @staticmethod - def get_notice_str_prefix(tv: T.Union[str, mesonlib.NoProjectVersion]) -> str: + def get_notice_str_prefix(tv: MesonVersionTarget) -> str: return '' - def log_usage_warning(self, tv: T.Union[str, mesonlib.NoProjectVersion], location: T.Optional['mparser.BaseNode']) -> None: + def log_usage_warning(self, tv: MesonVersionTarget, location: T.Optional['mparser.BaseNode']) -> None: if isinstance(tv, str): prefix = f'Project targets {tv!r}' else: @@ -786,7 +788,7 @@ class FeatureDeprecated(FeatureCheckBase): emit_notice = True @staticmethod - def check_version(target_version: T.Union[str, mesonlib.NoProjectVersion], feature_version: str) -> bool: + def check_version(target_version: MesonVersionTarget, feature_version: str) -> bool: if isinstance(target_version, str): # For deprecation checks we need to return the inverse of FeatureNew checks return not mesonlib.version_compare_condition_with_min(target_version, feature_version) @@ -795,14 +797,14 @@ def check_version(target_version: T.Union[str, mesonlib.NoProjectVersion], featu return False @staticmethod - def get_warning_str_prefix(tv: T.Union[str, mesonlib.NoProjectVersion]) -> str: + def get_warning_str_prefix(tv: MesonVersionTarget) -> str: return 'Deprecated features used:' @staticmethod - def get_notice_str_prefix(tv: T.Union[str, mesonlib.NoProjectVersion]) -> str: + def get_notice_str_prefix(tv: MesonVersionTarget) -> str: return 'Future-deprecated features used:' - def log_usage_warning(self, tv: T.Union[str, mesonlib.NoProjectVersion], location: T.Optional['mparser.BaseNode']) -> None: + def log_usage_warning(self, tv: MesonVersionTarget, location: T.Optional['mparser.BaseNode']) -> None: if isinstance(tv, str): prefix = f'Project targets {tv!r}' else: @@ -828,19 +830,19 @@ class FeatureBroken(FeatureCheckBase): unconditional = True @staticmethod - def check_version(target_version: T.Union[str, mesonlib.NoProjectVersion], feature_version: str) -> bool: + def check_version(target_version: MesonVersionTarget, feature_version: str) -> bool: # always warn for broken stuff return False @staticmethod - def get_warning_str_prefix(tv: T.Union[str, mesonlib.NoProjectVersion]) -> str: + def get_warning_str_prefix(tv: MesonVersionTarget) -> str: return 'Broken features used:' @staticmethod - def get_notice_str_prefix(tv: T.Union[str, mesonlib.NoProjectVersion]) -> str: + def get_notice_str_prefix(tv: MesonVersionTarget) -> str: return '' - def log_usage_warning(self, tv: T.Union[str, mesonlib.NoProjectVersion], location: T.Optional['mparser.BaseNode']) -> None: + def log_usage_warning(self, tv: MesonVersionTarget, location: T.Optional['mparser.BaseNode']) -> None: args = [ 'Project uses feature that was always broken,', 'and is now deprecated since', From 0bfffde31b21ffc9509f68c7ad7e07fd22fae052 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 20 Mar 2026 12:19:18 +0100 Subject: [PATCH 415/681] interpreter: track minimum Meson version as a Range[Version] Parse it just once, ahead of time, and pass it to version_compare_condition_with_min. This gives, for free, support for multi-argument meson.version().version_compare(). There are two changes in outputs: first, the output of version checks now canonicalizes the minimum Meson version; second, a '!=' or '<' meson_version does not count as a "minimum Meson version" in error messages (after all, they do not specify a minimum for the purpose of version_compare_condition_with_min) so a few testcases have to be adjusted. Signed-off-by: Paolo Bonzini --- mesonbuild/interpreter/interpreter.py | 2 +- mesonbuild/interpreter/primitives/string.py | 16 +++--- mesonbuild/interpreterbase/decorators.py | 16 +++--- mesonbuild/interpreterbase/interpreterbase.py | 2 +- mesonbuild/utils/universal.py | 11 +++- test cases/common/33 run program/test.json | 2 +- test cases/common/44 pkgconfig-gen/test.json | 4 +- .../common/62 string arithmetic/test.json | 2 +- .../1 version for string div/test.json | 2 +- test cases/warning/6 list add/test.json | 2 +- unittests/allplatformstests.py | 10 ++-- unittests/internaltests.py | 51 ++++++++++--------- 12 files changed, 64 insertions(+), 56 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index e58b6bfde9fa..88c93ac0f9c2 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -521,7 +521,7 @@ def process_new_values(self, invalues: list[TYPE_var | ExecutableSerialisation] def handle_meson_version(self, pv: str, location: mparser.BaseNode) -> None: if not mesonlib.version_compare(coredata.stable_version, pv): raise InterpreterException.from_node(f'Meson version is {coredata.version} but project requires {pv}', node=location) - mesonlib.project_meson_versions[self.subproject] = pv + mesonlib.project_meson_versions[self.subproject] = mesonlib.version_check_to_range([pv]) def handle_meson_version_from_ast(self) -> None: if not self.ast.lines: diff --git a/mesonbuild/interpreter/primitives/string.py b/mesonbuild/interpreter/primitives/string.py index 4092d056e565..9d6a5cc63268 100644 --- a/mesonbuild/interpreter/primitives/string.py +++ b/mesonbuild/interpreter/primitives/string.py @@ -8,7 +8,7 @@ import typing as T from ... import mlog -from ...mesonlib import version_compare_many, underscorify +from ...mesonlib import version_check_to_range, version_compare_many, underscorify from ...interpreterbase import ( InterpreterObject, MesonOperator, @@ -211,20 +211,20 @@ class MesonVersionStringHolder(StringHolder): @InterpreterObject.method('version_compare') @typed_pos_args('str.version_compare', varargs=str, min_varargs=1) def version_compare_method(self, args: T.Tuple[T.List[str]], kwargs: TYPE_kwargs) -> bool: - unsupported = [] + unsupported = False for constraint in args[0]: - if not constraint.strip().startswith('>'): - unsupported.append('non-upper-bounds (> or >=) constraints') + if constraint.strip().startswith('!'): + unsupported = True + break if len(args[0]) > 1: FeatureNew.single_use('meson.version().version_compare() with multiple arguments', '1.10.0', self.subproject, 'From 1.8.0 - 1.9.* it failed to match str.version_compare', location=self.current_node) - unsupported.append('multiple arguments') - else: - self.interpreter.tmp_meson_version = args[0][0] if unsupported: - mlog.debug('meson.version().version_compare() with', ' or '.join(unsupported), + mlog.debug('meson.version().version_compare() with != constraints', 'does not support overriding minimum meson_version checks.') + else: + self.interpreter.tmp_meson_version = version_check_to_range(args[0]) return version_compare_many(self.held_object, args[0])[0] diff --git a/mesonbuild/interpreterbase/decorators.py b/mesonbuild/interpreterbase/decorators.py index 27e33f907bb0..5ee87975f0f9 100644 --- a/mesonbuild/interpreterbase/decorators.py +++ b/mesonbuild/interpreterbase/decorators.py @@ -35,7 +35,7 @@ def __call__(s, self: _TV_IntegerObject, other: _TV_ARG1) -> TYPE_var: ... CalleeArgs: TypeAlias = T.Tuple[mparser.BaseNode, T.Optional[T.List[TYPE_var]], T.Optional[TYPE_kwargs], SubProject] - MesonVersionTarget = str | mesonlib.NoProjectVersion | None + MesonVersionTarget = mesonlib.Range[mesonlib.Version] | mesonlib.NoProjectVersion | None def is_module(obj: object) -> TypeIs[ModuleObject]: @@ -745,7 +745,7 @@ class FeatureNew(FeatureCheckBase): @staticmethod def check_version(target_version: MesonVersionTarget, feature_version: str) -> bool: - if isinstance(target_version, str): + if isinstance(target_version, mesonlib.Range): return mesonlib.version_compare_condition_with_min(target_version, feature_version) else: # Warn for anything newer than the current semver base slot. @@ -754,7 +754,7 @@ def check_version(target_version: MesonVersionTarget, feature_version: str) -> b @staticmethod def get_warning_str_prefix(tv: MesonVersionTarget) -> str: - if isinstance(tv, str): + if isinstance(tv, mesonlib.Range) and tv.min is not None: return f'Project specifies a minimum meson_version \'{tv}\' but uses features which were added in newer versions:' else: return 'Project specifies no minimum version but uses features which were added in versions:' @@ -764,8 +764,8 @@ def get_notice_str_prefix(tv: MesonVersionTarget) -> str: return '' def log_usage_warning(self, tv: MesonVersionTarget, location: T.Optional['mparser.BaseNode']) -> None: - if isinstance(tv, str): - prefix = f'Project targets {tv!r}' + if isinstance(tv, mesonlib.Range) and tv.min is not None: + prefix = f"Project targets '{tv}'" else: prefix = 'Project does not target a minimum version' args = [ @@ -789,7 +789,7 @@ class FeatureDeprecated(FeatureCheckBase): @staticmethod def check_version(target_version: MesonVersionTarget, feature_version: str) -> bool: - if isinstance(target_version, str): + if isinstance(target_version, mesonlib.Range): # For deprecation checks we need to return the inverse of FeatureNew checks return not mesonlib.version_compare_condition_with_min(target_version, feature_version) else: @@ -805,8 +805,8 @@ def get_notice_str_prefix(tv: MesonVersionTarget) -> str: return 'Future-deprecated features used:' def log_usage_warning(self, tv: MesonVersionTarget, location: T.Optional['mparser.BaseNode']) -> None: - if isinstance(tv, str): - prefix = f'Project targets {tv!r}' + if isinstance(tv, mesonlib.Range): + prefix = f"Project targets '{tv}'" else: prefix = 'Project does not target a minimum version' args = [ diff --git a/mesonbuild/interpreterbase/interpreterbase.py b/mesonbuild/interpreterbase/interpreterbase.py index 293c6b5fdebe..e8cc43d26841 100644 --- a/mesonbuild/interpreterbase/interpreterbase.py +++ b/mesonbuild/interpreterbase/interpreterbase.py @@ -96,7 +96,7 @@ def __init__(self, source_root: str, subdir: str, subproject: SubProject, subpro # meson.version().compare_version(version_string) # If it was part of a if-clause, it is used to temporally override the # current meson version target within that if-block. - self.tmp_meson_version: T.Optional[str] = None + self.tmp_meson_version: T.Optional[mesonlib.Range[mesonlib.Version]] = None def handle_meson_version_from_ast(self) -> None: # do nothing in an AST interpreter diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index a7193c7b63d7..a7c12f78ab23 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -176,6 +176,7 @@ def __gt__(self, other: Self) -> bool: ... 'typeslistify', 'unique_list', 'verbose_git', + 'version_check_to_range', 'version_compare', 'version_compare_condition_with_min', 'version_compare_many', @@ -195,7 +196,7 @@ class NoProjectVersion: # TODO: this is such a hack, this really should be either in coredata or in the # interpreter # {subproject: project_meson_version} -project_meson_versions: T.Dict[str, T.Union[str, NoProjectVersion]] = {} +project_meson_versions: T.Dict[str, T.Union[Range[Version], NoProjectVersion]] = {} from glob import glob @@ -1110,7 +1111,13 @@ def version_check_to_range(checks: T.List[str], start: Range[Version] = Range()) elif op is operator.eq: r = Range(min=Version(v), max=Version(v), min_eq=True, max_eq=True) elif op is operator.ne: - continue # cop out + v_ = Version(v) + # Do the best that we can, remove the extrema + r = Range() + if v_ == start.min: + r = Range(min=v_, min_eq=False) + if v_ == start.max: + r = r.intersect(Range(max=v_, max_eq=False)) start = start.intersect(r) return start diff --git a/test cases/common/33 run program/test.json b/test cases/common/33 run program/test.json index da8f128bf463..fd31c2fc08fa 100644 --- a/test cases/common/33 run program/test.json +++ b/test cases/common/33 run program/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/common/33 run program/meson.build:1: WARNING: Project targets '>=0.1.0' but uses feature introduced in '0.47.0': check arg in run_command.", + "line": "test cases/common/33 run program/meson.build:1: WARNING: Project targets '>= 0.1.0' but uses feature introduced in '0.47.0': check arg in run_command.", "comment": "This triggers on line 1 -- the line with the project() function" } ] diff --git a/test cases/common/44 pkgconfig-gen/test.json b/test cases/common/44 pkgconfig-gen/test.json index 875715fd3c09..e1b91b3c68e5 100644 --- a/test cases/common/44 pkgconfig-gen/test.json +++ b/test cases/common/44 pkgconfig-gen/test.json @@ -24,10 +24,10 @@ ], "stdout": [ { - "line": "test cases/common/44 pkgconfig-gen/meson.build:161: WARNING: Project targets '>=0.60.0' but uses feature introduced in '0.62.0': pkgconfig.generate implicit variable for builtin directories." + "line": "test cases/common/44 pkgconfig-gen/meson.build:161: WARNING: Project targets '>= 0.60.0' but uses feature introduced in '0.62.0': pkgconfig.generate implicit variable for builtin directories." }, { - "line": "test cases/common/44 pkgconfig-gen/meson.build:171: WARNING: Project targets '>=0.60.0' but uses feature introduced in '0.62.0': pkgconfig.generate implicit variable for builtin directories.", + "line": "test cases/common/44 pkgconfig-gen/meson.build:171: WARNING: Project targets '>= 0.60.0' but uses feature introduced in '0.62.0': pkgconfig.generate implicit variable for builtin directories.", "count": 0 }, { diff --git a/test cases/common/62 string arithmetic/test.json b/test cases/common/62 string arithmetic/test.json index 5a11650d6275..02b76ebf0a9a 100644 --- a/test cases/common/62 string arithmetic/test.json +++ b/test cases/common/62 string arithmetic/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/common/62 string arithmetic/meson.build:25: WARNING: Project targets '>=0.62.0' but uses feature introduced in '0.63.0': multiline format strings." + "line": "test cases/common/62 string arithmetic/meson.build:25: WARNING: Project targets '>= 0.62.0' but uses feature introduced in '0.63.0': multiline format strings." } ] } diff --git a/test cases/warning/1 version for string div/test.json b/test cases/warning/1 version for string div/test.json index e8acb698a97b..7fac8bc5a8c7 100644 --- a/test cases/warning/1 version for string div/test.json +++ b/test cases/warning/1 version for string div/test.json @@ -2,7 +2,7 @@ "stdout": [ { "comment": "literal '/' appears in output, irrespective of os.path.sep, as that's the operator", - "line": "test cases/warning/1 version for string div/meson.build:3: WARNING: Project targets '>=0.48.0' but uses feature introduced in '0.49.0': / with string arguments." + "line": "test cases/warning/1 version for string div/meson.build:3: WARNING: Project targets '>= 0.48.0' but uses feature introduced in '0.49.0': / with string arguments." } ] } diff --git a/test cases/warning/6 list add/test.json b/test cases/warning/6 list add/test.json index e3ce8d0053a2..2c4af191b0ac 100644 --- a/test cases/warning/6 list add/test.json +++ b/test cases/warning/6 list add/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/warning/6 list add/meson.build:4: WARNING: Project targets '>=0.59.0' but uses feature introduced in '0.60.0': list.. The right hand operand was not a list." + "line": "test cases/warning/6 list add/meson.build:4: WARNING: Project targets '>= 0.59.0' but uses feature introduced in '0.60.0': list.. The right hand operand was not a list." } ] } diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index cd9c918791e1..ebab9e3ca88c 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -2964,16 +2964,16 @@ def test_feature_check_usage_subprojects(self): testdir = os.path.join(self.unit_test_dir, '40 featurenew subprojects') out = self.init(testdir) # Parent project warns correctly - self.assertRegex(out, "WARNING: Project targets '>=0.45'.*'0.47.0': dict") + self.assertRegex(out, "WARNING: Project targets '>= 0.45'.*'0.47.0': dict") # Subprojects warn correctly - self.assertRegex(out, r"foo\| .*WARNING: Project targets '>=0.40'.*'0.44.0': disabler") - self.assertRegex(out, r"baz\| .*WARNING: Project targets '!=0.40'.*'0.44.0': disabler") + self.assertRegex(out, r"foo\| .*WARNING: Project targets '>= 0.40'.*'0.44.0': disabler") + self.assertRegex(out, r"baz\| .*WARNING: Project does not target a minimum version.*'0.44.0': disabler") # Subproject has a new-enough meson_version, no warning self.assertNotRegex(out, "WARNING: Project targets.*Python") # Ensure a summary is printed in the subproject and the outer project - self.assertRegex(out, r"\| WARNING: Project specifies a minimum meson_version '>=0.40'") + self.assertRegex(out, r"\| WARNING: Project specifies a minimum meson_version '>= 0.40'") self.assertRegex(out, r"\| \* 0.44.0: {'disabler'}") - self.assertRegex(out, "WARNING: Project specifies a minimum meson_version '>=0.45'") + self.assertRegex(out, "WARNING: Project specifies a minimum meson_version '>= 0.45'") self.assertRegex(out, " * 0.47.0: {'dict'}") def test_configure_file_warnings(self): diff --git a/unittests/internaltests.py b/unittests/internaltests.py index af71cbf54af2..d1ebb4af5ced 100644 --- a/unittests/internaltests.py +++ b/unittests/internaltests.py @@ -39,6 +39,7 @@ from mesonbuild.mesonlib import ( LibType, MachineChoice, PerMachine, SimpleABC, Version, is_windows, is_osx, is_cygwin, is_openbsd, search_version, MesonException, python_command, + version_check_to_range, ) from mesonbuild.options import OptionKey from mesonbuild.interpreter.type_checking import in_set_validator, NoneType @@ -1463,7 +1464,7 @@ def _(obj, node, args: T.Tuple, kwargs: T.Dict[str, str]) -> None: with self.subTest('use before available'), \ mock.patch('sys.stdout', io.StringIO()) as out, \ - mock.patch('mesonbuild.mesonlib.project_meson_versions', {'': '0.1'}): + mock.patch('mesonbuild.mesonlib.project_meson_versions', {'': version_check_to_range(['>=0.1'])}): # With Meson 0.1 it should trigger the "introduced" warning but not the "deprecated" warning _(None, mock.Mock(subproject=''), [], {'input': 'foo'}) self.assertRegex(out.getvalue(), r'WARNING:.*introduced.*input arg in testfunc. It\'s awesome, use it') @@ -1471,14 +1472,14 @@ def _(obj, node, args: T.Tuple, kwargs: T.Dict[str, str]) -> None: with self.subTest('no warnings should be triggered'), \ mock.patch('sys.stdout', io.StringIO()) as out, \ - mock.patch('mesonbuild.mesonlib.project_meson_versions', {'': '1.5'}): + mock.patch('mesonbuild.mesonlib.project_meson_versions', {'': version_check_to_range(['>=1.5'])}): # With Meson 1.5 it shouldn't trigger any warning _(None, mock.Mock(subproject=''), [], {'input': 'foo'}) self.assertNotRegex(out.getvalue(), r'WARNING:.*') with self.subTest('use after deprecated'), \ mock.patch('sys.stdout', io.StringIO()) as out, \ - mock.patch('mesonbuild.mesonlib.project_meson_versions', {'': '2.0'}): + mock.patch('mesonbuild.mesonlib.project_meson_versions', {'': version_check_to_range(['>=2.0'])}): # With Meson 2.0 it should trigger the "deprecated" warning but not the "introduced" warning _(None, mock.Mock(subproject=''), [], {'input': 'foo'}) self.assertRegex(out.getvalue(), r'WARNING:.*deprecated.*input arg in testfunc. It\'s terrible, don\'t use it') @@ -1509,7 +1510,7 @@ def _(obj, node, args: T.Tuple, kwargs: T.Dict[str, MachineChoice]) -> None: _(None, mock.Mock(), tuple(), dict(native=True)) - @mock.patch('mesonbuild.mesonlib.project_meson_versions', {'': '1.0'}) + @mock.patch('mesonbuild.mesonlib.project_meson_versions', {'': version_check_to_range(['>=1.0'])}) def test_typed_kwarg_since_values(self) -> None: @typed_kwargs( 'testfunc', @@ -1537,84 +1538,84 @@ def _(obj, node, args: T.Tuple, kwargs: T.Dict[str, str]) -> None: with self.subTest('deprecated array string value'), mock.patch('sys.stdout', io.StringIO()) as out: _(None, mock.Mock(subproject=''), [], {'input': ['foo']}) - self.assertRegex(out.getvalue(), r"""WARNING:.Project targets '1.0'.*deprecated since '0.9': "testfunc" keyword argument "input" value "foo".*""") + self.assertRegex(out.getvalue(), r"""WARNING:.Project targets '>= 1.0'.*deprecated since '0.9': "testfunc" keyword argument "input" value "foo".*""") with self.subTest('new array string value'), mock.patch('sys.stdout', io.StringIO()) as out: _(None, mock.Mock(subproject=''), [], {'input': ['bar']}) - self.assertRegex(out.getvalue(), r"""WARNING:.Project targets '1.0'.*introduced in '1.1': "testfunc" keyword argument "input" value "bar".*""") + self.assertRegex(out.getvalue(), r"""WARNING:.Project targets '>= 1.0'.*introduced in '1.1': "testfunc" keyword argument "input" value "bar".*""") with self.subTest('deprecated dict string value'), mock.patch('sys.stdout', io.StringIO()) as out: _(None, mock.Mock(subproject=''), [], {'output': {'foo': 'a'}}) - self.assertRegex(out.getvalue(), r"""WARNING:.Project targets '1.0'.*deprecated since '0.9': "testfunc" keyword argument "output" value "foo".*""") + self.assertRegex(out.getvalue(), r"""WARNING:.Project targets '>= 1.0'.*deprecated since '0.9': "testfunc" keyword argument "output" value "foo".*""") with self.subTest('deprecated dict string value with msg'), mock.patch('sys.stdout', io.StringIO()) as out: _(None, mock.Mock(subproject=''), [], {'output': {'foo2': 'a'}}) - self.assertRegex(out.getvalue(), r"""WARNING:.Project targets '1.0'.*deprecated since '0.9': "testfunc" keyword argument "output" value "foo2" in dict keys. don't use it.*""") + self.assertRegex(out.getvalue(), r"""WARNING:.Project targets '>= 1.0'.*deprecated since '0.9': "testfunc" keyword argument "output" value "foo2" in dict keys. don't use it.*""") with self.subTest('new dict string value'), mock.patch('sys.stdout', io.StringIO()) as out: _(None, mock.Mock(subproject=''), [], {'output': {'bar': 'b'}}) - self.assertRegex(out.getvalue(), r"""WARNING:.Project targets '1.0'.*introduced in '1.1': "testfunc" keyword argument "output" value "bar".*""") + self.assertRegex(out.getvalue(), r"""WARNING:.Project targets '>= 1.0'.*introduced in '1.1': "testfunc" keyword argument "output" value "bar".*""") with self.subTest('new dict string value with msg'), mock.patch('sys.stdout', io.StringIO()) as out: _(None, mock.Mock(subproject=''), [], {'output': {'bar2': 'a'}}) - self.assertRegex(out.getvalue(), r"""WARNING:.Project targets '1.0'.*introduced in '1.1': "testfunc" keyword argument "output" value "bar2" in dict keys. use this.*""") + self.assertRegex(out.getvalue(), r"""WARNING:.Project targets '>= 1.0'.*introduced in '1.1': "testfunc" keyword argument "output" value "bar2" in dict keys. use this.*""") with self.subTest('new string type'), mock.patch('sys.stdout', io.StringIO()) as out: _(None, mock.Mock(subproject=''), [], {'foo': 'foo'}) - self.assertRegex(out.getvalue(), r"""WARNING: Project targets '1.0'.*introduced in '1.1': "testfunc" keyword argument "foo" of type str.*""") + self.assertRegex(out.getvalue(), r"""WARNING: Project targets '>= 1.0'.*introduced in '1.1': "testfunc" keyword argument "foo" of type str.*""") with self.subTest('new array of string type'), mock.patch('sys.stdout', io.StringIO()) as out: _(None, mock.Mock(subproject=''), [], {'foo': ['foo']}) - self.assertRegex(out.getvalue(), r"""WARNING: Project targets '1.0'.*introduced in '1.2': "testfunc" keyword argument "foo" of type array\[str\].*""") + self.assertRegex(out.getvalue(), r"""WARNING: Project targets '>= 1.0'.*introduced in '1.2': "testfunc" keyword argument "foo" of type array\[str\].*""") with self.subTest('new dict of string type'), mock.patch('sys.stdout', io.StringIO()) as out: _(None, mock.Mock(subproject=''), [], {'foo': {'plop': 'foo'}}) - self.assertRegex(out.getvalue(), r"""WARNING: Project targets '1.0'.*introduced in '1.3': "testfunc" keyword argument "foo" of type dict\[str\].*""") + self.assertRegex(out.getvalue(), r"""WARNING: Project targets '>= 1.0'.*introduced in '1.3': "testfunc" keyword argument "foo" of type dict\[str\].*""") with self.subTest('deprecated int value'), mock.patch('sys.stdout', io.StringIO()) as out: _(None, mock.Mock(subproject=''), [], {'foo': 1}) - self.assertRegex(out.getvalue(), r"""WARNING:.Project targets '1.0'.*deprecated since '0.8': "testfunc" keyword argument "foo" of type int.*""") + self.assertRegex(out.getvalue(), r"""WARNING:.Project targets '>= 1.0'.*deprecated since '0.8': "testfunc" keyword argument "foo" of type int.*""") with self.subTest('deprecated array int value'), mock.patch('sys.stdout', io.StringIO()) as out: _(None, mock.Mock(subproject=''), [], {'foo': [1]}) - self.assertRegex(out.getvalue(), r"""WARNING:.Project targets '1.0'.*deprecated since '0.9': "testfunc" keyword argument "foo" of type array\[int\].*""") + self.assertRegex(out.getvalue(), r"""WARNING:.Project targets '>= 1.0'.*deprecated since '0.9': "testfunc" keyword argument "foo" of type array\[int\].*""") with self.subTest('new list[str] value'), mock.patch('sys.stdout', io.StringIO()) as out: _(None, mock.Mock(subproject=''), [], {'tuple': ['foo', 42]}) - self.assertRegex(out.getvalue(), r"""WARNING: Project targets '1.0'.*introduced in '1.1': "testfunc" keyword argument "tuple" of type array\[str\].*""") - self.assertRegex(out.getvalue(), r"""WARNING: Project targets '1.0'.*introduced in '1.2': "testfunc" keyword argument "tuple" of type array\[int\].*""") + self.assertRegex(out.getvalue(), r"""WARNING: Project targets '>= 1.0'.*introduced in '1.1': "testfunc" keyword argument "tuple" of type array\[str\].*""") + self.assertRegex(out.getvalue(), r"""WARNING: Project targets '>= 1.0'.*introduced in '1.2': "testfunc" keyword argument "tuple" of type array\[int\].*""") with self.subTest('deprecated array string value'), mock.patch('sys.stdout', io.StringIO()) as out: _(None, mock.Mock(subproject=''), [], {'input': 'foo'}) - self.assertRegex(out.getvalue(), r"""WARNING:.Project targets '1.0'.*deprecated since '0.9': "testfunc" keyword argument "input" value "foo".*""") + self.assertRegex(out.getvalue(), r"""WARNING:.Project targets '>= 1.0'.*deprecated since '0.9': "testfunc" keyword argument "input" value "foo".*""") with self.subTest('new array string value'), mock.patch('sys.stdout', io.StringIO()) as out: _(None, mock.Mock(subproject=''), [], {'input': 'bar'}) - self.assertRegex(out.getvalue(), r"""WARNING:.Project targets '1.0'.*introduced in '1.1': "testfunc" keyword argument "input" value "bar".*""") + self.assertRegex(out.getvalue(), r"""WARNING:.Project targets '>= 1.0'.*introduced in '1.1': "testfunc" keyword argument "input" value "bar".*""") with self.subTest('non string union'), mock.patch('sys.stdout', io.StringIO()) as out: _(None, mock.Mock(subproject=''), [], {'install_dir': False}) - self.assertRegex(out.getvalue(), r"""WARNING:.Project targets '1.0'.*deprecated since '0.9': "testfunc" keyword argument "install_dir" value "False".*""") + self.assertRegex(out.getvalue(), r"""WARNING:.Project targets '>= 1.0'.*deprecated since '0.9': "testfunc" keyword argument "install_dir" value "False".*""") with self.subTest('deprecated string union'), mock.patch('sys.stdout', io.StringIO()) as out: _(None, mock.Mock(subproject=''), [], {'mode': 'deprecated'}) - self.assertRegex(out.getvalue(), r"""WARNING:.Project targets '1.0'.*deprecated since '1.0': "testfunc" keyword argument "mode" value "deprecated".*""") + self.assertRegex(out.getvalue(), r"""WARNING:.Project targets '>= 1.0'.*deprecated since '1.0': "testfunc" keyword argument "mode" value "deprecated".*""") with self.subTest('new string union'), mock.patch('sys.stdout', io.StringIO()) as out: _(None, mock.Mock(subproject=''), [], {'mode': 'since'}) - self.assertRegex(out.getvalue(), r"""WARNING:.Project targets '1.0'.*introduced in '1.1': "testfunc" keyword argument "mode" value "since".*""") + self.assertRegex(out.getvalue(), r"""WARNING:.Project targets '>= 1.0'.*introduced in '1.1': "testfunc" keyword argument "mode" value "since".*""") with self.subTest('new container'), mock.patch('sys.stdout', io.StringIO()) as out: _(None, mock.Mock(subproject=''), [], {'dict': ['a=b']}) - self.assertRegex(out.getvalue(), r"""WARNING:.Project targets '1.0'.*introduced in '1.9': "testfunc" keyword argument "dict" of type list.*""") + self.assertRegex(out.getvalue(), r"""WARNING:.Project targets '>= 1.0'.*introduced in '1.9': "testfunc" keyword argument "dict" of type list.*""") with self.subTest('new container set to default'), mock.patch('sys.stdout', io.StringIO()) as out: _(None, mock.Mock(subproject=''), [], {'new_dict': {}}) - self.assertRegex(out.getvalue(), r"""WARNING:.Project targets '1.0'.*introduced in '1.1': "testfunc" keyword argument "new_dict" of type dict.*""") + self.assertRegex(out.getvalue(), r"""WARNING:.Project targets '>= 1.0'.*introduced in '1.1': "testfunc" keyword argument "new_dict" of type dict.*""") with self.subTest('new container default'), mock.patch('sys.stdout', io.StringIO()) as out: _(None, mock.Mock(subproject=''), [], {}) - self.assertNotRegex(out.getvalue(), r"""WARNING:.Project targets '1.0'.*introduced in '1.1': "testfunc" keyword argument "new_dict" of type dict.*""") + self.assertNotRegex(out.getvalue(), r"""WARNING:.Project targets '>= 1.0'.*introduced in '1.1': "testfunc" keyword argument "new_dict" of type dict.*""") def test_typed_kwarg_evolve(self) -> None: k = KwargInfo('foo', str, required=True, default='foo') From b108299627763d40289486b49c843eda5df4513a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 20 Mar 2026 12:26:12 +0100 Subject: [PATCH 416/681] interpreterbase: warn on redundant Meson version checks We might encounter a Meson version check that always evaluates to true or to false: project('t', 'c', meson_version: '>=0.60.0') if meson.version().version_compare('>=0.55.0') v = 1 endif if meson.version().version_compare('<0.60.0') v = 2 endif Print warnings in such cases. Co-authored-by: Benjamin Gilbert Signed-off-by: Paolo Bonzini --- mesonbuild/interpreterbase/interpreterbase.py | 20 ++++++----- mesonbuild/utils/universal.py | 12 +++++++ .../295 redundant version check/meson.build | 33 +++++++++++++++++ .../295 redundant version check/test.json | 19 ++++++++++ unittests/versiontests.py | 36 +++++++++++++++++++ 5 files changed, 112 insertions(+), 8 deletions(-) create mode 100644 test cases/common/295 redundant version check/meson.build create mode 100644 test cases/common/295 redundant version check/test.json diff --git a/mesonbuild/interpreterbase/interpreterbase.py b/mesonbuild/interpreterbase/interpreterbase.py index e8cc43d26841..015fe9caaa52 100644 --- a/mesonbuild/interpreterbase/interpreterbase.py +++ b/mesonbuild/interpreterbase/interpreterbase.py @@ -311,15 +311,19 @@ def evaluate_if(self, node: mparser.IfClauseNode) -> T.Optional[Disabler]: res = result.operator_call(MesonOperator.BOOL, None) if not isinstance(res, bool): raise InvalidCode(f'If clause {result!r} does not evaluate to true or false.') - if res: - prev_meson_version = mesonlib.project_meson_versions[self.subproject] - if self.tmp_meson_version: - mesonlib.project_meson_versions[self.subproject] = self.tmp_meson_version - try: + prev_meson_version = mesonlib.project_meson_versions[self.subproject] + if self.tmp_meson_version and isinstance(prev_meson_version, mesonlib.Range): + always = prev_meson_version.always(self.tmp_meson_version) + if always is not None: + mlog.warning(f"Conditional on version '{self.tmp_meson_version}' always evaluates to {str(always).lower()}", + location=self.current_node) + mesonlib.project_meson_versions[self.subproject] = prev_meson_version.intersect(self.tmp_meson_version) + try: + if res: self.evaluate_codeblock(i.block) - finally: - mesonlib.project_meson_versions[self.subproject] = prev_meson_version - return None + return None + finally: + mesonlib.project_meson_versions[self.subproject] = prev_meson_version if not isinstance(node.elseblock, mparser.EmptyNode): self.evaluate_codeblock(node.elseblock.block) return None diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index a7c12f78ab23..7b5f0a9a63e4 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -1095,6 +1095,18 @@ def intersect(self, x: Range[_V]) -> Range[_V]: result.__post_init__() return result + def always(self, inner: Range[_V]) -> T.Optional[bool]: + """Check if inner is always true or always false given self. + + Returns True if inner is always satisfied by any value in self, + False if no value in self satisfies inner, None if indeterminate.""" + narrowed = self.intersect(inner) + if narrowed.is_empty: + return False + if narrowed == self: + return True + return None + # note that Range is immutable, so no need to have Range() | None def version_check_to_range(checks: T.List[str], start: Range[Version] = Range()) -> Range[Version]: diff --git a/test cases/common/295 redundant version check/meson.build b/test cases/common/295 redundant version check/meson.build new file mode 100644 index 000000000000..e9a768c37cc3 --- /dev/null +++ b/test cases/common/295 redundant version check/meson.build @@ -0,0 +1,33 @@ +# written by Benjamin Gilbert +project('t', 'c', meson_version: '>=0.60.0') +if meson.version().version_compare('>=0.55.0') + v = 1 +endif +if meson.version().version_compare('<0.60.0') + v = 2 +endif +# narrowing: <0.60 is indeterminate, but >=0.40 is always true within >=0.50, <0.60 +if meson.version().version_compare('<99.0') + if meson.version().version_compare('>=0.40') + v = 3 + endif +endif + +# <= vs <, should not warn +if meson.version().version_compare('<=99.0') + if meson.version().version_compare('<99.0') + v = 4 + endif +endif + +# hide warnings about multiple args to version_compare() +if meson.version().version_compare('>1.10.0') + if meson.version().version_compare('<99.0') + if meson.version().version_compare('<99.0', '>=0.40') + v = 5 + endif + if meson.version().version_compare('>=0.40', '<99.0') + v = 6 + endif + endif +endif diff --git a/test cases/common/295 redundant version check/test.json b/test cases/common/295 redundant version check/test.json new file mode 100644 index 000000000000..2d95f693b3c1 --- /dev/null +++ b/test cases/common/295 redundant version check/test.json @@ -0,0 +1,19 @@ +{ + "stdout": [ + { + "line": "test cases/common/295 redundant version check/meson.build:3: WARNING: Conditional on version '>= 0.55.0' always evaluates to true" + }, + { + "line": "test cases/common/295 redundant version check/meson.build:6: WARNING: Conditional on version '< 0.60.0' always evaluates to false" + }, + { + "line": "test cases/common/295 redundant version check/meson.build:11: WARNING: Conditional on version '>= 0.40' always evaluates to true" + }, + { + "line": "test cases/common/295 redundant version check/meson.build:26: WARNING: Conditional on version '>= 0.40, < 99.0' always evaluates to true" + }, + { + "line": "test cases/common/295 redundant version check/meson.build:29: WARNING: Conditional on version '>= 0.40, < 99.0' always evaluates to true" + } + ] +} diff --git a/unittests/versiontests.py b/unittests/versiontests.py index 2652ef3597d3..820f90b6abdf 100644 --- a/unittests/versiontests.py +++ b/unittests/versiontests.py @@ -292,6 +292,42 @@ def test_range_intersect(self): r = a.intersect(b) self.assertTrue(r.is_empty) + def test_range_always(self): + """Range.always checks if inner is always true/false given outer.""" + outer = Range(min=5, min_eq=True, max=10, max_eq=True) + unbounded = Range() + empty = Range(min=7, max=3, min_eq=True, max_eq=True) + + # inner fully contains outer => true + self.assertTrue(outer.always(Range(min=3, min_eq=True))) + self.assertTrue(outer.always(Range(max=12, max_eq=True))) + self.assertTrue(outer.always(unbounded)) + + # inner same as outer => true + self.assertTrue(outer.always(Range(min=5, min_eq=True, max=10, max_eq=True))) + + # inner doesn't overlap outer => false + self.assertFalse(outer.always(Range(min=11, min_eq=True))) + self.assertFalse(outer.always(Range(max=4, max_eq=True))) + + # inner partially overlaps => indeterminate + self.assertIsNone(outer.always(Range(min=7, min_eq=True))) + self.assertIsNone(outer.always(Range(max=8, max_eq=True))) + + # exclusive boundaries count as a partial overlap + self.assertIsNone(outer.always(Range(min=5, min_eq=False))) + self.assertIsNone(outer.always(Range(max=10, max_eq=False))) + + # outer is unbounded + self.assertIsNone(unbounded.always(Range(min=5, min_eq=True))) + self.assertTrue(unbounded.always(unbounded)) + self.assertFalse(unbounded.always(empty)) + + # outer is empty => always false (intersection is empty) + self.assertFalse(empty.always(Range(min=5, min_eq=True))) + self.assertFalse(empty.always(unbounded)) + self.assertFalse(empty.always(empty)) + def test_range_str(self): """Test Range.__str__.""" self.assertEqual(str(Range()), '(any)') From 6cdb8a28b70a5c67457d1c8e25c33817c14313aa Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 1 Jun 2026 10:17:12 -0700 Subject: [PATCH 417/681] backend/xcode: fix object_filename_from_source signature There is still some serious missing work here, but at least the code could function --- mesonbuild/backend/xcodebackend.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index 80d8ac5d7605..c30c6ab7a910 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -288,7 +288,8 @@ def get_custom_target_output_dir(self, target: build.AnyTargetType) -> str: os.makedirs(os.path.join(self.environment.get_build_dir(), dirname), exist_ok=True) return dirname - def object_filename_from_source(self, target: build.BuildTarget, source: mesonlib.FileOrString, targetdir: T.Optional[str] = None) -> str: + def object_filename_from_source(self, target: build.BuildTarget, compiler: Compiler, + source: mesonlib.FileOrString, targetdir: T.Optional[str] = None) -> str: # Xcode has the following naming scheme: # projectname.build/debug/prog@exe.build/Objects-normal/x86_64/func.o project = self.build.project_name From 528bf15522f30923f10e12c078f109e919c987bb Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 1 Jun 2026 10:20:21 -0700 Subject: [PATCH 418/681] backend/xcode: fix generate_target_files_maps_impl typing --- mesonbuild/backend/xcodebackend.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index c30c6ab7a910..895171c7d12f 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -309,7 +309,7 @@ def determine_swift_dep_dirs(self, target: build.BuildTarget) -> T.List[str]: result.append(os.path.join(self.environment.get_build_dir(), self.get_target_dir(l))) return result - def set_arch(self): + def set_arch(self) -> None: self.arch = self.build.environment.machines.host.cpu if self.arch == 'aarch64': self.arch = 'arm64' @@ -596,7 +596,9 @@ def generate_target_file_maps(self) -> None: self.generate_target_file_maps_impl(self.build_targets) self.generate_target_file_maps_impl(self.custom_targets) - def generate_target_file_maps_impl(self, targets) -> None: + def generate_target_file_maps_impl( + self, targets: T.Mapping[str, build.BuildTarget | build.CustomTarget] + ) -> None: for tname, t in targets.items(): for s in t.sources: if not isinstance(s, mesonlib.File): @@ -607,7 +609,7 @@ def generate_target_file_maps_impl(self, targets) -> None: self.buildfile_ids[k] = self.gen_id() assert k not in self.fileref_ids self.fileref_ids[k] = self.gen_id() - if not hasattr(t, 'objects'): + if isinstance(t, build.CustomTarget): continue for o in t.objects: if isinstance(o, build.ExtractedObjects): From b0a1e34b2fb162a3d1bf4c0095e4375699f99451 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 1 Jun 2026 10:23:37 -0700 Subject: [PATCH 419/681] backend/xcode: Use isinstance for checking for AppleFramework dependencies This allows type checkers to actually understand what's going on. --- mesonbuild/backend/xcodebackend.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index 895171c7d12f..4c9574966eab 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -12,6 +12,7 @@ from .. import mesonlib from .. import mlog from ..arglist import CompilerArgs +from ..dependencies.platform import AppleFrameworks from ..mesonlib import MesonBugException, MesonException from ..options import OptionKey @@ -557,7 +558,7 @@ def generate_native_frameworks_map(self) -> None: self.native_frameworks_fileref = {} for t in self.build_targets.values(): for dep in t.get_external_deps(): - if dep.name == 'appleframeworks' and dep.found(): + if dep.found() and isinstance(dep, AppleFrameworks): for f in dep.frameworks: self.native_frameworks[f] = self.gen_id() self.native_frameworks_fileref[f] = self.gen_id() @@ -723,7 +724,7 @@ def generate_pbx_aggregate_target(self, objects_dict: PbxDict) -> None: def generate_pbx_build_file(self, objects_dict: PbxDict) -> None: for tname, t in self.build_targets.items(): for dep in t.get_external_deps(): - if dep.name == 'appleframeworks' and dep.found(): + if dep.found() and isinstance(dep, AppleFrameworks): for f in dep.frameworks: fw_dict = PbxDict() fwkey = self.native_frameworks[f] @@ -876,7 +877,7 @@ def generate_pbx_container_item_proxy(self, objects_dict: PbxDict) -> None: def generate_pbx_file_reference(self, objects_dict: PbxDict) -> None: for tname, t in self.build_targets.items(): for dep in t.get_external_deps(): - if dep.name == 'appleframeworks' and dep.found(): + if dep.found() and isinstance(dep, AppleFrameworks): for f in dep.frameworks: fw_dict = PbxDict() framework_fileref = self.native_frameworks_fileref[f] @@ -1045,7 +1046,7 @@ def generate_pbx_frameworks_buildphase(self, objects_dict: PbxDict) -> None: file_list = PbxArray() bt_dict.add_item('files', file_list) for dep in t.get_external_deps(): - if dep.name == 'appleframeworks' and dep.found(): + if dep.found() and isinstance(dep, AppleFrameworks): for f in dep.frameworks: file_list.add_item(self.native_frameworks[f], f'{f}.framework in Frameworks') bt_dict.add_item('runOnlyForDeploymentPostprocessing', 0) @@ -1111,7 +1112,7 @@ def generate_pbx_group(self, objects_dict: PbxDict) -> None: for t in self.build_targets.values(): for dep in t.get_external_deps(): - if dep.name == 'appleframeworks' and dep.found(): + if dep.found() and isinstance(dep, AppleFrameworks): for f in dep.frameworks: frameworks_children.add_item(self.native_frameworks_fileref[f], f) From 0933aee7e5bf4403a5c7cf1a9eff60dcebecc9ae Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 1 Jun 2026 10:25:48 -0700 Subject: [PATCH 420/681] backends: Fix get_target_private_dir type It can be AnyTargetType not just BuildTargetTypes --- mesonbuild/backend/backends.py | 2 +- mesonbuild/backend/vs2010backend.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index a19ad39bba44..bee2f2652617 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -411,7 +411,7 @@ def get_target_source_dir(self, target: build.Target) -> str: return os.path.join(self.build_to_src, target_dir) return self.build_to_src - def get_target_private_dir(self, target: build.BuildTargetTypes) -> str: + def get_target_private_dir(self, target: build.AnyTargetType) -> str: return os.path.join(self.get_target_filename(target, warn_multi_output=False) + '.p') def get_target_private_dir_abs(self, target: build.BuildTargetTypes) -> str: diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 1d72b0eace02..ceedf6d7a9c9 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -160,7 +160,7 @@ def __init__(self, build: T.Optional[build.Build], gen_lite: bool = False): def detect_toolset(self) -> None: pass - def get_target_private_dir(self, target: build.BuildTargetTypes) -> str: + def get_target_private_dir(self, target: build.AnyTargetType) -> str: return os.path.join(self.get_target_dir(target), target.get_id()) def generate_genlist_for_target( From 2e197143ba374fb0bd5e0f79a24f964386f9ed96 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 1 Jun 2026 10:27:21 -0700 Subject: [PATCH 421/681] backend/xcode: Add annotations to write_group_target_entry --- mesonbuild/backend/xcodebackend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index 4c9574966eab..8cc92fe112b6 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -1155,7 +1155,7 @@ def generate_pbx_group(self, objects_dict: PbxDict) -> None: product_dict.add_item('name', 'Products') product_dict.add_item('sourceTree', '') - def write_group_target_entry(self, objects_dict: PbxDict, t) -> str: + def write_group_target_entry(self, objects_dict: PbxDict, t: build.BuildTarget) -> str: tid = t.get_id() group_id = self.gen_id() target_dict = PbxDict() From 331ddc3b0c9c58e2d6ffa87d408672f9a5f72118 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 1 Jun 2026 10:30:54 -0700 Subject: [PATCH 422/681] backend/xcode: stop attaching attributes to Targets This is a code smell. Instead of adding attributes to the target dynamically, just store that information in the Backend itself. --- mesonbuild/backend/xcodebackend.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index 8cc92fe112b6..c7534e3ae27f 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -268,6 +268,8 @@ def __init__(self, build: T.Optional[build.Build]): # that is used in two targets gets a total of four unique ID numbers. self.fileref_ids = {} + self.buildphase_map: dict[str, dict[str, str]] = {} + def write_pbxfile(self, top_level_dict: PbxDict, ofilename: str) -> None: tmpname = ofilename + '.tmp' with open(tmpname, 'w', encoding='utf-8') as ofile: @@ -450,12 +452,14 @@ def generate_buildstylemap(self) -> None: def generate_build_phase_map(self) -> None: for tname, t in self.build_targets.items(): # generate id for our own target-name - t.buildphasemap = {} - t.buildphasemap[tname] = self.gen_id() - # each target can have its own Frameworks/Sources/..., generate id's for those - t.buildphasemap['Frameworks'] = self.gen_id() - t.buildphasemap['Resources'] = self.gen_id() - t.buildphasemap['Sources'] = self.gen_id() + self.buildphase_map[t.id] = { + tname: self.gen_id(), + # each target can have its own Frameworks/Sources/..., generate + # id's for those + 'Frameworks': self.gen_id(), + 'Resources': self.gen_id(), + 'Sources': self.gen_id(), + } def generate_build_configuration_map(self) -> None: self.buildconfmap = {} @@ -1040,7 +1044,7 @@ def generate_pbx_file_reference(self, objects_dict: PbxDict) -> None: def generate_pbx_frameworks_buildphase(self, objects_dict: PbxDict) -> None: for t in self.build_targets.values(): bt_dict = PbxDict() - objects_dict.add_item(t.buildphasemap['Frameworks'], bt_dict, 'Frameworks') + objects_dict.add_item(self.buildphase_map[t.id]['Frameworks'], bt_dict, 'Frameworks') bt_dict.add_item('isa', 'PBXFrameworksBuildPhase') bt_dict.add_item('buildActionMask', 2147483647) file_list = PbxArray() @@ -1273,7 +1277,7 @@ def generate_pbx_native_target(self, objects_dict: PbxDict) -> None: if isinstance(g, build.GeneratedList): buildphases_array.add_item(self.shell_targets[(tname, generator_id)], f'Generator {generator_id}/{tname}') generator_id += 1 - for bpname, bpval in t.buildphasemap.items(): + for bpname, bpval in self.buildphase_map[t.id].items(): buildphases_array.add_item(bpval, f'{bpname} yyy') build_rules = PbxArray() for language, build_rule_idval in self.build_rules[tname].items(): @@ -1504,7 +1508,7 @@ def generate_pbx_sources_build_phase(self, objects_dict: PbxDict) -> None: for name in self.source_phase: phase_dict = PbxDict() t = self.build_targets[name] - objects_dict.add_item(t.buildphasemap[name], phase_dict, 'Sources') + objects_dict.add_item(self.buildphase_map[t.id][name], phase_dict, 'Sources') phase_dict.add_item('isa', 'PBXSourcesBuildPhase') phase_dict.add_item('buildActionMask', 2147483647) file_arr = PbxArray() From dfd07398a004ffceb703025f1e6400e13a94d3b7 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 1 Jun 2026 10:37:56 -0700 Subject: [PATCH 423/681] backend/xcode: fix some low hanging issues --- mesonbuild/backend/xcodebackend.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index c7534e3ae27f..8170223ca608 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -138,6 +138,8 @@ def write(self, ofile: T.TextIO, indent_level: int) -> None: ofile.write(f'\n{self.text}\n') class PbxDictItem: + value: PbxArray | PbxDict | str | int + def __init__(self, key: str, value: T.Union[PbxArray, PbxDict, str, int], comment: str = ''): self.key = key if isinstance(value, str): @@ -694,6 +696,9 @@ def generate_pbx_aggregate_target(self, objects_dict: PbxDict) -> None: if isinstance(s, build.GeneratedList): build_phases.append(self.shell_targets[(tname, generator_id)]) for d in s.depends: + # TODO: what to do about this + if isinstance(d, build.GeneratedList): + continue dependencies.append(self.pbx_custom_dep_map[d.get_id()]) generator_id += 1 elif isinstance(s, build.ExtractedObjects): @@ -1194,8 +1199,9 @@ def write_group_target_entry(self, objects_dict: PbxDict, t: build.BuildTarget) continue if isinstance(o, mesonlib.File): o = os.path.join(o.subdir, o.fname) - else: + elif isinstance(o, str): o = os.path.join(t.subdir, o) + # TODO: handle CustomTarget, CustomTargeIndex, and GeneratedList target_children.add_item(self.fileref_ids[(tid, o)], o) for e in t.extra_files: if isinstance(e, mesonlib.File): @@ -1544,7 +1550,7 @@ def generate_pbx_target_dependency(self, objects_dict: PbxDict) -> None: objects_dict.add_item(self.build_all_tdep_id, all_dict, 'ALL_BUILD') all_dict.add_item('isa', 'PBXTargetDependency') all_dict.add_item('target', self.all_id) - targets = [] + targets: list[tuple[str, str, str, str | None]] = [] targets.append((self.regen_dependency_id, self.regen_id, 'REGEN', None)) for t in self.build_targets: idval = self.pbx_dep_map[t] # VERIFY: is this correct? @@ -1725,7 +1731,7 @@ def generate_single_build_target(self, objects_dict: PbxDict, target_name: str, else: raise RuntimeError(o) if isinstance(target, build.SharedModule): - ldargs += linker.get_std_shared_module_link_args(target.get_options()) + ldargs += linker.get_std_shared_module_link_args(target) elif isinstance(target, build.SharedLibrary): ldargs += linker.get_std_shared_lib_link_args() ldstr = ' '.join(ldargs) From 385e722af6315a6457ab503150c00de062287077 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 1 Jun 2026 10:39:38 -0700 Subject: [PATCH 424/681] backend/xcode: add assertions to help mypy with options --- mesonbuild/backend/xcodebackend.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index 8170223ca608..b14edfe76fbf 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -1744,7 +1744,9 @@ def generate_single_build_target(self, objects_dict: PbxDict, target_name: str, if compiler is None: continue # Start with warning args - warn_args = compiler.get_warn_args(self.get_target_option(target, 'warning_level')) + _warn_level = self.get_target_option(target, 'warning_level') + assert isinstance(_warn_level, str), 'for mypy' + warn_args = compiler.get_warn_args(_warn_level) std_args = compiler.get_option_compile_args(target, target.subproject) std_args += compiler.get_option_std_args(target, target.subproject) # Add compile args added using add_project_arguments() @@ -1795,9 +1797,13 @@ def generate_single_build_target(self, objects_dict: PbxDict, target_name: str, if target.suffix: suffix = '.' + target.suffix settings_dict.add_item('EXECUTABLE_SUFFIX', suffix) - settings_dict.add_item('GCC_GENERATE_DEBUGGING_SYMBOLS', BOOL2XCODEBOOL[self.get_target_option(target, 'debug')]) + debug = self.get_target_option(target, 'debug') + assert isinstance(debug, bool), 'for mypy' + settings_dict.add_item('GCC_GENERATE_DEBUGGING_SYMBOLS', BOOL2XCODEBOOL[debug]) settings_dict.add_item('GCC_INLINES_ARE_PRIVATE_EXTERN', 'NO') - opt_flag = OPT2XCODEOPT[self.get_target_option(target, 'optimization')] + opt = self.get_target_option(target, 'optimization') + assert isinstance(opt, str), 'for mypy' + opt_flag = OPT2XCODEOPT[opt] if opt_flag is not None: settings_dict.add_item('GCC_OPTIMIZATION_LEVEL', opt_flag) if target.has_pch: From 529b923aab75ef1a17e4765684449da90f0ac0ed Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 1 Jun 2026 10:42:10 -0700 Subject: [PATCH 425/681] backend/xcode: Add annotations to .generator_outputs --- mesonbuild/backend/xcodebackend.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index b14edfe76fbf..0a35b66b5860 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -256,7 +256,7 @@ def __init__(self, build: T.Optional[build.Build]): self.regen_buildconf_id = self.gen_id() self.regen_dependency_id = self.gen_id() self.top_level_dict = PbxDict() - self.generator_outputs = {} + self.generator_outputs: dict[tuple[str, int], list[str]] = {} self.set_arch() self.xcodeversion, self.objversion = autodetect_xcode_version() # In Xcode files are not accessed via their file names, but rather every one of them @@ -544,15 +544,15 @@ def gen_single_target_map(self, genlist: build.GeneratedList, tname: str, if genlist.extra_depends: raise NotImplementedError('XCode backend does not support depends for generator.process') self.shell_targets[k] = self.gen_id() - ofile_abs = [] + ofile_abs: list[str] = [] for i in genlist.get_inputs(): for o_base in genlist.get_outputs_for(i): o = os.path.join(self.get_target_private_dir(t), o_base) ofile_abs.append(os.path.join(self.environment.get_build_dir(), o)) assert k not in self.generator_outputs self.generator_outputs[k] = ofile_abs - buildfile_ids = [] - fileref_ids = [] + buildfile_ids: list[str] = [] + fileref_ids: list[str] = [] for i in range(len(ofile_abs)): buildfile_ids.append(self.gen_id()) fileref_ids.append(self.gen_id()) From 5d36c7e95e9b3d8fc4623ca6196a0f5ea2b189c9 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 1 Jun 2026 10:45:07 -0700 Subject: [PATCH 426/681] backend/xcode: add and fix annotations around buildfile_ids --- mesonbuild/backend/xcodebackend.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index 0a35b66b5860..9a2fc98e5f48 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -264,7 +264,7 @@ def __init__(self, build: T.Optional[build.Build]): # in. If you generate only one id per file and use them, compilation will work but the # UI will only show the file in one target but not the others. Thus they key is # a tuple containing the target and filename. - self.buildfile_ids = {} + self.buildfile_ids: dict[tuple[str, str] | str, str] = {} # That is not enough, though. Each target/file combination also gets a unique id # in the file reference section. Because why not. This means that a source file # that is used in two targets gets a total of four unique ID numbers. @@ -696,9 +696,9 @@ def generate_pbx_aggregate_target(self, objects_dict: PbxDict) -> None: if isinstance(s, build.GeneratedList): build_phases.append(self.shell_targets[(tname, generator_id)]) for d in s.depends: - # TODO: what to do about this if isinstance(d, build.GeneratedList): - continue + # TODO: what to do about this + raise MesonBugException('Not implemented, patches highly welcome') dependencies.append(self.pbx_custom_dep_map[d.get_id()]) generator_id += 1 elif isinstance(s, build.ExtractedObjects): @@ -769,6 +769,9 @@ def generate_pbx_build_file(self, objects_dict: PbxDict) -> None: o = os.path.join(o.subdir, o.fname) elif isinstance(o, str): o = os.path.join(t.subdir, o) + else: + # TODO: handle CustomTarget | CustomTargetIndex | GeneratedList + raise MesonBugException('Not implemented, patches highly welcome') idval = self.buildfile_ids[(tname, o)] k = (tname, o) fileref = self.fileref_ids[k] @@ -1201,7 +1204,9 @@ def write_group_target_entry(self, objects_dict: PbxDict, t: build.BuildTarget) o = os.path.join(o.subdir, o.fname) elif isinstance(o, str): o = os.path.join(t.subdir, o) - # TODO: handle CustomTarget, CustomTargeIndex, and GeneratedList + else: + # TODO: handle CustomTarget, CustomTargeIndex, and GeneratedList + raise MesonBugException('Not implemented, patches highly welcome') target_children.add_item(self.fileref_ids[(tid, o)], o) for e in t.extra_files: if isinstance(e, mesonlib.File): From 354ea43b790f9a93b633add418c2a96a697fbd03 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 1 Jun 2026 10:47:49 -0700 Subject: [PATCH 427/681] backend/xcode: add annotation to fileref_ids --- mesonbuild/backend/xcodebackend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index 9a2fc98e5f48..71f58c14de3d 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -268,7 +268,7 @@ def __init__(self, build: T.Optional[build.Build]): # That is not enough, though. Each target/file combination also gets a unique id # in the file reference section. Because why not. This means that a source file # that is used in two targets gets a total of four unique ID numbers. - self.fileref_ids = {} + self.fileref_ids: dict[tuple[str, str] | str, str] = {} self.buildphase_map: dict[str, dict[str, str]] = {} From 16b2634f7981557101c2f4f4164ec99fb1928402 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 1 Jun 2026 10:49:17 -0700 Subject: [PATCH 428/681] backend/xcode: add annotations to filemap, foldermap, and target_filemap --- mesonbuild/backend/xcodebackend.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index 71f58c14de3d..d03441b842bf 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -415,9 +415,9 @@ def get_xcodetype(self, fname: str) -> str: return xcodetype def generate_filemap(self) -> None: - self.filemap = {} # Key is source file relative to src root. - self.foldermap = {} - self.target_filemap = {} + self.filemap: dict[str, str] = {} # Key is source file relative to src root. + self.foldermap: dict[tuple[str, build.BuildTarget], str] = {} + self.target_filemap: dict[str, str] = {} for name, t in self.build_targets.items(): for s in t.sources: if isinstance(s, mesonlib.File): From a59b8ce5da23d6a05e462a313f0a79ac737b45ed Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 1 Jun 2026 10:58:34 -0700 Subject: [PATCH 429/681] backend/xcode: Add more missing annotations --- mesonbuild/backend/xcodebackend.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index d03441b842bf..8e942d8093ef 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -519,8 +519,8 @@ def generate_custom_target_map(self) -> None: def generate_generator_target_map(self) -> None: # Generator objects do not have natural unique ids # so use a counter. - self.generator_fileref_ids = {} - self.generator_buildfile_ids = {} + self.generator_fileref_ids: dict[tuple[str, int], list[str]] = {} + self.generator_buildfile_ids: dict[tuple[str, int], list[str]] = {} for tname, t in self.build_targets.items(): generator_id = 0 for genlist in t.generated: @@ -665,7 +665,7 @@ def generate_pbx_aggregate_target(self, objects_dict: PbxDict) -> None: custom_target_dependencies.append(self.pbx_custom_dep_map[t.get_id()]) elif isinstance(t, build.BuildTarget): target_dependencies.append(self.pbx_dep_map[t.get_id()]) - aggregated_targets = [] + aggregated_targets: list[tuple[str, str, str, list[str], list[str]]] = [] aggregated_targets.append((self.all_id, 'ALL_BUILD', self.all_buildconf_id, From 3909a2aae245691634a6c8c538005ba3cefe7362 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 1 Jun 2026 11:00:42 -0700 Subject: [PATCH 430/681] backend/xcode: fix more unhandled types issues --- mesonbuild/backend/xcodebackend.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index 8e942d8093ef..8db21f43ac4b 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -956,9 +956,12 @@ def generate_pbx_file_reference(self, objects_dict: PbxDict) -> None: if isinstance(o, mesonlib.File): fullpath = o.absolute_path(self.environment.get_source_dir(), self.environment.get_build_dir()) o = os.path.join(o.subdir, o.fname) - else: + elif isinstance(o, str): o = os.path.join(t.subdir, o) fullpath = os.path.join(self.environment.get_source_dir(), o) + else: + # TODO: handle CustomTarget, CustomTargeIndex, and GeneratedList + raise MesonBugException('Not implemented, patches highly welcome') idval = self.fileref_ids[(tname, o)] rel_name = mesonlib.relpath(fullpath, self.environment.get_source_dir()) o_dict = PbxDict() @@ -1696,6 +1699,8 @@ def generate_single_build_target(self, objects_dict: PbxDict, target_name: str, ldargs += target.link_args # Swift is special. Again. You can't mix Swift with other languages # in the same target. Thus for Swift we only use + # TODO: is this still true? + stdlib_args: list[str] if is_swift: linker, stdlib_args = target.compilers['swift'], [] else: From 3df28d8a933e14213a96eb166775b559fb70f948 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 1 Jun 2026 11:06:47 -0700 Subject: [PATCH 431/681] backend/xcode: Add helper to get PbxDict item values This uses a TypeVar to allow type checking in the getter, which avoids the need to add a large number of asserts, and to allow better chaining. --- mesonbuild/backend/xcodebackend.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index 8db21f43ac4b..5f1851a2ad1d 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -20,6 +20,8 @@ from ..build import BuildTarget from ..compilers import Compiler + PbxDictItemTypes = T.TypeVar('PbxDictItemTypes', 'PbxArray', 'PbxDict', str, int) + INDENT = '\t' XCODETYPEMAP = {'c': 'sourcecode.c.c', 'a': 'archive.ar', @@ -166,6 +168,7 @@ def quote_value(self, value: str) -> str: return quoted + class PbxDict: def __init__(self) -> None: # This class is a bit weird, because we want to write PBX dicts in @@ -188,6 +191,12 @@ def get_item(self, key: str) -> PbxDictItem: return item return None + def get_item_value(self, key: str, type_: type[PbxDictItemTypes]) -> PbxDictItemTypes: + i = self.get_item(key).value + if not isinstance(i, type_): + raise MesonBugException(f'Attempted to get the wrong type for key {key}, expected {type_} but got {type(i)}') + return i + def has_item(self, key: str) -> bool: return key in self.keys @@ -1188,7 +1197,7 @@ def write_group_target_entry(self, objects_dict: PbxDict, t: build.BuildTarget) # If the file is in a folder, add it to the group representing that folder. if '/' in s.fname: folder = '/'.join(s.fname.split('/')[:-1]) - folder_dict = objects_dict.get_item(self.foldermap[(folder, t)]).value.get_item('children').value + folder_dict = objects_dict.get_item_value(self.foldermap[(folder, t)], PbxDict).get_item_value('children', PbxDict) temp = os.path.join(s.subdir, s.fname) folder_dict.add_item(self.fileref_ids[(tid, temp)], temp) if self.foldermap[(folder, t)] in folder_ids: From 08b48575b5216900ecf15f948b993bbddd74981a Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 1 Jun 2026 11:09:41 -0700 Subject: [PATCH 432/681] backend/xcode: fix remaining mypy issues --- mesonbuild/backend/xcodebackend.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index 5f1851a2ad1d..85158f61b498 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -14,6 +14,7 @@ from ..arglist import CompilerArgs from ..dependencies.platform import AppleFrameworks from ..mesonlib import MesonBugException, MesonException +from ..linkers import StaticLinker from ..options import OptionKey if T.TYPE_CHECKING: @@ -1714,7 +1715,7 @@ def generate_single_build_target(self, objects_dict: PbxDict, target_name: str, linker, stdlib_args = target.compilers['swift'], [] else: linker, stdlib_args = self.determine_linker_and_stdlib_args(target) - if not isinstance(target, build.StaticLibrary): + if not isinstance(linker, StaticLinker): ldargs += linker.get_build_link_args(target, self.build) cargs = [] for dep in target.get_external_deps(): @@ -1750,8 +1751,10 @@ def generate_single_build_target(self, objects_dict: PbxDict, target_name: str, else: raise RuntimeError(o) if isinstance(target, build.SharedModule): + assert not isinstance(linker, StaticLinker), 'for mypy' ldargs += linker.get_std_shared_module_link_args(target) elif isinstance(target, build.SharedLibrary): + assert not isinstance(linker, StaticLinker), 'for mypy' ldargs += linker.get_std_shared_lib_link_args() ldstr = ' '.join(ldargs) valid = self.buildconfmap[target_name][buildtype] From 600d84ebc202c9d3b61423cd8cce4b6d1fe1ef51 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 1 Jun 2026 11:13:27 -0700 Subject: [PATCH 433/681] backend/xcode: Use immutable type annotations for constants This means that mypy will yell if someone tries to mutate one of these values. --- mesonbuild/backend/xcodebackend.py | 116 ++++++++++++++++------------- 1 file changed, 64 insertions(+), 52 deletions(-) diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index 85158f61b498..3621e0cdbba8 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -24,58 +24,70 @@ PbxDictItemTypes = T.TypeVar('PbxDictItemTypes', 'PbxArray', 'PbxDict', str, int) INDENT = '\t' -XCODETYPEMAP = {'c': 'sourcecode.c.c', - 'a': 'archive.ar', - 'cc': 'sourcecode.cpp.cpp', - 'cxx': 'sourcecode.cpp.cpp', - 'cpp': 'sourcecode.cpp.cpp', - 'c++': 'sourcecode.cpp.cpp', - 'm': 'sourcecode.c.objc', - 'mm': 'sourcecode.cpp.objcpp', - 'h': 'sourcecode.c.h', - 'hpp': 'sourcecode.cpp.h', - 'hxx': 'sourcecode.cpp.h', - 'hh': 'sourcecode.cpp.hh', - 'inc': 'sourcecode.c.h', - 'swift': 'sourcecode.swift', - 'dylib': 'compiled.mach-o.dylib', - 'o': 'compiled.mach-o.objfile', - 's': 'sourcecode.asm', - 'asm': 'sourcecode.nasm', - 'metal': 'sourcecode.metal', - 'glsl': 'sourcecode.glsl', - } -NEEDS_CUSTOM_RULES = {'nasm': 'sourcecode.nasm', - } -LANGNAMEMAP = {'c': 'C', - 'cpp': 'CPLUSPLUS', - 'objc': 'OBJC', - 'objcpp': 'OBJCPLUSPLUS', - 'swift': 'SWIFT_' - } -OPT2XCODEOPT = {'plain': None, - '0': '0', - 'g': '0', - '1': '1', - '2': '2', - '3': '3', - 's': 's', - } -BOOL2XCODEBOOL = {True: 'YES', False: 'NO'} -LINKABLE_EXTENSIONS = {'.o', '.a', '.obj', '.so', '.dylib'} -XCODEVERSIONS = {'1500': ('Xcode 15.0', 60), - '1400': ('Xcode 14.0', 56), - '1300': ('Xcode 13.0', 55), - '1200': ('Xcode 12.0', 54), - '1140': ('Xcode 11.4', 53), - '1100': ('Xcode 11.0', 52), - '1000': ('Xcode 10.0', 51), - '930': ('Xcode 9.3', 50), - '800': ('Xcode 8.0', 48), - '630': ('Xcode 6.3', 47), - '320': ('Xcode 3.2', 46), - '310': ('Xcode 3.1', 45) - } + +XCODETYPEMAP: T.Mapping[str, str] = { + 'c': 'sourcecode.c.c', + 'a': 'archive.ar', + 'cc': 'sourcecode.cpp.cpp', + 'cxx': 'sourcecode.cpp.cpp', + 'cpp': 'sourcecode.cpp.cpp', + 'c++': 'sourcecode.cpp.cpp', + 'm': 'sourcecode.c.objc', + 'mm': 'sourcecode.cpp.objcpp', + 'h': 'sourcecode.c.h', + 'hpp': 'sourcecode.cpp.h', + 'hxx': 'sourcecode.cpp.h', + 'hh': 'sourcecode.cpp.hh', + 'inc': 'sourcecode.c.h', + 'swift': 'sourcecode.swift', + 'dylib': 'compiled.mach-o.dylib', + 'o': 'compiled.mach-o.objfile', + 's': 'sourcecode.asm', + 'asm': 'sourcecode.nasm', + 'metal': 'sourcecode.metal', + 'glsl': 'sourcecode.glsl', +} + +NEEDS_CUSTOM_RULES: T.Mapping[str, str] = { + 'nasm': 'sourcecode.nasm', +} + +LANGNAMEMAP: T.Mapping[str, str] = { + 'c': 'C', + 'cpp': 'CPLUSPLUS', + 'objc': 'OBJC', + 'objcpp': 'OBJCPLUSPLUS', + 'swift': 'SWIFT_' +} + +OPT2XCODEOPT: T.Mapping[str, str | None] = { + 'plain': None, + '0': '0', + 'g': '0', + '1': '1', + '2': '2', + '3': '3', + 's': 's', +} + +BOOL2XCODEBOOL: T.Mapping[bool, str] = {True: 'YES', False: 'NO'} + +LINKABLE_EXTENSIONS: T.AbstractSet[str] = {'.o', '.a', '.obj', '.so', '.dylib'} + +XCODEVERSIONS: T.Mapping[str, tuple[str, int]] = { + '1500': ('Xcode 15.0', 60), + '1400': ('Xcode 14.0', 56), + '1300': ('Xcode 13.0', 55), + '1200': ('Xcode 12.0', 54), + '1140': ('Xcode 11.4', 53), + '1100': ('Xcode 11.0', 52), + '1000': ('Xcode 10.0', 51), + '930': ('Xcode 9.3', 50), + '800': ('Xcode 8.0', 48), + '630': ('Xcode 6.3', 47), + '320': ('Xcode 3.2', 46), + '310': ('Xcode 3.1', 45) +} def autodetect_xcode_version() -> T.Tuple[str, int]: try: From 70e70590878380365dda3c517c5b06ddd3d856d6 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 1 Jun 2026 11:14:12 -0700 Subject: [PATCH 434/681] Mark all backends as fully type safe --- run_mypy.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/run_mypy.py b/run_mypy.py index 37d79578190c..7943c2b620a7 100755 --- a/run_mypy.py +++ b/run_mypy.py @@ -15,6 +15,7 @@ modules = [ # fully typed submodules 'mesonbuild/ast/', + 'mesonbuild/backend', 'mesonbuild/cargo/', 'mesonbuild/cmake/', 'mesonbuild/compilers/', @@ -31,17 +32,6 @@ # specific files 'mesonbuild/arglist.py', 'mesonbuild/build.py', - 'mesonbuild/backend/backends.py', - 'mesonbuild/backend/ninjabackend.py', - 'mesonbuild/backend/nonebackend.py', - 'mesonbuild/backend/vs2010backend.py', - 'mesonbuild/backend/vs2012backend.py', - 'mesonbuild/backend/vs2013backend.py', - 'mesonbuild/backend/vs2015backend.py', - 'mesonbuild/backend/vs2017backend.py', - 'mesonbuild/backend/vs2019backend.py', - 'mesonbuild/backend/vs2022backend.py', - 'mesonbuild/backend/vs2026backend.py', 'mesonbuild/cmdline.py', 'mesonbuild/coredata.py', 'mesonbuild/depfile.py', From aa3e9523c7f02f9be9ba1e643d75800dc2e6aa89 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Tue, 2 Jun 2026 16:18:53 -0500 Subject: [PATCH 435/681] Update brew command to check for installed libomp only --- mesonbuild/compilers/mixins/apple.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/compilers/mixins/apple.py b/mesonbuild/compilers/mixins/apple.py index b4a5599a3325..374b4e860274 100644 --- a/mesonbuild/compilers/mixins/apple.py +++ b/mesonbuild/compilers/mixins/apple.py @@ -16,7 +16,7 @@ def _get_libomp_prefix() -> T.Optional[str]: """Call `brew --prefix libomp` once and cache it. Returns None if unavailable.""" try: return subprocess.run( - ['brew', '--prefix', 'libomp'], + ['brew', '--prefix', '--installed', 'libomp'], capture_output=True, encoding='utf-8', check=True, From 5754c9b4d9a2c0a77ae34095fd639470862c89e8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 17 Dec 2025 11:35:09 +0100 Subject: [PATCH 436/681] add typing for munstable_coredata.py --- mesonbuild/munstable_coredata.py | 28 +++++++++++++++++++++------- run_mypy.py | 1 + 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/mesonbuild/munstable_coredata.py b/mesonbuild/munstable_coredata.py index b647dd8fcfb4..4eb87f9ee7bb 100644 --- a/mesonbuild/munstable_coredata.py +++ b/mesonbuild/munstable_coredata.py @@ -3,25 +3,37 @@ from __future__ import annotations - from . import coredata as cdata from .mesonlib import MachineChoice from .options import OptionKey +import typing as T import os.path import pprint import textwrap +if T.TYPE_CHECKING: + import argparse + + from .import dependencies + from .compilers.compilers import CompilerDict + from .dependencies.detect import TV_DepID + + class Arguments(T.Protocol): + + builddir: str + all: bool + # Note: when adding arguments, please also add them to the completion # scripts in $MESONSRC/data/shell-completions/ -def add_arguments(parser): +def add_arguments(parser: argparse.ArgumentParser) -> None: parser.add_argument('--all', action='store_true', dest='all', default=False, help='Show data not used by current backend.') parser.add_argument('builddir', nargs='?', default='.', help='The build directory') -def dump_compilers(compilers): +def dump_compilers(compilers: CompilerDict) -> None: for lang, compiler in compilers.items(): print(' ' + lang + ':') print(' Id: ' + compiler.id) @@ -32,12 +44,12 @@ def dump_compilers(compilers): print(' Detected version: ' + compiler.version) -def dump_guids(d): +def dump_guids(d: dict[str, str]) -> None: for name, value in d.items(): print(' ' + name + ': ' + value) -def run(options): +def run(options: Arguments) -> int: datadir = 'meson-private' if options.builddir is not None: datadir = os.path.join(options.builddir, datadir) @@ -54,6 +66,7 @@ def run(options): coredata = cdata.load(options.builddir) backend = coredata.optstore.get_value_for(OptionKey('backend')) + assert isinstance(backend, str), 'for mypy' for k, v in sorted(coredata.__dict__.items()): if k in {'backend_options', 'base_options', 'builtins', 'compiler_options', 'user_options'}: # use `meson configure` to view these @@ -88,8 +101,8 @@ def run(options): for_machine.get_lower_case_name())) dump_compilers(v[for_machine]) elif k == 'deps': - def print_dep(dep_key, dep): - print(' ' + dep_key[0][1] + ": ") + def print_dep(dep_key: TV_DepID, dep: dependencies.Dependency) -> None: + print(' ' + str(dep_key[0][1]) + ": ") print(' compile args: ' + repr(dep.get_compile_args())) print(' link args: ' + repr(dep.get_link_args())) if dep.get_sources(): @@ -106,3 +119,4 @@ def print_dep(dep_key, dep): else: print(k + ':') print(textwrap.indent(pprint.pformat(v), ' ')) + return 0 diff --git a/run_mypy.py b/run_mypy.py index 7943c2b620a7..5a438c8b1b4b 100755 --- a/run_mypy.py +++ b/run_mypy.py @@ -53,6 +53,7 @@ 'mesonbuild/mparser.py', 'mesonbuild/msetup.py', 'mesonbuild/mtest.py', + 'mesonbuild/munstable_coredata.py', 'mesonbuild/optinterpreter.py', 'mesonbuild/options.py', 'mesonbuild/programs.py', From 2c63b423f2a4ff59fb4c6120a3717d64b4ab3ba9 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 3 Jun 2026 09:29:55 -0700 Subject: [PATCH 437/681] pathlib: Use WindowsPath directly instead of messing with Path for subclassing The code I came up with works with mypy for versions of Python >= 3.12, but for 3.11 and 3.10 it still complains that _BASE_CLASS isn't a static type. The problem is that in those versions `pathlib.Path` is a factory instead of a type. But we can side step that because this hack exists for Windows and only, and just directly inherit the WindowsPath --- mesonbuild/_pathlib.py | 20 +++++++++----------- run_mypy.py | 1 + 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/mesonbuild/_pathlib.py b/mesonbuild/_pathlib.py index ce8db37615ce..61bbedb0c1ee 100644 --- a/mesonbuild/_pathlib.py +++ b/mesonbuild/_pathlib.py @@ -23,7 +23,6 @@ import pathlib import os import platform -import sys __all__ = [ 'PurePath', @@ -37,16 +36,15 @@ PureWindowsPath = pathlib.PureWindowsPath # Only patch on platforms where the bug occurs -if platform.system().lower() in {'windows'}: +if platform.system().lower() == 'windows': # Can not directly inherit from pathlib.Path because the __new__ - # operator of pathlib.Path() returns a {Posix,Windows}Path object. - # Until Python 3.12, when this was changed so that Path can be directly inherited. - if sys.version_info >= (3, 12): - _BASE_CLASS = pathlib.Path - else: - _BASE_CLASS = type(pathlib.Path()) - - class _Path(_BASE_CLASS): + # operator of pathlib.Path() returns a {Posix,Windows}Path object + # until Python 3.12, when this was changed so that Path can be directly + # inherited. + # + # Since this particular code path is only relevant to Windows, just inherit + # WindowsPath directly. + class _Path(pathlib.WindowsPath): def resolve(self, strict: bool = False) -> _Path: ''' Work around a resolve bug on certain Windows systems: @@ -60,7 +58,7 @@ def resolve(self, strict: bool = False) -> _Path: except OSError: return _Path(os.path.normpath(self)) - Path: type[_BASE_CLASS] = _Path + Path: type[pathlib.Path] = _Path else: Path = pathlib.Path PosixPath = pathlib.PosixPath diff --git a/run_mypy.py b/run_mypy.py index 5a438c8b1b4b..bbc3fdcfab87 100755 --- a/run_mypy.py +++ b/run_mypy.py @@ -30,6 +30,7 @@ 'mesonbuild/wrap/', # specific files + 'mesonbuild/_pathlib.py', 'mesonbuild/arglist.py', 'mesonbuild/build.py', 'mesonbuild/cmdline.py', From ad432c509524e56da4fe77252e6566c5b926d477 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 4 Jun 2026 08:12:42 +0200 Subject: [PATCH 438/681] simplify run_mypy.py Drop the manual list of modules, and just add the mesonbuild/ directory. This adds __init__.py, _pathlib.py and mesonlib.py, which were previously not included. _pathlib.py needs workarounds for Python versions up to 3.11. Signed-off-by: Paolo Bonzini --- run_mypy.py | 57 ++++------------------------------------------------- 1 file changed, 4 insertions(+), 53 deletions(-) diff --git a/run_mypy.py b/run_mypy.py index bbc3fdcfab87..30cf2b41b074 100755 --- a/run_mypy.py +++ b/run_mypy.py @@ -12,55 +12,8 @@ from mesonbuild.mesonlib import version_compare -modules = [ - # fully typed submodules - 'mesonbuild/ast/', - 'mesonbuild/backend', - 'mesonbuild/cargo/', - 'mesonbuild/cmake/', - 'mesonbuild/compilers/', - 'mesonbuild/dependencies/', - 'mesonbuild/interpreter/', - 'mesonbuild/interpreterbase/', - 'mesonbuild/linkers/', - 'mesonbuild/modules/', - 'mesonbuild/scripts/', - 'mesonbuild/templates/', - 'mesonbuild/utils/', - 'mesonbuild/wrap/', - - # specific files - 'mesonbuild/_pathlib.py', - 'mesonbuild/arglist.py', - 'mesonbuild/build.py', - 'mesonbuild/cmdline.py', - 'mesonbuild/coredata.py', - 'mesonbuild/depfile.py', - 'mesonbuild/envconfig.py', - 'mesonbuild/environment.py', - 'mesonbuild/machinefile.py', - 'mesonbuild/mesondata.py', - 'mesonbuild/mesonmain.py', - 'mesonbuild/mcompile.py', - 'mesonbuild/mdevenv.py', - 'mesonbuild/mconf.py', - 'mesonbuild/mdist.py', - 'mesonbuild/mformat.py', - 'mesonbuild/minit.py', - 'mesonbuild/minstall.py', - 'mesonbuild/mintro.py', - 'mesonbuild/mlog.py', - 'mesonbuild/msubprojects.py', - 'mesonbuild/mparser.py', - 'mesonbuild/msetup.py', - 'mesonbuild/mtest.py', - 'mesonbuild/munstable_coredata.py', - 'mesonbuild/optinterpreter.py', - 'mesonbuild/options.py', - 'mesonbuild/programs.py', - 'mesonbuild/rewriter.py', - 'mesonbuild/tooldetect.py', -] +MESONBUILD = 'mesonbuild/' + additional = [ 'run_mypy.py', 'run_project_tests.py', @@ -107,9 +60,7 @@ def main() -> int: additional_to_check = [] # type: T.List[str] if opts.files: for f in opts.files: - if f in modules: - to_check.append(f) - elif any(f.startswith(i) for i in modules): + if f.startswith(MESONBUILD): to_check.append(f) elif f in additional: additional_to_check.append(f) @@ -119,7 +70,7 @@ def main() -> int: if not opts.quiet: print(f'skipping {f!r} because it is not yet typed') else: - to_check.extend(modules) + to_check.append(MESONBUILD) additional_to_check.extend(additional) if not to_check: From 44c3b9ccf0fda804061645fefbb1e93e2a7d8427 Mon Sep 17 00:00:00 2001 From: Hunter Kvalevog Date: Wed, 3 Jun 2026 19:25:12 -0500 Subject: [PATCH 439/681] cmake: fix typo in documentation --- docs/markdown/CMake-module.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/markdown/CMake-module.md b/docs/markdown/CMake-module.md index 982fa35d086e..e20fdbc83c5d 100644 --- a/docs/markdown/CMake-module.md +++ b/docs/markdown/CMake-module.md @@ -187,7 +187,7 @@ from CMake will be used. Meson will try to automatically guess most of the required CMake toolchain variables from existing entries in the cross and native -files. These variables will be stored in an automatically generate +files. These variables will be stored in an automatically generated CMake toolchain file in the build directory. The remaining variables that can't be guessed can be added by the user in the `[cmake]` cross/native file section (*new in 0.56.0*). From 4f527bb6abe0d359e756095fe99ad3dde9b612a7 Mon Sep 17 00:00:00 2001 From: "L. E. Segovia" Date: Mon, 1 Jun 2026 22:28:35 -0300 Subject: [PATCH 440/681] Revert "nasm: use the correct flag for response file support" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Upon further testing with a more complex project, I realised that Nasm wants newlines and does NOT accept quotes+spaces for splitting arguments. So for proper escaping this must be done with a custom generator à la GNOME module, with a CustomTarget. This reverts commit 173d7605f266c5e8019dafea1159b4700ef56e5c. --- mesonbuild/backend/ninjabackend.py | 2 -- mesonbuild/compilers/asm.py | 5 ----- mesonbuild/linkers/base.py | 1 - 3 files changed, 8 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 8ab7f324399b..87b74cc27ee0 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -254,8 +254,6 @@ def rule_iter() -> T.Iterable[str]: if rsp == '_RSP': if self.rspfile_quote_style is RSPFileSyntax.TASKING: outfile.write(' command = {} --option-file=$out.rsp\n'.format(' '.join([self._quoter(x) for x in self.command]))) - elif self.rspfile_quote_style is RSPFileSyntax.NASM: - outfile.write(' command = {} -@$out.rsp\n'.format(' '.join([self._quoter(x) for x in self.command]))) else: outfile.write(' command = {} @$out.rsp\n'.format(' '.join([self._quoter(x) for x in self.command]))) outfile.write(' rspfile = $out.rsp\n') diff --git a/mesonbuild/compilers/asm.py b/mesonbuild/compilers/asm.py index de299f9d72bd..6902652e8509 100644 --- a/mesonbuild/compilers/asm.py +++ b/mesonbuild/compilers/asm.py @@ -6,7 +6,6 @@ from ..mesonlib import EnvironmentException, get_meson_command from ..options import OptionKey from .compilers import Compiler -from ..linkers import RSPFileSyntax from ..linkers.linkers import VisualStudioLikeLinkerMixin from .mixins.metrowerks import MetrowerksCompiler, mwasmarm_instruction_set_args, mwasmeppc_instruction_set_args from .mixins.ti import TICompiler @@ -160,10 +159,6 @@ def get_crt_link_args(self, crt_val: str) -> T.List[str]: return [] return self.crt_args[self.get_crt_val(crt_val)] - # It uses '-@' instead of the normal '@' for denoting response files. - def rsp_file_syntax(self) -> RSPFileSyntax: - return RSPFileSyntax.NASM - class YasmCompiler(NasmCompiler): id = 'yasm' diff --git a/mesonbuild/linkers/base.py b/mesonbuild/linkers/base.py index d35a8df4c1c9..68fdb2ea3a70 100644 --- a/mesonbuild/linkers/base.py +++ b/mesonbuild/linkers/base.py @@ -19,7 +19,6 @@ class RSPFileSyntax(enum.Enum): MSVC = enum.auto() GCC = enum.auto() TASKING = enum.auto() - NASM = enum.auto() class ArLikeLinker: From fa6bdea6c69b0a606c073a107c7a9916bcc00dcf Mon Sep 17 00:00:00 2001 From: "L. E. Segovia" Date: Tue, 2 Jun 2026 15:03:33 -0300 Subject: [PATCH 441/681] nasm: disable response file support It requires a special flag `-@foo.rsp` and its contents must be separated with newlines. Both must be done externally to ninjabackend as the latter can only handle newlines in input objects, not in arguments. --- mesonbuild/compilers/asm.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mesonbuild/compilers/asm.py b/mesonbuild/compilers/asm.py index 6902652e8509..f5e725862366 100644 --- a/mesonbuild/compilers/asm.py +++ b/mesonbuild/compilers/asm.py @@ -159,6 +159,12 @@ def get_crt_link_args(self, crt_val: str) -> T.List[str]: return [] return self.crt_args[self.get_crt_val(crt_val)] + def can_linker_accept_rsp(self) -> bool: + """ + Determines whether the linker can accept arguments using the @rsp syntax. + """ + return False + class YasmCompiler(NasmCompiler): id = 'yasm' From 9e2abd0cae72db6ed9eb2bfef6df539c107ea1fc Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 27 May 2026 13:28:09 +0200 Subject: [PATCH 442/681] test cases: test recursive xgettext Signed-off-by: Paolo Bonzini --- .../38 gettext extractor/src/lib1/meson.build | 4 ++-- .../38 gettext extractor/src/lib2/meson.build | 2 +- .../38 gettext extractor/src/meson.build | 11 +++++++++++ .../38 gettext extractor/src/verify.py | 16 ++++++++++++++++ 4 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 test cases/frameworks/38 gettext extractor/src/verify.py diff --git a/test cases/frameworks/38 gettext extractor/src/lib1/meson.build b/test cases/frameworks/38 gettext extractor/src/lib1/meson.build index 3ec7fa987d6d..6839e141b749 100644 --- a/test cases/frameworks/38 gettext extractor/src/lib1/meson.build +++ b/test cases/frameworks/38 gettext extractor/src/lib1/meson.build @@ -1,3 +1,3 @@ -lib1 = library('mylib1', 'lib1.c') +lib1 = static_library('mylib1', 'lib1.c') lib1_pot = i18n.xgettext('lib1', lib1, args: xgettext_args) -lib1_includes = include_directories('.') \ No newline at end of file +lib1_includes = include_directories('.') diff --git a/test cases/frameworks/38 gettext extractor/src/lib2/meson.build b/test cases/frameworks/38 gettext extractor/src/lib2/meson.build index ac5e7fe4b050..88802bb873c7 100644 --- a/test cases/frameworks/38 gettext extractor/src/lib2/meson.build +++ b/test cases/frameworks/38 gettext extractor/src/lib2/meson.build @@ -1,3 +1,3 @@ -lib2 = library('mylib2', 'lib2.c', include_directories: lib1_includes, link_with: lib1) +lib2 = static_library('mylib2', 'lib2.c', include_directories: lib1_includes, link_with: lib1) lib2_pot = i18n.xgettext('lib2', lib2, args: xgettext_args) lib2_includes = include_directories('.') diff --git a/test cases/frameworks/38 gettext extractor/src/meson.build b/test cases/frameworks/38 gettext extractor/src/meson.build index b26646c1adb2..e0fecdce66e4 100644 --- a/test cases/frameworks/38 gettext extractor/src/meson.build +++ b/test cases/frameworks/38 gettext extractor/src/meson.build @@ -5,3 +5,14 @@ subdir('lib3') main = executable('say', 'main.c', link_with: [lib2], include_directories: lib2_includes) main_pot = i18n.xgettext('main', main, args: xgettext_args, install: true, install_dir: 'intl', install_tag: 'intl', recursive: true) + +verify = find_program('verify.py') + +# main links lib2 and lib2 links lib1, but main does not link lib1 directly. +# With recursive: true, the .pot must pick up lib1's "Something!" transitively +# through lib2, not just lib2's own "Something else!". +test( + 'recursive xgettext through static libraries', + verify, + args: [main_pot, 'Something else!', 'Something!'], +) diff --git a/test cases/frameworks/38 gettext extractor/src/verify.py b/test cases/frameworks/38 gettext extractor/src/verify.py new file mode 100644 index 000000000000..a4ee9a3632ad --- /dev/null +++ b/test cases/frameworks/38 gettext extractor/src/verify.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 + +import os +import sys + +assert len(sys.argv) >= 3 + +fname = sys.argv[1] +check_strs = sys.argv[2:] + +assert os.path.isfile(fname) +with open(fname, 'r', encoding='utf-8') as f: + content = f.read() + +for check_str in check_strs: + assert check_str in content, f'{check_str!r} not found in {fname}' From db767256dee7998b609219396763e9c2efa0a4b1 Mon Sep 17 00:00:00 2001 From: Charles Brunet Date: Tue, 2 Dec 2025 08:17:01 -0500 Subject: [PATCH 443/681] Fix recursion for i18n.xgettext [Paolo: add test] --- .../xgettext-include-private-dependencies.md | 24 +++++++++++++++++++ mesonbuild/modules/i18n.py | 4 +++- .../38 gettext extractor/src/lib1/meson.build | 6 +++++ .../38 gettext extractor/src/lib2/meson.build | 3 +++ .../38 gettext extractor/src/meson.build | 13 ++++++++++ 5 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 docs/markdown/snippets/xgettext-include-private-dependencies.md diff --git a/docs/markdown/snippets/xgettext-include-private-dependencies.md b/docs/markdown/snippets/xgettext-include-private-dependencies.md new file mode 100644 index 000000000000..81f97c654689 --- /dev/null +++ b/docs/markdown/snippets/xgettext-include-private-dependencies.md @@ -0,0 +1,24 @@ +## i18n.xgettext recursive option now includes "private" dependencies + +Suppose we have: + +``` +libA.dll -> libB.dll -> libC.dll +``` + +Here, `libA` links with `libB`, and `libB` links with `libC`, but `libA` does +not link with `libC` directly. So, `libC` is a "private" dependency of `libB`. +If we collect strings to translate using: + +``` +i18n.xgettext(libC) +i18n.xgettext(libB) +pot_file = i18n.xgettext(libA, recursive: true) +``` + +Previously, strings from `libC` would not be included in `pot_file`, since +`libC` is not a direct link dependency of `libA`. This has been fixed: when +the `recursive: true` option is used, `xgettext` now recursively includes +translations from all dependencies, including those of dependencies. This is +more logical, as even if `libA` does not directly link with `libC`, it may +still need translated strings from `libC`. diff --git a/mesonbuild/modules/i18n.py b/mesonbuild/modules/i18n.py index 25e9244610ce..2ef97e3e0111 100644 --- a/mesonbuild/modules/i18n.py +++ b/mesonbuild/modules/i18n.py @@ -208,9 +208,11 @@ def _get_depends(self, sources: T.Iterable[SourcesType]) -> T.Set[build.CustomTa depends = set() for source in sources: if isinstance(source, build.BuildTarget): - for source_id in self._get_source_id(source.get_dependencies()): + dependencies = source.get_dependencies() + for source_id in self._get_source_id(dependencies): if source_id in self.pot_files: depends.add(self.pot_files[source_id]) + depends.update(self._get_depends(dependencies)) elif isinstance(source, build.CustomTarget): # Dependency on another extracted pot file source_id = source.get_id() diff --git a/test cases/frameworks/38 gettext extractor/src/lib1/meson.build b/test cases/frameworks/38 gettext extractor/src/lib1/meson.build index 6839e141b749..5905dcb37ef7 100644 --- a/test cases/frameworks/38 gettext extractor/src/lib1/meson.build +++ b/test cases/frameworks/38 gettext extractor/src/lib1/meson.build @@ -1,3 +1,9 @@ lib1 = static_library('mylib1', 'lib1.c') lib1_pot = i18n.xgettext('lib1', lib1, args: xgettext_args) lib1_includes = include_directories('.') + +# Shared-library variant of the same chain, used to test that recursive +# xgettext descends through shared libraries (which, unlike static libraries, +# do not pull in their dependencies' objects). +shlib1 = shared_library('myshlib1', 'lib1.c') +shlib1_pot = i18n.xgettext('shlib1', shlib1, args: xgettext_args) diff --git a/test cases/frameworks/38 gettext extractor/src/lib2/meson.build b/test cases/frameworks/38 gettext extractor/src/lib2/meson.build index 88802bb873c7..bb81b4cd17ed 100644 --- a/test cases/frameworks/38 gettext extractor/src/lib2/meson.build +++ b/test cases/frameworks/38 gettext extractor/src/lib2/meson.build @@ -1,3 +1,6 @@ lib2 = static_library('mylib2', 'lib2.c', include_directories: lib1_includes, link_with: lib1) lib2_pot = i18n.xgettext('lib2', lib2, args: xgettext_args) lib2_includes = include_directories('.') + +shlib2 = shared_library('myshlib2', 'lib2.c', include_directories: lib1_includes, link_with: shlib1) +shlib2_pot = i18n.xgettext('shlib2', shlib2, args: xgettext_args) diff --git a/test cases/frameworks/38 gettext extractor/src/meson.build b/test cases/frameworks/38 gettext extractor/src/meson.build index e0fecdce66e4..1512081ee660 100644 --- a/test cases/frameworks/38 gettext extractor/src/meson.build +++ b/test cases/frameworks/38 gettext extractor/src/meson.build @@ -6,6 +6,9 @@ main = executable('say', 'main.c', link_with: [lib2], include_directories: lib2_ main_pot = i18n.xgettext('main', main, args: xgettext_args, install: true, install_dir: 'intl', install_tag: 'intl', recursive: true) +shmain = executable('shsay', 'main.c', link_with: [shlib2], include_directories: lib2_includes) +shmain_pot = i18n.xgettext('shmain', shmain, args: xgettext_args, recursive: true) + verify = find_program('verify.py') # main links lib2 and lib2 links lib1, but main does not link lib1 directly. @@ -16,3 +19,13 @@ test( verify, args: [main_pot, 'Something else!', 'Something!'], ) + +# Same chain, but with shared libraries. Unlike static libraries, shared +# libraries do not pull in their dependencies' objects, so shlib1 is not part +# of shmain.get_dependencies(). Recursive xgettext must still descend through +# shlib2 to reach shlib1's strings. +test( + 'recursive xgettext through shared libraries', + verify, + args: [shmain_pot, 'Something else!', 'Something!'], +) From 77564595213353969eab89c46b9996c7ff625acb Mon Sep 17 00:00:00 2001 From: Zephyr Lykos Date: Wed, 10 Dec 2025 04:26:35 +0800 Subject: [PATCH 444/681] compilers: clike: move `-fuse-ld=` to compiler args normal compilers does not rely on the position of this argument, but MSVC-like compilers treat `/link` and any argument after that as linker arguments. MSVC-like compiler's linker_to_compiler_args inject `/link` before largs, resulting it being forwarded to link.exe not the compiler driver. fixes cross compiling with cc = clang-cl and c_ld = lld-link. --- mesonbuild/compilers/mixins/clike.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py index f57221526339..e40c5fb89aa9 100644 --- a/mesonbuild/compilers/mixins/clike.py +++ b/mesonbuild/compilers/mixins/clike.py @@ -352,7 +352,7 @@ def _get_basic_compiler_args(self, mode: CompileCheckMode) -> T.Tuple[T.List[str if mode is CompileCheckMode.LINK: ld_value = self.environment.lookup_binary_entry(self.for_machine, self.language + '_ld') if ld_value is not None: - largs += self.use_linker_args(ld_value[0], self.version) + cargs += self.use_linker_args(ld_value[0], self.version) # Add LDFLAGS from the env sys_ld_args = self.environment.coredata.get_external_link_args(self.for_machine, self.language) From 1e50fe1adb64b5b7dae8b73df1e5b6ca5f6f37bc Mon Sep 17 00:00:00 2001 From: Zephyr Lykos Date: Wed, 10 Dec 2025 04:33:57 +0800 Subject: [PATCH 445/681] ninjabackend: msvc: make detect_vs_dep_prefix independent of stdlib this function does not pass enough args from the compiler object to find windows sdk without relying on environment variables. including a file from the same directory removes the dependency on stdlib. --- mesonbuild/backend/ninjabackend.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 87b74cc27ee0..d007488280d6 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -567,9 +567,13 @@ def detect_vs_dep_prefix(self, tempfilename: str) -> T.TextIO: filebase) with open(filename, 'w', encoding='utf-8') as f: f.write(dedent('''\ - #include + #include"incdetect2" int dummy; ''')) + filename_inc = os.path.join(self.environment.get_scratch_dir(), + 'incdetect2') + with open(filename_inc, 'w', encoding='utf-8') as f: + pass # The output of cl dependency information is language # and locale dependent. Any attempt at converting it to @@ -588,18 +592,18 @@ def detect_vs_dep_prefix(self, tempfilename: str) -> T.TextIO: stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = pc.communicate() - # We want to match 'Note: including file: ' in the line - # 'Note: including file: d:\MyDir\include\stdio.h', however + # 'Note: including file: d:\build\meson-private\incdetect2', however # different locales have different messages with a different - # number of colons. Match up to the drive name 'd:\'. + # number of colons. Match up to the drive name 'd:\' or + # a relative path '.\'. # When used in cross compilation, the path separator is a # forward slash rather than a backslash so handle both; i.e. - # the path is /MyDir/include/stdio.h. + # the path is /build/meson-private/incdetect or ./incdetect2. # With certain cross compilation wrappings of MSVC, the paths # use backslashes, but without the leading drive name, so # allow the path to start with any path separator, i.e. - # \MyDir\include\stdio.h. - matchre = re.compile(rb"^(.*\s)([a-zA-Z]:[\\/]|[\\\/]).*stdio.h$") + # \build\meson-private\incdetect2 + matchre = re.compile(rb"^(.*\s)([a-zA-Z]:[\\/]|\.?[\\/]).*incdetect2$") def detect_prefix(out: bytes) -> T.TextIO: for line in re.split(rb'\r?\n', out): From 3b7c81d46232a8dfdfe339e7e5ecf9d5d7a4121b Mon Sep 17 00:00:00 2001 From: Koushik Nakka Date: Thu, 28 May 2026 16:26:55 -0400 Subject: [PATCH 446/681] clang-cl: add c++23 support via /clang:-std= and remove CPP11AsCPP14Mixin clang-cl supports C++11 natively, so CPP11AsCPP14Mixin is not needed. Override get_option_std_args to use /clang:-std=c++N for all standards except c++latest/vc++latest which still require /std:c++latest. Add c++23 and vc++23 to the supported standards list. Fixes #15845 --- mesonbuild/compilers/cpp.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index 5a41f6a92cdc..a5e0bbb98384 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -942,7 +942,7 @@ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = def get_cpp_modules_args(self) -> T.List[str]: return ['/interface'] -class ClangClCPPCompiler(CPP11AsCPP14Mixin, VisualStudioLikeCPPCompilerMixin, ClangClCompiler, CPPCompiler): +class ClangClCPPCompiler(VisualStudioLikeCPPCompilerMixin, ClangClCompiler, CPPCompiler): id = 'clang-cl' @@ -955,9 +955,26 @@ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoic ClangClCompiler.__init__(self, target) def get_options(self) -> 'MutableKeyedOptionDictType': - cpp_stds = ['none', 'c++11', 'vc++11', 'c++14', 'vc++14', 'c++17', 'vc++17', 'c++20', 'vc++20', 'c++latest'] + cpp_stds = ['none', 'c++11', 'vc++11', 'c++14', 'vc++14', 'c++17', 'vc++17', 'c++20', 'vc++20', 'c++23', 'vc++23', 'c++latest'] return self._get_options_impl(super().get_options(), cpp_stds) + def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]: + std = self.get_compileropt_value('std', target, subproject) + assert isinstance(std, str) + if std == 'none': + return [] + # c++latest and vc++latest have no /clang:-std= equivalent + if std in {'c++latest', 'vc++latest'}: + args = ['/std:c++latest'] + if std == 'c++latest': + args.append('/permissive-') + return args + # vc++ variants: permissive mode, strip 'vc++' prefix to get clang std name + if std.startswith('vc++'): + return [f'/clang:-std=c++{std[4:]}'] + # c++ variants: strict conformance mode + return [f'/clang:-std={std}', '/permissive-'] + def get_cpp_modules_args(self) -> T.List[str]: # clang-cl does not support /interface. return ['-fmodules', '-fmodules-ts'] From 915a847ba2ba0daa11795166b26e570a250ab316 Mon Sep 17 00:00:00 2001 From: Koushik Nakka Date: Thu, 28 May 2026 17:06:29 -0400 Subject: [PATCH 447/681] use VC_VERSION_MAP as base for cpp_stds list --- mesonbuild/compilers/cpp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index a5e0bbb98384..41915038a2b8 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -955,7 +955,7 @@ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoic ClangClCompiler.__init__(self, target) def get_options(self) -> 'MutableKeyedOptionDictType': - cpp_stds = ['none', 'c++11', 'vc++11', 'c++14', 'vc++14', 'c++17', 'vc++17', 'c++20', 'vc++20', 'c++23', 'vc++23', 'c++latest'] + cpp_stds = list(self.VC_VERSION_MAP) + ['c++23', 'vc++23'] return self._get_options_impl(super().get_options(), cpp_stds) def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]: From 5c8df002295f15e1915a628445e2ebc5ff5f8dbb Mon Sep 17 00:00:00 2001 From: Gurchetan Singh Date: Wed, 13 May 2026 15:16:59 -0700 Subject: [PATCH 448/681] Add initial detection for Fuchsia Fuchsia is a microkernel-like OS that takes many cues from Plan9. Co-Authored-by: Dylan Baker --- docs/markdown/Reference-tables.md | 4 +++- mesonbuild/envconfig.py | 4 ++++ mesonbuild/utils/universal.py | 5 +++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/markdown/Reference-tables.md b/docs/markdown/Reference-tables.md index faa039bfe733..9c17ab35cfc9 100644 --- a/docs/markdown/Reference-tables.md +++ b/docs/markdown/Reference-tables.md @@ -176,6 +176,7 @@ These are provided by the `.system()` method call. | windows | Native Windows (not Cygwin or MSYS2) | | sunos | illumos and Solaris | | os/2 | OS/2 | +| fuchsia | Google's Fuchsia | Any string not listed above is not guaranteed to remain stable in future releases. @@ -197,7 +198,8 @@ Native names as returned by the `.kernel()` method. | solaris | Kernel derived from OpenSolaris by Oracle | | dragonfly | | | haiku| | -| none | For e.g. bare metal embedded | +| fuchsia | Google's Fuchsia | +| none | For e.g. bare metal embedded | ## Subsystem names (since 1.2.0) diff --git a/mesonbuild/envconfig.py b/mesonbuild/envconfig.py index b0072a49b874..c1940cd1fa63 100644 --- a/mesonbuild/envconfig.py +++ b/mesonbuild/envconfig.py @@ -396,6 +396,9 @@ def is_os2(self) -> bool: """ return self.system == 'os/2' + def is_fuchsia(self) -> bool: + return self.system == 'fuchsia' + # Various prefixes and suffixes for import libraries, shared libraries, # static libraries, and executables. # Versioning is added to these names in the backends as-needed. @@ -523,6 +526,7 @@ def get_variables(self) -> T.Dict[str, T.List[str]]: 'dragonfly': 'dragonfly', 'haiku': 'haiku', 'gnu': 'gnu', + 'fuchsia': 'fuchsia', } def detect_windows_arch(compilers: CompilerDict) -> str: diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index 7b5f0a9a63e4..60ffeb8b95e7 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -134,6 +134,7 @@ def __gt__(self, other: Self) -> bool: ... 'is_debianlike', 'is_dragonflybsd', 'is_freebsd', + 'is_fuchsia', 'is_haiku', 'is_hurd', 'is_irix', @@ -716,6 +717,10 @@ def is_android() -> bool: return _PLATFORM_SYSTEM_LOWER == 'android' +def is_fuchsia() -> bool: + return _PLATFORM_SYSTEM_LOWER == 'fuchsia' + + def is_haiku() -> bool: return _PLATFORM_SYSTEM_LOWER == 'haiku' From 2344a2e4e21b7572ff174cc4a7c8250225109246 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Storsj=C3=B6?= Date: Mon, 5 Jan 2026 00:46:02 +0200 Subject: [PATCH 449/681] compilers: clike: Fix cc.get_define() on MSVC with -std:c11, on Unix If doing cc.get_define() with MSVC, for a symbol that isn't defined, then get_define() can end up returning sentinel_undef with a trailing space, as an actual defined string. This happens if MSVC is called with -std:c11 (or -Zc:preprocessor, to opt in to the newer, standards conformant preprocessor mode) and the test source file is written with Unix newlines. (This is observed if cross compiling with MSVC wrapped in Wine on Unix, but probably would appear the same if Meson was running in Cygwin/msys2 Python as well.) This issue has been reported upstream to MSVC at [1]. [1] https://developercommunity.visualstudio.com/t/Inconsistent-whitespace-with-standard-pr/11023343 --- mesonbuild/compilers/mixins/clike.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py index e40c5fb89aa9..7f93ce78ea53 100644 --- a/mesonbuild/compilers/mixins/clike.py +++ b/mesonbuild/compilers/mixins/clike.py @@ -643,7 +643,12 @@ def get_define(self, dname: str, prefix: str, raise mesonlib.MesonBugException('Delimiters not found in preprocessor output.') define_value = p.stdout[star_idx + len(delim_start):end_idx] - if define_value == sentinel_undef: + # Due to https://developercommunity.visualstudio.com/t/Inconsistent-whitespace-with-standard-pr/11023343, + # MSVC can end up producing an unexpected space after the sentinel_undef + # string (if building with -std:c11, and if the test source is written + # with unix newlines). To avoid treating this as an actual predefined + # macro, look for the buggy value as well. + if define_value in {sentinel_undef, sentinel_undef + ' '}: define_value = None else: # Merge string literals From 546e47e804e587dbba456bbcf84e6ae6d9230d6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ayd=C4=B1n=20Mercan?= Date: Fri, 5 Jun 2026 14:35:43 +0300 Subject: [PATCH 450/681] automatically tag files installed under sbindir Targets created from functions such as `install_symlink` were not automatically tagged as "runtime" when installed under sbindir like it does when installed under bindir. --- mesonbuild/backend/backends.py | 3 ++- mesonbuild/environment.py | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index bee2f2652617..3b3389dc8367 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -1695,6 +1695,7 @@ def create_install_data_files(self) -> None: def guess_install_tag(self, fname: str, outdir: T.Optional[str] = None) -> T.Optional[str]: prefix = self.environment.get_prefix() + sbindir = Path(prefix, self.environment.get_sbindir()) bindir = Path(prefix, self.environment.get_bindir()) libdir = Path(prefix, self.environment.get_libdir()) incdir = Path(prefix, self.environment.get_includedir()) @@ -1702,7 +1703,7 @@ def guess_install_tag(self, fname: str, outdir: T.Optional[str] = None) -> T.Opt assert isinstance(_ldir, str), 'for mypy' localedir = Path(prefix, _ldir) dest_path = Path(prefix, outdir, Path(fname).name) if outdir else Path(prefix, fname) - if bindir in dest_path.parents: + if bindir in dest_path.parents or sbindir in dest_path.parents: return 'runtime' elif libdir in dest_path.parents: if dest_path.suffix in {'.a', '.pc'}: diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index de5daa48b4fb..76d75ebc1a33 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -499,6 +499,9 @@ def get_libexecdir(self) -> str: def get_bindir(self) -> str: return _as_str(self.coredata.optstore.get_value_for(OptionKey('bindir'))) + def get_sbindir(self) -> str: + return _as_str(self.coredata.optstore.get_value_for(OptionKey('sbindir'))) + def get_includedir(self) -> str: return _as_str(self.coredata.optstore.get_value_for(OptionKey('includedir'))) From ac3ee318dce97ada214afa7d54e495de25eafb60 Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Fri, 5 Jun 2026 13:40:58 -0400 Subject: [PATCH 451/681] docs: update release info for my secondary PGP signing key I have two signing keys. One is an older rsa4096 one, the newer ed25519 key I created when joining the Gentoo Developers team. Both keys are still valid, and the newer key is signed by the older key. When tagging release 1.11.1, I mistakenly uploaded assets signed by the newer key, which should not actually be a big deal but was confusing since it was not on this page. Update the docs so that people know this key is acceptable. Proofs for the new key: - cross-sig from the old to new key - recorded by https://github.com/eli-schwartz.gpg as a known key for me - recorded by https://codeberg.org/eschwartz.gpg as a known key for me - this git commit adding the new key to the docs page is signed by the old key - my online identity as a Gentoo Developer is associated with this key as observable via: https://wiki.gentoo.org/wiki/User:Eschwartz https://www.gentoo.org/inside-gentoo/developers/#eschwartz Fixes: https://github.com/mesonbuild/meson/issues/15901 --- docs/markdown/Releasing.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/markdown/Releasing.md b/docs/markdown/Releasing.md index 5fec13c6e6f7..f995aaf63c44 100644 --- a/docs/markdown/Releasing.md +++ b/docs/markdown/Releasing.md @@ -18,7 +18,9 @@ For each new meson release, several different artifacts are created: - Jussi Pakkanen. PGP key: [19E2D6D9B46D8DAA6288F877C24E631BABB1FE70](https://keyserver.ubuntu.com/pks/lookup?search=0x19E2D6D9B46D8DAA6288F877C24E631BABB1FE70&op=index) -- Eli Schwartz. PGP key: [BD27B07A5EF45C2ADAF70E0484818A6819AF4A9B](https://keyserver.ubuntu.com/pks/lookup?search=0xBD27B07A5EF45C2ADAF70E0484818A6819AF4A9B&op=index) +- Eli Schwartz. PGP key: + - [BD27B07A5EF45C2ADAF70E0484818A6819AF4A9B](https://keyserver.ubuntu.com/pks/lookup?search=0xBD27B07A5EF45C2ADAF70E0484818A6819AF4A9B&op=index) + - (alternate) [EF51D308B1CE230888BE0D84EA423E93A16343F1](https://keyserver.ubuntu.com/pks/lookup?search=0xEF51D308B1CE230888BE0D84EA423E93A16343F1&op=index) - Dylan Baker. PGP key: [71C4B75620BC75708B4BDB254C95FAAB3EB073EC](https://keyserver.ubuntu.com/pks/lookup?search=0x71C4B75620BC75708B4BDB254C95FAAB3EB073EC&op=index) The default release manager for new versions of Meson is Jussi Pakkanen. Starting with meson 1.8.0, the release team has been expanded with fallback options to reduce the bus factor, but will continue to be done by Jussi when possible. From 97d7e6091bb9bbdd8362e9d3f01219cf0654a550 Mon Sep 17 00:00:00 2001 From: "L. E. Segovia" Date: Mon, 1 Jun 2026 20:52:14 -0300 Subject: [PATCH 452/681] windows: disable the RC dependency detection script for Microsoft clang The rc helper script uses flags specific for MSVC, which clang does not understand: > [19/1419] Compiling Windows resource subprojects/ffmpeg/windows_compile_resources_0_avfilterres.rc > clang: error: no such file or directory: '/showIncludes' > clang: error: no such file or directory: '/EP' > clang: error: no such file or directory: '/nologo' > clang: error: no such file or directory: '/DRC_INVOKED' > clang: error: no such file or directory: '/Tcsubprojects/ffmpeg/avfilterres.rc' > clang: error: no input files --- mesonbuild/modules/windows.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/mesonbuild/modules/windows.py b/mesonbuild/modules/windows.py index d53ebe4fa6b5..b7aed3b0e816 100644 --- a/mesonbuild/modules/windows.py +++ b/mesonbuild/modules/windows.py @@ -185,12 +185,13 @@ def get_names() -> T.Iterable[T.Tuple[str, str, T.Union[mesonlib.File, build.Cus if rescomp_type == ResourceCompilerType.rc: compiler = self.detect_compiler(state.environment.coredata.compilers[MachineChoice.HOST]) - depfile_type = 'msvc' + if compiler.id in {'msvc', 'clang-cl'}: + depfile_type = 'msvc' - command.extend(state.environment.get_build_command()) - command.extend(['--internal', 'rc', - '--cl', compiler.get_exelist(False)[0], - '--rc']) + command.extend(state.environment.get_build_command()) + command.extend(['--internal', 'rc', + '--cl', compiler.get_exelist(False)[0], + '--rc']) command.append(rescomp) command.extend(res_args) From 31a1a87bd899a47ac0be45279569ff47c6070bae Mon Sep 17 00:00:00 2001 From: Arthur Grillo Date: Sat, 16 May 2026 20:43:44 -0300 Subject: [PATCH 453/681] mformat: Make --recursive recurse subprojects with --subprojects flag Currently, `meson format --recursive` only recurse meson.build files that are reachable via a `subdir` function. Add `--subprojects` flag to also recurse subprojects. --- .../snippets/mformat-recursive-subprojects.md | 4 ++ mesonbuild/mformat.py | 21 +++++++-- .../compare_format_diff.py | 44 +++++++++++++++++++ .../format/7 recurse subprojects/meson.build | 14 ++++++ .../subprojects/find-me/expected.diff | 7 +++ .../subprojects/find-me/meson.build | 3 ++ 6 files changed, 89 insertions(+), 4 deletions(-) create mode 100644 docs/markdown/snippets/mformat-recursive-subprojects.md create mode 100644 test cases/format/7 recurse subprojects/compare_format_diff.py create mode 100644 test cases/format/7 recurse subprojects/meson.build create mode 100644 test cases/format/7 recurse subprojects/subprojects/find-me/expected.diff create mode 100644 test cases/format/7 recurse subprojects/subprojects/find-me/meson.build diff --git a/docs/markdown/snippets/mformat-recursive-subprojects.md b/docs/markdown/snippets/mformat-recursive-subprojects.md new file mode 100644 index 000000000000..73416d2ef45e --- /dev/null +++ b/docs/markdown/snippets/mformat-recursive-subprojects.md @@ -0,0 +1,4 @@ +## New option `--subprojects` for `meson format` + +A new option `--subprojects`, to be specified together with `--recursive`, +tells `meson format` to also recurse into subprojects. diff --git a/mesonbuild/mformat.py b/mesonbuild/mformat.py index 67e4dbcdc814..a0567b623e6b 100644 --- a/mesonbuild/mformat.py +++ b/mesonbuild/mformat.py @@ -874,11 +874,16 @@ def visit_ParenthesizedNode(self, node: mparser.ParenthesizedNode) -> None: class SubdirFetcher(FullAstVisitor): - def __init__(self, current_dir: Path): + def __init__(self, current_dir: Path, fetch_subprojects: bool): self.current_dir = current_dir + self.fetch_subprojects = fetch_subprojects self.subdirs: T.List[Path] = [] def visit_FunctionNode(self, node: mparser.FunctionNode) -> None: + if self.fetch_subprojects and node.func_name.value == 'subproject': + if node.args.arguments and isinstance(node.args.arguments[0], mparser.StringNode): + subdir = node.args.arguments[0].value + self.subdirs.append(self.current_dir / 'subprojects' / subdir) if node.func_name.value == 'subdir': if node.args.arguments and isinstance(node.args.arguments[0], mparser.StringNode): subdir = node.args.arguments[0].value @@ -888,8 +893,9 @@ def visit_FunctionNode(self, node: mparser.FunctionNode) -> None: class Formatter: - def __init__(self, configuration_file: T.Optional[Path], use_editor_config: bool, fetch_subdirs: bool): + def __init__(self, configuration_file: T.Optional[Path], use_editor_config: bool, fetch_subdirs: bool, fetch_subprojects: bool = False): self.fetch_subdirs = fetch_subdirs + self.fetch_subprojects = fetch_subprojects self.use_editor_config = use_editor_config self.config = self.load_configuration(configuration_file) self.current_config = self.config @@ -974,7 +980,7 @@ def format(self, code: str, source_file: Path) -> str: ast = mparser.Parser(code, source_file.as_posix()).parse() if self.fetch_subdirs: - subdir_fetcher = SubdirFetcher(self.current_dir) + subdir_fetcher = SubdirFetcher(self.current_dir, self.fetch_subprojects) ast.accept(subdir_fetcher) self.subdirs = subdir_fetcher.subdirs @@ -1016,6 +1022,11 @@ def add_arguments(parser: argparse.ArgumentParser) -> None: action='store_true', help='recurse subdirs (requires --check-only, --check-diff or --inplace option)', ) + parser.add_argument( + '-s', '--subprojects', + action='store_true', + help='recurse subprojects (requires --recursive)', + ) parser.add_argument( '-c', '--configuration', metavar='meson.format', @@ -1058,6 +1069,8 @@ def run(options: argparse.Namespace) -> int: raise MesonException('--output argument implies having exactly one source file') if options.recursive and not (options.inplace or options.check_only or options.check_diff): raise MesonException('--recursive argument requires one of --inplace, --check-diff or --check-only') + if options.subprojects and not options.recursive: + raise MesonException('--subprojects argument requires --recursive option') from_stdin = len(options.sources) == 1 and options.sources[0].name == '-' and options.sources[0].parent == Path() if options.recursive and from_stdin: @@ -1074,7 +1087,7 @@ def run(options: argparse.Namespace) -> int: if not options.configuration: options.configuration = get_meson_format(sources) - formatter = Formatter(options.configuration, options.editor_config, options.recursive) + formatter = Formatter(options.configuration, options.editor_config, options.recursive, options.subprojects) err = 0 while sources: diff --git a/test cases/format/7 recurse subprojects/compare_format_diff.py b/test cases/format/7 recurse subprojects/compare_format_diff.py new file mode 100644 index 000000000000..119683b74d74 --- /dev/null +++ b/test cases/format/7 recurse subprojects/compare_format_diff.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 +# Copyright © 2026 Arthur Grillo + +import argparse +import subprocess +import sys +import difflib + + +def compare(actual: str, expected: str) -> int: + if actual == expected: + return 0 + + diff = difflib.ndiff(expected, actual) + for line in diff: + print(line, file=sys.stderr, end='') + return 1 + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument('meson_cmd_path', nargs='+') + + args = parser.parse_args() + + result = subprocess.run([ + *args.meson_cmd_path, + 'format', + '--recursive', + '--subprojects', + '--check-diff', + ], capture_output=True, text=True) + + with open('subprojects/find-me/expected.diff', 'r', encoding='utf-8') as f: + actual = result.stdout.replace('\\', '/') + expected = f.read() + + return compare( + actual, + expected, + ) + +if __name__ == "__main__": + sys.exit(main()) diff --git a/test cases/format/7 recurse subprojects/meson.build b/test cases/format/7 recurse subprojects/meson.build new file mode 100644 index 000000000000..a2017610edba --- /dev/null +++ b/test cases/format/7 recurse subprojects/meson.build @@ -0,0 +1,14 @@ +project('format') + +python = import('python') +meson_cmd = [python.find_installation(), find_program('meson').full_path()] +file_compare = find_program(files('compare_format_diff.py')) + +test( + 'recurse subprojects', + file_compare, + args: meson_cmd, + workdir: meson.project_source_root(), +) + +subproject('find-me') diff --git a/test cases/format/7 recurse subprojects/subprojects/find-me/expected.diff b/test cases/format/7 recurse subprojects/subprojects/find-me/expected.diff new file mode 100644 index 000000000000..1d324ace1c59 --- /dev/null +++ b/test cases/format/7 recurse subprojects/subprojects/find-me/expected.diff @@ -0,0 +1,7 @@ +--- subprojects/find-me/meson.build (original) ++++ subprojects/find-me/meson.build (reformatted) +@@ -1,3 +1 @@ +-project( +- 'a' +-) # should be on one line ++project('a') # should be on one line diff --git a/test cases/format/7 recurse subprojects/subprojects/find-me/meson.build b/test cases/format/7 recurse subprojects/subprojects/find-me/meson.build new file mode 100644 index 000000000000..feec13792851 --- /dev/null +++ b/test cases/format/7 recurse subprojects/subprojects/find-me/meson.build @@ -0,0 +1,3 @@ +project( + 'a' +) # should be on one line From 6719b6e0a2cc5a2e8c157bf8e00f3dc2cce1c4bf Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 8 Jun 2026 10:46:14 +0200 Subject: [PATCH 454/681] xcodebackend: fix expected type of "children" key As visible from generate_pbx_group(), folder_children is a PbxArray. Use the same variable names in write_group_target_entry() for clarity. Fixes: #15905 Signed-off-by: Paolo Bonzini --- mesonbuild/backend/xcodebackend.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index 3621e0cdbba8..48a3a719d4d5 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -1210,9 +1210,10 @@ def write_group_target_entry(self, objects_dict: PbxDict, t: build.BuildTarget) # If the file is in a folder, add it to the group representing that folder. if '/' in s.fname: folder = '/'.join(s.fname.split('/')[:-1]) - folder_dict = objects_dict.get_item_value(self.foldermap[(folder, t)], PbxDict).get_item_value('children', PbxDict) + folder_dict = objects_dict.get_item_value(self.foldermap[(folder, t)], PbxDict) + folder_children = folder_dict.get_item_value('children', PbxArray) temp = os.path.join(s.subdir, s.fname) - folder_dict.add_item(self.fileref_ids[(tid, temp)], temp) + folder_children.add_item(self.fileref_ids[(tid, temp)], temp) if self.foldermap[(folder, t)] in folder_ids: continue if len(folder.split('/')) == 1: From cf48eed25cc1dfefbb3ee8757b8a1bd7111be922 Mon Sep 17 00:00:00 2001 From: John Turner Date: Sun, 30 Nov 2025 15:00:14 -0500 Subject: [PATCH 455/681] add clippy-json target Add a clippy-json target to the ninja script. This is useful for rust-analyzer, which requires an "external check command" to function when not using cargo. Also we add a "clippy-json-prereq" target, and invoke it in the clippy.py script. The prereq target tries to build as much of the project as possible with "-k0". --- docs/markdown/Rust.md | 7 +++++++ docs/markdown/snippets/clippy-json.md | 7 +++++++ mesonbuild/backend/ninjabackend.py | 24 ++++++++++++++++++++++++ mesonbuild/scripts/clippy.py | 14 ++++++++++++-- 4 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 docs/markdown/snippets/clippy-json.md diff --git a/docs/markdown/Rust.md b/docs/markdown/Rust.md index d3616b9642bc..4575350a174e 100644 --- a/docs/markdown/Rust.md +++ b/docs/markdown/Rust.md @@ -91,6 +91,13 @@ write files into the source directory). [See the upstream docs](https://rust-analyzer.github.io/book/non_cargo_based_projects.html) for more information on how to configure that. +### Clippy +You can use the "clippy-json" build target as rust-analyer's "check command" to recieve clippy diagnostics in your editor. + +Without overriding the check command, the LSP will function in a limited state, only showing certain errors (for example, no borrow checking errors are shown). + +[Non cargo based projects](https://rust-analyzer.github.io/book/non_cargo_based_projects.html) shows how to override the check command, you probably want to set it to `ninja clippy-json -C build`. + ## Linking with standard libraries Meson will link the Rust standard libraries (e.g. libstd) statically, unless the diff --git a/docs/markdown/snippets/clippy-json.md b/docs/markdown/snippets/clippy-json.md new file mode 100644 index 000000000000..7e591453d9e5 --- /dev/null +++ b/docs/markdown/snippets/clippy-json.md @@ -0,0 +1,7 @@ +## New "clippy-json" Ninja Target For rust-analyzer + +A new `clippy-json` ninja target will now be generated for rust projects. + +rust-analyzer supports non-cargo based projects so long as you provide it with a `rust-project.json` file and a custom "check command" to provide compiler errors. Meson already for some time has generated a `rust-project.json` file for rust-analyzer, but had no way to hook up its rustc/clippy output to rust-analyzer. + +To use the new feature, you need to override the rust-analyzer check command as shown [in the rust-analyzer documentation](https://rust-analyzer.github.io/book/non_cargo_based_projects.html), and set it to a `ninja invocation along the lines of `ninja clippy-json -C build`. diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index d007488280d6..12b3904d8cb8 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -4024,6 +4024,28 @@ def generate_clippy(self) -> None: elem.add_dep(list(self.all_structured_sources)) self.add_build(elem) + def generate_clippy_json_prereq(self) -> None: + if 'clippy-json-prereq' in self.all_outputs or not self.have_language('rust'): + return + + elem = self.create_phony_target('clippy-json-prereq', 'CUSTOM_COMMAND', 'PHONY') + for crate in self.rust_crates.values(): + if crate.crate_type in {'rlib', 'dylib', 'proc-macro'}: + elem.add_dep(crate.target_name) + elem.add_dep(list(self.all_structured_sources)) + self.add_build(elem) + + def generate_clippy_json(self) -> None: + if 'clippy-json' in self.all_outputs or not self.have_language('rust'): + return + + cmd = self.environment.get_build_command() + \ + ['--internal', 'clippy', self.environment.build_dir, '--error-format=json'] + elem = self.create_phony_target('clippy-json', 'CUSTOM_COMMAND', 'PHONY') + elem.add_item('COMMAND', cmd) + elem.add_item('pool', 'console') + self.add_build(elem) + def generate_rustdoc(self) -> None: if 'rustdoc' in self.all_outputs or not self.have_language('rust'): return @@ -4113,6 +4135,8 @@ def generate_utils(self) -> None: self.generate_clangformat() self.generate_clangtidy() self.generate_clippy() + self.generate_clippy_json_prereq() + self.generate_clippy_json() self.generate_rustdoc() self.generate_tags('etags', 'TAGS') self.generate_tags('ctags', 'ctags') diff --git a/mesonbuild/scripts/clippy.py b/mesonbuild/scripts/clippy.py index 0ea7a3429cda..519d3ab22710 100644 --- a/mesonbuild/scripts/clippy.py +++ b/mesonbuild/scripts/clippy.py @@ -6,16 +6,18 @@ import os import tempfile import typing as T +import subprocess from .run_tool import run_tool_on_targets, run_with_buffered_output from .. import build, mlog from ..mesonlib import MachineChoice, PerMachine +from ..tooldetect import detect_ninja if T.TYPE_CHECKING: from ..compilers.rust import RustCompiler class ClippyDriver: - def __init__(self, build: build.Build, tempdir: str): + def __init__(self, build: build.Build, tempdir: str, args: list[str]): self.tools: PerMachine[T.List[str]] = PerMachine([], []) self.warned: T.DefaultDict[str, bool] = defaultdict(lambda: False) self.tempdir = tempdir @@ -24,6 +26,7 @@ def __init__(self, build: build.Build, tempdir: str): if 'rust' in compilers: compiler = T.cast('RustCompiler', compilers['rust']) self.tools[machine] = compiler.get_rust_tool('clippy-driver') + self.args = args def warn_missing_clippy(self, machine: str) -> None: if self.warned[machine]: @@ -67,10 +70,17 @@ def __call__(self, target: T.Dict[str, T.Any]) -> T.Iterable[T.Coroutine[None, N cmdlist.append('metadata') cmdlist.append('--out-dir') cmdlist.append(self.tempdir) + cmdlist += self.args yield run_with_buffered_output(cmdlist) def run(args: T.List[str]) -> int: os.chdir(args[0]) build_data = build.load(os.getcwd()) + + # Build as much of the project as possible, or else + # we get errors about missing libraries in the build directory and + # other related errors. + subprocess.run(detect_ninja() + ['clippy-json-prereq', '-k0']) + with tempfile.TemporaryDirectory() as d: - return run_tool_on_targets(ClippyDriver(build_data, d)) + return run_tool_on_targets(ClippyDriver(build_data, d, args[1:])) From 9f466735683cb08b26634f16478467a59af74b81 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 8 Jun 2026 11:16:08 +0200 Subject: [PATCH 456/681] interpreter: create build_subdir at include time include_directories() can point to a build_subdir that a custom_target() generated into, so the subdir must exist in the build tree at setup time. Create a new function to abstract os.makedirs() and use it validate_build_subdir(). Fixes: #15902 Signed-off-by: Paolo Bonzini --- mesonbuild/interpreter/interpreter.py | 10 +++++++--- .../257 generated header dep/meson.build | 18 +++++++++++++++++- .../common/257 generated header dep/subprog.c | 5 +++++ 3 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 test cases/common/257 generated header dep/subprog.c diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 88c93ac0f9c2..5ce7d73dcb4b 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -882,6 +882,9 @@ def disabled_subproject(self, subp_name: SubProject, disabled_feature: T.Optiona self.subprojects[subp_name] = sub return sub + def create_build_subdir(self, subdir: str) -> None: + os.makedirs(os.path.join(self.build.environment.get_build_dir(), subdir), exist_ok=True) + def do_subproject(self, subp_name: SubProject, kwargs: kwtypes.DoSubproject, force_method: T.Optional[wrap.Method] = None, forced_options: T.Optional[OptionDict] = None) -> SubprojectHolder: disabled, required, feature = extract_required_kwarg(kwargs, self.subproject) @@ -943,7 +946,7 @@ def do_subproject(self, subp_name: SubProject, kwargs: kwtypes.DoSubproject, for mlog.error(*msg) raise e - os.makedirs(os.path.join(self.build.environment.get_build_dir(), subdir), exist_ok=True) + self.create_build_subdir(subdir) self.global_args_frozen = True stack = ':'.join(self.subproject_stack + [subp_name]) @@ -2517,7 +2520,7 @@ def func_subdir(self, node: mparser.BaseNode, args: T.Tuple[str], kwargs: 'kwtyp if not is_new: raise InvalidArguments(f'Tried to enter directory "{subdir}", which has already been visited.') - os.makedirs(os.path.join(self.environment.build_dir, subdir), exist_ok=True) + self.create_build_subdir(subdir) if InterpreterRuleRelaxation.CARGO_SUBDIR in self.relaxations and \ os.path.exists(os.path.join(self.environment.get_source_dir(), subdir, 'Cargo.toml')): @@ -2657,6 +2660,7 @@ def validate_build_subdir(self, build_subdir: str, target: str) -> None: raise InvalidArguments(f'Build subdir "{build_subdir}" in "{target}" exists in source tree.') if '..' in build_subdir: raise InvalidArguments(f'Build subdir "{build_subdir}" in "{target}" contains ..') + self.create_build_subdir(os.path.join(self.subdir, build_subdir)) @noPosargs @typed_kwargs( @@ -2834,7 +2838,7 @@ def func_configure_file(self, node: mparser.BaseNode, args: T.List[TYPE_var], elif kwargs['copy']: if len(inputs_abs) != 1: raise InterpreterException('Exactly one input file must be given in copy mode') - os.makedirs(os.path.join(self.environment.build_dir, self.subdir), exist_ok=True) + self.create_build_subdir(self.subdir) shutil.copy2(inputs_abs[0], ofile_abs) # Install file if requested, we check for the empty string diff --git a/test cases/common/257 generated header dep/meson.build b/test cases/common/257 generated header dep/meson.build index 82db3b228ede..b0fd526f12cf 100644 --- a/test cases/common/257 generated header dep/meson.build +++ b/test cases/common/257 generated header dep/meson.build @@ -1,4 +1,4 @@ -project('generated header dep', 'c') +project('generated header dep', 'c', meson_version : '>= 1.10.0') # Regression test case for a very specific case: # - Uses both_libraries(), or library() with default_library=both. @@ -20,3 +20,19 @@ sources_dep = declare_dependency(sources: files('foo.c')) both_libraries('foo', header, dependencies: sources_dep, ) + +# include_directories() can point to a build_subdir that a custom_target() +# generated into, so the subdir must exist in the build tree at setup time +subheader = custom_target( + output: 'sub.h', + capture: true, + command: [python, '-c', 'print("#define SUB 0")'], + build_subdir: 'generated_includes', +) + +inc = include_directories('generated_includes') + +subprog = executable('subprog', 'subprog.c', subheader, + include_directories: inc, +) +test('subprog', subprog) diff --git a/test cases/common/257 generated header dep/subprog.c b/test cases/common/257 generated header dep/subprog.c new file mode 100644 index 000000000000..8076e0b5ed5d --- /dev/null +++ b/test cases/common/257 generated header dep/subprog.c @@ -0,0 +1,5 @@ +#include "sub.h" + +int main(void) { + return SUB; +} From 6e19d673a750ae8d904e440f0e2c69ff215c35d7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 8 Jun 2026 19:04:37 +0200 Subject: [PATCH 457/681] Revert "compilers: Deduplicate OpenMP linker arguments" This reverts commit f7758b9dd2c20a20fac34598e469b9ce99ce324d. Deduplicating OpenMP linker arguments is in principle a good idea, but it breaks -Xpreprocessor -fopenmp because deduplication has no idea of which options have an argument in a separate entry of the array. Fixes: #15764 Signed-off-by: Paolo Bonzini --- mesonbuild/compilers/mixins/clike.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py index 7f93ce78ea53..db440c30b4fb 100644 --- a/mesonbuild/compilers/mixins/clike.py +++ b/mesonbuild/compilers/mixins/clike.py @@ -65,7 +65,7 @@ class CLikeCompilerArgs(arglist.CompilerArgs): # https://github.com/mesonbuild/meson/pull/4593#pullrequestreview-182016038 dedup1_prefixes = ('-l', '-Wl,-l', '-Wl,-rpath,', '-Wl,-rpath-link,') dedup1_suffixes = ('.lib', '.dll', '.so', '.dylib', '.a') - dedup1_args = ('-c', '-S', '-E', '-pipe', '-pthread', '-Wl,--export-dynamic', '-fopenmp', '-qopenmp') + dedup1_args = ('-c', '-S', '-E', '-pipe', '-pthread', '-Wl,--export-dynamic') def to_native(self, copy: bool = False) -> T.List[str]: # This seems to be allowed, but could never work? From e7a2400592738157e60890493fa382f46741c4d3 Mon Sep 17 00:00:00 2001 From: Gabriel Guilbert Date: Thu, 9 Apr 2026 15:48:03 -0400 Subject: [PATCH 458/681] compilers: Don't use -I=path in TI toolchain --- mesonbuild/compilers/mixins/ti.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mesonbuild/compilers/mixins/ti.py b/mesonbuild/compilers/mixins/ti.py index bdab3e48cde0..329861785aa9 100644 --- a/mesonbuild/compilers/mixins/ti.py +++ b/mesonbuild/compilers/mixins/ti.py @@ -103,14 +103,12 @@ def get_werror_args(self) -> T.List[str]: def get_include_args(self, path: str, is_system: bool) -> T.List[str]: if path == '': path = '.' - return ['-I=' + path] + return ['-I' + path] @classmethod def _unix_args_to_native(cls, args: T.List[str], info: MachineInfo) -> T.List[str]: result: T.List[str] = [] for i in args: - if i.startswith('-D'): - i = '--define=' + i[2:] if i.startswith('-Wl,-rpath='): continue elif i == '--print-search-dirs': From 8e9c69cab1bc798a0d5fe0cda6821a72dda5b621 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 8 Jun 2026 10:39:15 -0700 Subject: [PATCH 459/681] build: Delete dead kwargs lists These were used by the old argument checking infrastructure, which has all been removed. --- mesonbuild/build.py | 47 +-------------------------------------------- 1 file changed, 1 insertion(+), 46 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 2f00aa17d441..8f20c03c9f95 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -30,7 +30,7 @@ from .options import OptionKey from .compilers import ( - is_header, is_object, is_source, clink_langs, sort_clink, all_languages, + is_header, is_object, is_source, clink_langs, sort_clink, is_known_suffix, detect_static_linker, LANGUAGES_USING_LDFLAGS, get_base_compile_args ) @@ -171,51 +171,6 @@ class JarKeywordArguments(BuildTargetKeywordArguments, total=False): 'cygwin': ('cyg', 'dll', 'dll.a'), } -pch_kwargs = {'c_pch', 'cpp_pch'} - -lang_arg_kwargs = {f'{lang}_args' for lang in all_languages} -lang_arg_kwargs |= { - 'd_import_dirs', - 'd_unittest', - 'd_module_versions', - 'd_debug', -} - -vala_kwargs = {'vala_header', 'vala_gir', 'vala_vapi', 'install_vala_header', 'install_vala_gir', 'install_vala_vapi'} -rust_kwargs = {'rust_crate_type', 'rust_dependency_map'} -cs_kwargs = {'resources', 'cs_args'} -swift_kwargs = {'swift_interoperability_mode', 'swift_module_name'} - -buildtarget_kwargs = { - 'build_by_default', - 'build_rpath', - 'build_subdir', - 'dependencies', - 'extra_files', - 'gui_app', - 'link_with', - 'link_whole', - 'link_args', - 'link_early_args', - 'link_depends', - 'implicit_include_directories', - 'include_directories', - 'install', - 'install_rpath', - 'install_dir', - 'install_mode', - 'install_tag', - 'name_prefix', - 'name_suffix', - 'native', - 'objects', - 'override_options', - 'sources', - 'gnu_symbol_visibility', - 'link_language', - 'win_subsystem', -} - def _process_install_tag(install_tag: T.Optional[T.List[T.Optional[str]]], num_outputs: int) -> T.List[T.Optional[str]]: _install_tag: T.List[T.Optional[str]] From c5d43af7043fa8e4a11061420f4863e62143fe5a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 9 Jun 2026 20:58:36 +0200 Subject: [PATCH 460/681] envconfig: allow running executables if subsystem is not set detect_subsystem(system) is just a simple transformation if system; do not require setting machine_info.subsystem at all costs, and if it is unset just assume that it is okay since the system has already been matched. Regression introduced by commit 61c5859b7 ("envconfig: Cannot run executables if subsystem doesn't match", 2026-02-04). Fixes: #15840 Signed-off-by: Paolo Bonzini --- mesonbuild/envconfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/envconfig.py b/mesonbuild/envconfig.py index c1940cd1fa63..a99c976a51aa 100644 --- a/mesonbuild/envconfig.py +++ b/mesonbuild/envconfig.py @@ -758,7 +758,7 @@ def machine_info_can_run(machine_info: MachineInfo) -> bool: system = detect_system() if machine_info.system != system: return False - if machine_info.subsystem != detect_subsystem(system): + if machine_info.subsystem and machine_info.subsystem != detect_subsystem(system): return False true_build_cpu_family = detect_cpu_family({}) assert machine_info.cpu_family is not None, 'called on incomplete machine_info' From 89847a5cc902454d7a1f9d0a3f9c90bef330f23e Mon Sep 17 00:00:00 2001 From: meator Date: Sun, 7 Jun 2026 23:00:42 +0200 Subject: [PATCH 461/681] docs: Qt6 module: document main kwarg --- docs/markdown/Dependencies.md | 4 ++++ docs/markdown/Qt6-module.md | 18 ++++++++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/docs/markdown/Dependencies.md b/docs/markdown/Dependencies.md index 09e1138056cc..cd2a4b4ff9c6 100644 --- a/docs/markdown/Dependencies.md +++ b/docs/markdown/Dependencies.md @@ -770,6 +770,10 @@ qt6_dep = dependency('qt6', modules : ['Core', 'Gui', 'Widgets']) An optional `method` keyword argument can be set: `auto` (default), `pkg-config`, `config-tool` or `qmake` (*deprecated since 0.58.0*; use `config-tool` instead). +An optional `main` boolean keyword argument can be set to `true` to add a link +dependency on `Qt6EntryPoint` on Windows, which is in most cases necessary to +be able to compile `win_subsystem: 'windows'` executables. + The `modules` keyword receives an array of Qt module names that will be required and linked against. diff --git a/docs/markdown/Qt6-module.md b/docs/markdown/Qt6-module.md index 39b21f76c123..d1fd73dc6cfa 100644 --- a/docs/markdown/Qt6-module.md +++ b/docs/markdown/Qt6-module.md @@ -115,15 +115,23 @@ inc = include_directories('.') qt6 = import('qt6') # Qml and QmlIntegration are needed here -qt6_dep = dependency('qt6', modules: ['Core', 'Gui', 'Network', 'Svg', 'Quick', 'Qml', 'QuickWidgets', 'QmlIntegration']) +qt6_dep = dependency( + 'qt6', + modules: ['Core', 'Gui', 'Network', 'Svg', 'Quick', 'Qml', 'QuickWidgets', 'QmlIntegration'], + # To make the `win_subsystem: 'windows'` line below work on windows, the + # program must provide a WinMain() or wWinMain() entrypoint. Qt provides a + # library which provides these functions (which just call main()). To link + # against the Qt6EntryPoint library, `main: true` must be supplied. + main: true, +) compiled_resources = qt6.compile_resources(sources: files('resources.qrc')) # compile 'moc_header_files' list with moc moc_files = qt6.compile_moc( - headers : moc_header_files, + headers: moc_header_files, include_directories: inc, - dependencies: qt6_dep + dependencies: qt6_dep, ) # everything involved in C++-QML interaction goes in here @@ -143,7 +151,9 @@ executable('MyExecutable', dependencies: [qt6_dep], win_subsystem: 'windows', # useful for Windows: displays the app without spawning # a console alongside it, no-op on other platforms. - install: true) + # see also comment above on `main: true` dependency() + install: true, +) ``` ## Functions From 9dae78216e2943518b5297cf2fc230224633fa90 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 6 Jun 2026 14:37:08 +0200 Subject: [PATCH 462/681] interpreter: do not warn for intentional ALLOW_BUILD_DIR_FILE_REFERENCES Instead of adding the relaxation universally, just comment out the raise statement. This allows the CMake module case (where the relaxation is added intentionally) to not warn. Add a test case that the warning happens, though. Signed-off-by: Paolo Bonzini --- mesonbuild/interpreter/interpreter.py | 13 +++++-------- test cases/common/299 built file by path/main.c | 6 ++++++ .../common/299 built file by path/meson.build | 4 ++++ test cases/common/299 built file by path/test.json | 8 ++++++++ 4 files changed, 23 insertions(+), 8 deletions(-) create mode 100644 test cases/common/299 built file by path/main.c create mode 100644 test cases/common/299 built file by path/meson.build create mode 100644 test cases/common/299 built file by path/test.json diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 5ce7d73dcb4b..b3f23fc355b7 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -179,7 +179,7 @@ class BuiltFileByNameError(InterpreterException): name: str def __str__(self) -> str: - return (f'{self.name} is a generated file, and should be passed by reference instead of string name. ' + return (f'{self.name!r} is a generated file, and should be passed by reference instead of string name. ' 'This will become a hard error in Meson 2.0') @@ -3283,10 +3283,6 @@ def source_strings_to_files(self, mesonlib.check_direntry_issues(sources) results: list[mesonlib.File | build.GeneratedTypes | build.BuildTarget | build.ExtractedObjects | build.StructuredSources | Program] = [] - # In Meson 2.0 this shoul not add ALLOW_BUILD_DIR_FILE_REFERENCES - # universally, and we should just use self.relaxations - relaxations: set[InterpreterRuleRelaxation] = self.relaxations | {InterpreterRuleRelaxation.ALLOW_BUILD_DIR_FILE_REFERENCES} - for s in sources: if isinstance(s, str): if s.endswith(' '): @@ -3294,9 +3290,10 @@ def source_strings_to_files(self, try: self.validate_within_subproject(self.subdir, s) except BuiltFileByNameError as e: - if InterpreterRuleRelaxation.ALLOW_BUILD_DIR_FILE_REFERENCES not in relaxations: - raise - mlog.warning(str(e), location=self.current_node) + # In Meson 2.0 this should just raise + if InterpreterRuleRelaxation.ALLOW_BUILD_DIR_FILE_REFERENCES not in self.relaxations: + #raise + mlog.warning(str(e), location=self.current_node) results.append(mesonlib.File.from_built_file(self.subdir, s)) else: results.append(mesonlib.File.from_source_file(self.environment.source_dir, self.subdir, s)) diff --git a/test cases/common/299 built file by path/main.c b/test cases/common/299 built file by path/main.c new file mode 100644 index 000000000000..a34d318387f8 --- /dev/null +++ b/test cases/common/299 built file by path/main.c @@ -0,0 +1,6 @@ +#include + +int main() +{ + printf("hello world\n"); +} diff --git a/test cases/common/299 built file by path/meson.build b/test cases/common/299 built file by path/meson.build new file mode 100644 index 000000000000..10e2003b9978 --- /dev/null +++ b/test cases/common/299 built file by path/meson.build @@ -0,0 +1,4 @@ +project('a', 'c') +configure_file(output: 'fullpath.c', input: 'main.c', copy: true) +exe = executable('fullpath', meson.project_build_root() / 'fullpath.c') +test('fullpath', exe) diff --git a/test cases/common/299 built file by path/test.json b/test cases/common/299 built file by path/test.json new file mode 100644 index 000000000000..7975e226df77 --- /dev/null +++ b/test cases/common/299 built file by path/test.json @@ -0,0 +1,8 @@ +{ + "stdout": [ + { + "line": "test cases/common/299 built file by path/meson.build:3: WARNING: '.*/fullpath.c' is a generated file, and should be passed by reference instead of string name. This will become a hard error in Meson 2.0", + "match": "re" + } + ] +} From 54e32a5f0a646ce6b49652de9c92c3845e2bcaf5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 4 Jun 2026 08:31:52 +0200 Subject: [PATCH 463/681] interpreter: do not create files with absolute fname Prior to commit 7851563c0 ("interpreter: remove source_strings_to_files strict argument", 2026-05-27), build files with an absolute path were added as strings to the result of source_strings_to_files. Now they are added as Files, but this creates (via File.from_built_file) malformed files with an absolute fname part. Catch them and properly make everything relative. Fixes: #15871 Signed-off-by: Paolo Bonzini --- mesonbuild/interpreter/interpreter.py | 12 +++++++++++- test cases/common/299 built file by path/main.c | 4 ++-- test cases/common/299 built file by path/meson.build | 11 ++++++++++- test cases/common/299 built file by path/test.json | 2 +- 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index b3f23fc355b7..d9caa60b4024 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -3294,7 +3294,17 @@ def source_strings_to_files(self, if InterpreterRuleRelaxation.ALLOW_BUILD_DIR_FILE_REFERENCES not in self.relaxations: #raise mlog.warning(str(e), location=self.current_node) - results.append(mesonlib.File.from_built_file(self.subdir, s)) + if path_has_root(s): + # A built File's name must be relative to the build + # root, otherwise the absolute fname leaks through + # File.relative_name() and breaks the backend. + # Use join and relpath together to handle paths without + # the drive part. + rel = os.path.relpath(os.path.join(self.environment.build_dir, s), + self.environment.build_dir) + results.append(mesonlib.File.from_built_relative(rel)) + else: + results.append(mesonlib.File.from_built_file(self.subdir, s)) else: results.append(mesonlib.File.from_source_file(self.environment.source_dir, self.subdir, s)) elif isinstance(s, (mesonlib.File, build.GeneratedList, Program, diff --git a/test cases/common/299 built file by path/main.c b/test cases/common/299 built file by path/main.c index a34d318387f8..fb64cbc6aee6 100644 --- a/test cases/common/299 built file by path/main.c +++ b/test cases/common/299 built file by path/main.c @@ -1,6 +1,6 @@ #include -int main() +int main(void) { - printf("hello world\n"); + printf("hello world\n"); } diff --git a/test cases/common/299 built file by path/meson.build b/test cases/common/299 built file by path/meson.build index 10e2003b9978..0945676e754d 100644 --- a/test cases/common/299 built file by path/meson.build +++ b/test cases/common/299 built file by path/meson.build @@ -1,4 +1,13 @@ project('a', 'c') configure_file(output: 'fullpath.c', input: 'main.c', copy: true) -exe = executable('fullpath', meson.project_build_root() / 'fullpath.c') + +path = meson.project_build_root() / 'fullpath.c' +if build_machine.system() == 'windows' + if '\\' in meson.project_build_root() + path = path.replace('\\', '/') + else + path = path.replace('/', '\\') + endif +endif +exe = executable('fullpath', path) test('fullpath', exe) diff --git a/test cases/common/299 built file by path/test.json b/test cases/common/299 built file by path/test.json index 7975e226df77..8fa2627bda13 100644 --- a/test cases/common/299 built file by path/test.json +++ b/test cases/common/299 built file by path/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/common/299 built file by path/meson.build:3: WARNING: '.*/fullpath.c' is a generated file, and should be passed by reference instead of string name. This will become a hard error in Meson 2.0", + "line": "test cases/common/299 built file by path/meson.build:12: WARNING: '.*/fullpath.c' is a generated file, and should be passed by reference instead of string name. This will become a hard error in Meson 2.0", "match": "re" } ] From 9a07a6600cb97189b6b28d17ae2cd617c3623c3c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 4 Jun 2026 08:34:52 +0200 Subject: [PATCH 464/681] ninjabackend: catch bad files with an absolute fname part generate_single_compile has code to handle files that return an absolute path from rel_to_builddir(), but the situation it tried to handle should not arise at all. For a built File, rel_to_builddir() returns relative_name(), i.e. os.path.join(subdir, fname). Neither component is supposed to be absolute: subdir is a directory relative to the build root and fname is a bare basename. So an absolute result means a malformed File was constructed somewhere upstream, and the old code was silently papering over it instead of catching it. The one reported way in which this could happen was through the ALLOW_BUILD_DIR_FILE_REFERENCES relaxation in source_strings_to_files(), which produced files such as: is_built = True subdir = subprojects\aom-cmake fname = C:/dev/.../aom-cmake/__CMake_build/gen_src/aom_av1_no_op.c That is fixed in the previous commit. With well-formed files guaranteed (at least in theory), drop the fragile startswith()/slice dance and instead reject an absolute fname outright. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 12b3904d8cb8..9985b06411c5 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -32,7 +32,7 @@ File, LibType, MachineChoice, MesonBugException, MesonException, OrderedSet, PerMachine, ProgressBar, quote_arg, unique_list ) -from ..mesonlib import get_compiler_for_source, has_path_sep, is_parent_path, lookbehind +from ..mesonlib import get_compiler_for_source, has_path_sep, is_parent_path, lookbehind, path_has_root from ..options import OptionKey from .backends import CleanTrees from ..build import GeneratedList, InvalidArguments @@ -3241,13 +3241,9 @@ def generate_single_compile(self, target: build.BuildTarget, src: FileOrString, build_dir = self.environment.get_build_dir() if isinstance(src, File): + if src.is_built and path_has_root(src.fname): + raise MesonBugException('absolute file name for built file ' + src.relative_name()) rel_src = src.rel_to_builddir(self.build_to_src) - if os.path.isabs(rel_src): - # Source files may not be from the source directory if they originate in source-only libraries, - # so we can't assert that the absolute path is anywhere in particular. - if src.is_built: - assert rel_src.startswith(build_dir) - rel_src = rel_src[len(build_dir) + 1:] elif is_generated: raise AssertionError(f'BUG: broken generated source file handling for {src!r}') else: From 3eaf128967eb252e42e71d01b2b2bdef5b33f8b7 Mon Sep 17 00:00:00 2001 From: Gleb Popov <6yearold@gmail.com> Date: Wed, 10 Jun 2026 21:59:54 +0300 Subject: [PATCH 465/681] Implement language_stdlib_only_link_flags() for ElbrusFortranCompiler e2k's gfortran mimics the GNU one in this regard. Fixes numpy's f2py tests. --- mesonbuild/compilers/fortran.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py index c099facb81ba..6ad4a6b59bcd 100644 --- a/mesonbuild/compilers/fortran.py +++ b/mesonbuild/compilers/fortran.py @@ -344,6 +344,11 @@ def get_options(self) -> 'MutableKeyedOptionDictType': def get_module_outdir_args(self, path: str) -> T.List[str]: return ['-J' + path] + def language_stdlib_only_link_flags(self) -> T.List[str]: + # No need to add search paths here, because LCC ships everything + # (C, C++, Fortran) and always knows where to look for its stuff + return ['-lgfortran', '-lm'] + class G95FortranCompiler(FortranCompiler): From 9e94d0c35a99db5d8ba71f5b39e4fdb20a1d15b2 Mon Sep 17 00:00:00 2001 From: Andrew McNulty Date: Fri, 14 Mar 2025 17:03:00 +0100 Subject: [PATCH 466/681] Misspellings: add closest match for function and method kwargs In the case of an unknown keyword argument passed to a function or method, attempt to find a closest match to present to the user in the error message. --- mesonbuild/interpreterbase/decorators.py | 10 +++++++++- .../failing/134 misspelled function kwargs/meson.build | 1 + .../failing/134 misspelled function kwargs/test.json | 7 +++++++ .../failing/135 misspelled method kwargs/meson.build | 4 ++++ .../failing/135 misspelled method kwargs/test.json | 7 +++++++ test cases/failing/55 bad option argument/test.json | 2 +- 6 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 test cases/failing/134 misspelled function kwargs/meson.build create mode 100644 test cases/failing/134 misspelled function kwargs/test.json create mode 100644 test cases/failing/135 misspelled method kwargs/meson.build create mode 100644 test cases/failing/135 misspelled method kwargs/test.json diff --git a/mesonbuild/interpreterbase/decorators.py b/mesonbuild/interpreterbase/decorators.py index 5ee87975f0f9..ad5f17e263c6 100644 --- a/mesonbuild/interpreterbase/decorators.py +++ b/mesonbuild/interpreterbase/decorators.py @@ -127,6 +127,14 @@ def wrapped(*wrapped_args: T.Any, **wrapped_kwargs: T.Any) -> T.Any: return ret return T.cast('TV_func', wrapped) +def kwargs_get_close_matches(invalid_kwargs: T.Set[str], valid_kwargs: T.Set[str]) -> T.List[str]: + with_close_matches = [] + from difflib import get_close_matches + for invalid in sorted(invalid_kwargs): + close_matches = get_close_matches(invalid, valid_kwargs) + with_close_matches.append(f'"{invalid}" (did you mean "{close_matches[0]}"?)' if close_matches else f'"{invalid}"') + return with_close_matches + def typed_operator(operator: MesonOperator, types: T.Union[T.Type, T.Tuple[T.Type, ...]]) -> T.Callable[['_TV_FN_Operator'], '_TV_FN_Operator']: """Decorator that does type checking for operator calls. @@ -551,7 +559,7 @@ def emit_feature_change(values: T.Dict[_T, T.Union[str, T.Tuple[str, str]]], fea all_names = {t.name for t in types} unknowns = set(kwargs).difference(all_names) if unknowns: - ustr = ', '.join([f'"{u}"' for u in sorted(unknowns)]) + ustr = ', '.join(kwargs_get_close_matches(unknowns, all_names)) raise InvalidArguments(f'{name} got unknown keyword arguments {ustr}') for info in types: diff --git a/test cases/failing/134 misspelled function kwargs/meson.build b/test cases/failing/134 misspelled function kwargs/meson.build new file mode 100644 index 000000000000..9c097d63d76c --- /dev/null +++ b/test cases/failing/134 misspelled function kwargs/meson.build @@ -0,0 +1 @@ +project('134 misspelled function kwargs', licence: 'GPL') diff --git a/test cases/failing/134 misspelled function kwargs/test.json b/test cases/failing/134 misspelled function kwargs/test.json new file mode 100644 index 000000000000..fcbd53d11a8e --- /dev/null +++ b/test cases/failing/134 misspelled function kwargs/test.json @@ -0,0 +1,7 @@ +{ + "stdout": [ + { + "line": "test cases/failing/134 misspelled function kwargs/meson.build:1:0: ERROR: project got unknown keyword arguments \"licence\" (did you mean \"license\"?)" + } + ] +} diff --git a/test cases/failing/135 misspelled method kwargs/meson.build b/test cases/failing/135 misspelled method kwargs/meson.build new file mode 100644 index 000000000000..6b2a70a11398 --- /dev/null +++ b/test cases/failing/135 misspelled method kwargs/meson.build @@ -0,0 +1,4 @@ +project('135 misspelled method kwargs') +py = import('python') +inst = py.find_installation() +mod = inst.extension_module('mod', srcs: 'mod.c', deps: dependency('threads')) diff --git a/test cases/failing/135 misspelled method kwargs/test.json b/test cases/failing/135 misspelled method kwargs/test.json new file mode 100644 index 000000000000..95b0616d1ba8 --- /dev/null +++ b/test cases/failing/135 misspelled method kwargs/test.json @@ -0,0 +1,7 @@ +{ + "stdout": [ + { + "line": "test cases/failing/135 misspelled method kwargs/meson.build:4:11: ERROR: python.extension_module got unknown keyword arguments \"deps\", \"srcs\" (did you mean \"sources\"?)" + } + ] +} diff --git a/test cases/failing/55 bad option argument/test.json b/test cases/failing/55 bad option argument/test.json index 0bfc4b9dca0f..8490888e14ca 100644 --- a/test cases/failing/55 bad option argument/test.json +++ b/test cases/failing/55 bad option argument/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/55 bad option argument/meson_options.txt:1:0: ERROR: string option got unknown keyword arguments \"value_\"" + "line": "test cases/failing/55 bad option argument/meson_options.txt:1:0: ERROR: string option got unknown keyword arguments \"value_\" (did you mean \"value\"?)" } ] } From fd7426f9416e094a0eb1e67ef5f82c3af45cef9d Mon Sep 17 00:00:00 2001 From: Andrew McNulty Date: Fri, 14 Mar 2025 20:18:15 +0100 Subject: [PATCH 467/681] Misspellings: add closest match for function names If the user misspells a meson function then suggest the closest match in the error message. --- mesonbuild/interpreterbase/interpreterbase.py | 4 ++++ .../failing/136 misspelled function name/meson.build | 2 ++ test cases/failing/136 misspelled function name/test.json | 7 +++++++ 3 files changed, 13 insertions(+) create mode 100644 test cases/failing/136 misspelled function name/meson.build create mode 100644 test cases/failing/136 misspelled function name/test.json diff --git a/mesonbuild/interpreterbase/interpreterbase.py b/mesonbuild/interpreterbase/interpreterbase.py index 015fe9caaa52..cfd8bfb5aec7 100644 --- a/mesonbuild/interpreterbase/interpreterbase.py +++ b/mesonbuild/interpreterbase/interpreterbase.py @@ -525,6 +525,10 @@ def function_call(self, node: mparser.FunctionNode) -> T.Optional[InterpreterObj res = func(node, func_args, kwargs) return self._holderify(res) if res is not None else None else: + from difflib import get_close_matches + close_matches = get_close_matches(func_name, self.funcs.keys()) + if close_matches: + raise InvalidCode(f'Unknown function "{func_name}". Did you mean "{close_matches[0]}"?') self.unknown_function_called(func_name) return None diff --git a/test cases/failing/136 misspelled function name/meson.build b/test cases/failing/136 misspelled function name/meson.build new file mode 100644 index 000000000000..6fd887bb9347 --- /dev/null +++ b/test cases/failing/136 misspelled function name/meson.build @@ -0,0 +1,2 @@ +project('136 misspelled function name') +x = executabl('x', 'x.c') diff --git a/test cases/failing/136 misspelled function name/test.json b/test cases/failing/136 misspelled function name/test.json new file mode 100644 index 000000000000..a48d28027c93 --- /dev/null +++ b/test cases/failing/136 misspelled function name/test.json @@ -0,0 +1,7 @@ +{ + "stdout": [ + { + "line": "test cases/failing/136 misspelled function name/meson.build:2:19: ERROR: Unknown function \"executabl\". Did you mean \"executable\"?" + } + ] +} From 19922d70dcdd0d9db2d1c8b3709dffe089dd1ac9 Mon Sep 17 00:00:00 2001 From: Andrew McNulty Date: Fri, 14 Mar 2025 20:59:04 +0100 Subject: [PATCH 468/681] Misspellings: add closest match for object method names If the user misspells an object method name then suggest the closest match in the error message. --- mesonbuild/interpreterbase/baseobjects.py | 8 +++++++- test cases/failing/137 misspelled method name/meson.build | 2 ++ test cases/failing/137 misspelled method name/test.json | 8 ++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 test cases/failing/137 misspelled method name/meson.build create mode 100644 test cases/failing/137 misspelled method name/test.json diff --git a/mesonbuild/interpreterbase/baseobjects.py b/mesonbuild/interpreterbase/baseobjects.py index 4f29aa038843..39f3cebdcec6 100644 --- a/mesonbuild/interpreterbase/baseobjects.py +++ b/mesonbuild/interpreterbase/baseobjects.py @@ -121,7 +121,13 @@ def method_call( if not getattr(method, 'no-second-level-holder-flattening', False): args, kwargs = resolve_second_level_holders(args, kwargs) return method(self, args, kwargs) - raise InvalidCode(f'Unknown method "{method_name}" in object {self} of type {type(self).__name__}.') + + ustr = f'Unknown method "{method_name}" in object {self} of type {type(self).__name__}.' + from difflib import get_close_matches + close_matches = get_close_matches(method_name, self.METHODS) + if close_matches: + ustr += f' Did you mean "{close_matches[0]}"?' + raise InvalidCode(ustr) def operator_call(self, operator: MesonOperator, other: TYPE_var) -> TYPE_var: if operator in self.TRIVIAL_OPERATORS: diff --git a/test cases/failing/137 misspelled method name/meson.build b/test cases/failing/137 misspelled method name/meson.build new file mode 100644 index 000000000000..8832a6a31d63 --- /dev/null +++ b/test cases/failing/137 misspelled method name/meson.build @@ -0,0 +1,2 @@ +project('137 misspelled method name') +version = meson.versoin() diff --git a/test cases/failing/137 misspelled method name/test.json b/test cases/failing/137 misspelled method name/test.json new file mode 100644 index 000000000000..9ce08763379a --- /dev/null +++ b/test cases/failing/137 misspelled method name/test.json @@ -0,0 +1,8 @@ +{ + "stdout": [ + { + "match": "re", + "line": "test cases/failing/137 misspelled method name/meson.build:2:16: ERROR: Unknown method \"versoin\" in object of type MesonMain. Did you mean \"version\"?" + } + ] +} From 81b19eb730f8cc6e65320090c84226a4133353f0 Mon Sep 17 00:00:00 2001 From: Andrew McNulty Date: Fri, 14 Mar 2025 21:55:18 +0100 Subject: [PATCH 469/681] Misspellings: add closest match for module method names As in e0f892c, if the user misspells a module method name then suggest the closest match in the error message. --- mesonbuild/interpreter/interpreterobjects.py | 7 ++++++- .../failing/138 misspelled module method name/meson.build | 3 +++ .../failing/138 misspelled module method name/test.json | 7 +++++++ 3 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 test cases/failing/138 misspelled module method name/meson.build create mode 100644 test cases/failing/138 misspelled module method name/test.json diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index deb347a65d27..c0a0b8410b9d 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -941,7 +941,12 @@ def method_call(self, method_name: str, args: T.List[TYPE_var], kwargs: TYPE_kwa modobj = self.held_object method = modobj.methods.get(method_name) if not method: - raise InvalidCode(f'Unknown method {method_name!r} in object.') + ustr = f'Unknown method "{method_name}" in object.' + from difflib import get_close_matches + close_matches = get_close_matches(method_name, modobj.methods.keys()) + if close_matches: + ustr += f' Did you mean "{close_matches[0]}"?' + raise InvalidCode(ustr) if not getattr(method, 'no-args-flattening', False): args = flatten(args) if not getattr(method, 'no-second-level-holder-flattening', False): diff --git a/test cases/failing/138 misspelled module method name/meson.build b/test cases/failing/138 misspelled module method name/meson.build new file mode 100644 index 000000000000..06c339d2f8d8 --- /dev/null +++ b/test cases/failing/138 misspelled module method name/meson.build @@ -0,0 +1,3 @@ +project('138 misspelled module method name') +py = import('python') +inst = py.find_installlation() diff --git a/test cases/failing/138 misspelled module method name/test.json b/test cases/failing/138 misspelled module method name/test.json new file mode 100644 index 000000000000..35bfae2876c0 --- /dev/null +++ b/test cases/failing/138 misspelled module method name/test.json @@ -0,0 +1,7 @@ +{ + "stdout": [ + { + "line": "test cases/failing/138 misspelled module method name/meson.build:3:10: ERROR: Unknown method \"find_installlation\" in object. Did you mean \"find_installation\"?" + } + ] +} From 0816c4ae4aec7d077332b62b1f8c978bddc627d0 Mon Sep 17 00:00:00 2001 From: Andrew McNulty Date: Fri, 14 Mar 2025 22:15:48 +0100 Subject: [PATCH 470/681] Misspellings: add closest match for variable names If the user misspells a variable name then suggest the closest match. Try to catch as many ways a user could reference a variable: 1. As a regular variable 2. As an argument to get_variable/unset_variable 3. As an fstring identifier 4. As a variable in a subproject --- mesonbuild/interpreter/interpreter.py | 14 ++++++++++++-- mesonbuild/interpreter/interpreterobjects.py | 7 ++++++- mesonbuild/interpreterbase/interpreterbase.py | 14 ++++++++++++-- .../139 misspelled variable name/meson.build | 3 +++ .../failing/139 misspelled variable name/test.json | 7 +++++++ .../meson.build | 3 +++ .../test.json | 7 +++++++ .../meson.build | 3 +++ .../test.json | 7 +++++++ .../meson.build | 4 ++++ .../142 misspelled variable name fstring/test.json | 7 +++++++ .../meson.build | 3 +++ .../subprojects/x/meson.build | 2 ++ .../test.json | 7 +++++++ 14 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 test cases/failing/139 misspelled variable name/meson.build create mode 100644 test cases/failing/139 misspelled variable name/test.json create mode 100644 test cases/failing/140 misspelled variable name get_variable/meson.build create mode 100644 test cases/failing/140 misspelled variable name get_variable/test.json create mode 100644 test cases/failing/141 misspelled variable name unset_variable/meson.build create mode 100644 test cases/failing/141 misspelled variable name unset_variable/test.json create mode 100644 test cases/failing/142 misspelled variable name fstring/meson.build create mode 100644 test cases/failing/142 misspelled variable name fstring/test.json create mode 100644 test cases/failing/143 misspelled variable name subproject.get_variable/meson.build create mode 100644 test cases/failing/143 misspelled variable name subproject.get_variable/subprojects/x/meson.build create mode 100644 test cases/failing/143 misspelled variable name subproject.get_variable/test.json diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index d9caa60b4024..cbe4a7ee0a34 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -3952,7 +3952,12 @@ def func_get_variable(self, node: mparser.BaseNode, args: T.Tuple[T.Union[str, D except KeyError: if fallback is not None: return self._holderify(fallback) - raise InterpreterException(f'Tried to get unknown variable "{varname}".') + ustr = f'Tried to get unknown variable "{varname}".' + from difflib import get_close_matches + close_matches = get_close_matches(varname, self.variables.keys()) + if close_matches: + ustr += f' Did you mean "{close_matches[0]}"?' + raise InterpreterException(ustr) @typed_pos_args('is_variable', str) @noKwargs @@ -3967,7 +3972,12 @@ def func_unset_variable(self, node: mparser.BaseNode, args: T.Tuple[str], kwargs try: del self.variables[varname] except KeyError: - raise InterpreterException(f'Tried to unset unknown variable "{varname}".') + ustr = f'Tried to unset unknown variable "{varname}".' + from difflib import get_close_matches + close_matches = get_close_matches(varname, self.variables.keys()) + if close_matches: + ustr += f' Did you mean "{close_matches[0]}"?' + raise InterpreterException(ustr) @FeatureNew('is_disabler', '0.52.0') @typed_pos_args('is_disabler', object) diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index c0a0b8410b9d..33e6ae66ea72 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -927,7 +927,12 @@ def get_variable(self, args: T.Tuple[str, T.Optional[str]], kwargs: TYPE_kwargs) except KeyError: if fallback is not None: return self.held_object._holderify(fallback) - raise InvalidArguments(f'Requested variable "{varname}" not found.') + ustr = f'Requested variable "{varname}" not found.' + from difflib import get_close_matches + close_matches = get_close_matches(varname, self.held_object.variables.keys()) + if close_matches: + ustr += f' Did you mean "{close_matches[0]}"?' + raise InvalidArguments(ustr) @noKwargs @typed_pos_args('subproject.get_variable', str, optargs=[object]) diff --git a/mesonbuild/interpreterbase/interpreterbase.py b/mesonbuild/interpreterbase/interpreterbase.py index cfd8bfb5aec7..7b517a0fd555 100644 --- a/mesonbuild/interpreterbase/interpreterbase.py +++ b/mesonbuild/interpreterbase/interpreterbase.py @@ -445,7 +445,12 @@ def replace(match: T.Match[str]) -> str: except InvalidArguments as e: raise InvalidArguments(f'f-string: {str(e)}') except KeyError: - raise InvalidCode(f'Identifier "{var}" does not name a variable.') + ustr = f'Identifier "{var}" does not name a variable.' + from difflib import get_close_matches + close_matches = get_close_matches(var, self.variables.keys()) + if close_matches: + ustr += f' Did you mean "{close_matches[0]}"?' + raise InvalidCode(ustr) res = re.sub(r'@([_a-zA-Z][_0-9a-zA-Z]*)@', replace, node.value) return self._holderify(res) @@ -660,7 +665,12 @@ def get_variable(self, varname: str) -> InterpreterObject: return self.builtin[varname] if varname in self.variables: return self.variables[varname] - raise InvalidCode(f'Unknown variable "{varname}".') + ustr = f'Unknown variable name "{varname}".' + from difflib import get_close_matches + close_matches = get_close_matches(varname, self.variables.keys()) + if close_matches: + ustr += f' Did you mean "{close_matches[0]}"?' + raise InvalidCode(ustr) def _load_option_file(self) -> None: from .. import optinterpreter # prevent circular import diff --git a/test cases/failing/139 misspelled variable name/meson.build b/test cases/failing/139 misspelled variable name/meson.build new file mode 100644 index 000000000000..c8f9653f133f --- /dev/null +++ b/test cases/failing/139 misspelled variable name/meson.build @@ -0,0 +1,3 @@ +project('139 misspelled variable name') +version = meson.version() +message('got version: ' + versoin) diff --git a/test cases/failing/139 misspelled variable name/test.json b/test cases/failing/139 misspelled variable name/test.json new file mode 100644 index 000000000000..fa1335ba1728 --- /dev/null +++ b/test cases/failing/139 misspelled variable name/test.json @@ -0,0 +1,7 @@ +{ + "stdout": [ + { + "line": "test cases/failing/139 misspelled variable name/meson.build:3:26: ERROR: Unknown variable name \"versoin\". Did you mean \"version\"?" + } + ] +} diff --git a/test cases/failing/140 misspelled variable name get_variable/meson.build b/test cases/failing/140 misspelled variable name get_variable/meson.build new file mode 100644 index 000000000000..a676a90d0a2f --- /dev/null +++ b/test cases/failing/140 misspelled variable name get_variable/meson.build @@ -0,0 +1,3 @@ +project('140 misspelled variable name get_variable') +hello = 'hello' +hello2 = get_variable('jello') diff --git a/test cases/failing/140 misspelled variable name get_variable/test.json b/test cases/failing/140 misspelled variable name get_variable/test.json new file mode 100644 index 000000000000..ca80826b5b85 --- /dev/null +++ b/test cases/failing/140 misspelled variable name get_variable/test.json @@ -0,0 +1,7 @@ +{ + "stdout": [ + { + "line": "test cases/failing/140 misspelled variable name get_variable/meson.build:3:9: ERROR: Tried to get unknown variable \"jello\". Did you mean \"hello\"?" + } + ] +} diff --git a/test cases/failing/141 misspelled variable name unset_variable/meson.build b/test cases/failing/141 misspelled variable name unset_variable/meson.build new file mode 100644 index 000000000000..982a04142c08 --- /dev/null +++ b/test cases/failing/141 misspelled variable name unset_variable/meson.build @@ -0,0 +1,3 @@ +project('141 misspelled variable name unset_variable') +hello = 'hello' +unset_variable('jello') diff --git a/test cases/failing/141 misspelled variable name unset_variable/test.json b/test cases/failing/141 misspelled variable name unset_variable/test.json new file mode 100644 index 000000000000..5590efe54978 --- /dev/null +++ b/test cases/failing/141 misspelled variable name unset_variable/test.json @@ -0,0 +1,7 @@ +{ + "stdout": [ + { + "line": "test cases/failing/141 misspelled variable name unset_variable/meson.build:3:0: ERROR: Tried to unset unknown variable \"jello\". Did you mean \"hello\"?" + } + ] +} diff --git a/test cases/failing/142 misspelled variable name fstring/meson.build b/test cases/failing/142 misspelled variable name fstring/meson.build new file mode 100644 index 000000000000..51e5d57a5082 --- /dev/null +++ b/test cases/failing/142 misspelled variable name fstring/meson.build @@ -0,0 +1,4 @@ +project('142 missspelled variable name fstring') +hello = 'hello' +v = f'@jello@ world' + diff --git a/test cases/failing/142 misspelled variable name fstring/test.json b/test cases/failing/142 misspelled variable name fstring/test.json new file mode 100644 index 000000000000..89c008e3ddf8 --- /dev/null +++ b/test cases/failing/142 misspelled variable name fstring/test.json @@ -0,0 +1,7 @@ +{ + "stdout": [ + { + "line": "test cases/failing/142 misspelled variable name fstring/meson.build:3:4: ERROR: Identifier \"jello\" does not name a variable. Did you mean \"hello\"?" + } + ] +} diff --git a/test cases/failing/143 misspelled variable name subproject.get_variable/meson.build b/test cases/failing/143 misspelled variable name subproject.get_variable/meson.build new file mode 100644 index 000000000000..f8468d62b55f --- /dev/null +++ b/test cases/failing/143 misspelled variable name subproject.get_variable/meson.build @@ -0,0 +1,3 @@ +project('143 misspelled variable name subproject.get_variable') +x = subproject('x') +hello = x.get_variable('hello') diff --git a/test cases/failing/143 misspelled variable name subproject.get_variable/subprojects/x/meson.build b/test cases/failing/143 misspelled variable name subproject.get_variable/subprojects/x/meson.build new file mode 100644 index 000000000000..4ce059d70500 --- /dev/null +++ b/test cases/failing/143 misspelled variable name subproject.get_variable/subprojects/x/meson.build @@ -0,0 +1,2 @@ +project('x') +jello = 'jello' diff --git a/test cases/failing/143 misspelled variable name subproject.get_variable/test.json b/test cases/failing/143 misspelled variable name subproject.get_variable/test.json new file mode 100644 index 000000000000..ce3f67372d31 --- /dev/null +++ b/test cases/failing/143 misspelled variable name subproject.get_variable/test.json @@ -0,0 +1,7 @@ +{ + "stdout": [ + { + "line": "test cases/failing/143 misspelled variable name subproject.get_variable/meson.build:3:10: ERROR: Requested variable \"hello\" not found. Did you mean \"jello\"?" + } + ] +} From a3f4ccb4c29e173900cf9b71831b8d343c53047e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 10 Jun 2026 12:00:27 +0200 Subject: [PATCH 471/681] Misspellings: add closest match for module names If the user misspells a module name then suggest the closest match, using pkgutil to enumerate the available modules --- mesonbuild/interpreter/interpreter.py | 11 +++++++++-- .../failing/144 misspelled module name/meson.build | 2 ++ .../failing/144 misspelled module name/test.json | 7 +++++++ 3 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 test cases/failing/144 misspelled module name/meson.build create mode 100644 test cases/failing/144 misspelled module name/test.json diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index cbe4a7ee0a34..8d9f921af89a 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -32,7 +32,7 @@ from ..interpreterbase import FeatureNew, FeatureDeprecated, FeatureBroken, FeatureNewKwargs from ..interpreterbase import ObjectHolder, ContextManagerObject from ..interpreterbase import stringifyUserArguments, Feature, FeatureValue -from ..modules import ExtensionModule, ModuleObject, MutableModuleObject, NewExtensionModule, NotFoundExtensionModule +from ..modules import ExtensionModule, ModuleObject, MutableModuleObject, NewExtensionModule, NotFoundExtensionModule, __path__ as modules_path from ..optinterpreter import optname_regex from . import interpreterobjects as OBJ @@ -648,7 +648,14 @@ def func_import(self, node: mparser.BaseNode, args: T.Tuple[str], mlog.debug(line) if required: - raise InvalidArguments(f'Module "{modname}" does not exist') + ustr = f'Module "{modname}" does not exist.' + from difflib import get_close_matches + from pkgutil import iter_modules + modnames = [mod.name for mod in iter_modules(modules_path) if not mod.name.startswith('_')] + close_matches = get_close_matches(modname, modnames) + if close_matches: + ustr += f' Did you mean "{close_matches[0]}"?' + raise InvalidArguments(ustr) ext_module = NotFoundExtensionModule(real_modname) else: ext_module = module.initialize(self) diff --git a/test cases/failing/144 misspelled module name/meson.build b/test cases/failing/144 misspelled module name/meson.build new file mode 100644 index 000000000000..bfc78ad26916 --- /dev/null +++ b/test cases/failing/144 misspelled module name/meson.build @@ -0,0 +1,2 @@ +project('144 misspelled module name') +py = import('pythin') diff --git a/test cases/failing/144 misspelled module name/test.json b/test cases/failing/144 misspelled module name/test.json new file mode 100644 index 000000000000..513ef1459a73 --- /dev/null +++ b/test cases/failing/144 misspelled module name/test.json @@ -0,0 +1,7 @@ +{ + "stdout": [ + { + "line": "test cases/failing/144 misspelled module name/meson.build:2:5: ERROR: Module \"pythin\" does not exist. Did you mean \"python\"?" + } + ] + } From d648f82e8a46d26188753bbda6bf7e2caf2cbf57 Mon Sep 17 00:00:00 2001 From: Andrew McNulty Date: Tue, 25 Mar 2025 23:46:49 +0100 Subject: [PATCH 472/681] Misspellings: add closest match for subproject names If the user misspells a subproject name then suggest the closest match. --- mesonbuild/wrap/wrap.py | 7 ++++++- .../failing/145 misspelled subproject name/meson.build | 2 ++ .../subprojects/help/meson.build | 1 + .../failing/145 misspelled subproject name/test.json | 7 +++++++ 4 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 test cases/failing/145 misspelled subproject name/meson.build create mode 100644 test cases/failing/145 misspelled subproject name/subprojects/help/meson.build create mode 100644 test cases/failing/145 misspelled subproject name/test.json diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py index 008b1efe1f43..df16b416b79b 100644 --- a/mesonbuild/wrap/wrap.py +++ b/mesonbuild/wrap/wrap.py @@ -520,7 +520,12 @@ def _resolve(self, packagename: str, force_method: T.Optional[Method] = None) -> if wrap is None: wrap = self.get_from_wrapdb(packagename) if wrap is None: - raise WrapNotFoundException(f'Neither a subproject directory nor a {packagename}.wrap file was found.') + ustr = f'Neither a subproject directory nor a {packagename}.wrap file was found.' + from difflib import get_close_matches + close_matches = get_close_matches(packagename, self.wraps.keys()) + if close_matches: + ustr += f' Did you mean "{close_matches[0]}"?' + raise WrapNotFoundException(ustr) self.wrap = wrap self.directory = self.wrap.directory self.dirname = os.path.join(self.wrap.subprojects_dir, self.wrap.directory) diff --git a/test cases/failing/145 misspelled subproject name/meson.build b/test cases/failing/145 misspelled subproject name/meson.build new file mode 100644 index 000000000000..45aec2f6e475 --- /dev/null +++ b/test cases/failing/145 misspelled subproject name/meson.build @@ -0,0 +1,2 @@ +project('145 misspelled subproject name') +sub = subproject('welp') diff --git a/test cases/failing/145 misspelled subproject name/subprojects/help/meson.build b/test cases/failing/145 misspelled subproject name/subprojects/help/meson.build new file mode 100644 index 000000000000..be044c09b06c --- /dev/null +++ b/test cases/failing/145 misspelled subproject name/subprojects/help/meson.build @@ -0,0 +1 @@ +project('help') diff --git a/test cases/failing/145 misspelled subproject name/test.json b/test cases/failing/145 misspelled subproject name/test.json new file mode 100644 index 000000000000..b0ece7cbb213 --- /dev/null +++ b/test cases/failing/145 misspelled subproject name/test.json @@ -0,0 +1,7 @@ +{ + "stdout": [ + { + "line": "test cases/failing/145 misspelled subproject name/meson.build:2:6: ERROR: Neither a subproject directory nor a welp.wrap file was found. Did you mean \"help\"?" + } + ] + } From 4dee8ca1aa50399b916868e4cf6d50b6fb4a8ad0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 12 Jun 2026 12:22:57 +0200 Subject: [PATCH 473/681] wraptool: support installing wraps for multiple projects Previously, `meson wrap install` took only one argument, so installing multiple projects needed multiple invocations, which is not too convenient. So add support for installing multiple projects. --- docs/markdown/Using-wraptool.md | 7 ++++- .../snippets/meson_wrap_install_multiple.md | 6 ++++ mesonbuild/wrap/wraptool.py | 30 ++++++++++++++----- 3 files changed, 34 insertions(+), 9 deletions(-) create mode 100644 docs/markdown/snippets/meson_wrap_install_multiple.md diff --git a/docs/markdown/Using-wraptool.md b/docs/markdown/Using-wraptool.md index edbceaadaec3..5268ac38cc2b 100644 --- a/docs/markdown/Using-wraptool.md +++ b/docs/markdown/Using-wraptool.md @@ -64,7 +64,12 @@ the install command. Installed libjpeg branch 9a revision 2 Now you can issue a `subproject('libjpeg')` in your `meson.build` file -to use it. +to use it. Since *1.12.0* you may also specify multiple dependencies to install. + + $ meson wrap install cairo pango harfbuzz + Installed cairo version 1.18.0 revision 1 + Installed pango version 1.51.0 revision 2 + Installed harfbuzz version 8.3.1 revision 1 To check if your projects are up to date you can issue the `status` command. diff --git a/docs/markdown/snippets/meson_wrap_install_multiple.md b/docs/markdown/snippets/meson_wrap_install_multiple.md new file mode 100644 index 000000000000..4d226c3a7a69 --- /dev/null +++ b/docs/markdown/snippets/meson_wrap_install_multiple.md @@ -0,0 +1,6 @@ +## Multiple positional arguments for `meson wrap install` + +Previously `meson wrap install` only accepted a single wrap name to install. +This limitation has now been lifted, and it is now possible to specify +multiple wrap names for installation. The exit code will be non-zero +if *any* of the wraps fail to install. diff --git a/mesonbuild/wrap/wraptool.py b/mesonbuild/wrap/wraptool.py index 85175114e289..925b105e0e72 100644 --- a/mesonbuild/wrap/wraptool.py +++ b/mesonbuild/wrap/wraptool.py @@ -36,7 +36,7 @@ def add_arguments(parser: 'argparse.ArgumentParser') -> None: p = subparsers.add_parser('install', help='install the specified project') p.add_argument('--allow-insecure', default=False, action='store_true', help='Allow insecure server connections.') - p.add_argument('name') + p.add_argument('name', nargs='+') p.set_defaults(wrap_func=install) p = msubprojects.add_wrap_update_parser(subparsers) @@ -87,22 +87,36 @@ def get_latest_version(name: str, allow_insecure: bool) -> T.Tuple[str, str]: version, revision = latest_version.rsplit('-', 1) return version, revision -def install(options: 'argparse.Namespace') -> None: - name = options.name +def install_one(name: str, allow_insecure: bool) -> None: subproject_dir_name = mesonlib.get_subproject_dir() - if subproject_dir_name is None or not os.path.isdir(subproject_dir_name): - raise SystemExit('Subprojects dir not found. Run this script in your source root directory.') if os.path.isdir(os.path.join(subproject_dir_name, name)): raise SystemExit('Subproject directory for this project already exists.') wrapfile = os.path.join(subproject_dir_name, name + '.wrap') if os.path.exists(wrapfile): - raise SystemExit('Wrap file already exists.') - (version, revision) = get_latest_version(name, options.allow_insecure) - url = open_wrapdburl(f'https://wrapdb.mesonbuild.com/v2/{name}_{version}-{revision}/{name}.wrap', options.allow_insecure, True, True) + raise SystemExit(f'Wrap file for {name} already exists.') + (version, revision) = get_latest_version(name, allow_insecure) + url = open_wrapdburl(f'https://wrapdb.mesonbuild.com/v2/{name}_{version}-{revision}/{name}.wrap', allow_insecure, True, True) with open(wrapfile, 'wb') as f: f.write(read_and_decompress(url)) print(f'Installed {name} version {version} revision {revision}') +def install(options: 'argparse.Namespace') -> None: + subproject_dir_name = mesonlib.get_subproject_dir() + if subproject_dir_name is None or not os.path.isdir(subproject_dir_name): + raise SystemExit('Subprojects dir not found. Run this script in your source root directory.') + + failed = 0 + + for name in dict.fromkeys(options.name): + try: + install_one(name, options.allow_insecure) + except WrapException as e: + print(str(e), file=sys.stderr) + failed += 1 + + if failed > 0: + raise SystemExit(1) + def get_current_version(wrapfile: str) -> T.Tuple[str, str, str, str, T.Optional[str]]: cp = configparser.ConfigParser(interpolation=None) cp.read(wrapfile) From 80bc12f4eca4df9b1414edd9cad5443fe5360e27 Mon Sep 17 00:00:00 2001 From: Fiona Behrens Date: Sat, 13 Jun 2026 12:42:13 +0000 Subject: [PATCH 474/681] mtest: allow ignore reasons in rust unittests Allow reasons to be stated to ignore rust unittests. This allows the rust unittest attribute `#[ignore = reason]` to not error the test but skip the result of the subtest. --- mesonbuild/mtest.py | 2 +- test cases/rust/9 unit tests/test.rs | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index 6ccbe5cb2b69..825f7f559f6a 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -1209,7 +1209,7 @@ async def parse(self, harness: 'TestHarness', lines: T.AsyncIterator[str]) -> No def parse_res(n: int, name: str, result: str) -> TAPParser.Test: if result == 'ok': return TAPParser.Test(n, name, TestResult.OK, None) - elif result == 'ignored': + elif result.startswith('ignored'): return TAPParser.Test(n, name, TestResult.SKIP, None) elif result == 'FAILED': return TAPParser.Test(n, name, TestResult.FAIL, None) diff --git a/test cases/rust/9 unit tests/test.rs b/test cases/rust/9 unit tests/test.rs index 0225be55b9b0..098b3f8a63d0 100644 --- a/test cases/rust/9 unit tests/test.rs +++ b/test cases/rust/9 unit tests/test.rs @@ -21,4 +21,10 @@ mod tests { fn test_add_intentional_fail2() { assert_eq!(add(1, 7), 5); } + + #[test] + #[ignore = "Ignore this with a reason"] + fn test_add_intentional_fail3() { + assert_eq!(add(1, 9), 7) + } } From 185e86c3bf08ad40ca39f325f90d71e96e592a56 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 11 Jun 2026 16:17:24 +0200 Subject: [PATCH 475/681] remove defensive asserts on arguments These should now be caught by mypy. There is little benefit in keeping them. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/backends.py | 2 -- mesonbuild/backend/xcodebackend.py | 2 -- mesonbuild/interpreterbase/interpreterbase.py | 7 ------- mesonbuild/modules/cmake.py | 1 - mesonbuild/options.py | 1 - 5 files changed, 13 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 3b3389dc8367..1575bb409b5d 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -326,7 +326,6 @@ def get_target_filename_abs(self, target: build.AnyTargetType) -> str: return os.path.join(self.environment.get_build_dir(), self.get_target_filename(target)) def get_target_debug_filename(self, target: build.BuildTarget) -> T.Optional[str]: - assert isinstance(target, build.BuildTarget), target if target.get_debug_filename(): debug_filename = target.get_debug_filename() return os.path.join(self.get_target_dir(target), debug_filename) @@ -334,7 +333,6 @@ def get_target_debug_filename(self, target: build.BuildTarget) -> T.Optional[str return None def get_target_debug_filename_abs(self, target: build.BuildTarget) -> T.Optional[str]: - assert isinstance(target, build.BuildTarget), target if not target.get_debug_filename(): return None return os.path.join(self.environment.get_build_dir(), self.get_target_debug_filename(target)) diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index 48a3a719d4d5..ecc1d4b77fd5 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -145,7 +145,6 @@ def __init__(self, value: str, comment: str = ''): class PbxComment: def __init__(self, text: str): - assert isinstance(text, str) assert '/*' not in text self.text = f'/* {text} */' @@ -668,7 +667,6 @@ def generate_target_file_maps_impl( def generate_build_file_maps(self) -> None: for buildfile in self.build.def_files: - assert isinstance(buildfile, str) self.buildfile_ids[buildfile] = self.gen_id() self.fileref_ids[buildfile] = self.gen_id() diff --git a/mesonbuild/interpreterbase/interpreterbase.py b/mesonbuild/interpreterbase/interpreterbase.py index 7b517a0fd555..3899858f5d58 100644 --- a/mesonbuild/interpreterbase/interpreterbase.py +++ b/mesonbuild/interpreterbase/interpreterbase.py @@ -296,7 +296,6 @@ def evaluate_notstatement(self, cur: mparser.NotNode) -> InterpreterObject: return self._holderify(v.operator_call(MesonOperator.NOT, None)) def evaluate_if(self, node: mparser.IfClauseNode) -> T.Optional[Disabler]: - assert isinstance(node, mparser.IfClauseNode) for i in node.ifs: # Reset self.tmp_meson_version to know if it gets set during this # statement evaluation. @@ -415,7 +414,6 @@ def evaluate_arithmeticstatement(self, cur: mparser.ArithmeticNode) -> Interpret return self._holderify(res) def evaluate_ternary(self, node: mparser.TernaryNode) -> T.Optional[InterpreterObject]: - assert isinstance(node, mparser.TernaryNode) result = self.evaluate_statement(node.condition) if result is None: raise mesonlib.MesonException('Cannot use a void statement as condition for ternary operator.') @@ -456,7 +454,6 @@ def replace(match: T.Match[str]) -> str: return self._holderify(res) def evaluate_foreach(self, node: mparser.ForeachClauseNode) -> None: - assert isinstance(node, mparser.ForeachClauseNode) items = self.evaluate_statement(node.items) if not isinstance(items, IterableObject): raise InvalidArguments('Items of foreach loop do not support iterating') @@ -485,7 +482,6 @@ def evaluate_foreach(self, node: mparser.ForeachClauseNode) -> None: break def evaluate_plusassign(self, node: mparser.PlusAssignmentNode) -> None: - assert isinstance(node, mparser.PlusAssignmentNode) varname = node.var_name.value addition = self.evaluate_statement(node.value) if addition is None: @@ -499,7 +495,6 @@ def evaluate_plusassign(self, node: mparser.PlusAssignmentNode) -> None: self.set_variable(varname, new_value) def evaluate_indexing(self, node: mparser.IndexNode) -> InterpreterObject: - assert isinstance(node, mparser.IndexNode) iobject = self.evaluate_statement(node.iobject) if iobject is None: raise InterpreterException('Tried to evaluate indexing on void.') @@ -593,7 +588,6 @@ def reduce_arguments( T.List[InterpreterObject], T.Dict[str, InterpreterObject] ]: - assert isinstance(args, mparser.ArgumentNode) if args.incorrect_order(): raise InvalidArguments('All keyword arguments must be after positional arguments.') self.argument_depth += 1 @@ -630,7 +624,6 @@ def expand_default_kwargs(self, kwargs: T.Dict[str, T.Optional[InterpreterObject return kwargs def assignment(self, node: mparser.AssignmentNode) -> None: - assert isinstance(node, mparser.AssignmentNode) if self.argument_depth != 0: raise InvalidArguments(textwrap.dedent('''\ Tried to assign values inside an argument list. diff --git a/mesonbuild/modules/cmake.py b/mesonbuild/modules/cmake.py index 682b5dd3727a..d433697e0412 100644 --- a/mesonbuild/modules/cmake.py +++ b/mesonbuild/modules/cmake.py @@ -104,7 +104,6 @@ class DependencyKW(TypedDict): class CMakeSubproject(ModuleObject): def __init__(self, subp: SubprojectHolder): - assert isinstance(subp, SubprojectHolder) assert subp.cm_interpreter is not None super().__init__() self.subp = subp diff --git a/mesonbuild/options.py b/mesonbuild/options.py index c16e28c61967..5ad5992d3040 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -850,7 +850,6 @@ def resolve_option(self, key: 'T.Union[OptionKey, str]') -> AnyOptionType: return potential def get_option_and_value_for(self, key: OptionKey) -> T.Tuple[AnyOptionType, ElementaryOptionValues]: - assert isinstance(key, OptionKey) key = self.ensure_and_validate_key(key) option_object = self.resolve_option(key) computed_value = option_object.value From 61b9f19aaf90f237f80c3fdf309e9d4028fae458 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 11 Jun 2026 15:58:44 +0200 Subject: [PATCH 476/681] build: remove dead function Signed-off-by: Paolo Bonzini --- mesonbuild/build.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 8f20c03c9f95..8746a03a4453 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -1008,15 +1008,6 @@ def process_sourcelist(self, sources: T.Iterable['SourceOutputs']) -> None: elif isinstance(s, (CustomTarget, CustomTargetIndex, GeneratedList)): self.generated.append(s) - @staticmethod - def can_compile_remove_sources(compiler: 'Compiler', sources: T.List['FileOrString']) -> bool: - removed = False - for s in sources[:]: - if compiler.can_compile(s): - sources.remove(s) - removed = True - return removed - def process_compilers_late(self) -> None: """Processes additional compilers after kwargs have been evaluated. From a8e60b041b9b6e27f7260988f25151a818eaf180 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 5 Jun 2026 19:18:06 +0200 Subject: [PATCH 477/681] dependencies: remove bogus StructuredSources annotation structured_sources is not allowed in declare_dependency()'s sources argument. Remove it, together with all of its ramifications. Signed-off-by: Paolo Bonzini --- mesonbuild/dependencies/base.py | 8 ++++---- mesonbuild/interpreter/interpreter.py | 2 +- mesonbuild/modules/hotdoc.py | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index 51f5822233ca..8d276268ace8 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -29,7 +29,7 @@ from ..interpreterbase import FeatureCheckBase from ..build import ( CustomTarget, IncludeDirs, CustomTargetIndex, LinkableTargetTypes, - StaticLibrary, StructuredSources, ExtractedObjects, GeneratedTypes + StaticLibrary, ExtractedObjects, GeneratedTypes ) from ..interpreter.type_checking import PkgConfigDefineType @@ -148,7 +148,7 @@ def __init__(self, kwargs: DependencyObjectKWs) -> None: # Raw -L and -l arguments without manual library searching # If None, self.link_args will be used self.raw_link_args: T.Optional[T.List[str]] = None - self.sources: T.List[T.Union[mesonlib.File, GeneratedTypes, 'StructuredSources']] = [] + self.sources: T.List[T.Union[mesonlib.File, GeneratedTypes]] = [] self.extra_files: T.List[mesonlib.File] = [] self.include_type = kwargs.get('include_type', 'preserve') self.ext_deps: T.List[Dependency] = [] @@ -220,7 +220,7 @@ def get_all_link_args(self) -> T.List[str]: def found(self) -> bool: return self.is_found - def get_sources(self) -> T.List[T.Union[mesonlib.File, GeneratedTypes, 'StructuredSources']]: + def get_sources(self) -> T.List[T.Union[mesonlib.File, GeneratedTypes]]: """Source files that need to be added to the target. As an example, gtest-all.cc when using GTest.""" return self.sources @@ -311,7 +311,7 @@ def __init__(self, version: str, incdirs: T.Optional[T.List['IncludeDirs']] = No link_args: T.Optional[T.List[str]] = None, libraries: T.Optional[T.List[LinkableTargetTypes]] = None, whole_libraries: T.Optional[T.List[T.Union[StaticLibrary, CustomTarget, CustomTargetIndex]]] = None, - sources: T.Optional[T.Sequence[T.Union[mesonlib.File, GeneratedTypes, StructuredSources]]] = None, + sources: T.Optional[T.Sequence[T.Union[mesonlib.File, GeneratedTypes]]] = None, extra_files: T.Optional[T.Sequence[mesonlib.File]] = None, ext_deps: T.Optional[T.List[Dependency]] = None, variables: T.Optional[T.Dict[str, str]] = None, d_module_versions: T.Optional[T.List[T.Union[str, int]]] = None, diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 8d9f921af89a..2035d65b61c2 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -489,7 +489,7 @@ def append_holder_map(self, held_type: T.Type[mesonlib.HoldableObject], holder_t held_type: holder_type }) - def process_new_values(self, invalues: list[TYPE_var | ExecutableSerialisation] | list[build.GeneratedTypes | mesonlib.File | build.StructuredSources]) -> None: + def process_new_values(self, invalues: list[TYPE_var | ExecutableSerialisation] | list[build.GeneratedTypes | mesonlib.File]) -> None: for v in invalues: if isinstance(v, ObjectHolder): raise InterpreterException('Modules must not return ObjectHolders') diff --git a/mesonbuild/modules/hotdoc.py b/mesonbuild/modules/hotdoc.py index 4a3ebdd34c13..08b033753fc9 100644 --- a/mesonbuild/modules/hotdoc.py +++ b/mesonbuild/modules/hotdoc.py @@ -176,8 +176,8 @@ def process_gi_c_source_roots(self) -> None: self.cmd += ['--gi-c-source-roots'] + value - def process_dependencies(self, deps: T.Sequence[TargetDepends | Dependency | File | build.ExtractedObjects | build.StructuredSources]) -> T.List[str]: - # build.StructuredSources and build.ExtractedObjects shouldn't actually + def process_dependencies(self, deps: T.Sequence[TargetDepends | Dependency | File | build.ExtractedObjects]) -> T.List[str]: + # build.ExtractedObjects shouldn't actually # happen here, but we get them from Dependency. cflags = set() for dep in mesonlib.listify(ensure_list(deps)): From 4eaa4a5e22230df1fb05e18003d37c9a0f2742e2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 11 Jun 2026 13:20:56 +0200 Subject: [PATCH 478/681] interpreter: narrow input to check_for_jar_sources Jar sources, like all other sources, do not include build targets. In fact they are not handled in the body. Signed-off-by: Paolo Bonzini --- mesonbuild/interpreter/interpreter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 2035d65b61c2..fb5cd863e76d 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -3921,7 +3921,7 @@ def add_stdlib_info(self, target: build.BuildTarget) -> None: if dep: target.add_deps([dep]) - def check_for_jar_sources(self, sources: T.Union[T.List[str], T.Sequence[T.Union[mesonlib.File, build.BuildTargetTypes, build.GeneratedList, build.StructuredSources]]], + def check_for_jar_sources(self, sources: T.Union[T.List[str], T.Sequence[T.Union[mesonlib.File, build.GeneratedTypes, build.StructuredSources]]], targetclass: T.Type[build.BuildTarget]) -> None: for s in sources: if isinstance(s, (str, mesonlib.File)) and compilers.is_java(s): From 66d84abdd5b8663cbaed5b31840d7cf222d176ca Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 10 Jun 2026 22:34:23 +0200 Subject: [PATCH 479/681] backends: split caching part of get_target_dir CustomTargetIndex is not hashable and therefore should not be an argument to a lru_cached function. Extract the conversion from AnyTargetType to Target to a separate function. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/backends.py | 8 +++++++- mesonbuild/backend/xcodebackend.py | 3 +-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 1575bb409b5d..2098e8c643d5 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -380,7 +380,7 @@ def get_target_filename_for_linking(self, target: build.AnyTargetType) -> T.Opti raise AssertionError(f'BUG: Tried to link to {target!r} which is not linkable') @lru_cache(maxsize=None) - def get_target_dir(self, target: build.AnyTargetType) -> str: + def get_target_dir_cached(self, target: build.Target) -> str: if isinstance(target, build.RunTarget): # this produces no output, only a dummy top-level name dirname = '' @@ -393,6 +393,12 @@ def get_target_dir(self, target: build.AnyTargetType) -> str: dirname = os.path.join(dirname, build_subdir) return dirname + def get_target_dir(self, target: build.AnyTargetType) -> str: + if isinstance(target, build.CustomTargetIndex): + return self.get_target_dir_cached(target.target) + else: + return self.get_target_dir_cached(target) + def get_target_dir_relative_to(self, t: T.Union[build.Target, build.CustomTargetIndex], o: T.Union[build.Target, build.CustomTargetIndex], diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index ecc1d4b77fd5..4d9b681f2aaf 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -304,9 +304,8 @@ def gen_id(self) -> str: return str(uuid.uuid4()).upper().replace('-', '')[:24] @functools.lru_cache(maxsize=None) - def get_target_dir(self, target: build.AnyTargetType) -> str: + def get_target_dir_cached(self, target: build.Target) -> str: dirname = os.path.join(target.get_subdir(), T.cast('str', self.environment.coredata.optstore.get_value_for(OptionKey('buildtype')))) - #os.makedirs(os.path.join(self.environment.get_build_dir(), dirname), exist_ok=True) return dirname def get_custom_target_output_dir(self, target: build.AnyTargetType) -> str: From 995d4311c09104cb92df7c89e92ea4c2cc0d8bc5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 11 Jun 2026 08:03:53 +0200 Subject: [PATCH 480/681] build: remove str from ObjectTypes Remove str from build_target's objects argument before it reaches build.py. This is a straightforward source_strings_to_files transformation. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/backends.py | 6 +----- mesonbuild/build.py | 4 ++-- mesonbuild/interpreter/interpreter.py | 9 ++++++++- mesonbuild/interpreter/kwargs.py | 3 ++- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 2098e8c643d5..4ba8b501a946 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -507,11 +507,7 @@ def _flatten_object_list(self, target: build.BuildTarget, obj_list: T.List[str] = [] deps: T.List[build.BuildTarget] = [] for obj in objects: - if isinstance(obj, str): - o = os.path.join(proj_dir_to_build_root, - self.build_to_src, target.get_subdir(), obj) - obj_list.append(o) - elif isinstance(obj, mesonlib.File): + if isinstance(obj, mesonlib.File): if obj.is_built: o = os.path.join(proj_dir_to_build_root, obj.rel_to_builddir(self.build_to_src)) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 8746a03a4453..ddae54aa2ab4 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -58,7 +58,7 @@ LinkableTargetTypes: TypeAlias = T.Union['SharedLibrary', 'StaticLibrary', 'CustomTarget', 'CustomTargetIndex', 'Executable'] BuildTargetTypes: TypeAlias = T.Union['BuildTarget', 'CustomTarget', 'CustomTargetIndex'] StaticTargetTypes: TypeAlias = T.Union['StaticLibrary', 'CustomTarget', 'CustomTargetIndex'] - ObjectTypes: TypeAlias = T.Union[str, 'File', 'ExtractedObjects', 'GeneratedTypes'] + ObjectTypes: TypeAlias = T.Union['File', 'ExtractedObjects', 'GeneratedTypes'] AnyTargetType: TypeAlias = T.Union['Target', 'CustomTargetIndex'] RustCrateType: TypeAlias = Literal['bin', 'lib', 'rlib', 'dylib', 'cdylib', 'staticlib', 'proc-macro'] _LibraryType: TypeAlias = Literal['auto', 'shared', 'static'] @@ -976,7 +976,7 @@ def process_objectlist(self, objects: T.List[ObjectTypes]) -> None: assert isinstance(objects, list) deprecated_non_objects = [] for s in objects: - if isinstance(s, (str, File, ExtractedObjects)): + if isinstance(s, (File, ExtractedObjects)): self.objects.append(s) if not isinstance(s, ExtractedObjects) and not is_object(s): deprecated_non_objects.append(s) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index fb5cd863e76d..2d32975711ec 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -3247,6 +3247,10 @@ def source_strings_to_files(self, sources: list[T.Union[mesonlib.FileOrString, b @T.overload def source_strings_to_files(self, sources: list[kwtypes.CustomTargetInputs]) -> list['CustomTargetSources']: ... # type: ignore[overload-overlap] + @T.overload + def source_strings_to_files(self, sources: list[kwtypes.BuildTargetObjects], # type: ignore[overload-overlap] + ) -> list[T.Union[build.ObjectTypes]]: ... + @T.overload def source_strings_to_files(self, sources: list[T.Union[mesonlib.FileOrString, build.BuildTargetTypes, build.BothLibraries, build.ExtractedObjects, build.GeneratedTypes]], # type: ignore[overload-overlap] ) -> list[T.Union[mesonlib.File, build.BuildTargetTypes, build.BothLibraries, build.ExtractedObjects, build.GeneratedTypes]]: ... @@ -3269,6 +3273,7 @@ def source_strings_to_files(self, list[mesonlib.File | str | build.GeneratedTypes], list[kwtypes.CustomTargetInputs], list[mesonlib.File | str | build.BuildTargetTypes | build.BothLibraries | build.ExtractedObjects | build.GeneratedTypes], + list[kwtypes.BuildTargetObjects], list[mesonlib.File | str | build.GeneratedTypes | build.StructuredSources], list[SourceInputs], list[SourcesVarargsType], @@ -3277,6 +3282,7 @@ def source_strings_to_files(self, list[mesonlib.File | build.BuildTargetTypes], list[mesonlib.File | build.GeneratedTypes], list[mesonlib.File | build.BuildTargetTypes | build.BothLibraries | build.ExtractedObjects | build.GeneratedTypes], + list[build.ObjectTypes], list[mesonlib.File | build.GeneratedTypes | build.StructuredSources], list[CustomTargetSources], list[SourceOutputs]]: @@ -3843,7 +3849,8 @@ def build_target(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargs # backwards compatibility anyway sources = self.source_strings_to_files([ s for s in raw_sources if not isinstance(s, (build.BuildTarget, build.ExtractedObjects))]) - objs = kwargs['objects'] + + objs = self.source_strings_to_files(kwargs['objects']) srcs: T.List[SourceOutputs] = [] struct: T.Optional[build.StructuredSources] = build.StructuredSources() diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index cfabcd67b46e..7f18edf847df 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -25,6 +25,7 @@ TargetDepends = T.Union[build.CustomTarget, build.CustomTargetIndex, build.BuildTarget, build.GeneratedList, Program] CustomTargetInputs = T.Union[str, build.BuildTarget, build.GeneratedTypes, build.ExtractedObjects, Program, File] +BuildTargetObjects = T.Union[str, File, build.ExtractedObjects, build.GeneratedTypes] RustAbi = Literal['rust', 'c'] class NativeKW(TypedDict): @@ -384,7 +385,7 @@ class BaseBuildTarget(TypedDict): name_prefix: T.Optional[str] name_suffix: T.Optional[str] native: MachineChoice - objects: T.List[build.ObjectTypes] + objects: T.List[BuildTargetObjects] override_options: T.Dict[str, options.ElementaryOptionValues] depend_files: NotRequired[T.List[File]] resources: T.List[str] From 96948af1211e0d7a9816709c351748bbf272c9ff Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 11 Jun 2026 08:19:36 +0200 Subject: [PATCH 481/681] build: remove GeneratedTypes from ObjectTypes Generated objects are moved to self.generated by process_objectlist(), so they are not part of the objects list. Signed-off-by: Paolo Bonzini --- mesonbuild/build.py | 16 ++++++++-------- mesonbuild/interpreter/interpreter.py | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index ddae54aa2ab4..6c8b6718aad6 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -58,7 +58,7 @@ LinkableTargetTypes: TypeAlias = T.Union['SharedLibrary', 'StaticLibrary', 'CustomTarget', 'CustomTargetIndex', 'Executable'] BuildTargetTypes: TypeAlias = T.Union['BuildTarget', 'CustomTarget', 'CustomTargetIndex'] StaticTargetTypes: TypeAlias = T.Union['StaticLibrary', 'CustomTarget', 'CustomTargetIndex'] - ObjectTypes: TypeAlias = T.Union['File', 'ExtractedObjects', 'GeneratedTypes'] + ObjectTypes: TypeAlias = T.Union['File', 'ExtractedObjects'] AnyTargetType: TypeAlias = T.Union['Target', 'CustomTargetIndex'] RustCrateType: TypeAlias = Literal['bin', 'lib', 'rlib', 'dylib', 'cdylib', 'staticlib', 'proc-macro'] _LibraryType: TypeAlias = Literal['auto', 'shared', 'static'] @@ -748,7 +748,7 @@ def __init__( for_machine: MachineChoice, sources: T.List['SourceOutputs'], structured_sources: T.Optional[StructuredSources], - objects: T.List[ObjectTypes], + objects: T.Sequence[ObjectTypes | GeneratedTypes], environment: Environment, compilers: CompilerDict, kwargs: BuildTargetKeywordArguments): @@ -972,7 +972,7 @@ def validate_install(self) -> None: else: mlog.warning('Installing target build for the build machine. This will fail in a cross build.') - def process_objectlist(self, objects: T.List[ObjectTypes]) -> None: + def process_objectlist(self, objects: T.Iterable[ObjectTypes | GeneratedTypes]) -> None: assert isinstance(objects, list) deprecated_non_objects = [] for s in objects: @@ -2164,7 +2164,7 @@ def __init__( for_machine: MachineChoice, sources: T.List['SourceOutputs'], structured_sources: T.Optional[StructuredSources], - objects: T.List[ObjectTypes], + objects: T.Sequence[ObjectTypes | GeneratedTypes], environment: Environment, compilers: CompilerDict, kwargs: ExecutableKeywordArguments): @@ -2297,7 +2297,7 @@ def __init__( for_machine: MachineChoice, sources: T.List['SourceOutputs'], structured_sources: T.Optional[StructuredSources], - objects: T.List[ObjectTypes], + objects: T.Sequence[ObjectTypes | GeneratedTypes], environment: Environment, compilers: CompilerDict, kwargs: StaticLibraryKeywordArguments): @@ -2479,7 +2479,7 @@ def __init__( for_machine: MachineChoice, sources: T.List['SourceOutputs'], structured_sources: T.Optional[StructuredSources], - objects: T.List[ObjectTypes], + objects: T.Sequence[ObjectTypes | GeneratedTypes], environment: Environment, compilers: CompilerDict, kwargs: SharedLibraryKeywordArguments): @@ -2815,7 +2815,7 @@ def __init__( for_machine: MachineChoice, sources: T.List['SourceOutputs'], structured_sources: T.Optional[StructuredSources], - objects: T.List[ObjectTypes], + objects: T.Sequence[ObjectTypes | GeneratedTypes], environment: Environment, compilers: CompilerDict, kwargs: SharedModuleKeywordArguments): @@ -3307,7 +3307,7 @@ class Jar(BuildTarget): def __init__(self, name: str, subdir: str, subproject: SubProject, for_machine: MachineChoice, sources: T.List[SourceOutputs], structured_sources: T.Optional['StructuredSources'], - objects: T.List[ObjectTypes], environment: Environment, compilers: CompilerDict, + objects: T.Sequence[ObjectTypes | GeneratedTypes], environment: Environment, compilers: CompilerDict, kwargs: JarKeywordArguments): super().__init__(name, subdir, subproject, for_machine, sources, structured_sources, objects, environment, compilers, kwargs) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 2d32975711ec..a7082c97b289 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -3249,7 +3249,7 @@ def source_strings_to_files(self, sources: list[kwtypes.CustomTargetInputs]) -> @T.overload def source_strings_to_files(self, sources: list[kwtypes.BuildTargetObjects], # type: ignore[overload-overlap] - ) -> list[T.Union[build.ObjectTypes]]: ... + ) -> list[T.Union[build.ObjectTypes, build.GeneratedTypes]]: ... @T.overload def source_strings_to_files(self, sources: list[T.Union[mesonlib.FileOrString, build.BuildTargetTypes, build.BothLibraries, build.ExtractedObjects, build.GeneratedTypes]], # type: ignore[overload-overlap] @@ -3282,7 +3282,7 @@ def source_strings_to_files(self, list[mesonlib.File | build.BuildTargetTypes], list[mesonlib.File | build.GeneratedTypes], list[mesonlib.File | build.BuildTargetTypes | build.BothLibraries | build.ExtractedObjects | build.GeneratedTypes], - list[build.ObjectTypes], + list[build.ObjectTypes | build.GeneratedTypes], list[mesonlib.File | build.GeneratedTypes | build.StructuredSources], list[CustomTargetSources], list[SourceOutputs]]: From d710074c5aaf4652f4258f12b713a9d3d59dc575 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 11 Jun 2026 12:17:35 +0200 Subject: [PATCH 482/681] build, interpreter: do not pass str for link_depends Remove str from build_target's link_depends and vs_module_defs argument before it reaches build.py. This is a straightforward source_strings_to_files transformation, but a variant of the function is needed to handle vs_module_defs being not a list *and* optional. Leave the restriction on not having generators; but mark it as FIXME so that it can be lifted if not true anymore. Signed-off-by: Paolo Bonzini --- mesonbuild/build.py | 34 ++++--------------------- mesonbuild/interpreter/interpreter.py | 19 +++++++++++--- mesonbuild/interpreter/type_checking.py | 5 ++++ 3 files changed, 25 insertions(+), 33 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 6c8b6718aad6..bc01dff7219e 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -129,12 +129,12 @@ class ExecutableKeywordArguments(BuildTargetKeywordArguments, total=False): implib: T.Optional[str] export_dynamic: bool pie: bool - vs_module_defs: T.Union[str, File, CustomTarget, CustomTargetIndex] + vs_module_defs: T.Union[File, CustomTarget, CustomTargetIndex] win_subsystem: str class SharedModuleKeywordArguments(BuildTargetKeywordArguments, total=False): - vs_module_defs: T.Union[str, File, CustomTarget, CustomTargetIndex] + vs_module_defs: T.Union[File, CustomTarget, CustomTargetIndex] class SharedLibraryKeywordArguments(SharedModuleKeywordArguments, total=False): @@ -770,7 +770,7 @@ def __init__( self.link_targets: T.List[LinkableTargetTypes] = [] self.link_whole_targets: T.List[StaticTargetTypes] = [] self.depend_files = kwargs.get('depend_files', []) - self.link_depends: T.List[T.Union[File, BuildTargetTypes]] = [] + self.link_depends = kwargs.get('link_depends', []) self.added_deps: T.Set[dependencies.Dependency] = set() self.name_prefix_set = False self.name_suffix_set = False @@ -827,7 +827,6 @@ def __init__( This will become a hard error in a future Meson release. ''')) self.link_early_args = kwargs.get('link_early_args', []) - self.process_link_depends(kwargs.get('link_depends', [])) # Target-specific include dirs must be added BEFORE include dirs from # internal deps (added inside self.add_deps()) to override them. self.add_include_dirs(kwargs.get('include_directories', [])) @@ -1187,24 +1186,6 @@ def validate_sources(self) -> None: langs = ', '.join(self.compilers.keys()) raise InvalidArguments(f'Cannot mix those languages into a target: {langs}') - def process_link_depends(self, sources: T.Iterable[T.Union[str, File, BuildTargetTypes]]) -> None: - """Process the link_depends keyword argument. - - This is designed to handle strings, Files, and the output of Custom - Targets. Notably it doesn't handle generator() returned objects, since - adding them as a link depends would inherently cause them to be - generated twice, since the output needs to be passed to the ld_args and - link_depends. - """ - for s in sources: - if isinstance(s, File): - self.link_depends.append(s) - elif isinstance(s, str): - self.link_depends.append( - File.from_source_file(self.environment.source_dir, self.get_subdir(), s)) - else: - self.link_depends.append(s) - def extract_objects(self, srclist: T.List[T.Union['FileOrString', 'GeneratedTypes']], is_unity: bool) -> ExtractedObjects: sources_set = set(self.sources) generated_set = set(self.generated) @@ -1774,18 +1755,13 @@ def process_vs_module_defs_kw(self, kwargs: ExecutableKeywordArguments | SharedM if path is None: return - if isinstance(path, str): - if os.path.isabs(path): - self.vs_module_defs = File.from_absolute_file(path) - else: - self.vs_module_defs = File.from_source_file(self.environment.source_dir, self.subdir, path) - elif isinstance(path, File): + self.link_depends.append(path) + if isinstance(path, File): # When passing a generated file. self.vs_module_defs = path else: # When passing output of a Custom Target self.vs_module_defs = File.from_built_file(path.get_builddir(), path.get_filename()) - self.process_link_depends([path]) def _default_library_type(self) -> _LibraryType: bl_type = self.environment.coredata.optstore.get_value_for(OptionKey('default_both_libraries')) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index a7082c97b289..bcd14ec0d9e9 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -3238,6 +3238,9 @@ def source_strings_to_files(self, sources: list[str]) -> list[mesonlib.File]: .. @T.overload def source_strings_to_files(self, sources: list['mesonlib.FileOrString']) -> list['mesonlib.File']: ... + @T.overload + def source_strings_to_files(self, sources: list[T.Union[mesonlib.FileOrString, build.CustomTarget, build.CustomTargetIndex]]) -> list[T.Union[mesonlib.File, build.CustomTarget, build.CustomTargetIndex]]: ... + @T.overload def source_strings_to_files(self, sources: list[T.Union[mesonlib.FileOrString, build.BuildTargetTypes]]) -> list[T.Union[mesonlib.File, build.BuildTargetTypes]]: ... # type: ignore[overload-overlap] @@ -3269,6 +3272,7 @@ def source_strings_to_files(self, sources: T.Union[ list[str], list[mesonlib.File | str], + list[mesonlib.File | str | build.CustomTarget | build.CustomTargetIndex], list[mesonlib.File | str | build.BuildTargetTypes], list[mesonlib.File | str | build.GeneratedTypes], list[kwtypes.CustomTargetInputs], @@ -3280,6 +3284,7 @@ def source_strings_to_files(self, ] ) -> T.Union[list[mesonlib.File], list[mesonlib.File | build.BuildTargetTypes], + list[mesonlib.File | build.CustomTarget | build.CustomTargetIndex], list[mesonlib.File | build.GeneratedTypes], list[mesonlib.File | build.BuildTargetTypes | build.BothLibraries | build.ExtractedObjects | build.GeneratedTypes], list[build.ObjectTypes | build.GeneratedTypes], @@ -3329,6 +3334,11 @@ def source_strings_to_files(self, 'string or File-type object') return results # type: ignore[return-value] + def source_string_to_file(self, source: str | mesonlib.File | build.CustomTarget | build.CustomTargetIndex | None) -> mesonlib.File | build.CustomTarget | build.CustomTargetIndex: + if source is None: + return None + return self.source_strings_to_files([source])[0] + def validate_forbidden_targets(self, name: str, in_root: bool) -> None: if name.startswith('meson-internal__'): raise InvalidArguments("Target names starting with 'meson-internal__' are reserved " @@ -3640,8 +3650,9 @@ def __convert_executable_kwargs(self, node: mparser.BaseNode, kwargs: kwtypes.Ex self.__convert_build_target_base_kwargs(kwargs, final) # Exe exclusive arguments - for exe_arg in ('android_exe_type', 'pie', 'vs_module_defs'): + for exe_arg in ('android_exe_type', 'pie'): final[exe_arg] = kwargs[exe_arg] + final['vs_module_defs'] = self.source_string_to_file(kwargs['vs_module_defs']) # rewrite `gui_app` to `win_subsystem` if kwargs['gui_app'] is not None: @@ -3718,8 +3729,9 @@ def __convert_shared_library_kwargs(self, node: mparser.BaseNode, kwargs: kwtype final: build.SharedLibraryKeywordArguments = {} self.__convert_build_target_base_kwargs(kwargs, final) - for arg in ('version', 'soversion', 'darwin_versions', 'shortname', 'vs_module_defs'): + for arg in ('version', 'soversion', 'darwin_versions', 'shortname'): final[arg] = kwargs[arg] + final['vs_module_defs'] = self.source_string_to_file(kwargs['vs_module_defs']) for lang in compilers.all_languages - {'java'}: deps, args = self.__convert_file_args(kwargs.get(f'{lang}_shared_args', [])) # type: ignore[arg-type] @@ -3741,8 +3753,7 @@ def __convert_shared_module_kwargs(self, node: mparser.BaseNode, kwargs: kwtypes final: build.SharedModuleKeywordArguments = {} self.__convert_build_target_base_kwargs(kwargs, final) - for arg in ('vs_module_defs', ): - final[arg] = kwargs[arg] + final['vs_module_defs'] = self.source_string_to_file(kwargs['vs_module_defs']) for lang in compilers.all_languages - {'java'}: deps, args = self.__convert_file_args(kwargs.get(f'{lang}_shared_args', [])) # type: ignore[arg-type] diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py index fdbfc20f05ba..ff9e0199de0f 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -563,6 +563,7 @@ def suite_convertor(suite: T.List[str]) -> T.List[str]: since='1.3.0', validator=in_set_validator({'rust', 'c'})) +# this is implicitly an extra link_depends and thus shares the same type _VS_MODULE_DEFS_KW: KwargInfo[T.Optional[T.Union[str, File, CustomTarget, CustomTargetIndex]]] = KwargInfo( 'vs_module_defs', (str, File, CustomTarget, CustomTargetIndex, NoneType), @@ -809,6 +810,10 @@ def _pch_convertor(args: T.List[str]) -> T.Optional[T.Tuple[str, T.Optional[str] since='0.48.0', ), KwargInfo('install_rpath', str, default=''), + # FIXME: an old comment said that link_depends "doesn't handle generator() + # returned objects, since adding them as a link depends would inherently + # cause them to be generated twice, since the output needs to be passed + # to the ld_args and link_depends". Is this still true? KwargInfo( 'link_depends', ContainerTypeInfo(list, (str, File, CustomTarget, CustomTargetIndex, BuildTarget)), From 7f7ed268de3e6f1f0b629eff7d7f6ffe0e16f67b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 11 Jun 2026 16:03:11 +0200 Subject: [PATCH 483/681] build, interpreter: do not pass string to extract_objects Remove str from build_target's objects argument before it reaches build.py. This is a different transformation than source_strings_to_files, but the code simply has to be lifted from build.py to the interpreter object. Signed-off-by: Paolo Bonzini --- mesonbuild/build.py | 6 +----- mesonbuild/interpreter/interpreterobjects.py | 13 ++++++++++++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index bc01dff7219e..ac206d730543 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -1186,7 +1186,7 @@ def validate_sources(self) -> None: langs = ', '.join(self.compilers.keys()) raise InvalidArguments(f'Cannot mix those languages into a target: {langs}') - def extract_objects(self, srclist: T.List[T.Union['FileOrString', 'GeneratedTypes']], is_unity: bool) -> ExtractedObjects: + def extract_objects(self, srclist: T.List[T.Union[File, 'GeneratedTypes']], is_unity: bool) -> ExtractedObjects: sources_set = set(self.sources) generated_set = set(self.generated) @@ -1194,10 +1194,6 @@ def extract_objects(self, srclist: T.List[T.Union['FileOrString', 'GeneratedType obj_gen: T.List['GeneratedTypes'] = [] for src in srclist: if isinstance(src, (str, File)): - if isinstance(src, str): - src = File(False, self.subdir, src) - else: - FeatureNew.single_use('File argument for extract_objects', '0.50.0', self.subproject) if src not in sources_set: raise MesonException(f'Tried to extract unknown source {src}.') obj_src.append(src) diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index 33e6ae66ea72..20f6901c1193 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -1043,7 +1043,18 @@ def extract_objects_method(self, args: T.Tuple[T.List[T.Union[mesonlib.FileOrStr tobj = self._target_object unity_value = self.interpreter.coredata.get_option_for_target(tobj, "unity") is_unity = (unity_value == 'on' or (unity_value == 'subprojects' and tobj.subproject != '')) - return tobj.extract_objects(args[0], is_unity) + + obj_src: list[mesonlib.File | build.GeneratedTypes] = [] + for raw_src in args[0]: + if isinstance(raw_src, str): + src = File(False, tobj.subdir, raw_src) + else: + if isinstance(raw_src, File): + FeatureNew.single_use('File argument for extract_objects', '0.50.0', self.subproject) + src = raw_src + obj_src.append(src) + + return tobj.extract_objects(obj_src, is_unity) @noPosargs @typed_kwargs( From 3d84d927d5b540069e750680b2bda680d202c1fd Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 11 Jun 2026 10:54:47 +0200 Subject: [PATCH 484/681] qt: standardize types in kwarg TypedDicts The typing of the functions that take sources is consistent enough that the TypedDicts in _qt.py can all be made to use lists. There are also a bunch of cases where the types didn't allow all GeneratedTypes, but the other Python code allows it so make the type wider. Signed-off-by: Paolo Bonzini --- mesonbuild/modules/_qt.py | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/mesonbuild/modules/_qt.py b/mesonbuild/modules/_qt.py index 8397ccbfc497..752a853df737 100644 --- a/mesonbuild/modules/_qt.py +++ b/mesonbuild/modules/_qt.py @@ -40,7 +40,7 @@ class ResourceCompilerKwArgs(TypedDict): """Keyword arguments for the Resource Compiler method.""" name: T.Optional[str] - sources: T.Sequence[T.Union[FileOrString, build.GeneratedTypes]] + sources: T.List[T.Union[FileOrString, build.GeneratedTypes]] extra_args: T.List[str] method: DependencyMethods @@ -48,7 +48,7 @@ class UICompilerKwArgs(TypedDict): """Keyword arguments for the Ui Compiler method.""" - sources: T.Sequence[T.Union[FileOrString, build.GeneratedTypes]] + sources: T.List[T.Union[FileOrString, build.GeneratedTypes]] extra_args: T.List[str] method: DependencyMethods preserve_paths: bool @@ -57,8 +57,8 @@ class MocCompilerKwArgs(TypedDict): """Keyword arguments for the Moc Compiler method.""" - sources: T.Sequence[T.Union[FileOrString, build.GeneratedTypes]] - headers: T.Sequence[T.Union[FileOrString, build.GeneratedTypes]] + sources: T.List[T.Union[FileOrString, build.GeneratedTypes]] + headers: T.List[T.Union[FileOrString, build.GeneratedTypes]] extra_args: T.List[str] method: DependencyMethods include_directories: T.List[T.Union[str, build.IncludeDirs]] @@ -69,10 +69,10 @@ class MocCompilerKwArgs(TypedDict): class PreprocessKwArgs(TypedDict): sources: T.List[FileOrString] - moc_sources: T.List[T.Union[FileOrString, build.CustomTarget]] - moc_headers: T.List[T.Union[FileOrString, build.CustomTarget]] + moc_sources: T.List[T.Union[FileOrString, build.GeneratedTypes]] + moc_headers: T.List[T.Union[FileOrString, build.GeneratedTypes]] qresources: T.List[FileOrString] - ui_files: T.List[T.Union[FileOrString, build.CustomTarget]] + ui_files: T.List[T.Union[FileOrString, build.GeneratedTypes]] moc_extra_arguments: T.List[str] rcc_extra_arguments: T.List[str] uic_extra_arguments: T.List[str] @@ -100,8 +100,8 @@ class CompileTranslationsKwArgs(TypedDict): class GenQrcKwArgs(TypedDict): - sources: T.Sequence[File] - aliases: T.Sequence[str] + sources: T.List[File] + aliases: T.List[str] prefix: str output: str @@ -110,9 +110,9 @@ class GenQmldirKwArgs(TypedDict): module_name: str module_version: str module_prefix: str - qml_sources: T.Sequence[T.Union[FileOrString, build.GeneratedTypes]] - qml_singletons: T.Sequence[T.Union[FileOrString, build.GeneratedTypes]] - qml_internals: T.Sequence[T.Union[FileOrString, build.GeneratedTypes]] + qml_sources: T.List[T.Union[FileOrString, build.GeneratedTypes]] + qml_singletons: T.List[T.Union[FileOrString, build.GeneratedTypes]] + qml_internals: T.List[T.Union[FileOrString, build.GeneratedTypes]] designer_supported: bool imports: T.List[str] optional_imports: T.List[str] @@ -124,7 +124,7 @@ class GenQmldirKwArgs(TypedDict): class GenQmlCachegenKwArgs(TypedDict): target_name: str - qml_sources: T.Sequence[T.Union[FileOrString, build.GeneratedTypes]] + qml_sources: T.List[T.Union[FileOrString, build.GeneratedTypes]] qml_qrc: File extra_args: T.List[str] module_prefix: str @@ -648,7 +648,9 @@ def preprocess(self, state: ModuleState, args: T.List[T.Union[str, File]], kwarg if kwargs['qresources']: # custom output name set? -> one output file, multiple otherwise - rcc_kwargs: ResourceCompilerKwArgs = {'name': '', 'sources': kwargs['qresources'], 'extra_args': kwargs['rcc_extra_arguments'], 'method': method} + rcc_kwargs: ResourceCompilerKwArgs = {'name': '', + 'sources': T.cast('T.List[FileOrString | build.GeneratedTypes]', kwargs['qresources']), + 'extra_args': kwargs['rcc_extra_arguments'], 'method': method} if args: name = args[0] if not isinstance(name, str): @@ -761,7 +763,7 @@ def compile_translations(self, state: ModuleState, args: T.Tuple, kwargs: Compil else: return ModuleReturnValue(translations, [translations]) - def _source_to_files(self, state: ModuleState, sources: T.Sequence[T.Union[FileOrString, build.GeneratedTypes]]) -> T.List[File]: + def _source_to_files(self, state: ModuleState, sources: T.List[T.Union[FileOrString, build.GeneratedTypes]]) -> T.List[File]: content_files = [] for s in sources: @@ -819,7 +821,7 @@ def _gen_qmldir(self, state: ModuleState, kwargs: GenQmldirKwArgs) -> File: with open(fileout_abs, 'w', encoding='utf-8') as fd: - def __gen_import(import_type: str, importlist: T.Sequence[str]) -> None: + def __gen_import(import_type: str, importlist: T.List[str]) -> None: for import_string in importlist: match = import_re.match(import_string) if not match: @@ -828,7 +830,7 @@ def __gen_import(import_type: str, importlist: T.Sequence[str]) -> None: version: str = match.group(4) or '' fd.write(f'{import_type} {module} {version}\n') - def __gen_declaration(qualifier: str, version: str, importlist: T.Sequence[T.Union[FileOrString, build.GeneratedTypes]]) -> None: + def __gen_declaration(qualifier: str, version: str, importlist: T.List[T.Union[FileOrString, build.GeneratedTypes]]) -> None: importpathlist = self._source_to_files(state, importlist) for s in importpathlist: basename: str = os.path.basename(s.fname) @@ -1072,7 +1074,7 @@ def qml_module(self, state: ModuleState, args: T.Tuple[str], kwargs: QmlModuleKw target_name = re.sub(r'[^A-Za-z0-9]', '_', module_name) qrc_resouces: T.List[T.Union[FileOrString, build.GeneratedTypes]] = [] - all_qml: T.Sequence[T.Union[FileOrString, build.GeneratedTypes]] = kwargs['qml_sources'] + kwargs['qml_singletons'] + kwargs['qml_internals'] + all_qml: T.List[T.Union[FileOrString, build.GeneratedTypes]] = kwargs['qml_sources'] + kwargs['qml_singletons'] + kwargs['qml_internals'] all_qml_files: T.List[File] = self._source_to_files(state, all_qml) all_qml_basename: T.List[str] = [os.path.basename(p.fname) for p in all_qml_files] From 5f283facda17e2a60cbb54bcb70c9c3b6a4d8db4 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 13 Jun 2026 08:55:31 +0200 Subject: [PATCH 485/681] qt: allow CustomTargetIndex and generators for preprocess Corresponding to the TypedDict change from the previous patch, extend the choice of GeneratedTypes to the DSL and not just the internal dictionaries. Signed-off-by: Paolo Bonzini --- docs/markdown/snippets/qt-preprocess-gen.md | 5 +++++ mesonbuild/modules/_qt.py | 9 ++++++--- test cases/frameworks/4 qt/meson.build | 8 ++++++-- 3 files changed, 17 insertions(+), 5 deletions(-) create mode 100644 docs/markdown/snippets/qt-preprocess-gen.md diff --git a/docs/markdown/snippets/qt-preprocess-gen.md b/docs/markdown/snippets/qt-preprocess-gen.md new file mode 100644 index 000000000000..3ac05486cf52 --- /dev/null +++ b/docs/markdown/snippets/qt-preprocess-gen.md @@ -0,0 +1,5 @@ +## More types of generated files allowed by `qt.preprocess` + +`qt.preprocess` now allows custom target indexes or generators +in addition to custom targets for `ui_files`, `moc_sources` +and `moc_headers`. diff --git a/mesonbuild/modules/_qt.py b/mesonbuild/modules/_qt.py index 752a853df737..6c8157b1c714 100644 --- a/mesonbuild/modules/_qt.py +++ b/mesonbuild/modules/_qt.py @@ -623,9 +623,12 @@ def _compile_moc_impl(self, state: ModuleState, kwargs: MocCompilerKwArgs) -> T. DEPENDENCY_METHOD_KW, KwargInfo('sources', ContainerTypeInfo(list, (File, str)), listify=True, default=[], deprecated='0.59.0'), KwargInfo('qresources', ContainerTypeInfo(list, (File, str)), listify=True, default=[]), - KwargInfo('ui_files', ContainerTypeInfo(list, (File, str, build.CustomTarget)), listify=True, default=[]), - KwargInfo('moc_sources', ContainerTypeInfo(list, (File, str, build.CustomTarget)), listify=True, default=[]), - KwargInfo('moc_headers', ContainerTypeInfo(list, (File, str, build.CustomTarget)), listify=True, default=[]), + KwargInfo('ui_files', ContainerTypeInfo(list, (File, str, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList)), listify=True, default=[], + since_values={ContainerTypeInfo(list, (build.CustomTargetIndex, build.GeneratedList)): '1.12'}), + KwargInfo('moc_sources', ContainerTypeInfo(list, (File, str, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList)), listify=True, default=[], + since_values={ContainerTypeInfo(list, (build.CustomTargetIndex, build.GeneratedList)): '1.12'}), + KwargInfo('moc_headers', ContainerTypeInfo(list, (File, str, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList)), listify=True, default=[], + since_values={ContainerTypeInfo(list, (build.CustomTargetIndex, build.GeneratedList)): '1.12'}), KwargInfo('moc_extra_arguments', ContainerTypeInfo(list, str), listify=True, default=[], since='0.44.0'), KwargInfo('rcc_extra_arguments', ContainerTypeInfo(list, str), listify=True, default=[], since='0.49.0'), KwargInfo('uic_extra_arguments', ContainerTypeInfo(list, str), listify=True, default=[], since='0.49.0'), diff --git a/test cases/frameworks/4 qt/meson.build b/test cases/frameworks/4 qt/meson.build index d8faf07cb97c..dd82fddddcf6 100644 --- a/test cases/frameworks/4 qt/meson.build +++ b/test cases/frameworks/4 qt/meson.build @@ -5,8 +5,12 @@ project('qt4, qt5, and qt6 build test', 'cpp', # Visit the subdir before entering the loop subdir('mocdep') +fs = import('fs') qt5_modules = ['Widgets'] qt6_modules = ['Widgets'] + +manualinclude_cpp = fs.copyfile('manualinclude.cpp') +manualinclude_h = fs.copyfile('manualinclude.h') foreach qt : ['qt4', 'qt5', 'qt6'] qt_modules = ['Core', 'Gui'] if qt == 'qt5' @@ -131,8 +135,8 @@ foreach qt : ['qt4', 'qt5', 'qt6'] # files from sources. qtmodule.preprocess( moc_extra_arguments : ['-DMOC_EXTRA_FLAG'], # This is just a random macro to test `extra_arguments` - moc_sources : 'manualinclude.cpp', - moc_headers : 'manualinclude.h', + moc_sources : manualinclude_cpp, + moc_headers : manualinclude_h[0], method : get_option('method'), dependencies: mocdep, ) From 4af0860138302b996596cb46b33a1008a1bfc45d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 11 Jun 2026 11:15:01 +0200 Subject: [PATCH 486/681] build: introduce TargetSources Introduce a single type for all cases where a file or generated source is allowed. It can be used alone, or unioned with str if still in the interpreter. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/backends.py | 7 ++- mesonbuild/backend/ninjabackend.py | 22 +++++----- mesonbuild/build.py | 9 ++-- mesonbuild/dependencies/base.py | 8 ++-- mesonbuild/interpreter/interpreter.py | 34 +++++++-------- mesonbuild/interpreter/interpreterobjects.py | 4 +- mesonbuild/interpreter/kwargs.py | 4 +- mesonbuild/interpreter/type_checking.py | 4 +- mesonbuild/mintro.py | 2 +- mesonbuild/modules/_qt.py | 46 ++++++++++---------- mesonbuild/modules/gnome.py | 8 ++-- mesonbuild/modules/icestorm.py | 6 +-- mesonbuild/modules/sourceset.py | 22 +++++----- 13 files changed, 88 insertions(+), 88 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 4ba8b501a946..74f506446c08 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -37,6 +37,7 @@ if T.TYPE_CHECKING: from .._typing import ImmutableListProtocol from ..arglist import CompilerArgs + from ..build import TargetSources from ..compilers.compilers import Compiler, Language from ..environment import Environment from ..interpreter import Test @@ -46,8 +47,6 @@ from typing_extensions import Literal, TypedDict, NotRequired, TypeAlias - _ALL_SOURCES_TYPE = T.List[T.Union[File, build.GeneratedTypes]] - class CompilerIntrospectionData(TypedDict): language: T.Union[Language, Literal['unknown']] @@ -2084,7 +2083,7 @@ def have_language(self, langname: str) -> bool: def compiler_to_generator(self, target: build.BuildTarget, compiler: 'Compiler', - sources: _ALL_SOURCES_TYPE, + sources: T.List[TargetSources], output_templ: str, depends: T.Optional[T.List[build.BuildTargetTypes]] = None, ) -> build.GeneratedList: @@ -2102,7 +2101,7 @@ def compiler_to_generator(self, target: build.BuildTarget, return generator.process_files(sources) def compile_target_to_generator(self, target: build.CompileTarget) -> build.GeneratedList: - all_sources = T.cast('_ALL_SOURCES_TYPE', target.sources) + T.cast('_ALL_SOURCES_TYPE', target.generated) + all_sources: T.List[TargetSources] = [*target.sources, *target.generated] return self.compiler_to_generator(target, target.compiler, all_sources, target.output_templ, target.depends) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 9985b06411c5..ed4398d2d315 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -799,13 +799,13 @@ def get_generated_headers(self, target: build.BuildTarget) -> T.List[FileOrStrin self._generated_header_cache[tid] = header_deps return header_deps - def get_target_generated_sources(self, target: build.BuildTarget) -> T.MutableMapping[str, File | build.GeneratedTypes]: + def get_target_generated_sources(self, target: build.BuildTarget) -> T.MutableMapping[str, build.TargetSources]: """ Returns a dictionary with the keys being the path to the file (relative to the build directory) and the value being the File object representing the same path. """ - srcs: T.MutableMapping[str, File | build.GeneratedTypes] = {} + srcs: T.MutableMapping[str, build.TargetSources] = {} for gensrc in target.get_generated_sources(): for s in gensrc.get_outputs(): rel_src = self.get_target_generated_dir(target, gensrc, s) @@ -962,7 +962,7 @@ def generate_target(self, target: T.Union[build.Target]) -> None: # GeneratedList and CustomTarget sources to be built; dict of the full # path to source relative to build root and the generating target/list - generated_sources: T.MutableMapping[str, File | build.GeneratedTypes] + generated_sources: T.MutableMapping[str, build.TargetSources] # List of sources that have been transpiled from a DSL (like Vala) into # a language that is handled below, such as C or C++ @@ -1677,8 +1677,8 @@ def determine_dep_vapis(self, target: build.BuildTarget) -> T.List[str]: return list(result) def split_vala_sources(self, t: build.BuildTarget) -> \ - T.Tuple[T.MutableMapping[str, File | build.GeneratedTypes], T.MutableMapping[str, File | build.GeneratedTypes], - T.MutableMapping[str, File], T.MutableMapping[str, File | build.GeneratedTypes]]: + T.Tuple[T.MutableMapping[str, build.TargetSources], T.MutableMapping[str, build.TargetSources], + T.MutableMapping[str, File], T.MutableMapping[str, build.TargetSources]]: """ Splits the target's sources into .vala, .gs, .vapi, and other sources. Handles both preexisting and generated sources. @@ -1687,10 +1687,10 @@ def split_vala_sources(self, t: build.BuildTarget) -> \ the keys being the path to the file (relative to the build directory) and the value being the object that generated or represents the file. """ - vala: T.MutableMapping[str, File | build.GeneratedTypes] = OrderedDict() - vapi: T.MutableMapping[str, File | build.GeneratedTypes] = OrderedDict() + vala: T.MutableMapping[str, build.TargetSources] = OrderedDict() + vapi: T.MutableMapping[str, build.TargetSources] = OrderedDict() others: T.MutableMapping[str, File] = OrderedDict() - othersgen: T.MutableMapping[str, File | build.GeneratedTypes] = OrderedDict() + othersgen: T.MutableMapping[str, build.TargetSources] = OrderedDict() # Split preexisting sources for s in t.get_sources(): # BuildTarget sources are always mesonlib.File files which are @@ -1729,7 +1729,7 @@ def split_vala_sources(self, t: build.BuildTarget) -> \ return vala, vapi, others, othersgen def generate_vala_compile(self, target: build.BuildTarget) -> \ - T.Tuple[T.MutableMapping[str, File], T.MutableMapping[str, File | build.GeneratedTypes], T.List[str]]: + T.Tuple[T.MutableMapping[str, File], T.MutableMapping[str, build.TargetSources], T.List[str]]: """Vala is compiled into C. Set up all necessary build steps here.""" (vala_src, vapi_src, others, othersgen) = self.split_vala_sources(target) extra_dep_files = [] @@ -1850,11 +1850,11 @@ def generate_vala_compile(self, target: build.BuildTarget) -> \ return others, othersgen, vala_c_src def generate_cython_transpile(self, target: build.BuildTarget) -> \ - T.Tuple[T.MutableMapping[str, File], T.MutableMapping[str, File | build.GeneratedTypes], T.List[str]]: + T.Tuple[T.MutableMapping[str, File], T.MutableMapping[str, build.TargetSources], T.List[str]]: """Generate rules for transpiling Cython files to C or C++""" static_sources: T.MutableMapping[str, File] = OrderedDict() - generated_sources: T.MutableMapping[str, File | build.GeneratedTypes] = OrderedDict() + generated_sources: T.MutableMapping[str, build.TargetSources] = OrderedDict() cython_sources: T.List[str] = [] cython = target.compilers['cython'] diff --git a/mesonbuild/build.py b/mesonbuild/build.py index ac206d730543..0dacb4e5459f 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -53,6 +53,7 @@ from .mparser import BaseNode from .options import ElementaryOptionValues + TargetSources: TypeAlias = T.Union['File', 'GeneratedTypes'] GeneratedTypes: TypeAlias = T.Union['CustomTarget', 'CustomTargetIndex', 'GeneratedList'] LibTypes: TypeAlias = T.Union['SharedLibrary', 'StaticLibrary', 'CustomTarget', 'CustomTargetIndex'] LinkableTargetTypes: TypeAlias = T.Union['SharedLibrary', 'StaticLibrary', 'CustomTarget', 'CustomTargetIndex', 'Executable'] @@ -578,7 +579,7 @@ class StructuredSources(HoldableObject): represent the required filesystem layout. """ - sources: T.DefaultDict[str, T.List[T.Union[File, GeneratedTypes]]] = field( + sources: T.DefaultDict[str, T.List[TargetSources]] = field( default_factory=lambda: defaultdict(list)) def __add__(self, other: StructuredSources) -> StructuredSources: @@ -590,7 +591,7 @@ def __add__(self, other: StructuredSources) -> StructuredSources: def __bool__(self) -> bool: return bool(self.sources) - def as_list(self) -> T.List[T.Union[File, GeneratedTypes]]: + def as_list(self) -> T.List[TargetSources]: return list(itertools.chain.from_iterable(self.sources.values())) def needs_copy(self) -> bool: @@ -1186,7 +1187,7 @@ def validate_sources(self) -> None: langs = ', '.join(self.compilers.keys()) raise InvalidArguments(f'Cannot mix those languages into a target: {langs}') - def extract_objects(self, srclist: T.List[T.Union[File, 'GeneratedTypes']], is_unity: bool) -> ExtractedObjects: + def extract_objects(self, srclist: T.List[TargetSources], is_unity: bool) -> ExtractedObjects: sources_set = set(self.sources) generated_set = set(self.generated) @@ -1999,7 +2000,7 @@ def get_arglist(self, inname: str) -> T.List[str]: basename = os.path.splitext(plainname)[0] return [x.replace('@BASENAME@', basename).replace('@PLAINNAME@', plainname) for x in self.arglist] - def process_files(self, files: T.Iterable[T.Union[str, File, GeneratedTypes]], + def process_files(self, files: T.Iterable[str | TargetSources], subdir: str = '', preserve_path_from: T.Optional[str] = None, extra_args: T.Optional[T.List[str]] = None, diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index 8d276268ace8..99cdfa2667d5 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -29,7 +29,7 @@ from ..interpreterbase import FeatureCheckBase from ..build import ( CustomTarget, IncludeDirs, CustomTargetIndex, LinkableTargetTypes, - StaticLibrary, ExtractedObjects, GeneratedTypes + StaticLibrary, ExtractedObjects, TargetSources ) from ..interpreter.type_checking import PkgConfigDefineType @@ -148,7 +148,7 @@ def __init__(self, kwargs: DependencyObjectKWs) -> None: # Raw -L and -l arguments without manual library searching # If None, self.link_args will be used self.raw_link_args: T.Optional[T.List[str]] = None - self.sources: T.List[T.Union[mesonlib.File, GeneratedTypes]] = [] + self.sources: T.List[TargetSources] = [] self.extra_files: T.List[mesonlib.File] = [] self.include_type = kwargs.get('include_type', 'preserve') self.ext_deps: T.List[Dependency] = [] @@ -220,7 +220,7 @@ def get_all_link_args(self) -> T.List[str]: def found(self) -> bool: return self.is_found - def get_sources(self) -> T.List[T.Union[mesonlib.File, GeneratedTypes]]: + def get_sources(self) -> T.List[TargetSources]: """Source files that need to be added to the target. As an example, gtest-all.cc when using GTest.""" return self.sources @@ -311,7 +311,7 @@ def __init__(self, version: str, incdirs: T.Optional[T.List['IncludeDirs']] = No link_args: T.Optional[T.List[str]] = None, libraries: T.Optional[T.List[LinkableTargetTypes]] = None, whole_libraries: T.Optional[T.List[T.Union[StaticLibrary, CustomTarget, CustomTargetIndex]]] = None, - sources: T.Optional[T.Sequence[T.Union[mesonlib.File, GeneratedTypes]]] = None, + sources: T.Optional[T.Sequence[TargetSources]] = None, extra_files: T.Optional[T.Sequence[mesonlib.File]] = None, ext_deps: T.Optional[T.List[Dependency]] = None, variables: T.Optional[T.Dict[str, str]] = None, d_module_versions: T.Optional[T.List[T.Union[str, int]]] = None, diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index bcd14ec0d9e9..c87c9f4feae4 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -127,16 +127,16 @@ from .type_checking import SourcesVarargsType, FullEnvInitValueType # Input source types passed to Targets - SourceInputs = T.Union[mesonlib.FileOrString, build.GeneratedTypes, build.BuildTarget, + SourceInputs = T.Union[str, build.TargetSources, build.BuildTarget, build.BothLibraries, build.ExtractedObjects] # Input source types passed to the build.Target classes - SourceOutputs = T.Union[mesonlib.File, build.GeneratedTypes, build.BuildTarget, + SourceOutputs = T.Union[build.TargetSources, build.BuildTarget, build.ExtractedObjects, build.StructuredSources] # Sources for custom targets, which can also include ExternalProgram - CustomTargetSources = T.Union[mesonlib.File, build.GeneratedTypes, build.BuildTarget, + CustomTargetSources = T.Union[build.TargetSources, build.BuildTarget, build.ExtractedObjects, Program] - BuildTargetSource = T.Union[mesonlib.FileOrString, build.GeneratedTypes, build.StructuredSources] + BuildTargetSource = T.Union[str, build.TargetSources, build.StructuredSources] ProgramVersionFunc = T.Callable[[Program], str] @@ -1951,7 +1951,7 @@ def func_library(self, node: mparser.BaseNode, @typed_pos_args('jar', str, varargs=(str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList, build.ExtractedObjects, build.BuildTarget)) @typed_kwargs('jar', *JAR_KWS) def func_jar(self, node: mparser.BaseNode, - args: T.Tuple[str, T.List[T.Union[str, mesonlib.File, build.GeneratedTypes]]], + args: T.Tuple[str, T.List[str | build.TargetSources]], kwargs: kwtypes.Jar) -> build.Jar: return self.build_target(node, T.cast('tuple[str, SourcesVarargsType]', args), kwargs, build.Jar) @@ -2475,7 +2475,7 @@ def func_structured_sources( args: T.Tuple[object, T.Optional[T.Dict[str, object]]], kwargs: 'TYPE_kwargs') -> build.StructuredSources: valid_types = (str, mesonlib.File, build.GeneratedList, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList) - sources: T.DefaultDict[str, T.List[T.Union[mesonlib.File, 'build.GeneratedTypes']]] = collections.defaultdict(list) + sources: T.DefaultDict[str, T.List[T.Union[build.TargetSources]]] = collections.defaultdict(list) for arg in mesonlib.listify(args[0]): if not isinstance(arg, valid_types): @@ -3245,7 +3245,7 @@ def source_strings_to_files(self, sources: list[T.Union[mesonlib.FileOrString, b def source_strings_to_files(self, sources: list[T.Union[mesonlib.FileOrString, build.BuildTargetTypes]]) -> list[T.Union[mesonlib.File, build.BuildTargetTypes]]: ... # type: ignore[overload-overlap] @T.overload - def source_strings_to_files(self, sources: list[T.Union[mesonlib.FileOrString, build.GeneratedTypes]]) -> list[T.Union[mesonlib.File, build.GeneratedTypes]]: ... + def source_strings_to_files(self, sources: list[T.Union[str, build.TargetSources]]) -> list[build.TargetSources]: ... @T.overload def source_strings_to_files(self, sources: list[kwtypes.CustomTargetInputs]) -> list['CustomTargetSources']: ... # type: ignore[overload-overlap] @@ -3255,12 +3255,12 @@ def source_strings_to_files(self, sources: list[kwtypes.BuildTargetObjects], # ) -> list[T.Union[build.ObjectTypes, build.GeneratedTypes]]: ... @T.overload - def source_strings_to_files(self, sources: list[T.Union[mesonlib.FileOrString, build.BuildTargetTypes, build.BothLibraries, build.ExtractedObjects, build.GeneratedTypes]], # type: ignore[overload-overlap] - ) -> list[T.Union[mesonlib.File, build.BuildTargetTypes, build.BothLibraries, build.ExtractedObjects, build.GeneratedTypes]]: ... + def source_strings_to_files(self, sources: list[T.Union[str, build.TargetSources, build.BuildTargetTypes, build.BothLibraries, build.ExtractedObjects]], # type: ignore[overload-overlap] + ) -> list[T.Union[build.TargetSources, build.BuildTargetTypes, build.BothLibraries, build.ExtractedObjects]]: ... @T.overload - def source_strings_to_files(self, sources: list[T.Union[mesonlib.FileOrString, build.GeneratedTypes, build.StructuredSources]], - ) -> list[T.Union[mesonlib.File, build.GeneratedTypes, build.StructuredSources]]: ... # noqa: F811 + def source_strings_to_files(self, sources: list[T.Union[str, build.TargetSources, build.StructuredSources]], + ) -> list[T.Union[build.TargetSources, build.StructuredSources]]: ... # noqa: F811 @T.overload def source_strings_to_files(self, sources: list[SourcesVarargsType]) -> list['SourceOutputs']: ... @@ -3274,21 +3274,21 @@ def source_strings_to_files(self, list[mesonlib.File | str], list[mesonlib.File | str | build.CustomTarget | build.CustomTargetIndex], list[mesonlib.File | str | build.BuildTargetTypes], - list[mesonlib.File | str | build.GeneratedTypes], + list[str | build.TargetSources], + list[str | build.TargetSources | build.StructuredSources], list[kwtypes.CustomTargetInputs], list[mesonlib.File | str | build.BuildTargetTypes | build.BothLibraries | build.ExtractedObjects | build.GeneratedTypes], list[kwtypes.BuildTargetObjects], - list[mesonlib.File | str | build.GeneratedTypes | build.StructuredSources], list[SourceInputs], list[SourcesVarargsType], ] ) -> T.Union[list[mesonlib.File], list[mesonlib.File | build.BuildTargetTypes], list[mesonlib.File | build.CustomTarget | build.CustomTargetIndex], - list[mesonlib.File | build.GeneratedTypes], + list[build.TargetSources], + list[build.TargetSources | build.StructuredSources], list[mesonlib.File | build.BuildTargetTypes | build.BothLibraries | build.ExtractedObjects | build.GeneratedTypes], list[build.ObjectTypes | build.GeneratedTypes], - list[mesonlib.File | build.GeneratedTypes | build.StructuredSources], list[CustomTargetSources], list[SourceOutputs]]: """Lower inputs to a list of Targets and Files, replacing any strings. @@ -3299,7 +3299,7 @@ def source_strings_to_files(self, :return: A list of Targets and Files """ mesonlib.check_direntry_issues(sources) - results: list[mesonlib.File | build.GeneratedTypes | build.BuildTarget | build.ExtractedObjects | build.StructuredSources | Program] = [] + results: list[build.TargetSources | build.BuildTarget | build.ExtractedObjects | build.StructuredSources | Program] = [] for s in sources: if isinstance(s, str): @@ -3939,7 +3939,7 @@ def add_stdlib_info(self, target: build.BuildTarget) -> None: if dep: target.add_deps([dep]) - def check_for_jar_sources(self, sources: T.Union[T.List[str], T.Sequence[T.Union[mesonlib.File, build.GeneratedTypes, build.StructuredSources]]], + def check_for_jar_sources(self, sources: T.Union[T.List[str], T.Sequence[T.Union[build.TargetSources, build.StructuredSources]]], targetclass: T.Type[build.BuildTarget]) -> None: for s in sources: if isinstance(s, (str, mesonlib.File)) and compilers.is_java(s): diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index 20f6901c1193..41bb50cae9e2 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -1037,7 +1037,7 @@ def outdir_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: @noKwargs @typed_pos_args('extract_objects', varargs=(mesonlib.File, str, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList)) @InterpreterObject.method('extract_objects') - def extract_objects_method(self, args: T.Tuple[T.List[T.Union[mesonlib.FileOrString, 'build.GeneratedTypes']]], kwargs: TYPE_nkwargs) -> build.ExtractedObjects: + def extract_objects_method(self, args: T.Tuple[T.List[str | build.TargetSources]], kwargs: TYPE_nkwargs) -> build.ExtractedObjects: if self.subproject != self.held_object.subproject: raise InterpreterException('Tried to extract objects from a different subproject.') tobj = self._target_object @@ -1238,7 +1238,7 @@ def __init__(self, gen: build.Generator, interpreter: 'Interpreter'): ) @InterpreterObject.method('process') def process_method(self, - args: T.Tuple[T.List[T.Union[str, mesonlib.File, 'build.GeneratedTypes']]], + args: T.Tuple[T.List[str | build.TargetSources]], kwargs: 'kwargs.GeneratorProcess') -> build.GeneratedList: preserve_path_from = kwargs['preserve_path_from'] if preserve_path_from is not None: diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index 7f18edf847df..d9aa399449c9 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -524,7 +524,7 @@ class _JarMixin(TypedDict): class Jar(BaseBuildTarget, _JarMixin): - sources: T.Union[str, File, build.GeneratedTypes, build.ExtractedObjects, build.BuildTarget] + sources: T.Union[str, build.TargetSources, build.ExtractedObjects, build.BuildTarget] class BuildTargetFunc(Library, _ExecutableMixin, _JarMixin): @@ -545,7 +545,7 @@ class FuncDeclareDependency(TypedDict): link_whole: T.List[build.StaticTargetTypes] link_with: T.List[build.LinkableTargetTypes] objects: T.List[build.ExtractedObjects] - sources: T.List[T.Union[FileOrString, build.GeneratedTypes]] + sources: T.List[str | build.TargetSources] variables: T.Dict[str, str] version: T.Optional[str] diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py index ff9e0199de0f..90d4d1bd14ed 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -26,7 +26,7 @@ from typing_extensions import Literal from .kwargs import CustomTargetInputs - from ..build import ObjectTypes, GeneratedTypes, BuildTargetTypes + from ..build import ObjectTypes, GeneratedTypes, BuildTargetTypes, TargetSources from ..interpreterbase import TYPE_var from ..options import ElementaryOptionValues from ..mesonlib import EnvInitValueType @@ -490,7 +490,7 @@ def link_whole_validator(values: T.List[T.Union[StaticLibrary, CustomTarget, Cus extra_types={Dependency: lambda _: _LINK_WITH_ERROR} ) -DEPENDENCY_SOURCES_KW: KwargInfo[T.List[T.Union[str, File, GeneratedTypes]]] = KwargInfo( +DEPENDENCY_SOURCES_KW: KwargInfo[T.List[str | TargetSources]] = KwargInfo( 'sources', ContainerTypeInfo(list, (str, File, CustomTarget, CustomTargetIndex, GeneratedList)), listify=True, diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 424c77537e73..0365255ef043 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -332,7 +332,7 @@ def list_deps_from_source(intr: IntrospectionInterpreter) -> T.List[T.Dict[str, def list_deps(coredata: cdata.CoreData, builddata: build.Build, backend: backends.Backend) -> T.List[T.Dict[str, T.Union[str, T.List[str]]]]: result: T.Dict[str, T.Dict[str, T.Union[str, T.List[str]]]] = {} - def _src_to_str(src_file: T.Union[mesonlib.FileOrString, build.GeneratedTypes, build.StructuredSources]) -> T.List[str]: + def _src_to_str(src_file: T.Union[str, build.TargetSources, build.StructuredSources]) -> T.List[str]: if isinstance(src_file, str): return [src_file] if isinstance(src_file, mesonlib.File): diff --git a/mesonbuild/modules/_qt.py b/mesonbuild/modules/_qt.py index 6c8157b1c714..d71fedb252eb 100644 --- a/mesonbuild/modules/_qt.py +++ b/mesonbuild/modules/_qt.py @@ -22,6 +22,7 @@ if T.TYPE_CHECKING: from . import ModuleState + from ..build import TargetSources from ..dependencies.qt import QtPkgConfigDependency, QmakeQtDependency from ..dependencies.base import DependencyObjectKWs from ..interpreter import Interpreter @@ -40,7 +41,7 @@ class ResourceCompilerKwArgs(TypedDict): """Keyword arguments for the Resource Compiler method.""" name: T.Optional[str] - sources: T.List[T.Union[FileOrString, build.GeneratedTypes]] + sources: T.List[str | build.TargetSources] extra_args: T.List[str] method: DependencyMethods @@ -48,7 +49,7 @@ class UICompilerKwArgs(TypedDict): """Keyword arguments for the Ui Compiler method.""" - sources: T.List[T.Union[FileOrString, build.GeneratedTypes]] + sources: T.List[str | build.TargetSources] extra_args: T.List[str] method: DependencyMethods preserve_paths: bool @@ -57,8 +58,8 @@ class MocCompilerKwArgs(TypedDict): """Keyword arguments for the Moc Compiler method.""" - sources: T.List[T.Union[FileOrString, build.GeneratedTypes]] - headers: T.List[T.Union[FileOrString, build.GeneratedTypes]] + sources: T.List[str | build.TargetSources] + headers: T.List[str | build.TargetSources] extra_args: T.List[str] method: DependencyMethods include_directories: T.List[T.Union[str, build.IncludeDirs]] @@ -69,10 +70,10 @@ class MocCompilerKwArgs(TypedDict): class PreprocessKwArgs(TypedDict): sources: T.List[FileOrString] - moc_sources: T.List[T.Union[FileOrString, build.GeneratedTypes]] - moc_headers: T.List[T.Union[FileOrString, build.GeneratedTypes]] + moc_sources: T.List[str | build.TargetSources] + moc_headers: T.List[str | build.TargetSources] qresources: T.List[FileOrString] - ui_files: T.List[T.Union[FileOrString, build.GeneratedTypes]] + ui_files: T.List[str | build.TargetSources] moc_extra_arguments: T.List[str] rcc_extra_arguments: T.List[str] uic_extra_arguments: T.List[str] @@ -96,7 +97,7 @@ class CompileTranslationsKwArgs(TypedDict): method: DependencyMethods qresource: T.Optional[str] rcc_extra_arguments: T.List[str] - ts_files: T.List[T.Union[str, File, build.GeneratedTypes]] + ts_files: T.List[str | build.TargetSources] class GenQrcKwArgs(TypedDict): @@ -110,9 +111,9 @@ class GenQmldirKwArgs(TypedDict): module_name: str module_version: str module_prefix: str - qml_sources: T.List[T.Union[FileOrString, build.GeneratedTypes]] - qml_singletons: T.List[T.Union[FileOrString, build.GeneratedTypes]] - qml_internals: T.List[T.Union[FileOrString, build.GeneratedTypes]] + qml_sources: T.List[str | build.TargetSources] + qml_singletons: T.List[str | build.TargetSources] + qml_internals: T.List[str | build.TargetSources] designer_supported: bool imports: T.List[str] optional_imports: T.List[str] @@ -124,7 +125,7 @@ class GenQmldirKwArgs(TypedDict): class GenQmlCachegenKwArgs(TypedDict): target_name: str - qml_sources: T.List[T.Union[FileOrString, build.GeneratedTypes]] + qml_sources: T.List[str | build.TargetSources] qml_qrc: File extra_args: T.List[str] module_prefix: str @@ -154,11 +155,11 @@ class MocJsonCollectKwArgs(TypedDict): class QmlModuleKwArgs(TypedDict): version: str - qml_sources: T.List[T.Union[FileOrString, build.GeneratedTypes]] - qml_singletons: T.List[T.Union[FileOrString, build.GeneratedTypes]] - qml_internals: T.List[T.Union[FileOrString, build.GeneratedTypes]] + qml_sources: T.List[str | build.TargetSources] + qml_singletons: T.List[str | build.TargetSources] + qml_internals: T.List[str | build.TargetSources] resources_prefix: str - moc_headers: T.List[T.Union[FileOrString, build.GeneratedTypes]] + moc_headers: T.List[str | build.TargetSources] include_directories: T.List[T.Union[str, build.IncludeDirs]] imports: T.List[str] optional_imports: T.List[str] @@ -642,8 +643,7 @@ def preprocess(self, state: ModuleState, args: T.List[T.Union[str, File]], kwarg if _sources: FeatureDeprecated.single_use('qt.preprocess positional sources', '0.59', state.subproject, location=state.current_node) # List is invariant, os we have to cast... - sources = T.cast('T.List[T.Union[str, File, build.GeneratedList, build.CustomTarget]]', - _sources + kwargs['sources']) + sources: T.List[str | build.TargetSources] = [*_sources, *kwargs['sources']] for s in sources: if not isinstance(s, (str, File)): raise build.InvalidArguments('Variadic arguments to qt.preprocess must be Strings or Files') @@ -652,7 +652,7 @@ def preprocess(self, state: ModuleState, args: T.List[T.Union[str, File]], kwarg if kwargs['qresources']: # custom output name set? -> one output file, multiple otherwise rcc_kwargs: ResourceCompilerKwArgs = {'name': '', - 'sources': T.cast('T.List[FileOrString | build.GeneratedTypes]', kwargs['qresources']), + 'sources': T.cast('T.List[str | TargetSources]', kwargs['qresources']), 'extra_args': kwargs['rcc_extra_arguments'], 'method': method} if args: name = args[0] @@ -766,7 +766,7 @@ def compile_translations(self, state: ModuleState, args: T.Tuple, kwargs: Compil else: return ModuleReturnValue(translations, [translations]) - def _source_to_files(self, state: ModuleState, sources: T.List[T.Union[FileOrString, build.GeneratedTypes]]) -> T.List[File]: + def _source_to_files(self, state: ModuleState, sources: T.List[str | build.TargetSources]) -> T.List[File]: content_files = [] for s in sources: @@ -833,7 +833,7 @@ def __gen_import(import_type: str, importlist: T.List[str]) -> None: version: str = match.group(4) or '' fd.write(f'{import_type} {module} {version}\n') - def __gen_declaration(qualifier: str, version: str, importlist: T.List[T.Union[FileOrString, build.GeneratedTypes]]) -> None: + def __gen_declaration(qualifier: str, version: str, importlist: T.List[str | build.TargetSources]) -> None: importpathlist = self._source_to_files(state, importlist) for s in importpathlist: basename: str = os.path.basename(s.fname) @@ -1076,8 +1076,8 @@ def qml_module(self, state: ModuleState, args: T.Tuple[str], kwargs: QmlModuleKw #same format as the one derived from qmltyperegistrar target_name = re.sub(r'[^A-Za-z0-9]', '_', module_name) - qrc_resouces: T.List[T.Union[FileOrString, build.GeneratedTypes]] = [] - all_qml: T.List[T.Union[FileOrString, build.GeneratedTypes]] = kwargs['qml_sources'] + kwargs['qml_singletons'] + kwargs['qml_internals'] + qrc_resouces: T.List[str | build.TargetSources] = [] + all_qml: T.List[str | build.TargetSources] = kwargs['qml_sources'] + kwargs['qml_singletons'] + kwargs['qml_internals'] all_qml_files: T.List[File] = self._source_to_files(state, all_qml) all_qml_basename: T.List[str] = [os.path.basename(p.fname) for p in all_qml_files] diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 84341244c065..19e2a37756c9 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -103,7 +103,7 @@ class GenerateGir(TypedDict): link_with: T.List[T.Union[build.SharedLibrary, build.StaticLibrary]] namespace: str nsversion: str - sources: T.List[T.Union[FileOrString, build.GeneratedTypes]] + sources: T.List[str | build.TargetSources] symbol_prefix: T.List[str] class GtkDoc(TypedDict): @@ -191,7 +191,7 @@ class MkEnumsSimple(_MkEnumsCommon): class MkEnums(_MkEnumsCommon): - sources: T.List[T.Union[FileOrString, build.GeneratedTypes]] + sources: T.List[str | build.TargetSources] c_template: T.Optional[FileOrString] h_template: T.Optional[FileOrString] comments: T.Optional[str] @@ -1656,10 +1656,10 @@ def gtkdoc_html_dir(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'TYP validator=in_set_validator({'all', 'none', 'objects'})), INSTALL_DIR_KW.evolve(since='0.46.0') ) - def gdbus_codegen(self, state: 'ModuleState', args: T.Tuple[str, T.Optional[T.Union['FileOrString', build.GeneratedTypes]]], + def gdbus_codegen(self, state: 'ModuleState', args: T.Tuple[str, T.Optional[str | build.TargetSources]], kwargs: 'GdbusCodegen') -> ModuleReturnValue: namebase = args[0] - xml_files: T.List[T.Union[mesonlib.File, build.GeneratedTypes]] = \ + xml_files: T.List[build.TargetSources] = \ self.interpreter.source_strings_to_files([args[1]]) if args[1] else [] cmd: CommandList = [self._find_tool(state, 'gdbus-codegen')] cmd.extend(kwargs['extra_args']) diff --git a/mesonbuild/modules/icestorm.py b/mesonbuild/modules/icestorm.py index b688ce7e2c3a..e9d02b44685c 100644 --- a/mesonbuild/modules/icestorm.py +++ b/mesonbuild/modules/icestorm.py @@ -20,8 +20,8 @@ class ProjectKwargs(TypedDict): - sources: T.List[T.Union[mesonlib.FileOrString, build.GeneratedTypes]] - constraint_file: T.Union[mesonlib.FileOrString, build.GeneratedTypes] + sources: T.List[str | build.TargetSources] + constraint_file: str | build.TargetSources class IceStormModule(ExtensionModule): @@ -54,7 +54,7 @@ def detect_tools(self, state: ModuleState) -> None: ) ) def project(self, state: ModuleState, - args: T.Tuple[str, T.List[T.Union[mesonlib.FileOrString, build.GeneratedTypes]]], + args: T.Tuple[str, T.List[str | build.TargetSources]], kwargs: ProjectKwargs) -> ModuleReturnValue: if not self.tools: self.detect_tools(state) diff --git a/mesonbuild/modules/sourceset.py b/mesonbuild/modules/sourceset.py index 1b30c7a791f7..e2b16c2bcc29 100644 --- a/mesonbuild/modules/sourceset.py +++ b/mesonbuild/modules/sourceset.py @@ -25,8 +25,8 @@ class AddKwargs(TypedDict): when: T.List[T.Union[str, dependencies.Dependency]] - if_true: T.List[T.Union[mesonlib.FileOrString, build.GeneratedTypes, dependencies.Dependency]] - if_false: T.List[T.Union[mesonlib.FileOrString, build.GeneratedTypes, dependencies.Dependency]] + if_true: T.List[T.Union[str, build.TargetSources, dependencies.Dependency]] + if_false: T.List[T.Union[str, build.TargetSources, dependencies.Dependency]] class AddAllKw(TypedDict): @@ -53,7 +53,7 @@ class SourceSetRule(T.NamedTuple): deps: T.List[dependencies.Dependency] """Dependencies that enable this rule if true""" - sources: T.List[T.Union[mesonlib.FileOrString, build.GeneratedTypes]] + sources: T.List[str | build.TargetSources] """Source files added when this rule's conditions are true""" extra_deps: T.List[dependencies.Dependency] @@ -63,7 +63,7 @@ class SourceSetRule(T.NamedTuple): sourcesets: T.List[SourceSetImpl] """Other sourcesets added when this rule's conditions are true""" - if_false: T.List[T.Union[mesonlib.FileOrString, build.GeneratedTypes]] + if_false: T.List[str | build.TargetSources] """Source files added when this rule's conditions are false""" if_false_deps: T.List[dependencies.Dependency] @@ -72,7 +72,7 @@ class SourceSetRule(T.NamedTuple): class SourceFiles(T.NamedTuple): - sources: OrderedSet[T.Union[mesonlib.FileOrString, build.GeneratedTypes]] + sources: OrderedSet[str | build.TargetSources] deps: OrderedSet[dependencies.Dependency] @@ -97,9 +97,9 @@ def __init__(self, interpreter: Interpreter): 'apply': self.apply_method, }) - def check_source_files(self, args: T.Sequence[T.Union[mesonlib.FileOrString, build.GeneratedTypes, dependencies.Dependency]], - ) -> T.Tuple[T.List[T.Union[mesonlib.FileOrString, build.GeneratedTypes]], T.List[dependencies.Dependency]]: - sources: T.List[T.Union[mesonlib.FileOrString, build.GeneratedTypes]] = [] + def check_source_files(self, args: T.Sequence[T.Union[str, build.TargetSources, dependencies.Dependency]], + ) -> T.Tuple[T.List[T.Union[str, build.TargetSources]], T.List[dependencies.Dependency]]: + sources: T.List[T.Union[str, build.TargetSources]] = [] deps: T.List[dependencies.Dependency] = [] for x in args: if isinstance(x, dependencies.Dependency): @@ -148,7 +148,7 @@ def check_conditions(self, args: T.Sequence[T.Union[str, dependencies.Dependency ), ) def add_method(self, state: ModuleState, - args: T.Tuple[T.List[T.Union[mesonlib.FileOrString, build.GeneratedTypes, dependencies.Dependency]]], + args: T.Tuple[T.List[T.Union[str, build.TargetSources, dependencies.Dependency]]], kwargs: AddKwargs) -> None: if self.frozen: raise InvalidCode('Tried to use \'add\' after querying the source set') @@ -215,7 +215,7 @@ def collect(self, enabled_fn: T.Callable[[str], bool], @noKwargs @noPosargs def all_sources_method(self, state: ModuleState, args: T.List[TYPE_var], kwargs: TYPE_kwargs - ) -> T.List[T.Union[mesonlib.FileOrString, build.GeneratedTypes]]: + ) -> T.List[str | build.TargetSources]: self.frozen = True files = self.collect(lambda x: True, True) return list(files.sources) @@ -271,7 +271,7 @@ def __init__(self, files: SourceFiles): @noPosargs @noKwargs def sources_method(self, state: ModuleState, args: T.List[TYPE_var], kwargs: TYPE_kwargs - ) -> T.List[T.Union[mesonlib.FileOrString, build.GeneratedTypes]]: + ) -> T.List[str | build.TargetSources]: return list(self.files.sources) @noPosargs From fb2c53d869c84a6bbac24fd8d6687eb0b313d112 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 11 Jun 2026 16:01:06 +0200 Subject: [PATCH 487/681] build: remove unnecessary str occurrences around target sources Build targets generally don't have to deal with strings any more. The exception is the unity source mechanism which uses strings for generated sources. Signed-off-by: Paolo Bonzini --- mesonbuild/build.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 0dacb4e5459f..8fcb4d38e5ca 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -535,20 +535,20 @@ def __repr__(self) -> str: return r.format(self.__class__.__name__, self.target.name, self.srclist) @staticmethod - def get_sources(sources: T.Sequence['FileOrString'], generated_sources: T.Sequence['GeneratedTypes']) -> T.List['FileOrString']: + def get_sources(sources: T.Sequence['File'], generated_sources: T.Sequence['GeneratedTypes']) -> T.List['FileOrString']: # Merge sources and generated sources - sources = list(sources) + ret: T.List[FileOrString] = list(sources) for gensrc in generated_sources: for s in gensrc.get_outputs(): # We cannot know the path where this source will be generated, # but all we need here is the file extension to determine the # compiler. - sources.append(s) + ret.append(s) # Filter out headers and all non-source files - return [s for s in sources if is_source(s)] + return [s for s in ret if is_source(s)] - def classify_all_sources(self, sources: T.Sequence[FileOrString], generated_sources: T.Sequence['GeneratedTypes']) -> T.Dict['Compiler', T.List['FileOrString']]: + def classify_all_sources(self, sources: T.Sequence[File], generated_sources: T.Sequence['GeneratedTypes']) -> T.Dict['Compiler', T.List['FileOrString']]: sources_ = self.get_sources(sources, generated_sources) return classify_unity_sources(self.target.compilers.values(), sources_) @@ -1106,7 +1106,7 @@ def process_compilers(self) -> T.List[Language]: if self.structured_sources: for v in self.structured_sources.sources.values(): for src in v: - if isinstance(src, (str, File)): + if isinstance(src, File): sources.append(src) else: generated.append(src) @@ -1120,9 +1120,10 @@ def process_compilers(self) -> T.List[Language]: # which is what we need. if not is_object(s): sources.append(s) + for d in self.external_deps: for s in d.sources: - if isinstance(s, (str, File)): + if isinstance(s, File): sources.append(s) # Sources that were used to create our extracted objects From 60f1f0ad155fc93d637af0692b258f53849cdc41 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 11 Jun 2026 15:53:00 +0200 Subject: [PATCH 488/681] backends: remove FileOrString from object_filename_from_source object_filename_from_source's callers have every source name converted to File. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/backends.py | 9 ++++----- mesonbuild/backend/xcodebackend.py | 6 ++---- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 74f506446c08..73e09cc17ddf 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -42,7 +42,7 @@ from ..environment import Environment from ..interpreter import Test from ..linkers import StaticLinker - from ..mesonlib import FileMode, FileOrString + from ..mesonlib import FileMode from ..options import ElementaryOptionValues from typing_extensions import Literal, TypedDict, NotRequired, TypeAlias @@ -775,8 +775,7 @@ def canonicalize_filename(fname: str) -> str: fname = fname.replace(ch, '_') return hashed + fname - def object_filename_from_source(self, target: build.BuildTarget, compiler: Compiler, source: 'FileOrString', targetdir: T.Optional[str] = None) -> str: - assert isinstance(source, mesonlib.File) + def object_filename_from_source(self, target: build.BuildTarget, compiler: Compiler, source: File, targetdir: T.Optional[str] = None) -> str: if isinstance(target, build.CompileTarget): return target.sources_map[source] build_dir = self.environment.get_build_dir() @@ -828,14 +827,14 @@ def _determine_ext_objs(self, extobj: 'build.ExtractedObjects') -> T.List[str]: targetdir = self.get_target_private_dir(extobj.target) # Merge sources and generated sources - raw_sources = list(extobj.srclist) + raw_sources: T.List[File] = list(extobj.srclist) for gensrc in extobj.genlist: for r in gensrc.get_outputs(): path = self.get_target_generated_dir(extobj.target, gensrc, r) raw_sources.append(File.from_built_relative(path)) # Filter out headers and all non-source files - sources: T.List['FileOrString'] = [] + sources: T.List[File] = [] for s in raw_sources: if compilers.is_source(s): sources.append(s) diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index 4d9b681f2aaf..ecc89015aa49 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -314,15 +314,13 @@ def get_custom_target_output_dir(self, target: build.AnyTargetType) -> str: return dirname def object_filename_from_source(self, target: build.BuildTarget, compiler: Compiler, - source: mesonlib.FileOrString, targetdir: T.Optional[str] = None) -> str: + source: mesonlib.File, targetdir: T.Optional[str] = None) -> str: # Xcode has the following naming scheme: # projectname.build/debug/prog@exe.build/Objects-normal/x86_64/func.o project = self.build.project_name buildtype = self.buildtype tname = target.get_id() - if isinstance(source, mesonlib.File): - source = source.fname - stem = os.path.splitext(os.path.basename(source))[0] + stem = os.path.splitext(os.path.basename(source.fname))[0] # Append "build" before the actual object path to match OBJROOT obj_path = f'build/{project}.build/{buildtype}/{tname}.build/Objects-normal/{self.arch}/{stem}.o' return obj_path From 1217faedcda6202c5ff1b3ce925f373552fd9a29 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 11 Jun 2026 10:46:27 +0200 Subject: [PATCH 489/681] build: never pass str to Generator.process_files Do the conversion from str before calling into Generator. This is a straightforward source_strings_to_files transformation, though _qt.py has its own _source_to_files. Signed-off-by: Paolo Bonzini --- mesonbuild/build.py | 2 +- mesonbuild/interpreter/interpreterobjects.py | 3 ++- mesonbuild/modules/_qt.py | 18 +++++++++++------- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 8fcb4d38e5ca..2da4e32605a2 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2001,7 +2001,7 @@ def get_arglist(self, inname: str) -> T.List[str]: basename = os.path.splitext(plainname)[0] return [x.replace('@BASENAME@', basename).replace('@PLAINNAME@', plainname) for x in self.arglist] - def process_files(self, files: T.Iterable[str | TargetSources], + def process_files(self, files: T.Iterable[TargetSources], subdir: str = '', preserve_path_from: T.Optional[str] = None, extra_args: T.Optional[T.List[str]] = None, diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index 41bb50cae9e2..198b6d1e834b 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -1252,7 +1252,8 @@ def process_method(self, 'Calling generator.process with CustomTarget or Index of CustomTarget.', '0.57.0', self.interpreter.subproject) - gl = self.held_object.process_files(args[0], self.interpreter.subdir, + sources = self.interpreter.source_strings_to_files(args[0]) + gl = self.held_object.process_files(sources, self.interpreter.subdir, preserve_path_from, extra_args=kwargs['extra_args'], env=kwargs['env'], extra_depends=kwargs['depends']) diff --git a/mesonbuild/modules/_qt.py b/mesonbuild/modules/_qt.py index d71fedb252eb..619c99d074dd 100644 --- a/mesonbuild/modules/_qt.py +++ b/mesonbuild/modules/_qt.py @@ -524,7 +524,8 @@ def _compile_ui_impl(self, state: ModuleState, kwargs: UICompilerKwArgs) -> buil kwargs['extra_args'] + ['-o', '@OUTPUT@', '@INPUT@'], ['ui_@BASENAME@.h'], name=f'Qt{self.qt_version} ui') - return gen.process_files(kwargs['sources'], state.subdir, preserve_path_from) + sources = self._source_to_files(state, kwargs['sources']) + return gen.process_files(sources, state.subdir, preserve_path_from) @FeatureNew('qt.compile_moc', '0.59.0') @noPosargs @@ -572,7 +573,7 @@ def _compile_moc_impl(self, state: ModuleState, kwargs: MocCompilerKwArgs) -> T. inc = state.get_include_args(include_dirs=kwargs['include_directories']) compile_args: T.List[str] = [] - sources: T.List[T.Union[build.BuildTarget, build.CustomTarget, build.CustomTargetIndex]] = [] + depends: T.List[T.Union[build.BuildTarget, build.CustomTarget, build.CustomTargetIndex]] = [] for dep in kwargs['dependencies']: compile_args.extend(a for a in dep.get_all_compile_args() if a.startswith(('-I', '-F', '-D'))) if isinstance(dep, InternalDependency): @@ -580,7 +581,7 @@ def _compile_moc_impl(self, state: ModuleState, kwargs: MocCompilerKwArgs) -> T. compile_args.extend(f'-I{i}' for i in incl.abs_string_list(self.interpreter.source_root, self.interpreter.environment.build_dir)) for src in dep.sources: if isinstance(src, (build.CustomTarget, build.BuildTarget, build.CustomTargetIndex)): - sources.append(src) + depends.append(src) output: T.List[build.GeneratedList] = [] @@ -601,10 +602,11 @@ def _compile_moc_impl(self, state: ModuleState, kwargs: MocCompilerKwArgs) -> T. moc_gen = build.Generator( state.environment, self.tools['moc'], arguments, header_gen_output, - depends=sources, + depends=depends, depfile='moc_@BASENAME@.cpp.d', name=f'Qt{self.qt_version} moc header') - output.append(moc_gen.process_files(kwargs['headers'], state.subdir, preserve_path_from)) + headers = self._source_to_files(state, kwargs['headers']) + output.append(moc_gen.process_files(headers, state.subdir, preserve_path_from)) if kwargs['sources']: source_gen_output: T.List[str] = ['@BASENAME@.moc'] if do_output_json: @@ -614,7 +616,8 @@ def _compile_moc_impl(self, state: ModuleState, kwargs: MocCompilerKwArgs) -> T. self.tools['moc'], arguments, source_gen_output, depfile='@BASENAME@.moc.d', name=f'Qt{self.qt_version} moc source') - output.append(moc_gen.process_files(kwargs['sources'], state.subdir, preserve_path_from)) + sources = self._source_to_files(state, kwargs['sources']) + output.append(moc_gen.process_files(sources, state.subdir, preserve_path_from)) return output @@ -922,7 +925,8 @@ def _gen_qml_cachegen(self, state: ModuleState, kwargs: GenQmlCachegenKwArgs) -> name=f'Qml cache generation for {target_name}') output: T.List[T.Union[build.CustomTarget, build.GeneratedList]] = [] - output.append(cache_gen.process_files(kwargs['qml_sources'], state.subdir)) + qml_sources = self._source_to_files(state, kwargs['qml_sources']) + output.append(cache_gen.process_files(qml_sources, state.subdir)) cachegen_inputs: T.List[str] = [] qml_sources_paths = self._source_to_files(state, kwargs['qml_sources']) From 580588aca3cf2ef435f01a7d9b870efa774a8035 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 10 Jun 2026 22:31:29 +0200 Subject: [PATCH 490/681] vs2010backend: remove AnyTargetType from get_target_deps A specific isinstance on CustomTargetIndex only needs to be added in one place, and it's probably fixing a bug. With that change, the id mapping dictionary can have Target as the destination type. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/vs2010backend.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index ceedf6d7a9c9..8e9170872063 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -361,8 +361,8 @@ def get_obj_target_deps(self, obj_list: list[build.ObjectTypes]) -> T.Iterable[t result[o.target.get_id()] = o.target return result.items() - def get_target_deps(self, t: T.Mapping[str, build.AnyTargetType], recursive: bool = False) -> T.Dict[str, build.AnyTargetType]: - all_deps: T.Dict[str, build.AnyTargetType] = {} + def get_target_deps(self, t: T.Mapping[str, build.Target], recursive: bool = False) -> T.Dict[str, build.Target]: + all_deps: T.Dict[str, build.Target] = {} for target in t.values(): if isinstance(target, build.CustomTargetIndex): # just transfer it to the CustomTarget code @@ -379,7 +379,10 @@ def get_target_deps(self, t: T.Mapping[str, build.AnyTargetType], recursive: boo d = d.program if isinstance(d, (programs.Program, build.GeneratedList)): continue - all_deps[d.get_id()] = d + if isinstance(d, build.CustomTargetIndex): + all_deps[d.get_id()] = d.target + else: + all_deps[d.get_id()] = d elif isinstance(target, build.BuildTarget): for ldep in target.link_targets: if isinstance(ldep, build.CustomTargetIndex): @@ -471,7 +474,7 @@ def generate_solution(self, sln_filename: str, projlist: T.List[Project]) -> Non self.environment.coredata.lang_guids[lang], prj[0], prj[1], prj[2]) ofile.write(prj_line) - target_dict: dict[str, build.AnyTargetType] = {target.get_id(): target} + target_dict: dict[str, build.Target] = {target.get_id(): target} # Get recursive deps recursive_deps = self.get_target_deps( target_dict, recursive=True) @@ -641,8 +644,8 @@ def add_project_reference(self, root: ET.Element, include: str, projid: str, lin # objects and .lib files manually. ET.SubElement(pref, 'LinkLibraryDependencies').text = 'false' - def add_target_deps(self, root: ET.Element, target: build.AnyTargetType) -> None: - target_dict: T.Dict[str, build.AnyTargetType] = {target.get_id(): target} + def add_target_deps(self, root: ET.Element, target: build.Target) -> None: + target_dict: T.Dict[str, build.Target] = {target.get_id(): target} for dep in self.get_target_deps(target_dict).values(): if dep.get_id() in self.handled_target_deps[target.get_id()]: # This dependency was already handled manually. From 932f0ba21be20f4277b2b73723ca3da884d1e102 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 11 Jun 2026 12:49:55 +0200 Subject: [PATCH 491/681] fs: introduce FilePathTypes Keep going on the convention of declaring Union as much as possible outside the functions. Signed-off-by: Paolo Bonzini --- mesonbuild/modules/fs.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/mesonbuild/modules/fs.py b/mesonbuild/modules/fs.py index ba7449026fcb..07c0b7fb29b4 100644 --- a/mesonbuild/modules/fs.py +++ b/mesonbuild/modules/fs.py @@ -26,6 +26,8 @@ from typing_extensions import TypedDict + FilePathTypes = str | File | BuildTargetTypes + class ReadKwArgs(TypedDict): """Keyword Arguments for fs.read.""" @@ -78,7 +80,7 @@ def _absolute_dir(self, state: ModuleState, arg: FileOrString) -> str: return os.path.join(state.source_root, state.subdir, os.path.expanduser(arg)) @staticmethod - def _obj_to_pathstr(feature_new_prefix: str, obj: T.Union[FileOrString, BuildTargetTypes], state: ModuleState) -> str: + def _obj_to_pathstr(feature_new_prefix: str, obj: FilePathTypes, state: ModuleState) -> str: if isinstance(obj, str): return obj @@ -203,7 +205,7 @@ def is_samepath(self, state: ModuleState, args: T.Tuple[FileOrString, FileOrStri @noKwargs @typed_pos_args('fs.replace_suffix', (str, File, CustomTarget, CustomTargetIndex, BuildTarget), str) - def replace_suffix(self, state: ModuleState, args: T.Tuple[T.Union[FileOrString, BuildTargetTypes], str], kwargs: T.Dict[str, T.Any]) -> str: + def replace_suffix(self, state: ModuleState, args: T.Tuple[FilePathTypes, str], kwargs: T.Dict[str, T.Any]) -> str: if args[1] and not args[1].startswith('.'): raise ValueError(f"Invalid suffix {args[1]!r}") path = self._obj_to_pathstr('fs.replace_suffix', args[0], state) @@ -211,27 +213,27 @@ def replace_suffix(self, state: ModuleState, args: T.Tuple[T.Union[FileOrString, @noKwargs @typed_pos_args('fs.parent', (str, File, CustomTarget, CustomTargetIndex, BuildTarget)) - def parent(self, state: ModuleState, args: T.Tuple[T.Union[FileOrString, BuildTargetTypes]], kwargs: T.Dict[str, T.Any]) -> str: + def parent(self, state: ModuleState, args: T.Tuple[FilePathTypes], kwargs: T.Dict[str, T.Any]) -> str: path = self._obj_to_pathstr('fs.parent', args[0], state) return os.path.split(path)[0] or '.' @noKwargs @typed_pos_args('fs.name', (str, File, CustomTarget, CustomTargetIndex, BuildTarget)) - def name(self, state: ModuleState, args: T.Tuple[T.Union[FileOrString, BuildTargetTypes]], kwargs: T.Dict[str, T.Any]) -> str: + def name(self, state: ModuleState, args: T.Tuple[FilePathTypes], kwargs: T.Dict[str, T.Any]) -> str: path = self._obj_to_pathstr('fs.name', args[0], state) return os.path.basename(path) @noKwargs @typed_pos_args('fs.stem', (str, File, CustomTarget, CustomTargetIndex, BuildTarget)) @FeatureNew('fs.stem', '0.54.0') - def stem(self, state: ModuleState, args: T.Tuple[T.Union[FileOrString, BuildTargetTypes]], kwargs: T.Dict[str, T.Any]) -> str: + def stem(self, state: ModuleState, args: T.Tuple[FilePathTypes], kwargs: T.Dict[str, T.Any]) -> str: path = self._obj_to_pathstr('fs.name', args[0], state) return os.path.splitext(os.path.basename(path))[0] @noKwargs @typed_pos_args('fs.suffix', (str, File, CustomTarget, CustomTargetIndex, BuildTarget)) @FeatureNew('fs.suffix', '1.9.0') - def suffix(self, state: ModuleState, args: T.Tuple[T.Union[FileOrString, BuildTargetTypes]], kwargs: T.Dict[str, T.Any]) -> str: + def suffix(self, state: ModuleState, args: T.Tuple[FilePathTypes], kwargs: T.Dict[str, T.Any]) -> str: path = self._obj_to_pathstr('fs.suffix', args[0], state) return os.path.splitext(path)[1] @@ -326,8 +328,8 @@ def copyfile(self, state: ModuleState, args: T.Tuple[FileOrString, T.Optional[st @FeatureNew('fs.relative_to', '1.3.0') @typed_pos_args('fs.relative_to', (str, File, CustomTarget, CustomTargetIndex, BuildTarget), (str, File, CustomTarget, CustomTargetIndex, BuildTarget)) @noKwargs - def relative_to(self, state: ModuleState, args: T.Tuple[T.Union[FileOrString, BuildTargetTypes], T.Union[FileOrString, BuildTargetTypes]], kwargs: TYPE_kwargs) -> str: - def to_path(arg: T.Union[FileOrString, CustomTarget, CustomTargetIndex, BuildTarget]) -> str: + def relative_to(self, state: ModuleState, args: T.Tuple[FilePathTypes, FilePathTypes], kwargs: TYPE_kwargs) -> str: + def to_path(arg: FilePathTypes) -> str: if isinstance(arg, File): return arg.absolute_path(state.environment.source_dir, state.environment.build_dir) elif isinstance(arg, (CustomTarget, CustomTargetIndex, BuildTarget)): From 4f6d4e819e88244e5034ecc0d50426601e5cca4e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 11 Jun 2026 13:14:22 +0200 Subject: [PATCH 492/681] build: introduce CommandTypes Keep going on the convention of giving a name to common Unions. In particular, commands use the same union no matter if they are for custom targets or tests, and no matter if they are produced by the interpreter or used to create an ExecutableSerialisation. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/backends.py | 9 ++++----- mesonbuild/build.py | 9 ++++----- mesonbuild/interpreter/interpreterobjects.py | 2 +- mesonbuild/interpreter/kwargs.py | 9 ++++----- mesonbuild/interpreter/mesonmain.py | 3 +-- mesonbuild/interpreter/type_checking.py | 4 ++-- mesonbuild/modules/i18n.py | 6 +++--- mesonbuild/modules/rust.py | 4 ++-- 8 files changed, 21 insertions(+), 25 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 73e09cc17ddf..92dbabe2c4e2 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -547,7 +547,7 @@ def determine_swift_dep_dirs(self, target: build.BuildTarget) -> T.List[str]: return result def get_executable_serialisation( - self, cmd: T.Sequence[T.Union[programs.Program, build.BuildTargetTypes, File, str]], + self, cmd: T.Iterable[build.CommandTypes], workdir: T.Optional[str] = None, extra_bdeps: T.Optional[T.Iterable[build.BuildTargetTypes]] = None, capture: T.Optional[str] = None, @@ -636,8 +636,8 @@ def get_executable_serialisation( exe_wrapper, workdir, extra_paths, capture, feed, tag, verbose, installdir_map) - def as_meson_exe_cmdline(self, exe: T.Union[str, mesonlib.File, build.BuildTargetTypes, programs.Program], - cmd_args: T.Sequence[T.Union[str, mesonlib.File, build.BuildTargetTypes, programs.Program]], + def as_meson_exe_cmdline(self, exe: build.CommandTypes, + cmd_args: T.Iterable[build.CommandTypes], workdir: T.Optional[str] = None, extra_bdeps: T.Optional[T.Iterable[build.BuildTargetTypes]] = None, capture: T.Optional[str] = None, @@ -649,7 +649,7 @@ def as_meson_exe_cmdline(self, exe: T.Union[str, mesonlib.File, build.BuildTarge ''' Serialize an executable for running with a generator or a custom target ''' - cmd: T.List[T.Union[str, mesonlib.File, build.BuildTargetTypes, programs.Program]] = [] + cmd: T.List[build.CommandTypes] = [] cmd.append(exe) cmd.extend(cmd_args) es = self.get_executable_serialisation(cmd, workdir, extra_bdeps, capture, feed, env, can_use_rsp_file, verbose=verbose) @@ -1201,7 +1201,6 @@ def create_test_serialisation(self, tests: T.List['Test']) -> T.List[TestSeriali # we allow passing compiled executables to tests, which may be cross built. # We need to consider these as well when considering whether the target is cross or not. - a: T.Union[str, File, build.Target, build.BuildTargetTypes, programs.Program] for a in t.cmd_args: if isinstance(a, build.LocalProgram): a = a.program diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 2da4e32605a2..f7ef90a6b2cc 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -54,6 +54,7 @@ from .options import ElementaryOptionValues TargetSources: TypeAlias = T.Union['File', 'GeneratedTypes'] + CommandTypes: TypeAlias = T.Union['programs.Program', 'BuildTargetTypes', File, str] GeneratedTypes: TypeAlias = T.Union['CustomTarget', 'CustomTargetIndex', 'GeneratedList'] LibTypes: TypeAlias = T.Union['SharedLibrary', 'StaticLibrary', 'CustomTarget', 'CustomTargetIndex'] LinkableTargetTypes: TypeAlias = T.Union['SharedLibrary', 'StaticLibrary', 'CustomTarget', 'CustomTargetIndex', 'Executable'] @@ -2838,7 +2839,7 @@ def is_linkable_target(self) -> bool: return True -def flatten_command(cmd: T.Sequence[str | File | programs.Program | BuildTargetTypes], +def flatten_command(cmd: T.Iterable[CommandTypes], subproject: SubProject) -> tuple[list[str | File | BuildTarget | CustomTarget | programs.Program], list[File], list[BuildTarget | CustomTarget]]: final_cmd: list[str | File | programs.Program | BuildTarget | CustomTarget] = [] @@ -2934,9 +2935,7 @@ def __init__(self, subdir: str, subproject: SubProject, environment: Environment, - command: T.Sequence[T.Union[ - str, BuildTargetTypes, - programs.Program, File]], + command: T.Sequence[CommandTypes], sources: T.Sequence[CustomTargetSources], outputs: T.List[str], *, @@ -3221,7 +3220,7 @@ class RunTarget(Target): typename = 'run' def __init__(self, name: str, - command: T.Sequence[T.Union[str, File, BuildTargetTypes, programs.Program]], + command: T.Sequence[CommandTypes], # the RunTarget case is used by gnome.yelp() dependencies: T.Sequence[Target | CustomTargetIndex | GeneratedList | programs.Program], subdir: str, diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index 198b6d1e834b..6e9a25dbf8b0 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -842,7 +842,7 @@ def __init__(self, name: str, project: str, suite: T.List[str], exe: T.Union[Program, build.Executable, build.CustomTarget, build.CustomTargetIndex, build.Jar], depends: T.Sequence[kwargs.TargetDepends], is_parallel: bool, - cmd_args: T.List[T.Union[str, mesonlib.File, build.Target, Program]], + cmd_args: T.List[build.CommandTypes], env: mesonlib.EnvironmentVariables, expected_fail: bool, expected_exitcode: int, timeout: int, workdir: T.Optional[str], protocol: str, priority: int, verbose: bool): diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index d9aa399449c9..99e32a9504f7 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -21,7 +21,6 @@ from ..programs import Program, ExternalProgram from .type_checking import PkgConfigDefineType, SourcesVarargsType -TestArgs = T.Union[str, File, build.Target, Program] TargetDepends = T.Union[build.CustomTarget, build.CustomTargetIndex, build.BuildTarget, build.GeneratedList, Program] CustomTargetInputs = T.Union[str, build.BuildTarget, build.GeneratedTypes, build.ExtractedObjects, Program, File] @@ -69,7 +68,7 @@ class FuncBenchmark(BaseTest): """Keyword arguments used by `benchmark` and `test`, but not Rust""" # Needs complex refactor for rust test to go in BaseTest - args: T.List[TestArgs] + args: T.List[build.CommandTypes] protocol: Literal['exitcode', 'tap', 'gtest', 'rust'] class FuncTest(FuncBenchmark): @@ -201,7 +200,7 @@ class FuncAddLanguages(ExtractRequired): class RunTarget(TypedDict): - command: T.List[T.Union[str, build.BuildTargetTypes, Program, File]] + command: T.List[build.CommandTypes] depends: T.List[TargetDepends] env: EnvironmentVariables @@ -213,7 +212,7 @@ class CustomTarget(TypedDict): build_by_default: T.Optional[bool] build_subdir: str capture: bool - command: T.List[T.Union[str, build.BuildTargetTypes, Program, File]] + command: T.List[build.CommandTypes] console: bool depend_files: T.List[FileOrString] depends: T.List[TargetDepends] @@ -313,7 +312,7 @@ class ConfigurationDataSet(TypedDict): class VcsTag(TypedDict): - command: T.List[T.Union[str, build.BuildTargetTypes, Program, File]] + command: T.List[build.CommandTypes] fallback: T.Optional[str] input: list[CustomTargetInputs] output: T.List[str] diff --git a/mesonbuild/interpreter/mesonmain.py b/mesonbuild/interpreter/mesonmain.py index cea248e558a9..9f30fd625232 100644 --- a/mesonbuild/interpreter/mesonmain.py +++ b/mesonbuild/interpreter/mesonmain.py @@ -128,8 +128,7 @@ def _process_script_args( @InterpreterObject.method('add_install_script') def add_install_script_method( self, - args: T.Tuple[T.Union[str, mesonlib.File, build.Executable, Program], - T.List[T.Union[str, mesonlib.File, build.BuildTargetTypes, Program]]], + args: T.Tuple[T.Union[str, mesonlib.File, build.Executable, Program], T.List[build.CommandTypes]], kwargs: 'AddInstallScriptKW') -> None: script_args = self._process_script_args('add_install_script', args[1]) script = self._find_source_script('add_install_script', args[0], script_args, allow_built_program=True) diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py index 90d4d1bd14ed..7399ddfadb2e 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -26,7 +26,7 @@ from typing_extensions import Literal from .kwargs import CustomTargetInputs - from ..build import ObjectTypes, GeneratedTypes, BuildTargetTypes, TargetSources + from ..build import ObjectTypes, GeneratedTypes, CommandTypes, TargetSources from ..interpreterbase import TYPE_var from ..options import ElementaryOptionValues from ..mesonlib import EnvInitValueType @@ -287,7 +287,7 @@ def _env_convertor(value: FullEnvInitValueType) -> EnvironmentVariables: default=[], ) -COMMAND_KW: KwargInfo[T.List[T.Union[str, BuildTargetTypes, Program, File]]] = KwargInfo( +COMMAND_KW: KwargInfo[T.List[T.Union[str, CommandTypes, Program, File]]] = KwargInfo( 'command', ContainerTypeInfo(list, (str, BuildTarget, CustomTarget, CustomTargetIndex, Program, File), allow_empty=False), required=True, diff --git a/mesonbuild/modules/i18n.py b/mesonbuild/modules/i18n.py index 2ef97e3e0111..dc27f3f5c740 100644 --- a/mesonbuild/modules/i18n.py +++ b/mesonbuild/modules/i18n.py @@ -25,7 +25,7 @@ from typing_extensions import Literal, TypedDict from . import ModuleState - from ..build import Target + from ..build import CommandTypes, Target from ..interpreter import Interpreter from ..interpreter.interpreter import CustomTargetSources from ..interpreter.kwargs import CustomTargetInputs @@ -309,7 +309,7 @@ def merge_file(self, state: 'ModuleState', args: T.List['TYPE_var'], kwargs: 'Me ddirs = self._get_data_dirs(state, kwargs['data_dirs']) datadirs = '--datadirs=' + ':'.join(ddirs) if ddirs else None - command: T.List[T.Union[str, build.BuildTargetTypes, Program, mesonlib.File]] = [] + command: T.List[CommandTypes] = [] command.extend(state.environment.get_build_command()) command.extend([ '--internal', 'msgfmthelper', @@ -492,7 +492,7 @@ def itstool_join(self, state: 'ModuleState', args: T.List['TYPE_var'], kwargs: ' for target in mo_targets: mo_fnames.append(path.join(target.get_builddir(), target.get_outputs()[0])) - command: T.List[T.Union[str, build.BuildTargetTypes, Program, mesonlib.File]] = [] + command: T.List[CommandTypes] = [] command.extend(state.environment.get_build_command()) itstool_cmd = self.tools['itstool'].get_command() diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index 5e5e8575c0b6..887cfb5926ac 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -31,7 +31,7 @@ if T.TYPE_CHECKING: from . import ModuleState from .. import cargo - from ..build import ExecutableKeywordArguments, GeneratedTypes, IncludeDirs, LinkableTargetTypes + from ..build import ExecutableKeywordArguments, GeneratedTypes, IncludeDirs, LinkableTargetTypes, CommandTypes from ..cargo.interpreter import RUST_ABI from ..compilers.compilers import Language from ..compilers.rust import RustCompiler @@ -57,7 +57,7 @@ class FuncRustTest(_kwargs.BaseTest, T.Generic[ArgsType]): link_whole: T.List[T.Union[StaticLibrary, CustomTarget, CustomTargetIndex]] rust_args: T.List[str] - FuncTest = FuncRustTest[_kwargs.TestArgs] + FuncTest = FuncRustTest[CommandTypes] FuncDoctest = FuncRustTest[str] class FuncBindgen(TypedDict): From 2d87b25b08727c4777eb102a96fade1581816a59 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 11 Jun 2026 15:12:20 +0200 Subject: [PATCH 493/681] pkgconfig: cleanup TypeAliases Use the existing unions throughout, and introduce another one for _PKG_REQUIRES. Also refine the usage of unions from build.py Signed-off-by: Paolo Bonzini --- mesonbuild/modules/pkgconfig.py | 40 +++++++++++++++++---------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py index dbd5b918204e..57c6392f69f6 100644 --- a/mesonbuild/modules/pkgconfig.py +++ b/mesonbuild/modules/pkgconfig.py @@ -30,7 +30,8 @@ from .. import mparser from ..interpreter import Interpreter - ANY_DEP = T.Union[dependencies.Dependency, build.BuildTargetTypes, str] + ANY_DEP = T.Union[dependencies.Dependency, build.LinkableTargetTypes, str] + REQS = T.Union[dependencies.Dependency, build.LibTypes, str] LIBS = T.Union[build.LibTypes, str] class GenerateKw(TypedDict): @@ -44,10 +45,12 @@ class GenerateKw(TypedDict): subdirs: T.List[str] conflicts: T.List[str] dataonly: bool + # Executable is not accepted by the DSL, but the DependenciesHelper + # has to handle it for link_with so use ANY_DEP here. libraries: T.List[ANY_DEP] libraries_private: T.List[ANY_DEP] - requires: T.List[T.Union[str, build.StaticLibrary, build.SharedLibrary, dependencies.Dependency]] - requires_private: T.List[T.Union[str, build.StaticLibrary, build.SharedLibrary, dependencies.Dependency]] + requires: T.List[REQS] + requires_private: T.List[REQS] install_dir: T.Optional[str] d_module_versions: T.List[T.Union[str, int]] extra_cflags: T.List[str] @@ -58,7 +61,7 @@ class GenerateKw(TypedDict): unescaped_uninstalled_variables: T.Dict[str, str] -_PKG_LIBRARIES: KwargInfo[T.List[T.Union[str, dependencies.Dependency, build.SharedLibrary, build.StaticLibrary, build.CustomTarget, build.CustomTargetIndex]]] = KwargInfo( +_PKG_LIBRARIES: KwargInfo[T.List[ANY_DEP]] = KwargInfo( 'libraries', ContainerTypeInfo(list, (str, dependencies.Dependency, build.SharedLibrary, build.StaticLibrary, @@ -67,7 +70,7 @@ class GenerateKw(TypedDict): listify=True, ) -_PKG_REQUIRES: KwargInfo[T.List[T.Union[str, build.SharedLibrary, build.StaticLibrary, dependencies.Dependency]]] = KwargInfo( +_PKG_REQUIRES: KwargInfo[T.List[REQS]] = KwargInfo( 'requires', ContainerTypeInfo(list, (str, build.SharedLibrary, build.StaticLibrary, dependencies.Dependency)), default=[], @@ -101,7 +104,7 @@ def __init__(self, state: ModuleState, name: str, metadata: T.Dict[str, MetaData self.cflags: T.List[str] = [] self.cflags_private: T.List[str] = [] self.version_reqs: T.DefaultDict[str, T.Set[str]] = defaultdict(set) - self.link_whole_targets: T.List[T.Union[build.CustomTarget, build.CustomTargetIndex, build.StaticLibrary]] = [] + self.link_whole_targets: T.List[build.StaticTargetTypes] = [] self.uninstalled_incdirs: mesonlib.OrderedSet[str] = mesonlib.OrderedSet() def add_pub_libs(self, libs: T.List[ANY_DEP]) -> None: @@ -115,13 +118,13 @@ def add_priv_libs(self, libs: T.List[ANY_DEP]) -> None: self.priv_libs = p_libs + self.priv_libs self.priv_reqs += reqs - def add_pub_reqs(self, reqs: T.List[T.Union[str, build.StaticLibrary, build.SharedLibrary, dependencies.Dependency]]) -> None: + def add_pub_reqs(self, reqs: T.List[REQS]) -> None: self.pub_reqs += self._process_reqs(reqs) - def add_priv_reqs(self, reqs: T.List[T.Union[str, build.StaticLibrary, build.SharedLibrary, dependencies.Dependency]]) -> None: + def add_priv_reqs(self, reqs: T.List[REQS]) -> None: self.priv_reqs += self._process_reqs(reqs) - def _check_generated_pc_deprecation(self, obj: T.Union[build.CustomTarget, build.CustomTargetIndex, build.StaticLibrary, build.SharedLibrary]) -> None: + def _check_generated_pc_deprecation(self, obj: build.LibTypes) -> None: if obj.get_id() in self.metadata: return data = self.metadata[obj.get_id()] @@ -138,7 +141,7 @@ def _check_generated_pc_deprecation(self, obj: T.Union[build.CustomTarget, build location=data.location) data.warned = True - def _process_reqs(self, reqs: T.Sequence[T.Union[str, build.StaticLibrary, build.SharedLibrary, dependencies.Dependency]]) -> T.List[str]: + def _process_reqs(self, reqs: T.Sequence[REQS]) -> T.List[str]: '''Returns string names of requirements''' processed_reqs: T.List[str] = [] for obj in mesonlib.listify(reqs): @@ -197,9 +200,9 @@ def _add_uninstalled_incdirs(self, incdirs: T.List[build.IncludeDirs], subdir: T def _process_libs( self, libs: T.List[ANY_DEP], public: bool - ) -> T.Tuple[T.List[T.Union[str, build.SharedLibrary, build.StaticLibrary, build.CustomTarget, build.CustomTargetIndex]], T.List[str], T.List[str]]: + ) -> T.Tuple[T.List[LIBS], T.List[str], T.List[str]]: libs = mesonlib.listify(libs) - processed_libs: T.List[T.Union[str, build.SharedLibrary, build.StaticLibrary, build.CustomTarget, build.CustomTargetIndex]] = [] + processed_libs: T.List[LIBS] = [] processed_reqs: T.List[str] = [] processed_cflags: T.List[str] = [] for obj in libs: @@ -264,8 +267,8 @@ def _process_libs( return processed_libs, processed_reqs, processed_cflags def _add_lib_dependencies( - self, link_targets: T.Sequence[build.BuildTargetTypes], - link_whole_targets: T.Sequence[T.Union[build.StaticLibrary, build.CustomTarget, build.CustomTargetIndex]], + self, link_targets: T.Sequence[build.LinkableTargetTypes], + link_whole_targets: T.Sequence[build.StaticTargetTypes], external_deps: T.List[dependencies.Dependency], public: bool, private_external_deps: bool = False) -> None: @@ -289,7 +292,7 @@ def _add_lib_dependencies( else: add_libs(T.cast('T.List[ANY_DEP]', external_deps)) - def _add_link_whole(self, t: T.Union[build.CustomTarget, build.CustomTargetIndex, build.StaticLibrary], public: bool) -> None: + def _add_link_whole(self, t: build.StaticTargetTypes, public: bool) -> None: # Don't include static libraries that we link_whole. But we still need to # include their dependencies: a static library we link_whole # could itself link to a shared library or an installed static library. @@ -347,7 +350,7 @@ def remove_dups(self) -> None: # We can't just check if 'x' is excluded because we could have copies of # the same SharedLibrary object for example. - def _ids(x: T.Union[str, build.CustomTarget, build.CustomTargetIndex, build.StaticLibrary, build.SharedLibrary]) -> T.Iterable[str]: + def _ids(x: LIBS) -> T.Iterable[str]: if isinstance(x, str): yield x else: @@ -356,7 +359,7 @@ def _ids(x: T.Union[str, build.CustomTarget, build.CustomTargetIndex, build.Stat yield x.get_id() # Exclude 'x' in all its forms and return if it was already excluded - def _add_exclude(x: T.Union[str, build.CustomTarget, build.CustomTargetIndex, build.StaticLibrary, build.SharedLibrary]) -> bool: + def _add_exclude(x: LIBS) -> bool: was_excluded = False for i in _ids(x): if i in exclude: @@ -424,8 +427,7 @@ def postconf_hook(self, b: build.Build) -> None: if self.devenv is not None: b.devenv.append(self.devenv) - def _get_lname(self, l: T.Union[build.SharedLibrary, build.StaticLibrary, build.CustomTarget, build.CustomTargetIndex], - msg: str, pcfile: str) -> str: + def _get_lname(self, l: build.LibTypes, msg: str, pcfile: str) -> str: if isinstance(l, (build.CustomTargetIndex, build.CustomTarget)): basename = os.path.basename(l.get_filename()) name = os.path.splitext(basename)[0] From 5b9f61fbf728bc94eea2189dcadfe950d2f2edc5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 11 Jun 2026 22:36:01 +0200 Subject: [PATCH 494/681] utils: do not use Sequence[str] --- mesonbuild/utils/universal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index 60ffeb8b95e7..9d6c1e24602e 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -528,7 +528,7 @@ def get_compiler_for_source(compilers: T.Iterable['Compiler'], src: 'FileOrStrin raise MesonException(f'No specified compiler can handle file {src!s}') -def classify_unity_sources(compilers: T.Iterable['Compiler'], sources: T.Sequence[FileLike]) -> T.Dict['Compiler', T.List[FileLike]]: +def classify_unity_sources(compilers: T.Iterable['Compiler'], sources: T.List[FileLike]) -> T.Dict['Compiler', T.List[FileLike]]: compsrclist: T.Dict['Compiler', T.List[FileLike]] = {} for src in sources: comp = get_compiler_for_source(compilers, src) From 01678d4a484e180a8e4f2732f44b455745c438a6 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 11 Jun 2026 12:41:00 +0200 Subject: [PATCH 495/681] build: add get_target() Signed-off-by: Paolo Bonzini --- mesonbuild/backend/backends.py | 5 +---- mesonbuild/backend/ninjabackend.py | 4 +--- mesonbuild/backend/vs2010backend.py | 31 +++++++-------------------- mesonbuild/backend/xcodebackend.py | 28 +++++++----------------- mesonbuild/build.py | 8 ++++++- mesonbuild/interpreter/interpreter.py | 6 ++---- mesonbuild/interpreter/mesonmain.py | 5 +---- mesonbuild/modules/hotdoc.py | 13 ++++------- 8 files changed, 32 insertions(+), 68 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 92dbabe2c4e2..05b70cd66106 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -393,10 +393,7 @@ def get_target_dir_cached(self, target: build.Target) -> str: return dirname def get_target_dir(self, target: build.AnyTargetType) -> str: - if isinstance(target, build.CustomTargetIndex): - return self.get_target_dir_cached(target.target) - else: - return self.get_target_dir_cached(target) + return self.get_target_dir_cached(target.get_target()) def get_target_dir_relative_to(self, t: T.Union[build.Target, build.CustomTargetIndex], diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index ed4398d2d315..d10f7cb07548 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1241,9 +1241,7 @@ def select_sources_to_scan(self, compiled_sources: T.List[str], def process_target_dependencies(self, target: build.BuildTarget) -> None: for t in target.get_dependencies(): if t.get_id() not in self.processed_targets: - if isinstance(t, build.CustomTargetIndex): - t = t.target - self.generate_target(t) + self.generate_target(t.get_target()) def custom_target_generator_inputs(self, target: build.CustomTarget) -> None: for s in target.sources: diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 8e9170872063..139e31a4c436 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -379,30 +379,19 @@ def get_target_deps(self, t: T.Mapping[str, build.Target], recursive: bool = Fal d = d.program if isinstance(d, (programs.Program, build.GeneratedList)): continue - if isinstance(d, build.CustomTargetIndex): - all_deps[d.get_id()] = d.target - else: - all_deps[d.get_id()] = d + all_deps[d.get_id()] = d.get_target() elif isinstance(target, build.BuildTarget): for ldep in target.link_targets: - if isinstance(ldep, build.CustomTargetIndex): - all_deps[ldep.get_id()] = ldep.target - else: - all_deps[ldep.get_id()] = ldep + all_deps[ldep.get_id()] = ldep.get_target() for ldep in target.link_whole_targets: - if isinstance(ldep, build.CustomTargetIndex): - all_deps[ldep.get_id()] = ldep.target - else: - all_deps[ldep.get_id()] = ldep + all_deps[ldep.get_id()] = ldep.get_target() for ldep in target.link_depends: - if isinstance(ldep, build.CustomTargetIndex): - all_deps[ldep.get_id()] = ldep.target - elif isinstance(ldep, File): + if isinstance(ldep, File): # Already built, no target references needed pass else: - all_deps[ldep.get_id()] = ldep + all_deps[ldep.get_id()] = ldep.get_target() for obj_id, objdep in self.get_obj_target_deps(target.objects): all_deps[obj_id] = objdep @@ -410,10 +399,8 @@ def get_target_deps(self, t: T.Mapping[str, build.Target], recursive: bool = Fal raise MesonException(f'Unknown target type for target {target}') for gendep in target.get_generated_sources(): - if isinstance(gendep, build.CustomTarget): - all_deps[gendep.get_id()] = gendep - elif isinstance(gendep, build.CustomTargetIndex): - all_deps[gendep.target.get_id()] = gendep.target + if isinstance(gendep, (build.CustomTarget, build.CustomTargetIndex)): + all_deps[gendep.get_id()] = gendep.get_target() else: generator = gendep.get_generator() for d in itertools.chain(generator.depends, gendep.depends, gendep.extra_depends): @@ -481,9 +468,7 @@ def generate_solution(self, sln_filename: str, projlist: T.List[Project]) -> Non ofile.write('EndProject\n') for dep, target in recursive_deps.items(): if prj[0] in default_projlist: - if isinstance(target, build.CustomTargetIndex): - target = target.target - default_projlist[dep] = target + default_projlist[dep] = target.get_target() test_line = prj_templ % (self.environment.coredata.lang_guids['default'], 'RUN_TESTS', 'RUN_TESTS.vcxproj', diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index ecc89015aa49..1f15402d132c 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -591,13 +591,8 @@ def generate_target_dependency_map(self) -> None: self.target_dependency_map: T.Dict[T.Union[str, T.Tuple[str, str]], str] = {} for tname, t in self.build_targets.items(): for target in t.link_targets: - if isinstance(target, build.CustomTargetIndex): - k = (tname, target.target.get_basename()) - if k in self.target_dependency_map: - continue - else: - k = (tname, target.get_basename()) - assert k not in self.target_dependency_map + k = (tname, target.get_basename()) + assert k not in self.target_dependency_map self.target_dependency_map[k] = self.gen_id() for tname, t in self.custom_targets.items(): k = tname @@ -1324,10 +1319,8 @@ def generate_pbx_native_target(self, objects_dict: PbxDict) -> None: for lt in self.build_targets[tname].link_targets: # NOT DOCUMENTED, may need to make different links # to same target have different targetdependency item. - if isinstance(lt, build.CustomTarget): - dep_array.add_item(self.pbx_custom_dep_map[lt.get_id()], lt.name) - elif isinstance(lt, build.CustomTargetIndex): - dep_array.add_item(self.pbx_custom_dep_map[lt.target.get_id()], lt.target.name) + if isinstance(lt, (build.CustomTarget, build.CustomTargetIndex)): + dep_array.add_item(self.pbx_custom_dep_map[lt.get_id()], lt.get_basename()) else: idval = self.pbx_dep_map[lt.get_id()] dep_array.add_item(idval, 'PBXTargetDependency') @@ -1338,10 +1331,8 @@ def generate_pbx_native_target(self, objects_dict: PbxDict) -> None: dep_array.add_item(idval, 'PBXTargetDependency') generator_id = 0 for o in t.generated: - if isinstance(o, build.CustomTarget): - dep_array.add_item(self.pbx_custom_dep_map[o.get_id()], o.name) - elif isinstance(o, build.CustomTargetIndex): - dep_array.add_item(self.pbx_custom_dep_map[o.target.get_id()], o.target.name) + if isinstance(o, (build.CustomTarget, build.CustomTargetIndex)): + dep_array.add_item(self.pbx_custom_dep_map[o.get_id()], o.get_basename()) generator_id += 1 @@ -1659,11 +1650,8 @@ def determine_internal_dep_link_args(self, target: build.BuildTarget, buildtype: for l in target.link_targets: if isinstance(target, build.SharedModule) and isinstance(l, build.Executable): continue - if isinstance(l, build.CustomTargetIndex): - rel_dir = self.get_custom_target_output_dir(l.target) - libname = l.get_filename() - elif isinstance(l, build.CustomTarget): - rel_dir = self.get_custom_target_output_dir(l) + if isinstance(l, (build.CustomTarget, build.CustomTargetIndex)): + rel_dir = self.get_custom_target_output_dir(l.get_target()) libname = l.get_filename() else: rel_dir = self.get_target_dir(l) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index f7ef90a6b2cc..1d2745102767 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -37,7 +37,7 @@ from .interpreterbase import FeatureNew, FeatureDeprecated if T.TYPE_CHECKING: - from typing_extensions import Literal, TypeAlias, TypedDict + from typing_extensions import Literal, Self, TypeAlias, TypedDict from .arglist import CompilerArgs from .environment import Environment @@ -684,6 +684,9 @@ def get_build_subdir(self) -> str: def get_builddir(self) -> str: return self.builddir + def get_target(self) -> Self: + return self + @staticmethod def _get_id_hash(target_id: str) -> str: # We don't really need cryptographic security here. @@ -3394,6 +3397,9 @@ def get_filename(self) -> str: def get_id(self) -> str: return self.target.get_id() + def get_target(self) -> BuildTarget | CustomTarget: + return self.target + def get_all_link_deps(self) -> ImmutableListProtocol[BuildTargetTypes]: return self.target.get_all_link_deps() diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index c87c9f4feae4..9c7dd243582e 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -2307,10 +2307,8 @@ def make_test(self, node: mparser.BaseNode, raise InvalidArguments('Tried to use not-found external program as test exe') elif isinstance(exe, mesonlib.File): exe = self.find_program_impl([exe]) - if isinstance(exe, (build.Executable, build.CustomTarget)): - kwargs.setdefault('depends', []).append(exe) - elif isinstance(exe, build.CustomTargetIndex): - kwargs.setdefault('depends', []).append(exe.target) + if isinstance(exe, (build.Executable, build.CustomTarget, build.CustomTargetIndex)): + kwargs.setdefault('depends', []).append(exe.get_target()) if kwargs['timeout'] <= 0: FeatureNew.single_use('test() timeout <= 0', '0.57.0', self.subproject, location=node) diff --git a/mesonbuild/interpreter/mesonmain.py b/mesonbuild/interpreter/mesonmain.py index 9f30fd625232..d5f03d01c6ea 100644 --- a/mesonbuild/interpreter/mesonmain.py +++ b/mesonbuild/interpreter/mesonmain.py @@ -99,10 +99,7 @@ def _process_script_args( # this without completely rewriting install script handling. # This is complicated by the fact that the install target # depends on all. - if isinstance(a, build.CustomTargetIndex): - a.target.build_by_default = True - else: - a.build_by_default = True + a.get_target().build_by_default = True else: script_args.extend(a.get_command()) new = True diff --git a/mesonbuild/modules/hotdoc.py b/mesonbuild/modules/hotdoc.py index 08b033753fc9..5f6a21d53ed6 100644 --- a/mesonbuild/modules/hotdoc.py +++ b/mesonbuild/modules/hotdoc.py @@ -202,10 +202,8 @@ def process_dependencies(self, deps: T.Sequence[TargetDepends | Dependency | Fil self.include_paths.add(os.path.join(self.builddir, dep.hotdoc_conf.subdir)) self.cmd += ['--extra-assets=' + p for p in dep.extra_assets] self.add_extension_paths(dep.extra_extension_paths) - elif isinstance(dep, (CustomTarget, build.BuildTarget)): - self.extra_depends.append(dep) - elif isinstance(dep, CustomTargetIndex): - self.extra_depends.append(dep.target) + elif isinstance(dep, (CustomTarget, build.BuildTarget, CustomTargetIndex)): + self.extra_depends.append(dep.get_target()) return [f.strip('-I') for f in cflags] @@ -230,11 +228,8 @@ def flatten_config_command(self) -> T.List[str]: elif isinstance(arg, build.IncludeDirs): cmd.extend(arg.abs_string_list(self.sourcedir, self.builddir)) continue - elif isinstance(arg, (build.BuildTarget, CustomTarget)): - self.extra_depends.append(arg) - arg = self.interpreter.backend.get_target_filename_abs(arg) - elif isinstance(arg, CustomTargetIndex): - self.extra_depends.append(arg.target) + elif isinstance(arg, (build.BuildTarget, CustomTarget, CustomTargetIndex)): + self.extra_depends.append(arg.get_target()) arg = self.interpreter.backend.get_target_filename_abs(arg) cmd.append(arg) From 5d590c8dfc669c1bca7d53f56c706aa54a784e45 Mon Sep 17 00:00:00 2001 From: Zephyr Lykos Date: Wed, 10 Dec 2025 05:45:03 +0800 Subject: [PATCH 496/681] compilers: assume MSVC supports C++ Modules if VSCMD_VER not set Co-authored-by: Paolo Bonzini --- mesonbuild/utils/universal.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index 9d6c1e24602e..e430d70686e3 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -911,14 +911,13 @@ def detect_vcs(source_dir: T.Union[str, Path]) -> T.Optional[VcsData]: return None def current_vs_supports_modules() -> bool: + # if in a developer terminal, the version is available + # and can be used to avoid using modules for older versions + # of the MSVC executable. vsver = os.environ.get('VSCMD_VER', '') - nums = vsver.split('.', 2) - major = int(nums[0]) - if major >= 17: - return True - if major == 16 and int(nums[1]) >= 10: - return True - return vsver.startswith('16.9.0') and '-pre.' in vsver + return not vsver \ + or version_compare(vsver, '>=16.10.0') \ + or (vsver.startswith('16.9.0') and '-pre.' in vsver) _VERSION_TOK_RE = re.compile(r'(\d+)|([a-zA-Z]+)') From 1e2cbc34db83be6de3dec8be37866e04c98cfe7f Mon Sep 17 00:00:00 2001 From: Zephyr Lykos Date: Wed, 18 Mar 2026 11:05:07 +0800 Subject: [PATCH 497/681] Add win_subsystem to SharedLibrary and SharedModule --- mesonbuild/backend/ninjabackend.py | 2 +- mesonbuild/build.py | 3 +++ mesonbuild/interpreter/interpreter.py | 4 ++++ mesonbuild/interpreter/kwargs.py | 12 ++++++------ mesonbuild/interpreter/type_checking.py | 20 +++++++++++++------- 5 files changed, 27 insertions(+), 14 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index d10f7cb07548..927d3c48c4ca 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -3611,7 +3611,7 @@ def get_target_type_link_args(self, target: build.BuildTarget, linker: T.Union[S def get_target_type_link_args_post_dependencies(self, target: build.BuildTarget, linker: T.Union[Compiler, StaticLinker]) -> T.List[str]: commands: T.List[str] = [] - if isinstance(target, build.Executable): + if isinstance(target, (build.Executable, build.SharedLibrary)): assert isinstance(linker, Compiler) # If win_subsystem is significant on this platform, add the appropriate linker arguments. diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 1d2745102767..f1648f5c176c 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -137,6 +137,7 @@ class ExecutableKeywordArguments(BuildTargetKeywordArguments, total=False): class SharedModuleKeywordArguments(BuildTargetKeywordArguments, total=False): vs_module_defs: T.Union[File, CustomTarget, CustomTargetIndex] + win_subsystem: str class SharedLibraryKeywordArguments(SharedModuleKeywordArguments, total=False): @@ -2482,6 +2483,8 @@ def __init__( # If unspecified, pick the soversion self.darwin_versions = (self.soversion, self.soversion) + self.win_subsystem = kwargs.get('win_subsystem') or 'console' + self.vs_module_defs: File | None = None # Visual Studio module-definitions file self.process_vs_module_defs_kw(kwargs) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 9c7dd243582e..c3e7ac949f5d 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -3739,6 +3739,8 @@ def __convert_shared_library_kwargs(self, node: mparser.BaseNode, kwargs: kwtype kwargs['rust_abi'], kwargs['rust_crate_type'], 'dylib', 'cdylib', build.SharedLibrary.typename, extra_valid_types={'proc-macro'}) + final['win_subsystem'] = kwargs['win_subsystem'] or 'console' + return final def __convert_shared_module_kwargs(self, node: mparser.BaseNode, kwargs: kwtypes.SharedModule) -> build.SharedModuleKeywordArguments: @@ -3761,6 +3763,8 @@ def __convert_shared_module_kwargs(self, node: mparser.BaseNode, kwargs: kwtypes kwargs['rust_abi'], kwargs['rust_crate_type'], 'dylib', 'cdylib', build.SharedModule.typename, extra_valid_types={'proc-macro'}) + final['win_subsystem'] = kwargs['win_subsystem'] or 'console' + return final def __convert_jar_kwargs(self, node: mparser.BaseNode, kwargs: kwtypes.Jar) -> build.JarKeywordArguments: diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index 99e32a9504f7..a3a2ace86798 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -435,9 +435,10 @@ class _LibraryMixin(TypedDict): rust_abi: T.Optional[RustAbi] -class _VsModuleDefsMixin(TypedDict): +class _LinkableTargetMixin(TypedDict): vs_module_defs: T.Optional[T.Union[str, File, build.CustomTarget, build.CustomTargetIndex]] + win_subsystem: T.Optional[str] class _ExecutableMixin(TypedDict): @@ -446,11 +447,10 @@ class _ExecutableMixin(TypedDict): gui_app: T.Optional[bool] implib: T.Optional[T.Union[str, bool]] pie: T.Optional[bool] - win_subsystem: T.Optional[str] android_exe_type: T.Optional[Literal['application', 'executable']] -class Executable(BuildTarget, _ExecutableMixin, _VsModuleDefsMixin): +class Executable(BuildTarget, _ExecutableMixin, _LinkableTargetMixin): pass @@ -472,15 +472,15 @@ class _SharedLibMixin(TypedDict): shortname: str -class SharedLibrary(BuildTarget, _SharedLibMixin, _LibraryMixin, _VsModuleDefsMixin): +class SharedLibrary(BuildTarget, _SharedLibMixin, _LibraryMixin, _LinkableTargetMixin): pass -class SharedModule(BuildTarget, _LibraryMixin, _VsModuleDefsMixin): +class SharedModule(BuildTarget, _LibraryMixin, _LinkableTargetMixin): pass -class Library(BuildTarget, _SharedLibMixin, _StaticLibMixin, _LibraryMixin, _VsModuleDefsMixin): +class Library(BuildTarget, _SharedLibMixin, _StaticLibMixin, _LibraryMixin, _LinkableTargetMixin): """For library, both_library, and as a base for build_target""" diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py index 7399ddfadb2e..5d9e3f1a1ac1 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -849,6 +849,13 @@ def _validate_win_subsystem(value: T.Optional[str]) -> T.Optional[str]: return f'Invalid value for win_subsystem: {value}.' return None +_WIN_SUBSYSTEM_KW: KwargInfo[T.Optional[str]] = KwargInfo( + 'win_subsystem', + (str, NoneType), + convertor=lambda x: x.lower() if isinstance(x, str) else None, + validator=_validate_win_subsystem, +) + def _validate_darwin_versions(darwin_versions: T.List[T.Union[str, int]]) -> T.Optional[str]: if len(darwin_versions) > 2: @@ -906,12 +913,6 @@ def _convert_darwin_versions(val: T.List[T.Union[str, int]]) -> T.Optional[T.Tup }, ), KwargInfo('pie', (bool, NoneType)), - KwargInfo( - 'win_subsystem', - (str, NoneType), - convertor=lambda x: x.lower() if isinstance(x, str) else None, - validator=_validate_win_subsystem, - ), KwargInfo( 'android_exe_type', (str, NoneType), @@ -925,6 +926,7 @@ def _convert_darwin_versions(val: T.List[T.Union[str, int]]) -> T.Optional[T.Tup *_BUILD_TARGET_KWS, *EXCLUSIVE_EXECUTABLE_KWS, _VS_MODULE_DEFS_KW.evolve(since='1.3.0', since_values=None), + _WIN_SUBSYSTEM_KW, _JAVA_LANG_KW, ] @@ -968,6 +970,7 @@ def _shortname_validator(shortname: T.Optional[str]) -> T.Optional[str]: *_EXCLUSIVE_SHARED_LIB_KWS, *_EXCLUSIVE_LIB_KWS, _VS_MODULE_DEFS_KW, + _WIN_SUBSYSTEM_KW.evolve(since='1.12.0'), _JAVA_LANG_KW, ] @@ -981,6 +984,7 @@ def _shortname_validator(shortname: T.Optional[str]) -> T.Optional[str]: *_EXCLUSIVE_SHARED_MOD_KWS, *_EXCLUSIVE_LIB_KWS, _VS_MODULE_DEFS_KW, + _WIN_SUBSYSTEM_KW.evolve(since='1.12.0'), _JAVA_LANG_KW, ] @@ -1022,10 +1026,11 @@ def _shortname_validator(shortname: T.Optional[str]) -> T.Optional[str]: *_EXCLUSIVE_STATIC_LIB_KWS, *_SHARED_STATIC_ARGS, _VS_MODULE_DEFS_KW, + _WIN_SUBSYSTEM_KW, _JAVA_LANG_KW, ] -# Arguments used by build_Target +# Arguments used by build_target BUILD_TARGET_KWS = [ *_BUILD_TARGET_KWS, *_EXCLUSIVE_SHARED_LIB_KWS, @@ -1034,6 +1039,7 @@ def _shortname_validator(shortname: T.Optional[str]) -> T.Optional[str]: *EXCLUSIVE_EXECUTABLE_KWS, *_SHARED_STATIC_ARGS, _VS_MODULE_DEFS_KW, + _WIN_SUBSYSTEM_KW.evolve(since='1.12.0'), 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 4595afe035130749f7cd97d6547efa9de0eea4eb Mon Sep 17 00:00:00 2001 From: Zephyr Lykos Date: Thu, 28 May 2026 02:20:38 +0800 Subject: [PATCH 498/681] vs2010backend: better handling of win_subsystem --- mesonbuild/backend/vs2010backend.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 139e31a4c436..ef916c0618ea 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -1352,7 +1352,8 @@ def add_non_makefile_vcxproj_elements( type_config: ET.Element, target: build.BuildTarget, platform: str, - subsystem: str, + subsystem: str | None, + version: str | None, build_args: list[str], target_args: list[str], target_defines: list[str], @@ -1583,8 +1584,13 @@ def add_non_makefile_vcxproj_elements( ET.SubElement(link, 'AdditionalDependencies').text = ';'.join(additional_links) ofile = ET.SubElement(link, 'OutputFile') ofile.text = f'$(OutDir){target.get_filename()}' - subsys = ET.SubElement(link, 'SubSystem') - subsys.text = subsystem + if subsystem is not None: + subsys = ET.SubElement(link, 'SubSystem') + subsys.text = subsystem + if version is not None: + # Undocumented, but you can find a reference in WDK props + minver = ET.SubElement(link, 'MinimumRequiredVersion') + minver.text = version if isinstance(target, (build.SharedLibrary, build.Executable)) and target.get_import_filename(): # DLLs built with MSVC always have an import library except when # they're data-only DLLs, but we don't support those yet. @@ -1647,7 +1653,8 @@ def relocate_generated_file_paths_to_concrete_build_dir(self, gen_files: T.List[ # we need to respect that not all targets will have generated a project. def gen_vcxproj(self, target: build.Target, ofname: str, guid: str, vslite_ctx: _VSLITE_CTX | None = None) -> bool: mlog.debug(f'Generating vcxproj {target.name}.') - subsystem = 'Windows' + subsystem: str | None = None + version: str | None = None self.handled_target_deps[target.get_id()] = [] # vs backend does not support link_early_args @@ -1664,13 +1671,12 @@ def gen_vcxproj(self, target: build.Target, ofname: str, guid: str, vslite_ctx: conftype = 'Makefile' elif isinstance(target, build.Executable): conftype = 'Application' - # If someone knows how to set the version properly, - # please send a patch. - subsystem = target.win_subsystem.split(',')[0] + subsystem, _, version = target.win_subsystem.partition(',') elif isinstance(target, build.StaticLibrary): conftype = 'StaticLibrary' elif isinstance(target, build.SharedLibrary): conftype = 'DynamicLibrary' + subsystem, _, version = target.win_subsystem.partition(',') elif isinstance(target, build.CustomTarget): self.gen_custom_target_vcxproj(target, ofname, guid) return True @@ -1730,7 +1736,7 @@ def gen_vcxproj(self, target: build.Target, ofname: str, guid: str, vslite_ctx: primary_src_lang = get_primary_source_lang(target.sources, custom_src) self.add_gen_lite_makefile_vcxproj_elements(root, platform, tfilename[1], vslite_ctx, target, proj_to_build_root, primary_src_lang) else: - self.add_non_makefile_vcxproj_elements(root, type_config, target, platform, subsystem, build_args, target_args, target_defines, target_inc_dirs, file_args) + self.add_non_makefile_vcxproj_elements(root, type_config, target, platform, subsystem, version, build_args, target_args, target_defines, target_inc_dirs, file_args) meson_file_group = ET.SubElement(root, 'ItemGroup') ET.SubElement(meson_file_group, 'None', Include=os.path.join(proj_to_src_dir, build_filename)) From a3fe58fea0e5a94930c04c2fcbff46c116279ff2 Mon Sep 17 00:00:00 2001 From: Zephyr Lykos Date: Thu, 28 May 2026 02:33:55 +0800 Subject: [PATCH 499/681] docs: mention win_subsystem support in release notes --- docs/markdown/snippets/win_subsystem_for_more.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 docs/markdown/snippets/win_subsystem_for_more.md diff --git a/docs/markdown/snippets/win_subsystem_for_more.md b/docs/markdown/snippets/win_subsystem_for_more.md new file mode 100644 index 000000000000..e89784639cb5 --- /dev/null +++ b/docs/markdown/snippets/win_subsystem_for_more.md @@ -0,0 +1,10 @@ +## Added `win_subsystem` to `shared_library()` and `shared_module()` + +Synonym for the one found in `executable()`. + +Mostly useful for setting the subsystem version in PE headers. +This can affects Windows's ShimEngine, but not SxS lookups. Usually you still +want to provide a manifest when targeting modern Windows. +Sometime also useful for targeting other subsystems. + +Visual Studio backends now also properly supports setting subsystem versions. From dea8d2d1116416d0105657321da4e0eb25794e42 Mon Sep 17 00:00:00 2001 From: Wang Yu Date: Thu, 11 Jun 2026 10:23:32 +0800 Subject: [PATCH 500/681] mtest: fix tap tests with nonzero exit code reported as passed Commit daa443188 narrowed the exit-code check from `not was_killed()` to `is_ok()`, but TAP parsing leaves self.res as RUNNING when no explicit FAIL/Bailout is found. RUNNING is not in is_ok(), so nonzero exit codes are not upgraded to ERROR and _complete() defaults RUNNING to OK. Fix by using `not is_bad()` which covers RUNNING and all other non-failure states. Fixes #15885 --- mesonbuild/mtest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index 825f7f559f6a..0661fa529d29 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -1150,7 +1150,7 @@ def needs_parsing(self) -> bool: return True def complete(self) -> None: - if self.returncode != 0 and self.res.is_ok(): + if self.returncode != 0 and not self.res.is_bad(): self.res = TestResult.ERROR self.stde = self.stde or '' self.stde += f'\n(test program exited with status code {self.returncode})' From ef3dff374cf5a26513ef8cb8377c71a82907193c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 10 Jun 2026 13:01:59 +0200 Subject: [PATCH 501/681] i18n module: add msgfmt_args argument to i18n.gettext This allows passing additional arguments like `--use-fuzzy` to msgfmt whenn building the translations. --- docs/markdown/i18n-module.md | 2 ++ docs/markdown/snippets/i18n_msgfmt_args.md | 12 ++++++++++++ mesonbuild/modules/i18n.py | 15 ++++++++++++++- test cases/frameworks/6 gettext/po/meson.build | 2 +- 4 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 docs/markdown/snippets/i18n_msgfmt_args.md diff --git a/docs/markdown/i18n-module.md b/docs/markdown/i18n-module.md index 767e0c87f4ab..7db92647dbc7 100644 --- a/docs/markdown/i18n-module.md +++ b/docs/markdown/i18n-module.md @@ -17,6 +17,8 @@ argument which is the name of the gettext module. * `args`: list of extra arguments to pass to `xgettext` when generating the pot file +* `msgfmt_args`: (*Added 1.12.0*) list of extra arguments to pass to `msgfmt` when + building the translations * `data_dirs`: (*Added 0.36.0*) list of directories to be set for `GETTEXTDATADIRS` env var (Requires gettext 0.19.8+), used for local its files diff --git a/docs/markdown/snippets/i18n_msgfmt_args.md b/docs/markdown/snippets/i18n_msgfmt_args.md new file mode 100644 index 000000000000..d7106d4ae282 --- /dev/null +++ b/docs/markdown/snippets/i18n_msgfmt_args.md @@ -0,0 +1,12 @@ +## Added `msgfmt_args` arg to `i18n.gettext` + +`i18n.gettext` now supports the `msgfmt_args` argument. +This argument can be used to pass additional arguments to +`msgfmt` when building the translations. + +```meson +i18n = import('i18n') +i18n.gettext('mycatalog', + args : ['-k_', '-kN_'], + msgfmt_args : ['--use-fuzzy']) +``` diff --git a/mesonbuild/modules/i18n.py b/mesonbuild/modules/i18n.py index dc27f3f5c740..5b14e8fa05ff 100644 --- a/mesonbuild/modules/i18n.py +++ b/mesonbuild/modules/i18n.py @@ -48,6 +48,7 @@ class MergeFile(TypedDict): class Gettext(TypedDict): args: T.List[str] + msgfmt_args: T.List[str] data_dirs: T.List[str] install: bool install_dir: T.Optional[str] @@ -83,6 +84,14 @@ class XgettextProgramT(TypedDict): listify=True, ) +_MSGFMT_ARGS: KwargInfo[T.List[str]] = KwargInfo( + 'msgfmt_args', + ContainerTypeInfo(list, str), + default=[], + listify=True, + since='1.12.0', +) + _DATA_DIRS: KwargInfo[T.List[str]] = KwargInfo( 'data_dirs', ContainerTypeInfo(list, str), @@ -350,6 +359,7 @@ def merge_file(self, state: 'ModuleState', args: T.List['TYPE_var'], kwargs: 'Me @typed_kwargs( 'i18n.gettext', _ARGS, + _MSGFMT_ARGS, _DATA_DIRS.evolve(since='0.36.0'), INSTALL_KW.evolve(default=True), INSTALL_DIR_KW.evolve(since='0.50.0'), @@ -422,12 +432,15 @@ def gettext(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'Gettext') - if isinstance(install_dir, OptionString): name = path.join(install_dir.optname, l, 'LC_MESSAGES') mo_install_dir = OptionString(mo_install_dir, name) + + gmobasecmd: list[str | Program] = [self.tools['msgfmt'], '-o', '@OUTPUT@', '@INPUT@'] + gmotarget = build.CustomTarget( f'{packagename}-{l}.mo', path.join(state.subdir, l, 'LC_MESSAGES'), state.subproject, state.environment, - [self.tools['msgfmt'], '-o', '@OUTPUT@', '@INPUT@'], + [*gmobasecmd, *kwargs['msgfmt_args']], [po_file], [f'{packagename}.mo'], install=install, diff --git a/test cases/frameworks/6 gettext/po/meson.build b/test cases/frameworks/6 gettext/po/meson.build index 5510e424b35b..a7e72315de00 100644 --- a/test cases/frameworks/6 gettext/po/meson.build +++ b/test cases/frameworks/6 gettext/po/meson.build @@ -1,4 +1,4 @@ langs = ['fi', 'de', 'ru'] -gettext_targets = i18n.gettext('intltest', languages : langs) +gettext_targets = i18n.gettext('intltest', languages : langs, msgfmt_args : ['--use-fuzzy']) mo_targets = gettext_targets[0] From 88bc1f35feddfaaf0ebe63a76cf20cbed64dee6f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 19 Jan 2026 19:31:27 +0100 Subject: [PATCH 502/681] codegen: do not use Programs.for_machine Program.for_machine is used, when building the command line for executable or tests, to possibly apply an exe_wrapper to LocalProgram instances. However, mesonbuild/modules/codegen.py was incorrectly using flex's for_machine field to look up a compiler. When checking for FlexLexer.h, the code needs the compiler for the host machine (where the generated C/C++ code will be compiled), not the build machine (where flex runs). Track which machine the generated output is for, and use it as needed. Signed-off-by: Paolo Bonzini --- mesonbuild/modules/codegen.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/mesonbuild/modules/codegen.py b/mesonbuild/modules/codegen.py index d90a12dd592e..57ef04d318df 100644 --- a/mesonbuild/modules/codegen.py +++ b/mesonbuild/modules/codegen.py @@ -86,6 +86,7 @@ class _CodeGenerator(HoldableObject): name: str program: Program + for_machine: MachineChoice arguments: ImmutableListProtocol[str] = dataclasses.field(default_factory=list) def command(self) -> CommandList: @@ -146,9 +147,8 @@ def generate_method(self, args: T.Tuple[T.Union[str, File, GeneratedList, Custom ext = kwargs['source'].rsplit('.', 1)[1] is_cpp = ext in lang_suffixes['cpp'] - for_machine = self.held_object.program.for_machine - # Flex uses FlexLexer.h for C++ code + for_machine = self.held_object.for_machine if is_cpp and self.held_object.name in {'flex', 'win_flex'}: try: comp = self.interpreter.environment.coredata.compilers[for_machine]['cpp'] @@ -250,7 +250,7 @@ def generate_method(self, args: T.Tuple[T.Union[str, File, CustomTarget, CustomT if kwargs['locations'] is not None: outputs.append(kwargs['locations']) - for_machine = self.held_object.program.for_machine + for_machine = self.held_object.for_machine target = CustomTarget( f'codegen-yacc-{name}-{for_machine.get_lower_case_name()}', self.interpreter.subdir, @@ -302,7 +302,7 @@ def lex_method(self, state: ModuleState, args: T.Tuple, kwargs: FindLexKwargs) - disabled, required, feature = extract_required_kwarg(kwargs, state.subproject) if disabled: mlog.log('generator lex skipped: feature', mlog.bold(feature), 'disabled') - return LexGenerator('lex', NonExistingExternalProgram('lex')) + return LexGenerator('lex', NonExistingExternalProgram('lex'), kwargs['native']) names: T.List[LexImpls] = [] if kwargs['implementations']: @@ -348,7 +348,7 @@ def lex_method(self, state: ModuleState, args: T.Tuple, kwargs: FindLexKwargs) - raise MesonException.from_node( 'Could not find a lex implementation. Tried: ', ", ".join(names), node=state.current_node) - return LexGenerator(name, bin) + return LexGenerator(name, bin, kwargs['native']) lex_args: T.List[str] = [] # This option allows compiling with MSVC @@ -356,7 +356,7 @@ def lex_method(self, state: ModuleState, args: T.Tuple, kwargs: FindLexKwargs) - if bin.name == 'win_flex' and state.environment.machines[kwargs['native']].is_windows(): lex_args.append('--wincompat') lex_args.extend(['-o', '@OUTPUT0@']) - return LexGenerator(name, bin, T.cast('ImmutableListProtocol[str]', lex_args)) + return LexGenerator(name, bin, kwargs['native'], T.cast('ImmutableListProtocol[str]', lex_args)) @noPosargs @typed_kwargs( @@ -381,7 +381,7 @@ def yacc_method(self, state: ModuleState, args: T.Tuple, kwargs: FindYaccKwargs) disabled, required, feature = extract_required_kwarg(kwargs, state.subproject) if disabled: mlog.log('generator yacc skipped: feature', mlog.bold(feature), 'disabled') - return YaccGenerator('yacc', NonExistingExternalProgram('yacc')) + return YaccGenerator('yacc', NonExistingExternalProgram('yacc'), kwargs['native']) names: T.List[YaccImpls] if kwargs['implementations']: names = kwargs['implementations'] @@ -409,7 +409,7 @@ def yacc_method(self, state: ModuleState, args: T.Tuple, kwargs: FindYaccKwargs) raise MesonException.from_node( 'Could not find a yacc implementation. Tried: ', ", ".join(names), node=state.current_node) - return YaccGenerator(name, bin) + return YaccGenerator(name, bin, kwargs['native']) yacc_args: T.List[str] = [] @@ -434,7 +434,7 @@ def yacc_method(self, state: ModuleState, args: T.Tuple, kwargs: FindYaccKwargs) fatal=False) yacc_args.append('-H') yacc_args.extend(['-o', '@OUTPUT0@', '@INPUT@']) - return YaccGenerator(name, bin, T.cast('ImmutableListProtocol[str]', yacc_args)) + return YaccGenerator(name, bin, kwargs['native'], T.cast('ImmutableListProtocol[str]', yacc_args)) def initialize(interpreter: Interpreter) -> CodeGenModule: From c91907f9e41246f3120f39e82fb11422bf72bee8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 8 Jan 2026 18:53:43 +0100 Subject: [PATCH 503/681] build: simplify rel_string_list os.path.normpath will remove a trailing "/" or "/.", thus undoing the effect of os.path.normpath(os.path.join(self.curdir, idir)) if idir is "" or ".". As such, expdir will always be the same as self.curdir; remove it. Signed-off-by: Paolo Bonzini --- mesonbuild/build.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index f1648f5c176c..2848c491634a 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -507,15 +507,11 @@ def rel_string_list(self, build_to_src: str, build_root: T.Optional[str] = None) strlist: T.List[str] = [] for idirs, add_src in [(self.incdirs, True), (self.extra_build_dirs, False)]: for idir in idirs: - bld_dir = os.path.normpath(os.path.join(self.curdir, idir)) - if idir not in {'', '.'}: - expdir = bld_dir - else: - expdir = self.curdir - if build_root is None or os.path.isdir(os.path.join(build_root, expdir)): - strlist.append(bld_dir) + dir = os.path.normpath(os.path.join(self.curdir, idir)) + if build_root is None or os.path.isdir(os.path.join(build_root, dir)): + strlist.append(dir) if add_src: - strlist.append(os.path.normpath(os.path.join(build_to_src, expdir))) + strlist.append(os.path.normpath(os.path.join(build_to_src, dir))) return strlist From 0a19f252308417c185cf5cc79ef46570cb193ddd Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 8 Jan 2026 18:09:55 +0100 Subject: [PATCH 504/681] Revert "build: move find_overrides away" This reverts commit 8842bd44a703cf4da3a17a337c8488204c438844. It is incorrect to keep find_overrides in Interpreter because it won't be rolled back properly if a non-required subproject fails. --- mesonbuild/build.py | 1 + mesonbuild/interpreter/interpreter.py | 12 +++++------- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 2848c491634a..84980603d080 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -342,6 +342,7 @@ def __init__(self, environment: Environment): self.dep_manifest: T.Dict[str, DepManifest] = {} self.test_setups: T.Dict[str, TestSetup] = {} self.test_setup_default_name: str | None = None + self.find_overrides: T.Dict[str, programs.Program] = {} self.searched_programs: T.Set[str] = set() # The list of all programs that have been searched for. # If we are doing a cross build we need two caches, if we're doing a diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index c3e7ac949f5d..b4da7b984326 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -314,7 +314,6 @@ def __init__( self.build_func_dict() self.build_holder_map() self.user_defined_options = user_defined_options - self.find_overrides: T.Dict[str, Program] = {} # Languages added in the current subproject self.compilers: PerMachine[CompilerDict] = PerMachine({}, {}) self.parse_project() @@ -786,7 +785,7 @@ def func_run_command(self, node: mparser.BaseNode, def _compiled_exe_error(self, cmd: T.Union[Program, build.Executable]) -> T.NoReturn: descr = cmd.name if isinstance(cmd, build.Executable) else cmd.description() - for name, exe in self.find_overrides.items(): + for name, exe in self.build.find_overrides.items(): if cmd == exe: raise InterpreterException(f'Program {name!r} was overridden with the compiled executable {descr!r} and therefore cannot be used during configuration') raise InterpreterException(f'Program {descr!r} is a compiled executable and therefore cannot be used during configuration') @@ -1020,7 +1019,6 @@ def _do_subproject_meson(self, subp_name: SubProject, subdir: str, subi.holder_map = self.holder_map subi.bound_holder_map = self.bound_holder_map subi.summary = self.summary - subi.find_overrides = self.find_overrides subi.subproject_stack = self.subproject_stack + [subp_name] current_active = self.active_projectname @@ -1676,8 +1674,8 @@ def program_from_overrides(self, command_names: T.List[mesonlib.FileOrString], for name in command_names: if not isinstance(name, str): continue - if name in self.find_overrides: - exe = self.find_overrides[name] + if name in self.build.find_overrides: + exe = self.build.find_overrides[name] extra_info.append(mlog.blue('(overridden)')) return exe return None @@ -1690,9 +1688,9 @@ def store_name_lookups(self, command_names: T.List[mesonlib.FileOrString]) -> No def add_find_program_override(self, name: str, exe: Program) -> None: if name in self.build.searched_programs: raise InterpreterException(f'Tried to override finding of executable "{name}" which has already been found.') - if name in self.find_overrides: + if name in self.build.find_overrides: raise InterpreterException(f'Tried to override executable "{name}" which has already been overridden.') - self.find_overrides[name] = exe + self.build.find_overrides[name] = exe if name == 'pkg-config' and isinstance(exe, ExternalProgram): from ..dependencies.pkgconfig import PkgConfigInterface PkgConfigInterface.set_program_override(exe, MachineChoice.HOST) From f33474edd2677e0aeff2c3f0960ee457d451f5f8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 9 Jun 2026 16:51:29 +0200 Subject: [PATCH 505/681] test cases: expand test for nonexisting property Cover various combination of the native keyword argument and whether the property is in a native or cross file. Signed-off-by: Paolo Bonzini --- .../meson.build | 3 -- .../test.json | 7 ---- .../meson.build | 4 ++ .../meson_options.txt | 1 + .../propfile | 2 + unittests/machinefiletests.py | 39 +++++++++++++++++++ 6 files changed, 46 insertions(+), 10 deletions(-) delete mode 100644 test cases/failing/103 no build get_external_property/meson.build delete mode 100644 test cases/failing/103 no build get_external_property/test.json create mode 100644 test cases/unit/137 external property nonexisting/meson.build create mode 100644 test cases/unit/137 external property nonexisting/meson_options.txt create mode 100644 test cases/unit/137 external property nonexisting/propfile diff --git a/test cases/failing/103 no build get_external_property/meson.build b/test cases/failing/103 no build get_external_property/meson.build deleted file mode 100644 index 8a4215c9fef8..000000000000 --- a/test cases/failing/103 no build get_external_property/meson.build +++ /dev/null @@ -1,3 +0,0 @@ -project('missing property') - -message(meson.get_external_property('nonexisting', native : true)) diff --git a/test cases/failing/103 no build get_external_property/test.json b/test cases/failing/103 no build get_external_property/test.json deleted file mode 100644 index 74ecb759b0f7..000000000000 --- a/test cases/failing/103 no build get_external_property/test.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "stdout": [ - { - "line": "test cases/failing/103 no build get_external_property/meson.build:3:14: ERROR: Unknown property for build machine: nonexisting" - } - ] -} diff --git a/test cases/unit/137 external property nonexisting/meson.build b/test cases/unit/137 external property nonexisting/meson.build new file mode 100644 index 000000000000..e4e96d1c2618 --- /dev/null +++ b/test cases/unit/137 external property nonexisting/meson.build @@ -0,0 +1,4 @@ +project('missing property') + +assert(meson.get_external_property('existing', native : get_option('native')) == '42') +message(meson.get_external_property('nonexisting', native : get_option('native'))) diff --git a/test cases/unit/137 external property nonexisting/meson_options.txt b/test cases/unit/137 external property nonexisting/meson_options.txt new file mode 100644 index 000000000000..77c44d6855dc --- /dev/null +++ b/test cases/unit/137 external property nonexisting/meson_options.txt @@ -0,0 +1 @@ +option('native', type : 'boolean', value: false) diff --git a/test cases/unit/137 external property nonexisting/propfile b/test cases/unit/137 external property nonexisting/propfile new file mode 100644 index 000000000000..f039e632236d --- /dev/null +++ b/test cases/unit/137 external property nonexisting/propfile @@ -0,0 +1,2 @@ +[properties] +existing = '42' diff --git a/unittests/machinefiletests.py b/unittests/machinefiletests.py index 6eb050ae3f8e..7f9a6f0b66ce 100644 --- a/unittests/machinefiletests.py +++ b/unittests/machinefiletests.py @@ -1014,6 +1014,45 @@ def test_for_build_env_vars(self) -> None: break self.assertEqual(found, 2, 'Did not find all sections.') + def test_external_property_build_machine_native(self): + testdir = os.path.join(self.unit_test_dir, '137 external property nonexisting') + self.meson_native_files = [os.path.join(testdir, "propfile")] + out = self.init(testdir, allow_fail=True, extra_args=['-Dnative=true']) + self.assertIn('Unknown property for build machine: nonexisting', out) + + def test_external_property_host_machine_native(self): + testdir = os.path.join(self.unit_test_dir, '137 external property nonexisting') + self.meson_native_files = [os.path.join(testdir, "propfile")] + out = self.init(testdir, allow_fail=True, extra_args=['-Dnative=false']) + self.assertIn('Unknown property for host machine: nonexisting', out) + + def test_external_property_build_machine_cross(self): + # meson.get_external_property(..., native : true) refers to the build + # machine. In a cross build the build and host machines are distinct, so + # the error for a missing property must refer to the build machine. + crossdir = os.path.join(self.unit_test_dir, '69 cross') + # Reuse the cross test project to generate a native and a cross file + # describing this machine, so the configuration below is treated as a + # cross build (see test_identity_cross). + self.init(crossdir, extra_args=['-Dgenerate=true']) + + nativefile = os.path.join(self.builddir, "nativefile") + crossfile = os.path.join(self.builddir, "crossfile") + + self.new_builddir() + testdir = os.path.join(self.unit_test_dir, '137 external property nonexisting') + self.meson_native_files = [nativefile, os.path.join(testdir, "propfile")] + self.meson_cross_files = [crossfile] + out = self.init(testdir, allow_fail=True, extra_args=['-Dnative=true']) + self.assertIn('Unknown property for build machine: nonexisting', out) + + self.new_builddir() + testdir = os.path.join(self.unit_test_dir, '137 external property nonexisting') + self.meson_native_files = [nativefile] + self.meson_cross_files = [crossfile, os.path.join(testdir, "propfile")] + out = self.init(testdir, allow_fail=True, extra_args=['-Dnative=false']) + self.assertIn('Unknown property for host machine: nonexisting', out) + def test_project_options_native_only(self) -> None: # Do not load project options from a native file when doing a cross # build From a63debede76e3680838448ee4f904ce6c2ddf171 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 9 Jun 2026 12:59:33 +0200 Subject: [PATCH 506/681] utils: add indexing to PerThreeMachine PerThreeMachine does not allow accessing via the [] operator (__getitem__/__setitem__). Allow that, using a new enum as the index type. ThreeMachineChoice overlaps MachineChoice enough that the accessors can take either enum. Signed-off-by: Paolo Bonzini --- mesonbuild/utils/universal.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index e430d70686e3..1ef4a300b90a 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -539,7 +539,7 @@ def classify_unity_sources(compilers: T.Iterable['Compiler'], sources: T.List[Fi return compsrclist -MACHINE_NAMES = ['build', 'host'] +MACHINE_NAMES = ['build', 'host', 'target'] MACHINE_PREFIXES = ['build.', ''] @@ -562,6 +562,23 @@ def get_prefix(self) -> str: return MACHINE_PREFIXES[self.value] +class ThreeMachineChoice(enum.IntEnum): + + """Enum class representing any of the three abstract machine names: + the build, host, and target, machines. + """ + + BUILD = MachineChoice.BUILD.value + HOST = MachineChoice.HOST.value + TARGET = 2 + + def __str__(self) -> str: + return f'{self.get_lower_case_name()} machine' + + def get_lower_case_name(self) -> str: + return MACHINE_NAMES[self.value] + + @dataclasses.dataclass(eq=False, order=False) class PerMachine(T.Generic[_T]): build: _T @@ -611,6 +628,12 @@ class PerThreeMachine(PerMachine[_T]): target: _T + def __getitem__(self, machine: MachineChoice | ThreeMachineChoice) -> _T: + return [self.build, self.host, self.target][machine.value] + + def __setitem__(self, machine: MachineChoice | ThreeMachineChoice, val: _T) -> None: + setattr(self, machine.get_lower_case_name(), val) + def miss_defaulting(self) -> "PerThreeMachineDefaultable[T.Optional[_T]]": """Unset definition duplicated from their previous to None From c24754bb79b3fc0f6296bc5fd08e22c1c909af15 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 9 Jun 2026 19:33:06 +0200 Subject: [PATCH 507/681] pkgconfig: do not map machine in PkgConfigInterface._cli It is only ever used with MachineChoice.HOST, so it does nothing. Signed-off-by: Paolo Bonzini --- mesonbuild/dependencies/pkgconfig.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mesonbuild/dependencies/pkgconfig.py b/mesonbuild/dependencies/pkgconfig.py index 91f783da2fdd..f356bd314a3d 100644 --- a/mesonbuild/dependencies/pkgconfig.py +++ b/mesonbuild/dependencies/pkgconfig.py @@ -66,7 +66,6 @@ def _cli(env: Environment, for_machine: MachineChoice, Even when we use another implementation internally, external tools might still need the CLI implementation. ''' - for_machine = for_machine if env.is_cross_build() else MachineChoice.HOST impl: T.Union[Literal[False], T.Optional[PkgConfigInterface]] # Help confused mypy impl = PkgConfigInterface.instance(env, for_machine, silent) if impl and not isinstance(impl, PkgConfigCLI): From a8b97400c8b06146bfcaa73f57fe8be5ce446d45 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 9 Jun 2026 11:52:10 +0200 Subject: [PATCH 508/681] environment, build: store which machines are in use An environment does not necessarily specify all the machines which are available: both build and target machines can default to host. And ultimately, what defines if a machine is "native" is whether the build machine resolves to it. Place all the information on how to go from machine to its defaulted definition into a new MachineMap object. Ultimately this has two purposes: - allowing the user to keep reasoning on build/host/target if desirable, while letting environment operate on N+1 machines (one native and N cross) - removing PerMachine and PerMachineDefaultable, in favor of using dict[str, ...] (possibly with MachineChoice as a newtype for the key) anywhere except in the interpreter. Signed-off-by: Paolo Bonzini --- mesonbuild/build.py | 15 ++++++--- mesonbuild/environment.py | 44 ++++++++++++++++++++++++--- mesonbuild/interpreter/interpreter.py | 6 ++-- mesonbuild/interpreter/mesonmain.py | 4 +-- mesonbuild/utils/universal.py | 1 + run_tests.py | 1 + 6 files changed, 56 insertions(+), 15 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 84980603d080..05952cf1b21f 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -21,8 +21,8 @@ from . import programs from .mesonlib import ( HoldableObject, SecondLevelHolder, SimpleABC, SubProject, - File, MesonException, MachineChoice, PerMachine, OrderedSet, - classify_unity_sources, ROOT_SUBPROJECT, + File, MesonException, MachineChoice, PerMachine, + OrderedSet, classify_unity_sources, ROOT_SUBPROJECT, get_filenames_templates_dict, substitute_values, has_path_sep, is_parent_path, relpath, PerMachineDefaultable, MesonBugException, EnvironmentVariables, pickle_load, lazy_property, @@ -353,6 +353,7 @@ def __init__(self, environment: Environment): environment.is_cross_build(), {}, {}) self.devenv: T.List[EnvironmentVariables] = [] + self.machine_map = self.environment.machine_map self.modules: T.Set[str] = set() """Used to track which modules are enabled in all subprojects. @@ -393,6 +394,10 @@ def copy(self) -> Build: def merge(self, other: Build) -> None: for k, v in other.__dict__.items(): + # This one is modified for build-only configs, but it is + # local. No need to copy it. + if k == 'machine_map': + continue # These are not modified in subprojects if k in {'global_args', 'global_link_args'}: continue @@ -405,11 +410,11 @@ def merge(self, other: Build) -> None: self.__dict__[k] = v def ensure_static_linker(self, compiler: Compiler) -> None: - for_machine = compiler.for_machine if self.environment.is_cross_build() else MachineChoice.HOST + for_machine = self.machine_map[compiler.for_machine] if self.static_linker[for_machine] is None and compiler.needs_static_linker(): self.static_linker[for_machine] = detect_static_linker(self.environment, compiler) - if not self.environment.is_cross_build(): - self.static_linker[MachineChoice.BUILD] = self.static_linker[MachineChoice.HOST] + if self.machine_map.build is self.machine_map.host: + self.static_linker.build = self.static_linker.host def get_project(self) -> str: return self.projects[ROOT_SUBPROJECT].name diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 76d75ebc1a33..aecefc06d2f0 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -4,6 +4,7 @@ from __future__ import annotations +import dataclasses import itertools import os, re import typing as T @@ -18,7 +19,7 @@ from .mesonlib import ( MesonException, MachineChoice, Popen_safe, PerMachine, PerMachineDefaultable, PerThreeMachineDefaultable, split_args, - MesonBugException + MesonBugException, ThreeMachineChoice ) from .options import OptionKey from . import mlog @@ -37,6 +38,8 @@ from .options import OptionDict, ElementaryOptionValues from .wrap.wrap import Resolver + import enum + NON_LANG_ENV_OPTIONS = [ ('PKG_CONFIG_PATH', 'pkg_config_path'), @@ -78,6 +81,31 @@ def _get_env_var(for_machine: MachineChoice, is_cross: bool, var_name: str) -> T return value +@dataclasses.dataclass +class MachineMap: + # BUILD if cross compiling, HOST if not cross compiling + build: MachineChoice + # This is not entirely correct: it is possible in principle for + # the host machine to be originally configured as a target, and + # switched to be the host for a single subproject. Ultimately, + # Environment should include N arbitrarily-named configurations, + # and kwargs['native'] would be supplanted by kwargs['for_machine'] + # of type T.NewType('MachineChoice', str) after parameter parsing. + host: MachineChoice + target: ThreeMachineChoice + + @T.overload + def __getitem__(self, machine: MachineChoice) -> MachineChoice: + ... + + @T.overload + def __getitem__(self, machine: ThreeMachineChoice) -> ThreeMachineChoice: + ... + + def __getitem__(self, machine: MachineChoice | ThreeMachineChoice) -> enum.IntEnum: + return [self.build, self.host, self.target][machine.value] + + class Environment: private_dir = 'meson-private' log_dir = 'meson-logs' @@ -121,6 +149,13 @@ def __init__(self, source_dir: str, build_dir: T.Optional[str], cmd_options: cmd self.scratch_dir = '' self.create_new_coredata(cmd_options) + # Store which machine is actually returned by self.machines after + # taking into account defaults + if self.coredata.cross_files: + self.machine_map = MachineMap(MachineChoice.BUILD, MachineChoice.HOST, ThreeMachineChoice.HOST) + else: + self.machine_map = MachineMap(MachineChoice.HOST, MachineChoice.HOST, ThreeMachineChoice.HOST) + ## locally bind some unfrozen configuration # Stores machine infos, the only *three* machine one because we have a @@ -168,9 +203,7 @@ def __init__(self, source_dir: str, build_dir: T.Optional[str], cmd_options: cmd binaries.build = BinaryTable(config.get('binaries', {})) properties.build = Properties(config.get('properties', {})) cmakevars.build = CMakeVariables(config.get('cmake', {})) - self._load_machine_file_options( - config, properties.build, - MachineChoice.BUILD if self.coredata.cross_files else MachineChoice.HOST) + self._load_machine_file_options(config, properties.build, self.machine_map.build) ## Read in cross file(s) to override host machine configuration @@ -183,6 +216,7 @@ def __init__(self, source_dir: str, build_dir: T.Optional[str], cmd_options: cmd machines.host = MachineInfo.from_literal(config['host_machine']) if 'target_machine' in config: machines.target = MachineInfo.from_literal(config['target_machine']) + self.machine_map.target = ThreeMachineChoice.TARGET # Keep only per machine options from the native file. The cross # file takes precedence over all other options. for key, value in list(self.options.items()): @@ -430,7 +464,7 @@ def init_backend_options(self, backend_name: str) -> None: self.coredata.optstore.set_option(k, v) def is_cross_build(self, when_building_for: MachineChoice = MachineChoice.HOST) -> bool: - return self.coredata.is_cross_build(when_building_for) + return self.machine_map[when_building_for] is not self.machine_map.build def dump_coredata(self) -> str: return coredata.save(self.coredata, self.get_build_dir()) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index b4da7b984326..385f9d944f54 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -340,11 +340,11 @@ def _redetect_machines(self) -> None: self.build.environment.update_build_machine() self.builtin['build_machine'] = \ - OBJ.MachineHolder(self.build.environment.machines.build, self) + OBJ.MachineHolder(self.build.environment.machines[self.build.machine_map.build], self) self.builtin['host_machine'] = \ - OBJ.MachineHolder(self.build.environment.machines.host, self) + OBJ.MachineHolder(self.build.environment.machines[self.build.machine_map.host], self) self.builtin['target_machine'] = \ - OBJ.MachineHolder(self.build.environment.machines.target, self) + OBJ.MachineHolder(self.build.environment.machines[self.build.machine_map.target], self) def build_func_dict(self) -> None: self.funcs.update({'add_global_arguments': self.func_add_global_arguments, diff --git a/mesonbuild/interpreter/mesonmain.py b/mesonbuild/interpreter/mesonmain.py index d5f03d01c6ea..3a7e08f6ecbd 100644 --- a/mesonbuild/interpreter/mesonmain.py +++ b/mesonbuild/interpreter/mesonmain.py @@ -265,7 +265,7 @@ def can_run_host_binaries_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_k def _can_run_host_binaries_impl(self) -> bool: return not ( - self.build.environment.is_cross_build() and + self.build.machine_map.host is not self.build.machine_map.build and self.build.environment.need_exe_wrapper() and self.build.environment.exe_wrapper is None ) @@ -274,7 +274,7 @@ def _can_run_host_binaries_impl(self) -> bool: @noKwargs @InterpreterObject.method('is_cross_build') def is_cross_build_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> bool: - return self.build.environment.is_cross_build() + return self.build.machine_map.host is not self.build.machine_map.build @typed_pos_args('meson.get_compiler', str) @typed_kwargs('meson.get_compiler', NATIVE_KW) diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index 1ef4a300b90a..cb4c402c4f62 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -81,6 +81,7 @@ def __gt__(self, other: Self) -> bool: ... 'GitException', 'LibType', 'MachineChoice', + 'ThreeMachineChoice', 'EnvironmentException', 'FileOrString', 'GitException', diff --git a/run_tests.py b/run_tests.py index 05486713e38a..2e375cbd77c4 100755 --- a/run_tests.py +++ b/run_tests.py @@ -136,6 +136,7 @@ def _using_intelcl() -> bool: class FakeBuild: def __init__(self, env): self.environment = env + self.machine_map = env.machine_map def get_fake_options(prefix: str = '') -> SharedCMDOptions: opts = T.cast('SharedCMDOptions', argparse.Namespace()) From 288763a863372c10a90ad12e4619c68162943428 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 9 Jun 2026 17:05:26 +0200 Subject: [PATCH 509/681] interpreter: introduce apply_machine_map Introduce a decorator that applies the transformation in self.build.machine_map to kwargs['native']. In the future this will also handle conversion of kwargs['native'] to kwargs['for_machine']. Signed-off-by: Paolo Bonzini --- mesonbuild/interpreter/decorators.py | 47 +++++++++++++++++++ mesonbuild/interpreter/interpreter.py | 16 +++++++ mesonbuild/interpreter/kwargs.py | 6 +++ mesonbuild/interpreter/mesonmain.py | 5 ++ mesonbuild/modules/codegen.py | 3 ++ mesonbuild/modules/python.py | 2 + mesonbuild/modules/rust.py | 2 + .../unit/116 empty project/expected_mods.json | 3 +- unittests/machinefiletests.py | 2 +- 9 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 mesonbuild/interpreter/decorators.py diff --git a/mesonbuild/interpreter/decorators.py b/mesonbuild/interpreter/decorators.py new file mode 100644 index 000000000000..afbe167c61ce --- /dev/null +++ b/mesonbuild/interpreter/decorators.py @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2012-2021 The Meson development team + +from __future__ import annotations + +from functools import wraps +import typing as T + +from ..interpreterbase import ObjectHolder +from ..interpreterbase.decorators import get_callee_args +from ..mesonlib import MesonBugException + +if T.TYPE_CHECKING: + from ..interpreterbase import TV_func + from .kwargs import MachineMapArgs + + +def apply_machine_map(f: TV_func) -> TV_func: + """Enforce build-only subproject constraints on native/install kwargs. + + Must be placed AFTER @typed_kwargs (so converters have run). + """ + @wraps(f) + def wrapped(*wrapped_args: T.Any, **wrapped_kwargs: T.Any) -> T.Any: + # Import here to avoid circular imports at module load time + from . import Interpreter + from .mesonmain import MesonMain + + # First argument could be Interpreter, InterpreterObject (with an interpreter + # attribute) or ModuleObject. In the case of a ModuleObject it is the 2nd + # argument (ModuleState) that contains the needed information. + s: Interpreter + if not hasattr(wrapped_args[0], 'current_node'): + s = wrapped_args[1]._interpreter + elif isinstance(wrapped_args[0], (MesonMain, ObjectHolder)): + s = wrapped_args[0].interpreter + elif isinstance(wrapped_args[0], Interpreter): + s = wrapped_args[0] + else: + raise MesonBugException(f'cannot use @apply_machine_map if the object is a {type(wrapped_args[0])}') + + _, _, kwargs, _ = get_callee_args(wrapped_args) + if 'native' in kwargs: + s.apply_machine_map_to_kwargs(T.cast('MachineMapArgs', kwargs)) + + return f(*wrapped_args, **wrapped_kwargs) + return T.cast('TV_func', wrapped) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 385f9d944f54..6f7bda83d32f 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -27,6 +27,7 @@ from ..depfile import DepFile from ..interpreterbase import ContainerTypeInfo, InterpreterBase, KwargInfo, typed_kwargs, typed_pos_args from ..interpreterbase import noPosargs, noKwargs, noArgsFlattening, noSecondLevelHolderResolving, unholder_return +from .decorators import apply_machine_map from ..interpreterbase import InterpreterException, InvalidArguments, InvalidCode, SubdirDoneRequest from ..interpreterbase import Disabler, disablerIfNotFound from ..interpreterbase import FeatureNew, FeatureDeprecated, FeatureBroken, FeatureNewKwargs @@ -346,6 +347,11 @@ def _redetect_machines(self) -> None: self.builtin['target_machine'] = \ OBJ.MachineHolder(self.build.environment.machines[self.build.machine_map.target], self) + def apply_machine_map_to_kwargs(self, kwargs: kwtypes.MachineMapArgs) -> None: + orig_for_machine: MachineChoice | None = kwargs.get('native', None) + if orig_for_machine is not None: + kwargs['native'] = self.build.machine_map[orig_for_machine] + def build_func_dict(self) -> None: self.funcs.update({'add_global_arguments': self.func_add_global_arguments, 'add_global_link_arguments': self.func_add_global_link_arguments, @@ -1830,6 +1836,7 @@ def find_program_fallback(self, fallback: SubProject, args: T.List[mesonlib.File DEFAULT_OPTIONS.evolve(since='1.3.0') ) @disablerIfNotFound + @apply_machine_map def func_find_program(self, node: mparser.BaseNode, args: T.Tuple[T.List[mesonlib.FileOrString]], kwargs: 'kwtypes.FindProgram', ) -> Program: @@ -1848,6 +1855,7 @@ def func_find_program(self, node: mparser.BaseNode, args: T.Tuple[T.List[mesonli @typed_pos_args('dependency', varargs=str, min_varargs=1) @typed_kwargs('dependency', *DEPENDENCY_KWS) @disablerIfNotFound + @apply_machine_map def func_dependency(self, node: mparser.BaseNode, args: T.Tuple[T.List[str]], kwargs: kwtypes.FuncDependency) -> Dependency: # Replace '' by empty list of names names = [n for n in args[0] if n] @@ -1998,6 +2006,7 @@ def func_build_target(self, node: mparser.BaseNode, INSTALL_TAG_KW.evolve(since='1.7.0'), INSTALL_MODE_KW.evolve(since='1.7.0'), ) + @apply_machine_map def func_vcs_tag(self, node: mparser.BaseNode, args: T.List['TYPE_var'], kwargs: 'kwtypes.VcsTag') -> build.CustomTarget: if kwargs['fallback'] is None: FeatureNew.single_use('Optional fallback in vcs_tag', '0.41.0', self.subproject, location=node) @@ -2108,6 +2117,7 @@ def _validate_custom_target_outputs(self, has_multi_in: bool, outputs: T.Iterabl KwargInfo('console', bool, default=False, since='0.48.0'), KwargInfo('build_subdir', str, default='', since='1.10.0'), ) + @apply_machine_map def func_custom_target(self, node: mparser.FunctionNode, args: T.Tuple[str], kwargs: 'kwtypes.CustomTarget') -> build.CustomTarget: if kwargs['depfile'] and ('@BASENAME@' in kwargs['depfile'] or '@PLAINNAME@' in kwargs['depfile']): @@ -2703,6 +2713,7 @@ def validate_build_subdir(self, build_subdir: str, target: str) -> None: KwargInfo('macro_name', (str, NoneType), default=None, since='1.3.0'), KwargInfo('build_subdir', str, default='', since='1.10.0'), ) + @apply_machine_map def func_configure_file(self, node: mparser.BaseNode, args: T.List[TYPE_var], kwargs: kwtypes.ConfigureFile) -> mesonlib.File: actions = sorted(x for x in ('configuration', 'command', 'copy') @@ -2996,22 +3007,26 @@ def func_add_test_setup(self, node: mparser.BaseNode, args: T.Tuple[str], kwargs @typed_pos_args('add_global_arguments', varargs=str) @typed_kwargs('add_global_arguments', NATIVE_KW, LANGUAGE_KW) + @apply_machine_map def func_add_global_arguments(self, node: mparser.FunctionNode, args: T.Tuple[T.List[str]], kwargs: 'kwtypes.FuncAddProjectArgs') -> None: self._add_global_arguments(node, self.build.global_args[kwargs['native']], args[0], kwargs) @typed_pos_args('add_global_link_arguments', varargs=str) @typed_kwargs('add_global_link_arguments', NATIVE_KW, LANGUAGE_KW) + @apply_machine_map def func_add_global_link_arguments(self, node: mparser.FunctionNode, args: T.Tuple[T.List[str]], kwargs: 'kwtypes.FuncAddProjectArgs') -> None: self._add_global_arguments(node, self.build.global_link_args[kwargs['native']], args[0], kwargs) @typed_pos_args('add_project_arguments', varargs=str) @typed_kwargs('add_project_arguments', NATIVE_KW, LANGUAGE_KW) + @apply_machine_map def func_add_project_arguments(self, node: mparser.FunctionNode, args: T.Tuple[T.List[str]], kwargs: 'kwtypes.FuncAddProjectArgs') -> None: self._add_project_arguments(node, self.current_build_project().project_args[kwargs['native']], args[0], kwargs) @typed_pos_args('add_project_link_arguments', varargs=str) @typed_kwargs('add_project_link_arguments', NATIVE_KW, LANGUAGE_KW) + @apply_machine_map def func_add_project_link_arguments(self, node: mparser.FunctionNode, args: T.Tuple[T.List[str]], kwargs: 'kwtypes.FuncAddProjectArgs') -> None: self._add_project_arguments(node, self.current_build_project().project_link_args[kwargs['native']], args[0], kwargs) @@ -3022,6 +3037,7 @@ def current_build_project(self) -> build.BuildProject: @FeatureNew('add_project_dependencies', '0.63.0') @typed_pos_args('add_project_dependencies', varargs=dependencies.Dependency) @typed_kwargs('add_project_dependencies', NATIVE_KW, LANGUAGE_KW) + @apply_machine_map def func_add_project_dependencies(self, node: mparser.FunctionNode, args: T.Tuple[T.List[dependencies.Dependency]], kwargs: 'kwtypes.FuncAddProjectArgs') -> None: for_machine = kwargs['native'] for lang in kwargs['language']: diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index a3a2ace86798..b6be5a17b6c2 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -580,3 +580,9 @@ class FuncEnvironment(TypedDict): method: Literal['set', 'prepend', 'append'] separator: str + + +class MachineMapArgs(TypedDict): + + native: NotRequired[MachineChoice] + install: NotRequired[bool] diff --git a/mesonbuild/interpreter/mesonmain.py b/mesonbuild/interpreter/mesonmain.py index 3a7e08f6ecbd..e8808c666eea 100644 --- a/mesonbuild/interpreter/mesonmain.py +++ b/mesonbuild/interpreter/mesonmain.py @@ -19,6 +19,7 @@ from ..interpreterbase import (MesonInterpreterObject, FeatureNew, FeatureDeprecated, FeatureBroken, typed_pos_args, noArgsFlattening, noPosargs, noKwargs, typed_kwargs, KwargInfo, InterpreterException, InterpreterObject) +from .decorators import apply_machine_map from .primitives import MesonVersionString from .type_checking import NATIVE_KW, NoneType @@ -279,6 +280,7 @@ def is_cross_build_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') @typed_pos_args('meson.get_compiler', str) @typed_kwargs('meson.get_compiler', NATIVE_KW) @InterpreterObject.method('get_compiler') + @apply_machine_map def get_compiler_method(self, args: T.Tuple[str], kwargs: 'NativeKW') -> 'Compiler': from ..compilers.compilers import all_languages lang = args[0] @@ -343,6 +345,7 @@ def override_find_program_method(self, args: T.Tuple[str, T.Union[mesonlib.File, @typed_pos_args('meson.override_dependency', str, dependencies.Dependency) @FeatureNew('meson.override_dependency', '0.54.0') @InterpreterObject.method('override_dependency') + @apply_machine_map def override_dependency_method(self, args: T.Tuple[str, dependencies.Dependency], kwargs: 'FuncOverrideDependency') -> None: name, dep = args if not name: @@ -458,6 +461,7 @@ def get_cross_property_method(self, args: T.Tuple[str, T.Optional[object]], kwar @typed_pos_args('meson.get_external_property', str, optargs=[object]) @typed_kwargs('meson.get_external_property', NATIVE_KW) @InterpreterObject.method('get_external_property') + @apply_machine_map def get_external_property_method(self, args: T.Tuple[str, T.Optional[object]], kwargs: 'NativeKW') -> object: propname, fallback = args return self.__get_external_property_impl(propname, fallback, kwargs['native']) @@ -466,6 +470,7 @@ def get_external_property_method(self, args: T.Tuple[str, T.Optional[object]], k @typed_pos_args('meson.has_external_property', str) @typed_kwargs('meson.has_external_property', NATIVE_KW) @InterpreterObject.method('has_external_property') + @apply_machine_map def has_external_property_method(self, args: T.Tuple[str], kwargs: 'NativeKW') -> bool: prop_name = args[0] return prop_name in self.interpreter.environment.properties[kwargs['native']] diff --git a/mesonbuild/modules/codegen.py b/mesonbuild/modules/codegen.py index 57ef04d318df..cf3bf6e81bbb 100644 --- a/mesonbuild/modules/codegen.py +++ b/mesonbuild/modules/codegen.py @@ -9,6 +9,7 @@ from . import ExtensionModule, ModuleInfo from ..build import CustomTarget, CustomTargetIndex, GeneratedList from ..compilers.compilers import lang_suffixes +from ..interpreter.decorators import apply_machine_map from ..interpreter.interpreterobjects import extract_required_kwarg from ..interpreter.type_checking import NoneType, REQUIRED_KW, DISABLER_KW, NATIVE_KW from ..interpreterbase import ( @@ -297,6 +298,7 @@ def __init__(self, interpreter: Interpreter) -> None: DISABLER_KW, NATIVE_KW ) + @apply_machine_map @disablerIfNotFound def lex_method(self, state: ModuleState, args: T.Tuple, kwargs: FindLexKwargs) -> LexGenerator: disabled, required, feature = extract_required_kwarg(kwargs, state.subproject) @@ -376,6 +378,7 @@ def lex_method(self, state: ModuleState, args: T.Tuple, kwargs: FindLexKwargs) - DISABLER_KW, NATIVE_KW, ) + @apply_machine_map @disablerIfNotFound def yacc_method(self, state: ModuleState, args: T.Tuple, kwargs: FindYaccKwargs) -> YaccGenerator: disabled, required, feature = extract_required_kwarg(kwargs, state.subproject) diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py index 59388686a2bc..e8c359529f95 100644 --- a/mesonbuild/modules/python.py +++ b/mesonbuild/modules/python.py @@ -14,6 +14,7 @@ from ..dependencies.detect import get_dep_identifier, find_external_dependency from ..dependencies.python import BasicPythonExternalProgram, python_factory, _PythonDependencyBase from ..interpreter import extract_required_kwarg, primitives as P_OBJ +from ..interpreter.decorators import apply_machine_map from ..interpreter.interpreterobjects import ProgramHolder from ..interpreter.type_checking import NoneType, DEPENDENCY_KWS, PRESERVE_PATH_KW, REQUIRED_KW, SHARED_MOD_KWS from ..interpreterbase import ( @@ -152,6 +153,7 @@ def as_(obj: object, type_: T.Type[_T]) -> _T: _LIMITED_API_KW, KwargInfo('install_dir', (str, bool, NoneType)), ) + @apply_machine_map @InterpreterObject.method('extension_module') def extension_module_method(self, args: T.Tuple[str, T.List[BuildTargetSource]], kwargs: ExtensionModuleKw) -> 'SharedModule': target_kwargs = T.cast('SharedModuleKw', {k: v for k, v in kwargs.items() if k not in {'install_dir', 'subdir', 'limited_api'}}) diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index 887cfb5926ac..d4e422cd1170 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -18,6 +18,7 @@ from ..compilers.compilers import are_asserts_disabled_for_subproject, lang_suffixes from ..compilers.rust import parse_target, RustSystemDependency from ..dependencies import Dependency +from ..interpreter.decorators import apply_machine_map from ..interpreter.type_checking import ( DEPENDENCIES_KW, LINK_WITH_KW, LINK_WHOLE_KW, SHARED_LIB_KWS, TEST_KWS, TEST_KWS_NO_ARGS, OUTPUT_KW, INCLUDE_DIRECTORIES, SOURCES_VARARGS, NATIVE_KW, NoneType, in_set_validator, @@ -976,6 +977,7 @@ def bindgen(self, state: ModuleState, args: T.List, kwargs: FuncBindgen) -> Modu @FeatureNew('rust.compiler_target', '1.11.0') @noPosargs @typed_kwargs('rust.compiler_target', NATIVE_KW) + @apply_machine_map def compiler_target(self, state: ModuleState, args: T.List, kwargs: '_kwargs.NativeKW') -> str: """Returns the Rust target triple for the specified machine's Rust compiler.""" for_machine = kwargs['native'] diff --git a/test cases/unit/116 empty project/expected_mods.json b/test cases/unit/116 empty project/expected_mods.json index 678fea94a9f8..e33ada2470e7 100644 --- a/test cases/unit/116 empty project/expected_mods.json +++ b/test cases/unit/116 empty project/expected_mods.json @@ -194,6 +194,7 @@ "mesonbuild.environment", "mesonbuild.interpreter", "mesonbuild.interpreter.compiler", + "mesonbuild.interpreter.decorators", "mesonbuild.interpreter.dependencyfallbacks", "mesonbuild.interpreter.interpreter", "mesonbuild.interpreter.interpreterobjects", @@ -241,6 +242,6 @@ "mesonbuild.wrap", "mesonbuild.wrap.wrap" ], - "count": 72 + "count": 73 } } diff --git a/unittests/machinefiletests.py b/unittests/machinefiletests.py index 7f9a6f0b66ce..b20b8172f9e4 100644 --- a/unittests/machinefiletests.py +++ b/unittests/machinefiletests.py @@ -1018,7 +1018,7 @@ def test_external_property_build_machine_native(self): testdir = os.path.join(self.unit_test_dir, '137 external property nonexisting') self.meson_native_files = [os.path.join(testdir, "propfile")] out = self.init(testdir, allow_fail=True, extra_args=['-Dnative=true']) - self.assertIn('Unknown property for build machine: nonexisting', out) + self.assertIn('Unknown property for host machine: nonexisting', out) def test_external_property_host_machine_native(self): testdir = os.path.join(self.unit_test_dir, '137 external property nonexisting') From c27e4177eed5e2a29e89fa7f797ce950857f16f6 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 19 Jan 2026 22:22:54 +0100 Subject: [PATCH 510/681] interpreter: apply machine map when decorator can't be used There are a few rare cases where it is not possible to use @apply_machine_map_to_kwargs: - add_project_*_arguments, because in build-only subprojects `native: true` must be discarded - add_languages, where absent `native` is not the same as `native: false` - build targets (at least for now; this may change later if the interface to build is not anymore build/host/target but the machine name), so that the transformation is done in def build_target() - modules that do not yet support a `native` argument Handle them. Signed-off-by: Paolo Bonzini --- mesonbuild/interpreter/interpreter.py | 21 +++++++++++++++++---- mesonbuild/modules/__init__.py | 1 + mesonbuild/modules/_qt.py | 5 +++-- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 6f7bda83d32f..63a72f279d71 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -347,7 +347,7 @@ def _redetect_machines(self) -> None: self.builtin['target_machine'] = \ OBJ.MachineHolder(self.build.environment.machines[self.build.machine_map.target], self) - def apply_machine_map_to_kwargs(self, kwargs: kwtypes.MachineMapArgs) -> None: + def apply_machine_map_to_kwargs(self, kwargs: kwtypes.BaseBuildTarget | kwtypes.MachineMapArgs) -> None: orig_for_machine: MachineChoice | None = kwargs.get('native', None) if orig_for_machine is not None: kwargs['native'] = self.build.machine_map[orig_for_machine] @@ -1362,7 +1362,7 @@ def func_project(self, node: mparser.FunctionNode, args: T.Tuple[str, T.List[str @typed_pos_args('add_languages', varargs=str) def func_add_languages(self, node: mparser.FunctionNode, args: T.Tuple[T.List[str]], kwargs: 'kwtypes.FuncAddLanguages') -> bool: disabled, required, feature = extract_required_kwarg(kwargs, self.subproject) - native = kwargs['native'] + for_machine = kwargs['native'] langs = self._validate_languages(args[0], required, node) @@ -1370,8 +1370,9 @@ def func_add_languages(self, node: mparser.FunctionNode, args: T.Tuple[T.List[st for lang in sorted(langs, key=compilers.sort_clink): mlog.log('Compiler for language', mlog.bold(lang), 'skipped: feature', mlog.bold(feature), 'disabled') return False - if native is not None: - return self.add_languages(langs, required, native) + if for_machine is not None: + for_machine = self.build.machine_map[for_machine] + return self.add_languages(langs, required, for_machine) else: # absent 'native' means 'both' for backwards compatibility tv = FeatureNew.get_target_version(self.subproject) @@ -3103,8 +3104,18 @@ def _add_project_arguments(self, node: mparser.FunctionNode, argsdict: T.Dict[La args: T.List[str], kwargs: 'kwtypes.FuncAddProjectArgs') -> None: self._add_arguments(node, argsdict, self.project_args_frozen, args, kwargs) + def is_dup_machine(self, for_machine: MachineChoice) -> bool: + # Return whether applying the caller's effect to the build and + # host machine would duplicate the effect + return \ + for_machine is not MachineChoice.HOST and \ + self.build.machine_map[for_machine] is self.build.machine_map.host + def _add_arguments(self, node: mparser.FunctionNode, argsdict: T.Dict[Language, T.List[str]], args_frozen: bool, args: T.List[str], kwargs: 'kwtypes.FuncAddProjectArgs') -> None: + if self.is_dup_machine(kwargs['native']): + return + if args_frozen: msg = f'Tried to use \'{node.func_name.value}\' after a build target has been declared.\n' \ 'This is not permitted. Please declare all arguments before your targets.' @@ -3846,7 +3857,9 @@ def build_target(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargs mlog.debug('Unknown target type:', str(targetclass)) raise RuntimeError('Unreachable code') + self.apply_machine_map_to_kwargs(kwargs) 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. diff --git a/mesonbuild/modules/__init__.py b/mesonbuild/modules/__init__.py index 45ca2286f9d0..b06d72501a8e 100644 --- a/mesonbuild/modules/__init__.py +++ b/mesonbuild/modules/__init__.py @@ -58,6 +58,7 @@ def __init__(self, interpreter: 'Interpreter') -> None: self.global_args = interpreter.build.global_args.host self.project_args = interpreter.current_build_project().project_args.host self.current_node = interpreter.current_node + self.machine_map = interpreter.build.machine_map def get_include_args(self, include_dirs: T.Iterable[T.Union[str, build.IncludeDirs]], implicit: bool = False, prefix: str = '-I') -> T.List[str]: srcdir = self.environment.get_source_dir() diff --git a/mesonbuild/modules/_qt.py b/mesonbuild/modules/_qt.py index 619c99d074dd..bb006ab5ac34 100644 --- a/mesonbuild/modules/_qt.py +++ b/mesonbuild/modules/_qt.py @@ -14,7 +14,7 @@ from .. import build from .. import mlog from ..dependencies import DependencyMethods, find_external_dependency, Dependency, ExternalLibrary, InternalDependency -from ..mesonlib import MachineChoice, MesonException, File, FileMode, version_compare, Popen_safe +from ..mesonlib import MesonException, File, FileMode, version_compare, Popen_safe from ..interpreter import extract_required_kwarg from ..interpreter.type_checking import DEPENDENCY_METHOD_KW, INSTALL_DIR_KW, INSTALL_KW, REQUIRED_KW, NoneType from ..interpreterbase import ContainerTypeInfo, FeatureDeprecated, KwargInfo, noPosargs, FeatureNew, typed_kwargs, typed_pos_args @@ -272,8 +272,9 @@ def _detect_tools(self, state: ModuleState, method: DependencyMethods, required: return self._tools_detected = True mlog.log(f'Detecting Qt{self.qt_version} tools') + native = state.machine_map.host version = version or [] - kwargs: DependencyObjectKWs = {'required': required, 'modules': ['Core'], 'method': method, 'native': MachineChoice.HOST, 'version': version} + kwargs: DependencyObjectKWs = {'required': required, 'modules': ['Core'], 'method': method, 'native': native, 'version': version} # Just pick one to make mypy happy qt = T.cast('QtPkgConfigDependency', find_external_dependency(f'qt{self.qt_version}', state.environment, kwargs)) if qt.found(): From 0b4c3ca0c4b54002f0088982afec9828db795811 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 9 Jun 2026 19:37:04 +0200 Subject: [PATCH 511/681] pkgconfig: do not map machine in PkgConfigInterface.instance It is only ever called from _cli (which does not need to map the MachineChoice) or PkgConfigDependency (which is created - via DependencyFallbacksHolder - from DependencyObjectKWs that already have a properly mapped machine. Signed-off-by: Paolo Bonzini --- mesonbuild/dependencies/pkgconfig.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mesonbuild/dependencies/pkgconfig.py b/mesonbuild/dependencies/pkgconfig.py index f356bd314a3d..5a290959a7d3 100644 --- a/mesonbuild/dependencies/pkgconfig.py +++ b/mesonbuild/dependencies/pkgconfig.py @@ -46,7 +46,6 @@ def set_program_override(pkg_bin: ExternalProgram, for_machine: MachineChoice) - def instance(env: Environment, for_machine: MachineChoice, silent: bool, extra_paths: T.Optional[T.List[str]] = None) -> T.Optional[PkgConfigInterface]: '''Return a pkg-config implementation singleton''' - for_machine = for_machine if env.is_cross_build() else MachineChoice.HOST extra_paths_key = tuple(extra_paths) if extra_paths is not None else None impl = PkgConfigInterface.class_impl[for_machine].get(extra_paths_key, False) if impl is False: From 3799c8823fc7e3142211f52692ce740bdf3f12c0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 9 Jun 2026 18:16:11 +0200 Subject: [PATCH 512/681] build: add subproject to BuildProject Signed-off-by: Paolo Bonzini --- mesonbuild/build.py | 1 + mesonbuild/interpreter/interpreter.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 05952cf1b21f..4d8844631656 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -304,6 +304,7 @@ def to_json(self) -> T.Dict[str, T.Union[str, T.List[str]]]: class BuildProject: name: str version: str + subproject: SubProject project_args: PerMachine[T.Dict[Language, T.List[str]]] = field(default_factory=lambda: PerMachine({}, {})) project_link_args: PerMachine[T.Dict[Language, T.List[str]]] = field(default_factory=lambda: PerMachine({}, {})) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 63a72f279d71..994c11b4bbde 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1339,7 +1339,8 @@ def func_project(self, node: mparser.FunctionNode, args: T.Tuple[str, T.List[str if self.cargo is None: self.load_root_cargo_lock_file() - self.build.projects[self.subproject] = build.BuildProject(proj_name, self.project_version) + self.build.projects[self.subproject] = build.BuildProject(proj_name, self.project_version, + self.subproject) mlog.log('Project name:', mlog.bold(proj_name)) mlog.log('Project version:', mlog.bold(self.project_version)) From d7bedd8cc1e075912274c3acd482dc8b9d8c3757 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 9 Jun 2026 15:53:22 +0200 Subject: [PATCH 513/681] build: replace SubProject with BuildProject for target The SubProject alone will not be a unique key into build.projects. Just store the object itself, as it allows retrieving the key. Signed-off-by: Paolo Bonzini --- mesonbuild/ast/introspection.py | 8 ++- mesonbuild/build.py | 77 ++++++++++++++------------ mesonbuild/interpreter/compiler.py | 2 +- mesonbuild/interpreter/interpreter.py | 31 ++++++----- mesonbuild/modules/__init__.py | 1 + mesonbuild/modules/_qt.py | 12 ++-- mesonbuild/modules/codegen.py | 4 +- mesonbuild/modules/external_project.py | 6 +- mesonbuild/modules/fs.py | 2 +- mesonbuild/modules/gnome.py | 54 +++++++++--------- mesonbuild/modules/hotdoc.py | 10 ++-- mesonbuild/modules/i18n.py | 23 ++++---- mesonbuild/modules/icestorm.py | 10 ++-- mesonbuild/modules/java.py | 5 +- mesonbuild/modules/rust.py | 7 ++- mesonbuild/modules/wayland.py | 4 +- mesonbuild/modules/windows.py | 2 +- unittests/allplatformstests.py | 7 ++- 18 files changed, 143 insertions(+), 122 deletions(-) diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py index 75e942beaedb..f62ae2c5d47f 100644 --- a/mesonbuild/ast/introspection.py +++ b/mesonbuild/ast/introspection.py @@ -10,7 +10,7 @@ import typing as T from .. import compilers, environment, mesonlib -from ..build import Executable, Jar, SharedLibrary, SharedModule, StaticLibrary +from ..build import Executable, Jar, SharedLibrary, SharedModule, StaticLibrary, BuildProject from ..compilers import detect_compiler_for from ..interpreterbase import InvalidArguments, UnknownValue, Feature from ..interpreter import type_checking @@ -76,6 +76,7 @@ def __init__(self, self.cross_file = cross_file self.backend = backend + self.build_project = BuildProject(subproject, '', subproject) self.project_data: T.Dict[str, T.Any] = {} self.targets: T.List[IntrospectionBuildTarget] = [] self.dependencies: T.List[IntrospectionDependency] = [] @@ -263,8 +264,9 @@ def build_target(self, node: BaseNode, args: T.List[TYPE_var], kwargs_raw: T.Dic empty_sources: T.List[T.Any] = [] # Passing the unresolved sources list causes errors kwargs_reduced['_allow_no_sources'] = True - target = targetclass(name, self.subdir, self.subproject, for_machine, empty_sources, None, objects, - self.environment, self.coredata.compilers[for_machine], kwargs_reduced) + target = targetclass(name, self.subdir, for_machine, empty_sources, None, objects, + self.environment, self.coredata.compilers[for_machine], + self.build_project, kwargs_reduced) target.process_compilers_late() build_by_default: T.Union[UnknownValue, bool] = target.build_by_default diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 4d8844631656..4eafe038a9ae 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -455,7 +455,8 @@ def get_global_args(self, compiler: 'Compiler', for_machine: 'MachineChoice') -> return d.get(compiler.get_language(), []) def get_project_args(self, compiler: 'Compiler', target: BuildTarget) -> T.List[str]: - args = self.projects[target.subproject].project_args[target.for_machine] + d = target.build_project + args = d.project_args[target.for_machine] if not args: return [] return args.get(compiler.get_language(), []) @@ -465,7 +466,8 @@ def get_global_link_args(self, compiler: 'Compiler', for_machine: 'MachineChoice return d.get(compiler.get_language(), []) def get_project_link_args(self, compiler: 'Compiler', target: BuildTarget) -> T.List[str]: - link_args = self.projects[target.subproject].project_link_args[target.for_machine] + d = target.build_project + link_args = d.project_link_args[target.for_machine] if not link_args: return [] @@ -623,10 +625,10 @@ class Target(HoldableObject, metaclass=SimpleABC): name: str subdir: str - subproject: 'SubProject' build_by_default: bool for_machine: MachineChoice environment: Environment + build_project: BuildProject install: bool = False build_always_stale: bool = False extra_files: T.List[File] = field(default_factory=list) @@ -720,6 +722,10 @@ def construct_id_from_path(subdir: str, name: str, type_suffix: str) -> str: return subdir_part + '@@' + my_id return my_id + @lazy_property + def subproject(self) -> SubProject: + return self.build_project.subproject + @lazy_property def id(self) -> str: return self.construct_id_from_path( @@ -753,18 +759,19 @@ def __init__( self, name: str, subdir: str, - subproject: SubProject, for_machine: MachineChoice, sources: T.List['SourceOutputs'], structured_sources: T.Optional[StructuredSources], objects: T.Sequence[ObjectTypes | GeneratedTypes], environment: Environment, compilers: CompilerDict, + build_project: BuildProject, kwargs: BuildTargetKeywordArguments): - super().__init__(name, subdir, subproject, kwargs.get('build_by_default', True), + super().__init__(name, subdir, kwargs.get('build_by_default', True), for_machine, environment, install=kwargs.get('install', False), - build_subdir=kwargs.get('build_subdir', '')) + build_subdir=kwargs.get('build_subdir', ''), + build_project=build_project) self.original_kwargs = kwargs # all_compilers is a reference to Interpreter.compilers, as such we # cannot mutate it inside build. Use a Mapping to get help from the @@ -2142,18 +2149,18 @@ def __init__( self, name: str, subdir: str, - subproject: SubProject, for_machine: MachineChoice, sources: T.List['SourceOutputs'], structured_sources: T.Optional[StructuredSources], objects: T.Sequence[ObjectTypes | GeneratedTypes], environment: Environment, compilers: CompilerDict, + build_project: BuildProject, kwargs: ExecutableKeywordArguments): self.export_dynamic = kwargs.get('export_dynamic', False) self.rust_crate_type = kwargs.get('rust_crate_type', 'bin') - super().__init__(name, subdir, subproject, for_machine, sources, structured_sources, objects, - environment, compilers, kwargs) + super().__init__(name, subdir, for_machine, sources, structured_sources, objects, + environment, compilers, build_project, kwargs) self.win_subsystem = kwargs.get('win_subsystem') or 'console' self.pie = self._extract_pic_pie(kwargs, 'pie', 'b_pie') # Check for export_dynamic @@ -2275,18 +2282,18 @@ def __init__( self, name: str, subdir: str, - subproject: SubProject, for_machine: MachineChoice, sources: T.List['SourceOutputs'], structured_sources: T.Optional[StructuredSources], objects: T.Sequence[ObjectTypes | GeneratedTypes], environment: Environment, compilers: CompilerDict, + build_project: BuildProject, kwargs: StaticLibraryKeywordArguments): self.prelink = kwargs.get('prelink', False) self.rust_crate_type = kwargs.get('rust_crate_type', 'rlib') - super().__init__(name, subdir, subproject, for_machine, sources, structured_sources, objects, - environment, compilers, kwargs) + super().__init__(name, subdir, for_machine, sources, structured_sources, objects, + environment, compilers, build_project, kwargs) self.pic = self._extract_pic_pie(kwargs, 'pic', 'b_staticpic') if not self.pic: self.pie = self._extract_pic_pie(kwargs, 'pie', 'b_pie') @@ -2457,16 +2464,16 @@ def __init__( self, name: str, subdir: str, - subproject: SubProject, for_machine: MachineChoice, sources: T.List['SourceOutputs'], structured_sources: T.Optional[StructuredSources], objects: T.Sequence[ObjectTypes | GeneratedTypes], environment: Environment, compilers: CompilerDict, + build_project: BuildProject, kwargs: SharedLibraryKeywordArguments): - super().__init__(name, subdir, subproject, for_machine, sources, structured_sources, objects, - environment, compilers, kwargs) + super().__init__(name, subdir, for_machine, sources, structured_sources, objects, + environment, compilers, build_project, kwargs) # Max length 2, first element is compatibility_version, second is current_version self.darwin_versions: T.Optional[T.Tuple[str, str]] = None self.soversion: T.Optional[str] = None @@ -2795,16 +2802,16 @@ def __init__( self, name: str, subdir: str, - subproject: SubProject, for_machine: MachineChoice, sources: T.List['SourceOutputs'], structured_sources: T.Optional[StructuredSources], objects: T.Sequence[ObjectTypes | GeneratedTypes], environment: Environment, compilers: CompilerDict, + build_project: BuildProject, kwargs: SharedModuleKeywordArguments): - super().__init__(name, subdir, subproject, for_machine, sources, - structured_sources, objects, environment, compilers, + super().__init__(name, subdir, for_machine, sources, + structured_sources, objects, environment, compilers, build_project, # SharedModuleKeywordArguments is a subclass, it's annoying mypy can't figure this out T.cast('SharedLibraryKeywordArguments', kwargs)) # We need to set the soname in cases where build files link the module @@ -2942,11 +2949,11 @@ class CustomTarget(Target, CustomTargetBase): def __init__(self, name: T.Optional[str], subdir: str, - subproject: SubProject, environment: Environment, command: T.Sequence[CommandTypes], sources: T.Sequence[CustomTargetSources], outputs: T.List[str], + build_project: BuildProject, *, build_always_stale: bool = False, build_by_default: T.Optional[bool] = None, @@ -2969,8 +2976,8 @@ def __init__(self, build_subdir: str = '', ): # TODO expose keyword arg to make MachineChoice.HOST configurable - super().__init__(name, subdir, subproject, False, MachineChoice.HOST, environment, - install, build_always_stale, build_subdir = build_subdir) + super().__init__(name, subdir, False, MachineChoice.HOST, environment, + build_project, install, build_always_stale, build_subdir = build_subdir) self.sources = list(sources) self.outputs = substitute_values( outputs, get_filenames_templates_dict( @@ -2982,7 +2989,7 @@ def __init__(self, self.depend_files = list(depend_files or []) self.dependencies: T.List[T.Union[CustomTarget, BuildTarget]] = [] # must be after depend_files and dependencies - c, df, d = flatten_command(command, self.subproject) + c, df, d = flatten_command(command, build_project.subproject) self.command = c self.depend_files.extend(df) self.dependencies.extend(d) @@ -3173,7 +3180,6 @@ class CompileTarget(BuildTarget): def __init__(self, name: str, subdir: str, - subproject: SubProject, environment: Environment, sources: T.List['SourceOutputs'], output_templ: str, @@ -3182,6 +3188,7 @@ def __init__(self, compile_args: T.List[str], include_directories: T.List[IncludeDirs], dependencies: T.List[dependencies.Dependency], + build_project: BuildProject, depends: T.List[BuildTargetTypes]): compilers = {compiler.get_language(): compiler} kwargs: BuildTargetKeywordArguments = { @@ -3190,8 +3197,9 @@ def __init__(self, 'include_directories': include_directories, 'dependencies': dependencies, } - super().__init__(name, subdir, subproject, compiler.for_machine, - sources, None, [], environment, compilers, kwargs) + super().__init__(name, subdir, compiler.for_machine, + sources, None, [], environment, compilers, + build_project, kwargs) self.filename = name self.compiler = compiler self.output_templ = output_templ @@ -3233,14 +3241,14 @@ def __init__(self, name: str, # the RunTarget case is used by gnome.yelp() dependencies: T.Sequence[Target | CustomTargetIndex | GeneratedList | programs.Program], subdir: str, - subproject: SubProject, environment: Environment, + build_project: BuildProject, env: T.Optional[EnvironmentVariables] = None, default_env: bool = True): # These don't produce output artifacts - super().__init__(name, subdir, subproject, False, MachineChoice.BUILD, environment) + super().__init__(name, subdir, False, MachineChoice.BUILD, environment, build_project) self.dependencies = list(dependencies) - self.command, self.depend_files, d = flatten_command(command, subproject) + self.command, self.depend_files, d = flatten_command(command, build_project.subproject) self.dependencies.extend(d) self.absolute_paths = False self.env = env @@ -3276,8 +3284,9 @@ class AliasTarget(RunTarget): typename = 'alias' def __init__(self, name: str, dependencies: T.Sequence[Target], - subdir: str, subproject: SubProject, environment: Environment): - super().__init__(name, [], dependencies, subdir, subproject, environment) + subdir: str, environment: Environment, + build_project: BuildProject): + super().__init__(name, [], dependencies, subdir, environment, build_project) def __repr__(self) -> str: repr_str = "<{0} {1}>" @@ -3287,12 +3296,12 @@ class Jar(BuildTarget): typename = 'jar' rust_crate_type = '' # type: ignore[assignment] - def __init__(self, name: str, subdir: str, subproject: SubProject, for_machine: MachineChoice, + def __init__(self, name: str, subdir: str, for_machine: MachineChoice, sources: T.List[SourceOutputs], structured_sources: T.Optional['StructuredSources'], objects: T.Sequence[ObjectTypes | GeneratedTypes], environment: Environment, compilers: CompilerDict, - kwargs: JarKeywordArguments): - super().__init__(name, subdir, subproject, for_machine, sources, structured_sources, objects, - environment, compilers, kwargs) + build_project: BuildProject, kwargs: JarKeywordArguments): + super().__init__(name, subdir, for_machine, sources, structured_sources, objects, + environment, compilers, build_project, kwargs) for s in self.sources: if not s.endswith('.java'): raise InvalidArguments(f'Jar source {s} is not a java file.') diff --git a/mesonbuild/interpreter/compiler.py b/mesonbuild/interpreter/compiler.py index f697f2bc3444..1cf9553fa792 100644 --- a/mesonbuild/interpreter/compiler.py +++ b/mesonbuild/interpreter/compiler.py @@ -908,7 +908,6 @@ def preprocess_method(self, args: T.Tuple[T.List['mesonlib.FileOrString']], kwar tg = build.CompileTarget( tg_name, self.interpreter.subdir, - self.subproject, self.environment, sources, kwargs['output'], @@ -917,6 +916,7 @@ def preprocess_method(self, args: T.Tuple[T.List['mesonlib.FileOrString']], kwar kwargs['compile_args'], self.interpreter.extract_incdirs(kwargs['include_directories']), kwargs['dependencies'], + self.interpreter.current_build_project(), kwargs['depends']) self.interpreter.add_target(tg.name, tg) # Expose this target as list of its outputs, so user can pass them to diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 994c11b4bbde..378cb368d615 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -2058,11 +2058,11 @@ def func_vcs_tag(self, node: mparser.BaseNode, args: T.List['TYPE_var'], kwargs: tg = build.CustomTarget( kwargs['output'][0], self.subdir, - self.subproject, self.environment, cmd, self.source_strings_to_files(kwargs['input']), kwargs['output'], + self.current_build_project(), build_by_default=True, build_always_stale=True, install=install, @@ -2189,11 +2189,11 @@ def func_custom_target(self, node: mparser.FunctionNode, args: T.Tuple[str], tg = build.CustomTarget( name, self.subdir, - self.subproject, self.environment, command, inputs, kwargs['output'], + self.current_build_project(), build_always_stale=build_always_stale, build_by_default=build_by_default, capture=kwargs['capture'], @@ -2237,8 +2237,8 @@ def func_run_target(self, node: mparser.FunctionNode, args: T.Tuple[str], raise InterpreterException('run_target does not have support for @DEPFILE@') name = args[0] - tg = build.RunTarget(name, all_args, kwargs['depends'], self.subdir, self.subproject, self.environment, - kwargs['env']) + tg = build.RunTarget(name, all_args, kwargs['depends'], self.subdir, self.environment, + self.current_build_project(), kwargs['env']) self.add_target(name, tg) return tg @@ -2257,7 +2257,8 @@ def func_alias_target(self, node: mparser.BaseNode, args: T.Tuple[str, T.List[T. real_deps.append(d.static) else: real_deps.append(d) - tg = build.AliasTarget(name, real_deps, self.subdir, self.subproject, self.environment) + tg = build.AliasTarget(name, real_deps, self.subdir, self.environment, + self.current_build_project()) self.add_target(name, tg) return tg @@ -3931,30 +3932,30 @@ def build_target(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargs if targetclass is build.Executable: nkwargs = self.__convert_executable_kwargs( node, T.cast('kwtypes.Executable', kwargs)) - target = build.Executable(name, self.subdir, self.subproject, for_machine, srcs, struct, objs, - self.environment, self.compilers[for_machine], nkwargs) + target = build.Executable(name, self.subdir, for_machine, srcs, struct, objs, + self.environment, self.compilers[for_machine], self.current_build_project(), nkwargs) elif targetclass is build.StaticLibrary: nkwargs = self.__convert_static_library_kwargs( node, T.cast('kwtypes.StaticLibrary', kwargs)) - target = build.StaticLibrary(name, self.subdir, self.subproject, for_machine, srcs, struct, objs, - self.environment, self.compilers[for_machine], nkwargs) + target = build.StaticLibrary(name, self.subdir, for_machine, srcs, struct, objs, + self.environment, self.compilers[for_machine], self.current_build_project(), nkwargs) elif targetclass is build.SharedLibrary: nkwargs = self.__convert_shared_library_kwargs( node, T.cast('kwtypes.SharedLibrary', kwargs)) - target = build.SharedLibrary(name, self.subdir, self.subproject, for_machine, srcs, struct, objs, - self.environment, self.compilers[for_machine], nkwargs) + target = build.SharedLibrary(name, self.subdir, for_machine, srcs, struct, objs, + self.environment, self.compilers[for_machine], self.current_build_project(), nkwargs) target.shared_library_only = shared_library_only elif targetclass is build.SharedModule: nkwargs = self.__convert_shared_module_kwargs( node, T.cast('kwtypes.SharedModule', kwargs)) - target = build.SharedModule(name, self.subdir, self.subproject, for_machine, srcs, struct, objs, - self.environment, self.compilers[for_machine], nkwargs) + target = build.SharedModule(name, self.subdir, for_machine, srcs, struct, objs, + self.environment, self.compilers[for_machine], self.current_build_project(), nkwargs) target.shared_library_only = shared_library_only else: nkwargs = self.__convert_jar_kwargs( node, T.cast('kwtypes.Jar', kwargs)) - target = build.Jar(name, self.subdir, self.subproject, for_machine, srcs, struct, objs, - self.environment, self.compilers[for_machine], nkwargs) + target = build.Jar(name, self.subdir, for_machine, srcs, struct, objs, + self.environment, self.compilers[for_machine], self.current_build_project(), nkwargs) if objs and target.uses_rust(): FeatureNew.single_use('objects in Rust targets', '1.8.0', self.subproject) diff --git a/mesonbuild/modules/__init__.py b/mesonbuild/modules/__init__.py index b06d72501a8e..38be13de291a 100644 --- a/mesonbuild/modules/__init__.py +++ b/mesonbuild/modules/__init__.py @@ -59,6 +59,7 @@ def __init__(self, interpreter: 'Interpreter') -> None: self.project_args = interpreter.current_build_project().project_args.host self.current_node = interpreter.current_node self.machine_map = interpreter.build.machine_map + self.current_build_project = interpreter.current_build_project() def get_include_args(self, include_dirs: T.Iterable[T.Union[str, build.IncludeDirs]], implicit: bool = False, prefix: str = '-I') -> T.List[str]: srcdir = self.environment.get_source_dir() diff --git a/mesonbuild/modules/_qt.py b/mesonbuild/modules/_qt.py index bb006ab5ac34..b29e6648fa2d 100644 --- a/mesonbuild/modules/_qt.py +++ b/mesonbuild/modules/_qt.py @@ -456,11 +456,11 @@ def _compile_resources_impl(self, state: 'ModuleState', kwargs: 'ResourceCompile res_target = build.CustomTarget( name, state.subdir, - state.subproject, state.environment, cmd, sources, [f'{name}.cpp'], + state.current_build_project, depend_files=qrc_deps, depfile=f'{name}.d', description='Compiling Qt resources {}', @@ -475,11 +475,11 @@ def _compile_resources_impl(self, state: 'ModuleState', kwargs: 'ResourceCompile res_target = build.CustomTarget( name, state.subdir, - state.subproject, state.environment, cmd, [rcc_file], [f'{name}.cpp'], + state.current_build_project, depend_files=qrc_deps, depfile=f'{name}.d', description='Compiling Qt resources {}', @@ -753,11 +753,11 @@ def compile_translations(self, state: ModuleState, args: T.Tuple, kwargs: Compil lrelease_target = build.CustomTarget( f'qt{self.qt_version}-compile-{ts}', outdir, - state.subproject, state.environment, cmd, [ts_file], ['@BASENAME@.qm'], + state.current_build_project, install=kwargs['install'], install_dir=[kwargs['install_dir']], install_tag=['i18n'], @@ -893,11 +893,11 @@ def _moc_json_collect(self, state: ModuleState, kwargs: MocJsonCollectKwArgs) -> return build.CustomTarget( f'moc_collect_json_{target_name}', state.subdir, - state.subproject, state.environment, cmd, moc_json, [f'{target_name}_json_collect.json'], + state.current_build_project, description=f'Collecting json type information for {target_name}', ) @@ -942,12 +942,12 @@ def _gen_qml_cachegen(self, state: ModuleState, kwargs: GenQmlCachegenKwArgs) -> cacheloader_target = build.CustomTarget( f'cacheloader_{target_name}', state.subdir, - state.subproject, state.environment, cmd, [kwargs['qml_qrc']], #output name format matters here [f'{target_name}_qmlcache_loader.cpp'], + state.current_build_project, description=f'Qml cache loader for {target_name}', ) output.append(cacheloader_target) @@ -999,11 +999,11 @@ def _qml_type_registrar(self, state: ModuleState, kwargs: GenQmlTypeRegistrarKwA return build.CustomTarget( f'typeregistrar_{target_name}', state.subdir, - state.subproject, state.environment, cmd, inputs, outputs, + state.current_build_project, install=kwargs['install'], install_dir=install_dir, install_tag=install_tag, diff --git a/mesonbuild/modules/codegen.py b/mesonbuild/modules/codegen.py index cf3bf6e81bbb..0c959090a7d5 100644 --- a/mesonbuild/modules/codegen.py +++ b/mesonbuild/modules/codegen.py @@ -180,11 +180,11 @@ def generate_method(self, args: T.Tuple[T.Union[str, File, GeneratedList, Custom target = CustomTarget( f'codegen-lex-{name}-{for_machine.get_lower_case_name()}', self.interpreter.subdir, - self.interpreter.subproject, self.interpreter.environment, command, [input], outputs, + self.interpreter.current_build_project(), backend=self.interpreter.backend, description='Generating lexer {{}} with {}'.format(self.held_object.name), ) @@ -255,11 +255,11 @@ def generate_method(self, args: T.Tuple[T.Union[str, File, CustomTarget, CustomT target = CustomTarget( f'codegen-yacc-{name}-{for_machine.get_lower_case_name()}', self.interpreter.subdir, - self.interpreter.subproject, self.interpreter.environment, command, [input], outputs, + self.interpreter.current_build_project(), backend=self.interpreter.backend, description='Generating parser {{}} with {}'.format(self.held_object.name), ) diff --git a/mesonbuild/modules/external_project.py b/mesonbuild/modules/external_project.py index b945066d9533..a800f961d57d 100644 --- a/mesonbuild/modules/external_project.py +++ b/mesonbuild/modules/external_project.py @@ -101,7 +101,7 @@ def __init__(self, self._configure(state) - self.targets = self._create_targets(extra_depends) + self.targets = self._create_targets(extra_depends, state.current_build_project) def _cygpath_convert(self, winpath: Path) -> Path: # On Cygwin, MSYS2 and GitBash, the configure command and the prefix @@ -234,7 +234,7 @@ def _run(self, step: str, command: T.List[str], workdir: Path) -> None: print(contents) raise MesonException(m) - def _create_targets(self, extra_depends: T.List[TargetDepends]) -> T.List['TYPE_var']: + def _create_targets(self, extra_depends: T.List[TargetDepends], build_project: build.BuildProject) -> T.List['TYPE_var']: cmd = self.env.get_build_command() cmd += ['--internal', 'externalproject', '--name', self.name, @@ -250,11 +250,11 @@ def _create_targets(self, extra_depends: T.List[TargetDepends]) -> T.List['TYPE_ self.target = build.CustomTarget( self.name, self.subdir.as_posix(), - self.subproject, self.env, cmd + ['@OUTPUT@', '@DEPFILE@'], [], [f'{self.name}.stamp'], + build_project, depfile=f'{self.name}.d', console=True, extra_depends=extra_depends, diff --git a/mesonbuild/modules/fs.py b/mesonbuild/modules/fs.py index 07c0b7fb29b4..c7df1cd36290 100644 --- a/mesonbuild/modules/fs.py +++ b/mesonbuild/modules/fs.py @@ -308,11 +308,11 @@ def copyfile(self, state: ModuleState, args: T.Tuple[FileOrString, T.Optional[st ct = CustomTarget( dest, state.subdir, - state.subproject, state.environment, state.environment.get_build_command() + ['--internal', 'copy', '@INPUT@', '@OUTPUT@'], [src], [dest], + state.current_build_project, build_by_default=True, install=kwargs['install'], install_dir=[kwargs['install_dir']], diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 19e2a37756c9..e6e51b2d1369 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -515,11 +515,11 @@ def compile_resources(self, state: 'ModuleState', args: T.Tuple[str, CustomTarge target_c = GResourceTarget( name, state.subdir, - state.subproject, state.environment, target_cmd, [input_file], [output], + state.current_build_project, build_by_default=kwargs['build_by_default'], depfile=depfile, depend_files=depend_files, @@ -538,11 +538,11 @@ def compile_resources(self, state: 'ModuleState', args: T.Tuple[str, CustomTarge target_h = GResourceHeaderTarget( f'{target_name}_h', state.subdir, - state.subproject, state.environment, cmd, [input_file], [f'{target_name}.h'], + state.current_build_project, build_by_default=kwargs['build_by_default'], extra_depends=depends, install=install_header, @@ -1016,11 +1016,11 @@ def _make_gir_target( return GirTarget( girfile, state.subdir, - state.subproject, state.environment, scan_command, generated_files, [girfile], + state.current_build_project, build_by_default=kwargs['build_by_default'], extra_depends=depends, install=install, @@ -1048,11 +1048,11 @@ def _make_typelib_target(state: 'ModuleState', typelib_output: str, return TypelibTarget( typelib_output, state.subdir, - state.subproject, state.environment, typelib_cmd, generated_files, [typelib_output], + state.current_build_project, install=install, install_dir=[install_dir], install_tag=['typelib'], @@ -1291,11 +1291,11 @@ def compile_schemas(self, state: 'ModuleState', args: T.List['TYPE_var'], kwargs target_g = CustomTarget( targetname, state.subdir, - state.subproject, state.environment, cmd, [], ['gschemas.compiled'], + state.current_build_project, build_by_default=kwargs['build_by_default'], depend_files=state._interpreter.source_strings_to_files(kwargs['depend_files']), description='Compiling gschemas {}', @@ -1366,8 +1366,8 @@ def yelp(self, state: 'ModuleState', args: T.Tuple[str, T.List[str]], kwargs: 'Y pot_args: CommandList = [itstool, '-o', pot_file] pot_args.extend(pot_sources) pottarget = build.RunTarget(f'help-{project_id}-pot', pot_args, [], - os.path.join(state.subdir, 'C'), state.subproject, - state.environment) + os.path.join(state.subdir, 'C'), + state.environment, state.current_build_project) targets.append(pottarget) for l in langs: @@ -1399,8 +1399,8 @@ def yelp(self, state: 'ModuleState', args: T.Tuple[str, T.List[str]], kwargs: 'Y '--internal', 'exe', '--capture', po_path, '--', msgmerge, '-q', po_path, pot_file] potarget = build.RunTarget(f'help-{project_id}-{l}-update-po', - po_args, [pottarget], l_subdir, state.subproject, - state.environment) + po_args, [pottarget], l_subdir, + state.environment, state.current_build_project) targets.append(potarget) potargets.append(potarget) @@ -1409,11 +1409,11 @@ def yelp(self, state: 'ModuleState', args: T.Tuple[str, T.List[str]], kwargs: 'Y gmotarget = CustomTarget( f'help-{project_id}-{l}-gmo', l_subdir, - state.subproject, state.environment, [msgfmt, '@INPUT@', '-o', '@OUTPUT@'], [po_file], [gmo_file], + state.current_build_project, install_tag=['doc'], description='Generating yelp doc {}', ) @@ -1422,11 +1422,11 @@ def yelp(self, state: 'ModuleState', args: T.Tuple[str, T.List[str]], kwargs: 'Y mergetarget = CustomTarget( f'help-{project_id}-{l}', l_subdir, - state.subproject, state.environment, [itstool, '-m', os.path.join(l_subdir, gmo_file), '--lang', l, '-o', '@OUTDIR@', '@INPUT@'], sources_files, sources, + state.current_build_project, extra_depends=[gmotarget], install=True, install_dir=[l_install_dir], @@ -1436,7 +1436,8 @@ def yelp(self, state: 'ModuleState', args: T.Tuple[str, T.List[str]], kwargs: 'Y targets.append(mergetarget) allpotarget = build.AliasTarget(f'help-{project_id}-update-po', potargets, - state.subdir, state.subproject, state.environment) + state.subdir, state.environment, + state.current_build_project) targets.append(allpotarget) return ModuleReturnValue(None, targets) @@ -1571,16 +1572,17 @@ def abs_filenames(files: T.Iterable['FileOrString']) -> T.Iterator[str]: custom_target = CustomTarget( targetname, state.subdir, - state.subproject, state.environment, command + t_args, [], [f'{modulename}-decl.txt'], + state.current_build_project, build_always_stale=True, extra_depends=new_depends, description='Generating gtkdoc {}', ) - alias_target = build.AliasTarget(targetname, [custom_target], state.subdir, state.subproject, state.environment) + alias_target = build.AliasTarget(targetname, [custom_target], state.subdir, + state.environment, state.current_build_project) if kwargs['check']: check_cmd = state.find_program('gtkdoc-check') check_env = ['DOC_MODULE=' + modulename, @@ -1739,11 +1741,11 @@ def gdbus_codegen(self, state: 'ModuleState', args: T.Tuple[str, T.Optional[str cfile_custom_target = CustomTarget( output, state.subdir, - state.subproject, state.environment, c_cmd, xml_files, [output], + state.current_build_project, build_by_default=build_by_default, description='Generating gdbus source {}', ) @@ -1760,11 +1762,11 @@ def gdbus_codegen(self, state: 'ModuleState', args: T.Tuple[str, T.Optional[str hfile_custom_target = CustomTarget( output, state.subdir, - state.subproject, state.environment, hfile_cmd, xml_files, [output], + state.current_build_project, build_by_default=build_by_default, extra_depends=depends, install=install_header, @@ -1792,11 +1794,11 @@ def gdbus_codegen(self, state: 'ModuleState', args: T.Tuple[str, T.Optional[str docbook_custom_target = CustomTarget( output, state.subdir, - state.subproject, state.environment, docbook_cmd, xml_files, outputs, + state.current_build_project, build_by_default=build_by_default, extra_depends=depends, description='Generating gdbus docbook {}', @@ -1814,11 +1816,11 @@ def gdbus_codegen(self, state: 'ModuleState', args: T.Tuple[str, T.Optional[str rst_custom_target = CustomTarget( output, state.subdir, - state.subproject, state.environment, cmd + ['--output-directory', '@OUTDIR@', '--generate-rst', rst, '@INPUT@'], xml_files, outputs, + state.current_build_project, build_by_default=build_by_default, description='Generating gdbus reStructuredText {}', ) @@ -1835,11 +1837,11 @@ def gdbus_codegen(self, state: 'ModuleState', args: T.Tuple[str, T.Optional[str markdown_custom_target = CustomTarget( output, state.subdir, - state.subproject, state.environment, cmd + ['--output-directory', '@OUTDIR@', '--generate-md', markdown, '@INPUT@'], xml_files, outputs, + state.current_build_project, build_by_default=build_by_default, description='Generating gdbus markdown {}', ) @@ -2074,11 +2076,11 @@ def _make_mkenum_impl( return CustomTarget( output, state.subdir, - state.subproject, state.environment, real_cmd, sources, [output], + state.current_build_project, capture=True, install=install, install_dir=[_install_dir], @@ -2147,11 +2149,11 @@ def genmarshal(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'GenMarsh header = CustomTarget( output + '_h', state.subdir, - state.subproject, state.environment, h_cmd, sources, [header_file], + state.current_build_project, install=install_header, install_dir=[kwargs['install_dir']] if kwargs['install_dir'] else [], install_tag=['devel'], @@ -2169,11 +2171,11 @@ def genmarshal(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'GenMarsh body = CustomTarget( output + '_c', state.subdir, - state.subproject, state.environment, c_cmd, sources, [f'{output}.c'], + state.current_build_project, capture=capture, depend_files=kwargs['depend_files'], extra_depends=extra_deps, @@ -2297,11 +2299,11 @@ def generate_vapi(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'Gener vapi_target = VapiTarget( vapi_output, state.subdir, - state.subproject, state.environment, - command=cmd, - sources=inputs, - outputs=[vapi_output], + cmd, + inputs, + [vapi_output], + state.current_build_project, extra_depends=vapi_depends, install=kwargs['install'], install_dir=[install_dir], diff --git a/mesonbuild/modules/hotdoc.py b/mesonbuild/modules/hotdoc.py index 5f6a21d53ed6..2a42956f39c0 100644 --- a/mesonbuild/modules/hotdoc.py +++ b/mesonbuild/modules/hotdoc.py @@ -26,6 +26,7 @@ from typing_extensions import TypedDict from . import ModuleState + from ..build import BuildProject from ..environment import Environment from ..interpreter import Interpreter from ..interpreter.kwargs import TargetDepends @@ -326,7 +327,6 @@ def make_targets(self) -> T.Tuple[HotdocTarget, mesonlib.ExecutableSerialisation target = HotdocTarget(fullname, subdir=self.subdir, - subproject=self.state.subproject, environment=self.state.environment, hotdoc_conf=File.from_built_file( self.subdir, hotdoc_config_name), @@ -337,6 +337,7 @@ def make_targets(self) -> T.Tuple[HotdocTarget, mesonlib.ExecutableSerialisation extra_depends=self.extra_depends, outputs=[fullname], sources=[], + build_project=self.state.current_build_project, depfile=os.path.basename(depfile), build_by_default=self.build_by_default) @@ -382,10 +383,11 @@ def config_path_method(self, *args: T.Any, **kwargs: T.Any) -> str: class HotdocTarget(CustomTarget): - def __init__(self, name: str, subdir: str, subproject: mesonlib.SubProject, hotdoc_conf: File, + def __init__(self, name: str, subdir: str, hotdoc_conf: File, extra_extension_paths: T.Set[str], extra_assets: T.List[str], - subprojects: T.List['HotdocTarget'], environment: Environment, **kwargs: T.Any): - super().__init__(name, subdir, subproject, environment, **kwargs, absolute_paths=True) + subprojects: T.List['HotdocTarget'], environment: Environment, + build_project: BuildProject, **kwargs: T.Any): + super().__init__(name, subdir, environment, **kwargs, build_project=build_project, absolute_paths=True) self.hotdoc_conf = hotdoc_conf self.extra_extension_paths = extra_extension_paths self.extra_assets = extra_assets diff --git a/mesonbuild/modules/i18n.py b/mesonbuild/modules/i18n.py index 5b14e8fa05ff..5180b797f459 100644 --- a/mesonbuild/modules/i18n.py +++ b/mesonbuild/modules/i18n.py @@ -143,6 +143,7 @@ def __init__(self, xgettext: ExternalProgram, interpreter: Interpreter): self.interpreter = interpreter def extract(self, + state: ModuleState, name: str, sources: T.List[SourcesType], args: T.List[str], @@ -177,11 +178,11 @@ def extract(self, ct = build.CustomTarget( '', self.interpreter.subdir, - self.interpreter.subproject, self.interpreter.environment, command, inputs, [name], + state.current_build_project, depend_files = depend_files, extra_depends = depends, install = install, @@ -341,11 +342,11 @@ def merge_file(self, state: 'ModuleState', args: T.List['TYPE_var'], kwargs: 'Me ct = build.CustomTarget( '', state.subdir, - state.subproject, state.environment, command, inputs, [kwargs['output']], + state.current_build_project, build_by_default=build_by_default, install=kwargs['install'], install_dir=[kwargs['install_dir']] if kwargs['install_dir'] is not None else None, @@ -417,8 +418,8 @@ def gettext(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'Gettext') - potargs.append(extra_arg) if self.tools['xgettext'].found(): potargs.append('--xgettext=' + self.tools['xgettext'].get_path()) - pottarget = build.RunTarget(packagename + '-pot', potargs, [], state.subdir, state.subproject, - state.environment, default_env=False) + pottarget = build.RunTarget(packagename + '-pot', potargs, [], state.subdir, + state.environment, state.current_build_project, default_env=False) targets.append(pottarget) install = kwargs['install'] @@ -438,11 +439,11 @@ def gettext(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'Gettext') - gmotarget = build.CustomTarget( f'{packagename}-{l}.mo', path.join(state.subdir, l, 'LC_MESSAGES'), - state.subproject, state.environment, [*gmobasecmd, *kwargs['msgfmt_args']], [po_file], [f'{packagename}.mo'], + state.current_build_project, install=install, # We have multiple files all installed as packagename+'.mo' in different install subdirs. # What we really wanted to do, probably, is have a rename: kwarg, but that's not available @@ -455,8 +456,8 @@ def gettext(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'Gettext') - targets.append(gmotarget) gmotargets.append(gmotarget) - allgmotarget = build.AliasTarget(packagename + '-gmo', gmotargets, state.subdir, state.subproject, - state.environment) + allgmotarget = build.AliasTarget(packagename + '-gmo', gmotargets, state.subdir, + state.environment, state.current_build_project) targets.append(allgmotarget) updatepoargs = state.environment.get_build_command() + ['--internal', 'gettext', 'update_po', pkg_arg] @@ -472,8 +473,8 @@ def gettext(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'Gettext') - for tool in ['msginit', 'msgmerge']: if self.tools[tool].found(): updatepoargs.append(f'--{tool}=' + self.tools[tool].get_path()) - updatepotarget = build.RunTarget(packagename + '-update-po', updatepoargs, [], state.subdir, state.subproject, - state.environment, default_env=False) + updatepotarget = build.RunTarget(packagename + '-update-po', updatepoargs, [], state.subdir, + state.environment, state.current_build_project, default_env=False) targets.append(updatepotarget) return ModuleReturnValue([gmotargets, pottarget, updatepotarget], targets) @@ -533,11 +534,11 @@ def itstool_join(self, state: 'ModuleState', args: T.List['TYPE_var'], kwargs: ' ct = build.CustomTarget( '', state.subdir, - state.subproject, state.environment, command, inputs, [kwargs['output']], + state.current_build_project, build_by_default=build_by_default, extra_depends=mo_targets, install=kwargs['install'], @@ -572,7 +573,7 @@ def xgettext(self, state: ModuleState, args: T.Tuple[str, T.List[SourcesType]], raise InvalidArguments('i18n.xgettext: "install_dir" keyword argument must be set when "install" is true.') xgettext_program = XgettextProgram(T.cast('ExternalProgram', self.tools[toolname]), self.interpreter) - return xgettext_program.extract(*args, **kwargs) + return xgettext_program.extract(state, *args, **kwargs) def initialize(interp: 'Interpreter') -> I18nModule: diff --git a/mesonbuild/modules/icestorm.py b/mesonbuild/modules/icestorm.py index e9d02b44685c..8445edb0c90f 100644 --- a/mesonbuild/modules/icestorm.py +++ b/mesonbuild/modules/icestorm.py @@ -66,31 +66,31 @@ def project(self, state: ModuleState, blif_target = build.CustomTarget( f'{proj_name}_blif', state.subdir, - state.subproject, state.environment, [self.tools['yosys'], '-q', '-p', 'synth_ice40 -blif @OUTPUT@', '@INPUT@'], all_sources, [f'{proj_name}.blif'], + state.current_build_project, ) asc_target = build.CustomTarget( f'{proj_name}_asc', state.subdir, - state.subproject, state.environment, [self.tools['arachne'], '-q', '-d', '1k', '-p', '@INPUT@', '-o', '@OUTPUT@'], [constraint_file, blif_target], [f'{proj_name}.asc'], + state.current_build_project, ) bin_target = build.CustomTarget( f'{proj_name}_bin', state.subdir, - state.subproject, state.environment, [self.tools['icepack'], '@INPUT@', '@OUTPUT@'], [asc_target], [f'{proj_name}.bin'], + state.current_build_project, build_by_default=True, ) @@ -99,8 +99,8 @@ def project(self, state: ModuleState, [self.tools['iceprog'], bin_target], [], state.subdir, - state.subproject, state.environment, + state.current_build_project, ) time_target = build.RunTarget( @@ -108,8 +108,8 @@ def project(self, state: ModuleState, [self.tools['icetime'], bin_target], [], state.subdir, - state.subproject, state.environment, + state.current_build_project, ) return ModuleReturnValue( diff --git a/mesonbuild/modules/java.py b/mesonbuild/modules/java.py index 2794129ef1ee..d01c87242713 100644 --- a/mesonbuild/modules/java.py +++ b/mesonbuild/modules/java.py @@ -93,10 +93,11 @@ def __native_headers(self, state: ModuleState, args: T.Tuple[T.List[mesonlib.Fil if isinstance(s, str) else s for s in args[0]] target = CustomTarget(f'{prefix}-native-headers', state.subdir, - state.subproject, state.environment, command, - sources=sources, outputs=headers, backend=state.backend) + sources, headers, + state.current_build_project, + backend=state.backend) # It is only known that 1.8.0 won't pre-create the directory. 11 and 16 # do not exhibit this behavior. diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index d4e422cd1170..1ec86b60a816 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -666,10 +666,11 @@ def test_common(self, funcname: str, state: ModuleState, args: T.Tuple[str, Buil sources.extend(base_target.generated) new_target = Executable( - name, base_target.subdir, state.subproject, base_target.for_machine, + name, base_target.subdir, base_target.for_machine, sources, base_target.structured_sources, base_target.objects, base_target.environment, base_target.compilers, - new_target_kwargs) + state.current_build_project, new_target_kwargs + ) return new_target, tkwargs @typed_pos_args('rust.test', str, BuildTarget) @@ -960,11 +961,11 @@ def bindgen(self, state: ModuleState, args: T.List, kwargs: FuncBindgen) -> Modu target = CustomTarget( f'rustmod-bindgen-{name}'.replace('/', '_'), state.subdir, - state.subproject, state.environment, cmd, [header], outputs, + state.current_build_project, depfile='@PLAINNAME@.d', extra_depends=depends, depend_files=depend_files, diff --git a/mesonbuild/modules/wayland.py b/mesonbuild/modules/wayland.py index e77a2b13b309..f4822fadfa61 100644 --- a/mesonbuild/modules/wayland.py +++ b/mesonbuild/modules/wayland.py @@ -78,11 +78,11 @@ def scan_xml(self, state: ModuleState, args: T.Tuple[T.List[FileOrString]], kwar code = CustomTarget( f'{name}-protocol', state.subdir, - state.subproject, state.environment, [self.scanner_bin, f'{scope}-code', '@INPUT@', '@OUTPUT@'], [xml_file], [f'{name}-protocol.c'], + state.current_build_project, backend=state.backend, ) targets.append(code) @@ -95,11 +95,11 @@ def scan_xml(self, state: ModuleState, args: T.Tuple[T.List[FileOrString]], kwar header = CustomTarget( f'{name}-{side}-protocol', state.subdir, - state.subproject, state.environment, command, [xml_file], [f'{name}-{side}-protocol.h'], + state.current_build_project, backend=state.backend, ) targets.append(header) diff --git a/mesonbuild/modules/windows.py b/mesonbuild/modules/windows.py index b7aed3b0e816..f990635dac46 100644 --- a/mesonbuild/modules/windows.py +++ b/mesonbuild/modules/windows.py @@ -207,11 +207,11 @@ def get_names() -> T.Iterable[T.Tuple[str, str, T.Union[mesonlib.File, build.Cus res_targets.append(build.CustomTarget( name_formatted, state.subdir, - state.subproject, state.environment, command, [src], [output], + state.current_build_project, depfile=depfile, depfile_type=depfile_type, depend_files=wrc_depend_files, diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index ebab9e3ca88c..2c7e9899ae28 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -21,6 +21,7 @@ from pathlib import (PurePath, Path) import typing as T +from mesonbuild.build import BuildProject import mesonbuild.mlog import mesonbuild.depfile import mesonbuild.dependencies.base @@ -34,7 +35,7 @@ DirectoryLock, DirectoryLockAction, MachineChoice, is_windows, is_osx, is_cygwin, is_dragonflybsd, is_sunos, windows_proof_rmtree, python_command, version_compare, split_args, quote_arg, relpath, is_linux, git, search_version, do_conf_file, do_conf_str, default_prefix, - MesonException, EnvironmentException, + SubProject, MesonException, EnvironmentException, windows_proof_rm, first ) from mesonbuild.options import OptionKey @@ -5003,11 +5004,11 @@ def test_introspect_install_plan(self): env = get_fake_env(testdir, self.builddir, self.prefix) def output_name(name, type_): - target = type_(name=name, subdir=None, subproject=None, + target = type_(name=name, subdir='', for_machine=MachineChoice.HOST, sources=[], structured_sources=None, objects=[], environment=env, compilers=env.coredata.compilers[MachineChoice.HOST], - kwargs={}) + build_project=BuildProject('', '', SubProject('')), kwargs={}) target.process_compilers_late() return target.filename From c561bd8861b9df1cf41e126057d73da5d09e7b51 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 27 May 2026 19:57:44 +0200 Subject: [PATCH 514/681] build, interpreter: make project-related fields per machine This is a first step towards making it possible to allow subprojects to be built for both the build and host machine. Even though the concept of a build-machine subproject doesn't exist yet, prepare for it by making the list of (sub)projects per machine, and storing the machine in the interpreter's subproject stack. Extracted from a patch by Dylan Baker Signed-off-by: Paolo Bonzini --- mesonbuild/ast/introspection.py | 2 +- mesonbuild/build.py | 15 +++-- mesonbuild/interpreter/dependencyfallbacks.py | 2 +- mesonbuild/interpreter/interpreter.py | 65 ++++++++++++------- mesonbuild/interpreter/interpreterobjects.py | 4 +- mesonbuild/mdist.py | 3 +- mesonbuild/mintro.py | 8 ++- mesonbuild/msetup.py | 10 ++- unittests/allplatformstests.py | 2 +- 9 files changed, 71 insertions(+), 40 deletions(-) diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py index f62ae2c5d47f..1cc80ad843a2 100644 --- a/mesonbuild/ast/introspection.py +++ b/mesonbuild/ast/introspection.py @@ -76,7 +76,7 @@ def __init__(self, self.cross_file = cross_file self.backend = backend - self.build_project = BuildProject(subproject, '', subproject) + self.build_project = BuildProject(subproject, '', subproject, MachineChoice.HOST) self.project_data: T.Dict[str, T.Any] = {} self.targets: T.List[IntrospectionBuildTarget] = [] self.dependencies: T.List[IntrospectionDependency] = [] diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 4eafe038a9ae..f7d40c6d50bc 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -305,6 +305,7 @@ class BuildProject: name: str version: str subproject: SubProject + for_machine: MachineChoice project_args: PerMachine[T.Dict[Language, T.List[str]]] = field(default_factory=lambda: PerMachine({}, {})) project_link_args: PerMachine[T.Dict[Language, T.List[str]]] = field(default_factory=lambda: PerMachine({}, {})) @@ -321,7 +322,7 @@ def __init__(self, environment: Environment): self.project_name = 'name of master project' self.project_version: T.Optional[str] = None self.environment = environment - self.projects: T.Dict[SubProject, BuildProject] = {} + self.projects: PerMachine[T.Dict[SubProject, BuildProject]] = PerMachine({}, {}) self.targets: dict[str, Target] = {} self.targetnames: T.Set[T.Tuple[str, str]] = set() # Set of executable names and their subdir self.global_args: PerMachine[T.Dict[Language, T.List[str]]] = PerMachine({}, {}) @@ -418,7 +419,7 @@ def ensure_static_linker(self, compiler: Compiler) -> None: self.static_linker.build = self.static_linker.host def get_project(self) -> str: - return self.projects[ROOT_SUBPROJECT].name + return self.projects.host[ROOT_SUBPROJECT].name def get_subproject_dir(self) -> str: return self.subproject_dir @@ -899,7 +900,7 @@ 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.validate_install() + self.validate_cross() self.check_module_linking() @lazy_property @@ -980,7 +981,13 @@ def __repr__(self) -> str: def __str__(self) -> str: return f"{self.name}" - def validate_install(self) -> None: + def validate_cross(self) -> None: + if self.build_project.for_machine is MachineChoice.BUILD: + if self.for_machine is MachineChoice.HOST: + raise MesonBugException('Tried to build a target for the host machine in a build-only subproject.') + if self.install: + raise MesonBugException('Tried to build an installable target in a build-only subproject.') + if self.for_machine is MachineChoice.BUILD and self.install: if self.environment.is_cross_build(): raise InvalidArguments('Tried to install a target for the build machine in a cross build.') diff --git a/mesonbuild/interpreter/dependencyfallbacks.py b/mesonbuild/interpreter/dependencyfallbacks.py index c6027b0f047d..7d08c6435e06 100644 --- a/mesonbuild/interpreter/dependencyfallbacks.py +++ b/mesonbuild/interpreter/dependencyfallbacks.py @@ -137,7 +137,7 @@ def _do_subproject(self, kwargs: DependencyObjectKWs, name: str, func_kwargs: Do return self._get_subproject_dep(subp_name, varname, kwargs) def _get_subproject(self, subp_name: str) -> T.Optional[SubprojectHolder]: - sub = self.interpreter.subprojects.get(subp_name) + sub = self.interpreter.subprojects[self.for_machine].get(subp_name) if sub and sub.found(): return sub return None diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 378cb368d615..4de62552c5e0 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -19,8 +19,8 @@ from ..wrap import wrap, WrapMode from .. import mesonlib from ..mesonlib import (EnvironmentVariables, ExecutableSerialisation, MesonBugException, MesonException, HoldableObject, - FileMode, MachineChoice, is_parent_path, listify, - has_path_sep, path_has_root, path_is_in_root, PerMachine) + FileMode, MachineChoice, PerMachine, PerMachineDefaultable, is_parent_path, listify, + has_path_sep, path_has_root, path_is_in_root) from ..options import OptionKey from ..programs import ExternalProgram, NonExistingExternalProgram, Program from ..dependencies import Dependency @@ -302,8 +302,9 @@ def __init__( self.validated_cache: T.Set[str] = set() self.project_args_frozen = False self.global_args_frozen = False # implies self.project_args_frozen - self.subprojects: T.Dict[str, SubprojectHolder] = {} - self.subproject_stack: T.List[str] = [] + self.subprojects: PerMachine[T.Dict[str, SubprojectHolder]] = PerMachineDefaultable.default( + self.environment.is_cross_build(), {}, {}) + self.subproject_stack: T.List[T.Tuple[str, MachineChoice]] = [] self.configure_file_outputs: T.Dict[str, int] = {} # Passed from the outside, only used in subprojects. if invoker_method_default_options: @@ -891,12 +892,25 @@ def disabled_subproject(self, subp_name: SubProject, disabled_feature: T.Optiona exception: T.Optional[Exception] = None) -> SubprojectHolder: sub = SubprojectHolder(NullSubprojectInterpreter(), os.path.join(self.subproject_dir, subp_name), disabled_feature=disabled_feature, exception=exception) - self.subprojects[subp_name] = sub + self.subprojects.host[subp_name] = sub return sub def create_build_subdir(self, subdir: str) -> None: os.makedirs(os.path.join(self.build.environment.get_build_dir(), subdir), exist_ok=True) + @staticmethod + def format_subproject_stack(stack: T.List[T.Tuple[str, MachineChoice]], join: str = ' => ') -> str: + prefix = '' + result = '' + for_build = False + for subp_name, machine in stack: + result += prefix + subp_name + if machine is MachineChoice.BUILD and not for_build: + result += ' (build)' + for_build = True + prefix = join + return result + def do_subproject(self, subp_name: SubProject, kwargs: kwtypes.DoSubproject, force_method: T.Optional[wrap.Method] = None, forced_options: T.Optional[OptionDict] = None) -> SubprojectHolder: disabled, required, feature = extract_required_kwarg(kwargs, self.subproject) @@ -927,16 +941,16 @@ def do_subproject(self, subp_name: SubProject, kwargs: kwtypes.DoSubproject, for if has_path_sep(subp_name): mlog.warning('Subproject name has a path separator. This may cause unexpected behaviour.', location=self.current_node) - if subp_name in self.subproject_stack: - fullstack = self.subproject_stack + [subp_name] - incpath = ' => '.join(fullstack) + fullstack = self.subproject_stack + [(subp_name, MachineChoice.HOST)] + if (subp_name, MachineChoice.HOST) in self.subproject_stack: + incpath = self.format_subproject_stack(fullstack) raise InvalidCode(f'Recursive include of subprojects: {incpath}.') - if subp_name in self.subprojects: - subproject = self.subprojects[subp_name] + if subp_name in self.subprojects.host: + subproject = self.subprojects.host[subp_name] if required and not subproject.found(): raise InterpreterException(f'Subproject "{subproject.subdir}" required but not found.') if kwargs['version']: - pv = self.build.projects[subp_name].version + pv = self.build.projects.host[subp_name].version wanted = kwargs['version'] if pv == 'undefined' or not mesonlib.version_compare_many(pv, wanted)[0]: raise InterpreterException(f'Subproject {subp_name} version is {pv} but {wanted} required.') @@ -961,7 +975,7 @@ def do_subproject(self, subp_name: SubProject, kwargs: kwtypes.DoSubproject, for self.create_build_subdir(subdir) self.global_args_frozen = True - stack = ':'.join(self.subproject_stack + [subp_name]) + stack = self.format_subproject_stack(fullstack, ':') m = ['\nExecuting subproject', mlog.bold(stack)] if method != 'meson': m += ['method', mlog.bold(method)] @@ -1026,7 +1040,7 @@ def _do_subproject_meson(self, subp_name: SubProject, subdir: str, subi.bound_holder_map = self.bound_holder_map subi.summary = self.summary - subi.subproject_stack = self.subproject_stack + [subp_name] + subi.subproject_stack = self.subproject_stack + [(subp_name, MachineChoice.HOST)] current_active = self.active_projectname with mlog.nested_warnings(): subi.run() @@ -1041,16 +1055,16 @@ def _do_subproject_meson(self, subp_name: SubProject, subdir: str, if pv == 'undefined' or not mesonlib.version_compare_many(pv, wanted)[0]: raise InterpreterException(f'Subproject {subp_name} version is {pv} but {wanted} required.') self.active_projectname = current_active - self.subprojects.update(subi.subprojects) - self.subprojects[subp_name] = SubprojectHolder(subi, subdir, warnings=subi_warnings, - callstack=self.subproject_stack) + self.subprojects.host.update(subi.subprojects.host) + self.subprojects.host[subp_name] = SubprojectHolder(subi, subdir, warnings=subi_warnings, + callstack=self.subproject_stack) # Duplicates are possible when subproject uses files from project root if build_def_files: self.build_def_files.update(build_def_files) # We always need the subi.build_def_files, to propagate sub-sub-projects self.build_def_files.update(subi.get_build_def_files()) self.build.merge(subi.build) - return self.subprojects[subp_name] + return self.subprojects.host[subp_name] def _do_subproject_cmake(self, subp_name: SubProject, subdir: str, default_options: OptionDict, @@ -1307,7 +1321,7 @@ def func_project(self, node: mparser.FunctionNode, args: T.Tuple[str, T.List[str proj_license_files.append((ifname, i)) self.build.dep_manifest[proj_name] = build.DepManifest(self.project_version, proj_license, proj_license_files, self.subproject) - if self.subproject in self.build.projects: + if self.subproject in self.build.projects.host: raise InvalidCode('Second call to project().') # spdirname is the subproject_dir for this project, relative to self.subdir. @@ -1339,8 +1353,8 @@ def func_project(self, node: mparser.FunctionNode, args: T.Tuple[str, T.List[str if self.cargo is None: self.load_root_cargo_lock_file() - self.build.projects[self.subproject] = build.BuildProject(proj_name, self.project_version, - self.subproject) + self.build.projects.host[self.subproject] = build.BuildProject(proj_name, self.project_version, + self.subproject, MachineChoice.HOST) mlog.log('Project name:', mlog.bold(proj_name)) mlog.log('Project version:', mlog.bold(self.project_version)) @@ -1434,7 +1448,7 @@ def summary_impl(self, section: str, values: dict[str, mlog.TV_Loggable | mlog.T def _print_summary(self) -> None: # Add automatic 'Subprojects' section in main project. all_subprojects: dict[str, mlog.TV_Loggable | mlog.TV_LoggableList] = {} - for name, subp in sorted(self.subprojects.items()): + for name, subp in sorted(self.subprojects.host.items()): value: mlog.TV_LoggableList = [subp.found()] if subp.disabled_feature: value += [f'Feature {subp.disabled_feature!r} disabled'] @@ -1443,7 +1457,7 @@ def _print_summary(self) -> None: elif subp.warnings > 0: value += [f'{subp.warnings} warnings'] if subp.callstack: - stack = ' => '.join(subp.callstack) + stack = self.format_subproject_stack(subp.callstack) value += [f'(from {stack})'] all_subprojects[name] = value if all_subprojects: @@ -1464,7 +1478,7 @@ def _print_summary(self) -> None: mlog.log('') # newline main_summary = self.summary.pop('', None) for subp_name, summary in sorted(self.summary.items()): - if self.subprojects[subp_name].found(): + if self.subprojects.host[subp_name].found(): summary.dump() if main_summary: main_summary.dump() @@ -1795,7 +1809,7 @@ def check_program_version(self, progobj: Program, version = version_func(progobj) elif isinstance(progobj, build.Executable): if progobj.subproject: - interp = self.subprojects[progobj.subproject].held_object + interp = self.subprojects.host[progobj.subproject].held_object else: interp = self assert isinstance(interp, Interpreter), 'for mypy' @@ -3035,7 +3049,8 @@ def func_add_project_link_arguments(self, node: mparser.FunctionNode, args: T.Tu args[0], kwargs) def current_build_project(self) -> build.BuildProject: - return self.build.projects[self.subproject] + for_machine = self.build.machine_map.host + return self.build.projects[for_machine][self.subproject] @FeatureNew('add_project_dependencies', '0.63.0') @typed_pos_args('add_project_dependencies', varargs=dependencies.Dependency) diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index 6e9a25dbf8b0..35bb8085ba1c 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -26,7 +26,7 @@ from ..interpreter.type_checking import NoneType, ENV_KW, ENV_SEPARATOR_KW, PKGCONFIG_DEFINE_KW from ..dependencies import Dependency, ExternalLibrary, InternalDependency from ..programs import Program -from ..mesonlib import File, HoldableObject, listify, MesonException +from ..mesonlib import File, HoldableObject, listify, MachineChoice, MesonException import typing as T @@ -898,7 +898,7 @@ def __init__(self, subinterpreter: T.Union['Interpreter', NullSubprojectInterpre warnings: int = 0, disabled_feature: T.Optional[str] = None, exception: T.Optional[Exception] = None, - callstack: T.Optional[T.List[str]] = None) -> None: + callstack: T.Optional[T.List[T.Tuple[str, MachineChoice]]] = None) -> None: super().__init__() self.held_object = subinterpreter self.warnings = warnings diff --git a/mesonbuild/mdist.py b/mesonbuild/mdist.py index df1d9144dbac..919df0653c66 100644 --- a/mesonbuild/mdist.py +++ b/mesonbuild/mdist.py @@ -7,6 +7,7 @@ import abc import argparse +import itertools import os import sys import shlex @@ -394,7 +395,7 @@ def run(options: argparse.Namespace) -> int: extra_meson_args = [] if options.include_subprojects: subproject_dir = os.path.join(src_root, b.subproject_dir) - for sub in b.projects: + for sub in set(itertools.chain(b.projects.host, b.projects.build)): if sub: directory = wrap.get_directory(subproject_dir, sub) subprojects[sub] = os.path.join(b.subproject_dir, directory) diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 0365255ef043..c7b7b03489d7 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -12,6 +12,7 @@ from contextlib import redirect_stdout import dataclasses +import itertools import json import os from pathlib import Path, PurePath @@ -416,9 +417,12 @@ def list_projinfo(coredata: cdata.CoreData, builddata: build.Build, backend: bac 'subproject_dir': builddata.subproject_dir, } subprojects = [] - for k, build_proj in builddata.projects.items(): - if not k: + seen = set(['']) + for k, build_proj in itertools.chain(builddata.projects.host.items(), + builddata.projects.build.items()): + if k in seen: continue + seen.add(k) c: T.Dict[str, str] = { 'name': k, 'version': build_proj.version, diff --git a/mesonbuild/msetup.py b/mesonbuild/msetup.py index df06245a394c..68b3bf999863 100644 --- a/mesonbuild/msetup.py +++ b/mesonbuild/msetup.py @@ -11,7 +11,7 @@ from . import build, cmdline, coredata, environment, interpreter, mesonlib, mintro, mlog from .dependencies import Dependency -from .mesonlib import MesonException +from .mesonlib import MesonException, MachineChoice from .interpreterbase import ObjectHolder from .options import OptionKey @@ -19,6 +19,7 @@ from typing_extensions import Protocol from .cmdline import SharedCMDOptions from .interpreter import SubprojectHolder + from .mesonlib import PerMachine class CMDOptions(SharedCMDOptions, Protocol): @@ -195,9 +196,12 @@ def generate(self, capture: bool = False, vslite_ctx: T.Optional[dict] = None) - 'Some other Meson process is already using this build directory. Exiting.'): return self._generate(env, capture, vslite_ctx) - def check_unused_options(self, coredata: 'coredata.CoreData', cmd_line_options: T.Dict[OptionKey, str], all_subprojects: T.Mapping[str, SubprojectHolder]) -> None: + def check_unused_options(self, coredata: 'coredata.CoreData', cmd_line_options: T.Dict[OptionKey, str], + all_subprojects: PerMachine[T.Dict[str, SubprojectHolder]]) -> None: errlist: T.List[str] = [] - known_subprojects = [name for name, obj in all_subprojects.items() if obj.found()] + known_subprojects: T.Set[str] = set() + for m in MachineChoice: + known_subprojects.update((name for name, obj in all_subprojects[m].items() if obj.found())) for opt in cmd_line_options: # Accept options that exist or could appear in subsequent reconfigurations, # including options for subprojects that were not used diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 2c7e9899ae28..830cd310470f 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -5008,7 +5008,7 @@ def output_name(name, type_): for_machine=MachineChoice.HOST, sources=[], structured_sources=None, objects=[], environment=env, compilers=env.coredata.compilers[MachineChoice.HOST], - build_project=BuildProject('', '', SubProject('')), kwargs={}) + build_project=BuildProject('', '', SubProject(''), MachineChoice.HOST), kwargs={}) target.process_compilers_late() return target.filename From b74736b160e9e713186231dab5f2ea7b7a2f82a3 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 16 Dec 2025 10:17:50 +0100 Subject: [PATCH 515/681] interpreter: block install_* commands for build subprojects The object however has to be returned anyway. Signed-off-by: Paolo Bonzini --- mesonbuild/interpreter/interpreter.py | 24 +++++++++++++++++------- mesonbuild/interpreter/mesonmain.py | 4 ++++ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 4de62552c5e0..89ac5b1197fe 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -348,6 +348,11 @@ def _redetect_machines(self) -> None: self.builtin['target_machine'] = \ OBJ.MachineHolder(self.build.environment.machines[self.build.machine_map.target], self) + def is_internal_machine(self, for_machine: MachineChoice) -> bool: + # Return whether applying the caller's effect to the build and + # host machine would duplicate the effect + return self.build.machine_map[for_machine] is MachineChoice.BUILD + def apply_machine_map_to_kwargs(self, kwargs: kwtypes.BaseBuildTarget | kwtypes.MachineMapArgs) -> None: orig_for_machine: MachineChoice | None = kwargs.get('native', None) if orig_for_machine is not None: @@ -2425,7 +2430,8 @@ def func_install_headers(self, node: mparser.BaseNode, follow_symlinks=kwargs['follow_symlinks'], install_tag=kwargs['install_tag']) ret_headers.append(h) - self.build.headers.append(h) + if not self.is_internal_machine(MachineChoice.HOST): + self.build.headers.append(h) return ret_headers @@ -2454,7 +2460,8 @@ def func_install_man(self, node: mparser.BaseNode, m = build.Man(sources, kwargs['install_dir'], install_mode, self.subproject, kwargs['locale'], kwargs['install_tag']) - self.build.man.append(m) + if not self.is_internal_machine(MachineChoice.HOST): + self.build.man.append(m) return m @@ -2467,8 +2474,8 @@ def func_install_man(self, node: mparser.BaseNode, def func_install_emptydir(self, node: mparser.BaseNode, args: T.Tuple[str], kwargs: kwtypes.FuncInstallEmptyDir) -> build.EmptyDir: d = build.EmptyDir(args[0], kwargs['install_mode'], self.subproject, kwargs['install_tag']) - self.build.emptydir.append(d) - + if not self.is_internal_machine(MachineChoice.HOST): + self.build.emptydir.append(d) return d @FeatureNew('install_symlink', '0.61.0') @@ -2486,7 +2493,8 @@ def func_install_symlink(self, node: mparser.BaseNode, target = kwargs['pointing_to'] l = build.SymlinkData(target, name, kwargs['install_dir'], self.subproject, kwargs['install_tag']) - self.build.symlinks.append(l) + if not self.is_internal_machine(MachineChoice.HOST): + self.build.symlinks.append(l) return l @FeatureNew('structured_sources', '0.62.0') @@ -2637,7 +2645,8 @@ def install_data_impl(self, sources: T.List[mesonlib.File], install_dir: str, follow_symlinks) ret_data.append(d) - self.build.data.extend(ret_data) + if not self.is_internal_machine(MachineChoice.HOST): + self.build.data.extend(ret_data) return ret_data @typed_pos_args('install_subdir', str) @@ -2681,7 +2690,8 @@ def func_install_subdir(self, node: mparser.BaseNode, args: T.Tuple[str], self.subproject, install_tag=kwargs['install_tag'], follow_symlinks=kwargs['follow_symlinks']) - self.build.install_dirs.append(idir) + if not self.is_internal_machine(MachineChoice.HOST): + self.build.install_dirs.append(idir) return idir def validate_build_subdir(self, build_subdir: str, target: str) -> None: diff --git a/mesonbuild/interpreter/mesonmain.py b/mesonbuild/interpreter/mesonmain.py index e8808c666eea..11ac225b68e4 100644 --- a/mesonbuild/interpreter/mesonmain.py +++ b/mesonbuild/interpreter/mesonmain.py @@ -128,6 +128,8 @@ def add_install_script_method( self, args: T.Tuple[T.Union[str, mesonlib.File, build.Executable, Program], T.List[build.CommandTypes]], kwargs: 'AddInstallScriptKW') -> None: + if self.interpreter.current_build_project().for_machine is MachineChoice.BUILD: + return script_args = self._process_script_args('add_install_script', args[1]) script = self._find_source_script('add_install_script', args[0], script_args, allow_built_program=True) script.skip_if_destdir = kwargs['skip_if_destdir'] @@ -147,6 +149,8 @@ def add_postconf_script_method( args: T.Tuple[T.Union[str, mesonlib.File, Program], T.List[T.Union[str, mesonlib.File, Program]]], kwargs: 'TYPE_kwargs') -> None: + if self.interpreter.current_build_project().for_machine is MachineChoice.BUILD: + return script_args = self._process_script_args('add_postconf_script', args[1]) script = self._find_source_script('add_postconf_script', args[0], script_args) self.build.postconf_scripts.append(script) From 3cba9ee127628e6ec3d37a59afdf91cedc567b69 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 9 Jun 2026 13:36:09 +0200 Subject: [PATCH 516/681] interpreter: add native argument to override_find_program() Co-authored-by: Dylan Baker Signed-off-by: Paolo Bonzini --- docs/yaml/builtins/meson.yaml | 9 +++++++ mesonbuild/build.py | 7 +++-- mesonbuild/interpreter/interpreter.py | 37 +++++++++++++++------------ mesonbuild/interpreter/mesonmain.py | 8 +++--- mesonbuild/modules/__init__.py | 2 +- 5 files changed, 40 insertions(+), 23 deletions(-) diff --git a/docs/yaml/builtins/meson.yaml b/docs/yaml/builtins/meson.yaml index 3e486dac0a30..9d6c5ef2eb5a 100644 --- a/docs/yaml/builtins/meson.yaml +++ b/docs/yaml/builtins/meson.yaml @@ -392,6 +392,15 @@ methods: type: exe | file | program description: The program to set as the override for `progname`. + native: + type: bool + since: 1.12.0 + default: false + description : | + If set to `true`, the override is for the build machine, and will be returned + by `find_program(..., native : true)`, otherwise for the host machine. This + is an important distinction when cross compiling. + - name: override_dependency returns: void since: 0.54.0 diff --git a/mesonbuild/build.py b/mesonbuild/build.py index f7d40c6d50bc..ce73bae8e9e9 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -344,8 +344,11 @@ def __init__(self, environment: Environment): self.dep_manifest: T.Dict[str, DepManifest] = {} self.test_setups: T.Dict[str, TestSetup] = {} self.test_setup_default_name: str | None = None - self.find_overrides: T.Dict[str, programs.Program] = {} - self.searched_programs: T.Set[str] = set() # The list of all programs that have been searched for. + self.find_overrides: PerMachine[T.Dict[str, programs.Program]] = PerMachineDefaultable.default( + environment.is_cross_build(), {}, {}) + # The list of all programs that have been searched for. + self.searched_programs: PerMachine[T.Set[str]] = PerMachineDefaultable.default( + environment.is_cross_build(), set(), set()) # If we are doing a cross build we need two caches, if we're doing a # build == host compilation the both caches should point to the same place. diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 89ac5b1197fe..40b546c4ef59 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -797,9 +797,10 @@ def func_run_command(self, node: mparser.BaseNode, def _compiled_exe_error(self, cmd: T.Union[Program, build.Executable]) -> T.NoReturn: descr = cmd.name if isinstance(cmd, build.Executable) else cmd.description() - for name, exe in self.build.find_overrides.items(): - if cmd == exe: - raise InterpreterException(f'Program {name!r} was overridden with the compiled executable {descr!r} and therefore cannot be used during configuration') + for for_machine in MachineChoice: + for name, exe in self.build.find_overrides[for_machine].items(): + if cmd == exe: + raise InterpreterException(f'Program {name!r} was overridden with the compiled executable {descr!r} and therefore cannot be used during configuration') raise InterpreterException(f'Program {descr!r} is a compiled executable and therefore cannot be used during configuration') def run_command_impl(self, @@ -1696,31 +1697,32 @@ def program_from_system(self, args: T.List[mesonlib.FileOrString], search_dirs: return None def program_from_overrides(self, command_names: T.List[mesonlib.FileOrString], + for_machine: MachineChoice, extra_info: T.List['mlog.TV_Loggable'] ) -> T.Optional[Program]: for name in command_names: if not isinstance(name, str): continue - if name in self.build.find_overrides: - exe = self.build.find_overrides[name] + if name in self.build.find_overrides[for_machine]: + exe = self.build.find_overrides[for_machine][name] extra_info.append(mlog.blue('(overridden)')) return exe return None - def store_name_lookups(self, command_names: T.List[mesonlib.FileOrString]) -> None: + def store_name_lookups(self, command_names: T.List[mesonlib.FileOrString], for_machine: MachineChoice) -> None: for name in command_names: if isinstance(name, str): - self.build.searched_programs.add(name) + self.build.searched_programs[for_machine].add(name) - def add_find_program_override(self, name: str, exe: Program) -> None: - if name in self.build.searched_programs: + def add_find_program_override(self, name: str, exe: Program, for_machine: MachineChoice) -> None: + if name in self.build.searched_programs[for_machine]: raise InterpreterException(f'Tried to override finding of executable "{name}" which has already been found.') - if name in self.build.find_overrides: + if name in self.build.find_overrides[for_machine]: raise InterpreterException(f'Tried to override executable "{name}" which has already been overridden.') - self.build.find_overrides[name] = exe + self.build.find_overrides[for_machine][name] = exe if name == 'pkg-config' and isinstance(exe, ExternalProgram): from ..dependencies.pkgconfig import PkgConfigInterface - PkgConfigInterface.set_program_override(exe, MachineChoice.HOST) + PkgConfigInterface.set_program_override(exe, for_machine) def notfound_program(self, args: T.List[mesonlib.FileOrString]) -> ExternalProgram: return NonExistingExternalProgram(' '.join( @@ -1754,7 +1756,7 @@ def find_program_impl(self, args: T.List[mesonlib.FileOrString], return progobj # Only store successful lookups - self.store_name_lookups(args) + self.store_name_lookups(args, for_machine) if not silent: mlog.log('Program', mlog.bold(progobj.name), 'found:', mlog.green('YES'), *extra_info) return progobj @@ -1768,7 +1770,7 @@ def program_lookup(self, args: T.List[mesonlib.FileOrString], for_machine: Machi version_func: T.Optional[ProgramVersionFunc], extra_info: T.List[mlog.TV_Loggable] ) -> T.Optional[Program]: - progobj = self.program_from_overrides(args, extra_info) + progobj = self.program_from_overrides(args, for_machine, extra_info) if progobj: return progobj @@ -1783,7 +1785,7 @@ def program_lookup(self, args: T.List[mesonlib.FileOrString], for_machine: Machi if wrap_mode != WrapMode.nofallback and self.environment.wrap_resolver: fallback = self.environment.wrap_resolver.find_program_provider(args) if fallback and wrap_mode == WrapMode.forcefallback: - return self.find_program_fallback(fallback, args, default_options, required, extra_info) + return self.find_program_fallback(fallback, args, for_machine, default_options, required, extra_info) progobj = self.program_from_file_for(for_machine, args) if progobj is None: @@ -1801,7 +1803,7 @@ def program_lookup(self, args: T.List[mesonlib.FileOrString], for_machine: Machi progobj = self.notfound_program(args) mlog.log('Program', mlog.bold(progobj.get_name()), 'found:', mlog.red('NO'), *extra_info) extra_info.clear() - progobj = self.find_program_fallback(fallback, args, default_options, required, extra_info) + progobj = self.find_program_fallback(fallback, args, for_machine, default_options, required, extra_info) return progobj @@ -1830,6 +1832,7 @@ def check_program_version(self, progobj: Program, return True def find_program_fallback(self, fallback: SubProject, args: T.List[mesonlib.FileOrString], + for_machine: MachineChoice, default_options: OptionDict, required: bool, extra_info: T.List[mlog.TV_Loggable] ) -> T.Optional[Program]: @@ -1843,7 +1846,7 @@ def find_program_fallback(self, fallback: SubProject, args: T.List[mesonlib.File 'options': None, } self.do_subproject(fallback, sp_kwargs) - return self.program_from_overrides(args, extra_info) + return self.program_from_overrides(args, for_machine, extra_info) @typed_pos_args('find_program', varargs=(str, mesonlib.File), min_varargs=1) @typed_kwargs( diff --git a/mesonbuild/interpreter/mesonmain.py b/mesonbuild/interpreter/mesonmain.py index 11ac225b68e4..95011319b45b 100644 --- a/mesonbuild/interpreter/mesonmain.py +++ b/mesonbuild/interpreter/mesonmain.py @@ -326,9 +326,11 @@ def install_dependency_manifest_method(self, args: T.Tuple[str], kwargs: 'TYPE_k @FeatureNew('meson.override_find_program', '0.46.0') @typed_pos_args('meson.override_find_program', str, (mesonlib.File, Program, build.Executable)) - @noKwargs + @typed_kwargs('meson.override_find_program', NATIVE_KW.evolve(since='1.12.0')) @InterpreterObject.method('override_find_program') - def override_find_program_method(self, args: T.Tuple[str, T.Union[mesonlib.File, Program, build.Executable]], kwargs: 'TYPE_kwargs') -> None: + @apply_machine_map + def override_find_program_method(self, args: T.Tuple[str, T.Union[mesonlib.File, Program, build.Executable]], + kwargs: NativeKW) -> None: name, exe = args if isinstance(exe, mesonlib.File): abspath = exe.absolute_path(self.interpreter.environment.source_dir, @@ -339,7 +341,7 @@ def override_find_program_method(self, args: T.Tuple[str, T.Union[mesonlib.File, exe = build.LocalProgram(prog, self.interpreter.project_version, file=exe) elif isinstance(exe, build.Executable): exe = build.LocalProgram(exe, self.interpreter.project_version) - self.interpreter.add_find_program_override(name, exe) + self.interpreter.add_find_program_override(name, exe, kwargs['native']) @typed_kwargs( 'meson.override_dependency', diff --git a/mesonbuild/modules/__init__.py b/mesonbuild/modules/__init__.py index 38be13de291a..fd689f0d71fb 100644 --- a/mesonbuild/modules/__init__.py +++ b/mesonbuild/modules/__init__.py @@ -92,7 +92,7 @@ def find_program(self, prog: T.Union[mesonlib.FileOrString, T.List[mesonlib.File def find_tool(self, name: str, depname: str, varname: str, required: bool = True, wanted: T.Optional[str] = None, native: bool = True) -> Program: # Look in overrides in case it's built as subproject - progobj = self._interpreter.program_from_overrides([name], []) + progobj = self._interpreter.program_from_overrides([name], MachineChoice.HOST, []) if progobj is not None: return progobj From 499f73da174cbf5db98146dfe5be3d19d898be7d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 20 Jan 2026 10:50:44 +0100 Subject: [PATCH 517/681] build: differentiate build directory according to a subproject's build machine Signed-off-by: Paolo Bonzini --- mesonbuild/build.py | 28 +++++++++++++------- mesonbuild/interpreter/interpreter.py | 2 +- mesonbuild/interpreter/interpreterobjects.py | 3 ++- mesonbuild/modules/gnome.py | 3 ++- mesonbuild/modules/windows.py | 3 ++- 5 files changed, 26 insertions(+), 13 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index ce73bae8e9e9..79dbea6d58ec 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -309,6 +309,10 @@ class BuildProject: project_args: PerMachine[T.Dict[Language, T.List[str]]] = field(default_factory=lambda: PerMachine({}, {})) project_link_args: PerMachine[T.Dict[Language, T.List[str]]] = field(default_factory=lambda: PerMachine({}, {})) + @lazy_property + def prefix(self) -> str: + return 'build.' if self.for_machine is MachineChoice.BUILD else '' + # literally everything isn't dataclass stuff class Build: @@ -491,6 +495,7 @@ class IncludeDirs(HoldableObject): curdir: str incdirs: T.List[str] is_system: bool + build_project: BuildProject extra_build_dirs: T.List[str] = field(default_factory=list) def abs_string_list(self, sourcedir: str, builddir: str) -> T.List[str]: @@ -501,12 +506,14 @@ def abs_string_list(self, sourcedir: str, builddir: str) -> T.List[str]: be added if this is unset :returns: A list of strings (without compiler argument) """ + bsubdir = self.build_project.prefix + self.curdir + strlist: T.List[str] = [] for idir in self.incdirs: strlist.append(os.path.join(sourcedir, self.curdir, idir)) - strlist.append(os.path.join(builddir, self.curdir, idir)) + strlist.append(os.path.join(builddir, bsubdir, idir)) for idir in self.extra_build_dirs: - strlist.append(os.path.join(builddir, self.curdir, idir)) + strlist.append(os.path.join(builddir, bsubdir, idir)) return strlist def rel_string_list(self, build_to_src: str, build_root: T.Optional[str] = None) -> T.List[str]: @@ -518,13 +525,15 @@ def rel_string_list(self, build_to_src: str, build_root: T.Optional[str] = None) :return: A list if strings (without compiler argument) """ strlist: T.List[str] = [] + bsubdir = self.build_project.prefix + self.curdir + for idirs, add_src in [(self.incdirs, True), (self.extra_build_dirs, False)]: for idir in idirs: - dir = os.path.normpath(os.path.join(self.curdir, idir)) - if build_root is None or os.path.isdir(os.path.join(build_root, dir)): - strlist.append(dir) + bld_dir = os.path.normpath(os.path.join(bsubdir, idir)) + if build_root is None or os.path.isdir(os.path.join(build_root, bld_dir)): + strlist.append(bld_dir) if add_src: - strlist.append(os.path.normpath(os.path.join(build_to_src, dir))) + strlist.append(os.path.normpath(os.path.join(build_to_src, self.curdir, idir))) return strlist @@ -651,9 +660,9 @@ def __post_init__(self) -> None: Target "{self.name}" has a path separator in its name. This is not supported, it can cause unexpected failures and will become a hard error in the future.''')) - self.builddir = self.subdir + self.builddir = self.build_project.prefix + self.subdir if self.build_subdir: - self.builddir = os.path.join(self.subdir, self.build_subdir) + self.builddir = os.path.join(self.builddir, self.build_subdir) # dataclass comparators? def __lt__(self, other: object) -> bool: @@ -1586,7 +1595,8 @@ def check_can_link_together(self, t: BuildTargetTypes) -> None: def add_include_dirs(self, args: T.Sequence['IncludeDirs'], set_is_system: str = 'preserve') -> None: if set_is_system != 'preserve': is_system = set_is_system == 'system' - self.include_dirs.extend([IncludeDirs(x.curdir, x.incdirs, is_system, x.extra_build_dirs) for x in args]) + self.include_dirs.extend([IncludeDirs(x.curdir, x.incdirs, is_system, x.build_project, + x.extra_build_dirs) for x in args]) else: self.include_dirs.extend(args) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 40b546c4ef59..11c2b53d0245 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -2994,7 +2994,7 @@ def build_incdir_object(self, incdir_strings: T.List[str], is_system: bool = Fal absdir_build = os.path.join(absbase_build, a) if not os.path.isdir(absdir_src) and not os.path.isdir(absdir_build): raise InvalidArguments(f'Include dir {a} does not exist.') - i = build.IncludeDirs(self.subdir, incdir_strings, is_system) + i = build.IncludeDirs(self.subdir, incdir_strings, is_system, self.current_build_project()) return i @typed_pos_args('add_test_setup', str) diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index 35bb8085ba1c..ebd256f45f5c 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -1013,7 +1013,8 @@ def found_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool: @noKwargs @InterpreterObject.method('private_dir_include') def private_dir_include_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> build.IncludeDirs: - return build.IncludeDirs('', [], False, [self.interpreter.backend.get_target_private_dir(self._target_object)]) + return build.IncludeDirs('', [], False, self.interpreter.current_build_project(), + [self.interpreter.backend.get_target_private_dir(self._target_object)]) @noPosargs @noKwargs diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index e6e51b2d1369..c177e3f43e98 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -2314,7 +2314,8 @@ def generate_vapi(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'Gener # - link with the correct library # - include the vapi and dependent vapi files in sources # - add relevant directories to include dirs - incs = [build.IncludeDirs(state.subdir, ['.'] + vapi_includes, False)] + incs = [build.IncludeDirs(state.subdir, ['.'] + vapi_includes, False, + state.current_build_project)] sources = [vapi_target] + vapi_depends rv = InternalDependency(None, incs, [], [], link_with, [], sources, [], [], {}, [], [], []) created_values.append(rv) diff --git a/mesonbuild/modules/windows.py b/mesonbuild/modules/windows.py index f990635dac46..2c5089629b27 100644 --- a/mesonbuild/modules/windows.py +++ b/mesonbuild/modules/windows.py @@ -124,7 +124,8 @@ def compile_resources(self, state: 'ModuleState', for d in wrc_depends: if isinstance(d, build.CustomTarget): extra_args += state.get_include_args([ - build.IncludeDirs('', [], False, [self.interpreter.backend.get_target_dir(d)]) + build.IncludeDirs('', [], False, state.current_build_project, + [self.interpreter.backend.get_target_dir(d)]) ]) extra_args += state.get_include_args(kwargs['include_directories'], kwargs['implicit_include_directories']) From a75c360c24aaab0c5e93a223175da634e0c06f39 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 9 Jun 2026 17:18:04 +0200 Subject: [PATCH 518/681] build: keep host/build separate when merging from a build-only subproject Signed-off-by: Paolo Bonzini --- mesonbuild/build.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 79dbea6d58ec..c3592721efe8 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -413,8 +413,11 @@ def merge(self, other: Build) -> None: if isinstance(v, PerMachine): dest = self.__dict__[k] + if dest.host is dest.build: + dest.host = v.build + elif v.host is not v.build: + dest.host = v.host dest.build = v.build - dest.host = v.host else: self.__dict__[k] = v From 2631a5efc4af55518d5e19a93e9caa83ef17a9ae Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 9 Jun 2026 13:26:30 +0200 Subject: [PATCH 519/681] interpreter: allow subprojects to be requested for the build machine The current situation in Meson is that subprojects are always built for the host machine. This means that while cross compiling, a tool that must be run on the build machine must have all of its dependencies met by the system (pkg-config, cmake, config-tool, etc); but dependencies for the host machine can be built. With this patch subprojects may also be built for the build machine. Nothing should ever be installed from a subproject for the build machine, while cross compiling. However, if we are not cross compiling they are installed, since Meson can't really tell the difference between a host and build dependency in that case. Extracted from a patch by Dylan Baker Signed-off-by: Paolo Bonzini --- .../subproject_and_override_program_native.md | 13 +++ docs/yaml/functions/subproject.yaml | 7 ++ mesonbuild/build.py | 16 ++- mesonbuild/cmake/interpreter.py | 4 +- mesonbuild/interpreter/dependencyfallbacks.py | 1 + mesonbuild/interpreter/interpreter.py | 98 +++++++++++++------ mesonbuild/interpreter/kwargs.py | 2 + mesonbuild/modules/__init__.py | 3 +- mesonbuild/modules/cmake.py | 1 + mesonbuild/modules/rust.py | 1 + unittests/allplatformstests.py | 2 +- 11 files changed, 115 insertions(+), 33 deletions(-) create mode 100644 docs/markdown/snippets/subproject_and_override_program_native.md diff --git a/docs/markdown/snippets/subproject_and_override_program_native.md b/docs/markdown/snippets/subproject_and_override_program_native.md new file mode 100644 index 000000000000..489cf9c2ba1e --- /dev/null +++ b/docs/markdown/snippets/subproject_and_override_program_native.md @@ -0,0 +1,13 @@ +## subproject() and meson.override_find_program() now support the native keyword argument + +Subprojects may now be built for the host or build machine (or both). Therefore, +in a cross compile setup, build-time dependencies can be built for the machine +running the build. When a subproject is run for the build machine it will act +just like a normal build == host setup, except that no targets will be installed. + +This necessarily means that `meson.override_find_program()` must differentiate +between programs for the host and those for the build machine, as you may need +two versions of the same program, which have different outputs based on the +machine they are for. For backwards compatibility reasons the default is for the +host machine. Inside a native subproject the host and build machine will both be +the build machine. diff --git a/docs/yaml/functions/subproject.yaml b/docs/yaml/functions/subproject.yaml index 508837c2ba67..04af2a38ff57 100644 --- a/docs/yaml/functions/subproject.yaml +++ b/docs/yaml/functions/subproject.yaml @@ -65,3 +65,10 @@ kwargs: default: true description: | Works just the same as in [[dependency]]. + + native: + type: bool + since: 1.12.0 + default: false + description : | + Whether to build this subproject for the host or build machine. diff --git a/mesonbuild/build.py b/mesonbuild/build.py index c3592721efe8..70aab9340637 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -19,9 +19,10 @@ from . import dependencies from . import mlog from . import programs +from .environment import MachineMap from .mesonlib import ( HoldableObject, SecondLevelHolder, SimpleABC, SubProject, - File, MesonException, MachineChoice, PerMachine, + File, MesonException, MachineChoice, ThreeMachineChoice, PerMachine, OrderedSet, classify_unity_sources, ROOT_SUBPROJECT, get_filenames_templates_dict, substitute_values, has_path_sep, is_parent_path, relpath, PerMachineDefaultable, @@ -401,6 +402,19 @@ def copy(self) -> Build: other.__dict__[k] = copy.copy(v) return other + def copy_for_build_machine(self) -> Build: + """Create a build-only copy for build machine subprojects. + + Build-only subprojects run with host==build configuration so that get_compiler(), + dependency() etc. look up the native (non-cross) environments. However, note + that the environment is still a cross one so that the build-machine options, + dependencies, etc. are looked up. + """ + new = self.copy() + new.machine_map = MachineMap(self.machine_map.build, self.machine_map.build, + ThreeMachineChoice(self.machine_map.host)) + return new + def merge(self, other: Build) -> None: for k, v in other.__dict__.items(): # This one is modified for build-only configs, but it is diff --git a/mesonbuild/cmake/interpreter.py b/mesonbuild/cmake/interpreter.py index f369d7b38c6f..733a68b077d4 100644 --- a/mesonbuild/cmake/interpreter.py +++ b/mesonbuild/cmake/interpreter.py @@ -796,14 +796,14 @@ def log(self) -> None: mlog.log(' -- depends: ', mlog.bold(str(self.depends))) class CMakeInterpreter: - def __init__(self, subdir: Path, env: 'Environment', backend: 'Backend'): + def __init__(self, subdir: Path, env: 'Environment', backend: 'Backend', for_machine: MachineChoice): self.subdir = subdir self.src_dir = Path(env.get_source_dir(), subdir) self.build_dir_rel = subdir / '__CMake_build' self.build_dir = Path(env.get_build_dir()) / self.build_dir_rel self.install_prefix = Path(T.cast('str', env.coredata.optstore.get_value_for(OptionKey('prefix')))) self.env = env - self.for_machine = MachineChoice.HOST # TODO make parameter + self.for_machine = for_machine self.backend_name = backend.name self.linkers: T.Set[str] = set() self.fileapi = CMakeFileAPI(self.build_dir) diff --git a/mesonbuild/interpreter/dependencyfallbacks.py b/mesonbuild/interpreter/dependencyfallbacks.py index 7d08c6435e06..8f59832d90ca 100644 --- a/mesonbuild/interpreter/dependencyfallbacks.py +++ b/mesonbuild/interpreter/dependencyfallbacks.py @@ -361,6 +361,7 @@ def lookup(self, kwargs: DependencyObjectKWs, force_fallback: bool = False) -> D kwargs['required'] = required and (i == last) func_kwargs: DoSubproject = { 'required': kwargs['required'], + 'for_machine': self.for_machine, 'cmake_options': [], 'default_options': self.default_options, 'options': None, diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 11c2b53d0245..e3a6915072da 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -355,8 +355,16 @@ def is_internal_machine(self, for_machine: MachineChoice) -> bool: def apply_machine_map_to_kwargs(self, kwargs: kwtypes.BaseBuildTarget | kwtypes.MachineMapArgs) -> None: orig_for_machine: MachineChoice | None = kwargs.get('native', None) - if orig_for_machine is not None: - kwargs['native'] = self.build.machine_map[orig_for_machine] + if orig_for_machine is None: + return + + for_machine = self.build.machine_map[orig_for_machine] + if for_machine is orig_for_machine: + return + + if 'install' in kwargs and for_machine is MachineChoice.BUILD: + kwargs['install'] = False + kwargs['native'] = for_machine def build_func_dict(self) -> None: self.funcs.update({'add_global_arguments': self.func_add_global_arguments, @@ -881,24 +889,33 @@ def func_option(self, nodes: mparser.BaseNode, args: list[TYPE_var], kwargs: TYP @typed_kwargs( 'subproject', REQUIRED_KW, + NATIVE_KW.evolve(since='1.12.0'), DEFAULT_OPTIONS.evolve(since='0.38.0'), KwargInfo('version', ContainerTypeInfo(list, str), default=[], listify=True), ) def func_subproject(self, nodes: mparser.BaseNode, args: T.Tuple[SubProject], kwargs: kwtypes.Subproject) -> SubprojectHolder: + # note that this does not use @apply_machine_map. 'for_machine' + # is only used by _do_subproject_meson to decide between + # Build.copy() and Build.copy_for_build_machine(), and copying + # a build-only subproject provides the correct result for "native: + # false". This is needed so that it takes two *explicit* levels + # of "native: true" for the target_machine to become the build_machine. kw: kwtypes.DoSubproject = { 'required': kwargs['required'], 'default_options': kwargs['default_options'], 'version': kwargs['version'], 'options': None, 'cmake_options': [], + 'for_machine': kwargs['native'], } return self.do_subproject(args[0], kw) def disabled_subproject(self, subp_name: SubProject, disabled_feature: T.Optional[str] = None, - exception: T.Optional[Exception] = None) -> SubprojectHolder: + exception: T.Optional[Exception] = None, + for_machine: MachineChoice = MachineChoice.HOST) -> SubprojectHolder: sub = SubprojectHolder(NullSubprojectInterpreter(), os.path.join(self.subproject_dir, subp_name), disabled_feature=disabled_feature, exception=exception) - self.subprojects.host[subp_name] = sub + self.subprojects[for_machine][subp_name] = sub return sub def create_build_subdir(self, subdir: str) -> None: @@ -920,9 +937,12 @@ def format_subproject_stack(stack: T.List[T.Tuple[str, MachineChoice]], join: st def do_subproject(self, subp_name: SubProject, kwargs: kwtypes.DoSubproject, force_method: T.Optional[wrap.Method] = None, forced_options: T.Optional[OptionDict] = None) -> SubprojectHolder: disabled, required, feature = extract_required_kwarg(kwargs, self.subproject) + kwargs.setdefault('for_machine', MachineChoice.HOST) + for_machine = self.build.machine_map[kwargs['for_machine']] + if disabled: mlog.log('Subproject', mlog.bold(subp_name), ':', 'skipped: feature', mlog.bold(feature), 'disabled') - return self.disabled_subproject(subp_name, disabled_feature=feature) + return self.disabled_subproject(subp_name, disabled_feature=feature, for_machine=for_machine) default_options = kwargs['default_options'] @@ -947,16 +967,16 @@ def do_subproject(self, subp_name: SubProject, kwargs: kwtypes.DoSubproject, for if has_path_sep(subp_name): mlog.warning('Subproject name has a path separator. This may cause unexpected behaviour.', location=self.current_node) - fullstack = self.subproject_stack + [(subp_name, MachineChoice.HOST)] - if (subp_name, MachineChoice.HOST) in self.subproject_stack: + fullstack = self.subproject_stack + [(subp_name, for_machine)] + if (subp_name, for_machine) in self.subproject_stack: incpath = self.format_subproject_stack(fullstack) raise InvalidCode(f'Recursive include of subprojects: {incpath}.') - if subp_name in self.subprojects.host: - subproject = self.subprojects.host[subp_name] + if subp_name in self.subprojects[for_machine]: + subproject = self.subprojects[for_machine][subp_name] if required and not subproject.found(): raise InterpreterException(f'Subproject "{subproject.subdir}" required but not found.') if kwargs['version']: - pv = self.build.projects.host[subp_name].version + pv = self.build.projects[for_machine][subp_name].version wanted = kwargs['version'] if pv == 'undefined' or not mesonlib.version_compare_many(pv, wanted)[0]: raise InterpreterException(f'Subproject {subp_name} version is {pv} but {wanted} required.') @@ -985,6 +1005,7 @@ def do_subproject(self, subp_name: SubProject, kwargs: kwtypes.DoSubproject, for m = ['\nExecuting subproject', mlog.bold(stack)] if method != 'meson': m += ['method', mlog.bold(method)] + m.extend(['for machine:', mlog.bold(for_machine.get_lower_case_name())]) mlog.log(*m, '\n', nested=False) methods_map: T.Dict[wrap.Method, T.Callable[[SubProject, str, OptionDict, kwtypes.DoSubproject], @@ -1029,10 +1050,16 @@ def _do_subproject_meson(self, subp_name: SubProject, subdir: str, build_def_files: T.Optional[T.List[str]] = None, relaxations: T.Optional[T.Set[InterpreterRuleRelaxation]] = None, cargo: T.Optional[cargo.Interpreter] = None) -> SubprojectHolder: + for_machine = kwargs['for_machine'] + if for_machine is MachineChoice.BUILD: + new_build = self.build.copy_for_build_machine() + else: + new_build = self.build.copy() + with mlog.nested(subp_name): if ast: self._save_ast(subdir, ast) - new_build = self.build.copy() + subi = Interpreter(new_build, self.backend, subp_name, subdir, self.subproject_dir, default_options, ast=ast, relaxations=relaxations, user_defined_options=self.user_defined_options, @@ -1046,7 +1073,7 @@ def _do_subproject_meson(self, subp_name: SubProject, subdir: str, subi.bound_holder_map = self.bound_holder_map subi.summary = self.summary - subi.subproject_stack = self.subproject_stack + [(subp_name, MachineChoice.HOST)] + subi.subproject_stack = self.subproject_stack + [(subp_name, for_machine)] current_active = self.active_projectname with mlog.nested_warnings(): subi.run() @@ -1061,26 +1088,27 @@ def _do_subproject_meson(self, subp_name: SubProject, subdir: str, if pv == 'undefined' or not mesonlib.version_compare_many(pv, wanted)[0]: raise InterpreterException(f'Subproject {subp_name} version is {pv} but {wanted} required.') self.active_projectname = current_active - self.subprojects.host.update(subi.subprojects.host) - self.subprojects.host[subp_name] = SubprojectHolder(subi, subdir, warnings=subi_warnings, - callstack=self.subproject_stack) + self.subprojects[for_machine].update(subi.subprojects[for_machine]) + self.subprojects[for_machine][subp_name] = SubprojectHolder( + subi, subdir, warnings=subi_warnings, callstack=self.subproject_stack) # Duplicates are possible when subproject uses files from project root if build_def_files: self.build_def_files.update(build_def_files) # We always need the subi.build_def_files, to propagate sub-sub-projects self.build_def_files.update(subi.get_build_def_files()) self.build.merge(subi.build) - return self.subprojects.host[subp_name] + return self.subprojects[for_machine][subp_name] def _do_subproject_cmake(self, subp_name: SubProject, subdir: str, default_options: OptionDict, kwargs: kwtypes.DoSubproject) -> SubprojectHolder: from ..cmake import CMakeInterpreter + for_machine = kwargs['for_machine'] with mlog.nested(subp_name): from ..modules.cmake import CMakeSubprojectOptions kw_opts = kwargs.get('options') or CMakeSubprojectOptions() cmake_options = kwargs.get('cmake_options', []) + kw_opts.cmake_options - cm_int = CMakeInterpreter(Path(subdir), self.build.environment, self.backend) + cm_int = CMakeInterpreter(Path(subdir), self.build.environment, self.backend, for_machine) cm_int.initialise(cmake_options) cm_int.analyse() @@ -1327,7 +1355,9 @@ def func_project(self, node: mparser.FunctionNode, args: T.Tuple[str, T.List[str proj_license_files.append((ifname, i)) self.build.dep_manifest[proj_name] = build.DepManifest(self.project_version, proj_license, proj_license_files, self.subproject) - if self.subproject in self.build.projects.host: + + for_machine = self.build.machine_map.host + if self.subproject in self.build.projects[for_machine]: raise InvalidCode('Second call to project().') # spdirname is the subproject_dir for this project, relative to self.subdir. @@ -1359,9 +1389,13 @@ def func_project(self, node: mparser.FunctionNode, args: T.Tuple[str, T.List[str if self.cargo is None: self.load_root_cargo_lock_file() - self.build.projects.host[self.subproject] = build.BuildProject(proj_name, self.project_version, - self.subproject, MachineChoice.HOST) - mlog.log('Project name:', mlog.bold(proj_name)) + build_project = build.BuildProject(proj_name, self.project_version, self.subproject, for_machine) + self.build.projects[for_machine][self.subproject] = build_project + + extra_args: T.List[mlog.TV_Loggable] = [] + if self.is_subproject() and for_machine is MachineChoice.BUILD: + extra_args.append('(for build machine)') + mlog.log('Project name:', mlog.bold(proj_name), *extra_args) mlog.log('Project version:', mlog.bold(self.project_version)) self.add_languages(proj_langs, True, MachineChoice.HOST) @@ -1451,10 +1485,10 @@ def summary_impl(self, section: str, values: dict[str, mlog.TV_Loggable | mlog.T self.summary[self.subproject].add_section( section, values, bool_yn, list_sep, self.subproject) - def _print_summary(self) -> None: + def _print_subprojects(self, for_machine: MachineChoice) -> None: # Add automatic 'Subprojects' section in main project. all_subprojects: dict[str, mlog.TV_Loggable | mlog.TV_LoggableList] = {} - for name, subp in sorted(self.subprojects.host.items()): + for name, subp in sorted(self.subprojects[for_machine].items()): value: mlog.TV_LoggableList = [subp.found()] if subp.disabled_feature: value += [f'Feature {subp.disabled_feature!r} disabled'] @@ -1467,7 +1501,13 @@ def _print_summary(self) -> None: value += [f'(from {stack})'] all_subprojects[name] = value if all_subprojects: - self.summary_impl('Subprojects', all_subprojects, bool_yn=True, list_sep=' ') + self.summary_impl(f'Subprojects (for {for_machine.get_lower_case_name()} machine)', all_subprojects, + bool_yn=True, list_sep=' ') + + def _print_summary(self) -> None: + self._print_subprojects(MachineChoice.HOST) + if self.environment.is_cross_build(): + self._print_subprojects(MachineChoice.BUILD) # Add automatic section with all user defined options if self.user_defined_options: values: dict[str, mlog.TV_Loggable | mlog.TV_LoggableList] = {} @@ -1744,7 +1784,7 @@ def find_program_impl(self, args: T.List[mesonlib.FileOrString], extra_info: T.List[mlog.TV_Loggable] = [] progobj = self.program_lookup(args, for_machine, default_options, required, search_dirs, wanted, version_arg, version_func, extra_info) - if progobj is None or not self.check_program_version(progobj, wanted, version_func, extra_info): + if progobj is None or not self.check_program_version(progobj, wanted, version_func, for_machine, extra_info): progobj = self.notfound_program(args) if isinstance(progobj, ExternalProgram) and not progobj.found(): @@ -1796,7 +1836,7 @@ def program_lookup(self, args: T.List[mesonlib.FileOrString], for_machine: Machi if isinstance(progobj, ExternalProgram) and version_arg: progobj.version_arg = version_arg - if progobj and not self.check_program_version(progobj, wanted, version_func, extra_info): + if progobj and not self.check_program_version(progobj, wanted, version_func, for_machine, extra_info): progobj = None if progobj is None and fallback and required: @@ -1810,13 +1850,14 @@ def program_lookup(self, args: T.List[mesonlib.FileOrString], for_machine: Machi def check_program_version(self, progobj: Program, wanted: T.Union[str, T.List[str]], version_func: T.Optional[ProgramVersionFunc], + for_machine: MachineChoice, extra_info: T.List[mlog.TV_Loggable]) -> bool: if wanted: if version_func: version = version_func(progobj) elif isinstance(progobj, build.Executable): if progobj.subproject: - interp = self.subprojects.host[progobj.subproject].held_object + interp = self.subprojects[for_machine][progobj.subproject].held_object else: interp = self assert isinstance(interp, Interpreter), 'for mypy' @@ -1834,7 +1875,7 @@ def check_program_version(self, progobj: Program, def find_program_fallback(self, fallback: SubProject, args: T.List[mesonlib.FileOrString], for_machine: MachineChoice, default_options: OptionDict, - required: bool, extra_info: T.List[mlog.TV_Loggable] + required: bool, extra_info: T.List[mlog.TV_Loggable], ) -> T.Optional[Program]: mlog.log('Fallback to subproject', mlog.bold(fallback), 'which provides program', mlog.bold(' '.join(str(a) for a in args))) @@ -1844,6 +1885,7 @@ def find_program_fallback(self, fallback: SubProject, args: T.List[mesonlib.File 'version': [], 'cmake_options': [], 'options': None, + 'for_machine': for_machine, } self.do_subproject(fallback, sp_kwargs) return self.program_from_overrides(args, for_machine, extra_info) diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index b6be5a17b6c2..9233db19ccbb 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -347,6 +347,7 @@ class Subproject(ExtractRequired): default_options: T.Dict[OptionKey, options.ElementaryOptionValues] version: T.List[str] + native: MachineChoice class DoSubproject(ExtractRequired): @@ -355,6 +356,7 @@ class DoSubproject(ExtractRequired): version: T.List[str] cmake_options: T.List[str] options: T.Optional[CMakeSubprojectOptions] + for_machine: MachineChoice class BaseBuildTarget(TypedDict): diff --git a/mesonbuild/modules/__init__.py b/mesonbuild/modules/__init__.py index fd689f0d71fb..26b7dcd21767 100644 --- a/mesonbuild/modules/__init__.py +++ b/mesonbuild/modules/__init__.py @@ -92,7 +92,8 @@ def find_program(self, prog: T.Union[mesonlib.FileOrString, T.List[mesonlib.File def find_tool(self, name: str, depname: str, varname: str, required: bool = True, wanted: T.Optional[str] = None, native: bool = True) -> Program: # Look in overrides in case it's built as subproject - progobj = self._interpreter.program_from_overrides([name], MachineChoice.HOST, []) + for_machine = MachineChoice.BUILD if native else MachineChoice.HOST + progobj = self._interpreter.program_from_overrides([name], for_machine, []) if progobj is not None: return progobj diff --git a/mesonbuild/modules/cmake.py b/mesonbuild/modules/cmake.py index d433697e0412..845c26c65d5c 100644 --- a/mesonbuild/modules/cmake.py +++ b/mesonbuild/modules/cmake.py @@ -445,6 +445,7 @@ def subproject(self, state: ModuleState, args: T.Tuple[str], kwargs_: Subproject 'cmake_options': kwargs_['cmake_options'], 'default_options': {}, 'version': [], + 'for_machine': mesonlib.MachineChoice.HOST, } subp = self.interpreter.do_subproject(subp_name, kw, force_method='cmake') if not subp.found(): diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index 1ec86b60a816..11cd3e810b00 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -172,6 +172,7 @@ def _do_subproject(self, pkg: cargo.PackageState) -> None: 'options': None, 'cmake_options': [], 'default_options': {}, + 'for_machine': MachineChoice.HOST, } subp_name = pkg.get_subproject_name() self.interpreter.do_subproject(subp_name, kw, force_method='cargo') diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 830cd310470f..affd0d91cabc 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -4120,7 +4120,7 @@ def test_summary(self): long comma list: alpha, alphacolor, apetag, audiofx, audioparsers, auparse, autodetect, avi - Subprojects + Subprojects (for host machine) sub : YES sub2 : NO Problem encountered: This subproject failed subsub : YES (from sub2) From bff2e5831a13385cf52b68512a05bfdaef97fe43 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Sat, 7 Feb 2026 09:53:04 +0100 Subject: [PATCH 520/681] tests for native subprojects --- .../native/10 native subproject/maingen.c | 8 ++++ .../native/10 native subproject/meson.build | 31 ++++++++++++++ .../subprojects/both/main.c | 3 ++ .../subprojects/both/meson.build | 4 ++ .../subprojects/buildtool/buildtool.c | 9 +++++ .../subprojects/buildtool/meson.build | 16 ++++++++ .../buildtool/subprojects/hostp/hp.c | 3 ++ .../buildtool/subprojects/hostp/meson.build | 5 +++ .../subprojects/recursive-both/lib.c | 3 ++ .../subprojects/recursive-both/lib.def | 2 + .../subprojects/recursive-both/meson.build | 11 +++++ .../recursive-both/recursive-both.h.in | 1 + .../subprojects/recursive-build-only/main.c | 13 ++++++ .../recursive-build-only/meson.build | 9 +++++ .../recursive-host-only/meson.build | 16 ++++++++ .../subprojects/test installs/header.h | 0 .../subprojects/test installs/man.1 | 0 .../subprojects/test installs/meson.build | 11 +++++ .../subprojects/test installs/script.py | 4 ++ .../native/10 native subproject/test.json | 6 +++ test cases/native/11 native dependency/main.c | 6 +++ .../native/11 native dependency/meson.build | 7 ++++ .../subprojects/made up/lib.c | 1 + .../subprojects/made up/lib.h | 1 + .../subprojects/made up/meson.build | 7 ++++ .../rust/22 cargo subproject/meson.build | 6 +++ unittests/linuxcrosstests.py | 40 ++++++++++++++++++- 27 files changed, 221 insertions(+), 2 deletions(-) create mode 100644 test cases/native/10 native subproject/maingen.c create mode 100644 test cases/native/10 native subproject/meson.build create mode 100644 test cases/native/10 native subproject/subprojects/both/main.c create mode 100644 test cases/native/10 native subproject/subprojects/both/meson.build create mode 100644 test cases/native/10 native subproject/subprojects/buildtool/buildtool.c create mode 100644 test cases/native/10 native subproject/subprojects/buildtool/meson.build create mode 100644 test cases/native/10 native subproject/subprojects/buildtool/subprojects/hostp/hp.c create mode 100644 test cases/native/10 native subproject/subprojects/buildtool/subprojects/hostp/meson.build create mode 100644 test cases/native/10 native subproject/subprojects/recursive-both/lib.c create mode 100644 test cases/native/10 native subproject/subprojects/recursive-both/lib.def create mode 100644 test cases/native/10 native subproject/subprojects/recursive-both/meson.build create mode 100644 test cases/native/10 native subproject/subprojects/recursive-both/recursive-both.h.in create mode 100644 test cases/native/10 native subproject/subprojects/recursive-build-only/main.c create mode 100644 test cases/native/10 native subproject/subprojects/recursive-build-only/meson.build create mode 100644 test cases/native/10 native subproject/subprojects/recursive-host-only/meson.build create mode 100644 test cases/native/10 native subproject/subprojects/test installs/header.h create mode 100644 test cases/native/10 native subproject/subprojects/test installs/man.1 create mode 100644 test cases/native/10 native subproject/subprojects/test installs/meson.build create mode 100755 test cases/native/10 native subproject/subprojects/test installs/script.py create mode 100644 test cases/native/10 native subproject/test.json create mode 100644 test cases/native/11 native dependency/main.c create mode 100644 test cases/native/11 native dependency/meson.build create mode 100644 test cases/native/11 native dependency/subprojects/made up/lib.c create mode 100644 test cases/native/11 native dependency/subprojects/made up/lib.h create mode 100644 test cases/native/11 native dependency/subprojects/made up/meson.build diff --git a/test cases/native/10 native subproject/maingen.c b/test cases/native/10 native subproject/maingen.c new file mode 100644 index 000000000000..c05cfe553997 --- /dev/null +++ b/test cases/native/10 native subproject/maingen.c @@ -0,0 +1,8 @@ +#include + +int main(void) { + printf("const char * gen_main(void) {\n"); + printf(" return \"int main() \";\n"); + printf("}\n"); + return 0; +} diff --git a/test cases/native/10 native subproject/meson.build b/test cases/native/10 native subproject/meson.build new file mode 100644 index 000000000000..f35bb2e4d2e8 --- /dev/null +++ b/test cases/native/10 native subproject/meson.build @@ -0,0 +1,31 @@ +project('native subproject', 'c', meson_version : '>= 1.12.0') + +# Test running a subproject for both host and build +subproject('both', native : true) +subproject('both', native : false) + +maingen = executable('maingen', 'maingen.c', native : true) +meson.override_find_program('maingen', maingen, native : true) +gen = subproject('buildtool', native : true).get_variable('e') + +ct = custom_target( + 'generated', + command : [gen], + output : 'generated.c', + capture : true, +) + +e = executable('testprog', ct, install : true) + +test('main', e) + +if meson.is_cross_build() + subproject('test installs', native : true) +endif + +# Test a subproject configured from the main project, and then called as a nested subproject +subproject('recursive-both', native : true) +subproject('recursive-both', native : false) + +subproject('recursive-build-only', native : true) +subproject('recursive-host-only', native : false) diff --git a/test cases/native/10 native subproject/subprojects/both/main.c b/test cases/native/10 native subproject/subprojects/both/main.c new file mode 100644 index 000000000000..9b6bdc2ec2f0 --- /dev/null +++ b/test cases/native/10 native subproject/subprojects/both/main.c @@ -0,0 +1,3 @@ +int main(void) { + return 0; +} diff --git a/test cases/native/10 native subproject/subprojects/both/meson.build b/test cases/native/10 native subproject/subprojects/both/meson.build new file mode 100644 index 000000000000..eacbaf7e47df --- /dev/null +++ b/test cases/native/10 native subproject/subprojects/both/meson.build @@ -0,0 +1,4 @@ +project('both', 'c') + +e = executable('main', 'main.c') +test('main', e) diff --git a/test cases/native/10 native subproject/subprojects/buildtool/buildtool.c b/test cases/native/10 native subproject/subprojects/buildtool/buildtool.c new file mode 100644 index 000000000000..face28e25f73 --- /dev/null +++ b/test cases/native/10 native subproject/subprojects/buildtool/buildtool.c @@ -0,0 +1,9 @@ +#include + +const char * gen_main(void); + +int main() { + printf("%s", gen_main()); + printf("{ return 0; }\n"); + return 0; +} diff --git a/test cases/native/10 native subproject/subprojects/buildtool/meson.build b/test cases/native/10 native subproject/subprojects/buildtool/meson.build new file mode 100644 index 000000000000..7352e71fa96a --- /dev/null +++ b/test cases/native/10 native subproject/subprojects/buildtool/meson.build @@ -0,0 +1,16 @@ +project('buildtool', 'c', meson_version : '>= 1.12.0') + +maingen = find_program('maingen') + +ct = custom_target( + 'gen', + command : [maingen], + output : 'gen.c', + capture : true, +) + +e = executable('buildtool', 'buildtool.c', ct) + +meson.override_find_program('buildtool', e) + +subproject('hostp', native : false) diff --git a/test cases/native/10 native subproject/subprojects/buildtool/subprojects/hostp/hp.c b/test cases/native/10 native subproject/subprojects/buildtool/subprojects/hostp/hp.c new file mode 100644 index 000000000000..9b6bdc2ec2f0 --- /dev/null +++ b/test cases/native/10 native subproject/subprojects/buildtool/subprojects/hostp/hp.c @@ -0,0 +1,3 @@ +int main(void) { + return 0; +} diff --git a/test cases/native/10 native subproject/subprojects/buildtool/subprojects/hostp/meson.build b/test cases/native/10 native subproject/subprojects/buildtool/subprojects/hostp/meson.build new file mode 100644 index 000000000000..90cd1f0ee345 --- /dev/null +++ b/test cases/native/10 native subproject/subprojects/buildtool/subprojects/hostp/meson.build @@ -0,0 +1,5 @@ +project('host project', 'c') + +e = executable('hp', 'hp.c') + +test('host project', e) diff --git a/test cases/native/10 native subproject/subprojects/recursive-both/lib.c b/test cases/native/10 native subproject/subprojects/recursive-both/lib.c new file mode 100644 index 000000000000..db3e30c886f9 --- /dev/null +++ b/test cases/native/10 native subproject/subprojects/recursive-both/lib.c @@ -0,0 +1,3 @@ +#include "recursive-both.h" + +int rcb(void) { return 7; } diff --git a/test cases/native/10 native subproject/subprojects/recursive-both/lib.def b/test cases/native/10 native subproject/subprojects/recursive-both/lib.def new file mode 100644 index 000000000000..6f4a5c9ba170 --- /dev/null +++ b/test cases/native/10 native subproject/subprojects/recursive-both/lib.def @@ -0,0 +1,2 @@ +EXPORTS + rcb diff --git a/test cases/native/10 native subproject/subprojects/recursive-both/meson.build b/test cases/native/10 native subproject/subprojects/recursive-both/meson.build new file mode 100644 index 000000000000..c3bac8bbf5a4 --- /dev/null +++ b/test cases/native/10 native subproject/subprojects/recursive-both/meson.build @@ -0,0 +1,11 @@ +project('recursive-both', 'c') + +fs = import('fs') + +header = fs.copyfile('recursive-both.h.in', 'recursive-both.h') + +lib = library('lib', 'lib.c', header, vs_module_defs : 'lib.def') + +dep = declare_dependency(link_with : lib, sources : header, include_directories : '.') + +meson.override_dependency('recursive-both', dep) diff --git a/test cases/native/10 native subproject/subprojects/recursive-both/recursive-both.h.in b/test cases/native/10 native subproject/subprojects/recursive-both/recursive-both.h.in new file mode 100644 index 000000000000..c0ccb514b169 --- /dev/null +++ b/test cases/native/10 native subproject/subprojects/recursive-both/recursive-both.h.in @@ -0,0 +1 @@ +int rcb(void); diff --git a/test cases/native/10 native subproject/subprojects/recursive-build-only/main.c b/test cases/native/10 native subproject/subprojects/recursive-build-only/main.c new file mode 100644 index 000000000000..c80100f9c592 --- /dev/null +++ b/test cases/native/10 native subproject/subprojects/recursive-build-only/main.c @@ -0,0 +1,13 @@ +#include +#include "recursive-both.h" + +int main(void) { + const int v = rcb(); + printf("int main(void) {\n"); + if (v == 7) + printf(" return 0;\n"); + else + printf(" return 1;\n"); + printf("}\n"); + return 0; +} diff --git a/test cases/native/10 native subproject/subprojects/recursive-build-only/meson.build b/test cases/native/10 native subproject/subprojects/recursive-build-only/meson.build new file mode 100644 index 000000000000..f0a6939bd31b --- /dev/null +++ b/test cases/native/10 native subproject/subprojects/recursive-build-only/meson.build @@ -0,0 +1,9 @@ +project('recursive-build-only', 'c') + +assert(meson.is_cross_build() == false, 'this project should never be considered a cross target') + +dep = dependency('recursive-both') + +exe = executable('main', 'main.c', dependencies : dep) + +meson.override_find_program('build-only-tool', exe) diff --git a/test cases/native/10 native subproject/subprojects/recursive-host-only/meson.build b/test cases/native/10 native subproject/subprojects/recursive-host-only/meson.build new file mode 100644 index 000000000000..6b564d635d1c --- /dev/null +++ b/test cases/native/10 native subproject/subprojects/recursive-host-only/meson.build @@ -0,0 +1,16 @@ +project('recursive-host-only', 'c') + +subp = subproject('recursive-build-only', native : true) + +prog = subp.get_variable('exe') + +ct = custom_target( + 'main.c', + output : 'main.c', + command : prog, + capture : true, +) + +exe = executable('main', ct, native : true) + +test('main', exe) diff --git a/test cases/native/10 native subproject/subprojects/test installs/header.h b/test cases/native/10 native subproject/subprojects/test installs/header.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/test cases/native/10 native subproject/subprojects/test installs/man.1 b/test cases/native/10 native subproject/subprojects/test installs/man.1 new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/test cases/native/10 native subproject/subprojects/test installs/meson.build b/test cases/native/10 native subproject/subprojects/test installs/meson.build new file mode 100644 index 000000000000..7f4ddcd17884 --- /dev/null +++ b/test cases/native/10 native subproject/subprojects/test installs/meson.build @@ -0,0 +1,11 @@ +project('test installs') + +install_emptydir('usr/share/foo') +install_headers('header.h') +install_man('man.1') +install_data('header.h', install_dir : 'foo') +install_symlink('link', install_dir : 'usr/bin', pointing_to : '/usr/bin/testprog') +install_subdir('subdir', install_dir : 'share') + +meson.add_install_script('script.py') +meson.add_postconf_script('script.py') diff --git a/test cases/native/10 native subproject/subprojects/test installs/script.py b/test cases/native/10 native subproject/subprojects/test installs/script.py new file mode 100755 index 000000000000..620b0ae87143 --- /dev/null +++ b/test cases/native/10 native subproject/subprojects/test installs/script.py @@ -0,0 +1,4 @@ +#!/usr/bin/env python3 + +# Do nothing +exit(0) diff --git a/test cases/native/10 native subproject/test.json b/test cases/native/10 native subproject/test.json new file mode 100644 index 000000000000..e92a0059e50f --- /dev/null +++ b/test cases/native/10 native subproject/test.json @@ -0,0 +1,6 @@ +{ + "installed": [ + {"type": "exe", "file": "usr/bin/testprog"}, + {"type": "pdb", "file": "usr/bin/testprog"} + ] +} diff --git a/test cases/native/11 native dependency/main.c b/test cases/native/11 native dependency/main.c new file mode 100644 index 000000000000..6bdbc263b2ed --- /dev/null +++ b/test cases/native/11 native dependency/main.c @@ -0,0 +1,6 @@ +#include "lib.h" + +int main(void) { + const int v = foo() - 1; + return v; +} diff --git a/test cases/native/11 native dependency/meson.build b/test cases/native/11 native dependency/meson.build new file mode 100644 index 000000000000..f8f34e3bbb3e --- /dev/null +++ b/test cases/native/11 native dependency/meson.build @@ -0,0 +1,7 @@ +project('native dependency fallback', 'c', meson_version : '>= 1.12.0') + +d = dependency('made up', native : true) + +e = executable('main', 'main.c', dependencies : d, native : true) + +test('main', e) diff --git a/test cases/native/11 native dependency/subprojects/made up/lib.c b/test cases/native/11 native dependency/subprojects/made up/lib.c new file mode 100644 index 000000000000..b4d9546c26a0 --- /dev/null +++ b/test cases/native/11 native dependency/subprojects/made up/lib.c @@ -0,0 +1 @@ +int foo(void) { return 1; } diff --git a/test cases/native/11 native dependency/subprojects/made up/lib.h b/test cases/native/11 native dependency/subprojects/made up/lib.h new file mode 100644 index 000000000000..5a2ca62ad296 --- /dev/null +++ b/test cases/native/11 native dependency/subprojects/made up/lib.h @@ -0,0 +1 @@ +int foo(void); diff --git a/test cases/native/11 native dependency/subprojects/made up/meson.build b/test cases/native/11 native dependency/subprojects/made up/meson.build new file mode 100644 index 000000000000..a1515b843a2f --- /dev/null +++ b/test cases/native/11 native dependency/subprojects/made up/meson.build @@ -0,0 +1,7 @@ +project('made up', 'c', meson_version : '>= 1.12.0') + +s = static_library('lib', 'lib.c') + +dep = declare_dependency(link_with : s, include_directories : '.') + +meson.override_dependency('made up', dep) diff --git a/test cases/rust/22 cargo subproject/meson.build b/test cases/rust/22 cargo subproject/meson.build index 3a79bc8a6f16..5916cae6fd4c 100644 --- a/test cases/rust/22 cargo subproject/meson.build +++ b/test cases/rust/22 cargo subproject/meson.build @@ -1,5 +1,11 @@ project('cargo subproject', 'c') +foo_dep = dependency('foo-0', native : true) +exe = executable('app_native', 'main.c', + dependencies: foo_dep, + native : true, +) + foo_dep = dependency('foo-0') exe = executable('app', 'main.c', dependencies: foo_dep, diff --git a/unittests/linuxcrosstests.py b/unittests/linuxcrosstests.py index 768fa7ab04f9..5292dca1e214 100644 --- a/unittests/linuxcrosstests.py +++ b/unittests/linuxcrosstests.py @@ -23,7 +23,7 @@ class BaseLinuxCrossTests(BasePlatformTests): def should_run_cross_arm_tests(): - return shutil.which('arm-linux-gnueabihf-gcc') and not platform.machine().lower().startswith('arm') + return shutil.which('armv7l-unknown-linux-gnueabihf-gcc') and not platform.machine().lower().startswith('arm') @unittest.skipUnless(not is_windows() and should_run_cross_arm_tests(), "requires ability to cross compile to ARM") class LinuxCrossArmTests(BaseLinuxCrossTests): @@ -33,7 +33,43 @@ class LinuxCrossArmTests(BaseLinuxCrossTests): def setUp(self): super().setUp() - self.meson_cross_files = [os.path.join(self.src_root, 'cross', 'ubuntu-armhf.txt')] + self.meson_cross_files = [os.path.join(self.src_root, 'cross', 'nixos-armhf.ini')] + + def test_nested_for_build_subprojects(self) -> None: + """Test that when cross compiled nested native subprojects report + themselves as for the build machine. + + This cannot be done without a unit test because in a host == build + configuration this wont be reported at all + """ + testdir = os.path.join(self.src_root, 'test cases', 'native', '10 native subproject') + + with self.subTest('configuring'): + out = self.init(testdir) + + with self.subTest('nested build-only subproject reports correct machine'): + self.assertIn('Executing subproject buildtool:hostp for machine: build', out) + + with self.subTest('summary reports correct host subprojects'): + expected = '\n'.join([ + 'Subprojects (for host machine)', + ' both : YES', + ' recursive-both : YES', + ' recursive-host-only : YES', + ]) + self.assertIn(expected, out) + + with self.subTest('summary reports correct build subprojects'): + expected = '\n'.join([ + 'Subprojects (for build machine)', + ' both : YES', + ' buildtool : YES', + ' hostp : YES', + ' recursive-both : YES', + ' recursive-build-only: YES', + ' test installs : YES', + ]) + self.assertIn(expected, out) def test_cflags_cross_environment_pollution(self): ''' From 26e4e7d4ca15fd155a35e4978dccac9920b6de9c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 9 Jun 2026 14:45:56 +0200 Subject: [PATCH 521/681] tests: add test that native subprojects have different introspection ids --- .../meson.build | 8 +++++++ .../subprojects/recursive-both/lib.c | 3 +++ .../subprojects/recursive-both/lib.def | 2 ++ .../subprojects/recursive-both/meson.build | 11 +++++++++ .../recursive-both/recursive-both.h.in | 1 + test cases/unit/69 cross/crossfile.in | 3 +++ test cases/unit/69 cross/meson.build | 7 ++++++ unittests/allplatformstests.py | 23 +++++++++++++++++++ 8 files changed, 58 insertions(+) create mode 100644 test cases/unit/136 native subproject introspect/meson.build create mode 100644 test cases/unit/136 native subproject introspect/subprojects/recursive-both/lib.c create mode 100644 test cases/unit/136 native subproject introspect/subprojects/recursive-both/lib.def create mode 100644 test cases/unit/136 native subproject introspect/subprojects/recursive-both/meson.build create mode 100644 test cases/unit/136 native subproject introspect/subprojects/recursive-both/recursive-both.h.in diff --git a/test cases/unit/136 native subproject introspect/meson.build b/test cases/unit/136 native subproject introspect/meson.build new file mode 100644 index 000000000000..8118b78d9992 --- /dev/null +++ b/test cases/unit/136 native subproject introspect/meson.build @@ -0,0 +1,8 @@ +project('native subproject introspect', 'c') + +# The same subproject is configured for both the build machine (native : true) +# and the host machine (native : false). In a cross build these are two +# distinct configurations, so the library target it defines is introspected +# twice: with the same name but a different id. +subproject('recursive-both', native : true) +subproject('recursive-both', native : false) diff --git a/test cases/unit/136 native subproject introspect/subprojects/recursive-both/lib.c b/test cases/unit/136 native subproject introspect/subprojects/recursive-both/lib.c new file mode 100644 index 000000000000..db3e30c886f9 --- /dev/null +++ b/test cases/unit/136 native subproject introspect/subprojects/recursive-both/lib.c @@ -0,0 +1,3 @@ +#include "recursive-both.h" + +int rcb(void) { return 7; } diff --git a/test cases/unit/136 native subproject introspect/subprojects/recursive-both/lib.def b/test cases/unit/136 native subproject introspect/subprojects/recursive-both/lib.def new file mode 100644 index 000000000000..6f4a5c9ba170 --- /dev/null +++ b/test cases/unit/136 native subproject introspect/subprojects/recursive-both/lib.def @@ -0,0 +1,2 @@ +EXPORTS + rcb diff --git a/test cases/unit/136 native subproject introspect/subprojects/recursive-both/meson.build b/test cases/unit/136 native subproject introspect/subprojects/recursive-both/meson.build new file mode 100644 index 000000000000..c3bac8bbf5a4 --- /dev/null +++ b/test cases/unit/136 native subproject introspect/subprojects/recursive-both/meson.build @@ -0,0 +1,11 @@ +project('recursive-both', 'c') + +fs = import('fs') + +header = fs.copyfile('recursive-both.h.in', 'recursive-both.h') + +lib = library('lib', 'lib.c', header, vs_module_defs : 'lib.def') + +dep = declare_dependency(link_with : lib, sources : header, include_directories : '.') + +meson.override_dependency('recursive-both', dep) diff --git a/test cases/unit/136 native subproject introspect/subprojects/recursive-both/recursive-both.h.in b/test cases/unit/136 native subproject introspect/subprojects/recursive-both/recursive-both.h.in new file mode 100644 index 000000000000..c0ccb514b169 --- /dev/null +++ b/test cases/unit/136 native subproject introspect/subprojects/recursive-both/recursive-both.h.in @@ -0,0 +1 @@ +int rcb(void); diff --git a/test cases/unit/69 cross/crossfile.in b/test cases/unit/69 cross/crossfile.in index beab9bc45edc..a3c3e42b3534 100644 --- a/test cases/unit/69 cross/crossfile.in +++ b/test cases/unit/69 cross/crossfile.in @@ -1,3 +1,6 @@ +[binaries] +c = @c_compiler@ + [host_machine] system = '@system@' cpu_family = '@cpu_family@' diff --git a/test cases/unit/69 cross/meson.build b/test cases/unit/69 cross/meson.build index 645d453c5ba4..90984fa0a140 100644 --- a/test cases/unit/69 cross/meson.build +++ b/test cases/unit/69 cross/meson.build @@ -2,7 +2,14 @@ project('crosstest') add_languages('c', native: true) if get_option('generate') + cc = meson.get_compiler('c', native: true) + quoted = [] + foreach part : cc.cmd_array() + quoted += '\'' + part + '\'' + endforeach + conf_data = configuration_data() + conf_data.set('c_compiler', '[' + ', '.join(quoted) + ']') conf_data.set('system', build_machine.system()) conf_data.set('cpu', build_machine.cpu()) conf_data.set('cpu_family', build_machine.cpu_family()) diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index affd0d91cabc..15aa5b243357 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -3376,6 +3376,29 @@ def test_introspection_target_subproject(self): name = entry['name'] self.assertEqual(entry['subproject'], expected[name]) + def test_introspection_target_native_subproject(self): + # When the same subproject is built for both the build machine + # (native : true) and the host machine (native : false) in a cross + # build, it is configured twice. The two copies of its 'lib' target + # share the same name and subproject but must have different ids. + crossdir = os.path.join(self.unit_test_dir, '69 cross') + # Reuse the cross test project to generate a native and a cross file + # describing this machine, so the configuration below is treated as a + # cross build (see test_identity_cross). + self.init(crossdir, extra_args=['-Dgenerate=true']) + self.meson_native_files = [os.path.join(self.builddir, "nativefile")] + self.meson_cross_files = [os.path.join(self.builddir, "crossfile")] + + self.new_builddir() + testdir = os.path.join(self.unit_test_dir, '136 native subproject introspect') + self.init(testdir) + res = self.introspect('--targets') + + ids = [entry['id'] for entry in res + if entry['name'] == 'lib' and entry['subproject'] == 'recursive-both'] + self.assertEqual(len(ids), 2, 'subproject should be built for both machines') + self.assertEqual(len(set(ids)), 2, 'native and non-native targets must have different ids') + def test_introspect_projectinfo_subproject_dir(self): testdir = os.path.join(self.common_test_dir, '75 custom subproject dir') self.init(testdir) From 192ac47bd473b16e18b5e1e482c7767ac3b436db Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 16 Dec 2025 11:05:23 +0100 Subject: [PATCH 522/681] interpreter, cmake: add native argument to subproject() Extracted from a patch by Dylan Baker Signed-off-by: Paolo Bonzini --- mesonbuild/modules/cmake.py | 6 ++++-- test cases/cmake/2 advanced/meson.build | 11 ++++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/mesonbuild/modules/cmake.py b/mesonbuild/modules/cmake.py index 845c26c65d5c..cd7fbe5020d8 100644 --- a/mesonbuild/modules/cmake.py +++ b/mesonbuild/modules/cmake.py @@ -14,7 +14,7 @@ from ..cmake import TargetOptions, cmake_defines_to_args from ..dependencies.cmake import CMakeDependency from ..interpreter import SubprojectHolder -from ..interpreter.type_checking import REQUIRED_KW, INSTALL_DIR_KW, INCLUDE_TYPE, NoneType, in_set_validator +from ..interpreter.type_checking import NATIVE_KW, REQUIRED_KW, INSTALL_DIR_KW, INCLUDE_TYPE, NoneType, in_set_validator from ..interpreterbase import ( FeatureNew, @@ -59,6 +59,7 @@ class Subproject(kwargs.ExtractRequired): options: T.Optional[CMakeSubprojectOptions] cmake_options: T.List[str] + native: mesonlib.MachineChoice class TargetKW(TypedDict): @@ -425,6 +426,7 @@ def configure_package_config_file(self, state: ModuleState, args: TYPE_var, kwar @typed_kwargs( 'cmake.subproject', REQUIRED_KW, + NATIVE_KW.evolve(since='1.12.0'), KwargInfo('options', (CMakeSubprojectOptions, NoneType), since='0.55.0'), KwargInfo( 'cmake_options', @@ -445,7 +447,7 @@ def subproject(self, state: ModuleState, args: T.Tuple[str], kwargs_: Subproject 'cmake_options': kwargs_['cmake_options'], 'default_options': {}, 'version': [], - 'for_machine': mesonlib.MachineChoice.HOST, + 'for_machine': kwargs_['native'], } subp = self.interpreter.do_subproject(subp_name, kw, force_method='cmake') if not subp.found(): diff --git a/test cases/cmake/2 advanced/meson.build b/test cases/cmake/2 advanced/meson.build index 43b5f8b30eaa..a8235f71d47e 100644 --- a/test cases/cmake/2 advanced/meson.build +++ b/test cases/cmake/2 advanced/meson.build @@ -1,4 +1,4 @@ -project('cmakeSubTest_advanced', ['c', 'cpp']) +project('cmakeSubTest_advanced', ['c', 'cpp'], meson_version: '>= 1.12.0') dep_test = dependency('ZLIB', method: 'cmake', required: false) if not dep_test.found() @@ -45,3 +45,12 @@ else lib_version_ok = true endif assert(lib_version_ok, f'Shared library version @lib_file_name@ not picked up correctly') + +# Test the "normal" subproject call for the build machine +sub_pro_b = cm.subproject('cmMod', native : true) +sub_dep_b = sub_pro_b.dependency('cmModLib') +sub_sta_b = sub_pro_b.dependency('cmModLibStatic') + +# Build some files +exe1_b = executable('main1_b', ['main.cpp'], dependencies: [sub_dep_b], native : true) +exe2_b = executable('main2_b', ['main.cpp'], dependencies: [sub_sta_b], native : true) From dc559de85a8723eeae049ab5a99b04e638835914 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 7 Mar 2026 22:24:33 +0100 Subject: [PATCH 523/681] interpreter, rust: add native argument to workspace object Signed-off-by: Paolo Bonzini --- docs/markdown/Rust-module.md | 14 ++++++- mesonbuild/modules/rust.py | 73 +++++++++++++++++++++++------------- 2 files changed, 59 insertions(+), 28 deletions(-) diff --git a/docs/markdown/Rust-module.md b/docs/markdown/Rust-module.md index 2475a72dddbc..2b268e051ddc 100644 --- a/docs/markdown/Rust-module.md +++ b/docs/markdown/Rust-module.md @@ -280,7 +280,7 @@ when the workspace was created. ### workspace.package() ```meson -pkg = ws.package([package_name]) +pkg = ws.package([package_name], ...) ``` Returns a package object for the given package member. If empty, returns @@ -290,6 +290,11 @@ Arguments: - `package_name`: (str, optional) Name of the package; not needed for the root package of a workspace +Keyword arguments: +- `native`: (`bool`) Whether the package is compiled for the +host machine (false) or the build machine (true). The argument is +forwarded to Meson by methods such as `executable`, `library`, etc. + Example usage: ```meson rustmod = import('rust') @@ -301,7 +306,7 @@ pkg.executable(install: true) ### workspace.subproject() ```meson -package = ws.subproject(package_name, api) +package = ws.subproject(package_name, api, ...) ``` Returns a `package` object for managing a specific package within the workspace. @@ -310,6 +315,11 @@ Positional arguments: - `package_name`: (`str`) The name of the package to retrieve - `api`: (`str`, optional) The version constraints for the package in Cargo format +Keyword arguments: +- `native`: (`bool`) Whether the subproject is compiled for the +host machine (false) or the build machine (true). Default is false +(proc-macro crates are only built once). + ## Package object The package object returned by `workspace.subproject()` provides methods diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index 11cd3e810b00..011ee6fd2e1e 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -21,7 +21,7 @@ from ..interpreter.decorators import apply_machine_map from ..interpreter.type_checking import ( DEPENDENCIES_KW, LINK_WITH_KW, LINK_WHOLE_KW, SHARED_LIB_KWS, TEST_KWS, TEST_KWS_NO_ARGS, - OUTPUT_KW, INCLUDE_DIRECTORIES, SOURCES_VARARGS, NATIVE_KW, NoneType, in_set_validator, + NATIVE_KW, OUTPUT_KW, INCLUDE_DIRECTORIES, SOURCES_VARARGS, NoneType, in_set_validator, EXECUTABLE_KWS, LIBRARY_KWS, SHARED_MOD_KWS, _BASE_LANG_KW ) from ..interpreterbase import ContainerTypeInfo, InterpreterException, KwargInfo, typed_kwargs, typed_pos_args, noKwargs, noPosargs @@ -73,6 +73,12 @@ class FuncBindgen(TypedDict): language: T.Optional[Literal['c', 'cpp']] bindgen_version: T.List[str] + class FuncSubproject(TypedDict): + native: MachineChoice + + class FuncPackage(TypedDict): + native: MachineChoice + class FuncWorkspace(TypedDict): default_features: T.Optional[bool] features: T.List[str] @@ -160,26 +166,39 @@ def packages_method(self, state: ModuleState, args: T.List, kwargs: TYPE_kwargs) return sorted(package_names) @typed_pos_args('workspace.package', optargs=[str]) - def package_method(self, state: 'ModuleState', args: T.List, kwargs: TYPE_kwargs) -> RustPackage: + @typed_kwargs( + 'workspace.package', + NATIVE_KW.evolve(since='1.12.0')) + def package_method(self, state: 'ModuleState', args: T.List, kwargs: FuncPackage) -> RustPackage: """Returns a package object.""" package_name = args[0] if args else None - return RustPackage(self, self.interpreter.cargo.load_package(self.ws, package_name)) + return RustPackage(state, self, self.interpreter.cargo.load_package(self.ws, package_name), + kwargs['native']) + + def _do_subproject(self, state: ModuleState, pkg: cargo.PackageState, for_machine: MachineChoice) -> None: + # Unless the crate builds as both a procedural macro and something else, + # it can be built just once; the same dependency is used as an override + # for both the host and the build machine. In that case, always build the + # subproject for the build machine to ensure that it is not run twice. + if pkg.manifest.lib.crate_type == ['proc-macro']: + for_machine = state.machine_map.build - def _do_subproject(self, pkg: cargo.PackageState) -> None: kw: _kwargs.DoSubproject = { 'required': True, 'version': None, 'options': None, 'cmake_options': [], 'default_options': {}, - 'for_machine': MachineChoice.HOST, + 'for_machine': for_machine, } subp_name = pkg.get_subproject_name() self.interpreter.do_subproject(subp_name, kw, force_method='cargo') @typed_pos_args('workspace.subproject', str, optargs=[str]) - @noKwargs - def subproject_method(self, state: ModuleState, args: T.Tuple[str, T.Optional[str]], kwargs: TYPE_kwargs) -> RustSubproject: + @typed_kwargs( + 'workspace.subproject', + NATIVE_KW.evolve(since='1.12.0')) + def subproject_method(self, state: ModuleState, args: T.Tuple[str, T.Optional[str]], kwargs: FuncSubproject) -> RustSubproject: """Returns a package object for a subproject package.""" package_name = args[0] pkg = self.interpreter.cargo.resolve_package(package_name, args[1] or '') @@ -189,15 +208,17 @@ def subproject_method(self, state: ModuleState, args: T.Tuple[str, T.Optional[st else: raise MesonException(f'Cargo package "{package_name}" not available') - self._do_subproject(pkg) - return RustSubproject(self, pkg) + self._do_subproject(state, pkg, kwargs['native']) + return RustSubproject(state, self, pkg, kwargs['native']) class RustCrate(ModuleObject): """Abstract base class for Rust crate representations.""" - def __init__(self, rust_ws: RustWorkspace, package: cargo.PackageState) -> None: + def __init__(self, state: ModuleState, rust_ws: RustWorkspace, package: cargo.PackageState, for_machine: MachineChoice) -> None: super().__init__() + + self.for_machine = state.machine_map[for_machine] self.rust_ws = rust_ws self.package = package self.methods.update({ @@ -245,7 +266,7 @@ def features_method(self, state: ModuleState, args: T.List, kwargs: TYPE_kwargs) @noKwargs def rust_args_method(self, state: ModuleState, args: T.List, kwargs: TYPE_kwargs) -> T.List[str]: """Returns rustc arguments for this package.""" - return self.package.get_rustc_args(state.environment, state.subdir, mesonlib.MachineChoice.HOST) + return self.package.get_rustc_args(state.environment, state.subdir, self.for_machine) @noPosargs @noKwargs @@ -263,8 +284,8 @@ def rust_dependency_map_method(self, state: ModuleState, args: T.List, kwargs: T class RustPackage(RustCrate): """Represents a Rust package within a workspace.""" - def __init__(self, rust_ws: RustWorkspace, package: cargo.PackageState) -> None: - super().__init__(rust_ws, package) + def __init__(self, state: ModuleState, rust_ws: RustWorkspace, package: cargo.PackageState, for_machine: MachineChoice) -> None: + super().__init__(state, rust_ws, package, for_machine) if not package.cfg: raise MesonException(f"package {self.package.manifest.package.name}-{self.package.manifest.package.version} not configured") self.methods.update({ @@ -287,7 +308,7 @@ def _dependencies_method(self, state: ModuleState, kwargs: RustPackageDependenci if dep_pkg.ws_subdir != self.rust_ws.subdir or \ is_parent_path(os.path.join(self.rust_ws.subdir, state.subproject_dir), dep_pkg.path): - self.rust_ws._do_subproject(dep_pkg) + self.rust_ws._do_subproject(state, dep_pkg, for_machine) # Get the dependency name for this package (rust or proc-macro ABI) depname = dep_pkg.get_rust_dependency_name() dependency = state.overridden_dependency(depname, for_machine) @@ -314,7 +335,7 @@ def _dependencies_method(self, state: ModuleState, kwargs: RustPackageDependenci KwargInfo('system_dependencies', bool, default=True)) def dependencies_method(self, state: ModuleState, args: T.List, kwargs: RustPackageDependencies) -> T.List[Dependency]: """Returns the dependencies for this package.""" - return self._dependencies_method(state, kwargs, MachineChoice.HOST) + return self._dependencies_method(state, kwargs, self.for_machine) @staticmethod def validate_pos_args(name: str, args: T.Tuple[ @@ -327,7 +348,7 @@ def validate_pos_args(name: str, args: T.Tuple[ return None, args[0] def merge_kw_args(self, state: ModuleState, kwargs: T.Union[RustPackageExecutable, RustPackageLibrary]) -> None: - kwargs.setdefault('native', MachineChoice.HOST) + kwargs.setdefault('native', self.for_machine) deps = kwargs['dependencies'] kwargs['dependencies'] = self._dependencies_method(state, { @@ -407,20 +428,20 @@ def override_dependency_method(self, state: ModuleState, args: T.Tuple[Dependenc dep = args[0] rust_abi = self.package.abi_resolve_default(kwargs['rust_abi']) depname = self.package.get_dependency_name(rust_abi) + assert rust_abi in self.package.supported_abis() - if rust_abi == 'proc-macro': + if state.environment.is_cross_build() and self.package.manifest.lib.crate_type == ['proc-macro']: state.override_dependency(depname, dep, for_machine=MachineChoice.HOST) state.override_dependency(depname, dep, static=False, for_machine=MachineChoice.HOST) - if state.environment.is_cross_build(): - state.override_dependency(depname, dep, for_machine=MachineChoice.BUILD) - state.override_dependency(depname, dep, static=False, for_machine=MachineChoice.BUILD) + state.override_dependency(depname, dep, for_machine=MachineChoice.BUILD) + state.override_dependency(depname, dep, static=False, for_machine=MachineChoice.BUILD) return - state.override_dependency(depname, dep) + state.override_dependency(depname, dep, for_machine=self.for_machine) if self.package.abi_has_static(rust_abi): - state.override_dependency(depname, dep, static=True) + state.override_dependency(depname, dep, static=True, for_machine=self.for_machine) if self.package.abi_has_shared(rust_abi): - state.override_dependency(depname, dep, static=False) + state.override_dependency(depname, dep, static=False, for_machine=self.for_machine) @typed_pos_args('package.library', optargs=[(str, StructuredSources), StructuredSources]) @typed_kwargs( @@ -532,8 +553,8 @@ def executable_method(self, state: 'ModuleState', args: T.Tuple[ class RustSubproject(RustCrate): """Represents a Cargo subproject.""" - def __init__(self, rust_ws: RustWorkspace, package: cargo.PackageState) -> None: - super().__init__(rust_ws, package) + def __init__(self, state: ModuleState, rust_ws: RustWorkspace, package: cargo.PackageState, for_machine: MachineChoice) -> None: + super().__init__(state, rust_ws, package, for_machine) self.methods.update({ 'dependency': self.dependency_method, }) @@ -544,7 +565,7 @@ def __init__(self, rust_ws: RustWorkspace, package: cargo.PackageState) -> None: def dependency_method(self, state: ModuleState, args: T.List, kwargs: FuncDependency) -> Dependency: """Returns dependency for the package with the given ABI.""" depname = self.package.get_dependency_name(kwargs['rust_abi']) - return state.overridden_dependency(depname) + return state.overridden_dependency(depname, for_machine=self.for_machine) class RustModule(ExtensionModule): From 65ffb1d07b66f22a27fc1f5e7c36cc9c6b3ceda2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 19 May 2026 14:58:50 +0200 Subject: [PATCH 524/681] cargo: store machine-specific configuration Make the PackageConfiguration a PerMachine object. This makes it possible to do dependency resolution separately for build- and/or host-side libraries. For now, all users only fill in or consume the host side, but that will change. --- mesonbuild/cargo/interpreter.py | 31 ++++++++++++++++--------------- mesonbuild/modules/rust.py | 23 ++++++++++++----------- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index 7e940573d310..714508ee5635 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -26,7 +26,7 @@ from .manifest import Manifest, CargoLock, CargoLockPackage, Workspace, fixup_meson_varname from ..mesonlib import ( is_parent_path, lazy_property, MesonException, MachineChoice, - unique_list, SubProject, + PerMachine, unique_list, SubProject, ) from .. import coredata, mlog from ..wrap.wrap import PackageDefinition @@ -91,8 +91,10 @@ class PackageState: # If this package is member of a workspace. ws_subdir: T.Optional[str] = None ws_member: T.Optional[str] = None - # Package configuration state - cfg: T.Optional[PackageConfiguration] = None + # Per-machine configuration state + cfg: PerMachine[T.Optional[PackageConfiguration]] = dataclasses.field( + default_factory=lambda: PerMachine(None, None) + ) # Subproject name as known to the wrap resolver (may differ from the # meson dep name for git sources, where the wrap is named after the # git directory rather than the crate name + api version). @@ -185,8 +187,7 @@ def get_rustc_args(self, environment: Environment, subdir: str, machine: Machine machine = MachineChoice.HOST rustc = T.cast('RustCompiler', environment.coredata.compilers[machine]['rust']) - - cfg = self.cfg + cfg = self.cfg[MachineChoice.HOST] args: T.List[str] = [] args.extend(self.get_lint_args(rustc)) @@ -403,7 +404,7 @@ def _process_member(member: str) -> None: if member in processed_members: return pkg = ws.packages[member] - cfg = pkg.cfg + cfg = pkg.cfg[MachineChoice.HOST] if not cfg: raise MesonException(f'Package {pkg.manifest.package.name!r} is not enabled for this build ' 'configuration. Maybe you forgot to enable a Cargo feature, or to check ' @@ -544,10 +545,10 @@ def _fetch_package_from_subproject(self, package_name: str, subp_name: str) -> P def _prepare_package(self, pkg: PackageState) -> None: key = PackageKey(pkg.manifest.package.name, pkg.manifest.package.api) assert key in self.packages - if pkg.cfg: + if pkg.cfg[MachineChoice.HOST]: return - pkg.cfg = PackageConfiguration() + pkg.cfg[MachineChoice.HOST] = PackageConfiguration() # Merge target specific dependencies that are enabled cfgs = self._get_cfgs(MachineChoice.HOST) for condition, dependencies in pkg.manifest.target.items(): @@ -572,7 +573,7 @@ def _prepare_package(self, pkg: PackageState) -> None: if not dep.optional: self._add_dependency(pkg, depname) - def _dep_package(self, pkg: PackageState, dep: Dependency) -> PackageState: + def _dep_package(self, pkg: PackageState, dep: Dependency, cfg: PackageConfiguration) -> PackageState: if dep.path: ws = self.workspaces[pkg.ws_subdir] dep_member = os.path.normpath(os.path.join(pkg.ws_member, dep.path)) @@ -594,8 +595,8 @@ def _dep_package(self, pkg: PackageState, dep: Dependency) -> PackageState: dep.update_version(f'={dep_pkg.manifest.package.version}') dep_key = PackageKey(dep.package, dep.api) - pkg.cfg.dep_packages.setdefault(dep_key, dep_pkg) - assert pkg.cfg.dep_packages[dep_key] == dep_pkg + cfg.dep_packages.setdefault(dep_key, dep_pkg) + assert cfg.dep_packages[dep_key] == dep_pkg return dep_pkg def _load_manifest(self, subdir: str, workspace: T.Optional[Workspace] = None, member_path: str = '') -> T.Tuple[T.Union[Manifest, Workspace], bool]: @@ -620,7 +621,7 @@ def _load_manifest(self, subdir: str, workspace: T.Optional[Workspace] = None, m return manifest_, False def _add_dependency(self, pkg: PackageState, depname: str) -> None: - cfg = pkg.cfg + cfg = pkg.cfg[MachineChoice.HOST] if depname in cfg.required_deps: return dep = pkg.manifest.dependencies.get(depname) @@ -628,7 +629,7 @@ def _add_dependency(self, pkg: PackageState, depname: str) -> None: # It could be build/dev/target dependency. Just ignore it. return cfg.required_deps.add(depname) - dep_pkg = self._dep_package(pkg, dep) + dep_pkg = self._dep_package(pkg, dep, cfg) self._prepare_package(dep_pkg) if dep.default_features: self._enable_feature(dep_pkg, 'default') @@ -638,7 +639,7 @@ def _add_dependency(self, pkg: PackageState, depname: str) -> None: self._enable_feature(dep_pkg, f) def _enable_feature(self, pkg: PackageState, feature: str) -> None: - cfg = pkg.cfg + cfg = pkg.cfg[MachineChoice.HOST] if feature in cfg.features: return cfg.features.add(feature) @@ -653,7 +654,7 @@ def _enable_feature(self, pkg: PackageState, feature: str) -> None: self._add_dependency(pkg, depname) if depname in cfg.required_deps: dep = pkg.manifest.dependencies[depname] - dep_pkg = self._dep_package(pkg, dep) + dep_pkg = self._dep_package(pkg, dep, cfg) self._enable_feature(dep_pkg, dep_f) else: # This feature will be enabled only if that dependency diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index 011ee6fd2e1e..a45e1740607c 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -33,7 +33,7 @@ from . import ModuleState from .. import cargo from ..build import ExecutableKeywordArguments, GeneratedTypes, IncludeDirs, LinkableTargetTypes, CommandTypes - from ..cargo.interpreter import RUST_ABI + from ..cargo.interpreter import RUST_ABI, PackageConfiguration from ..compilers.compilers import Language from ..compilers.rust import RustCompiler from ..dependencies import ExternalLibrary @@ -162,7 +162,7 @@ def packages_method(self, state: ModuleState, args: T.List, kwargs: TYPE_kwargs) """Returns list of package names in workspace.""" package_names = [pkg.manifest.package.name for pkg in self.ws.packages.values() - if pkg.cfg] + if pkg.cfg.host or pkg.cfg.build] return sorted(package_names) @typed_pos_args('workspace.package', optargs=[str]) @@ -232,6 +232,10 @@ def __init__(self, state: ModuleState, rust_ws: RustWorkspace, package: cargo.Pa 'rust_dependency_map': self.rust_dependency_map_method, # type: ignore[dict-item] }) + @property + def cfg(self) -> PackageConfiguration: + return self.package.cfg[MachineChoice.HOST] + @noPosargs @noKwargs def name_method(self, state: ModuleState, args: T.List, kwargs: TYPE_kwargs) -> str: @@ -260,7 +264,7 @@ def all_features_method(self, state: ModuleState, args: T.List, kwargs: TYPE_kwa @noKwargs def features_method(self, state: ModuleState, args: T.List, kwargs: TYPE_kwargs) -> T.List[str]: """Returns chosen features for specific package.""" - return sorted(list(self.package.cfg.features)) + return sorted(list(self.cfg.features)) @noPosargs @noKwargs @@ -278,7 +282,7 @@ def env_method(self, state: ModuleState, args: T.List, kwargs: TYPE_kwargs) -> T @noKwargs def rust_dependency_map_method(self, state: ModuleState, args: T.List, kwargs: TYPE_kwargs) -> T.Dict[str, str]: """Returns rust dependency mapping for this package.""" - return self.package.cfg.get_dependency_map(self.package.manifest) + return self.cfg.get_dependency_map(self.package.manifest) class RustPackage(RustCrate): @@ -286,7 +290,7 @@ class RustPackage(RustCrate): def __init__(self, state: ModuleState, rust_ws: RustWorkspace, package: cargo.PackageState, for_machine: MachineChoice) -> None: super().__init__(state, rust_ws, package, for_machine) - if not package.cfg: + if not package.cfg[MachineChoice.HOST]: raise MesonException(f"package {self.package.manifest.package.name}-{self.package.manifest.package.version} not configured") self.methods.update({ 'dependencies': self.dependencies_method, @@ -300,10 +304,8 @@ def __init__(self, state: ModuleState, rust_ws: RustWorkspace, package: cargo.Pa def _dependencies_method(self, state: ModuleState, kwargs: RustPackageDependencies, for_machine: MachineChoice) -> T.List[Dependency]: dependencies: T.List[Dependency] = [] - cfg = self.package.cfg - if kwargs['dependencies']: - for dep_key, dep_pkg in cfg.dep_packages.items(): + for dep_key, dep_pkg in self.cfg.dep_packages.items(): if dep_pkg.manifest.lib: if dep_pkg.ws_subdir != self.rust_ws.subdir or \ is_parent_path(os.path.join(self.rust_ws.subdir, state.subproject_dir), @@ -319,7 +321,7 @@ def _dependencies_method(self, state: ModuleState, kwargs: RustPackageDependenci if kwargs['system_dependencies']: for name, sys_dep in self.package.manifest.system_dependencies.items(): - if sys_dep.enabled(cfg.features): + if sys_dep.enabled(self.cfg.features): # System dependencies use the original dependency name from Cargo.toml dependency = state.dependency(sys_dep.name, native=(for_machine == MachineChoice.BUILD), required=not sys_dep.optional, @@ -359,8 +361,7 @@ def merge_kw_args(self, state: ModuleState, kwargs: T.Union[RustPackageExecutabl kwargs['dependencies'].extend(deps) depmap = kwargs['rust_dependency_map'] - kwargs['rust_dependency_map'] = \ - self.package.cfg.get_dependency_map(self.package.manifest) + kwargs['rust_dependency_map'] = self.cfg.get_dependency_map(self.package.manifest) kwargs['rust_dependency_map'].update(depmap) rust_args = kwargs['rust_args'] From e6bb05783983cbd5db326153387be0216e6255d7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 23 Oct 2025 16:55:47 +0200 Subject: [PATCH 525/681] cargo: implement machine-specific dependency tracking Change the dependency resolution code to take a MachineChoice as an argument. Create build- and/or host-side configurations through the machines_from() method in Manifest. --- .../subproject_and_override_program_native.md | 3 + mesonbuild/cargo/interpreter.py | 95 +++++++++++-------- mesonbuild/cargo/manifest.py | 29 +++++- mesonbuild/interpreter/interpreter.py | 1 + mesonbuild/modules/rust.py | 21 ++-- .../34 rust.workspace workspace/meson.build | 2 +- 6 files changed, 105 insertions(+), 46 deletions(-) diff --git a/docs/markdown/snippets/subproject_and_override_program_native.md b/docs/markdown/snippets/subproject_and_override_program_native.md index 489cf9c2ba1e..75142524b5b7 100644 --- a/docs/markdown/snippets/subproject_and_override_program_native.md +++ b/docs/markdown/snippets/subproject_and_override_program_native.md @@ -11,3 +11,6 @@ two versions of the same program, which have different outputs based on the machine they are for. For backwards compatibility reasons the default is for the host machine. Inside a native subproject the host and build machine will both be the build machine. + +Cargo workspaces use this feature automatically when building procedural macro +crates and their dependencies. diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index 714508ee5635..523ebc6eb9e6 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -187,7 +187,7 @@ def get_rustc_args(self, environment: Environment, subdir: str, machine: Machine machine = MachineChoice.HOST rustc = T.cast('RustCompiler', environment.coredata.compilers[machine]['rust']) - cfg = self.cfg[MachineChoice.HOST] + cfg = self.cfg[machine] args: T.List[str] = [] args.extend(self.get_lint_args(rustc)) @@ -299,6 +299,10 @@ def __init__(self, env: Environment, subdir: str, subprojects_dir: str) -> None: self.environment.wrap_resolver.merge_wraps(self.cargolock.wraps) self.build_def_files.append(filename) + @property + def is_cross(self) -> bool: + return self.environment.is_cross_build() + @property def features(self) -> T.List[str]: """Get the features list. Once read, it cannot be modified.""" @@ -329,9 +333,10 @@ def load_workspace(self, subdir: str, extra_members: T.Optional[T.List[str]]) -> def _prepare_entry_point(self, ws: WorkspaceState) -> None: pkgs = [self._require_workspace_member(ws, m) for m in ws.workspace.default_members] for pkg in pkgs: - self._prepare_package(pkg) - for feature in self.features: - self._enable_feature(pkg, feature) + for machine in pkg.manifest.machines_from(MachineChoice.HOST, bin=True, is_cross=self.is_cross): + self._prepare_package(pkg, machine) + for feature in self.features: + self._enable_feature(pkg, feature, machine) def load_package(self, ws: WorkspaceState, package_name: T.Optional[str]) -> PackageState: if package_name is None: @@ -369,9 +374,15 @@ def interpret_package(self, manifest: Manifest, build: builder.Builder, subdir: return build.block(ast) def _create_package(self, pkg: PackageState, build: builder.Builder, subdir: str) -> T.List[mparser.BaseNode]: + # proc_macro automatically adds native: true; passing "native: true" + # to cargo_ws.package() is only needed to query the features and + # pass them to meson/meson.build. + native = pkg.manifest.lib.crate_type == ['proc-macro'] ast: T.List[mparser.BaseNode] = [ build.assign(build.method('package', build.identifier('cargo_ws'), - [build.string(pkg.manifest.package.name)]), 'pkg_obj'), + [build.string(pkg.manifest.package.name)], + {'native': build.bool(native)}), + 'pkg_obj'), build.assign(build.method('features', build.identifier('pkg_obj')), 'features'), build.function('message', [ build.string('Enabled features:'), @@ -404,16 +415,22 @@ def _process_member(member: str) -> None: if member in processed_members: return pkg = ws.packages[member] - cfg = pkg.cfg[MachineChoice.HOST] - if not cfg: + # Process dependencies for all configured machines + found = False + for machine in MachineChoice: + cfg = pkg.cfg[machine] + if not cfg: + continue + for depname in cfg.required_deps: + dep = pkg.manifest.dependencies[depname] + if dep.path: + dep_member = os.path.normpath(os.path.join(pkg.ws_member, dep.path)) + _process_member(dep_member) + found = True + if not found: raise MesonException(f'Package {pkg.manifest.package.name!r} is not enabled for this build ' 'configuration. Maybe you forgot to enable a Cargo feature, or to check ' 'a Meson option?') - for depname in cfg.required_deps: - dep = pkg.manifest.dependencies[depname] - if dep.path: - dep_member = os.path.normpath(os.path.join(pkg.ws_member, dep.path)) - _process_member(dep_member) if member == '.': ast.extend(self._create_package(pkg, build, subdir)) elif is_parent_path(self.subprojects_dir, member): @@ -542,17 +559,17 @@ def _fetch_package_from_subproject(self, package_name: str, subp_name: str) -> P pkg.subproject_name = subp_name return pkg - def _prepare_package(self, pkg: PackageState) -> None: + def _prepare_package(self, pkg: PackageState, machine: MachineChoice) -> None: key = PackageKey(pkg.manifest.package.name, pkg.manifest.package.api) assert key in self.packages - if pkg.cfg[MachineChoice.HOST]: - return + if pkg.cfg[machine] is not None: + return # Already prepared for this machine - pkg.cfg[MachineChoice.HOST] = PackageConfiguration() - # Merge target specific dependencies that are enabled - cfgs = self._get_cfgs(MachineChoice.HOST) + pkg.cfg[machine] = PackageConfiguration() + # Merge target-specific dependencies that are enabled for this machine + target_cfgs = self._get_cfgs(machine) for condition, dependencies in pkg.manifest.target.items(): - if eval_cfg(condition, cfgs): + if eval_cfg(condition, target_cfgs): pkg.manifest.dependencies.update(dependencies) # If you specify the optional dependency with the dep: prefix anywhere in the [features] @@ -568,10 +585,10 @@ def _prepare_package(self, pkg: PackageState) -> None: pkg.manifest.features[name].append(f'dep:{name}') deps.add(name) - # Fetch required dependencies recursively. + # Fetch required dependencies recursively for this machine for depname, dep in pkg.manifest.dependencies.items(): if not dep.optional: - self._add_dependency(pkg, depname) + self._add_dependency(pkg, depname, machine) def _dep_package(self, pkg: PackageState, dep: Dependency, cfg: PackageConfiguration) -> PackageState: if dep.path: @@ -620,8 +637,8 @@ def _load_manifest(self, subdir: str, workspace: T.Optional[Workspace] = None, m self.manifests[subdir] = manifest_ return manifest_, False - def _add_dependency(self, pkg: PackageState, depname: str) -> None: - cfg = pkg.cfg[MachineChoice.HOST] + def _add_dependency(self, pkg: PackageState, depname: str, machine: MachineChoice) -> None: + cfg = pkg.cfg[machine] if depname in cfg.required_deps: return dep = pkg.manifest.dependencies.get(depname) @@ -630,16 +647,18 @@ def _add_dependency(self, pkg: PackageState, depname: str) -> None: return cfg.required_deps.add(depname) dep_pkg = self._dep_package(pkg, dep, cfg) - self._prepare_package(dep_pkg) - if dep.default_features: - self._enable_feature(dep_pkg, 'default') - for f in dep.features: - self._enable_feature(dep_pkg, f) - for f in cfg.optional_deps_features[depname]: - self._enable_feature(dep_pkg, f) - - def _enable_feature(self, pkg: PackageState, feature: str) -> None: - cfg = pkg.cfg[MachineChoice.HOST] + # Use machines_from() to determine which machines the dependency needs + for dep_machine in dep_pkg.manifest.machines_from(machine, self.is_cross): + self._prepare_package(dep_pkg, dep_machine) + if dep.default_features: + self._enable_feature(dep_pkg, 'default', dep_machine) + for f in dep.features: + self._enable_feature(dep_pkg, f, dep_machine) + for f in cfg.optional_deps_features[depname]: + self._enable_feature(dep_pkg, f, dep_machine) + + def _enable_feature(self, pkg: PackageState, feature: str, machine: MachineChoice) -> None: + cfg = pkg.cfg[machine] if feature in cfg.features: return cfg.features.add(feature) @@ -651,19 +670,21 @@ def _enable_feature(self, pkg: PackageState, feature: str) -> None: if depname[-1] == '?': depname = depname[:-1] else: - self._add_dependency(pkg, depname) + self._add_dependency(pkg, depname, machine) if depname in cfg.required_deps: dep = pkg.manifest.dependencies[depname] dep_pkg = self._dep_package(pkg, dep, cfg) - self._enable_feature(dep_pkg, dep_f) + # Use machines_from() to determine which machines the dependency needs + for dep_machine in dep_pkg.manifest.machines_from(machine, self.is_cross): + self._enable_feature(dep_pkg, dep_f, dep_machine) else: # This feature will be enabled only if that dependency # is later added. cfg.optional_deps_features[depname].add(dep_f) elif f.startswith('dep:'): - self._add_dependency(pkg, f[4:]) + self._add_dependency(pkg, f[4:], machine) else: - self._enable_feature(pkg, f) + self._enable_feature(pkg, f, machine) def has_check_cfg(self, machine: MachineChoice) -> bool: if not self.environment.is_cross_build(): diff --git a/mesonbuild/cargo/manifest.py b/mesonbuild/cargo/manifest.py index a568e0e04288..8afd5fe7c2a2 100644 --- a/mesonbuild/cargo/manifest.py +++ b/mesonbuild/cargo/manifest.py @@ -15,7 +15,7 @@ from . import version -from ..mesonlib import MesonException, lazy_property +from ..mesonlib import MesonException, lazy_property, MachineChoice from .. import mlog if T.TYPE_CHECKING: @@ -539,6 +539,33 @@ class Manifest: def __post_init__(self) -> None: self.features.setdefault('default', []) + def machines_from(self, parent_machine: MachineChoice, is_cross: bool, + bin: bool = False) -> T.Iterable[MachineChoice]: + """Return the machines this manifest should be built for based on the machine + for the package that depended on this one.""" + assert is_cross or parent_machine == MachineChoice.HOST + if self.lib is None: + if bin and self.bin: + yield parent_machine + return + + if not is_cross: + yield parent_machine + return + + need_build = False + need_host = False + for crate_type in self.lib.crate_type: + if crate_type == 'proc-macro' or parent_machine == MachineChoice.BUILD: + need_build = True + else: + need_host = True + + if need_build: + yield MachineChoice.BUILD + if need_host: + yield MachineChoice.HOST + @lazy_property def system_dependencies(self) -> T.Dict[str, SystemDependency]: return {k: SystemDependency.from_raw(k, v) for k, v in self.package.metadata.get('system-deps', {}).items()} diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index e3a6915072da..2b648d6976c9 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1133,6 +1133,7 @@ def _do_subproject_cargo(self, subp_name: SubProject, subdir: str, mlog.warning('Cargo subproject is an experimental feature and has no backwards compatibility guarantees.', once=True, location=self.current_node) self.add_languages(['rust'], True, MachineChoice.HOST) + self.add_languages(['rust'], True, MachineChoice.BUILD) with mlog.nested(subp_name): try: cargo_int = self.cargo or cargo.Interpreter(self.environment, subdir, self.subproject_dir) diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index a45e1740607c..72f4ff422871 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -234,7 +234,7 @@ def __init__(self, state: ModuleState, rust_ws: RustWorkspace, package: cargo.Pa @property def cfg(self) -> PackageConfiguration: - return self.package.cfg[MachineChoice.HOST] + return self.package.cfg[self.for_machine] @noPosargs @noKwargs @@ -290,8 +290,9 @@ class RustPackage(RustCrate): def __init__(self, state: ModuleState, rust_ws: RustWorkspace, package: cargo.PackageState, for_machine: MachineChoice) -> None: super().__init__(state, rust_ws, package, for_machine) - if not package.cfg[MachineChoice.HOST]: - raise MesonException(f"package {self.package.manifest.package.name}-{self.package.manifest.package.version} not configured") + if not package.cfg[self.for_machine]: + raise MesonException(f"package {self.package.manifest.package.name}-{self.package.manifest.package.version} not configured for {self.for_machine}") + self.methods.update({ 'dependencies': self.dependencies_method, 'library': self.library_method, @@ -304,8 +305,10 @@ def __init__(self, state: ModuleState, rust_ws: RustWorkspace, package: cargo.Pa def _dependencies_method(self, state: ModuleState, kwargs: RustPackageDependencies, for_machine: MachineChoice) -> T.List[Dependency]: dependencies: T.List[Dependency] = [] + cfg = self.package.cfg[for_machine] + if kwargs['dependencies']: - for dep_key, dep_pkg in self.cfg.dep_packages.items(): + for dep_key, dep_pkg in cfg.dep_packages.items(): if dep_pkg.manifest.lib: if dep_pkg.ws_subdir != self.rust_ws.subdir or \ is_parent_path(os.path.join(self.rust_ws.subdir, state.subproject_dir), @@ -321,7 +324,7 @@ def _dependencies_method(self, state: ModuleState, kwargs: RustPackageDependenci if kwargs['system_dependencies']: for name, sys_dep in self.package.manifest.system_dependencies.items(): - if sys_dep.enabled(self.cfg.features): + if sys_dep.enabled(cfg.features): # System dependencies use the original dependency name from Cargo.toml dependency = state.dependency(sys_dep.name, native=(for_machine == MachineChoice.BUILD), required=not sys_dep.optional, @@ -351,6 +354,9 @@ def validate_pos_args(name: str, args: T.Tuple[ def merge_kw_args(self, state: ModuleState, kwargs: T.Union[RustPackageExecutable, RustPackageLibrary]) -> None: kwargs.setdefault('native', self.for_machine) + cfg = self.package.cfg[kwargs['native']] + if not cfg: + raise MesonException(f"package {self.package.manifest.package.name}-{self.package.manifest.package.version} not configured for {self.for_machine}") deps = kwargs['dependencies'] kwargs['dependencies'] = self._dependencies_method(state, { @@ -361,7 +367,7 @@ def merge_kw_args(self, state: ModuleState, kwargs: T.Union[RustPackageExecutabl kwargs['dependencies'].extend(deps) depmap = kwargs['rust_dependency_map'] - kwargs['rust_dependency_map'] = self.cfg.get_dependency_map(self.package.manifest) + kwargs['rust_dependency_map'] = cfg.get_dependency_map(self.package.manifest) kwargs['rust_dependency_map'].update(depmap) rust_args = kwargs['rust_args'] @@ -415,7 +421,8 @@ def _library_method(self, state: ModuleState, args: T.Tuple[ def _proc_macro_method(self, state: 'ModuleState', args: T.Tuple[ T.Optional[T.Union[str, StructuredSources]], T.Optional[StructuredSources]], kwargs: RustPackageLibrary) -> SharedLibrary: - kwargs['native'] = MachineChoice.BUILD + if state.environment.is_cross_build(): + kwargs['native'] = MachineChoice.BUILD kwargs['rust_abi'] = None kwargs['rust_crate_type'] = 'proc-macro' kwargs['rust_args'] = kwargs['rust_args'] + ['--extern', 'proc_macro'] diff --git a/test cases/rust/34 rust.workspace workspace/meson.build b/test cases/rust/34 rust.workspace workspace/meson.build index fbafc4a1d9f0..b838c3a0d164 100644 --- a/test cases/rust/34 rust.workspace workspace/meson.build +++ b/test cases/rust/34 rust.workspace workspace/meson.build @@ -37,7 +37,7 @@ endtestcase testcase expect_error('workspace member "nonexistent" not found') cargo_ws.package('nonexistent') endtestcase -testcase expect_error('package another-0.1.0 not configured') +testcase expect_error('package another-0.1.0 not configured for host machine') cargo_ws.package('another') endtestcase From 891f276550eb1e45499e995c198e7bebc767b1cb Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 15 Apr 2026 09:46:42 +0200 Subject: [PATCH 526/681] cargo: assert that proc-macro crates are built once Signed-off-by: Paolo Bonzini --- mesonbuild/cargo/interpreter.py | 3 +++ mesonbuild/modules/rust.py | 2 ++ 2 files changed, 5 insertions(+) diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index 523ebc6eb9e6..cf57ef0e5d8d 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -115,6 +115,9 @@ def library_name(self, lib_type: RUST_ABI = 'rust') -> str: return name return f'{name}+{self.manifest.package.api.replace(".", "_")}' + def has_both_machines(self) -> bool: + return bool(self.cfg.host and self.cfg.build) + def get_env_dict(self, environment: Environment, subdir: str) -> T.Dict[str, str]: """Get environment variables for this package.""" # Common variables for build.rs and crates diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index 72f4ff422871..fd3c4b60f66f 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -181,6 +181,7 @@ def _do_subproject(self, state: ModuleState, pkg: cargo.PackageState, for_machin # for both the host and the build machine. In that case, always build the # subproject for the build machine to ensure that it is not run twice. if pkg.manifest.lib.crate_type == ['proc-macro']: + assert not pkg.has_both_machines() for_machine = state.machine_map.build kw: _kwargs.DoSubproject = { @@ -439,6 +440,7 @@ def override_dependency_method(self, state: ModuleState, args: T.Tuple[Dependenc assert rust_abi in self.package.supported_abis() if state.environment.is_cross_build() and self.package.manifest.lib.crate_type == ['proc-macro']: + assert not self.package.has_both_machines() state.override_dependency(depname, dep, for_machine=MachineChoice.HOST) state.override_dependency(depname, dep, static=False, for_machine=MachineChoice.HOST) state.override_dependency(depname, dep, for_machine=MachineChoice.BUILD) From 142f44a3c4d71ed68fa473ec2904e02b7ce4a81a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 12 Sep 2025 12:49:00 +0200 Subject: [PATCH 527/681] cargo: vary crate names per machine When a crate name is compiled for both the build and the host machine, give it two different names to avoid conflicts. This is not needed for subprojects, but it is still necessary for packages that are part of the toplevel workspace. Signed-off-by: Paolo Bonzini --- mesonbuild/cargo/interpreter.py | 13 ++++++++----- mesonbuild/modules/rust.py | 6 +++--- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index cf57ef0e5d8d..c1b0bdee48f8 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -58,6 +58,7 @@ def _extra_deps_varname() -> str: @dataclasses.dataclass class PackageConfiguration: """Configuration for a package during dependency resolution.""" + for_machine: MachineChoice features: T.Set[str] = dataclasses.field(default_factory=set) required_deps: T.Set[str] = dataclasses.field(default_factory=set) optional_deps_features: T.Dict[str, T.Set[str]] = dataclasses.field(default_factory=lambda: collections.defaultdict(set)) @@ -78,7 +79,7 @@ def get_dependency_map(self, manifest: Manifest) -> T.Dict[str, str]: dep = manifest.dependencies[name] dep_key = PackageKey(dep.package, dep.api) dep_pkg = self.dep_packages[dep_key] - dep_lib_name = dep_pkg.library_name() + dep_lib_name = dep_pkg.library_name(self.for_machine) dep_crate_name = name if name != dep.package else dep_pkg.manifest.lib.name dependency_map[dep_lib_name] = dep_crate_name return dependency_map @@ -106,14 +107,15 @@ def path(self) -> T.Optional[str]: return None return os.path.normpath(os.path.join(self.ws_subdir, self.ws_member)) - def library_name(self, lib_type: RUST_ABI = 'rust') -> str: + def library_name(self, machine: MachineChoice = MachineChoice.HOST, lib_type: RUST_ABI = 'rust') -> str: # Add the API version to the library name to avoid conflicts when multiple # versions of the same crate are used. The Ninja backend removed everything # after the + to form the crate name. name = fixup_meson_varname(self.manifest.package.name) + suffix = '+build' if machine == MachineChoice.BUILD else '' if lib_type == 'c': return name - return f'{name}+{self.manifest.package.api.replace(".", "_")}' + return f'{name}+{self.manifest.package.api.replace(".", "_")}{suffix}' def has_both_machines(self) -> bool: return bool(self.cfg.host and self.cfg.build) @@ -568,7 +570,7 @@ def _prepare_package(self, pkg: PackageState, machine: MachineChoice) -> None: if pkg.cfg[machine] is not None: return # Already prepared for this machine - pkg.cfg[machine] = PackageConfiguration() + pkg.cfg[machine] = PackageConfiguration(for_machine=machine) # Merge target-specific dependencies that are enabled for this machine target_cfgs = self._get_cfgs(machine) for condition, dependencies in pkg.manifest.target.items(): @@ -778,8 +780,9 @@ def _create_meson_subdir(self, build: builder.Builder) -> T.List[mparser.BaseNod def _create_lib(self, pkg: PackageState, build: builder.Builder, subdir: str, lib_type: RUST_ABI) -> T.List[mparser.BaseNode]: + machine = MachineChoice.BUILD if lib_type == 'proc-macro' else MachineChoice.HOST posargs: T.List[mparser.BaseNode] = [ - build.string(pkg.library_name(lib_type)), + build.string(pkg.library_name(machine, lib_type)), ] kwargs: T.Dict[str, mparser.BaseNode] = { diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index fd3c4b60f66f..c4c763bb8efd 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -387,6 +387,8 @@ def _library_method(self, state: ModuleState, args: T.Tuple[ if not self.package.manifest.lib: raise MesonException("no [lib] section in Cargo package") + self.merge_kw_args(state, kwargs) + sources: T.Union[StructuredSources, str] tgt_name, sources = tgt_args if not tgt_name: @@ -395,14 +397,12 @@ def _library_method(self, state: ModuleState, args: T.Tuple[ rust_abi = 'rust' if kwargs['rust_crate_type'] in {'lib', 'rlib', 'dylib', 'proc-macro'} else 'c' else: rust_abi = kwargs['rust_abi'] - tgt_name = self.package.library_name(rust_abi) + tgt_name = self.package.library_name(kwargs['native'], rust_abi) if not sources: sources = os.path.relpath(os.path.join(self.package.path, self.package.manifest.lib.path), state.subdir) lib_args: T.Tuple[str, SourcesVarargsType] = (tgt_name, [sources]) - self.merge_kw_args(state, kwargs) - if shared_mod: return state._interpreter.build_target(state.current_node, lib_args, T.cast('_kwargs.SharedModule', kwargs), From b6f1863c09a689024f1195dfbbcd3e70b353af96 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 11 Jun 2026 09:23:01 -0700 Subject: [PATCH 528/681] internaltests: Fix test_program_version type (and use subtests) This was passing a `list[list[str] | str]` to `ExternalProgram(command)`, which is invalid. --- unittests/internaltests.py | 47 ++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/unittests/internaltests.py b/unittests/internaltests.py index d1ebb4af5ced..4229769b9e01 100644 --- a/unittests/internaltests.py +++ b/unittests/internaltests.py @@ -773,33 +773,36 @@ def _call_pkgbin(self, args, env=None): for lib in ('pthread', 'm', 'c', 'dl', 'rt'): self.assertNotIn(f'lib{lib}.a', link_arg, msg=link_args) - def test_program_version(self): + def test_program_version(self) -> None: with tempfile.TemporaryDirectory() as tmpdir: script_path = Path(tmpdir) / 'script.py' script_path.write_text('import sys\nprint(sys.argv[1])\n', encoding='utf-8') script_path.chmod(0o755) - for output, expected in { - '': None, - '1': None, - '1.2.4': '1.2.4', - '1 1.2.4': '1.2.4', - 'foo version 1.2.4': '1.2.4', - 'foo 1.2.4.': '1.2.4', - 'foo 1.2.4': '1.2.4', - 'foo 1.2.4 bar': '1.2.4', - 'foo 10.0.0': '10.0.0', - '50 5.4.0': '5.4.0', - 'This is perl 5, version 40, subversion 0 (v5.40.0)': '5.40.0', - 'git version 2.48.0.rc1': '2.48.0', - }.items(): - prog = ExternalProgram('script', command=[python_command, str(script_path), output], silent=True) - - if expected is None: - with self.assertRaisesRegex(MesonException, 'Could not find a version number'): - prog.get_version() - else: - self.assertEqual(prog.get_version(), expected) + inputs: list[tuple[str, str | None]] = [ + ('', None), + ('1', None), + ('1.2.4', '1.2.4'), + ('1 1.2.4', '1.2.4'), + ('foo version 1.2.4', '1.2.4'), + ('foo 1.2.4.', '1.2.4'), + ('foo 1.2.4', '1.2.4'), + ('foo 1.2.4 bar', '1.2.4'), + ('foo 10.0.0', '10.0.0'), + ('50 5.4.0', '5.4.0'), + ('This is perl 5, version 40, subversion 0 (v5.40.0)', '5.40.0'), + ('git version 2.48.0.rc1', '2.48.0'), + ] + + for output, expected in inputs: + prog = ExternalProgram('script', command=python_command + [str(script_path), output], silent=True) + + with self.subTest(output=output, expected=expected): + if expected is None: + with self.assertRaisesRegex(MesonException, 'Could not find a version number'): + prog.get_version() + else: + self.assertEqual(prog.get_version(), expected) def test_version_compare(self): comparefunc = mesonbuild.mesonlib.version_compare_many From 46c4034df36472ce0302b66c118441406441b6e4 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 10 Jun 2026 14:36:44 -0700 Subject: [PATCH 529/681] compilers: Add a method to get a static linker or error Sometimes we just need a static_linker, and not having one is an error. This removes a use of `assert` to help mypy, and makes the code clearer. --- mesonbuild/backend/backends.py | 4 +--- mesonbuild/build.py | 6 ++++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 05b70cd66106..fd838cb4a7cf 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -749,9 +749,7 @@ def determine_linker_and_stdlib_args(self, target: build.BuildTarget) -> T.Tuple Otherwise, we query the target for the dynamic linker. ''' if isinstance(target, build.StaticLibrary): - static_linker = self.build.static_linker[target.for_machine] - assert static_linker is not None, "Compiler.needs_static_linker does not match backend logic" - return static_linker, [] + return self.build.get_static_linker(target), [] l, stdlib_args = target.get_clink_dynamic_linker_and_stdlibs() return l, stdlib_args diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 70aab9340637..a008605e3bd8 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -498,6 +498,12 @@ def get_project_link_args(self, compiler: 'Compiler', target: BuildTarget) -> T. return link_args.get(compiler.get_language(), []) + def get_static_linker(self, target: BuildTarget) -> StaticLinker: + archiver = self.static_linker[target.for_machine] + if archiver is None: + raise MesonBugException(f'Required static_linker for {target.for_machine} not found') + return archiver + @dataclass(eq=False) class IncludeDirs(HoldableObject): From f3877152b468bec95a12c1916ccb9280da3e622e Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 16 Jun 2026 14:28:07 -0700 Subject: [PATCH 530/681] compilers/rust: Use `get_linker_id()` instead of .linker.id Which never returns None. The result will be the same either way, but this fixes strict null issues. --- mesonbuild/compilers/rust.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index cc491d1b0a88..b6109c798c9e 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -167,8 +167,7 @@ def _sanity_check_compile_args(self, sourcename: str, binname: str ) -> T.Tuple[T.List[str], T.List[str]]: cmdlist = self.exelist.copy() largs: T.List[str] = [] - assert self.linker is not None, 'for mypy' - if self.info.kernel == 'none' and 'ld.' in self.linker.id: + if self.info.kernel == 'none' and 'ld.' in self.get_linker_id(): largs.extend(rustc_link_args(['-nostartfiles'])) cmdlist.extend(self.get_output_args(binname)) cmdlist.append(sourcename) From 6f52381199afbbdd2363270a5fd089cbdc465b6a Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 10 Jun 2026 14:41:26 -0700 Subject: [PATCH 531/681] utils: make get_meson_command return a list[str] or error We have one, and only one case where we use the fact it can return None, and otherwise assert (or should assert) that the value is not None. Instead, raise if the command is None, and in the one case catch the exception. --- mesonbuild/backend/vs2010backend.py | 2 +- mesonbuild/environment.py | 12 ++++-------- mesonbuild/utils/universal.py | 4 +++- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index ef916c0618ea..cccd5912176e 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -1225,7 +1225,7 @@ def _extract_nmake_fields(captured_build_args: list[str]) -> T.Tuple[str, str, s @staticmethod def get_nmake_base_meson_command_and_exe_search_paths() -> T.Tuple[str, str]: meson_cmd_list = mesonlib.get_meson_command() - assert (len(meson_cmd_list) == 1) or (len(meson_cmd_list) == 2) + assert len(meson_cmd_list) in {1, 2} # We expect get_meson_command() to either be of the form - # 1: ['path/to/meson.exe'] # or - diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index aecefc06d2f0..a27ca5a2e844 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -444,11 +444,10 @@ def create_new_coredata(self, options: cmdline.SharedCMDOptions) -> None: # re-initialized with project options by the interpreter during # build file parsing. # meson_command is used by the regenchecker script, which runs meson - meson_command = mesonlib.get_meson_command() - if meson_command is None: + try: + meson_command = mesonlib.get_meson_command().copy() + except MesonBugException: meson_command = [] - else: - meson_command = meson_command.copy() self.coredata = coredata.CoreData(options, self.scratch_dir, meson_command) self.first_invocation = True @@ -477,10 +476,7 @@ def get_coredata(self) -> coredata.CoreData: @staticmethod def get_build_command(unbuffered: bool = False) -> T.List[str]: - cmd = mesonlib.get_meson_command() - if cmd is None: - raise MesonBugException('No command?') - cmd = cmd.copy() + cmd = mesonlib.get_meson_command().copy() if unbuffered and 'python' in os.path.basename(cmd[0]): cmd.insert(1, '-u') return cmd diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index cb4c402c4f62..6c2a3ea3b0a2 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -268,7 +268,9 @@ def set_meson_command(mainfile: str) -> None: mlog.log(f'meson_command is {_meson_command!r}') -def get_meson_command() -> T.Optional['ImmutableListProtocol[str]']: +def get_meson_command() -> ImmutableListProtocol[str]: + if _meson_command is None: + raise MesonBugException('Attempting to use meson_command before it is set') return _meson_command From 9151be00886dc2883c857ee7f24e556e4c2b210d Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 16 Jun 2026 14:23:12 -0700 Subject: [PATCH 532/681] backend: Don't return none from backend getters Instead raise an exception. There is never a case where we don't error if we get a None, so stop returning it. --- mesonbuild/backend/backends.py | 8 ++++---- mesonbuild/interpreter/interpreter.py | 2 -- mesonbuild/mintro.py | 1 - 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index fd838cb4a7cf..25d95b1efaf4 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -225,7 +225,7 @@ def __post_init__(self) -> None: assert isinstance(self.exe_wrapper, programs.ExternalProgram) -def get_backend_from_name(backend: str, build: T.Optional[build.Build] = None) -> T.Optional['Backend']: +def get_backend_from_name(backend: str, build: T.Optional[build.Build] = None) -> Backend: if backend == 'ninja': from . import ninjabackend return ninjabackend.NinjaBackend(build) @@ -262,17 +262,17 @@ def get_backend_from_name(backend: str, build: T.Optional[build.Build] = None) - elif backend == 'none': from . import nonebackend return nonebackend.NoneBackend(build) - return None + raise MesonException(f'Unknown backend {backend}') -def get_genvslite_backend(genvsname: str, build: T.Optional[build.Build] = None) -> T.Optional['Backend']: +def get_genvslite_backend(genvsname: str, build: T.Optional[build.Build] = None) -> Backend: if genvsname == 'vs2022': from . import vs2022backend return vs2022backend.Vs2022Backend(build, gen_lite = True) if genvsname == 'vs2026': from . import vs2026backend return vs2026backend.Vs2026Backend(build, gen_lite = True) - return None + raise MesonException(f'Unknown genvslite backend {genvsname}') # This class contains the basic functionality that is needed by all backends. # Feel free to move stuff in and out of it as you see fit. diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 2b648d6976c9..467ccae1507f 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1226,8 +1226,6 @@ def set_backend(self) -> None: assert isinstance(backend_name, str), 'for mypy' self.backend = backends.get_backend_from_name(backend_name, self.build) - if self.backend is None: - raise InterpreterException(f'Unknown backend "{backend_name}".') if backend_name != self.backend.name: if self.backend.name.startswith('vs'): mlog.log('Auto detected Visual Studio backend:', mlog.bold(self.backend.name)) diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index c7b7b03489d7..cef85bb2000b 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -530,7 +530,6 @@ def run(options: argparse.Namespace) -> int: # Make sure that log entries in other parts of meson don't interfere with the JSON output with redirect_stdout(sys.stderr): backend = backends.get_backend_from_name(options.backend) - assert backend is not None intr = IntrospectionInterpreter(sourcedir, '', backend.name, visitors = [AstIDGenerator(), AstIndentationGenerator(), AstConditionLevel()]) intr.analyze() From f302ad477d1c92e2488df3c466ce5f8ba788da17 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 10 Jun 2026 14:53:29 -0700 Subject: [PATCH 533/681] backends/ninja: refactor RustrCrate to remove is_proc_macro It's always derived from the value of proc_macro_dylib_path anyway, so move that into the function itself. This also resolves some strict null issues. --- mesonbuild/backend/ninjabackend.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 927d3c48c4ca..892e38f9c176 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -470,7 +470,6 @@ class RustCrate: edition: RUST_EDITIONS deps: T.List[RustDep] cfg: T.List[str] - is_proc_macro: bool # This is set to True for members of this project, and False for all # subprojects @@ -483,13 +482,12 @@ def to_json(self) -> T.Dict[str, object]: "root_module": self.root_module, "edition": self.edition, "cfg": self.cfg, - "is_proc_macro": self.is_proc_macro, + "is_proc_macro": self.proc_macro_dylib_path is not None, "deps": [d.to_json() for d in self.deps], } - if self.is_proc_macro: - assert self.proc_macro_dylib_path is not None, "This shouldn't happen" - ret["proc_macro_dylib_path"] = self.proc_macro_dylib_path + if (proc_macro := self.proc_macro_dylib_path) is not None: + ret["proc_macro_dylib_path"] = proc_macro return ret @@ -1982,7 +1980,6 @@ def _add_rust_project_entry(self, name: str, main_rust_file: str, args: Compiler deps, cfg, is_workspace_member=not from_subproject, - is_proc_macro=proc_macro_dylib_path is not None, proc_macro_dylib_path=proc_macro_dylib_path, ) From 4ed8dedd572726f33e47731b4bd9e82e2e91051f Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 16 Jun 2026 14:17:04 -0700 Subject: [PATCH 534/681] cmake/interpreter: ConverterCustomTarget.get_ref cannot return None In every case where None is returned we'd either hit an assertion failure, or an uncaught exception. So instead, raise if we don't find an output. --- mesonbuild/cmake/interpreter.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/mesonbuild/cmake/interpreter.py b/mesonbuild/cmake/interpreter.py index 733a68b077d4..291157f2f827 100644 --- a/mesonbuild/cmake/interpreter.py +++ b/mesonbuild/cmake/interpreter.py @@ -458,9 +458,8 @@ def non_optional(inputs: T.Iterable[T.Optional[Path]]) -> T.List[Path]: for gen_file in self.generated_raw: ctgt = output_target_map.generated(gen_file) if ctgt: - assert isinstance(ctgt, ConverterCustomTarget) ref = ctgt.get_ref(gen_file) - assert isinstance(ref, CustomTargetReference) and ref.valid() + assert ref.valid() self.generated_ctgt += [ref] else: self.generated += [gen_file] @@ -759,7 +758,6 @@ def ensure_absolute(x: Path) -> Path: self.depends += [tgt] elif gen: ctgt_ref = gen.get_ref(raw) - assert ctgt_ref is not None self.inputs += [ctgt_ref] def process_inter_target_dependencies(self) -> None: @@ -775,15 +773,16 @@ def process_inter_target_dependencies(self) -> None: new_deps += [i] self.depends = list(OrderedSet(new_deps)) - def get_ref(self, fname: Path) -> T.Optional[CustomTargetReference]: + def get_ref(self, fname: Path) -> CustomTargetReference: name = fname.name + if name in self.conflict_map: + name = self.conflict_map[name] try: - if name in self.conflict_map: - name = self.conflict_map[name] idx = self.outputs.index(name) - return CustomTargetReference(self, idx) except ValueError: - return None + raise mesonlib.MesonBugException(f'Attempted to get an output for {fname}, but none exists') + + return CustomTargetReference(self, idx) def log(self) -> None: mlog.log('Custom Target', mlog.bold(self.name), f'({self.cmake_name})') From 28f3dbbbace28662bb73ad9642fbb36b5b42914e Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 16 Jun 2026 14:33:18 -0700 Subject: [PATCH 535/681] interpreter: Project(version) cannot be None We have a default value of `'undefined'`, so it can't actually be None. Remove the annotations that would let None pass. --- mesonbuild/interpreter/interpreter.py | 2 +- mesonbuild/interpreter/kwargs.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 467ccae1507f..6ae858963fa9 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1263,7 +1263,7 @@ def _validate_languages(self, langs: T.List[str], required: bool, node: mparser. KwargInfo('meson_version', (str, NoneType)), KwargInfo( 'version', - (str, mesonlib.File, NoneType, list), + (str, mesonlib.File, list), default='undefined', validator=_project_version_validator, convertor=lambda x: x[0] if isinstance(x, list) else x, diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index 9233db19ccbb..37ac74f713d9 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -238,7 +238,7 @@ class AddTestSetup(TypedDict): class Project(TypedDict): - version: T.Optional[FileOrString] + version: FileOrString meson_version: T.Optional[str] default_options: options.OptionDict license: T.List[str] From 43b301f3eed88c3f6af5ab346c03d3e142b867f8 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 9 Jun 2026 09:11:30 -0700 Subject: [PATCH 536/681] utils/universal: Fix typing of PerThreeMachine.miss_defaulting It doesn't return a `_T | None`, the type Defaultable type already has `_T | None` baked in, so it just needs a `PerThreeMachineDefaultable[_T]` --- mesonbuild/utils/universal.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index 6c2a3ea3b0a2..5df20ae90255 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -637,14 +637,14 @@ def __getitem__(self, machine: MachineChoice | ThreeMachineChoice) -> _T: def __setitem__(self, machine: MachineChoice | ThreeMachineChoice, val: _T) -> None: setattr(self, machine.get_lower_case_name(), val) - def miss_defaulting(self) -> "PerThreeMachineDefaultable[T.Optional[_T]]": + def miss_defaulting(self) -> "PerThreeMachineDefaultable[_T]": """Unset definition duplicated from their previous to None This is the inverse of ''default_missing''. By removing defaulted machines, we can elaborate the original and then redefault them and thus avoid repeating the elaboration explicitly. """ - unfreeze: PerThreeMachineDefaultable[T.Optional[_T]] = PerThreeMachineDefaultable() + unfreeze: PerThreeMachineDefaultable[_T] = PerThreeMachineDefaultable() unfreeze.build = self.build unfreeze.host = self.host unfreeze.target = self.target From f22f674855faaf74462fb63d3dfe59d1d6fcca97 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 17 Jun 2026 09:40:16 -0700 Subject: [PATCH 537/681] utils/universal: Use ParamSpec for decorators Sot that they have correct typing. --- mesonbuild/utils/universal.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index 5df20ae90255..9e6496d23840 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -64,6 +64,7 @@ def __gt__(self, other: Self) -> bool: ... FileOrString = T.Union['File', str] +_P = T.ParamSpec('_P') _T = T.TypeVar('_T') _U = T.TypeVar('_U') @@ -2592,11 +2593,11 @@ def get_wine_shortpath(winecmd: T.List[str], wine_paths: T.List[str], return wine_path -def run_once(func: T.Callable[..., _T]) -> T.Callable[..., _T]: +def run_once(func: T.Callable[_P, _T]) -> T.Callable[_P, _T]: ret: T.List[_T] = [] @wraps(func) - def wrapper(*args: T.Any, **kwargs: T.Any) -> _T: + def wrapper(*args: _P.args, **kwargs: _P.kwargs) -> _T: if ret: return ret[0] @@ -2607,9 +2608,9 @@ def wrapper(*args: T.Any, **kwargs: T.Any) -> _T: return wrapper -def generate_list(func: T.Callable[..., T.Generator[_T, None, None]]) -> T.Callable[..., T.List[_T]]: +def generate_list(func: T.Callable[_P, T.Generator[_T, None, None]]) -> T.Callable[_P, T.List[_T]]: @wraps(func) - def wrapper(*args: T.Any, **kwargs: T.Any) -> T.List[_T]: + def wrapper(*args: _P.args, **kwargs: _P.kwargs) -> T.List[_T]: return list(func(*args, **kwargs)) return wrapper From ba834349b028c1b272e7491c2d3da6b81e611934 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 17 Jun 2026 09:11:52 -0700 Subject: [PATCH 538/681] utils/universal: Add a late_property This is a decorator that allows declaring a property as initialized late. This allows for more ergonomic typing than using a union of `T | None` for late initialized variables. --- mesonbuild/utils/universal.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index 9e6496d23840..7929f9cdc749 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -152,6 +152,7 @@ def __gt__(self, other: Self) -> bool: ... 'is_wsl', 'iter_regexin_iter', 'join_args', + 'late_property', 'lazy_property', 'listify', 'listify_array_value', @@ -2710,6 +2711,28 @@ def __get__(self, instance: object, cls: T.Type) -> _T: return value +class late_property(T.Generic[_T]): + """Descriptor that allows setting a property late and erroring if it's + accessed early. + + This property allows a more ergonomically typed equivalent of + self.value: T | None = None, since you then don't need to worry about + whether self.value is None. + """ + + def __init__(self) -> None: + self.__name: str | None = None + + def __set_name__(self, owner: object, name: str) -> None: + if self.__name is None: + self.__name = name + else: + assert self.__name == name + + def __get__(self, instance: T.Any, cls: type) -> _T: + raise MesonBugException(f'Attempted to access attribute {self.__name} before it is set') + + def get_subproject_dir(directory: str = '.') -> T.Optional[str]: """Get the name of the subproject directory for a specific project. From 777981033f99bc7cbf94cefc10be8cff3575fea5 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 17 Jun 2026 09:15:56 -0700 Subject: [PATCH 539/681] interpreter: Use the late_property for Interpreter.backend --- mesonbuild/interpreter/interpreter.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 6ae858963fa9..fef9c6a2b9df 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -6,6 +6,7 @@ import io, sys, traceback import dataclasses +import functools from .. import mparser from .. import environment @@ -272,6 +273,8 @@ class InterpreterRuleRelaxation(Enum): See also: https://github.com/mesonbuild/meson/issues/9300""" class Interpreter(InterpreterBase, HoldableObject): + backend: mesonlib.late_property[Backend] = mesonlib.late_property() + def __init__( self, _build: build.Build, @@ -288,7 +291,8 @@ def __init__( super().__init__(_build.environment.get_source_dir(), subdir, subproject, subproject_dir, _build.environment) self.active_projectname = '' self.build = _build - self.backend = backend + if backend is not None: + self.backend = backend self.cargo = cargo self.summary: T.Dict[str, 'Summary'] = {} self.modules: T.Dict[str, NewExtensionModule] = {} @@ -1209,10 +1213,8 @@ def func_configuration_data(self, node: mparser.BaseNode, args: T.Tuple[T.Option f'"configuration_data": initial value dictionary key "{k!r}"" must be "str | int | bool", not "{v!r}"') return build.ConfigurationData(initial_values) + @functools.lru_cache(None) def set_backend(self) -> None: - # The backend is already set when parsing subprojects - if self.backend is not None: - return from ..backend import backends if OptionKey('genvslite') in self.user_defined_options.cmd_line_options: From 55321d5cd72e2f6c5528892e9c3e2f118a72a3e0 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 17 Jun 2026 10:34:18 -0700 Subject: [PATCH 540/681] compilers/compilers: Use wallrus operator for exe_wrapper less code, tighter typing --- mesonbuild/compilers/compilers.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 0ba0adbca4e3..1ba9d91d67a8 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -1460,9 +1460,8 @@ def _sanity_check_run_with_exe_wrapper(self, command: T.List[str]) -> T.List[str :param command: The string list of commands to run :return: The list of commands wrapped by the exe_wrapper if it is needed, otherwise the original commands """ - if self.is_cross and self.environment.has_exe_wrapper(): - assert self.environment.exe_wrapper is not None, 'for mypy' - return self.environment.exe_wrapper.get_command() + command + if self.is_cross and (exe_wrapper := self.environment.get_exe_wrapper()): + return exe_wrapper.get_command() + command return command def _run_sanity_check(self, cmdlist: T.List[str], work_dir: str) -> None: From d075ba33cacdb2d078372ef50ed9a268c5f375eb Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 9 Jun 2026 14:37:27 -0700 Subject: [PATCH 541/681] utils/universal: Add unwrap functions for removing None values from unions --- mesonbuild/utils/universal.py | 39 ++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index 7929f9cdc749..2116709977be 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -28,7 +28,7 @@ import dataclasses from mesonbuild import mlog -from .core import MesonException, HoldableObject +from .core import MesonException, MesonBugException, HoldableObject if T.TYPE_CHECKING: from typing_extensions import Literal, Protocol, Self @@ -179,6 +179,8 @@ def __gt__(self, other: Self) -> bool: ... 'substring_is_in_list', 'typeslistify', 'unique_list', + 'unwrap', + 'unwrap_err', 'verbose_git', 'version_check_to_range', 'version_compare', @@ -2812,3 +2814,38 @@ def alphanum_key(key: str) -> tuple[int | str, ...]: return tuple((key.count('/') <= idx, alphanum_key(x)) for idx, x in enumerate(key.split('/'))) + + +def unwrap(value: _T | None, msg: str | None = None) -> _T: + """Remove None from a union type when it is a Meson bug. + + This is used for cases where None being in the Union is a bug in Meson + itself. + + :param value: The Union + :param msg: A message to print when a buggy value occurs, defaults to None + :raises MesonBugException: When None is in value + :return: The value union with None removed + """ + if value is not None: + return value + raise MesonBugException(msg or 'Unexpected None value') + + +def unwrap_err(value: _T | None, msg: str) -> _T: + """Remove None from a union type when it is not a Meson bug. + + This is for cases where None is possible, but it represents a problem + outside of Meson itself. + + For example, a missing external program, or a read only file system, or a + missing file + + :param value: The Union to remove None from + :param msg: The message to print when None is found + :raises MesonException: When None is found in the union + :return: The Union with None removed + """ + if value is not None: + return value + raise MesonException(msg) From 7498aa155371949211e90ea3dbcbea6cf8d66928 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 9 Jun 2026 14:53:19 -0700 Subject: [PATCH 542/681] make use of the unwrap function --- mesonbuild/backend/backends.py | 3 +-- mesonbuild/backend/ninjabackend.py | 3 +-- mesonbuild/build.py | 7 +++---- mesonbuild/compilers/detect.py | 16 ++++++--------- mesonbuild/interpreter/interpreter.py | 2 -- mesonbuild/interpreter/interpreterobjects.py | 21 ++++++++------------ mesonbuild/modules/codegen.py | 2 -- mesonbuild/modules/gnome.py | 3 +-- mesonbuild/utils/universal.py | 13 ++++++------ mesonbuild/wrap/wraptool.py | 3 +-- 10 files changed, 27 insertions(+), 46 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 25d95b1efaf4..5ec516806e28 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -1455,8 +1455,7 @@ def get_custom_target_sources(self, target: build.RunTarget | build.CustomTarget fname = self.determine_ext_objs(i) elif isinstance(i, programs.Program): assert i.found(), "This shouldn't be possible" - assert i.get_path() is not None, 'for mypy' - fname = [i.get_path()] + fname = [mesonlib.unwrap(i.get_path())] else: fname = [i.rel_to_builddir(self.build_to_src)] if target.absolute_paths: diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 892e38f9c176..d56eab25785f 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -191,8 +191,7 @@ def strToCommandArg(c: T.Union[NinjaCommandArg, str]) -> NinjaCommandArg: # shell constructs shouldn't be shell quoted return NinjaCommandArg(c, Quoting.notShell) if c.startswith('$'): - varp = re.match(r'\$\{?(\w*)\}?', c) - assert varp is not None, 'for mypy' + varp = mesonlib.unwrap(re.match(r'\$\{?(\w*)\}?', c)) var: str = varp.group(1) if var not in raw_names: # ninja variables shouldn't be ninja quoted, and their value diff --git a/mesonbuild/build.py b/mesonbuild/build.py index a008605e3bd8..9942c8109f8d 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -27,6 +27,7 @@ get_filenames_templates_dict, substitute_values, has_path_sep, is_parent_path, relpath, PerMachineDefaultable, MesonBugException, EnvironmentVariables, pickle_load, lazy_property, + unwrap, ) from .options import OptionKey @@ -1400,7 +1401,6 @@ def _extract_pic_pie(self, kwargs: T.Union[StaticLibraryKeywordArguments, Execut # PIC is always on for Windows (all code is position-independent # since library loading is done differently) m = self.environment.machines[self.for_machine] - assert m is not None, 'for mypy' if arg == 'pic' and (m.is_darwin() or m.is_windows()): return True @@ -2915,9 +2915,8 @@ def flatten_command(cmd: T.Iterable[CommandTypes], elif isinstance(c, programs.Program): if not c.found(): raise InvalidArguments('Tried to use not-found external program in "command"') - path = c.get_path() - # We know path is not non if c.found() - assert path is not None, 'for mypy' + # We know path is not none if c.found() + path = unwrap(c.get_path()) if os.path.isabs(path): # Can only add a dependency on an external program which we # know the absolute path of diff --git a/mesonbuild/compilers/detect.py b/mesonbuild/compilers/detect.py index 040a16cf921a..ed81c5e6c931 100644 --- a/mesonbuild/compilers/detect.py +++ b/mesonbuild/compilers/detect.py @@ -6,6 +6,7 @@ from ..mesonlib import ( MesonException, EnvironmentException, MachineChoice, join_args, search_version, is_windows, Popen_safe, Popen_safe_logged, version_compare, windows_proof_rm, + unwrap, ) from ..programs import ExternalProgram from ..envconfig import BinaryTable, detect_cpu_family @@ -396,8 +397,7 @@ def sanitize(p: T.Optional[str]) -> T.Optional[str]: linker=linker, full_version=full_version) if 'Arm C/C++/Fortran Compiler' in out: - arm_ver_match = re.search(r'version (\d+)\.(\d+)\.?(\d+)? \(build number (\d+)\)', out) - assert arm_ver_match is not None, 'for mypy' # because mypy *should* be complaining that this could be None + arm_ver_match = unwrap(re.search(r'version (\d+)\.(\d+)\.?(\d+)? \(build number (\d+)\)', out)) version = '.'.join([x for x in arm_ver_match.groups() if x is not None]) if lang == 'c': cls = c.ArmLtdClangCCompiler @@ -624,8 +624,7 @@ def sanitize(p: T.Optional[str]) -> T.Optional[str]: cls = c.MetrowerksCCompilerEmbeddedPowerPC if lang == 'c' else cpp.MetrowerksCPPCompilerEmbeddedPowerPC lnk = linkers.MetrowerksLinkerEmbeddedPowerPC - mwcc_ver_match = re.search(r'Version (\d+)\.(\d+)\.?(\d+)? build (\d+)', out) - assert mwcc_ver_match is not None, 'for mypy' # because mypy *should* be complaining that this could be None + mwcc_ver_match = unwrap(re.search(r'Version (\d+)\.(\d+)\.?(\d+)? build (\d+)', out)) compiler_version = '.'.join(x for x in mwcc_ver_match.groups() if x is not None) env.add_lang_args(cls.language, cls, for_machine) @@ -634,8 +633,7 @@ def sanitize(p: T.Optional[str]) -> T.Optional[str]: if ld is not None: _, o_ld, _ = Popen_safe(ld + ['--version']) - mwld_ver_match = re.search(r'Version (\d+)\.(\d+)\.?(\d+)? build (\d+)', o_ld) - assert mwld_ver_match is not None, 'for mypy' # because mypy *should* be complaining that this could be None + mwld_ver_match = unwrap(re.search(r'Version (\d+)\.(\d+)\.?(\d+)? build (\d+)', o_ld)) linker_version = '.'.join(x for x in mwld_ver_match.groups() if x is not None) linker = lnk(ld, env, for_machine, version=linker_version) @@ -649,8 +647,7 @@ def sanitize(p: T.Optional[str]) -> T.Optional[str]: cls = c.TaskingCCompiler lnk = linkers.TaskingLinker - tasking_ver_match = re.search(r'v([0-9]+)\.([0-9]+)r([0-9]+) Build ([0-9]+)', err) - assert tasking_ver_match is not None, 'for mypy' + tasking_ver_match = unwrap(re.search(r'v([0-9]+)\.([0-9]+)r([0-9]+) Build ([0-9]+)', err)) tasking_version = '.'.join(x for x in tasking_ver_match.groups() if x is not None) env.add_lang_args(cls.language, cls, for_machine) @@ -776,8 +773,7 @@ def detect_fortran_compiler(env: 'Environment', for_machine: MachineChoice) -> C if 'Arm C/C++/Fortran Compiler' in out: cls = fortran.ArmLtdFlangFortranCompiler - arm_ver_match = re.search(r'version (\d+)\.(\d+)\.?(\d+)? \(build number (\d+)\)', out) - assert arm_ver_match is not None, 'for mypy' # because mypy *should* be complaining that this could be None + arm_ver_match = unwrap(re.search(r'version (\d+)\.(\d+)\.?(\d+)? \(build number (\d+)\)', out)) version = '.'.join([x for x in arm_ver_match.groups() if x is not None]) linker = guess_nix_linker(env, compiler, cls, version, for_machine) return cls( diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index fef9c6a2b9df..316884e7c081 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1322,7 +1322,6 @@ def func_project(self, node: mparser.FunctionNode, args: T.Tuple[str, T.List[str self.active_projectname = proj_name version = kwargs['version'] - assert version is not None, 'for mypy' if isinstance(version, mesonlib.File): FeatureNew.single_use('version from file', '0.57.0', self.subproject, location=node) self.add_build_def_file(version) @@ -1384,7 +1383,6 @@ def func_project(self, node: mparser.FunctionNode, args: T.Tuple[str, T.List[str wrap_mode = WrapMode.from_string(wrap_mode_s) self.environment.wrap_resolver = wrap.Resolver(self.environment.get_source_dir(), subprojects_dir, self.subproject, wrap_mode) else: - assert self.environment.wrap_resolver is not None, 'for mypy' self.environment.wrap_resolver.load_and_merge(subprojects_dir, self.subproject) if self.cargo is None: diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index ebd256f45f5c..3ef37e78424d 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -689,9 +689,7 @@ def _full_path(self) -> str: assert isinstance(self.held_object, build.LocalProgram) and \ isinstance(self.held_object.program, (build.BuildTarget, build.CustomTargetIndex)) return self.interpreter.backend.get_target_filename_abs(self.held_object.program) - path = self.held_object.get_path() - assert path is not None - return path + return mesonlib.unwrap(self.held_object.get_path()) @noPosargs @noKwargs @@ -702,9 +700,7 @@ def cmd_array_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.Lis raise InterpreterException('Unable to get the path of a not-found external program') if not self.held_object.runnable(): return [self._full_path()] - cmd = self.held_object.get_command() - assert cmd is not None - return cmd + return self.held_object.get_command() @noPosargs @noKwargs @@ -1098,9 +1094,9 @@ def vala_header_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> mes if self._target_object.vala_header is None: raise mesonlib.MesonException("Attempted to get a Vala header from a target that doesn't generate one") - assert self.interpreter.backend is not None, 'for mypy' return mesonlib.File.from_built_file( - self.interpreter.backend.get_target_dir(self._target_object), self._target_object.vala_header) + self.interpreter.backend.get_target_dir(self._target_object), + self._target_object.vala_header) @FeatureNew('vala_vapi', '1.10.0') @noPosargs @@ -1110,9 +1106,9 @@ def vala_vapi_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> meson if self._target_object.vala_vapi is None: raise mesonlib.MesonException("Attempted to get a Vala VAPI from a target that doesn't generate one") - assert self.interpreter.backend is not None, 'for mypy' return mesonlib.File.from_built_file( - self.interpreter.backend.get_target_dir(self._target_object), self._target_object.vala_vapi) + self.interpreter.backend.get_target_dir(self._target_object), + self._target_object.vala_vapi) @FeatureNew('vala_gir', '1.10.0') @noPosargs @@ -1122,9 +1118,9 @@ def vala_gir_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> mesonl if self._target_object.vala_gir is None: raise mesonlib.MesonException("Attempted to get a Vala GIR from a target that doesn't generate one") - assert self.interpreter.backend is not None, 'for mypy' return mesonlib.File.from_built_file( - self.interpreter.backend.get_target_dir(self._target_object), self._target_object.vala_gir) + self.interpreter.backend.get_target_dir(self._target_object), + self._target_object.vala_gir) class ExecutableHolder(BuildTargetHolder[build.Executable]): @@ -1177,7 +1173,6 @@ def __init__(self, target: build.CustomTargetIndex, interp: 'Interpreter'): @noKwargs @InterpreterObject.method('full_path') def full_path_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: - assert self.interpreter.backend is not None return self.interpreter.backend.get_target_filename_abs(self.held_object) _CT = T.TypeVar('_CT', bound=build.CustomTarget) diff --git a/mesonbuild/modules/codegen.py b/mesonbuild/modules/codegen.py index 0c959090a7d5..1f2dd2298bb1 100644 --- a/mesonbuild/modules/codegen.py +++ b/mesonbuild/modules/codegen.py @@ -310,7 +310,6 @@ def lex_method(self, state: ModuleState, args: T.Tuple, kwargs: FindLexKwargs) - if kwargs['implementations']: names = kwargs['implementations'] else: - assert state.environment.machines[kwargs['native']] is not None, 'for mypy' if state.environment.machines[kwargs['native']].system == 'windows': names.append('win_flex') names.extend(['flex', 'reflex', 'lex']) @@ -389,7 +388,6 @@ def yacc_method(self, state: ModuleState, args: T.Tuple, kwargs: FindYaccKwargs) if kwargs['implementations']: names = kwargs['implementations'] else: - assert state.environment.machines[kwargs['native']] is not None, 'for mypy' if state.environment.machines[kwargs['native']].system == 'windows': names = ['win_bison', 'bison', 'yacc'] else: diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index c177e3f43e98..7e1ef2042618 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -1522,8 +1522,7 @@ def abs_filenames(files: T.Iterable['FileOrString']) -> T.Iterator[str]: for tool in ['scan', 'scangobj', 'mkdb', 'mkhtml', 'fixxref']: program_name = 'gtkdoc-' + tool program = state.find_program(program_name) - path = program.get_path() - assert path is not None, "This shouldn't be possible since program should be found" + path = mesonlib.unwrap(program.get_path()) t_args.append(f'--{program_name}={path}') if namespace: t_args.append('--namespace=' + namespace) diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index 2116709977be..5b2ef7571931 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -228,8 +228,7 @@ def __init__(self, msg: str, output: T.Optional[str] = None): GIT = shutil.which('git') def git(cmd: T.List[str], workingdir: StrOrBytesPath, check: bool = False, **kwargs: T.Any) -> T.Tuple[subprocess.Popen[str], str, str]: - assert GIT is not None, 'Callers should make sure it exists' - cmd = [GIT, *cmd] + cmd = [unwrap(GIT, 'Callers should make sure it exists'), *cmd] p, o, e = Popen_safe(cmd, cwd=workingdir, **kwargs) if check and p.returncode != 0: raise GitException('Git command failed: ' + str(cmd), e) @@ -690,8 +689,8 @@ def default_missing(self) -> PerMachine[_T]: This allows just specifying nothing in the native case, and just host in the cross non-compiler case. """ - assert self.build is not None, 'Cannot fill in missing when all fields are empty' - return PerMachine(self.build, self.host if self.host is not None else self.build) + build = unwrap(self.build, 'Cannot fill in missing when all fields are empty') + return PerMachine(build, self.host if self.host is not None else build) @classmethod def default(cls, is_cross: bool, build: _T, host: _T) -> PerMachine[_T]: @@ -722,10 +721,10 @@ def default_missing(self) -> PerThreeMachine[_T]: cross non-compiler case, and just target in the native-built cross-compiler case. """ - assert self.build is not None, 'Cannot default a PerMachine when all values are None' - host = self.host if self.host is not None else self.build + build = unwrap(self.build, 'Cannot fill in missing when all fields are empty') + host = self.host if self.host is not None else build target = self.target if self.target is not None else host - return PerThreeMachine(self.build, host, target) + return PerThreeMachine(build, host, target) _PLATFORM_SYSTEM_LOWER = platform.system().lower() diff --git a/mesonbuild/wrap/wraptool.py b/mesonbuild/wrap/wraptool.py index 925b105e0e72..d3484dcfe412 100644 --- a/mesonbuild/wrap/wraptool.py +++ b/mesonbuild/wrap/wraptool.py @@ -192,8 +192,7 @@ def promote(options: 'argparse.Namespace') -> None: def status(options: 'argparse.Namespace') -> None: print('Subproject status') - subdir = mesonlib.get_subproject_dir() - assert subdir is not None, "This should only happen in a non-native subproject" + subdir = mesonlib.unwrap(mesonlib.get_subproject_dir(), "This should only happen in a non-native subproject") for w in glob(f'{subdir}/*.wrap'): name = os.path.basename(w)[:-5] try: From 9e31ba0cb2bbfd33cf00535b8e3c0786f9d84a13 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 9 Jun 2026 08:48:02 -0700 Subject: [PATCH 543/681] tooldetect: use strict optional This makes use of mypy.ini to enable stricter checking. --- .mypy.ini | 3 +++ mesonbuild/tooldetect.py | 10 ++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.mypy.ini b/.mypy.ini index 936b1ac2c614..3c358e94d134 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -26,3 +26,6 @@ check_untyped_defs = True # future default behaviors allow_redefinition_new = True local_partial_types = True + +[mypy-mesonbuild.tooldetect] +strict_optional = True diff --git a/mesonbuild/tooldetect.py b/mesonbuild/tooldetect.py index 44ffba9fde13..cc8e8ddf6af3 100644 --- a/mesonbuild/tooldetect.py +++ b/mesonbuild/tooldetect.py @@ -78,11 +78,13 @@ def compute_llvm_suffix(coredata: coredata.CoreData) -> T.Optional[str]: # Neither compiler is a Clang, or no compilers are for C or C++ return None -def detect_lcov_genhtml(lcov_exe: str = 'lcov', genhtml_exe: str = 'genhtml') \ - -> T.Tuple[str, T.Optional[str], str]: - lcov_exe, lcov_version = detect_lcov(lcov_exe) - if shutil.which(genhtml_exe) is None: +def detect_lcov_genhtml(lcov_exe_: str = 'lcov', genhtml_exe_: str = 'genhtml') \ + -> tuple[str | None, str | None, str | None]: + lcov_exe, lcov_version = detect_lcov(lcov_exe_) + if shutil.which(genhtml_exe_) is None: genhtml_exe = None + else: + genhtml_exe = genhtml_exe_ return lcov_exe, lcov_version, genhtml_exe From cc21ae35df2236f5769a0420a0d64fefdacfd8f4 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 9 Jun 2026 08:50:50 -0700 Subject: [PATCH 544/681] depfile: Turn on mypy strict optional --- .mypy.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.mypy.ini b/.mypy.ini index 3c358e94d134..1f6e7e74c0b7 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -27,5 +27,8 @@ check_untyped_defs = True allow_redefinition_new = True local_partial_types = True +[mypy-mesonbuild.depfile] +strict_optional = True + [mypy-mesonbuild.tooldetect] strict_optional = True From 4098ece0dbde71673bde124d677a6a290c88fe2a Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 9 Jun 2026 08:51:33 -0700 Subject: [PATCH 545/681] machinefile: Turn on mypy strict optional --- .mypy.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.mypy.ini b/.mypy.ini index 1f6e7e74c0b7..200b34059782 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -30,5 +30,8 @@ local_partial_types = True [mypy-mesonbuild.depfile] strict_optional = True +[mypy-mesonbuild.machinefile] +strict_optional = True + [mypy-mesonbuild.tooldetect] strict_optional = True From 73bb614328578b720ffbb4f03ed32a63b9f56506 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 9 Jun 2026 08:55:08 -0700 Subject: [PATCH 546/681] mlog: Turn on mypy strict optional --- .mypy.ini | 3 +++ mesonbuild/mlog.py | 9 +++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.mypy.ini b/.mypy.ini index 200b34059782..fb175990c605 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -33,5 +33,8 @@ strict_optional = True [mypy-mesonbuild.machinefile] strict_optional = True +[mypy-mesonbuild.mlog] +strict_optional = True + [mypy-mesonbuild.tooldetect] strict_optional = True diff --git a/mesonbuild/mlog.py b/mesonbuild/mlog.py index e1d998a5d340..27214fdf06a5 100644 --- a/mesonbuild/mlog.py +++ b/mesonbuild/mlog.py @@ -160,10 +160,10 @@ def start_pager(self) -> None: raise MesonException(f'Failed to start pager: {str(e)}') def stop_pager(self) -> None: - if self.log_pager: + if self.log_pager and (stdin := self.log_pager.stdin) is not None: try: - self.log_pager.stdin.flush() - self.log_pager.stdin.close() + stdin.flush() + stdin.close() except OSError: pass self.log_pager.wait() @@ -374,6 +374,7 @@ def nested(self, name: str = '') -> T.Generator[None, None, None]: self.log_depth.pop() def get_log_dir(self) -> str: + assert self.log_dir is not None, 'attempted to use logger without initializing' return self.log_dir def get_log_depth(self) -> int: @@ -396,7 +397,7 @@ def redirect(self, to_stderr: bool) -> None: def colorize_console(self) -> bool: output = sys.stderr if self.log_to_stderr else sys.stdout - _colorize_console: bool = getattr(output, 'colorize_console', None) + _colorize_console: bool | None = getattr(output, 'colorize_console', None) if _colorize_console is not None: return _colorize_console try: From 7cd66d39f44c045653d130d024a3baa032cfaf8a Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 9 Jun 2026 08:55:47 -0700 Subject: [PATCH 547/681] coredata: Turn on mypy strict optional --- .mypy.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.mypy.ini b/.mypy.ini index fb175990c605..35ae4c7e53af 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -27,6 +27,9 @@ check_untyped_defs = True allow_redefinition_new = True local_partial_types = True +[mypy-mesonbuild.coredata] +strict_optional = True + [mypy-mesonbuild.depfile] strict_optional = True From da834ea82acf755696d7f4d9991e73b2fe01391b Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 9 Jun 2026 08:57:51 -0700 Subject: [PATCH 548/681] cmdline: Turn on mypy strict optional --- .mypy.ini | 3 +++ mesonbuild/cmdline.py | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.mypy.ini b/.mypy.ini index 35ae4c7e53af..2b7f9373196c 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -27,6 +27,9 @@ check_untyped_defs = True allow_redefinition_new = True local_partial_types = True +[mypy-mesonbuild.cmdline] +strict_optional = True + [mypy-mesonbuild.coredata] strict_optional = True diff --git a/mesonbuild/cmdline.py b/mesonbuild/cmdline.py index 2de75ae07d98..d495a122f78e 100644 --- a/mesonbuild/cmdline.py +++ b/mesonbuild/cmdline.py @@ -64,7 +64,7 @@ def read_cmd_line_file(build_dir: str, options: SharedCMDOptions) -> None: # Do a copy because config is not really a dict. options.cmd_line_options # overrides values from the file. - d = {OptionKey.from_string(k): v for k, v in config['options'].items()} + d: dict[OptionKey, str | None] = {OptionKey.from_string(k): v for k, v in config['options'].items()} d.update(options.cmd_line_options) options.cmd_line_options = d options.builtin_keys = set() @@ -131,7 +131,7 @@ def __init__(self, option_strings: str, dest: str, nargs: T.Optional[T.Union[int super().__init__(option_strings, dest, nargs=1, **kwargs) def __call__(self, parser: argparse.ArgumentParser, namespace: argparse.Namespace, - arg: T.List[str], option_string: str = None) -> None: # type: ignore[override] + arg: T.List[str], option_string: str | None = None) -> None: # type: ignore[override] current_dict = getattr(namespace, self.dest) if current_dict is None: current_dict = {} @@ -168,7 +168,7 @@ def __init__(self, option_strings: str, dest: str, help=f'{h}{help_suffix}.', **kwargs) def __call__(self, parser: argparse.ArgumentParser, namespace: argparse.Namespace, - arg: T.Optional[T.List[str]], option_string: str = None) -> None: # type: ignore[override] + arg: T.Optional[T.List[str]], option_string: str | None = None) -> None: # type: ignore[override] current_dict = getattr(namespace, self.dest) if current_dict is None: current_dict = {} @@ -189,7 +189,7 @@ def __init__(self, option_strings: str, dest: str, nargs: T.Optional[T.Union[int super().__init__(option_strings, dest, nargs=1, **kwargs) def __call__(self, parser: argparse.ArgumentParser, namespace: argparse.Namespace, - arg: T.List[str], option_string: str = None) -> None: # type: ignore[override] + arg: T.List[str], option_string: str | None = None) -> None: # type: ignore[override] current_dict = getattr(namespace, self.dest) if current_dict is None: current_dict = {} From 91fd982c7fecbccb452a31316b097d97aaef8789 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 9 Jun 2026 08:59:29 -0700 Subject: [PATCH 549/681] envconfig: Turn on mypy strict optional We might want to go the otherway on the MachineInfo, not allowing the cpu_fmaily and cpu to be None. Unsure yet --- .mypy.ini | 3 +++ mesonbuild/envconfig.py | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.mypy.ini b/.mypy.ini index 2b7f9373196c..99d2a3459e59 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -36,6 +36,9 @@ strict_optional = True [mypy-mesonbuild.depfile] strict_optional = True +[mypy-mesonbuild.envconfig] +strict_optional = True + [mypy-mesonbuild.machinefile] strict_optional = True diff --git a/mesonbuild/envconfig.py b/mesonbuild/envconfig.py index a99c976a51aa..4d68c19d0165 100644 --- a/mesonbuild/envconfig.py +++ b/mesonbuild/envconfig.py @@ -267,8 +267,8 @@ def get(self, key: str, default: T.Optional[T.Union[str, bool, int, T.List[str]] @dataclass(unsafe_hash=True) class MachineInfo(HoldableObject): system: str - cpu_family: str - cpu: str + cpu_family: str | None + cpu: str | None endian: str kernel: T.Optional[str] subsystem: T.Optional[str] From e3b1eca62528c19962f482813d0713405d4baf07 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 9 Jun 2026 09:16:37 -0700 Subject: [PATCH 550/681] environment: Turn on mypy strict optional --- .mypy.ini | 3 +++ mesonbuild/environment.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.mypy.ini b/.mypy.ini index 99d2a3459e59..08bd76d7ff6d 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -39,6 +39,9 @@ strict_optional = True [mypy-mesonbuild.envconfig] strict_optional = True +[mypy-mesonbuild.environment] +strict_optional = True + [mypy-mesonbuild.machinefile] strict_optional = True diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index a27ca5a2e844..5805346dc816 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -111,6 +111,8 @@ class Environment: log_dir = 'meson-logs' info_dir = 'meson-info' + exe_wrapper: ExternalProgram | None + def __init__(self, source_dir: str, build_dir: T.Optional[str], cmd_options: cmdline.SharedCMDOptions) -> None: self.source_dir = source_dir # Do not try to create build directories when build_dir is none. @@ -258,6 +260,7 @@ def __init__(self, source_dir: str, build_dir: T.Optional[str], cmd_options: cmd if k.machine is MachineChoice.HOST or self.coredata.optstore.is_per_machine_option(k)} exe_wrapper = self.lookup_binary_entry(MachineChoice.HOST, 'exe_wrapper') + self.exe_wrapper: ExternalProgram | None if exe_wrapper is not None: self.exe_wrapper = ExternalProgram.from_bin_list(self, MachineChoice.HOST, 'exe_wrapper') else: From a4492c2710030b6f584f39058f34461b3f2a261b Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 9 Jun 2026 09:17:15 -0700 Subject: [PATCH 551/681] mcompile: Turn on mypy strict optional --- .mypy.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.mypy.ini b/.mypy.ini index 08bd76d7ff6d..d1541dbe5a54 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -45,6 +45,9 @@ strict_optional = True [mypy-mesonbuild.machinefile] strict_optional = True +[mypy-mesonbuild.mcompile] +strict_optional = True + [mypy-mesonbuild.mlog] strict_optional = True From 4e272176e6f291690c673419eb72c9adddb1ee88 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 9 Jun 2026 09:23:58 -0700 Subject: [PATCH 552/681] mconf: Turn on mypy strict optional --- .mypy.ini | 3 +++ mesonbuild/mconf.py | 16 ++++++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/.mypy.ini b/.mypy.ini index d1541dbe5a54..59b9872c569a 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -48,6 +48,9 @@ strict_optional = True [mypy-mesonbuild.mcompile] strict_optional = True +[mypy-mesonbuild.mconf] +strict_optional = True + [mypy-mesonbuild.mlog] strict_optional = True diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py index f7db6b56bf73..2b02eadf76c1 100644 --- a/mesonbuild/mconf.py +++ b/mesonbuild/mconf.py @@ -21,7 +21,7 @@ from . import mintro from . import mlog from .ast import AstIDGenerator, IntrospectionInterpreter -from .mesonlib import MachineChoice +from .mesonlib import MachineChoice, unwrap from .options import OptionKey from .optinterpreter import OptionInterpreter @@ -189,8 +189,8 @@ def wrap_text(text: LOGLINE, width: int) -> mlog.TV_LoggableList: mlog.log(*items) def split_options_per_subproject(self, opts: T.Union[options.MutableKeyedOptionDictType, options.OptionStore] - ) -> T.Dict[str, options.MutableKeyedOptionDictType]: - result: T.Dict[str, options.MutableKeyedOptionDictType] = {} + ) -> T.Dict[str | None, options.MutableKeyedOptionDictType]: + result: T.Dict[str | None, options.MutableKeyedOptionDictType] = {} for k, o in opts.items(): if k.subproject is not None: self.all_subprojects.add(k.subproject) @@ -278,10 +278,9 @@ def print_default_values_warning() -> None: dir_options[k] = v elif k in test_option_names: test_options[k] = v - elif k.has_module_prefix(): + elif (modname := k.get_module_prefix()) is not None: # Ignore module options if we did not use that module during # configuration. - modname = k.get_module_prefix() if self.build and modname not in self.build.modules: continue module_options[modname][k] = v @@ -293,7 +292,7 @@ def print_default_values_warning() -> None: host_compiler_options = self.split_options_per_subproject({k: v for k, v in self.coredata.optstore.items() if self.coredata.optstore.is_compiler_option(k) and k.machine is MachineChoice.HOST}) build_compiler_options = self.split_options_per_subproject({k: v for k, v in self.coredata.optstore.items() if self.coredata.optstore.is_compiler_option(k) and k.machine is MachineChoice.BUILD}) project_options = self.split_options_per_subproject({k: v for k, v in self.coredata.optstore.items() if self.coredata.optstore.is_project_option(k)}) - show_build_options = self.default_values_only or self.build.environment.is_cross_build() + show_build_options = self.default_values_only or unwrap(self.build).environment.is_cross_build() self.add_section('Global build options') self.print_options('Core options', host_core_options[None]) @@ -390,8 +389,9 @@ def run_impl(options: CMDOptions, builddir: str) -> int: save = True if save: c.save() - mintro.update_build_options(c.coredata, c.build.environment.info_dir) - mintro.write_meson_info_file(c.build, []) + build = unwrap(c.build) + mintro.update_build_options(c.coredata, build.environment.info_dir) + mintro.write_meson_info_file(build, []) except ConfException as e: mlog.log('Meson configurator encountered an error:') if c is not None and c.build is not None: From b4588460ddba232b7b6bd0e583ddbd192f73db10 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 9 Jun 2026 09:24:25 -0700 Subject: [PATCH 553/681] mdevenv: Turn on mypy strict optional --- .mypy.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.mypy.ini b/.mypy.ini index 59b9872c569a..4dee06576b66 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -51,6 +51,9 @@ strict_optional = True [mypy-mesonbuild.mconf] strict_optional = True +[mypy-mesonbuild.mdevenv] +strict_optional = True + [mypy-mesonbuild.mlog] strict_optional = True From 6f956acb5f71c37041a10a8e64f5c4987b1957bf Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 9 Jun 2026 09:27:26 -0700 Subject: [PATCH 554/681] mdist: Turn on mypy strict optional --- .mypy.ini | 3 +++ mesonbuild/mdist.py | 9 ++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.mypy.ini b/.mypy.ini index 4dee06576b66..029a412fa2e1 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -54,6 +54,9 @@ strict_optional = True [mypy-mesonbuild.mdevenv] strict_optional = True +[mypy-mesonbuild.mdist] +strict_optional = True + [mypy-mesonbuild.mlog] strict_optional = True diff --git a/mesonbuild/mdist.py b/mesonbuild/mdist.py index 919df0653c66..72ef1731b87a 100644 --- a/mesonbuild/mdist.py +++ b/mesonbuild/mdist.py @@ -24,7 +24,7 @@ from mesonbuild.environment import Environment from mesonbuild.tooldetect import detect_ninja from mesonbuild.mesonlib import (GIT, MesonException, RealPathAction, SimpleABC, get_meson_command, quiet_git, - windows_proof_rmtree, setup_vsenv, determine_worker_count) + windows_proof_rmtree, setup_vsenv, determine_worker_count, unwrap_err) from .options import OptionKey from mesonbuild.msetup import add_arguments as msetup_argparse from mesonbuild.wrap import wrap @@ -337,7 +337,8 @@ def check_dist(packagename: str, _meson_command: ImmutableListProtocol[str], ext if os.path.exists(p): windows_proof_rmtree(p) os.mkdir(p) - ninja_args = detect_ninja() + [f'-j{num_processes}'] + ninja = unwrap_err(detect_ninja(), 'Ninja is required but could not be found') + ninja_args = ninja + [f'-j{num_processes}'] shutil.unpack_archive(packagename, unpackdir) unpacked_files = glob(os.path.join(unpackdir, '*')) assert len(unpacked_files) == 1 @@ -387,7 +388,9 @@ def run(options: argparse.Namespace) -> int: bld_root = b.environment.build_dir priv_dir = os.path.join(bld_root, 'meson-private') - dist_name = b.project_name + '-' + b.project_version + dist_name = b.project_name + if b.project_version is not None: + dist_name = f'{dist_name}-{b.project_version}' archives = determine_archives_to_generate(options) From 0c106b2b8a9df886e8f03828698be773277eb451 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 9 Jun 2026 09:28:00 -0700 Subject: [PATCH 555/681] mesondata: Turn on mypy strict optional --- .mypy.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.mypy.ini b/.mypy.ini index 029a412fa2e1..c85c6d632378 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -57,6 +57,9 @@ strict_optional = True [mypy-mesonbuild.mdist] strict_optional = True +[mypy-mesonbuild.mesondata] +strict_optional = True + [mypy-mesonbuild.mlog] strict_optional = True From 7b6de79f065d98ac39fcd456ce58bf4554bce5ee Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 9 Jun 2026 09:35:21 -0700 Subject: [PATCH 556/681] mesonmain: Turn on mypy strict optional --- .mypy.ini | 3 +++ mesonbuild/mesonmain.py | 7 ++++--- mesonbuild/scripts/env2mfile.py | 4 +++- mesonbuild/scripts/reprotest.py | 6 +++--- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/.mypy.ini b/.mypy.ini index c85c6d632378..3373bdb28045 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -60,6 +60,9 @@ strict_optional = True [mypy-mesonbuild.mesondata] strict_optional = True +[mypy-mesonbuild.mesonmain] +strict_optional = True + [mypy-mesonbuild.mlog] strict_optional = True diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index ee2f5c8a8ea9..a087ad27757b 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -57,7 +57,7 @@ def errorhandler(e: Exception, command: str) -> int: elif isinstance(e, OSError): mlog.exception(Exception("Unhandled python OSError. This is probably not a Meson bug, " "but an issue with your build environment.")) - return e.errno + return e.errno or 0 else: # Exception msg = 'Unhandled python exception' if all(getattr(e, a, None) is not None for a in ['file', 'lineno', 'colno']): @@ -126,7 +126,8 @@ def __init__(self) -> None: help_msg=argparse.SUPPRESS) def add_command(self, name: str, add_arguments_func: T.Callable[[argparse.ArgumentParser], None], - run_func: T.Callable[[argparse.Namespace], int], help_msg: str, aliases: T.List[str] = None) -> None: + run_func: T.Callable[[argparse.Namespace], int], help_msg: str, + aliases: T.List[str] | None = None) -> None: aliases = aliases or [] # FIXME: Cannot have hidden subparser: # https://bugs.python.org/issue22848 @@ -185,7 +186,7 @@ def run(self, args: T.List[str]) -> int: command = None from . import mesonlib - args = mesonlib.expand_arguments(args) + args = mesonlib.unwrap_err(mesonlib.expand_arguments(args), 'Failed to expand arguments') options = T.cast('MesonMainCMDOptions', parser.parse_args(args)) if command is None: diff --git a/mesonbuild/scripts/env2mfile.py b/mesonbuild/scripts/env2mfile.py index ee973a923902..2703bdde9d39 100755 --- a/mesonbuild/scripts/env2mfile.py +++ b/mesonbuild/scripts/env2mfile.py @@ -537,7 +537,7 @@ def process_ndk(self, ndk: pathlib.Path) -> None: ofile.write("endian = 'little'\n") -def run(options: T.Any) -> None: +def run(options: argparse.Namespace) -> int: mlog.notice('This functionality is experimental and subject to change.') if options.cross: if options.use_for_build: @@ -554,3 +554,5 @@ def run(options: T.Any) -> None: ad.detect_toolchains() else: raise ValueError("Encountered unreachable code-path") + + return 0 diff --git a/mesonbuild/scripts/reprotest.py b/mesonbuild/scripts/reprotest.py index fc9315c8f21f..ef9a4cb7211a 100755 --- a/mesonbuild/scripts/reprotest.py +++ b/mesonbuild/scripts/reprotest.py @@ -114,10 +114,10 @@ def check_output(self) -> None: self.check_contents('buildrepro', 'buildrepro.1st', True) self.check_contents('buildrepro.1st', 'buildrepro', False) -def run(options: T.Any) -> None: +def run(options: argparse.Namespace) -> int: rt = ReproTester(options) try: - sys.exit(rt.run()) + return rt.run() except Exception as e: print(e) - sys.exit(1) + return 1 From feee3a3546944473f4660e6674f8c0121a380d70 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 9 Jun 2026 09:37:30 -0700 Subject: [PATCH 557/681] minit: Turn on mypy strict optional --- .mypy.ini | 3 +++ mesonbuild/minit.py | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.mypy.ini b/.mypy.ini index 3373bdb28045..56e8a16a2560 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -63,6 +63,9 @@ strict_optional = True [mypy-mesonbuild.mesonmain] strict_optional = True +[mypy-mesonbuild.minit] +strict_optional = True + [mypy-mesonbuild.mlog] strict_optional = True diff --git a/mesonbuild/minit.py b/mesonbuild/minit.py index 7d0d051ad7d7..9b843668bf04 100644 --- a/mesonbuild/minit.py +++ b/mesonbuild/minit.py @@ -198,7 +198,8 @@ def run(options: Arguments) -> int: if vsenv_active: mlog.log(mlog.green('INFO:'), 'automatically activated MSVC compiler environment') - cmd = detect_ninja() + ['-C', options.builddir] + ninja = mesonlib.unwrap_err(detect_ninja(), 'Could not detect ninja and it is required') + cmd = ninja + ['-C', options.builddir] ret = subprocess.run(cmd) if ret.returncode: raise SystemExit From de69a6ebe82b59a4f78725830b318dc2ce0515cb Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 16 Jun 2026 13:19:32 -0700 Subject: [PATCH 558/681] backend/backends: Make TargetInstallData.install_mode non-optional --- mesonbuild/backend/backends.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 5ec516806e28..43418b572664 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -27,7 +27,7 @@ from ..compilers import detect, lang_suffixes from ..mesonlib import ( File, MachineChoice, MesonException, MesonBugException, OrderedSet, - ExecutableSerialisation, EnvironmentException, + ExecutableSerialisation, EnvironmentException, FileMode, classify_unity_sources, get_compiler_for_source, get_rsp_threshold, unique_list ) @@ -42,7 +42,6 @@ from ..environment import Environment from ..interpreter import Test from ..linkers import StaticLinker - from ..mesonlib import FileMode from ..options import ElementaryOptionValues from typing_extensions import Literal, TypedDict, NotRequired, TypeAlias @@ -146,8 +145,7 @@ class TargetInstallData: install_name_mappings: T.Mapping[str, str] rpath_dirs_to_remove: T.Set[bytes] install_rpath: str - # TODO: install_mode should just always be a FileMode object - install_mode: T.Optional['FileMode'] + install_mode: FileMode subproject: str system: str optional: bool = False @@ -1728,7 +1726,7 @@ def generate_target_install(self, d: InstallData) -> None: 'using the default installation directory for an output.' raise MesonException(m.format(t.name, num_out, t.get_outputs(), num_outdirs, outdirs)) assert len(t.install_tag) == num_out - install_mode = t.get_custom_install_mode() + install_mode = t.get_custom_install_mode() or FileMode() # because mypy gets confused type narrowing in lists first_outdir = outdirs[0] first_outdir_name = install_dir_names[0] From e52dafea4ec25e3c19db547488638105c1d26277 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 9 Jun 2026 09:55:31 -0700 Subject: [PATCH 559/681] minstall: Turn on mypy strict optional --- .mypy.ini | 3 +++ mesonbuild/minstall.py | 19 +++++++++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/.mypy.ini b/.mypy.ini index 56e8a16a2560..fe481a84d7ea 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -66,6 +66,9 @@ strict_optional = True [mypy-mesonbuild.minit] strict_optional = True +[mypy-mesonbuild.minstall] +strict_optional = True + [mypy-mesonbuild.mlog] strict_optional = True diff --git a/mesonbuild/minstall.py b/mesonbuild/minstall.py index 39d966a2d34e..8839f3725df5 100644 --- a/mesonbuild/minstall.py +++ b/mesonbuild/minstall.py @@ -18,10 +18,12 @@ from . import build, tooldetect from .backend.backends import InstallData from .mesonlib import (MesonException, Popen_safe, RealPathAction, is_windows, - is_aix, setup_vsenv, path_has_root, pickle_load, is_osx) + is_aix, setup_vsenv, path_has_root, pickle_load, is_osx, + unwrap) from .options import OptionKey from .scripts import depfixer, destdir_join from .scripts.meson_exe import run_exe +main_file: str | None try: from __main__ import __file__ as main_file except ImportError: @@ -165,7 +167,11 @@ def set_chown(path: str, user: T.Union[str, int, None] = None, if sys.version_info >= (3, 13): # pylint: disable=unexpected-keyword-arg # cannot handle sys.version_info, https://github.com/pylint-dev/pylint/issues/9622 - shutil.chown(path, user, group, dir_fd=dir_fd, follow_symlinks=follow_symlinks) + # + # Mypy does not understand that the assert above ensures that user, + # group is either `None, int | str` or `int | str, None`, so we need to + # ignore the warning + shutil.chown(path, user, group, dir_fd=dir_fd, follow_symlinks=follow_symlinks) # type: ignore[arg-type] else: real_os_chown = os.chown @@ -181,7 +187,10 @@ def chown(path: T.Union[int, str, 'os.PathLike[str]', bytes, 'os.PathLike[bytes] try: os.chown = chown - shutil.chown(path, user, group) + # Mypy does not understand that the assert above ensures that user, + # group is either `None, int | str` or `int | str, None`, so we need to + # ignore the warning + shutil.chown(path, user, group) # type: ignore[arg-type] finally: os.chown = real_os_chown @@ -577,6 +586,7 @@ def do_install(self, datafilename: str) -> None: if is_windows() or destdir != '' or not os.isatty(sys.stdout.fileno()) or not os.isatty(sys.stderr.fileno()): # can't elevate to root except in an interactive unix environment *and* when not doing a destdir install raise + rootcmd = ( os.environ.get('MESON_ROOT_CMD') or shutil.which('sudo') @@ -606,7 +616,8 @@ def do_install(self, datafilename: str) -> None: if ans is not None: raise MesonException('Answer not one of [y/n]') if ans == 'y': - os.execlp(rootcmd, rootcmd, sys.executable, main_file, *sys.argv[1:], + mf = unwrap(main_file, 'main_file should only be None on Windows') + os.execlp(rootcmd, rootcmd, sys.executable, mf, *sys.argv[1:], '-C', os.getcwd(), '--no-rebuild') raise From 8cd27e2ed464fdc81b4d4725f32e0aceb2f25eb2 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 10 Jun 2026 09:51:04 -0700 Subject: [PATCH 560/681] programs: Fix strict-null issues ExternalProgram has as weird way of setting self.command to `list[str | None]`, but it should only be `list[None]` if `self.command = [None]`. Instead, just set self.command to `list[str]`, and use an empty list as the sentinel value. This also fixes a few issues where we use Any or reduce to Any. --- .mypy.ini | 3 +++ mesonbuild/programs.py | 31 ++++++++++++++++--------------- unittests/failuretests.py | 2 +- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/.mypy.ini b/.mypy.ini index fe481a84d7ea..42bd17e1a1c6 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -72,5 +72,8 @@ strict_optional = True [mypy-mesonbuild.mlog] strict_optional = True +[mypy-mesonbuild.programs] +strict_optional = True + [mypy-mesonbuild.tooldetect] strict_optional = True diff --git a/mesonbuild/programs.py b/mesonbuild/programs.py index faa03306a9cd..4d4ea9ea5aa5 100644 --- a/mesonbuild/programs.py +++ b/mesonbuild/programs.py @@ -74,6 +74,7 @@ class ExternalProgram(Program): :param exclude_paths: A list of directories to exclude when searching in PATH""" windows_exts = ('exe', 'msc', 'com', 'bat', 'cmd') + command: list[str] def __init__(self, name: str, command: T.Optional[T.List[str]] = None, silent: bool = False, search_dirs: T.Optional[T.List[T.Optional[str]]] = None, @@ -83,7 +84,7 @@ def __init__(self, name: str, command: T.Optional[T.List[str]] = None, self.cached_version: T.Optional[str] = None self.version_arg = '--version' if command is not None: - self.command = mesonlib.listify(command) + self.command = command.copy() if mesonlib.is_windows(): cmd = self.command[0] args = self.command[1:] @@ -130,7 +131,7 @@ def __init__(self, name: str, command: T.Optional[T.List[str]] = None, else: mlog.log('Program', mlog.bold(name), 'found:', mlog.red('NO')) - def summary_value(self) -> T.Union[str, mlog.AnsiDecorator]: + def summary_value(self) -> T.Union[str, mlog.AnsiDecorator, None]: if not self.found(): return mlog.red('NO') return self.path @@ -222,7 +223,7 @@ def from_entry(name: str, command: T.Union[str, T.List[str]]) -> 'ExternalProgra return ExternalProgram(command, silent=True) @staticmethod - def _shebang_to_cmd(script: str) -> T.Optional[T.List[str]]: + def _shebang_to_cmd(script: str) -> list[str]: """ Check if the file has a shebang and manually parse it to figure out the interpreter to use. This is useful if the script is not executable @@ -266,7 +267,7 @@ def _shebang_to_cmd(script: str) -> T.Optional[T.List[str]]: except Exception as e: mlog.debug(str(e)) mlog.debug(f'Unusable script {script!r}') - return None + return [] def _is_executable(self, path: str) -> bool: suffix = os.path.splitext(path)[-1].lower()[1:] @@ -278,9 +279,9 @@ def _is_executable(self, path: str) -> bool: return not os.path.isdir(path) return False - def _search_dir(self, name: str, search_dir: T.Optional[str]) -> T.Optional[list]: + def _search_dir(self, name: str, search_dir: T.Optional[str]) -> list[str]: if search_dir is None: - return None + return [] trial = os.path.join(search_dir, name) if os.path.exists(trial): if self._is_executable(trial): @@ -295,9 +296,9 @@ def _search_dir(self, name: str, search_dir: T.Optional[str]) -> T.Optional[list trial_ext = f'{trial}.{ext}' if os.path.exists(trial_ext): return [trial_ext] - return None + return [] - def _search_windows_special_cases(self, name: str, command: T.Optional[str], exclude_paths: T.Optional[T.List[str]]) -> T.List[T.Optional[str]]: + def _search_windows_special_cases(self, name: str, command: T.Optional[str], exclude_paths: T.Optional[T.List[str]]) -> list[str]: ''' Lots of weird Windows quirks: 1. PATH search for @name returns files with extensions from PATHEXT, @@ -327,7 +328,7 @@ def _search_windows_special_cases(self, name: str, command: T.Optional[str], exc commands = self._shebang_to_cmd(command) if commands: return commands - return [None] + return [] # Maybe the name is an absolute path to a native Windows # executable, but without the extension. This is technically wrong, # but many people do it because it works in the MinGW shell. @@ -346,9 +347,9 @@ def _search_windows_special_cases(self, name: str, command: T.Optional[str], exc commands = self._search_dir(name, search_dir) if commands: return commands - return [None] + return [] - def _search(self, name: str, search_dirs: T.List[T.Optional[str]], exclude_paths: T.Optional[T.List[str]]) -> T.List[T.Optional[str]]: + def _search(self, name: str, search_dirs: T.List[T.Optional[str]], exclude_paths: T.Optional[T.List[str]]) -> list[str]: ''' Search in the specified dirs for the specified executable by name and if not found search in PATH @@ -359,7 +360,7 @@ def _search(self, name: str, search_dirs: T.List[T.Optional[str]], exclude_paths return commands # If there is a directory component, do not look in PATH if os.path.dirname(name) and not os.path.isabs(name): - return [None] + return [] # Do a standard search in PATH path = os.environ.get('PATH', os.defpath) if mesonlib.is_windows() and path: @@ -377,13 +378,13 @@ def _search(self, name: str, search_dirs: T.List[T.Optional[str]], exclude_paths return self._search_windows_special_cases(name, command, exclude_paths) # On UNIX-like platforms, shutil.which() is enough to find # all executables whether in PATH or with an absolute path - return [command] + return [command] if command is not None else [] def runnable(self) -> bool: return self.found() def found(self) -> bool: - return self.command[0] is not None + return bool(self.command) def get_command(self) -> T.List[str]: return self.command[:] @@ -400,7 +401,7 @@ class NonExistingExternalProgram(ExternalProgram): # lgtm [py/missing-call-to-i def __init__(self, name: str = 'nonexistingprogram') -> None: self.name = name - self.command = [None] + self.command = [] self.path = None def __repr__(self) -> str: diff --git a/unittests/failuretests.py b/unittests/failuretests.py index 073ea5bd9353..5adf35777e4c 100644 --- a/unittests/failuretests.py +++ b/unittests/failuretests.py @@ -38,7 +38,7 @@ def no_pkgconfig(): def new_search(self, name, search_dirs, exclude_paths): if name == 'pkg-config': - return [None] + return [] return old_search(self, name, search_dirs, exclude_paths) def new_which(cmd, **kwargs): From e4c7e61ab99b0874adae9bd0c830d1da17d003df Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 7 Jun 2026 11:36:48 +0200 Subject: [PATCH 561/681] ninjabackend: add should_use_rspfile() method to NinjaRule Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index d56eab25785f..3138c097641f 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -273,8 +273,8 @@ def rule_iter() -> T.Iterable[str]: outfile.write('\n') outfile.write('\n') - def length_estimate(self, infiles: str, outfiles: str, - elems: T.List[T.Tuple[str, T.List[str]]]) -> int: + def _length_estimate(self, infiles: str, outfiles: str, + elems: T.List[T.Tuple[str, T.List[str]]]) -> int: # determine variables # this order of actions only approximates ninja's scoping rules, as # documented at: https://ninja-build.org/manual.html#ref_scope @@ -301,6 +301,17 @@ def length_estimate(self, infiles: str, outfiles: str, # determine command length return estimate + def should_use_rspfile(self, element: NinjaBuildElement) -> bool: + if not self.rspable: + return False + + infilenames = ' '.join([ninja_quote(i, True) for i in element.infilenames]) + outfilenames = ' '.join([ninja_quote(i, True) for i in element.outfilenames]) + + return self._length_estimate(infilenames, + outfilenames, + element.elems) >= rsp_threshold + class NinjaBuildElement: rule: NinjaRule @@ -353,15 +364,7 @@ def _should_use_rspfile(self) -> bool: if self.rulename == 'phony': return False - if not self.rule.rspable: - return False - - infilenames = ' '.join([ninja_quote(i, True) for i in self.infilenames]) - outfilenames = ' '.join([ninja_quote(i, True) for i in self.outfilenames]) - - return self.rule.length_estimate(infilenames, - outfilenames, - self.elems) >= rsp_threshold + return self.rule.should_use_rspfile(self) def count_rule_references(self) -> None: if self.rulename != 'phony': From d23570a90b4f52fb720a0a03d46a477dca616ff2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 7 Jun 2026 11:35:25 +0200 Subject: [PATCH 562/681] ninjabackend: extract NinjaBuild object Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 98 +++++++++++++++++++----------- 1 file changed, 62 insertions(+), 36 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 3138c097641f..8abec7d4f906 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -10,6 +10,7 @@ from functools import lru_cache from pathlib import PurePath, Path from textwrap import dedent +import dataclasses import itertools import json import os @@ -444,6 +445,59 @@ def check_outputs(self) -> None: self.output_errors = f'Multiple producers for Ninja target "{n}". Please rename your targets.' self.all_outputs.add(n) +@dataclass +class NinjaBuild: + rules: list[NinjaRule | NinjaComment] = dataclasses.field(default_factory=list, init=False) + ruledict: dict[str, NinjaRule] = dataclasses.field(default_factory=dict, init=False) + build_elements: list[NinjaBuildElement | NinjaComment] = dataclasses.field(default_factory=list, init=False) + + def add_rule_comment(self, comment: NinjaComment) -> None: + self.rules.append(comment) + + def add_build_comment(self, comment: NinjaComment) -> None: + self.build_elements.append(comment) + + def add_rule(self, rule: NinjaRule) -> None: + if rule.name in self.ruledict: + raise MesonException(f'Tried to add rule {rule.name} twice.') + self.rules.append(rule) + self.ruledict[rule.name] = rule + + def has_rule(self, name: str) -> bool: + return name in self.ruledict + + def should_use_rspfile(self, build: NinjaBuildElement) -> bool: + if build.rulename == 'phony': + return False + + if build.rulename in self.ruledict: + return self.ruledict[build.rulename].should_use_rspfile(build) + + mlog.warning(f"build statement for {build.outfilenames} references nonexistent rule {build.rulename}") + return False + + def add_build(self, build: NinjaBuildElement) -> None: + build.check_outputs() + self.build_elements.append(build) + + if build.rulename != 'phony': + # reference rule + if build.rulename in self.ruledict: + build.rule = self.ruledict[build.rulename] + else: + mlog.warning(f"build statement for {build.outfilenames} references nonexistent rule {build.rulename}") + + def write(self, outfile: T.TextIO) -> None: + for b in self.build_elements: + if isinstance(b, NinjaBuildElement): + b.count_rule_references() + + for r in self.rules: + r.write(outfile) + + for b in ProgressBar(self.build_elements, desc='Writing build.ninja'): + b.write(outfile) + @dataclass class RustDep: @@ -505,6 +559,7 @@ class NinjaBackend(backends.Backend): def __init__(self, build: T.Optional[build.Build]): super().__init__(build) self.name = 'ninja' + self.ninja = NinjaBuild() self.ninja_filename = 'build.ninja' self.fortran_deps: T.Dict[str, T.Dict[str, File]] = {} self.all_outputs: T.Set[str] = set() @@ -670,8 +725,6 @@ def generate(self, capture: bool = False, vslite_ctx: T.Optional[T.Dict] = None) with self.detect_vs_dep_prefix(tempfilename) as outfile: self.generate_rules() - - self.build_elements: T.List[T.Union[NinjaBuildElement, NinjaComment]] = [] self.generate_phony() self.add_build_comment(NinjaComment('Build rules for targets')) @@ -710,8 +763,8 @@ def generate(self, capture: bool = False, vslite_ctx: T.Optional[T.Dict] = None) mlog.log_timestamp("Utils generated") self.generate_ending() - self.write_rules(outfile) - self.write_builds(outfile) + self.ninja.write(outfile) + mlog.log_timestamp("build.ninja generated") default = 'default all\n\n' outfile.write(default) @@ -1411,9 +1464,6 @@ def generate_tests(self) -> None: self.add_build(elem) def generate_rules(self) -> None: - self.rules: T.List[T.Union[NinjaRule, NinjaComment]] = [] - self.ruledict: T.Dict[str, NinjaRule] = {} - self.add_rule_comment(NinjaComment('Rules for module scanning.')) self.generate_scanner_rules() self.add_rule_comment(NinjaComment('Rules for compiling.')) @@ -1449,40 +1499,16 @@ def generate_rules(self) -> None: extra='generator = 1')) def add_rule_comment(self, comment: NinjaComment) -> None: - self.rules.append(comment) + self.ninja.add_rule_comment(comment) def add_build_comment(self, comment: NinjaComment) -> None: - self.build_elements.append(comment) + self.ninja.add_build_comment(comment) def add_rule(self, rule: NinjaRule) -> None: - if rule.name in self.ruledict: - raise MesonException(f'Tried to add rule {rule.name} twice.') - self.rules.append(rule) - self.ruledict[rule.name] = rule + self.ninja.add_rule(rule) def add_build(self, build: NinjaBuildElement) -> None: - build.check_outputs() - self.build_elements.append(build) - - if build.rulename != 'phony': - # reference rule - if build.rulename in self.ruledict: - build.rule = self.ruledict[build.rulename] - else: - mlog.warning(f"build statement for {build.outfilenames} references nonexistent rule {build.rulename}") - - def write_rules(self, outfile: T.TextIO) -> None: - for b in self.build_elements: - if isinstance(b, NinjaBuildElement): - b.count_rule_references() - - for r in self.rules: - r.write(outfile) - - def write_builds(self, outfile: T.TextIO) -> None: - for b in ProgressBar(self.build_elements, desc='Writing build.ninja'): - b.write(outfile) - mlog.log_timestamp("build.ninja generated") + self.ninja.add_build(build) def generate_phony(self) -> None: self.add_build_comment(NinjaComment('Phony build target, always out of date')) @@ -2771,7 +2797,7 @@ def generate_pch_rule_for(self, langname: str, compiler: Compiler) -> None: def generate_scanner_rules(self) -> None: rulename = 'depscan' - if rulename in self.ruledict: + if self.ninja.has_rule(rulename): # Scanning command is the same for native and cross compilation. return From 739e86f3e4457a3935add20bb0c462bf9264a20a Mon Sep 17 00:00:00 2001 From: "L. E. Segovia" Date: Fri, 5 Jun 2026 00:33:56 -0300 Subject: [PATCH 563/681] nasm: use the correct flag for response file support, try 2 This commit reenables and implements correct response file support for Nasm. Fortified with the knowledge of #15867 and #15872, and with a quick chat with @nirbheek, I realised that the easiest way to fix this (since Ninja does not support custom argument spacing) is to do it the obvious way, with Meson's custom command support. This commit implements an abridged version of that: when a non-standard RspFileSyntax value is detected, the active NinjaBuildRule is temporarily resolved to the corresponding NinjaRule on the fly and if the latter says that a response file will be used, the NinjaBuildRule is immediately converted into a CUSTOM_COMMAND, completing the arguments with the bits for infile and outfile and triggering the executable serialization. That serializes the response file and pickles the command. Fixes building mpv with MSVC 2022+ and fdk-aac enabled. --- mesonbuild/backend/backends.py | 18 ++++++++++++------ mesonbuild/backend/ninjabackend.py | 25 ++++++++++++++++++++++++- mesonbuild/compilers/asm.py | 8 +++----- mesonbuild/linkers/base.py | 1 + 4 files changed, 40 insertions(+), 12 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 43418b572664..98804ad2e56d 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -549,10 +549,11 @@ def get_executable_serialisation( feed: T.Optional[str] = None, env: T.Optional[mesonlib.EnvironmentVariables] = None, can_use_rsp_file: bool = False, + separator: str = ' ', + rsp_file_flag: str = '@', tag: T.Optional[str] = None, verbose: bool = False, installdir_map: T.Optional[T.Dict[str, str]] = None) -> 'ExecutableSerialisation': - # XXX: cmd_args either need to be lowered to strings, or need to be checked for non-string arguments, right? exe, *raw_cmd_args = cmd if isinstance(exe, build.LocalProgram): @@ -618,14 +619,14 @@ def get_executable_serialisation( if needs_rsp_file: hasher = hashlib.sha1() - args = ' '.join(mesonlib.quote_arg(arg) for arg in cmd_args) + args = separator.join(mesonlib.quote_arg(arg) for arg in cmd_args) hasher.update(args.encode(encoding='utf-8', errors='ignore')) digest = hasher.hexdigest() scratch_file = f'meson_rsp_{digest}.rsp' rsp_file = os.path.join(self.environment.get_scratch_dir(), scratch_file) with open(rsp_file, 'w', encoding='utf-8', newline='\n') as f: f.write(args) - cmd_args = [f'@{rsp_file}'] + cmd_args = [f'{rsp_file_flag}{rsp_file}'] return ExecutableSerialisation(exe_cmd + cmd_args, env, exe_wrapper, workdir, @@ -640,6 +641,8 @@ def as_meson_exe_cmdline(self, exe: build.CommandTypes, force_serialize: bool = False, env: T.Optional[mesonlib.EnvironmentVariables] = None, can_use_rsp_file: bool = False, + separator: str = ' ', + rsp_file_flag: str = '@', verbose: bool = False) -> T.Tuple[T.List[str], str]: ''' Serialize an executable for running with a generator or a custom target @@ -647,7 +650,7 @@ def as_meson_exe_cmdline(self, exe: build.CommandTypes, cmd: T.List[build.CommandTypes] = [] cmd.append(exe) cmd.extend(cmd_args) - es = self.get_executable_serialisation(cmd, workdir, extra_bdeps, capture, feed, env, can_use_rsp_file, verbose=verbose) + es = self.get_executable_serialisation(cmd, workdir, extra_bdeps, capture, feed, env, can_use_rsp_file, separator=separator, rsp_file_flag=rsp_file_flag, verbose=verbose) reasons: T.List[str] = [] if es.extra_paths: reasons.append('to set PATH') @@ -664,6 +667,9 @@ def as_meson_exe_cmdline(self, exe: build.CommandTypes, if env and env.varnames: reasons.append('to set env') + if separator != ' ': + reasons.append('to use a custom argument separator') + # force_serialize passed to this function means that the VS backend has # decided it absolutely cannot use real commands. This is "always", # because it's not clear what will work (other than compilers) and so @@ -673,7 +679,7 @@ def as_meson_exe_cmdline(self, exe: build.CommandTypes, # It's also overridden for a few conditions that can't be handled # inside a command line - can_use_env = env.can_use_env and not force_serialize + can_use_env = env and env.can_use_env and not force_serialize force_serialize = force_serialize or bool(reasons) if capture: @@ -687,7 +693,7 @@ def as_meson_exe_cmdline(self, exe: build.CommandTypes, envlist.append(f'{k}={v}') return ['env'] + envlist + es.cmd_args, ', '.join(reasons) - if any(a.startswith('@') for a in es.cmd_args): + if can_use_rsp_file and any(a.startswith(rsp_file_flag) for a in es.cmd_args): reasons.append('because command is too long') if not force_serialize: diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 8abec7d4f906..98692dd164f6 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -365,6 +365,9 @@ def _should_use_rspfile(self) -> bool: if self.rulename == 'phony': return False + if not self.rule: + raise MesonBugException(f"build statement for {self.outfilenames} references unmapped rule {self.rulename}") + return self.rule.should_use_rspfile(self) def count_rule_references(self) -> None: @@ -3370,7 +3373,27 @@ def quote_make_target(targetName: str) -> str: result += c return result element.add_item('CUDA_ESCAPED_TARGET', quote_make_target(rel_obj)) - element.add_item('ARGS', commands) + if self.ninja.should_use_rspfile(element) and compiler.rsp_file_syntax() == RSPFileSyntax.NASM: + exe = compiler.get_exelist() + # Add to commands the args created by generate_compile_rule_for(). + # commands remain separate from exelist because they must stay + # a CompilerArgs. + if dep_file: + commands += compiler.get_dependency_gen_args(rel_obj, dep_file) + commands += [*compiler.get_output_args(rel_obj), *compiler.get_compile_only_args(), rel_src] + + element.rulename = 'CUSTOM_COMMAND' + meson_exe_cmd, reason = self.as_meson_exe_cmdline(exe[0], + exe[1:] + commands.to_native(), + separator='\n', + rsp_file_flag='-@', + can_use_rsp_file=True, + verbose=True) + cmd_type = f' (wrapped by meson {reason})' if reason else '' + element.add_item('COMMAND', meson_exe_cmd) + element.add_item('description', f'Compiling {compiler.get_display_language()} object {rel_obj}{cmd_type}') + else: + element.add_item('ARGS', commands) self.add_dependency_scanner_entries_to_element(target, compiler, element, src) self.add_build(element) diff --git a/mesonbuild/compilers/asm.py b/mesonbuild/compilers/asm.py index f5e725862366..81faff97e366 100644 --- a/mesonbuild/compilers/asm.py +++ b/mesonbuild/compilers/asm.py @@ -6,6 +6,7 @@ from ..mesonlib import EnvironmentException, get_meson_command from ..options import OptionKey from .compilers import Compiler +from ..linkers import RSPFileSyntax from ..linkers.linkers import VisualStudioLikeLinkerMixin from .mixins.metrowerks import MetrowerksCompiler, mwasmarm_instruction_set_args, mwasmeppc_instruction_set_args from .mixins.ti import TICompiler @@ -159,11 +160,8 @@ def get_crt_link_args(self, crt_val: str) -> T.List[str]: return [] return self.crt_args[self.get_crt_val(crt_val)] - def can_linker_accept_rsp(self) -> bool: - """ - Determines whether the linker can accept arguments using the @rsp syntax. - """ - return False + def rsp_file_syntax(self) -> RSPFileSyntax: + return RSPFileSyntax.NASM class YasmCompiler(NasmCompiler): id = 'yasm' diff --git a/mesonbuild/linkers/base.py b/mesonbuild/linkers/base.py index 68fdb2ea3a70..d35a8df4c1c9 100644 --- a/mesonbuild/linkers/base.py +++ b/mesonbuild/linkers/base.py @@ -19,6 +19,7 @@ class RSPFileSyntax(enum.Enum): MSVC = enum.auto() GCC = enum.auto() TASKING = enum.auto() + NASM = enum.auto() class ArLikeLinker: From 5461ecf31ea892fccfe68e2bb015c343be213a77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20=22sp1rit=22=E2=80=8B?= Date: Wed, 10 Jun 2026 11:50:34 +0200 Subject: [PATCH 564/681] find_program: Respect --force-fallback-for Previously, find_program only used the subproject fallback if it didn't find the tool the normal way or global forcefallback was activated. Now it should also fall back if the subproject that provides the fallback is part of force-fallback-for. --- docs/markdown/Subprojects.md | 37 +++++++++++-------- .../snippets/find_program-forcefallback.md | 5 +++ mesonbuild/interpreter/interpreter.py | 12 ++++-- test cases/unit/31 forcefallback/meson.build | 3 ++ .../subprojects/notfalse.wrap | 5 +++ .../subprojects/notfalse/meson.build | 4 ++ .../subprojects/notfalse/notfalse.c | 3 ++ unittests/allplatformstests.py | 4 +- 8 files changed, 51 insertions(+), 22 deletions(-) create mode 100644 docs/markdown/snippets/find_program-forcefallback.md create mode 100644 test cases/unit/31 forcefallback/subprojects/notfalse.wrap create mode 100644 test cases/unit/31 forcefallback/subprojects/notfalse/meson.build create mode 100644 test cases/unit/31 forcefallback/subprojects/notfalse/notfalse.c diff --git a/docs/markdown/Subprojects.md b/docs/markdown/Subprojects.md index 0375b5698160..920971156500 100644 --- a/docs/markdown/Subprojects.md +++ b/docs/markdown/Subprojects.md @@ -227,34 +227,35 @@ the following command-line options: * **--wrap-mode=nofallback** Meson will not use subproject fallbacks for any dependency - declarations in the build files, and will only look for them in the - system. Note that this does not apply to unconditional subproject() - calls, and those are meant to be used for sources that cannot be - provided by the system, such as copylibs. + declarations and program lookups in the build files, and will only + look for them in the system. Note that this does not apply to + unconditional subproject() calls, and those are meant to be used for + sources that cannot be provided by the system, such as copylibs. This option may be overridden by `--force-fallback-for` for specific dependencies. * **--wrap-mode=forcefallback** - Meson will not look at the system for any dependencies which have - subproject fallbacks available, and will *only* use subprojects for - them. This is useful when you want to test your fallback setup, or - want to specifically build against the library sources provided by - your subprojects. + Meson will not look at the system for any dependencies and programs + which have subproject fallbacks available, and will *only* use + subprojects for them. This is useful when you want to test your + fallback setup, or want to specifically build against the library + sources provided by your subprojects. -* **--force-fallback-for=list,of,dependencies** +* **--force-fallback-for=list,of,dependencies,or,subprojects** - Meson will not look at the system for any dependencies listed there, - provided a fallback was supplied when the dependency was declared. + Meson will not look at the system for any dependencies and + subprojects listed there, provided a fallback was supplied in the + wrapfile or when the dependency was declared. This option takes precedence over `--wrap-mode=nofallback`, and when used in combination with `--wrap-mode=nodownload` will only work - if the dependency has already been downloaded. + if the subproject has already been downloaded. - This is useful when your project has many fallback dependencies, - but you only want to build against the library sources for a few - of them. + This is useful when your project has many fallback dependencies or + programs, but you only want to build against the library sources for + a few of them. **Warning**: This could lead to mixing system and subproject version of the same library in the same process. Take this case as example: @@ -278,6 +279,10 @@ the following command-line options: `glib-2.0` must also be forced to fallback, in this case with `--force-fallback-for=glib,gsteamer`. + Starting with version 1.12, meson will also fallback into evaluating a + subproject during find_program, if a wrap file indicates it provides the + requested program and the wrap name of the is listed in `--force-fallback-for`. + * **--wrap-mode=nopromote** *Since 0.56.0* Meson will automatically use wrap files found in subprojects diff --git a/docs/markdown/snippets/find_program-forcefallback.md b/docs/markdown/snippets/find_program-forcefallback.md new file mode 100644 index 000000000000..5ed99de8a785 --- /dev/null +++ b/docs/markdown/snippets/find_program-forcefallback.md @@ -0,0 +1,5 @@ +## find_program() now respects --force-fallback-for + +If a subproject listed in --force-fallback-for provides a particular +program (via program_names in its wrapfile), find_program will use the +program from the subproject instead of looking it up from the system. diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 316884e7c081..3c4a8c6c8841 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1817,13 +1817,17 @@ def program_lookup(self, args: T.List[mesonlib.FileOrString], for_machine: Machi # Override find_program('meson') to return what we were invoked with return ExternalProgram('meson', self.environment.get_build_command(), silent=True) - fallback: SubProject | None = None wrap_mode_s = self.coredata.optstore.get_value_for(OptionKey('wrap_mode')) + force_fallback_for = self.coredata.optstore.get_value_for(OptionKey('force_fallback_for')) assert isinstance(wrap_mode_s, str), 'for mypy' + assert isinstance(force_fallback_for, list), 'for mypy' wrap_mode = WrapMode.from_string(wrap_mode_s) - if wrap_mode != WrapMode.nofallback and self.environment.wrap_resolver: - fallback = self.environment.wrap_resolver.find_program_provider(args) - if fallback and wrap_mode == WrapMode.forcefallback: + + fallback: SubProject | None = \ + self.environment.wrap_resolver.find_program_provider(args) \ + if self.environment.wrap_resolver is not None \ + else None + if fallback and (wrap_mode == WrapMode.forcefallback or fallback in force_fallback_for): return self.find_program_fallback(fallback, args, for_machine, default_options, required, extra_info) progobj = self.program_from_file_for(for_machine, args) diff --git a/test cases/unit/31 forcefallback/meson.build b/test cases/unit/31 forcefallback/meson.build index 8d84a603577f..a0cc613a9313 100644 --- a/test cases/unit/31 forcefallback/meson.build +++ b/test cases/unit/31 forcefallback/meson.build @@ -7,3 +7,6 @@ notfound_dep = dependency('cannotabletofind', fallback: ['definitelynotfound', ' test_not_zlib = executable('test_not_zlib', ['test_not_zlib.c'], dependencies: [zlib_dep, notfound_dep]) test('test_not_zlib', test_not_zlib) + +false_bin = find_program('false') +test('test_not_false', false_bin) diff --git a/test cases/unit/31 forcefallback/subprojects/notfalse.wrap b/test cases/unit/31 forcefallback/subprojects/notfalse.wrap new file mode 100644 index 000000000000..a06c47e6054c --- /dev/null +++ b/test cases/unit/31 forcefallback/subprojects/notfalse.wrap @@ -0,0 +1,5 @@ +[wrap-file] +directory = notfalse + +[provide] +program_names = false diff --git a/test cases/unit/31 forcefallback/subprojects/notfalse/meson.build b/test cases/unit/31 forcefallback/subprojects/notfalse/meson.build new file mode 100644 index 000000000000..1e3429c358a2 --- /dev/null +++ b/test cases/unit/31 forcefallback/subprojects/notfalse/meson.build @@ -0,0 +1,4 @@ +project('notfalse', 'c') + +notfalse_bin = executable('notfalse', 'notfalse.c') +meson.override_find_program('false', notfalse_bin) diff --git a/test cases/unit/31 forcefallback/subprojects/notfalse/notfalse.c b/test cases/unit/31 forcefallback/subprojects/notfalse/notfalse.c new file mode 100644 index 000000000000..9b130982db52 --- /dev/null +++ b/test cases/unit/31 forcefallback/subprojects/notfalse/notfalse.c @@ -0,0 +1,3 @@ +int main(int argc, char** argv) { + return 0; +} diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 15aa5b243357..b52ff2518836 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -708,13 +708,13 @@ def test_nopromote(self): def test_force_fallback_for(self): testdir = os.path.join(self.unit_test_dir, '31 forcefallback') - self.init(testdir, extra_args=['--force-fallback-for=zlib,foo']) + self.init(testdir, extra_args=['--force-fallback-for=zlib,foo,notfalse']) self.build() self.run_tests() def test_force_fallback_for_nofallback(self): testdir = os.path.join(self.unit_test_dir, '31 forcefallback') - self.init(testdir, extra_args=['--force-fallback-for=zlib,foo', '--wrap-mode=nofallback']) + self.init(testdir, extra_args=['--force-fallback-for=zlib,foo,notfalse', '--wrap-mode=nofallback']) self.build() self.run_tests() From dba79292df8ef33ecd7c9fab643666d7fd87b9ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Est=C3=A8ve?= Date: Sat, 1 Nov 2025 10:20:45 +0100 Subject: [PATCH 565/681] Make cython generated targets less nested --- mesonbuild/backend/ninjabackend.py | 18 +++++++++-- .../cython/5 nested folders/meson.build | 25 +++++++++++++++ .../5 nested folders/package/meson.build | 1 + .../_radius_neighbors_classmode.pyx | 2 ++ .../_pairwise_distances_reduction/meson.build | 7 ++++ .../5 nested folders/package/metrics/gen.py | 14 ++++++++ .../package/metrics/meson.build | 13 ++++++++ .../package/metrics/metric.pyx | 2 ++ test cases/cython/5 nested folders/test.json | 20 ++++++++++++ unittests/allplatformstests.py | 32 +++++++++++++++++++ 10 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 test cases/cython/5 nested folders/meson.build create mode 100644 test cases/cython/5 nested folders/package/meson.build create mode 100644 test cases/cython/5 nested folders/package/metrics/_pairwise_distances_reduction/_radius_neighbors_classmode.pyx create mode 100644 test cases/cython/5 nested folders/package/metrics/_pairwise_distances_reduction/meson.build create mode 100644 test cases/cython/5 nested folders/package/metrics/gen.py create mode 100644 test cases/cython/5 nested folders/package/metrics/meson.build create mode 100644 test cases/cython/5 nested folders/package/metrics/metric.pyx create mode 100644 test cases/cython/5 nested folders/test.json diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 98692dd164f6..19135af1f8dc 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1903,10 +1903,21 @@ def generate_cython_transpile(self, target: build.BuildTarget) -> \ ext = self.get_target_option(target, OptionKey('cython_language', machine=target.for_machine)) pyx_sources = [] # Keep track of sources we're adding to build + pyx_count = 0 + for src in target.get_sources(): + if src.endswith('.pyx'): + pyx_count += 1 + for gen in target.get_generated_sources(): + for ssrc in gen.get_outputs(): + if ssrc.endswith('.pyx'): + pyx_count += 1 for src in target.get_sources(): if src.endswith('.pyx'): - output = os.path.join(self.get_target_private_dir(target), f'{src}.{ext}') + # Use basename to avoid too nested targets which can cause a + # problem with MAX_PATH on Windows + outname = os.path.basename(src.fname) if pyx_count == 1 else src.fname + output = os.path.join(self.get_target_private_dir(target), f'{outname}.{ext}') element = NinjaBuildElement( self.all_outputs, [output], self.compiler_to_rule_name(cython), @@ -1928,7 +1939,10 @@ def generate_cython_transpile(self, target: build.BuildTarget) -> \ for ssrc in gen.get_outputs(): ssrc = os.path.join(builddir, ssrc) if ssrc.endswith('.pyx'): - output = os.path.join(self.get_target_private_dir(target), f'{ssrc}.{ext}') + # Use basename to avoid too nested targets which can cause + # a problem with MAX_PATH on Windows + outname = os.path.basename(ssrc) if pyx_count == 1 else ssrc + output = os.path.join(self.get_target_private_dir(target), f'{outname}.{ext}') element = NinjaBuildElement( self.all_outputs, [output], self.compiler_to_rule_name(cython), diff --git a/test cases/cython/5 nested folders/meson.build b/test cases/cython/5 nested folders/meson.build new file mode 100644 index 000000000000..52da9aa2ce6d --- /dev/null +++ b/test cases/cython/5 nested folders/meson.build @@ -0,0 +1,25 @@ +project( + 'nested folders', + ['c', 'cpp', 'cython'], + default_options : ['buildtype=release'], +) + +if meson.backend() != 'ninja' + error('MESON_SKIP_TEST: Ninja backend required') +endif + +py = import('python').find_installation() +py_dep = py.dependency(required: false) + +if not py_dep.found() + error('MESON_SKIP_TEST: Python library not found.') +endif + +metric = py.extension_module( + 'metric', + 'package/metrics/metric.pyx', + subdir: 'package/metrics', + install: true +) + +subdir('package') diff --git a/test cases/cython/5 nested folders/package/meson.build b/test cases/cython/5 nested folders/package/meson.build new file mode 100644 index 000000000000..c9b31425a138 --- /dev/null +++ b/test cases/cython/5 nested folders/package/meson.build @@ -0,0 +1 @@ +subdir('metrics') diff --git a/test cases/cython/5 nested folders/package/metrics/_pairwise_distances_reduction/_radius_neighbors_classmode.pyx b/test cases/cython/5 nested folders/package/metrics/_pairwise_distances_reduction/_radius_neighbors_classmode.pyx new file mode 100644 index 000000000000..f174823854e7 --- /dev/null +++ b/test cases/cython/5 nested folders/package/metrics/_pairwise_distances_reduction/_radius_neighbors_classmode.pyx @@ -0,0 +1,2 @@ +def test(): + pass diff --git a/test cases/cython/5 nested folders/package/metrics/_pairwise_distances_reduction/meson.build b/test cases/cython/5 nested folders/package/metrics/_pairwise_distances_reduction/meson.build new file mode 100644 index 000000000000..fb91c6d778ee --- /dev/null +++ b/test cases/cython/5 nested folders/package/metrics/_pairwise_distances_reduction/meson.build @@ -0,0 +1,7 @@ +_radius_neighbors_classmode = py.extension_module( + '_radius_neighbors_classmode', + '_radius_neighbors_classmode.pyx', + subdir: 'package/metrics/_pairwise_distances_reduction', + override_options: ['cython_language=cpp'], + install: true +) diff --git a/test cases/cython/5 nested folders/package/metrics/gen.py b/test cases/cython/5 nested folders/package/metrics/gen.py new file mode 100644 index 000000000000..5c0a82d568ad --- /dev/null +++ b/test cases/cython/5 nested folders/package/metrics/gen.py @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: Apache-2.0 + +import argparse +import textwrap + +parser = argparse.ArgumentParser() +parser.add_argument('output') +args = parser.parse_args() + +with open(args.output, 'w') as f: + f.write(textwrap.dedent('''\ + cpdef func(): + return "Hello, World!" + ''')) diff --git a/test cases/cython/5 nested folders/package/metrics/meson.build b/test cases/cython/5 nested folders/package/metrics/meson.build new file mode 100644 index 000000000000..5a5b7ac6f5d0 --- /dev/null +++ b/test cases/cython/5 nested folders/package/metrics/meson.build @@ -0,0 +1,13 @@ +generated_pyx = custom_target( + 'generated_pyx', + input : 'gen.py', + output : 'generated.pyx', + command : [py, '@INPUT@', '@OUTPUT@'], +) + +includestuff_ext = py.extension_module( + 'generated_pyx', + generated_pyx +) + +subdir('_pairwise_distances_reduction') diff --git a/test cases/cython/5 nested folders/package/metrics/metric.pyx b/test cases/cython/5 nested folders/package/metrics/metric.pyx new file mode 100644 index 000000000000..f174823854e7 --- /dev/null +++ b/test cases/cython/5 nested folders/package/metrics/metric.pyx @@ -0,0 +1,2 @@ +def test(): + pass diff --git a/test cases/cython/5 nested folders/test.json b/test cases/cython/5 nested folders/test.json new file mode 100644 index 000000000000..a009bd5b5b23 --- /dev/null +++ b/test cases/cython/5 nested folders/test.json @@ -0,0 +1,20 @@ +{ + "installed": [ + { + "type": "python_lib", + "file": "usr/@PYTHON_PLATLIB@/package/metrics/metric" + }, + { + "type": "py_implib", + "file": "usr/@PYTHON_PLATLIB@/package/metrics/metric" + }, + { + "type": "python_lib", + "file": "usr/@PYTHON_PLATLIB@/package/metrics/_pairwise_distances_reduction/_radius_neighbors_classmode" + }, + { + "type": "py_implib", + "file": "usr/@PYTHON_PLATLIB@/package/metrics/_pairwise_distances_reduction/_radius_neighbors_classmode" + } + ] +} diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index b52ff2518836..698e7cc2549c 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -1059,6 +1059,38 @@ def test_build_pyx_depfiles(self): self.assertBuildRelinkedOnlyTarget(name) + def test_cython_avoid_nested_generated_paths(self): + # Ensure that cython transpiled outputs are not in a too deeply nested folder + testdir = os.path.join("test cases/cython", "5 nested folders") + env = get_fake_env(testdir, self.builddir, self.prefix) + try: + detect_compiler_for(env, "cython", MachineChoice.HOST, True, "") + except EnvironmentException: + raise SkipTest("Cython is not installed") + self.init(testdir) + + targets = self.introspect("--targets") + + found = False + for target in targets: + for target_sources in target["target_sources"]: + for generated_source in target_sources.get("generated_sources", []): + if generated_source.endswith(".pyx.c") or generated_source.endswith("pyx.cpp"): + found = True + parts = os.path.normpath(generated_source).split(os.sep) + parent = parts[-2] + # We want the pyx.c files to be directly under the .p folder, + # for example: + # libdir/foo.cpython-313-x86_64-linux-gnu.so.p/foo.pyx.c + # rather than (additional libdir folder): + # libdir/foo.cpython-313-x86_64-linux-gnu.so.p/libdir/foo.pyx.c + self.assertTrue( + parent.endswith(".p"), + "pyx.c file should be directly under the .p folder," + f" got {generated_source!r}" + ) + self.assertTrue(found, "No cython transpiled outputs found") + def test_internal_include_order(self): if mesonbuild.envconfig.detect_msys2_arch() and ('MESON_RSP_THRESHOLD' in os.environ): raise SkipTest('Test does not yet support gcc rsp files on msys2') From 4146c447238459618af11830f7fc85162d4c3242 Mon Sep 17 00:00:00 2001 From: benjamin-otte Date: Fri, 16 Apr 2021 18:18:46 +0200 Subject: [PATCH 566/681] Improve error messages for wrap-redirect file While discussing error messages on IRC with xclaesse, we found 448b11cb7fb41199880cc60d9fce040ea0cf5a3b which improves one of the error messages, but leaves out the others. This commit updates them, too. --- mesonbuild/wrap/wrap.py | 8 ++++---- unittests/allplatformstests.py | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py index df16b416b79b..e35aa4ce0a84 100644 --- a/mesonbuild/wrap/wrap.py +++ b/mesonbuild/wrap/wrap.py @@ -253,15 +253,15 @@ def from_wrap_file(filename: str, subproject: SubProject = mesonlib.ROOT_SUBPROJ for i, p in enumerate(fname.parts): if i % 2 == 0: if p == '..': - raise WrapException('wrap-redirect filename cannot contain ".."') + raise WrapException(f"wrap-redirect filename {values['filename']!r} cannot contain '..'") else: if p != 'subprojects': - raise WrapException('wrap-redirect filename must be in the form foo/subprojects/bar.wrap') + raise WrapException(f"wrap-redirect filename {values['filename']!r} must be in the form foo/subprojects/bar.wrap") if fname.suffix != '.wrap': - raise WrapException('wrap-redirect filename must be a .wrap file') + raise WrapException(f"wrap-redirect filename {values['filename']!r} must be a .wrap file") fname = Path(subprojects_dir, fname) if not fname.is_file(): - raise WrapException(f'wrap-redirect {fname} filename does not exist') + raise WrapException(f"wrap-redirect filename {values['filename']!r} does not exist") wrap = PackageDefinition.from_wrap_file(str(fname), subproject) wrap.original_filename = filename wrap.redirected = True diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 698e7cc2549c..f91b7327c908 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -4649,7 +4649,7 @@ def test_wrap_redirect(self): [wrap-redirect] filename = foo/subprojects/real.wrapper ''')) - with self.assertRaisesRegex(WrapException, 'wrap-redirect filename must be a .wrap file'): + with self.assertRaisesRegex(WrapException, "wrap-redirect filename 'foo/subprojects/real.wrapper' must be a .wrap file"): PackageDefinition.from_wrap_file(redirect_wrap) # Invalid redirect, filename cannot be in parent directory @@ -4658,7 +4658,7 @@ def test_wrap_redirect(self): [wrap-redirect] filename = ../real.wrap ''')) - with self.assertRaisesRegex(WrapException, 'wrap-redirect filename cannot contain ".."'): + with self.assertRaisesRegex(WrapException, "wrap-redirect filename '../real.wrap' cannot contain '..'"): PackageDefinition.from_wrap_file(redirect_wrap) # Invalid redirect, filename must be in foo/subprojects/real.wrap @@ -4667,7 +4667,7 @@ def test_wrap_redirect(self): [wrap-redirect] filename = foo/real.wrap ''')) - with self.assertRaisesRegex(WrapException, 'wrap-redirect filename must be in the form foo/subprojects/bar.wrap'): + with self.assertRaisesRegex(WrapException, "wrap-redirect filename 'foo/real.wrap' must be in the form foo/subprojects/bar.wrap"): PackageDefinition.from_wrap_file(redirect_wrap) # Correct redirect From a5562ce95a99ad90174a2705f4f85a9df21d0514 Mon Sep 17 00:00:00 2001 From: Gabe Black Date: Tue, 2 Apr 2024 22:50:24 +0000 Subject: [PATCH 567/681] Add to_list() method to include_directories objects clean up fix tests for windows paths mimicing what was done in other unit tests (e.g., test case 74) Update mesonbuild/interpreter/interpreterobjects.py Co-authored-by: Charles Brunet Update docs/markdown/snippets/include_directories_to_list.md Co-authored-by: Charles Brunet Update mesonbuild/interpreter/interpreterobjects.py Co-authored-by: Charles Brunet Update docs/yaml/objects/inc.yaml Co-authored-by: Charles Brunet Fix comment language --- docs/markdown/snippets/include_directories_to_list.md | 10 ++++++++++ docs/yaml/objects/inc.yaml | 10 ++++++++++ mesonbuild/interpreter/interpreterobjects.py | 10 ++++++++-- test cases/common/18 includedir/meson.build | 8 ++++++++ test cases/common/18 includedir/src/meson.build | 2 ++ test cases/common/218 include_dir dot/meson.build | 2 +- test cases/common/218 include_dir dot/src/meson.build | 11 ++++++++++- test cases/common/250 system include dir/meson.build | 6 +++++- test cases/common/74 file object/meson.build | 4 +--- 9 files changed, 55 insertions(+), 8 deletions(-) create mode 100644 docs/markdown/snippets/include_directories_to_list.md diff --git a/docs/markdown/snippets/include_directories_to_list.md b/docs/markdown/snippets/include_directories_to_list.md new file mode 100644 index 000000000000..2b2d7dbf4984 --- /dev/null +++ b/docs/markdown/snippets/include_directories_to_list.md @@ -0,0 +1,10 @@ +## include_directories object now have a to_list() method + +The [[@inc]] object returned by [[include_directories]] now has a `to_list()` method that +returns a list of strings of absolute paths. Unless the include path is a +system path, there will be a path for the source root and one for the build root. + +``` +inc = include_directories('include') +include_paths = inc.to_list() +``` diff --git a/docs/yaml/objects/inc.yaml b/docs/yaml/objects/inc.yaml index 9f5e6844d419..cf88fece86c1 100644 --- a/docs/yaml/objects/inc.yaml +++ b/docs/yaml/objects/inc.yaml @@ -1,3 +1,13 @@ name: inc long_name: Include directories description: Opaque wrapper for storing include directories + +methods: +- name: to_list + returns: list[str] + since: 1.12.0 + description: | + Returns a list of strings of absolute paths to the directories + contained within the object. Unless the path is a system path, + there will be a path relative to the source root and one + relative to the build root. System paths are provided as-is. diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index 3ef37e78424d..441285ce65d5 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -797,9 +797,15 @@ def subsystem_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: return self.held_object.subsystem raise InterpreterException('Subsystem not defined or could not be autodetected.') - class IncludeDirsHolder(ObjectHolder[build.IncludeDirs]): - pass + @noPosargs + @noKwargs + @FeatureNew('inc.to_list', '1.12.0') + @InterpreterObject.method('to_list') + def to_list_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.List[str]: + if self.held_object.is_system: + return self.held_object.incdirs + return self.held_object.abs_string_list(self.env.source_dir, self.env.build_dir) class FileHolder(ObjectHolder[mesonlib.File]): def __init__(self, file: mesonlib.File, interpreter: 'Interpreter'): diff --git a/test cases/common/18 includedir/meson.build b/test cases/common/18 includedir/meson.build index 3180587270c6..35eeea67b141 100644 --- a/test cases/common/18 includedir/meson.build +++ b/test cases/common/18 includedir/meson.build @@ -30,3 +30,11 @@ testcase expect_error(errormsg) endtestcase # Test for issue #12217 include_directories(meson.current_source_dir() + 'xyz') + +# Test to see if an include_directory object will have the subdirectory in the path +source_dir = meson.current_source_dir() / 'src' +build_dir = meson.current_build_dir() / 'src' +expected = [source_dir / '.', build_dir / '.'] +# Use fs.as_posix() to ensure the results are the same across platforms. +fs = import('fs') +assert(fs.as_posix('@0@'.format(subdir_inc.to_list())) == '@0@'.format(expected)) diff --git a/test cases/common/18 includedir/src/meson.build b/test cases/common/18 includedir/src/meson.build index 30d2e0c87abd..b2b8e3976a74 100644 --- a/test cases/common/18 includedir/src/meson.build +++ b/test cases/common/18 includedir/src/meson.build @@ -3,3 +3,5 @@ test('inc test', exe) exe2 = executable('prog2', 'prog.c', 'func.c', include_directories : [['../include']]) test('inc test 2', exe2) + +subdir_inc = include_directories('.') diff --git a/test cases/common/218 include_dir dot/meson.build b/test cases/common/218 include_dir dot/meson.build index 71f7189c512e..74807370afb0 100644 --- a/test cases/common/218 include_dir dot/meson.build +++ b/test cases/common/218 include_dir dot/meson.build @@ -5,4 +5,4 @@ project('Include Here', 'c') inc = include_directories('.') -subdir('src') \ No newline at end of file +subdir('src') diff --git a/test cases/common/218 include_dir dot/src/meson.build b/test cases/common/218 include_dir dot/src/meson.build index fcbefb0b9df9..d0c82542a4ce 100644 --- a/test cases/common/218 include_dir dot/src/meson.build +++ b/test cases/common/218 include_dir dot/src/meson.build @@ -1,6 +1,15 @@ +# test to_list() for include_directories defined in the parent meson.build file +source_dir = meson.project_source_root() +build_dir = meson.project_build_root() +expected = [source_dir / '.', build_dir / '.'] +# Use fs.as_posix() to ensure the results are the same across platforms. +fs = import('fs') +assert(fs.as_posix('@0@'.format(inc.to_list())) == '@0@'.format(expected)) + +# test include directories t = executable( 'main', ['main.c', 'rone.c'], include_directories : inc, implicit_include_directories : false, -) \ No newline at end of file +) diff --git a/test cases/common/250 system include dir/meson.build b/test cases/common/250 system include dir/meson.build index 724a8e49cb7b..e6883172e719 100644 --- a/test cases/common/250 system include dir/meson.build +++ b/test cases/common/250 system include dir/meson.build @@ -9,5 +9,9 @@ if not ['gcc', 'clang', 'clang-cl'].contains(compiler_id) endif lib_include_directories = include_directories('lib', is_system: true) + +# test that a system library include_directories just contains a single path +assert('@0@'.format(lib_include_directories.to_list()) == '[\'lib\']') + add_project_arguments('-Wsign-conversion', language: 'cpp') -executable('system_include_dir_test', sources: 'main.cpp', include_directories: lib_include_directories) +executable('system_include_dir_b_test', sources: 'main.cpp', include_directories: lib_include_directories) diff --git a/test cases/common/74 file object/meson.build b/test cases/common/74 file object/meson.build index 05c24aef8348..c3977e41aa22 100644 --- a/test cases/common/74 file object/meson.build +++ b/test cases/common/74 file object/meson.build @@ -7,8 +7,6 @@ test('fobj', executable('fobj', prog0, lib0)) subdir('subdir1') subdir('subdir2') -# Use fs.as_posix() because / operator replaces \ with / in the path, but -# full_path() method is not doing that. This is a pretty inconsistent across all -# Meson APIs. +# Use fs.as_posix() to ensure the results are the same across platforms. fs = import('fs') assert(fs.as_posix(prog0[0].full_path()) == fs.as_posix(meson.current_source_dir() / 'prog.c')) From a7adffa210a736582b6dbe8aec1a2d18b39c2d1b Mon Sep 17 00:00:00 2001 From: "L. E. Segovia" Date: Sat, 6 Jun 2026 21:46:39 -0300 Subject: [PATCH 568/681] windows: Implement RC depfile generation for clang targeting MSVC --- mesonbuild/modules/windows.py | 33 +++++++++++++++++++++++++-------- mesonbuild/scripts/rc.py | 30 +++++++++++++----------------- 2 files changed, 38 insertions(+), 25 deletions(-) diff --git a/mesonbuild/modules/windows.py b/mesonbuild/modules/windows.py index 2c5089629b27..d4a550aade02 100644 --- a/mesonbuild/modules/windows.py +++ b/mesonbuild/modules/windows.py @@ -81,7 +81,7 @@ def search_programs(names: T.List[str]) -> T.Optional[ExternalProgram]: return None comp = self.detect_compiler(state.environment.coredata.compilers[for_machine]) - if comp.id in {'msvc', 'clang-cl', 'intel-cl'} or (comp.linker and comp.linker.id in {'link', 'lld-link'}): + if comp.linker and comp.linker.id in {'link', 'lld-link'}: rescomp = search_programs(['rc', 'llvm-rc']) else: rescomp = search_programs(['windres', 'llvm-windres']) @@ -186,15 +186,32 @@ def get_names() -> T.Iterable[T.Tuple[str, str, T.Union[mesonlib.File, build.Cus if rescomp_type == ResourceCompilerType.rc: compiler = self.detect_compiler(state.environment.coredata.compilers[MachineChoice.HOST]) - if compiler.id in {'msvc', 'clang-cl'}: - depfile_type = 'msvc' + command.extend(state.environment.get_build_command()) + command.extend(['--internal', 'rc', + '--rc', *rescomp.get_command(), + '--cl', *compiler.get_exelist(False)]) - command.extend(state.environment.get_build_command()) - command.extend(['--internal', 'rc', - '--cl', compiler.get_exelist(False)[0], - '--rc']) + if compiler.id in {'msvc', 'clang-cl', 'intel-cl'}: + depfile_type = 'msvc' + command.extend(['--Xarg=/showIncludes', + '--Xarg=/EP', + '--Xarg=/nologo', + '--Xarg=/DRC_INVOKED', + '--Xarg=/Tc@INPUT@', + ]) + else: + depfile = f'{output}.d' + depfile_type = 'gcc' + command.extend(['--Xarg=-xc', + '--Xarg=-E', + '--Xarg=-MD', + '--Xarg=-MQ@OUTPUT@', + '--Xarg=-MF@DEPFILE@', + '--Xarg=-DRC_INVOKED', + ]) + else: + command.extend(rescomp.get_command()) - command.append(rescomp) command.extend(res_args) # instruct binutils windres to generate a preprocessor depfile diff --git a/mesonbuild/scripts/rc.py b/mesonbuild/scripts/rc.py index c9cbcf93d5a8..877425c40f0d 100644 --- a/mesonbuild/scripts/rc.py +++ b/mesonbuild/scripts/rc.py @@ -1,41 +1,37 @@ # SPDX-License-Identifier: Apache-2.0 -"""Wrapper for rc.exe to make it output included files.""" +"""Wrapper for manually generating depfiles of resource files.""" from __future__ import annotations import argparse import subprocess -import shutil +import sys import typing as T parser = argparse.ArgumentParser() -parser.add_argument('--rc') -parser.add_argument('--cl') +parser.add_argument('--rc', nargs='+', required=True) +parser.add_argument('--cl', nargs='+', required=True) +parser.add_argument('--Xarg', action='append') def run(args: T.List[str]) -> int: options, rc_args = parser.parse_known_args(args) target = rc_args[-1] if rc_args else None - rc = options.rc if options.rc else shutil.which('rc.exe') - if rc is None: - print('rc.exe not found') - return 1 - - cl = options.cl if options.cl else shutil.which('cl.exe') - if cl is None: - print('cl.exe not found') - return 1 + rc = options.rc + cl = options.cl # Use preprocessor to display include files include_args = [a for a in rc_args if a.startswith(('/I', '-I'))] - cmd = [cl, '/showIncludes', '/EP', '/nologo', '/DRC_INVOKED'] + include_args + ['/Tc' + target] + cmd = [*cl, *include_args, *options.Xarg] + if target: + cmd += [target] result = subprocess.call(cmd, stdout=subprocess.DEVNULL) if result != 0: - print('Error running preprocessor to find resource dependencies') - # continue anyway. rc.exe should catch the error later + print('Error running preprocessor to find resource dependencies', file=sys.stderr) + # continue anyway. the resource compiler should catch the error later - cmd = [rc] + rc_args + cmd = [*rc, *rc_args] return subprocess.call(cmd) From ea5af50bd14e6d132fd86dd0ceebc7de4f715352 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Mon, 22 Jun 2026 16:57:10 +0200 Subject: [PATCH 569/681] ci: Disable git repository ownership checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Disable the new feature that ensures that the git repository is owned by the same user that runs the command. This apparently is not true when running the test suite and breaks at least the Gentoo CI job. Signed-off-by: Michał Górny --- .github/workflows/os_comp.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/os_comp.yml b/.github/workflows/os_comp.yml index 135635822c15..cd6132b92508 100644 --- a/.github/workflows/os_comp.yml +++ b/.github/workflows/os_comp.yml @@ -61,6 +61,8 @@ jobs: source /ci/env_vars.sh cd $GITHUB_WORKSPACE + # disable git ownership checks + git config --global safe.directory '*' ./run_tests.py $CI_ARGS pypy: From 82c25986d73d812452cfcf473202c3b4859b1a92 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 23 Jun 2026 10:00:10 +0200 Subject: [PATCH 570/681] docs: accept 429 status for github search and topic pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GitHub queries often returns a 429 Too Many Requests status code to the runners. Ignore the error, as it makes CI flaky and a Too Many Requests result (unlike a 404) is a sign that the URL itself is ok. While at it, improve slightly the formatting to avoid this: >>> ASAN_OPTIONS=halt_on_error=1:abort_on_error=1:print_summary=1 MESON_TEST_ITERATION=1 UBSAN_OPTIONS=halt_on_error=1:abort_on_error=1:print_summary=1:print_stacktrace=1 MSAN_OPTIONS=halt_on_error=1:abort_on_error=1:print_summary=1:print_stacktrace=1 MALLOC_PERTURB_=51 /usr/bin/python3 /home/runner/work/meson/meson/docs/./validatelinks.py /home/runner/work/meson/meson/docs/markdown/Users.md ――――――――――――――――――――――――――――――――――――― ✀ ――――――――――――――――――――――――――――――――――――― "`meson` GitHub topic" https://github.com/topics/meson 429 ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― Signed-off-by: Paolo Bonzini --- docs/validatelinks.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/validatelinks.py b/docs/validatelinks.py index 17dc5b85a8fc..649d1286207a 100644 --- a/docs/validatelinks.py +++ b/docs/validatelinks.py @@ -28,14 +28,20 @@ async def main(filename): tasks = [] for link in LINK.finditer(text): name, url = link.groups() + name = name.replace('\n', ' ') task = asyncio.ensure_future(fetch(session, name, url, timeout)) tasks.append(task) responses = asyncio.gather(*tasks) errors = [r for r in await responses if r is not None] + bad = False for name, url, result in errors: - print(f'"{name}" {url} {result}') + if re.match(r'https://github.com/(search|topics/)', url) and result == 429: + print(f'"{name}" {url} {result} (ignored)') + else: + print(f'"{name}" {url} {result}') + bad = True if errors: - sys.exit(1) + sys.exit(int(bad)) if __name__ == '__main__': From 1b4a6498fd3d5d6e8ff88ab4d41703dab0eb2e1a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 23 Jun 2026 10:02:34 +0200 Subject: [PATCH 571/681] docs: fix incorrect return type The reference manual expects "array[...]", fix the incorrect type added by PR #14488. Fixes: #15941 Signed-off-by: Paolo Bonzini --- docs/yaml/objects/inc.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/yaml/objects/inc.yaml b/docs/yaml/objects/inc.yaml index cf88fece86c1..0a68ceb3253f 100644 --- a/docs/yaml/objects/inc.yaml +++ b/docs/yaml/objects/inc.yaml @@ -4,7 +4,7 @@ description: Opaque wrapper for storing include directories methods: - name: to_list - returns: list[str] + returns: array[str] since: 1.12.0 description: | Returns a list of strings of absolute paths to the directories From bcd5dd45774e47abbb47e86de8a67e5276f1ccc5 Mon Sep 17 00:00:00 2001 From: Jesper Wramberg Date: Tue, 23 Jun 2026 13:03:40 +0200 Subject: [PATCH 572/681] docs: correct dep.as_system() default value The reference manual states that the optional value argument to dep.as_system() defaults to 'preserve'. In practice, omitting the argument makes the dependency use the 'system' include type. Update the documentation to describe the actual default behavior. Fixes #13294 --- docs/yaml/objects/dep.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/yaml/objects/dep.yaml b/docs/yaml/objects/dep.yaml index fdf5fe505b2f..89488735be93 100644 --- a/docs/yaml/objects/dep.yaml +++ b/docs/yaml/objects/dep.yaml @@ -90,7 +90,7 @@ methods: description: | Returns a copy of the dependency object, which has changed the value of `include_type` to `value`. The `value` argument is optional and - defaults to `'preserve'`. + defaults to `'system'`. optargs: value: From e0e728d556c907bd8e32b03c2601328308dd8a4a Mon Sep 17 00:00:00 2001 From: vanadiae Date: Tue, 25 Apr 2023 22:38:45 +0200 Subject: [PATCH 573/681] msetup: Create CACHEDIR.TAG file in build dir Fixes #11734 --- docs/markdown/snippets/cachedir_tag.md | 5 +++++ mesonbuild/msetup.py | 13 ++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 docs/markdown/snippets/cachedir_tag.md diff --git a/docs/markdown/snippets/cachedir_tag.md b/docs/markdown/snippets/cachedir_tag.md new file mode 100644 index 000000000000..470203933bc1 --- /dev/null +++ b/docs/markdown/snippets/cachedir_tag.md @@ -0,0 +1,5 @@ +## Support for CACHEDIR.TAG specification + +A CACHEDIR.TAG file (https://bford.info/cachedir/) is now created in the build +directory as part of the setup and regenerate phases, allowing backup software +to ignore the whole directory. diff --git a/mesonbuild/msetup.py b/mesonbuild/msetup.py index 68b3bf999863..f80ef0b6f4bc 100644 --- a/mesonbuild/msetup.py +++ b/mesonbuild/msetup.py @@ -41,6 +41,11 @@ class CMDOptions(SharedCMDOptions, Protocol): **/* ''' +cachedir_tag_file = '''Signature: 8a477f597d28d172789f06886806bc55 +# This file is a cache directory tag created by meson. +# For information about cache directory tags, see http://bford.info/cachedir/ +''' + # Note: when adding arguments, please also add them to the completion # scripts in $MESONSRC/data/shell-completions/ @@ -104,7 +109,7 @@ def __init__(self, options: CMDOptions) -> None: else: mesonlib.windows_proof_rm(l) finally: - self.add_vcs_ignore_files(self.build_dir) + self.add_ignore_files(self.build_dir) for b, f in restore: os.makedirs(os.path.dirname(f), exist_ok=True) shutil.move(b, f) @@ -149,18 +154,20 @@ def validate_core_dirs(self, dir1: T.Optional[str], dir2: T.Optional[str]) -> T. return ndir2, ndir1 raise MesonException(f'{invalid_msg_prefix} contain a build file {environment.build_filename}.') - def add_vcs_ignore_files(self, build_dir: str) -> None: + def add_ignore_files(self, build_dir: str) -> None: with open(os.path.join(build_dir, '.gitignore'), 'w', encoding='utf-8') as ofile: ofile.write(git_ignore_file) with open(os.path.join(build_dir, '.hgignore'), 'w', encoding='utf-8') as ofile: ofile.write(hg_ignore_file) + with open(os.path.join(build_dir, 'CACHEDIR.TAG'), 'w', encoding='utf-8') as ofile: + ofile.write(cachedir_tag_file) def validate_dirs(self) -> T.Tuple[str, str]: (src_dir, build_dir) = self.validate_core_dirs(self.options.builddir, self.options.sourcedir) if Path(build_dir) in Path(src_dir).parents: raise MesonException(f'Build directory {build_dir} cannot be a parent of source directory {src_dir}') if not os.listdir(build_dir): - self.add_vcs_ignore_files(build_dir) + self.add_ignore_files(build_dir) return src_dir, build_dir priv_dir = os.path.join(build_dir, 'meson-private') has_valid_build = os.path.exists(os.path.join(priv_dir, 'coredata.dat')) From 462758d7b60a527109a6e818ed887e79667357fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20=22sp1rit=22=E2=80=8B?= Date: Mon, 15 Jun 2026 11:38:01 +0200 Subject: [PATCH 574/681] install: Delay install script errors to install time Previously, `gnome.post_install` might've caused a configure to fail due to tools such as `update-desktop-database` not being installed or due to an `exe_wrapper` missing when cross-building glib. Those errors will now be delayed to install time, or not emitted at all if installing with `DESTDIR` set, since then they'll be skipped. Fixes: #14492 --- .../snippets/delayed_install_script_errors.md | 7 +++ mesonbuild/backend/backends.py | 8 ++-- mesonbuild/build.py | 4 +- mesonbuild/interpreter/interpreter.py | 10 ++-- mesonbuild/minstall.py | 25 ++++++---- mesonbuild/modules/__init__.py | 6 +-- mesonbuild/modules/gnome.py | 48 +++++++++++-------- mesonbuild/utils/universal.py | 14 +++++- 8 files changed, 80 insertions(+), 42 deletions(-) create mode 100644 docs/markdown/snippets/delayed_install_script_errors.md diff --git a/docs/markdown/snippets/delayed_install_script_errors.md b/docs/markdown/snippets/delayed_install_script_errors.md new file mode 100644 index 000000000000..7c81f22a01ee --- /dev/null +++ b/docs/markdown/snippets/delayed_install_script_errors.md @@ -0,0 +1,7 @@ +## Delay install script errors to install time + +Previously, `gnome.post_install` might've caused a configure to fail +due to tools such as `update-desktop-database` not being installed or +due to an `exe_wrapper` missing when cross-building glib. Those +errors will now be delayed to install time, or not emitted at all +if installing with `DESTDIR` set, since then they'll be skipped. diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 98804ad2e56d..21cc8bb0a65d 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -27,7 +27,7 @@ from ..compilers import detect, lang_suffixes from ..mesonlib import ( File, MachineChoice, MesonException, MesonBugException, OrderedSet, - ExecutableSerialisation, EnvironmentException, FileMode, + ExecutableSerialisation, EnvironmentException, FileMode, InstallScriptFailure, classify_unity_sources, get_compiler_for_source, get_rsp_threshold, unique_list ) @@ -42,6 +42,7 @@ from ..environment import Environment from ..interpreter import Test from ..linkers import StaticLinker + from ..mesonlib import InstallScript from ..options import ElementaryOptionValues from typing_extensions import Literal, TypedDict, NotRequired, TypeAlias @@ -133,7 +134,7 @@ def __post_init__(self) -> None: self.emptydir: T.List[InstallEmptyDir] = [] self.data: T.List[InstallDataBase] = [] self.symlinks: T.List[InstallSymlinkData] = [] - self.install_scripts: T.List[ExecutableSerialisation] = [] + self.install_scripts: T.List[InstallScript] = [] self.install_subdirs: T.List[SubdirInstallData] = [] @dataclass(eq=False) @@ -1855,7 +1856,8 @@ def generate_custom_install_script(self, d: InstallData) -> None: d.install_scripts = self.build.install_scripts for i in d.install_scripts: if not i.tag: - mlog.debug('Failed to guess install tag for install script:', ' '.join(i.cmd_args)) + name = i.name if isinstance(i, InstallScriptFailure) else ' '.join(i.cmd_args) + mlog.debug('Failed to guess install tag for install script:', name) def generate_header_install(self, d: InstallData) -> None: incroot = self.environment.get_includedir() diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 9942c8109f8d..d9fa10414f6d 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -51,7 +51,7 @@ from .interpreter.interpreterobjects import Test, Doctest from .interpreter.kwargs import TargetDepends from .linkers import StaticLinker - from .mesonlib import ExecutableSerialisation, FileMode, FileOrString + from .mesonlib import ExecutableSerialisation, FileMode, FileOrString, InstallScript from .mparser import BaseNode from .options import ElementaryOptionValues @@ -342,7 +342,7 @@ def __init__(self, environment: Environment): self.symlinks: T.List[SymlinkData] = [] self.static_linker: PerMachine[T.Optional[StaticLinker]] = PerMachine(None, None) self.subproject_dir = '' - self.install_scripts: T.List['ExecutableSerialisation'] = [] + self.install_scripts: T.List[InstallScript] = [] self.postconf_scripts: T.List['ExecutableSerialisation'] = [] self.dist_scripts: T.List['ExecutableSerialisation'] = [] self.install_dirs: T.List[InstallDir] = [] diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 3c4a8c6c8841..9c50557a65a3 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -20,8 +20,8 @@ from ..wrap import wrap, WrapMode from .. import mesonlib from ..mesonlib import (EnvironmentVariables, ExecutableSerialisation, MesonBugException, MesonException, HoldableObject, - FileMode, MachineChoice, PerMachine, PerMachineDefaultable, is_parent_path, listify, - has_path_sep, path_has_root, path_is_in_root) + FileMode, InstallScriptFailure, MachineChoice, PerMachine, PerMachineDefaultable, is_parent_path, + listify, has_path_sep, path_has_root, path_is_in_root) from ..options import OptionKey from ..programs import ExternalProgram, NonExistingExternalProgram, Program from ..dependencies import Dependency @@ -124,7 +124,7 @@ from ..compilers.compilers import CompilerDict, Language from ..interpreterbase.baseobjects import InterpreterObject, TYPE_var, TYPE_kwargs from ..options import OptionDict - from ..mesonlib import SubProject + from ..mesonlib import InstallScript, SubProject from ..cmdline import SharedCMDOptions from .type_checking import SourcesVarargsType, FullEnvInitValueType @@ -512,7 +512,7 @@ def append_holder_map(self, held_type: T.Type[mesonlib.HoldableObject], holder_t held_type: holder_type }) - def process_new_values(self, invalues: list[TYPE_var | ExecutableSerialisation] | list[build.GeneratedTypes | mesonlib.File]) -> None: + def process_new_values(self, invalues: list[TYPE_var | InstallScript] | list[build.GeneratedTypes | mesonlib.File]) -> None: for v in invalues: if isinstance(v, ObjectHolder): raise InterpreterException('Modules must not return ObjectHolders') @@ -520,7 +520,7 @@ def process_new_values(self, invalues: list[TYPE_var | ExecutableSerialisation] self.add_target(v.name, v) elif isinstance(v, list): self.process_new_values(v) - elif isinstance(v, ExecutableSerialisation): + elif isinstance(v, (ExecutableSerialisation, InstallScriptFailure)): v.subproject = self.subproject self.build.install_scripts.append(v) elif isinstance(v, build.Data): diff --git a/mesonbuild/minstall.py b/mesonbuild/minstall.py index 8839f3725df5..34b62169f29c 100644 --- a/mesonbuild/minstall.py +++ b/mesonbuild/minstall.py @@ -17,8 +17,8 @@ from . import build, tooldetect from .backend.backends import InstallData -from .mesonlib import (MesonException, Popen_safe, RealPathAction, is_windows, - is_aix, setup_vsenv, path_has_root, pickle_load, is_osx, +from .mesonlib import (InstallScriptFailure, MesonException, Popen_safe, RealPathAction, + is_windows, is_aix, setup_vsenv, path_has_root, pickle_load, is_osx, unwrap) from .options import OptionKey from .scripts import depfixer, destdir_join @@ -36,7 +36,7 @@ InstallDataBase, InstallEmptyDir, InstallSymlinkData, TargetInstallData ) - from .mesonlib import FileMode, EnvironOrDict, ExecutableSerialisation + from .mesonlib import FileMode, EnvironOrDict, ExecutableSerialisation, InstallScript try: from typing import Protocol @@ -389,7 +389,7 @@ def run_exe(self, exe: ExecutableSerialisation, extra_env: T.Optional[T.Dict[str def should_install(self, d: T.Union[TargetInstallData, InstallEmptyDir, InstallDataBase, InstallSymlinkData, - ExecutableSerialisation]) -> bool: + InstallScript]) -> bool: if d.subproject and (d.subproject in self.skip_subprojects or '*' in self.skip_subprojects): return False if self.tags and d.tag not in self.tags: @@ -706,6 +706,12 @@ def install_headers(self, d: InstallData, dm: DirMaker, destdir: str, fullprefix self.set_mode(outfilename, t.install_mode, d.install_umask) def run_install_script(self, d: InstallData, destdir: str, fullprefix: str) -> None: + failing_scripts = [script for script in d.install_scripts if isinstance(script, InstallScriptFailure)] + if not destdir and len(failing_scripts) > 0: + for script in failing_scripts: + self.log(f'ERROR: Failed to run install script {script.name}: {script.reason}') + raise MesonException('Install scripts failed to run') + env = {'MESON_SOURCE_ROOT': d.source_dir, 'MESON_BUILD_ROOT': d.build_dir, 'MESONINTROSPECT': ' '.join([shlex.quote(x) for x in d.mesonintrospect]), @@ -719,6 +725,13 @@ def run_install_script(self, d: InstallData, destdir: str, fullprefix: str) -> N if not self.should_install(i): continue + name = i.name if isinstance(i, InstallScriptFailure) else ' '.join(i.cmd_args) + if destdir and (isinstance(i, InstallScriptFailure) or i.skip_if_destdir): + self.log(f'Skipping custom install script because DESTDIR is set {name!r}') + continue + + assert not isinstance(i, InstallScriptFailure) # Should part of failing_scripts and thus this is not reached + if i.installdir_map is not None: mapp = i.installdir_map else: @@ -727,10 +740,6 @@ def run_install_script(self, d: InstallData, destdir: str, fullprefix: str) -> N localenv.update({'MESON_INSTALL_'+k.upper(): os.path.join(d.prefix, v) for k, v in mapp.items()}) localenv.update({'MESON_INSTALL_DESTDIR_'+k.upper(): get_destdir_path(destdir, fullprefix, v) for k, v in mapp.items()}) - name = ' '.join(i.cmd_args) - if i.skip_if_destdir and destdir: - self.log(f'Skipping custom install script because DESTDIR is set {name!r}') - continue self.did_install_something = True # Custom script must report itself if it does nothing. self.log(f'Running custom install script {name!r}') try: diff --git a/mesonbuild/modules/__init__.py b/mesonbuild/modules/__init__.py index 26b7dcd21767..842aed41b5d5 100644 --- a/mesonbuild/modules/__init__.py +++ b/mesonbuild/modules/__init__.py @@ -275,11 +275,11 @@ def is_module_library(fname: mesonlib.FileOrString) -> bool: class ModuleReturnValue: - def __init__(self, return_value: T.Optional['TYPE_var'], - new_objects: T.Sequence[T.Union['TYPE_var', 'mesonlib.ExecutableSerialisation']]) -> None: + def __init__(self, return_value: T.Optional[TYPE_var], + new_objects: T.Sequence[T.Union[TYPE_var, mesonlib.InstallScript]]) -> None: self.return_value = return_value assert isinstance(new_objects, list) - self.new_objects: T.List[T.Union['TYPE_var', 'mesonlib.ExecutableSerialisation']] = new_objects + self.new_objects: T.List[T.Union[TYPE_var, mesonlib.InstallScript]] = new_objects class GResourceTarget(build.CustomTarget): source_dirs: T.List[str] = [] diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 7e1ef2042618..3776d3927f30 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -31,7 +31,7 @@ from ..interpreterbase import typed_kwargs, KwargInfo, ContainerTypeInfo from ..interpreterbase.decorators import typed_pos_args from ..mesonlib import ( - MachineChoice, MesonException, OrderedSet, Popen_safe, join_args, quote_arg + InstallScriptFailure, MachineChoice, MesonException, OrderedSet, Popen_safe, join_args, quote_arg ) from ..options import OptionKey from ..scripts.gettext import read_linguas @@ -47,7 +47,7 @@ from ..interpreter.interpreter import CustomTargetSources from ..interpreter.kwargs import CustomTargetInputs, TargetDepends from ..interpreterbase import TYPE_var, TYPE_kwargs - from ..mesonlib import EnvironmentVariables, FileOrString + from ..mesonlib import EnvironmentVariables, FileOrString, InstallScript from ..programs import Program, CommandList, CommandListEntry class PostInstall(TypedDict): @@ -313,7 +313,7 @@ def _print_gdbus_warning() -> None: once=True, fatal=False) @staticmethod - def _find_tool(state: 'ModuleState', tool: str) -> Program: + def _find_tool(state: 'ModuleState', tool: str, required: bool = True) -> Program: tool_map = { 'gio-querymodules': 'gio-2.0', 'glib-compile-schemas': 'gio-2.0', @@ -326,7 +326,20 @@ def _find_tool(state: 'ModuleState', tool: str) -> Program: } depname = tool_map[tool] varname = tool.replace('-', '_') - return state.find_tool(tool, depname, varname, native=depname != "gobject-introspection-1.0") + return state.find_tool(tool, depname, varname, required=required, native=depname != "gobject-introspection-1.0") + + @staticmethod + def _get_install_script(state: ModuleState, name: str, exe: Program, args: T.List[str], skip_if_destdir: bool = True) -> InstallScript: + if not exe.found(): + mlog.warning(f'Program {name} was not found, installation without DESTDIR will fail', fatal=False) + return InstallScriptFailure(name, 'Executable was not found') + try: + install_script = state.backend.get_executable_serialisation([exe, *args]) + install_script.skip_if_destdir = skip_if_destdir + return install_script + except MesonException as e: + mlog.warning(f'Meson will be unable to run {name}, installation without DESTDIR will fail', fatal=False) + return InstallScriptFailure(name, str(e)) @typed_kwargs( 'gnome.post_install', @@ -339,46 +352,41 @@ def _find_tool(state: 'ModuleState', tool: str) -> Program: @noPosargs @FeatureNew('gnome.post_install', '0.57.0') def post_install(self, state: 'ModuleState', args: T.List['TYPE_var'], kwargs: 'PostInstall') -> ModuleReturnValue: - rv: T.List['mesonlib.ExecutableSerialisation'] = [] + rv: T.List[InstallScript] = [] datadir_abs = os.path.join(state.environment.get_prefix(), state.environment.get_datadir()) if kwargs['glib_compile_schemas'] and not self.install_glib_compile_schemas: self.install_glib_compile_schemas = True - prog = self._find_tool(state, 'glib-compile-schemas') + prog = self._find_tool(state, 'glib-compile-schemas', required=False) schemasdir = os.path.join(datadir_abs, 'glib-2.0', 'schemas') - script = state.backend.get_executable_serialisation([prog, schemasdir]) - script.skip_if_destdir = True + script = self._get_install_script(state, 'glib-compile-schemas', prog, [schemasdir]) rv.append(script) for d in kwargs['gio_querymodules']: if d not in self.install_gio_querymodules: self.install_gio_querymodules.append(d) - prog = self._find_tool(state, 'gio-querymodules') + prog = self._find_tool(state, 'gio-querymodules', required=False) moduledir = os.path.join(state.environment.get_prefix(), d) - script = state.backend.get_executable_serialisation([prog, moduledir]) - script.skip_if_destdir = True + script = self._get_install_script(state, 'gio-querymodules', prog, [moduledir]) rv.append(script) if kwargs['gtk_update_icon_cache'] and not self.install_gtk_update_icon_cache: self.install_gtk_update_icon_cache = True prog = state.find_program('gtk4-update-icon-cache', required=False) found = isinstance(prog, Executable) or prog.found() if not found: - prog = state.find_program('gtk-update-icon-cache') + prog = state.find_program('gtk-update-icon-cache', required=False) icondir = os.path.join(datadir_abs, 'icons', 'hicolor') - script = state.backend.get_executable_serialisation([prog, '-q', '-t', '-f', icondir]) - script.skip_if_destdir = True + script = self._get_install_script(state, 'gtk4-update-icon-cache', prog, ['-q', '-t', '-f', icondir]) rv.append(script) if kwargs['update_desktop_database'] and not self.install_update_desktop_database: self.install_update_desktop_database = True - prog = state.find_program('update-desktop-database') + prog = state.find_program('update-desktop-database', required=False) appdir = os.path.join(datadir_abs, 'applications') - script = state.backend.get_executable_serialisation([prog, '-q', appdir]) - script.skip_if_destdir = True + script = self._get_install_script(state, 'update-desktop-database', prog, ['-q', appdir]) rv.append(script) if kwargs['update_mime_database'] and not self.install_update_mime_database: self.install_update_mime_database = True - prog = state.find_program('update-mime-database') + prog = state.find_program('update-mime-database', required=False) appdir = os.path.join(datadir_abs, 'mime') - script = state.backend.get_executable_serialisation([prog, appdir]) - script.skip_if_destdir = True + script = self._get_install_script(state, 'update-mime-database', prog, [appdir]) rv.append(script) return ModuleReturnValue(None, rv) diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index 5b2ef7571931..41f0b9c59c94 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -28,7 +28,7 @@ import dataclasses from mesonbuild import mlog -from .core import MesonException, MesonBugException, HoldableObject +from .core import MesonException, MesonBugException, HoldableObject, ExecutableSerialisation if T.TYPE_CHECKING: from typing_extensions import Literal, Protocol, Self @@ -86,6 +86,8 @@ def __gt__(self, other: Self) -> bool: ... 'EnvironmentException', 'FileOrString', 'GitException', + 'InstallScript', + 'InstallScriptFailure', 'dump_conf_header', 'OrderedSet', 'PerMachine', @@ -726,6 +728,16 @@ def default_missing(self) -> PerThreeMachine[_T]: target = self.target if self.target is not None else host return PerThreeMachine(build, host, target) +@dataclasses.dataclass(eq=False) +class InstallScriptFailure: + + name: str + reason: str + tag: T.Optional[str] = None + + subproject = ROOT_SUBPROJECT + +InstallScript = T.Union[ExecutableSerialisation, InstallScriptFailure] _PLATFORM_SYSTEM_LOWER = platform.system().lower() _PLATFORM_RELEASE_LOWER = platform.release().lower() From a3de7ea399726aed1a6147cc87b28f4f8ff8542d Mon Sep 17 00:00:00 2001 From: mayank-dev-15 Date: Tue, 23 Jun 2026 20:26:44 +0530 Subject: [PATCH 575/681] backend/ninja: clean meson-dist directory during ninja clean ninja -t clean left meson-dist behind because it's not tracked by ninja. The meson-clean target now passes 'meson-dist' to the cleantrees script so it gets removed along with custom target dirs. Fixes #3789 --- mesonbuild/backend/ninjabackend.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 19135af1f8dc..d0ff82375062 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -4243,6 +4243,8 @@ def generate_ending(self) -> None: # Create a list of all custom target outputs for o in t.get_outputs(): ctlist.append(os.path.join(self.get_target_dir(t), o)) + # Also clean meson-dist directory created by `meson dist` + ctlist.append('meson-dist') if ctlist: elem.add_dep(self.generate_custom_target_clean(ctlist)) From f31d6f51559314543ebfdd8621c92de5ada18579 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 23 Jun 2026 12:35:35 +0200 Subject: [PATCH 576/681] cargo: force "native: false" when building Cargo packages in non-cross setups Cargo configures a single set of packages when not cross compiling, and therefore will not build separate subprojects for procedural macro crates and everything else; therefore we need to build everything for MachineChoice.HOST otherwise you end up with warnings like WARNING: Tried to mix a host machine library (proc_macro2) with a build machine target (cxxbridge-macro) But this is expected and will work correctly in cross setups. Signed-off-by: Paolo Bonzini --- mesonbuild/interpreter/interpreter.py | 2 +- mesonbuild/modules/rust.py | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 9c50557a65a3..f4cb0b6531f5 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -3935,7 +3935,7 @@ def build_target(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargs self.apply_machine_map_to_kwargs(kwargs) for_machine = kwargs['native'] - if kwargs.get('rust_crate_type') == 'proc-macro': + if self.environment.is_cross_build() and 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 diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index c4c763bb8efd..f949205fcc6e 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -354,7 +354,12 @@ def validate_pos_args(name: str, args: T.Tuple[ return None, args[0] def merge_kw_args(self, state: ModuleState, kwargs: T.Union[RustPackageExecutable, RustPackageLibrary]) -> None: - kwargs.setdefault('native', self.for_machine) + if state.environment.is_cross_build(): + kwargs.setdefault('native', self.for_machine) + else: + # dependent crates are not compiled separately for build and host; + # do not use 'native: true' to allow linking to them without warnings + kwargs.setdefault('native', MachineChoice.HOST) cfg = self.package.cfg[kwargs['native']] if not cfg: raise MesonException(f"package {self.package.manifest.package.name}-{self.package.manifest.package.version} not configured for {self.for_machine}") @@ -1025,6 +1030,8 @@ def compiler_target(self, state: ModuleState, args: T.List, kwargs: '_kwargs.Nat @typed_pos_args('rust.proc_macro', str, varargs=SOURCES_VARARGS) @typed_kwargs('rust.proc_macro', *_PROC_MACRO_KWS) def proc_macro(self, state: ModuleState, args: T.Tuple[str, SourcesVarargsType], kwargs: _kwargs.SharedLibrary) -> SharedLibrary: + # Silently force to native; rust.proc_macro() has always done + # that even when not cross compiling. kwargs['native'] = MachineChoice.BUILD kwargs['rust_crate_type'] = 'proc-macro' kwargs['rust_args'] = kwargs['rust_args'] + ['--extern', 'proc_macro'] From 4c664ad883d0f7693fdea198fd9dd918c776ad3d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 23 Jun 2026 13:54:25 +0200 Subject: [PATCH 577/681] cargo: ignore doc-scrape-examples field It is not necessary and pollutes the logs unnecessarily. Signed-off-by: Paolo Bonzini --- mesonbuild/cargo/manifest.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mesonbuild/cargo/manifest.py b/mesonbuild/cargo/manifest.py index 8afd5fe7c2a2..08d481e27d10 100644 --- a/mesonbuild/cargo/manifest.py +++ b/mesonbuild/cargo/manifest.py @@ -448,6 +448,7 @@ class Example(BuildTarget): def from_raw(cls, raw: raw.BuildTarget, pkg: Package) -> Self: name = raw["name"] return _raw_to_dataclass(raw, cls, f'Example entry {name}', + ignored_fields=['doc-scrape-examples'], path=DefaultValue(f'examples/{name}.rs'), edition=DefaultValue(pkg.edition), test=DefaultValue(False), From a07665393349bce6598797048fc23e2f9e5ec92d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 23 Jun 2026 13:53:05 +0200 Subject: [PATCH 578/681] cargo: do minimal validation of [patch.crates-io] Several high-profile projects use a [patch.crates-io] entry like [patch.crates-io] cxx = { path = "." } cxx-build = { path = "gen/build" } in order to replace other packages with the version in their own workspace. Recognize this situation to avoid an _EXTRA_KEYS_WARNING, but warn for all other [patch] entries because Meson does not support them. Signed-off-by: Paolo Bonzini --- mesonbuild/cargo/interpreter.py | 9 +++++++- mesonbuild/cargo/manifest.py | 34 ++++++++++++++++++++++++++++++ mesonbuild/cargo/raw.py | 2 +- unittests/cargotests.py | 37 ++++++++++++++++++++++++++++++++- 4 files changed, 79 insertions(+), 3 deletions(-) diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index c1b0bdee48f8..f9f27b7b8785 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -23,7 +23,10 @@ from . import builder, version from .cfg import eval_cfg from .toml import load_toml -from .manifest import Manifest, CargoLock, CargoLockPackage, Workspace, fixup_meson_varname +from .manifest import ( + Manifest, CargoLock, CargoLockPackage, Workspace, fixup_meson_varname, + validate_patch, +) from ..mesonlib import ( is_parent_path, lazy_property, MesonException, MachineChoice, PerMachine, unique_list, SubProject, @@ -332,6 +335,10 @@ def load_workspace(self, subdir: str, extra_members: T.Optional[T.List[str]]) -> manifest, cached = self._load_manifest(subdir) ws = self._get_workspace(manifest, subdir, extra_members, False) if not cached: + # [patch] only takes effect in the top-level Cargo.toml + for warning in validate_patch(ws.workspace.patch, ws.packages_to_member): + mlog.warning(warning) + self._prepare_entry_point(ws) return ws diff --git a/mesonbuild/cargo/manifest.py b/mesonbuild/cargo/manifest.py index 08d481e27d10..dde36a73a053 100644 --- a/mesonbuild/cargo/manifest.py +++ b/mesonbuild/cargo/manifest.py @@ -534,6 +534,9 @@ class Manifest: features: T.Dict[str, T.List[str]] = dataclasses.field(default_factory=dict) target: T.Dict[str, T.Dict[str, Dependency]] = dataclasses.field(default_factory=dict) lints: T.List[Lint] = dataclasses.field(default_factory=list) + # Kept in raw form: Meson does not implement [patch], it only validates it + # for the entry-point crate (see validate_patch). + patch: object = None # missing: profile @@ -642,6 +645,9 @@ class Workspace: # A workspace can also have a root package. root_package: T.Optional[Manifest] = None + # Top-level [patch] table, kept in raw form (see Manifest.validate_patch). + patch: object = None + @lazy_property def inheritable(self) -> T.Dict[str, object]: # the whole lints table is inherited. Do not add package, dependencies @@ -655,6 +661,10 @@ def from_raw(cls, raw: raw.Manifest, path: str) -> Self: ws = _raw_to_dataclass(raw['workspace'], cls, 'Workspace') if 'package' in raw: ws.root_package = Manifest.from_raw(raw, path, ws, '.') + ws.patch = ws.root_package.patch + else: + ws.patch = raw.get('patch') + ws.members = list(PurePath(m).as_posix() for m in ws.members) if ws.default_members: ws.default_members = list(PurePath(m).as_posix() for m in ws.default_members) @@ -731,3 +741,27 @@ def _versions(self) -> T.Dict[str, T.List[CargoLockPackage]]: def from_raw(cls, raw: raw.CargoLock) -> Self: return _raw_to_dataclass(raw, cls, 'Cargo.lock', package=ConvertValue(lambda x: [CargoLockPackage.from_raw(p) for p in x])) + + +def validate_patch(patch: object, resolved: T.Mapping[str, str]) -> T.Iterator[str]: + # Meson does not implement [patch]; recognize entries that simply point + # at a package Meson already builds from the workspace ('resolved' maps + # each such package name to its path), and warn for everything else. + # This is only done for the top-level Cargo.toml, since Cargo ignores + # [patch] in a dependency's manifest too. + if not patch: + return + if not isinstance(patch, dict): + yield '[patch] format not recognized' + elif list(patch.keys()) != ['crates-io']: + yield 'Found [patch] for a registry other than crates.io' + elif not isinstance(patch['crates-io'], dict): + yield '[patch.crates-io] format not recognized' + else: + for name, value in patch['crates-io'].items(): + if not isinstance(value, dict) or not isinstance(value.get('path'), str): + yield f'Unrecognized [patch.crates-io] entry {name!r}' + elif resolved.get(name) == os.path.normpath(value['path']): + continue + else: + yield f'[patch.crates-io] entry {name!r} is not for a dependency' diff --git a/mesonbuild/cargo/raw.py b/mesonbuild/cargo/raw.py index b683f06d82e2..dcb8b72c0f64 100644 --- a/mesonbuild/cargo/raw.py +++ b/mesonbuild/cargo/raw.py @@ -176,8 +176,8 @@ class Workspace(TypedDict): 'target': T.Dict[str, Target], 'workspace': Workspace, 'lints': T.Union[FromWorkspace, T.Dict[str, T.Dict[str, LintV]]], + 'patch': T.Dict[str, object], - # TODO: patch? # TODO: replace? }, total=False, diff --git a/unittests/cargotests.py b/unittests/cargotests.py index 360c0b604a80..e34615e93c04 100644 --- a/unittests/cargotests.py +++ b/unittests/cargotests.py @@ -11,7 +11,7 @@ from mesonbuild.cargo import cfg from mesonbuild.cargo.cfg import TokenType from mesonbuild.cargo.interpreter import load_cargo_lock -from mesonbuild.cargo.manifest import Dependency, Lint, Manifest, Package, Workspace +from mesonbuild.cargo.manifest import Dependency, Lint, Manifest, Package, Workspace, validate_patch from mesonbuild.cargo.toml import load_toml from mesonbuild.cargo.version import api, cargo_parse, SemVer from mesonbuild.mesonlib import MesonException @@ -679,3 +679,38 @@ def test_cargo_toml_features(self) -> None: self.assertEqual(manifest.features['v1_42'], ['pango-sys/v1_42']) self.assertEqual(manifest.features['v1_44'], ['v1_42', 'pango-sys/v1_44']) self.assertEqual(manifest.features['default'], []) + + def test_validate_patch(self) -> None: + # packages_to_member values are normalized with os.path.normpath (see + # _load_workspace_member), so do the same here + resolved = { + 'cxx': '.', + 'cxx-build': os.path.normpath('gen/build'), + 'member': os.path.normpath('vendor/member'), + } + + patch = {'crates-io': { + 'cxx': {'path': '.'}, + 'cxx-build': {'path': 'gen/build'}, + 'member': {'version': '1', 'path': 'vendor/member'}, + }} + + # Empty or absent [patch] + self.assertFalse(list(validate_patch(None, resolved))) + self.assertFalse(list(validate_patch({}, resolved))) + + # Invalid [patch] tables + self.assertTrue(list(validate_patch({'crates-io': {}, 'https://example.com': {}}, resolved))) + self.assertTrue(list(validate_patch({'crates-io': 'nonsense'}, resolved))) + self.assertTrue(list(validate_patch('nonsense', resolved))) + + # Invalid entries + self.assertTrue(list(validate_patch({'crates-io': {'cxx': {'git': 'https://example.com'}}}, resolved))) + self.assertTrue(list(validate_patch({'crates-io': {'cxx': '1.0'}}, resolved))) + + # Unknown crate + self.assertTrue(list(validate_patch({'crates-io': {'unknown': {'path': 'foo'}}}, resolved))) + + # The only entries that don't warn point at a package that Meson already builds + self.assertFalse(list(validate_patch(patch, resolved))) + self.assertTrue(list(validate_patch({'crates-io': {'cxx-build': {'path': 'elsewhere'}}}, resolved))) From b300d9578fe62c721afbf4e5c4672ad0c94cb96c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 24 Jun 2026 17:24:08 +0200 Subject: [PATCH 579/681] cargo: do not warn for [profile] in dependencies [profile] tables are only parsed for the toplevel Cargo.toml. Do not warn unnecessarily if a dependency has one. Signed-off-by: Paolo Bonzini --- mesonbuild/cargo/interpreter.py | 2 ++ mesonbuild/cargo/manifest.py | 6 ++++-- mesonbuild/cargo/raw.py | 27 +++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index f9f27b7b8785..62bc71e4ce0b 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -338,6 +338,8 @@ def load_workspace(self, subdir: str, extra_members: T.Optional[T.List[str]]) -> # [patch] only takes effect in the top-level Cargo.toml for warning in validate_patch(ws.workspace.patch, ws.packages_to_member): mlog.warning(warning) + if ws.workspace.profile: + mlog.warning('[profile] entries are not implemented yet') self._prepare_entry_point(ws) return ws diff --git a/mesonbuild/cargo/manifest.py b/mesonbuild/cargo/manifest.py index dde36a73a053..3f3e2db7731f 100644 --- a/mesonbuild/cargo/manifest.py +++ b/mesonbuild/cargo/manifest.py @@ -537,8 +537,7 @@ class Manifest: # Kept in raw form: Meson does not implement [patch], it only validates it # for the entry-point crate (see validate_patch). patch: object = None - - # missing: profile + profile: object = None def __post_init__(self) -> None: self.features.setdefault('default', []) @@ -647,6 +646,7 @@ class Workspace: # Top-level [patch] table, kept in raw form (see Manifest.validate_patch). patch: object = None + profile: object = None @lazy_property def inheritable(self) -> T.Dict[str, object]: @@ -662,8 +662,10 @@ def from_raw(cls, raw: raw.Manifest, path: str) -> Self: if 'package' in raw: ws.root_package = Manifest.from_raw(raw, path, ws, '.') ws.patch = ws.root_package.patch + ws.profile = ws.root_package.profile else: ws.patch = raw.get('patch') + ws.profile = raw.get('profile') ws.members = list(PurePath(m).as_posix() for m in ws.members) if ws.default_members: diff --git a/mesonbuild/cargo/raw.py b/mesonbuild/cargo/raw.py index dcb8b72c0f64..201e70329ab1 100644 --- a/mesonbuild/cargo/raw.py +++ b/mesonbuild/cargo/raw.py @@ -159,6 +159,32 @@ class Workspace(TypedDict): dependencies: T.Dict[str, DependencyV] +Profile = TypedDict( + 'Profile', + { + 'opt-level': T.Union[int, str], + 'debug': T.Union[bool, int, str], + 'split-debuginfo': str, + 'strip': T.Union[bool, str], + 'debug-assertions': bool, + 'overflow-checks': bool, + 'lto': T.Union[bool, str], + 'panic': str, + 'incremental': bool, + 'codegen-units': int, + 'rpath': bool, + 'inherits': str, + 'package': T.Dict[str, 'Profile'], + 'build-override': 'Profile', + }, + total=False, +) +"""An entry in the [profile] section. + +See https://doc.rust-lang.org/cargo/reference/profiles.html +""" + + Manifest = TypedDict( 'Manifest', { @@ -177,6 +203,7 @@ class Workspace(TypedDict): 'workspace': Workspace, 'lints': T.Union[FromWorkspace, T.Dict[str, T.Dict[str, LintV]]], 'patch': T.Dict[str, object], + 'profile': T.Dict[str, Profile], # TODO: replace? }, From 32e2b6b9b552c8165d811d6ff321c5c288cf7263 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 8 Jun 2026 10:44:40 -0700 Subject: [PATCH 580/681] interpreter: Fix KwargInfo.evolve return type It needs to return the Generic type, i.e., it needs to return `KwargInfo[_T]`, not just `KwargInfo` --- mesonbuild/interpreter/type_checking.py | 6 +++--- mesonbuild/interpreterbase/decorators.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py index 5d9e3f1a1ac1..7280f66303d7 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -577,7 +577,7 @@ def suite_convertor(suite: T.List[str]) -> T.List[str]: default=[], ) -_LANGUAGE_KWS: T.List[KwargInfo[T.List[str]]] = [ +_LANGUAGE_KWS: T.List[KwargInfo[T.List[str | File]]] = [ _BASE_LANG_KW.evolve(name=f'{lang}_args') for lang in compilers.all_languages - {'rust', 'vala', 'java'} ] @@ -587,7 +587,7 @@ def suite_convertor(suite: T.List[str]) -> T.List[str]: _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]] = _BASE_LANG_KW.evolve( +_JAVA_LANG_KW: KwargInfo[T.List[str | File]] = _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' @@ -1010,7 +1010,7 @@ def _shortname_validator(shortname: T.Optional[str]) -> T.Optional[str]: for a in _LANGUAGE_KWS], ] -_SHARED_STATIC_ARGS: T.List[KwargInfo[T.List[str]]] = [ +_SHARED_STATIC_ARGS: T.List[KwargInfo[T.List[str | File]]] = [ *[l.evolve(name=l.name.replace('_', '_static_'), since='1.3.0') for l in _LANGUAGE_KWS], *[l.evolve(name=l.name.replace('_', '_shared_'), since='1.3.0') diff --git a/mesonbuild/interpreterbase/decorators.py b/mesonbuild/interpreterbase/decorators.py index ad5f17e263c6..bfd7cc24afcd 100644 --- a/mesonbuild/interpreterbase/decorators.py +++ b/mesonbuild/interpreterbase/decorators.py @@ -441,7 +441,7 @@ def evolve(self, *, convertor: T.Union[T.Callable[[_T], object], None, _NULL_T] = _NULL, extra_types: T.Union[T.Mapping[T.Type, T.Callable[[object], str]], None, _NULL_T] = _NULL, as_default: T.Union[T.List[T.Tuple[object, T.Union[str, T.Tuple[str, str]]]], None, _NULL_T] = _NULL - ) -> 'KwargInfo': + ) -> 'KwargInfo[_T]': """Create a shallow copy of this KwargInfo, with modifications. This allows us to create a new copy of a KwargInfo with modifications. From d7ced03b42079e4fa4166ec2126e63e489038509 Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Tue, 23 Jun 2026 15:08:35 +0200 Subject: [PATCH 581/681] Add OpenHarmony (OHOS) support as an Android subsystem OHOS behaves like Android: applications are shared libraries and shared libraries are not versioned. But it uses musl instead of Bionic. Model it as an Android subsystem rather than a separate system: machine files set system = 'android' and subsystem = 'ohos', so is_android() applies and all Android handling (unversioned libfoo.so, the zlib include exception, PIE, android_exe_type) is inherited automatically. Add a MachineInfo.is_ohos() predicate (is_android() and subsystem == 'ohos') and use it to set CMAKE_SYSTEM_NAME = OHOS for CMake subprojects, since CMake has a dedicated OHOS system name. --- docs/markdown/Reference-tables.md | 15 ++++++++++++++- docs/markdown/snippets/ohos-support.md | 20 ++++++++++++++++++++ mesonbuild/cmake/toolchain.py | 8 +++++++- mesonbuild/envconfig.py | 11 +++++++++++ unittests/internaltests.py | 16 ++++++++++++++++ 5 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 docs/markdown/snippets/ohos-support.md diff --git a/docs/markdown/Reference-tables.md b/docs/markdown/Reference-tables.md index 9c17ab35cfc9..73573971fb3f 100644 --- a/docs/markdown/Reference-tables.md +++ b/docs/markdown/Reference-tables.md @@ -162,7 +162,7 @@ These are provided by the `.system()` method call. | Value | Comment | | ----- | ------- | -| android | | +| android | Android or OpenHarmony / HarmonyOS (OHOS) | | cygwin | Cygwin or MSYS2 environment on Windows | | darwin | Either OSX or iOS | | dragonfly | DragonFly BSD | @@ -208,6 +208,10 @@ A more specific description of the system in question. Most values are meant to be used in cross files only, as those platforms can not run Meson natively. +### `darwin` subsystems + +Set when `system` is `darwin`. + | Value | Comment | | ----- | ------- | | macos | Apple macOS (formerly OSX) | @@ -220,6 +224,15 @@ Meson natively. | watchos | Apple watchOS | | watchos-simulator | | +### `android` subsystems (since 1.12.0) + +Set when `system` is `android`. + +| Value | Comment | +| ----- | ------- | +| android | Android (Bionic libc) | +| ohos | OpenHarmony / HarmonyOS (OHOS) | + ## Language arguments parameter names These are the parameter names for passing language specific arguments diff --git a/docs/markdown/snippets/ohos-support.md b/docs/markdown/snippets/ohos-support.md new file mode 100644 index 000000000000..4331a8709c51 --- /dev/null +++ b/docs/markdown/snippets/ohos-support.md @@ -0,0 +1,20 @@ +## OpenHarmony (OHOS) is now recognized as an Android subsystem + +Meson now recognizes OpenHarmony / HarmonyOS (OHOS). OHOS behaves like +Android -- applications are shared libraries, shared libraries are not +versioned, and so on -- but uses musl instead of Bionic. It is therefore +modelled as an Android subsystem rather than a separate system. + +Machine files select it in the `[host_machine]` section with: + +```ini +[host_machine] +system = 'android' +subsystem = 'ohos' +``` + +With this, `host_machine.system()` is `'android'` (so all Android handling +applies, including unversioned `libfoo.so` output) while +`host_machine.subsystem()` is `'ohos'`. When cross-compiling a CMake +subproject, the toolchain file generated by the CMake module sets +`CMAKE_SYSTEM_NAME = OHOS`. diff --git a/mesonbuild/cmake/toolchain.py b/mesonbuild/cmake/toolchain.py index 033fc8c4cbc9..60b061557533 100644 --- a/mesonbuild/cmake/toolchain.py +++ b/mesonbuild/cmake/toolchain.py @@ -155,7 +155,13 @@ def get_defaults(self) -> T.Dict[str, T.List[str]]: # Only set these in a cross build. Otherwise CMake will trip up in native # builds and thing they are cross (which causes TRY_RUN() to break) if self.env.is_cross_build(when_building_for=self.for_machine): - defaults['CMAKE_SYSTEM_NAME'] = [SYSTEM_MAP.get(self.minfo.system, self.minfo.system)] + # OHOS is modelled as an Android subsystem in meson, but CMake has a + # dedicated OHOS system name, so map it explicitly. + if self.minfo.is_ohos(): + system_name = 'OHOS' + else: + system_name = SYSTEM_MAP.get(self.minfo.system, self.minfo.system) + defaults['CMAKE_SYSTEM_NAME'] = [system_name] defaults['CMAKE_SYSTEM_PROCESSOR'] = [self.minfo.cpu_family] defaults['CMAKE_SIZEOF_VOID_P'] = ['8' if self.minfo.is_64_bit else '4'] diff --git a/mesonbuild/envconfig.py b/mesonbuild/envconfig.py index 4d68c19d0165..7c5aa88505b1 100644 --- a/mesonbuild/envconfig.py +++ b/mesonbuild/envconfig.py @@ -344,6 +344,17 @@ def is_android(self) -> bool: """ return self.system == 'android' + def is_ohos(self) -> bool: + """ + Machine is OpenHarmony (OHOS)? + + OHOS is modelled as an Android subsystem: it behaves like Android + (apps are shared libraries, no versioned sonames, ...) but uses musl + instead of Bionic. Machine files select it with system = 'android' + and subsystem = 'ohos'. + """ + return self.is_android() and self.subsystem == 'ohos' + def is_haiku(self) -> bool: """ Machine is Haiku? diff --git a/unittests/internaltests.py b/unittests/internaltests.py index 4229769b9e01..6c4663036cb0 100644 --- a/unittests/internaltests.py +++ b/unittests/internaltests.py @@ -54,6 +54,22 @@ class InternalTests(unittest.TestCase): + def test_machine_info_is_ohos(self): + def machine(system: str, subsystem: str) -> mesonbuild.envconfig.MachineInfo: + return mesonbuild.envconfig.MachineInfo( + system=system, cpu_family='aarch64', cpu='aarch64', + endian='little', kernel='linux', subsystem=subsystem) + + # OHOS is modelled as an Android subsystem. + ohos = machine('android', 'ohos') + self.assertTrue(ohos.is_ohos()) + self.assertTrue(ohos.is_android()) + + # Plain Android is not OHOS. + self.assertFalse(machine('android', 'android').is_ohos()) + # A non-Android system with an 'ohos' subsystem is not OHOS either. + self.assertFalse(machine('linux', 'ohos').is_ohos()) + def test_version_number(self): self.assertEqual(search_version('foobar 1.2.3'), '1.2.3') self.assertEqual(search_version('1.2.3'), '1.2.3') From b16053c08aeae1a31b19f485e8d87532074b5b24 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 10 Jun 2026 12:02:13 -0700 Subject: [PATCH 582/681] unittests: split template tests per language And do some cleanups. This one test takes a ton of time to run, and cannot be parallelized. I have split the code so that we run one test per language, but the rest of the subtests remain. This allows us to run up to 11 parallel tests for templates, which can really help with the test runtime. --- unittests/allplatformstests.py | 197 +++++++++++++++++++++------------ 1 file changed, 127 insertions(+), 70 deletions(-) diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index f91b7327c908..cbd817fa62e4 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -1,7 +1,8 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2016-2021 The Meson development team -# Copyright © 2023-2025 Intel Corporation +# Copyright © 2023-2026 Intel Corporation +from __future__ import annotations import itertools import subprocess import re @@ -31,6 +32,7 @@ import mesonbuild.coredata import mesonbuild.machinefile import mesonbuild.modules.gnome +import mesonbuild.tooldetect from mesonbuild.mesonlib import ( DirectoryLock, DirectoryLockAction, MachineChoice, is_windows, is_osx, is_cygwin, is_dragonflybsd, is_sunos, windows_proof_rmtree, python_command, version_compare, split_args, quote_arg, @@ -49,7 +51,7 @@ from mesonbuild.compilers.cpp import VisualStudioCPPCompiler, ClangClCPPCompiler from mesonbuild.compilers import ( detect_static_linker, detect_c_compiler, compiler_from_language, - detect_compiler_for, lang_suffixes + detect_compiler_for ) from mesonbuild.linkers import linkers @@ -68,6 +70,10 @@ from .baseplatformtests import BasePlatformTests from .helpers import * +if T.TYPE_CHECKING: + from mesonbuild.compilers.compilers import Language + from mesonbuild.environment import Environment + UNIT_MACHINEFILE_DIR = Path(__file__).parent / 'machinefiles' @contextmanager @@ -2536,86 +2542,137 @@ def test_permitted_method_kwargs(self): self.init(tdir) self.assertIn('ERROR: compiler.has_header_symbol got unknown keyword arguments "prefixxx"', cm.exception.output) - def test_templates(self): + def _template_test_fresh(self, lang: Language, target_type: str, env: Environment, ninja: list[str]) -> None: + if is_windows() and lang == 'fortran' and target_type == 'library': + # non-Gfortran Windows Fortran compilers do not do shared libraries in a Fortran standard way + # see "test cases/fortran/6 dynamic" + fc = detect_compiler_for(env, 'fortran', MachineChoice.HOST, True, '') + if fc.get_id() in {'intel-cl', 'pgi'}: + raise SkipTest('The Intel (classic) and PGI fortran compilers do not support standard shared libraries') + + # test empty directory + with tempfile.TemporaryDirectory() as tmpdir: + self._run(self.meson_command + ['init', '--language', lang, '--type', target_type], + workdir=tmpdir) + self._run(self.setup_command + ['--backend=ninja', 'builddir'], workdir=tmpdir) + self._run(ninja, workdir=os.path.join(tmpdir, 'builddir')) + + # custom executable name + if target_type == 'executable': + with tempfile.TemporaryDirectory() as tmpdir: + self._run(self.meson_command + ['init', '--language', lang, '--type', target_type, + '--executable', 'foobar'], + workdir=tmpdir) + self._run(self.setup_command + ['--backend=ninja', 'builddir'], workdir=tmpdir) + self._run(ninja, workdir=os.path.join(tmpdir, 'builddir')) + + if lang not in {'cs', 'java'}: + exe = os.path.join(tmpdir, 'builddir', 'foobar' + exe_suffix) + self.assertTrue(os.path.exists(exe)) + + def _template_test_dirty(self, lang: Language, target_type: str, env: Environment, ninja: list[str]) -> None: + if is_windows() and lang == 'fortran' and target_type == 'library': + # non-Gfortran Windows Fortran compilers do not do shared libraries in a Fortran standard way + # see "test cases/fortran/6 dynamic" + fc = detect_compiler_for(env, 'fortran', MachineChoice.HOST, True, '') + if fc.get_id() in {'intel-cl', 'pgi'}: + raise SkipTest('The Intel (classic) and PGI fortran compilers do not support standard shared libraries') + + # test empty directory + with tempfile.TemporaryDirectory() as tmpdir: + self._run(self.meson_command + ['init', '--language', lang, '--type', target_type], + workdir=tmpdir) + self._run(self.setup_command + ['--backend=ninja', 'builddir'], workdir=tmpdir) + self._run(ninja, workdir=os.path.join(tmpdir, 'builddir')) + + # test directory with existing code file + if lang in {'c', 'cpp', 'd'}: + with tempfile.TemporaryDirectory() as tmpdir: + with open(os.path.join(tmpdir, 'foo.' + lang), 'w', encoding='utf-8') as f: + f.write('int main(void) {}') + self._run(self.meson_command + ['init', '-b'], workdir=tmpdir) + + elif lang in {'java'}: + with tempfile.TemporaryDirectory() as tmpdir: + with open(os.path.join(tmpdir, 'Foo.' + lang), 'w', encoding='utf-8') as f: + f.write('public class Foo { public static void main() {} }') + self._run(self.meson_command + ['init', '-b'], workdir=tmpdir) + + def _test_template(self, lang: Language) -> None: ninja = mesonbuild.tooldetect.detect_ninja() if ninja is None: raise SkipTest('This test currently requires ninja. Fix this once "meson build" works.') - langs = [] - env = get_fake_env() - for l in ['c', 'cpp', 'cs', 'cuda', 'd', 'fortran', 'java', 'objc', 'objcpp', 'rust', 'vala']: + env = get_fake_env(bdir=self.builddir) + + extra_lang: Language | None + match lang: + case 'cuda': + extra_lang = 'cpp' + case 'vala': + extra_lang = 'c' + case _: + extra_lang = None + + if extra_lang is not None: try: - comp = detect_compiler_for(env, l, MachineChoice.HOST, True, '') - with tempfile.TemporaryDirectory() as d: - comp.sanity_check(d) - langs.append(l) + comp = detect_compiler_for(env, extra_lang, MachineChoice.HOST, True, '') except EnvironmentException: - pass + comp = None + if comp is None: + raise SkipTest(f'Could not find compiler for {extra_lang}, which is required for {lang}') - # The D template fails under mac CI and we don't know why. - # Patches welcome + with tempfile.TemporaryDirectory() as d: + try: + comp = detect_compiler_for(env, lang, MachineChoice.HOST, True, '') + if comp is None: + raise SkipTest(f'Could not find compiler for language: {lang}') + comp.sanity_check(d) + except EnvironmentException: + raise SkipTest(f'Compiler for language {lang} failed to sanity check') + + for target_type, fresh in itertools.product(('executable', 'library'), (True, False)): + with self.subTest(type=target_type, fresh=fresh): + func = self._template_test_fresh if fresh else self._template_test_dirty + try: + func(lang, target_type, env, ninja) + except subprocess.CalledProcessError as e: + self.fail(e.stdout) + + def test_template_c(self) -> None: + self._test_template('c') + + def test_template_cpp(self) -> None: + self._test_template('cpp') + + def test_template_cs(self) -> None: + self._test_template('cs') + + def test_template_cuda(self) -> None: + self._test_template('cuda') + + def test_template_d(self) -> None: if is_osx(): - langs = [l for l in langs if l != 'd'] + raise SkipTest('The D template fails under MacOS CI, we are unsure why. Patches welcome.') + self._test_template('d') - def _template_test_fresh(lang, target_type): - if is_windows() and lang == 'fortran' and target_type == 'library': - # non-Gfortran Windows Fortran compilers do not do shared libraries in a Fortran standard way - # see "test cases/fortran/6 dynamic" - fc = detect_compiler_for(env, 'fortran', MachineChoice.HOST, True, '') - if fc.get_id() in {'intel-cl', 'pgi'}: - return + def test_template_fortran(self) -> None: + self._test_template('fortran') - # test empty directory - with tempfile.TemporaryDirectory() as tmpdir: - self._run(self.meson_command + ['init', '--language', lang, '--type', target_type], - workdir=tmpdir) - self._run(self.setup_command + ['--backend=ninja', 'builddir'], workdir=tmpdir) - self._run(ninja, workdir=os.path.join(tmpdir, 'builddir')) + def test_template_java(self) -> None: + self._test_template('java') - # custom executable name - if target_type == 'executable': - with tempfile.TemporaryDirectory() as tmpdir: - self._run(self.meson_command + ['init', '--language', lang, '--type', target_type, - '--executable', 'foobar'], - workdir=tmpdir) - self._run(self.setup_command + ['--backend=ninja', 'builddir'], workdir=tmpdir) - self._run(ninja, workdir=os.path.join(tmpdir, 'builddir')) - - if lang not in {'cs', 'java'}: - exe = os.path.join(tmpdir, 'builddir', 'foobar' + exe_suffix) - self.assertTrue(os.path.exists(exe)) - - def _template_test_dirty(lang, target_type): - if is_windows() and lang == 'fortran' and target_type == 'library': - # non-Gfortran Windows Fortran compilers do not do shared libraries in a Fortran standard way - # see "test cases/fortran/6 dynamic" - fc = detect_compiler_for(env, 'fortran', MachineChoice.HOST, True, '') - if fc.get_id() in {'intel-cl', 'pgi'}: - return - - # test empty directory - with tempfile.TemporaryDirectory() as tmpdir: - self._run(self.meson_command + ['init', '--language', lang, '--type', target_type], - workdir=tmpdir) - self._run(self.setup_command + ['--backend=ninja', 'builddir'], workdir=tmpdir) - self._run(ninja, workdir=os.path.join(tmpdir, 'builddir')) + def test_template_objc(self) -> None: + self._test_template('objc') + + def test_template_objcpp(self) -> None: + self._test_template('objcpp') + + def test_template_rust(self) -> None: + self._test_template('rust') - # test directory with existing code file - if lang in {'c', 'cpp', 'd'}: - with tempfile.TemporaryDirectory() as tmpdir: - with open(os.path.join(tmpdir, 'foo.' + lang), 'w', encoding='utf-8') as f: - f.write('int main(void) {}') - self._run(self.meson_command + ['init', '-b'], workdir=tmpdir) - - elif lang in {'java'}: - with tempfile.TemporaryDirectory() as tmpdir: - with open(os.path.join(tmpdir, 'Foo.' + lang), 'w', encoding='utf-8') as f: - f.write('public class Foo { public static void main() {} }') - self._run(self.meson_command + ['init', '-b'], workdir=tmpdir) - - for lang, target_type, fresh in itertools.product(langs, ('executable', 'library'), (True, False)): - with self.subTest(f'Language: {lang}; type: {target_type}; fresh: {fresh}'): - _template_test_fresh(lang, target_type) if fresh else _template_test_dirty(lang, target_type) + def test_template_vala(self) -> None: + self._test_template('vala') def test_compiler_run_command(self): ''' From 16a4cd6e3070a463e2ff1fc57a1ed3ba82cdf612 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 30 Jun 2026 13:45:57 -0700 Subject: [PATCH 583/681] rewriter: rewriter functions are not TV_Func types so stop calling them that. --- mesonbuild/rewriter.py | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/mesonbuild/rewriter.py b/mesonbuild/rewriter.py index f4e0cd0ad1cd..93e1393a0d07 100644 --- a/mesonbuild/rewriter.py +++ b/mesonbuild/rewriter.py @@ -11,7 +11,7 @@ from .ast import IntrospectionInterpreter, BUILD_TARGET_FUNCTIONS, AstConditionLevel, AstIDGenerator, AstIndentationGenerator, AstPrinter from .ast.interpreter import IntrospectionBuildTarget, IntrospectionDependency, _symbol -from .interpreterbase import UnknownValue, TV_func +from .interpreterbase import UnknownValue from .interpreterbase.helpers import flatten from mesonbuild.mesonlib import MesonException, pathname_sort_key, relpath, setup_vsenv from . import mlog, environment @@ -25,8 +25,14 @@ if T.TYPE_CHECKING: import argparse from argparse import ArgumentParser, _FormatterClass + + from typing_extensions import TypeAlias + from .mlog import AnsiDecorator + RewriterKeysT: TypeAlias = dict[str, tuple[type, object | None, list[str] | None]] + RewriterFuncT: TypeAlias = T.Callable[['Rewriter', dict[str, object]], None] + class RewriterException(MesonException): pass @@ -68,20 +74,13 @@ def add_arguments(parser: ArgumentParser, formatter: _FormatterClass) -> None: cmd_parser.add_argument('json', help='JSON string or file to execute') class RequiredKeys: - keys: T.Dict[str, T.Any] - - def __init__(self, keys: T.Dict[str, T.Any]): + def __init__(self, keys: RewriterKeysT): self.keys = keys - def __call__(self, f: TV_func) -> TV_func: + def __call__(self, f: RewriterFuncT) -> RewriterFuncT: @wraps(f) - def wrapped(*wrapped_args: T.Any, **wrapped_kwargs: T.Any) -> T.Any: - assert len(wrapped_args) >= 2 - cmd = wrapped_args[1] - for key, val in self.keys.items(): - typ = val[0] # The type of the value - default = val[1] # The default value -- None is required - choices = val[2] # Valid choices -- None is for everything + def wrapped(rewriter: 'Rewriter', cmd: dict[str, object]) -> None: + for key, (typ, default, choices) in self.keys.items(): if key not in cmd: if default is not None: cmd[key] = default @@ -92,13 +91,11 @@ def wrapped(*wrapped_args: T.Any, **wrapped_kwargs: T.Any) -> T.Any: raise RewriterException('Invalid type of "{}". Required is {} but provided was {}' .format(key, typ.__name__, type(cmd[key]).__name__)) if choices is not None: - assert isinstance(choices, list) if cmd[key] not in choices: raise RewriterException('Invalid value of "{}": Possible values are {} but provided was "{}"' .format(key, choices, cmd[key])) - return f(*wrapped_args, **wrapped_kwargs) - - return T.cast('TV_func', wrapped) + return f(rewriter, cmd) + return wrapped class MTypeBase: node: BaseNode @@ -314,7 +311,7 @@ def _check_regex_matches(node: BaseNode, regex: str) -> bool: def supported_element_nodes(cls) -> T.List[T.Type]: return [IdNode] -rewriter_keys: T.Dict[str, T.Dict[str, T.Any]] = { +rewriter_keys: T.Dict[str, RewriterKeysT] = { 'default_options': { 'operation': (str, None, ['set', 'delete']), 'options': (dict, {}, None) From dcb6d3789a30cec349c837c3460926209b480e2b Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Thu, 23 Apr 2026 22:56:36 +0300 Subject: [PATCH 584/681] Look up valac in cross file. Originally by Helmut Grohne from Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1134724 --- mesonbuild/compilers/detect.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mesonbuild/compilers/detect.py b/mesonbuild/compilers/detect.py index ed81c5e6c931..9bd76b648ffc 100644 --- a/mesonbuild/compilers/detect.py +++ b/mesonbuild/compilers/detect.py @@ -1045,7 +1045,10 @@ def detect_cython_compiler(env: 'Environment', for_machine: MachineChoice) -> Co def detect_vala_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler: from .vala import ValaCompiler - exelist = env.lookup_binary_entry(MachineChoice.BUILD, 'vala') + exelist = env.lookup_binary_entry(for_machine, 'vala') + if exelist is None and for_machine != MachineChoice.BUILD: + # This is wrong, but backwards-compatible. + exelist = env.lookup_binary_entry(MachineChoice.BUILD, 'vala') if exelist is None: # TODO support fallback exelist = [defaults['vala'][0]] From fe75baf6b76676f513cd6008d40514aedb6d7938 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sat, 4 Jul 2026 17:51:04 +0300 Subject: [PATCH 585/681] Test removal of valac fallback. --- mesonbuild/compilers/detect.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/mesonbuild/compilers/detect.py b/mesonbuild/compilers/detect.py index 9bd76b648ffc..d3ce7d823786 100644 --- a/mesonbuild/compilers/detect.py +++ b/mesonbuild/compilers/detect.py @@ -1046,9 +1046,6 @@ def detect_cython_compiler(env: 'Environment', for_machine: MachineChoice) -> Co def detect_vala_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler: from .vala import ValaCompiler exelist = env.lookup_binary_entry(for_machine, 'vala') - if exelist is None and for_machine != MachineChoice.BUILD: - # This is wrong, but backwards-compatible. - exelist = env.lookup_binary_entry(MachineChoice.BUILD, 'vala') if exelist is None: # TODO support fallback exelist = [defaults['vala'][0]] From 002de1e540a014dc7bc49f7ac1ea351fd985decb Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sat, 4 Jul 2026 17:52:09 +0300 Subject: [PATCH 586/681] Add valac to cross file. --- cross/ubuntu-armhf.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/cross/ubuntu-armhf.txt b/cross/ubuntu-armhf.txt index 97a1c21e8682..0312a92e90d0 100644 --- a/cross/ubuntu-armhf.txt +++ b/cross/ubuntu-armhf.txt @@ -5,6 +5,7 @@ c = ['/usr/bin/arm-linux-gnueabihf-gcc'] cpp = ['/usr/bin/arm-linux-gnueabihf-g++'] rust = ['rustc', '--target', 'arm-unknown-linux-gnueabihf', '-C', 'linker=/usr/bin/arm-linux-gnueabihf-gcc-7'] rustdoc = ['rustdoc', '--target', 'arm-unknown-linux-gnueabihf', '-C', 'linker=/usr/bin/arm-linux-gnueabihf-gcc-7'] +vala = ['/usr/bin/arm-linux-gnueabihf-valac'] ar = '/usr/arm-linux-gnueabihf/bin/ar' strip = '/usr/arm-linux-gnueabihf/bin/strip' pkg-config = '/usr/bin/arm-linux-gnueabihf-pkg-config' From 15231ceccdd8644c730a9d11854da0bbff2656f0 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sat, 4 Jul 2026 18:59:52 +0300 Subject: [PATCH 587/681] Add snippet for Vala cross. --- docs/markdown/snippets/vala-cross.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 docs/markdown/snippets/vala-cross.md diff --git a/docs/markdown/snippets/vala-cross.md b/docs/markdown/snippets/vala-cross.md new file mode 100644 index 000000000000..c3abc50741b1 --- /dev/null +++ b/docs/markdown/snippets/vala-cross.md @@ -0,0 +1,7 @@ +## Vala cross compilation changes + +Traditionally Meson has used the same Vala compiler for cross and +native compilation. It turned out that even though the Vala compiler +generated C code, the output and its dependencies are platform +dependent. Now Meson uses Vala in the same way as all other cross +compilers and it can be defined in the cross file. From e4b644b9c7699bc1ab3433057af228bb7678759b Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 2 Jun 2026 10:44:43 -0700 Subject: [PATCH 588/681] interpreter: Add a default() function This gives a way to conditionally override a keyword argument, such as when setting a value specifically for one platform. For example, ```meson library( ..., name_prefix : host_machine.system() == 'windows' ? '' : default(), ) ``` --- data/syntax-highlighting/vim/syntax/meson.vim | 1 + docs/markdown/snippets/default.md | 13 +++++ docs/yaml/functions/default.yaml | 4 ++ docs/yaml/objects/default.yaml | 5 ++ mesonbuild/ast/interpreter.py | 1 + mesonbuild/interpreter/interpreter.py | 13 +++-- mesonbuild/interpreterbase/__init__.py | 2 + mesonbuild/interpreterbase/baseobjects.py | 4 ++ mesonbuild/interpreterbase/decorators.py | 23 +++++++++ mesonbuild/interpreterbase/interpreterbase.py | 8 +++- test cases/common/300 default/meson.build | 48 +++++++++++++++++++ test cases/common/300 default/prog.cpp | 8 ++++ 12 files changed, 126 insertions(+), 4 deletions(-) create mode 100644 docs/markdown/snippets/default.md create mode 100644 docs/yaml/functions/default.yaml create mode 100644 docs/yaml/objects/default.yaml create mode 100644 test cases/common/300 default/meson.build create mode 100644 test cases/common/300 default/prog.cpp diff --git a/data/syntax-highlighting/vim/syntax/meson.vim b/data/syntax-highlighting/vim/syntax/meson.vim index 905e9fe64fa6..63429ef45fa7 100644 --- a/data/syntax-highlighting/vim/syntax/meson.vim +++ b/data/syntax-highlighting/vim/syntax/meson.vim @@ -86,6 +86,7 @@ syn keyword mesonBuiltin \ custom_target \ debug \ declare_dependency + \ default \ dependency \ disabler \ environment diff --git a/docs/markdown/snippets/default.md b/docs/markdown/snippets/default.md new file mode 100644 index 000000000000..f2e4b7351f28 --- /dev/null +++ b/docs/markdown/snippets/default.md @@ -0,0 +1,13 @@ +## Add a new `default()` function and object + +This function and object allow setting a keyword argument to it's default value. +This is especially useful for cases when a non-default value is wanted in some +cases but not all. + +For example: +```meson +library( + ... + name_prefix : host_machine.system() == 'windows' ? '' : default(), +) +``` diff --git a/docs/yaml/functions/default.yaml b/docs/yaml/functions/default.yaml new file mode 100644 index 000000000000..68ce8d10daea --- /dev/null +++ b/docs/yaml/functions/default.yaml @@ -0,0 +1,4 @@ +name: default +returns: default +description: Returns a [[@default]] object. +since: 1.12.0 diff --git a/docs/yaml/objects/default.yaml b/docs/yaml/objects/default.yaml new file mode 100644 index 000000000000..9cb7bad09ee5 --- /dev/null +++ b/docs/yaml/objects/default.yaml @@ -0,0 +1,5 @@ +name: default +long_name: Default +description: | + A default object can be passed to keyword arguments to get the default value. + diff --git a/mesonbuild/ast/interpreter.py b/mesonbuild/ast/interpreter.py index 035ee2af2f1b..f1ac3500ec5f 100644 --- a/mesonbuild/ast/interpreter.py +++ b/mesonbuild/ast/interpreter.py @@ -242,6 +242,7 @@ def __init__(self, source_root: str, subdir: str, subproject: SubProject, subpro 'is_disabler': self.func_do_nothing, 'is_variable': self.func_do_nothing, 'disabler': self.func_do_nothing, + 'default': self.func_do_nothing, 'jar': self.func_do_nothing, 'warning': self.func_do_nothing, 'shared_module': self.func_do_nothing, diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index f4cb0b6531f5..2a5a2e97ba14 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -32,7 +32,7 @@ from ..interpreterbase import InterpreterException, InvalidArguments, InvalidCode, SubdirDoneRequest from ..interpreterbase import Disabler, disablerIfNotFound from ..interpreterbase import FeatureNew, FeatureDeprecated, FeatureBroken, FeatureNewKwargs -from ..interpreterbase import ObjectHolder, ContextManagerObject +from ..interpreterbase import ObjectHolder, ContextManagerObject, DefaultObject from ..interpreterbase import stringifyUserArguments, Feature, FeatureValue from ..modules import ExtensionModule, ModuleObject, MutableModuleObject, NewExtensionModule, NotFoundExtensionModule, __path__ as modules_path from ..optinterpreter import optname_regex @@ -390,6 +390,7 @@ def build_func_dict(self) -> None: 'declare_dependency': self.func_declare_dependency, 'dependency': self.func_dependency, 'disabler': self.func_disabler, + 'default': self.func_default, 'environment': self.func_environment, 'error': self.func_error, 'executable': self.func_executable, @@ -1972,6 +1973,12 @@ def func_dependency(self, node: mparser.BaseNode, args: T.Tuple[T.List[str]], kw f.use(self.subproject, node) return d + @FeatureNew('default', '1.12.0') + @noKwargs + @noPosargs + def func_default(self, node: mparser.BaseNode, args: list[TYPE_var], kwargs: TYPE_kwargs) -> DefaultObject: + return DefaultObject() + @FeatureNew('disabler', '0.44.0') @noKwargs @noPosargs @@ -4056,7 +4063,7 @@ def check_for_jar_sources(self, sources: T.Union[T.List[str], T.Sequence[T.Union def is_subproject(self) -> bool: return self.subproject != '' - @typed_pos_args('set_variable', str, object) + @typed_pos_args('set_variable', str, (object, DefaultObject)) @noKwargs @noArgsFlattening @noSecondLevelHolderResolving @@ -4066,7 +4073,7 @@ def func_set_variable(self, node: mparser.BaseNode, args: T.Tuple[str, TYPE_var raise InvalidCode('Invalid variable name: ' + varname) self.set_variable(varname, value, holderify=True) - @typed_pos_args('get_variable', (str, Disabler), optargs=[object]) + @typed_pos_args('get_variable', (str, Disabler, DefaultObject), optargs=[object]) @noKwargs @noArgsFlattening @unholder_return diff --git a/mesonbuild/interpreterbase/__init__.py b/mesonbuild/interpreterbase/__init__.py index dddb2027f3da..9004b09afd32 100644 --- a/mesonbuild/interpreterbase/__init__.py +++ b/mesonbuild/interpreterbase/__init__.py @@ -8,6 +8,7 @@ 'IterableObject', 'MutableInterpreterObject', 'ContextManagerObject', + 'DefaultObject', 'MesonOperator', @@ -70,6 +71,7 @@ IterableObject, MutableInterpreterObject, ContextManagerObject, + DefaultObject, TV_func, TYPE_elementary, diff --git a/mesonbuild/interpreterbase/baseobjects.py b/mesonbuild/interpreterbase/baseobjects.py index 39f3cebdcec6..23407a42ff08 100644 --- a/mesonbuild/interpreterbase/baseobjects.py +++ b/mesonbuild/interpreterbase/baseobjects.py @@ -233,3 +233,7 @@ def size(self) -> int: class ContextManagerObject(MesonInterpreterObject, AbstractContextManager): def __init__(self, subproject: 'SubProject') -> None: super().__init__(subproject=subproject) + + +class DefaultObject(MesonInterpreterObject): + """A default value to a keyword argument.""" diff --git a/mesonbuild/interpreterbase/decorators.py b/mesonbuild/interpreterbase/decorators.py index bfd7cc24afcd..ff2848cc288e 100644 --- a/mesonbuild/interpreterbase/decorators.py +++ b/mesonbuild/interpreterbase/decorators.py @@ -5,6 +5,7 @@ from .. import coredata, mesonlib, mlog from .disabler import Disabler +from .baseobjects import DefaultObject from .exceptions import InterpreterException, InvalidArguments from ._unholder import _unholder @@ -216,6 +217,7 @@ def wrapper(*wrapped_args: T.Any, **wrapped_kwargs: T.Any) -> T.Any: num_args = len(args) num_types = len(types) a_types = types + last_pos = num_args if varargs: min_args = num_types + min_varargs @@ -238,6 +240,18 @@ def wrapper(*wrapped_args: T.Any, **wrapped_kwargs: T.Any) -> T.Any: for i, (arg, type_) in enumerate(itertools.zip_longest(args, a_types, fillvalue=varargs), start=1): if not isinstance(arg, type_): + # if DefaultObject is an explicit allowed allowed type allow + # it through. + if isinstance(arg, DefaultObject): + if i >= last_pos: + if varargs: + msg = 'not allowed for variadic arguments' + else: + msg = 'not allowed for optional positional arguments' + else: + msg = 'not allowed for required positional arguments' + raise InvalidArguments(f'default() objects are {msg}') + if isinstance(type_, tuple): shouldbe = 'one of: {}'.format(", ".join(f'"{t.__name__}"' for t in type_)) else: @@ -565,6 +579,15 @@ def emit_feature_change(values: T.Dict[_T, T.Union[str, T.Tuple[str, str]]], fea for info in types: types_tuple = info.types if isinstance(info.types, tuple) else (info.types,) value = kwargs.get(info.name) + if isinstance(value, DefaultObject): + # Ensure that default() is not used for required options + # Otherwise, set the value to None, which will send us down + # the "unset" path + if info.required: + raise InvalidArguments(f'"{name}" got a default() value for the required keyword argument "{info.name}". ' + 'default() may not be used for required keyword arguments.') + value = None + if value is not None: if info.since: feature_name = info.name + ' arg in ' + name diff --git a/mesonbuild/interpreterbase/interpreterbase.py b/mesonbuild/interpreterbase/interpreterbase.py index 3899858f5d58..247e25e83934 100644 --- a/mesonbuild/interpreterbase/interpreterbase.py +++ b/mesonbuild/interpreterbase/interpreterbase.py @@ -14,6 +14,7 @@ ObjectHolder, IterableObject, ContextManagerObject, + DefaultObject, HoldableTypes, ) @@ -612,7 +613,12 @@ def reduce_arguments( def expand_default_kwargs(self, kwargs: T.Dict[str, T.Optional[InterpreterObject]]) -> T.Dict[str, T.Optional[InterpreterObject]]: if 'kwargs' not in kwargs: return kwargs - to_expand = _unholder(kwargs.pop('kwargs')) + + kwargs_var = kwargs.pop('kwargs') + if isinstance(kwargs_var, DefaultObject): + return kwargs + + to_expand = _unholder(kwargs_var) if not isinstance(to_expand, dict): raise InterpreterException('Value of "kwargs" must be dictionary.') if 'kwargs' in to_expand: diff --git a/test cases/common/300 default/meson.build b/test cases/common/300 default/meson.build new file mode 100644 index 000000000000..7133e5e6a288 --- /dev/null +++ b/test cases/common/300 default/meson.build @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright © 2026 Intel Corporation + +project( + 'default', + 'cpp', + meson_version : '>= 1.12.0', + default_options : {'cpp_std' : 'c++14'}, +) + +exe = executable( + 'prog', + 'prog.cpp', + override_options : default(), +) + +exe2 = executable( + 'prog2', + 'prog.cpp', + kwargs : default(), +) + +test_kwargs = { + 'protocol' : default(), +} + +test('exe_test', exe, kwargs : test_kwargs) + +testcase expect_error('default() objects are not allowed for required positional arguments') + executable(default(), 'prog.cpp') +endtestcase + +testcase expect_error('default() objects are not allowed for variadic arguments') + add_languages(default()) +endtestcase + +testcase expect_error('default() objects are not allowed for optional positional arguments') + assert(true, default()) +endtestcase + +testcase expect_error('"install_symlink" got a default() value for the required keyword argument "install_dir". default() may not be used for required keyword arguments.') + install_symlink('foo', pointing_to : '/foo', install_dir : default()) +endtestcase + +foo = default() +get_variable('foo') +unset_variable('foo') +set_variable('var', default()) diff --git a/test cases/common/300 default/prog.cpp b/test cases/common/300 default/prog.cpp new file mode 100644 index 000000000000..54a466f64a99 --- /dev/null +++ b/test cases/common/300 default/prog.cpp @@ -0,0 +1,8 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright © 2026 Intel Corporation + */ + +int main() { + return __cplusplus == 201402L ? 0 : 1; +} From 25395cc32a0cd86a38a4b1f2176004d17bfa99c9 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 2 Jun 2026 10:56:17 -0700 Subject: [PATCH 589/681] interpreter: deprecate passing `[]` to build_target(*_prefix: ...) We have the `default()` function now, so we don't need this. --- docs/yaml/functions/_build_target_base.yaml | 10 ++++++---- mesonbuild/interpreter/type_checking.py | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/yaml/functions/_build_target_base.yaml b/docs/yaml/functions/_build_target_base.yaml index c2c4552e9fa1..b8612569d04b 100644 --- a/docs/yaml/functions/_build_target_base.yaml +++ b/docs/yaml/functions/_build_target_base.yaml @@ -223,7 +223,7 @@ kwargs: object files had to be placed in `sources`. name_prefix: - type: str | array[void] + type: str description: | The string that will be used as the prefix for the target output filename by overriding the default (only used for @@ -231,10 +231,11 @@ kwargs: except for MSVC shared libraries where it is omitted to follow convention, and Cygwin shared libraries where it is `cyg`. - Set this to `[]`, or omit the keyword argument for the default behaviour. + To conditionally keep the default behavior use a [[default]] object. + For Meson < 1.12, use `[]`. name_suffix: - type: str | array[void] + type: str description: | The string that will be used as the extension for the target by overriding the default. By default on Windows this is @@ -247,7 +248,8 @@ kwargs: potential name clash with shared libraries which also generate import libraries with a `lib` suffix. - Set this to `[]`, or omit the keyword argument for the default behaviour. + To conditionally keep the default behavior use a [[default]] object. + For Meson < 1.12, use `[]`. override_options: type: array[str] | dict[str | bool | int | array[str]] diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py index 7280f66303d7..b029b98358db 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -706,6 +706,7 @@ def _name_suffix_validator(arg: T.Optional[T.Union[str, T.List]]) -> T.Optional[ (str, NoneType, list), validator=_name_validator, convertor=lambda x: None if isinstance(x, list) else x, + deprecated_values={list: ('1.12.0', 'use the `default()` function instead')}, ) From f99d3c0e56c12220375c9cea60c9b64ab1695ccc Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sun, 5 Jul 2026 14:16:27 +0300 Subject: [PATCH 590/681] Renumber test case directories for rc1. --- run_format_tests.py | 2 +- .../meson.build | 0 .../simple.c | 0 .../simple.h | 0 .../subprojects/simple2/exports.def | 0 .../subprojects/simple2/meson.build | 0 .../subprojects/simple2/simple2.c | 0 .../subprojects/simple2/simple2.h | 0 .../test.json | 0 .../common/{287 atomic => 288 atomic}/a.c | 0 .../{287 atomic => 288 atomic}/meson.build | 0 .../meson.build | 0 .../useistd.cpp | 0 .../lib.c | 0 .../main.c | 0 .../meson.build | 0 .../subprojects/sub/meson.build | 0 .../meson.build | 0 .../subprojects/libfoobar/meson.build | 0 .../subprojects/subproject/meson.build | 0 .../subprojects/test-subproject.wrap | 0 .../subprojects/test.wrap | 0 .../intlib.cpp | 0 .../meson.build | 0 .../p2test.cpp | 0 .../subprojects/pcre2/meson.build | 0 .../subprojects/pcre2/pcre.c | 0 .../test.json | 0 .../cpplib.cpp | 0 .../cpplib.h | 0 .../cppmain.cpp | 0 .../libfile.c | 0 .../main.c | 0 .../meson.build | 0 .../test.json | 0 .../keyctl.c | 0 .../meson.build | 0 .../override/linux/keyctl.h | 0 .../override/sys/keyctl.h | 0 .../test.json | 0 .../uapi/linux/keyctl.h | 0 .../a.c | 0 .../all.h | 0 .../b.c | 0 .../main.c | 0 .../meson.build | 0 .../generate_config.py | 0 .../generate_main.py | 0 .../main.config.ini | 0 .../main.template | 0 .../meson.build | 0 .../meson.build | 0 .../test.json | 10 ++-- .../f.cc | 0 .../f.h | 0 .../meson.build | 0 .../pch/f_pch.h | 0 .../customtarget.py | 0 .../foo.py | 0 .../meson.build | 0 .../subprojects/sub/meson.build | 0 .../subprojects/sub/trivial.c | 0 .../test.py | 0 .../main.c | 0 .../meson.build | 0 .../test.json | 2 +- .../{300 default => 301 default}/meson.build | 0 .../{300 default => 301 default}/prog.cpp | 0 .../meson.build | 0 .../sub/meson.build | 0 .../test.json | 2 +- .../109 invalid fstring/meson.build | 0 .../109 invalid fstring/test.json | 0 .../meson.build | 0 .../test.json | 2 +- .../meson.build | 0 .../test.json | 2 +- .../meson.build | 0 .../subprojects/foo/meson.build | 0 .../test.json | 2 +- .../meson.build | 0 .../subprojects/cmlib/CMakeLists.txt | 0 .../subprojects/cmlib/main.c | 0 .../test.json | 2 +- .../meson.build | 0 .../test.json | 2 +- .../meson.build | 0 .../src/header.h | 0 .../src/source.c | 0 .../test.json | 2 +- .../meson.build | 0 .../test.json | 2 +- .../trivial.c | 0 .../meson.build | 0 .../test.json | 2 +- .../trivial.c | 0 .../meson.build | 0 .../test.json | 2 +- .../meson.build | 0 .../subprojects/A/meson.build | 0 .../subprojects/B/meson.build | 0 .../test.json | 2 +- .../main.rs | 0 .../meson.build | 0 .../test.json | 2 +- .../main.rs | 0 .../meson.build | 0 .../test.json | 2 +- .../meson.build | 0 .../subprojects/sub/main.c | 0 .../subprojects/sub/meson.build | 0 .../test.json | 2 +- .../meson.build | 0 .../subprojects/cmlib/CMakeLists.txt | 0 .../test.json | 2 +- .../meson.build | 0 .../test.json | 2 +- .../meson.build | 0 .../meson_options.txt | 0 .../subprojects/subproj1/file.txt | 0 .../subprojects/subproj1/meson.build | 0 .../subprojects/subproj1/nested/meson.build | 0 .../subprojects/subproj2/file.txt | 0 .../subprojects/subproj2/meson.build | 0 .../subprojects/subproj2/nested/meson.build | 0 .../subprojects/subproj3/file.txt | 0 .../subprojects/subproj3/meson.build | 0 .../test.json | 2 +- .../inc/lib.h | 0 .../lib.c | 0 .../meson.build | 0 .../subprojects/a/meson.build | 0 .../subprojects/a/prog.c | 0 .../test.json | 2 +- .../inc/lib.h | 0 .../lib.c | 0 .../meson.build | 0 .../test.json | 2 +- .../meson.build | 0 .../src1.c | 0 .../src2.c | 0 .../test.json | 2 +- .../exe.c | 0 .../lib.in | 0 .../meson.build | 0 .../test.json | 0 .../meson.build | 0 .../test.json | 2 +- .../meson.build | 0 .../test.json | 2 +- .../meson.build | 0 .../test.json | 2 +- .../meson.build | 0 .../subdir/meson.build | 0 .../test.json | 2 +- .../meson.build | 0 .../meson.options | 0 .../test.json | 2 +- .../meson.build | 0 .../test.json | 2 +- .../dub.json | 0 .../dub.selections.json | 0 .../meson.build | 0 .../source/app.d | 0 .../test.json | 2 +- .../Test.java | 0 .../meson.build | 0 .../test.json | 2 +- .../f.rs | 0 .../meson.build | 0 .../test.json | 2 +- .../existing-dir/config.h.in | 0 .../meson.build | 0 .../test.json | 2 +- .../meson.build | 0 .../test.json | 2 +- .../foo.c | 0 .../meson.build | 0 .../test.json | 2 +- .../meson.build | 0 .../test.json | 2 +- .../meson.build | 0 .../test.json | 2 +- .../meson.build | 0 .../test.json | 2 +- .../meson.build | 0 .../test.json | 2 +- .../meson.build | 0 .../meson.options | 0 .../test.json | 2 +- .../meson.build | 0 .../test.json | 2 +- .../meson.build | 0 .../meson.options | 0 .../test.json | 2 +- .../meson.build | 0 .../test.json | 2 +- .../meson.build | 0 .../test.json | 2 +- .../meson.build | 0 .../test.json | 2 +- .../meson.build | 0 .../subprojects/x/meson.build | 0 .../test.json | 2 +- .../meson.build | 0 .../test.json | 2 +- .../meson.build | 0 .../subprojects/help/meson.build | 0 .../test.json | 2 +- .../file.txt.in | 0 .../meson.build | 0 .../subdir/meson.build | 0 .../foo.py | 0 .../meson.build | 0 .../subdir/meson.build | 0 .../lib2.rs | 0 .../main.rs | 0 .../meson.build | 0 .../meson.build | 0 .../unit/{103 strip => 104 strip}/lib.c | 0 .../unit/{103 strip => 104 strip}/meson.build | 0 .../meson.build | 0 .../meson.build | 0 .../main.c | 0 .../meson.build | 0 .../runner.py | 0 .../cp.py | 0 .../main.c | 0 .../meson.build | 0 .../symlinked_subproject/datadir/datafile | 0 .../symlinked_subproject/datadir/meson.build | 0 .../symlinked_subproject/meson.build | 0 .../symlinked_subproject/src.c | 0 .../meson.build | 0 .../meson_options.txt | 0 .../subprojects/foo/foo.c | 0 .../subprojects/foo/meson.build | 0 .../meson.build | 0 .../meson_options.txt | 0 .../unit/{110 freeze => 111 freeze}/freeze.c | 0 .../{110 freeze => 111 freeze}/meson.build | 0 .../meson.build | 0 .../script.py | 0 .../com/mesonbuild/Simple.java | 0 .../meson.build | 0 .../meson.build | 0 .../meson_options.txt | 0 .../main.c | 0 .../meson.build | 0 .../s1.c | 0 .../s2.c | 0 .../s3.c | 0 .../meson.build | 0 .../meson.options | 0 .../expected_mods.json | 0 .../meson.build | 0 .../{117 genvslite => 118 genvslite}/main.cpp | 0 .../meson.build | 0 .../cache_dir/bar/meson.build | 0 .../cache_dir/foo.zip | Bin .../meson.build | 0 .../subprojects/bar.wrap | 0 .../subprojects/foo.wrap | 0 .../meson.build | 0 .../nativefile.ini | 0 .../{120 rewrite => 121 rewrite}/meson.build | 0 .../main.c | 0 .../meson.build | 0 .../meson.build | 0 .../meson.options | 0 .../subprojects/sub1/meson.build | 0 .../subprojects/sub1/meson.options | 0 .../subprojects/sub1/sub1.c | 0 .../subprojects/sub2/meson.build | 0 .../subprojects/sub2/meson.options | 0 .../subprojects/sub2/sub2.c | 0 .../toplevel.c | 0 .../meson.build | 0 .../subprojects/sub/meson.build | 0 .../meson.build | 0 .../script.py | 0 .../meson.build | 0 .../meson_options.txt | 0 .../foo.c | 0 .../meson.build | 0 .../meson.build | 0 .../subprojects/sub/meson.build | 0 .../meson.build | 0 .../test.py | 0 .../meson.build | 0 .../meson.build | 0 .../meson_options.txt | 0 .../lib.vala | 0 .../meson.build | 0 .../meson.options | 0 .../meson.build | 0 .../meson.build | 0 .../consumer.c | 0 .../header.h | 0 .../lib.cpp | 0 .../meson.build | 0 .../header.h | 0 .../meson.build | 0 .../meson.options | 0 .../meson.build | 0 .../subprojects/recursive-both/lib.c | 0 .../subprojects/recursive-both/lib.def | 0 .../subprojects/recursive-both/meson.build | 0 .../recursive-both/recursive-both.h.in | 0 .../meson.build | 0 .../meson_options.txt | 0 .../propfile | 0 .../foo.c | 0 .../foo.dat | 0 .../foo.h | 0 .../foo/foofile | 0 .../meson.build | 0 .../subprojects/bar/bar.c | 0 .../subprojects/bar/bar.dat | 0 .../subprojects/bar/bar.h | 0 .../subprojects/bar/bar/barfile | 0 .../subprojects/bar/meson.build | 0 .../meson.build | 0 .../meson_options.txt | 0 .../subprojects/sub/foo.c | 0 .../subprojects/sub/meson.build | 0 .../.clang-format | 0 .../.clang-format-ignore | 0 .../.clang-format-include | 0 .../meson.build | 0 .../not-included/badformat.cpp | 0 .../src/badformat.c | 0 .../src/badformat.cpp | 0 .../easytogrepfor/genh.py | 0 .../easytogrepfor/meson.build | 0 .../{94 custominc => 95 custominc}/helper.c | 0 .../meson.build | 0 .../{94 custominc => 95 custominc}/prog.c | 0 .../{94 custominc => 95 custominc}/prog2.c | 0 .../meson.build | 0 .../subprojects/something/meson.build | 0 .../meson.build | 0 .../test.c | 0 .../.gitignore | 0 .../libtestprovider/meson.build | 0 .../libtestprovider/provider.c | 0 .../proguser/meson.build | 0 .../proguser/receiver.c | 0 .../bar-custom.txt | 0 .../bar-devel.h | 0 .../bar-notag.txt | 0 .../custom_files/data.txt | 0 .../excludes/excluded.txt | 0 .../excludes/excluded/placeholder.txt | 0 .../excludes/installed.txt | 0 .../foo.in | 0 .../foo1-devel.h | 0 .../lib.c | 0 .../main.c | 0 .../meson.build | 0 .../script.py | 0 .../subdir/bar2-devel.h | 0 .../subdir/foo2.in | 0 .../subdir/foo3-devel.h | 0 .../subdir/lib.c | 0 .../subdir/main.c | 0 .../subdir/meson.build | 0 .../subdir/script.py | 0 .../subprojects/subproject/aaa.txt | 0 .../subprojects/subproject/bbb.txt | 0 .../subprojects/subproject/meson.build | 0 unittests/allplatformstests.py | 50 +++++++++--------- unittests/linuxliketests.py | 18 +++---- unittests/machinefiletests.py | 12 ++--- unittests/platformagnostictests.py | 20 +++---- unittests/rewritetests.py | 4 +- unittests/windowstests.py | 2 +- 377 files changed, 105 insertions(+), 105 deletions(-) rename test cases/common/{286 pkgconfig subproject => 287 pkgconfig subproject}/meson.build (100%) rename test cases/common/{286 pkgconfig subproject => 287 pkgconfig subproject}/simple.c (100%) rename test cases/common/{286 pkgconfig subproject => 287 pkgconfig subproject}/simple.h (100%) rename test cases/common/{286 pkgconfig subproject => 287 pkgconfig subproject}/subprojects/simple2/exports.def (100%) rename test cases/common/{286 pkgconfig subproject => 287 pkgconfig subproject}/subprojects/simple2/meson.build (100%) rename test cases/common/{286 pkgconfig subproject => 287 pkgconfig subproject}/subprojects/simple2/simple2.c (100%) rename test cases/common/{286 pkgconfig subproject => 287 pkgconfig subproject}/subprojects/simple2/simple2.h (100%) rename test cases/common/{286 pkgconfig subproject => 287 pkgconfig subproject}/test.json (100%) rename test cases/common/{287 atomic => 288 atomic}/a.c (100%) rename test cases/common/{287 atomic => 288 atomic}/meson.build (100%) rename test cases/common/{288 importstd => 289 importstd}/meson.build (100%) rename test cases/common/{288 importstd => 289 importstd}/useistd.cpp (100%) rename test cases/common/{289 invalid dependency arguments => 290 invalid dependency arguments}/lib.c (100%) rename test cases/common/{289 invalid dependency arguments => 290 invalid dependency arguments}/main.c (100%) rename test cases/common/{289 invalid dependency arguments => 290 invalid dependency arguments}/meson.build (100%) rename test cases/common/{289 invalid dependency arguments => 290 invalid dependency arguments}/subprojects/sub/meson.build (100%) rename test cases/common/{290 multiple provides => 291 multiple provides}/meson.build (100%) rename test cases/common/{290 multiple provides => 291 multiple provides}/subprojects/libfoobar/meson.build (100%) rename test cases/common/{290 multiple provides => 291 multiple provides}/subprojects/subproject/meson.build (100%) rename test cases/common/{290 multiple provides => 291 multiple provides}/subprojects/subproject/subprojects/test-subproject.wrap (100%) rename test cases/common/{290 multiple provides => 291 multiple provides}/subprojects/test.wrap (100%) rename test cases/common/{291 subproject with foreign lang => 292 subproject with foreign lang}/intlib.cpp (100%) rename test cases/common/{291 subproject with foreign lang => 292 subproject with foreign lang}/meson.build (100%) rename test cases/common/{291 subproject with foreign lang => 292 subproject with foreign lang}/p2test.cpp (100%) rename test cases/common/{291 subproject with foreign lang => 292 subproject with foreign lang}/subprojects/pcre2/meson.build (100%) rename test cases/common/{291 subproject with foreign lang => 292 subproject with foreign lang}/subprojects/pcre2/pcre.c (100%) rename test cases/common/{291 subproject with foreign lang => 292 subproject with foreign lang}/test.json (100%) rename test cases/common/{292 link early args => 293 link early args}/cpplib.cpp (100%) rename test cases/common/{292 link early args => 293 link early args}/cpplib.h (100%) rename test cases/common/{292 link early args => 293 link early args}/cppmain.cpp (100%) rename test cases/common/{292 link early args => 293 link early args}/libfile.c (100%) rename test cases/common/{292 link early args => 293 link early args}/main.c (100%) rename test cases/common/{292 link early args => 293 link early args}/meson.build (100%) rename test cases/common/{292 link early args => 293 link early args}/test.json (100%) rename test cases/common/{293 systemd regression => 294 systemd regression}/keyctl.c (100%) rename test cases/common/{293 systemd regression => 294 systemd regression}/meson.build (100%) rename test cases/common/{293 systemd regression => 294 systemd regression}/override/linux/keyctl.h (100%) rename test cases/common/{293 systemd regression => 294 systemd regression}/override/sys/keyctl.h (100%) rename test cases/common/{293 systemd regression => 294 systemd regression}/test.json (100%) rename test cases/common/{293 systemd regression => 294 systemd regression}/uapi/linux/keyctl.h (100%) rename test cases/common/{294 source set if_false deps => 295 source set if_false deps}/a.c (100%) rename test cases/common/{294 source set if_false deps => 295 source set if_false deps}/all.h (100%) rename test cases/common/{294 source set if_false deps => 295 source set if_false deps}/b.c (100%) rename test cases/common/{294 source set if_false deps => 295 source set if_false deps}/main.c (100%) rename test cases/common/{294 source set if_false deps => 295 source set if_false deps}/meson.build (100%) rename test cases/common/{295 depends in generator.process => 296 depends in generator.process}/generate_config.py (100%) rename test cases/common/{295 depends in generator.process => 296 depends in generator.process}/generate_main.py (100%) rename test cases/common/{295 depends in generator.process => 296 depends in generator.process}/main.config.ini (100%) rename test cases/common/{295 depends in generator.process => 296 depends in generator.process}/main.template (100%) rename test cases/common/{295 depends in generator.process => 296 depends in generator.process}/meson.build (100%) rename test cases/common/{295 redundant version check => 297 redundant version check}/meson.build (100%) rename test cases/common/{295 redundant version check => 297 redundant version check}/test.json (61%) rename test cases/common/{296 pch link_language => 298 pch link_language}/f.cc (100%) rename test cases/common/{296 pch link_language => 298 pch link_language}/f.h (100%) rename test cases/common/{296 pch link_language => 298 pch link_language}/meson.build (100%) rename test cases/common/{296 pch link_language => 298 pch link_language}/pch/f_pch.h (100%) rename test cases/common/{296 program in kwargs => 299 program in kwargs}/customtarget.py (100%) rename test cases/common/{296 program in kwargs => 299 program in kwargs}/foo.py (100%) rename test cases/common/{296 program in kwargs => 299 program in kwargs}/meson.build (100%) rename test cases/common/{296 program in kwargs => 299 program in kwargs}/subprojects/sub/meson.build (100%) rename test cases/common/{296 program in kwargs => 299 program in kwargs}/subprojects/sub/trivial.c (100%) rename test cases/common/{296 program in kwargs => 299 program in kwargs}/test.py (100%) rename test cases/common/{299 built file by path => 300 built file by path}/main.c (100%) rename test cases/common/{299 built file by path => 300 built file by path}/meson.build (100%) rename test cases/common/{299 built file by path => 300 built file by path}/test.json (76%) rename test cases/common/{300 default => 301 default}/meson.build (100%) rename test cases/common/{300 default => 301 default}/prog.cpp (100%) rename test cases/failing/{104 enter subdir twice => 103 enter subdir twice}/meson.build (100%) rename test cases/failing/{104 enter subdir twice => 103 enter subdir twice}/sub/meson.build (100%) rename test cases/failing/{104 enter subdir twice => 103 enter subdir twice}/test.json (64%) rename test cases/failing/{105 invalid fstring => 104 invalid fstring}/109 invalid fstring/meson.build (100%) rename test cases/failing/{105 invalid fstring => 104 invalid fstring}/109 invalid fstring/test.json (100%) rename test cases/failing/{105 invalid fstring => 104 invalid fstring}/meson.build (100%) rename test cases/failing/{105 invalid fstring => 104 invalid fstring}/test.json (63%) rename test cases/failing/{106 compiler argument checking => 105 compiler argument checking}/meson.build (100%) rename test cases/failing/{106 compiler argument checking => 105 compiler argument checking}/test.json (66%) rename test cases/failing/{107 empty fallback => 106 empty fallback}/meson.build (100%) rename test cases/failing/{107 empty fallback => 106 empty fallback}/subprojects/foo/meson.build (100%) rename test cases/failing/{107 empty fallback => 106 empty fallback}/test.json (65%) rename test cases/failing/{108 cmake executable dependency => 107 cmake executable dependency}/meson.build (100%) rename test cases/failing/{108 cmake executable dependency => 107 cmake executable dependency}/subprojects/cmlib/CMakeLists.txt (100%) rename test cases/failing/{108 cmake executable dependency => 107 cmake executable dependency}/subprojects/cmlib/main.c (100%) rename test cases/failing/{108 cmake executable dependency => 107 cmake executable dependency}/test.json (77%) rename test cases/failing/{109 allow_fallback with fallback => 108 allow_fallback with fallback}/meson.build (100%) rename test cases/failing/{109 allow_fallback with fallback => 108 allow_fallback with fallback}/test.json (70%) rename test cases/failing/{110 nonsensical bindgen => 109 nonsensical bindgen}/meson.build (100%) rename test cases/failing/{110 nonsensical bindgen => 109 nonsensical bindgen}/src/header.h (100%) rename test cases/failing/{110 nonsensical bindgen => 109 nonsensical bindgen}/src/source.c (100%) rename test cases/failing/{110 nonsensical bindgen => 109 nonsensical bindgen}/test.json (69%) rename test cases/failing/{111 run_target in test => 110 run_target in test}/meson.build (100%) rename test cases/failing/{111 run_target in test => 110 run_target in test}/test.json (78%) rename test cases/failing/{111 run_target in test => 110 run_target in test}/trivial.c (100%) rename test cases/failing/{112 run_target in add_install_script => 111 run_target in add_install_script}/meson.build (100%) rename test cases/failing/{112 run_target in add_install_script => 111 run_target in add_install_script}/test.json (80%) rename test cases/failing/{112 run_target in add_install_script => 111 run_target in add_install_script}/trivial.c (100%) rename test cases/failing/{113 pathsep in install_symlink => 112 pathsep in install_symlink}/meson.build (100%) rename test cases/failing/{113 pathsep in install_symlink => 112 pathsep in install_symlink}/test.json (74%) rename test cases/failing/{114 subproject version conflict => 113 subproject version conflict}/meson.build (100%) rename test cases/failing/{114 subproject version conflict => 113 subproject version conflict}/subprojects/A/meson.build (100%) rename test cases/failing/{114 subproject version conflict => 113 subproject version conflict}/subprojects/B/meson.build (100%) rename test cases/failing/{114 subproject version conflict => 113 subproject version conflict}/test.json (66%) rename test cases/failing/{115 structured source empty string => 114 structured source empty string}/main.rs (100%) rename test cases/failing/{115 structured source empty string => 114 structured source empty string}/meson.build (100%) rename test cases/failing/{115 structured source empty string => 114 structured source empty string}/test.json (71%) rename test cases/failing/{116 structured_sources conflicts => 115 structured_sources conflicts}/main.rs (100%) rename test cases/failing/{116 structured_sources conflicts => 115 structured_sources conflicts}/meson.build (100%) rename test cases/failing/{116 structured_sources conflicts => 115 structured_sources conflicts}/test.json (67%) rename test cases/failing/{117 missing compiler => 116 missing compiler}/meson.build (100%) rename test cases/failing/{117 missing compiler => 116 missing compiler}/subprojects/sub/main.c (100%) rename test cases/failing/{117 missing compiler => 116 missing compiler}/subprojects/sub/meson.build (100%) rename test cases/failing/{117 missing compiler => 116 missing compiler}/test.json (68%) rename test cases/failing/{118 cmake subproject error => 117 cmake subproject error}/meson.build (100%) rename test cases/failing/{118 cmake subproject error => 117 cmake subproject error}/subprojects/cmlib/CMakeLists.txt (100%) rename test cases/failing/{118 cmake subproject error => 117 cmake subproject error}/test.json (74%) rename test cases/failing/{119 pkgconfig not relocatable outside prefix => 118 pkgconfig not relocatable outside prefix}/meson.build (100%) rename test cases/failing/{119 pkgconfig not relocatable outside prefix => 118 pkgconfig not relocatable outside prefix}/test.json (79%) rename test cases/failing/{120 subproject sandbox violation => 119 subproject sandbox violation}/meson.build (100%) rename test cases/failing/{120 subproject sandbox violation => 119 subproject sandbox violation}/meson_options.txt (100%) rename test cases/failing/{120 subproject sandbox violation => 119 subproject sandbox violation}/subprojects/subproj1/file.txt (100%) rename test cases/failing/{120 subproject sandbox violation => 119 subproject sandbox violation}/subprojects/subproj1/meson.build (100%) rename test cases/failing/{120 subproject sandbox violation => 119 subproject sandbox violation}/subprojects/subproj1/nested/meson.build (100%) rename test cases/failing/{120 subproject sandbox violation => 119 subproject sandbox violation}/subprojects/subproj2/file.txt (100%) rename test cases/failing/{120 subproject sandbox violation => 119 subproject sandbox violation}/subprojects/subproj2/meson.build (100%) rename test cases/failing/{120 subproject sandbox violation => 119 subproject sandbox violation}/subprojects/subproj2/nested/meson.build (100%) rename test cases/failing/{120 subproject sandbox violation => 119 subproject sandbox violation}/subprojects/subproj3/file.txt (100%) rename test cases/failing/{120 subproject sandbox violation => 119 subproject sandbox violation}/subprojects/subproj3/meson.build (100%) rename test cases/failing/{120 subproject sandbox violation => 119 subproject sandbox violation}/test.json (82%) rename test cases/failing/{121 override and add_project_dependency => 120 override and add_project_dependency}/inc/lib.h (100%) rename test cases/failing/{121 override and add_project_dependency => 120 override and add_project_dependency}/lib.c (100%) rename test cases/failing/{121 override and add_project_dependency => 120 override and add_project_dependency}/meson.build (100%) rename test cases/failing/{121 override and add_project_dependency => 120 override and add_project_dependency}/subprojects/a/meson.build (100%) rename test cases/failing/{121 override and add_project_dependency => 120 override and add_project_dependency}/subprojects/a/prog.c (100%) rename test cases/failing/{121 override and add_project_dependency => 120 override and add_project_dependency}/test.json (66%) rename test cases/failing/{122 targets before add_project_dependency => 121 targets before add_project_dependency}/inc/lib.h (100%) rename test cases/failing/{122 targets before add_project_dependency => 121 targets before add_project_dependency}/lib.c (100%) rename test cases/failing/{122 targets before add_project_dependency => 121 targets before add_project_dependency}/meson.build (100%) rename test cases/failing/{122 targets before add_project_dependency => 121 targets before add_project_dependency}/test.json (70%) rename test cases/failing/{123 extract from unity => 122 extract from unity}/meson.build (100%) rename test cases/failing/{123 extract from unity => 122 extract from unity}/src1.c (100%) rename test cases/failing/{123 extract from unity => 122 extract from unity}/src2.c (100%) rename test cases/failing/{123 extract from unity => 122 extract from unity}/test.json (73%) rename test cases/failing/{124 generator host binary => 123 generator host binary}/exe.c (100%) rename test cases/failing/{124 generator host binary => 123 generator host binary}/lib.in (100%) rename test cases/failing/{124 generator host binary => 123 generator host binary}/meson.build (100%) rename test cases/failing/{124 generator host binary => 123 generator host binary}/test.json (100%) rename test cases/failing/{125 invalid ast => 124 invalid ast}/meson.build (100%) rename test cases/failing/{125 invalid ast => 124 invalid ast}/test.json (67%) rename test cases/failing/{126 invalid project function => 125 invalid project function}/meson.build (100%) rename test cases/failing/{126 invalid project function => 125 invalid project function}/test.json (69%) rename test cases/failing/{127 utf8 with bom => 126 utf8 with bom}/meson.build (100%) rename test cases/failing/{127 utf8 with bom => 126 utf8 with bom}/test.json (63%) rename test cases/failing/{128 utf8 with bom subdir => 127 utf8 with bom subdir}/meson.build (100%) rename test cases/failing/{128 utf8 with bom subdir => 127 utf8 with bom subdir}/subdir/meson.build (100%) rename test cases/failing/{128 utf8 with bom subdir => 127 utf8 with bom subdir}/test.json (66%) rename test cases/failing/{129 utf8 with bom options => 128 utf8 with bom options}/meson.build (100%) rename test cases/failing/{129 utf8 with bom options => 128 utf8 with bom options}/meson.options (100%) rename test cases/failing/{129 utf8 with bom options => 128 utf8 with bom options}/test.json (65%) rename test cases/failing/{130 module use inside project decl => 129 module use inside project decl}/meson.build (100%) rename test cases/failing/{130 module use inside project decl => 129 module use inside project decl}/test.json (72%) rename test cases/failing/{131 dub missing dependency => 130 dub missing dependency}/dub.json (100%) rename test cases/failing/{131 dub missing dependency => 130 dub missing dependency}/dub.selections.json (100%) rename test cases/failing/{131 dub missing dependency => 130 dub missing dependency}/meson.build (100%) rename test cases/failing/{131 dub missing dependency => 130 dub missing dependency}/source/app.d (100%) rename test cases/failing/{131 dub missing dependency => 130 dub missing dependency}/test.json (64%) rename test cases/failing/{132 java sources in non jar target => 131 java sources in non jar target}/Test.java (100%) rename test cases/failing/{132 java sources in non jar target => 131 java sources in non jar target}/meson.build (100%) rename test cases/failing/{132 java sources in non jar target => 131 java sources in non jar target}/test.json (71%) rename test cases/failing/{133 rust link_language => 132 rust link_language}/f.rs (100%) rename test cases/failing/{133 rust link_language => 132 rust link_language}/meson.build (100%) rename test cases/failing/{133 rust link_language => 132 rust link_language}/test.json (66%) rename test cases/failing/{134 invalid build_subdir => 133 invalid build_subdir}/existing-dir/config.h.in (100%) rename test cases/failing/{134 invalid build_subdir => 133 invalid build_subdir}/meson.build (100%) rename test cases/failing/{134 invalid build_subdir => 133 invalid build_subdir}/test.json (65%) rename test cases/failing/{135 misspelled method kwargs => 136 misspelled method kwargs}/meson.build (100%) rename test cases/failing/{135 misspelled method kwargs => 136 misspelled method kwargs}/test.json (71%) rename test cases/failing/{136 cython using implicit c explicitly => 137 cython using implicit c explicitly}/foo.c (100%) rename test cases/failing/{136 cython using implicit c explicitly => 137 cython using implicit c explicitly}/meson.build (100%) rename test cases/failing/{136 cython using implicit c explicitly => 137 cython using implicit c explicitly}/test.json (62%) rename test cases/failing/{136 misspelled function name => 138 misspelled function name}/meson.build (100%) rename test cases/failing/{136 misspelled function name => 138 misspelled function name}/test.json (65%) rename test cases/failing/{137 misspelled method name => 139 misspelled method name}/meson.build (100%) rename test cases/failing/{137 misspelled method name => 139 misspelled method name}/test.json (78%) rename test cases/failing/{137 ~ in meson.build => 140 ~ in meson.build}/meson.build (100%) rename test cases/failing/{137 ~ in meson.build => 140 ~ in meson.build}/test.json (55%) rename test cases/failing/{138 misspelled module method name => 141 misspelled module method name}/meson.build (100%) rename test cases/failing/{138 misspelled module method name => 141 misspelled module method name}/test.json (69%) rename test cases/failing/{138 ~ in meson.options => 142 ~ in meson.options}/meson.build (100%) rename test cases/failing/{138 ~ in meson.options => 142 ~ in meson.options}/meson.options (100%) rename test cases/failing/{138 ~ in meson.options => 142 ~ in meson.options}/test.json (56%) rename test cases/failing/{139 misspelled variable name => 143 misspelled variable name}/meson.build (100%) rename test cases/failing/{139 misspelled variable name => 143 misspelled variable name}/test.json (65%) rename test cases/failing/{139 ~ in meson.options value => 144 ~ in meson.options value}/meson.build (100%) rename test cases/failing/{139 ~ in meson.options value => 144 ~ in meson.options value}/meson.options (100%) rename test cases/failing/{139 ~ in meson.options value => 144 ~ in meson.options value}/test.json (58%) rename test cases/failing/{140 misspelled variable name get_variable => 145 misspelled variable name get_variable}/meson.build (100%) rename test cases/failing/{140 misspelled variable name get_variable => 145 misspelled variable name get_variable}/test.json (68%) rename test cases/failing/{141 misspelled variable name unset_variable => 146 misspelled variable name unset_variable}/meson.build (100%) rename test cases/failing/{141 misspelled variable name unset_variable => 146 misspelled variable name unset_variable}/test.json (68%) rename test cases/failing/{142 misspelled variable name fstring => 147 misspelled variable name fstring}/meson.build (100%) rename test cases/failing/{142 misspelled variable name fstring => 147 misspelled variable name fstring}/test.json (68%) rename test cases/failing/{143 misspelled variable name subproject.get_variable => 148 misspelled variable name subproject.get_variable}/meson.build (100%) rename test cases/failing/{143 misspelled variable name subproject.get_variable => 148 misspelled variable name subproject.get_variable}/subprojects/x/meson.build (100%) rename test cases/failing/{143 misspelled variable name subproject.get_variable => 148 misspelled variable name subproject.get_variable}/test.json (69%) rename test cases/failing/{144 misspelled module name => 149 misspelled module name}/meson.build (100%) rename test cases/failing/{144 misspelled module name => 149 misspelled module name}/test.json (66%) rename test cases/failing/{145 misspelled subproject name => 150 misspelled subproject name}/meson.build (100%) rename test cases/failing/{145 misspelled subproject name => 150 misspelled subproject name}/subprojects/help/meson.build (100%) rename test cases/failing/{145 misspelled subproject name => 150 misspelled subproject name}/test.json (71%) rename test cases/unit/{99 custom target name => 100 custom target name}/file.txt.in (100%) rename test cases/unit/{99 custom target name => 100 custom target name}/meson.build (100%) rename test cases/unit/{99 custom target name => 100 custom target name}/subdir/meson.build (100%) rename test cases/unit/{100 relative find program => 101 relative find program}/foo.py (100%) rename test cases/unit/{100 relative find program => 101 relative find program}/meson.build (100%) rename test cases/unit/{100 relative find program => 101 relative find program}/subdir/meson.build (100%) rename test cases/unit/{101 rlib linkage => 102 rlib linkage}/lib2.rs (100%) rename test cases/unit/{101 rlib linkage => 102 rlib linkage}/main.rs (100%) rename test cases/unit/{101 rlib linkage => 102 rlib linkage}/meson.build (100%) rename test cases/unit/{102 python without pkgconfig => 103 python without pkgconfig}/meson.build (100%) rename test cases/unit/{103 strip => 104 strip}/lib.c (100%) rename test cases/unit/{103 strip => 104 strip}/meson.build (100%) rename test cases/unit/{104 debug function => 105 debug function}/meson.build (100%) rename test cases/unit/{105 pkgconfig relocatable with absolute path => 106 pkgconfig relocatable with absolute path}/meson.build (100%) rename test cases/unit/{106 underspecified mtest => 107 underspecified mtest}/main.c (100%) rename test cases/unit/{106 underspecified mtest => 107 underspecified mtest}/meson.build (100%) rename test cases/unit/{106 underspecified mtest => 107 underspecified mtest}/runner.py (100%) rename test cases/unit/{107 subproject symlink => 108 subproject symlink}/cp.py (100%) rename test cases/unit/{107 subproject symlink => 108 subproject symlink}/main.c (100%) rename test cases/unit/{107 subproject symlink => 108 subproject symlink}/meson.build (100%) rename test cases/unit/{107 subproject symlink => 108 subproject symlink}/symlinked_subproject/datadir/datafile (100%) rename test cases/unit/{107 subproject symlink => 108 subproject symlink}/symlinked_subproject/datadir/meson.build (100%) rename test cases/unit/{107 subproject symlink => 108 subproject symlink}/symlinked_subproject/meson.build (100%) rename test cases/unit/{107 subproject symlink => 108 subproject symlink}/symlinked_subproject/src.c (100%) rename test cases/unit/{108 new subproject on reconfigure => 109 new subproject on reconfigure}/meson.build (100%) rename test cases/unit/{108 new subproject on reconfigure => 109 new subproject on reconfigure}/meson_options.txt (100%) rename test cases/unit/{108 new subproject on reconfigure => 109 new subproject on reconfigure}/subprojects/foo/foo.c (100%) rename test cases/unit/{108 new subproject on reconfigure => 109 new subproject on reconfigure}/subprojects/foo/meson.build (100%) rename test cases/unit/{109 configure same noop => 110 configure same noop}/meson.build (100%) rename test cases/unit/{109 configure same noop => 110 configure same noop}/meson_options.txt (100%) rename test cases/unit/{110 freeze => 111 freeze}/freeze.c (100%) rename test cases/unit/{110 freeze => 111 freeze}/meson.build (100%) rename test cases/unit/{111 replace unencodable xml chars => 112 replace unencodable xml chars}/meson.build (100%) rename test cases/unit/{111 replace unencodable xml chars => 112 replace unencodable xml chars}/script.py (100%) rename test cases/unit/{112 classpath => 113 classpath}/com/mesonbuild/Simple.java (100%) rename test cases/unit/{112 classpath => 113 classpath}/meson.build (100%) rename test cases/unit/{113 list build options => 114 list build options}/meson.build (100%) rename test cases/unit/{113 list build options => 114 list build options}/meson_options.txt (100%) rename test cases/unit/{114 complex link cases => 115 complex link cases}/main.c (100%) rename test cases/unit/{114 complex link cases => 115 complex link cases}/meson.build (100%) rename test cases/unit/{114 complex link cases => 115 complex link cases}/s1.c (100%) rename test cases/unit/{114 complex link cases => 115 complex link cases}/s2.c (100%) rename test cases/unit/{114 complex link cases => 115 complex link cases}/s3.c (100%) rename test cases/unit/{115 c cpp stds => 116 c cpp stds}/meson.build (100%) rename test cases/unit/{115 c cpp stds => 116 c cpp stds}/meson.options (100%) rename test cases/unit/{116 empty project => 117 empty project}/expected_mods.json (100%) rename test cases/unit/{116 empty project => 117 empty project}/meson.build (100%) rename test cases/unit/{117 genvslite => 118 genvslite}/main.cpp (100%) rename test cases/unit/{117 genvslite => 118 genvslite}/meson.build (100%) rename test cases/unit/{118 meson package cache dir => 119 meson package cache dir}/cache_dir/bar/meson.build (100%) rename test cases/unit/{118 meson package cache dir => 119 meson package cache dir}/cache_dir/foo.zip (100%) rename test cases/unit/{118 meson package cache dir => 119 meson package cache dir}/meson.build (100%) rename test cases/unit/{118 meson package cache dir => 119 meson package cache dir}/subprojects/bar.wrap (100%) rename test cases/unit/{118 meson package cache dir => 119 meson package cache dir}/subprojects/foo.wrap (100%) rename test cases/unit/{119 openssl cmake bug => 120 openssl cmake bug}/meson.build (100%) rename test cases/unit/{119 openssl cmake bug => 120 openssl cmake bug}/nativefile.ini (100%) rename test cases/unit/{120 rewrite => 121 rewrite}/meson.build (100%) rename test cases/unit/{121 executable suffix => 122 executable suffix}/main.c (100%) rename test cases/unit/{121 executable suffix => 122 executable suffix}/meson.build (100%) rename test cases/unit/{122 persp options => 123 persp options}/meson.build (100%) rename test cases/unit/{122 persp options => 123 persp options}/meson.options (100%) rename test cases/unit/{122 persp options => 123 persp options}/subprojects/sub1/meson.build (100%) rename test cases/unit/{122 persp options => 123 persp options}/subprojects/sub1/meson.options (100%) rename test cases/unit/{122 persp options => 123 persp options}/subprojects/sub1/sub1.c (100%) rename test cases/unit/{122 persp options => 123 persp options}/subprojects/sub2/meson.build (100%) rename test cases/unit/{122 persp options => 123 persp options}/subprojects/sub2/meson.options (100%) rename test cases/unit/{122 persp options => 123 persp options}/subprojects/sub2/sub2.c (100%) rename test cases/unit/{122 persp options => 123 persp options}/toplevel.c (100%) rename test cases/unit/{123 reconfigure base options => 124 reconfigure base options}/meson.build (100%) rename test cases/unit/{123 reconfigure base options => 124 reconfigure base options}/subprojects/sub/meson.build (100%) rename test cases/unit/{124 interactive tap => 125 interactive tap}/meson.build (100%) rename test cases/unit/{124 interactive tap => 125 interactive tap}/script.py (100%) rename test cases/unit/{125 declare_dep var => 126 declare_dep var}/meson.build (100%) rename test cases/unit/{125 declare_dep var => 126 declare_dep var}/meson_options.txt (100%) rename test cases/unit/{126 python extension => 127 python extension}/foo.c (100%) rename test cases/unit/{126 python extension => 127 python extension}/meson.build (100%) rename test cases/unit/{127 pkgsubproj => 128 pkgsubproj}/meson.build (100%) rename test cases/unit/{127 pkgsubproj => 128 pkgsubproj}/subprojects/sub/meson.build (100%) rename test cases/unit/{128 test slice => 129 test slice}/meson.build (100%) rename test cases/unit/{128 test slice => 129 test slice}/test.py (100%) rename test cases/unit/{129 sanitizers => 130 sanitizers}/meson.build (100%) rename test cases/unit/{130 long opt vs D => 131 long opt vs D}/meson.build (100%) rename test cases/unit/{130 long opt vs D => 131 long opt vs D}/meson_options.txt (100%) rename test cases/unit/{131 vala internal glib => 132 vala internal glib}/lib.vala (100%) rename test cases/unit/{131 vala internal glib => 132 vala internal glib}/meson.build (100%) rename test cases/unit/{131 vala internal glib => 132 vala internal glib}/meson.options (100%) rename test cases/unit/{132 custom target index test => 133 custom target index test}/meson.build (100%) rename test cases/unit/{133 nasm language only => 134 nasm language only}/meson.build (100%) rename test cases/unit/{134 promote link_language => 135 promote link_language}/consumer.c (100%) rename test cases/unit/{134 promote link_language => 135 promote link_language}/header.h (100%) rename test cases/unit/{134 promote link_language => 135 promote link_language}/lib.cpp (100%) rename test cases/unit/{134 promote link_language => 135 promote link_language}/meson.build (100%) rename test cases/unit/{135 minimal bindgen => 136 minimal bindgen}/header.h (100%) rename test cases/unit/{135 minimal bindgen => 136 minimal bindgen}/meson.build (100%) rename test cases/unit/{135 minimal bindgen => 136 minimal bindgen}/meson.options (100%) rename test cases/unit/{136 native subproject introspect => 137 native subproject introspect}/meson.build (100%) rename test cases/unit/{136 native subproject introspect => 137 native subproject introspect}/subprojects/recursive-both/lib.c (100%) rename test cases/unit/{136 native subproject introspect => 137 native subproject introspect}/subprojects/recursive-both/lib.def (100%) rename test cases/unit/{136 native subproject introspect => 137 native subproject introspect}/subprojects/recursive-both/meson.build (100%) rename test cases/unit/{136 native subproject introspect => 137 native subproject introspect}/subprojects/recursive-both/recursive-both.h.in (100%) rename test cases/unit/{137 external property nonexisting => 138 external property nonexisting}/meson.build (100%) rename test cases/unit/{137 external property nonexisting => 138 external property nonexisting}/meson_options.txt (100%) rename test cases/unit/{137 external property nonexisting => 138 external property nonexisting}/propfile (100%) rename test cases/unit/{91 install skip subprojects => 92 install skip subprojects}/foo.c (100%) rename test cases/unit/{91 install skip subprojects => 92 install skip subprojects}/foo.dat (100%) rename test cases/unit/{91 install skip subprojects => 92 install skip subprojects}/foo.h (100%) rename test cases/unit/{91 install skip subprojects => 92 install skip subprojects}/foo/foofile (100%) rename test cases/unit/{91 install skip subprojects => 92 install skip subprojects}/meson.build (100%) rename test cases/unit/{91 install skip subprojects => 92 install skip subprojects}/subprojects/bar/bar.c (100%) rename test cases/unit/{91 install skip subprojects => 92 install skip subprojects}/subprojects/bar/bar.dat (100%) rename test cases/unit/{91 install skip subprojects => 92 install skip subprojects}/subprojects/bar/bar.h (100%) rename test cases/unit/{91 install skip subprojects => 92 install skip subprojects}/subprojects/bar/bar/barfile (100%) rename test cases/unit/{91 install skip subprojects => 92 install skip subprojects}/subprojects/bar/meson.build (100%) rename test cases/unit/{92 new subproject in configured project => 93 new subproject in configured project}/meson.build (100%) rename test cases/unit/{92 new subproject in configured project => 93 new subproject in configured project}/meson_options.txt (100%) rename test cases/unit/{92 new subproject in configured project => 93 new subproject in configured project}/subprojects/sub/foo.c (100%) rename test cases/unit/{92 new subproject in configured project => 93 new subproject in configured project}/subprojects/sub/meson.build (100%) rename test cases/unit/{93 clangformat => 94 clangformat}/.clang-format (100%) rename test cases/unit/{93 clangformat => 94 clangformat}/.clang-format-ignore (100%) rename test cases/unit/{93 clangformat => 94 clangformat}/.clang-format-include (100%) rename test cases/unit/{93 clangformat => 94 clangformat}/meson.build (100%) rename test cases/unit/{93 clangformat => 94 clangformat}/not-included/badformat.cpp (100%) rename test cases/unit/{93 clangformat => 94 clangformat}/src/badformat.c (100%) rename test cases/unit/{93 clangformat => 94 clangformat}/src/badformat.cpp (100%) rename test cases/unit/{94 custominc => 95 custominc}/easytogrepfor/genh.py (100%) rename test cases/unit/{94 custominc => 95 custominc}/easytogrepfor/meson.build (100%) rename test cases/unit/{94 custominc => 95 custominc}/helper.c (100%) rename test cases/unit/{94 custominc => 95 custominc}/meson.build (100%) rename test cases/unit/{94 custominc => 95 custominc}/prog.c (100%) rename test cases/unit/{94 custominc => 95 custominc}/prog2.c (100%) rename test cases/unit/{95 implicit force fallback => 96 implicit force fallback}/meson.build (100%) rename test cases/unit/{95 implicit force fallback => 96 implicit force fallback}/subprojects/something/meson.build (100%) rename test cases/unit/{96 compiler.links file arg => 97 compiler.links file arg}/meson.build (100%) rename test cases/unit/{96 compiler.links file arg => 97 compiler.links file arg}/test.c (100%) rename test cases/unit/{97 link full name => 98 link full name}/.gitignore (100%) rename test cases/unit/{97 link full name => 98 link full name}/libtestprovider/meson.build (100%) rename test cases/unit/{97 link full name => 98 link full name}/libtestprovider/provider.c (100%) rename test cases/unit/{97 link full name => 98 link full name}/proguser/meson.build (100%) rename test cases/unit/{97 link full name => 98 link full name}/proguser/receiver.c (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/bar-custom.txt (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/bar-devel.h (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/bar-notag.txt (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/custom_files/data.txt (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/excludes/excluded.txt (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/excludes/excluded/placeholder.txt (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/excludes/installed.txt (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/foo.in (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/foo1-devel.h (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/lib.c (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/main.c (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/meson.build (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/script.py (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/subdir/bar2-devel.h (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/subdir/foo2.in (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/subdir/foo3-devel.h (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/subdir/lib.c (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/subdir/main.c (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/subdir/meson.build (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/subdir/script.py (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/subprojects/subproject/aaa.txt (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/subprojects/subproject/bbb.txt (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/subprojects/subproject/meson.build (100%) diff --git a/run_format_tests.py b/run_format_tests.py index 30c975882d9b..dee787b332ae 100755 --- a/run_format_tests.py +++ b/run_format_tests.py @@ -51,7 +51,7 @@ def check_format() -> None: 'work area', '.eggs', '_cache', # e.g. .mypy_cache 'venv', # virtualenvs have DOS line endings - '120 rewrite', # we explicitly test for tab in meson.build file + '121 rewrite', # we explicitly test for tab in meson.build file '3 editorconfig', } for (root, _, filenames) in os.walk('.'): diff --git a/test cases/common/286 pkgconfig subproject/meson.build b/test cases/common/287 pkgconfig subproject/meson.build similarity index 100% rename from test cases/common/286 pkgconfig subproject/meson.build rename to test cases/common/287 pkgconfig subproject/meson.build diff --git a/test cases/common/286 pkgconfig subproject/simple.c b/test cases/common/287 pkgconfig subproject/simple.c similarity index 100% rename from test cases/common/286 pkgconfig subproject/simple.c rename to test cases/common/287 pkgconfig subproject/simple.c diff --git a/test cases/common/286 pkgconfig subproject/simple.h b/test cases/common/287 pkgconfig subproject/simple.h similarity index 100% rename from test cases/common/286 pkgconfig subproject/simple.h rename to test cases/common/287 pkgconfig subproject/simple.h diff --git a/test cases/common/286 pkgconfig subproject/subprojects/simple2/exports.def b/test cases/common/287 pkgconfig subproject/subprojects/simple2/exports.def similarity index 100% rename from test cases/common/286 pkgconfig subproject/subprojects/simple2/exports.def rename to test cases/common/287 pkgconfig subproject/subprojects/simple2/exports.def diff --git a/test cases/common/286 pkgconfig subproject/subprojects/simple2/meson.build b/test cases/common/287 pkgconfig subproject/subprojects/simple2/meson.build similarity index 100% rename from test cases/common/286 pkgconfig subproject/subprojects/simple2/meson.build rename to test cases/common/287 pkgconfig subproject/subprojects/simple2/meson.build diff --git a/test cases/common/286 pkgconfig subproject/subprojects/simple2/simple2.c b/test cases/common/287 pkgconfig subproject/subprojects/simple2/simple2.c similarity index 100% rename from test cases/common/286 pkgconfig subproject/subprojects/simple2/simple2.c rename to test cases/common/287 pkgconfig subproject/subprojects/simple2/simple2.c diff --git a/test cases/common/286 pkgconfig subproject/subprojects/simple2/simple2.h b/test cases/common/287 pkgconfig subproject/subprojects/simple2/simple2.h similarity index 100% rename from test cases/common/286 pkgconfig subproject/subprojects/simple2/simple2.h rename to test cases/common/287 pkgconfig subproject/subprojects/simple2/simple2.h diff --git a/test cases/common/286 pkgconfig subproject/test.json b/test cases/common/287 pkgconfig subproject/test.json similarity index 100% rename from test cases/common/286 pkgconfig subproject/test.json rename to test cases/common/287 pkgconfig subproject/test.json diff --git a/test cases/common/287 atomic/a.c b/test cases/common/288 atomic/a.c similarity index 100% rename from test cases/common/287 atomic/a.c rename to test cases/common/288 atomic/a.c diff --git a/test cases/common/287 atomic/meson.build b/test cases/common/288 atomic/meson.build similarity index 100% rename from test cases/common/287 atomic/meson.build rename to test cases/common/288 atomic/meson.build diff --git a/test cases/common/288 importstd/meson.build b/test cases/common/289 importstd/meson.build similarity index 100% rename from test cases/common/288 importstd/meson.build rename to test cases/common/289 importstd/meson.build diff --git a/test cases/common/288 importstd/useistd.cpp b/test cases/common/289 importstd/useistd.cpp similarity index 100% rename from test cases/common/288 importstd/useistd.cpp rename to test cases/common/289 importstd/useistd.cpp diff --git a/test cases/common/289 invalid dependency arguments/lib.c b/test cases/common/290 invalid dependency arguments/lib.c similarity index 100% rename from test cases/common/289 invalid dependency arguments/lib.c rename to test cases/common/290 invalid dependency arguments/lib.c diff --git a/test cases/common/289 invalid dependency arguments/main.c b/test cases/common/290 invalid dependency arguments/main.c similarity index 100% rename from test cases/common/289 invalid dependency arguments/main.c rename to test cases/common/290 invalid dependency arguments/main.c diff --git a/test cases/common/289 invalid dependency arguments/meson.build b/test cases/common/290 invalid dependency arguments/meson.build similarity index 100% rename from test cases/common/289 invalid dependency arguments/meson.build rename to test cases/common/290 invalid dependency arguments/meson.build diff --git a/test cases/common/289 invalid dependency arguments/subprojects/sub/meson.build b/test cases/common/290 invalid dependency arguments/subprojects/sub/meson.build similarity index 100% rename from test cases/common/289 invalid dependency arguments/subprojects/sub/meson.build rename to test cases/common/290 invalid dependency arguments/subprojects/sub/meson.build diff --git a/test cases/common/290 multiple provides/meson.build b/test cases/common/291 multiple provides/meson.build similarity index 100% rename from test cases/common/290 multiple provides/meson.build rename to test cases/common/291 multiple provides/meson.build diff --git a/test cases/common/290 multiple provides/subprojects/libfoobar/meson.build b/test cases/common/291 multiple provides/subprojects/libfoobar/meson.build similarity index 100% rename from test cases/common/290 multiple provides/subprojects/libfoobar/meson.build rename to test cases/common/291 multiple provides/subprojects/libfoobar/meson.build diff --git a/test cases/common/290 multiple provides/subprojects/subproject/meson.build b/test cases/common/291 multiple provides/subprojects/subproject/meson.build similarity index 100% rename from test cases/common/290 multiple provides/subprojects/subproject/meson.build rename to test cases/common/291 multiple provides/subprojects/subproject/meson.build diff --git a/test cases/common/290 multiple provides/subprojects/subproject/subprojects/test-subproject.wrap b/test cases/common/291 multiple provides/subprojects/subproject/subprojects/test-subproject.wrap similarity index 100% rename from test cases/common/290 multiple provides/subprojects/subproject/subprojects/test-subproject.wrap rename to test cases/common/291 multiple provides/subprojects/subproject/subprojects/test-subproject.wrap diff --git a/test cases/common/290 multiple provides/subprojects/test.wrap b/test cases/common/291 multiple provides/subprojects/test.wrap similarity index 100% rename from test cases/common/290 multiple provides/subprojects/test.wrap rename to test cases/common/291 multiple provides/subprojects/test.wrap diff --git a/test cases/common/291 subproject with foreign lang/intlib.cpp b/test cases/common/292 subproject with foreign lang/intlib.cpp similarity index 100% rename from test cases/common/291 subproject with foreign lang/intlib.cpp rename to test cases/common/292 subproject with foreign lang/intlib.cpp diff --git a/test cases/common/291 subproject with foreign lang/meson.build b/test cases/common/292 subproject with foreign lang/meson.build similarity index 100% rename from test cases/common/291 subproject with foreign lang/meson.build rename to test cases/common/292 subproject with foreign lang/meson.build diff --git a/test cases/common/291 subproject with foreign lang/p2test.cpp b/test cases/common/292 subproject with foreign lang/p2test.cpp similarity index 100% rename from test cases/common/291 subproject with foreign lang/p2test.cpp rename to test cases/common/292 subproject with foreign lang/p2test.cpp diff --git a/test cases/common/291 subproject with foreign lang/subprojects/pcre2/meson.build b/test cases/common/292 subproject with foreign lang/subprojects/pcre2/meson.build similarity index 100% rename from test cases/common/291 subproject with foreign lang/subprojects/pcre2/meson.build rename to test cases/common/292 subproject with foreign lang/subprojects/pcre2/meson.build diff --git a/test cases/common/291 subproject with foreign lang/subprojects/pcre2/pcre.c b/test cases/common/292 subproject with foreign lang/subprojects/pcre2/pcre.c similarity index 100% rename from test cases/common/291 subproject with foreign lang/subprojects/pcre2/pcre.c rename to test cases/common/292 subproject with foreign lang/subprojects/pcre2/pcre.c diff --git a/test cases/common/291 subproject with foreign lang/test.json b/test cases/common/292 subproject with foreign lang/test.json similarity index 100% rename from test cases/common/291 subproject with foreign lang/test.json rename to test cases/common/292 subproject with foreign lang/test.json diff --git a/test cases/common/292 link early args/cpplib.cpp b/test cases/common/293 link early args/cpplib.cpp similarity index 100% rename from test cases/common/292 link early args/cpplib.cpp rename to test cases/common/293 link early args/cpplib.cpp diff --git a/test cases/common/292 link early args/cpplib.h b/test cases/common/293 link early args/cpplib.h similarity index 100% rename from test cases/common/292 link early args/cpplib.h rename to test cases/common/293 link early args/cpplib.h diff --git a/test cases/common/292 link early args/cppmain.cpp b/test cases/common/293 link early args/cppmain.cpp similarity index 100% rename from test cases/common/292 link early args/cppmain.cpp rename to test cases/common/293 link early args/cppmain.cpp diff --git a/test cases/common/292 link early args/libfile.c b/test cases/common/293 link early args/libfile.c similarity index 100% rename from test cases/common/292 link early args/libfile.c rename to test cases/common/293 link early args/libfile.c diff --git a/test cases/common/292 link early args/main.c b/test cases/common/293 link early args/main.c similarity index 100% rename from test cases/common/292 link early args/main.c rename to test cases/common/293 link early args/main.c diff --git a/test cases/common/292 link early args/meson.build b/test cases/common/293 link early args/meson.build similarity index 100% rename from test cases/common/292 link early args/meson.build rename to test cases/common/293 link early args/meson.build diff --git a/test cases/common/292 link early args/test.json b/test cases/common/293 link early args/test.json similarity index 100% rename from test cases/common/292 link early args/test.json rename to test cases/common/293 link early args/test.json diff --git a/test cases/common/293 systemd regression/keyctl.c b/test cases/common/294 systemd regression/keyctl.c similarity index 100% rename from test cases/common/293 systemd regression/keyctl.c rename to test cases/common/294 systemd regression/keyctl.c diff --git a/test cases/common/293 systemd regression/meson.build b/test cases/common/294 systemd regression/meson.build similarity index 100% rename from test cases/common/293 systemd regression/meson.build rename to test cases/common/294 systemd regression/meson.build diff --git a/test cases/common/293 systemd regression/override/linux/keyctl.h b/test cases/common/294 systemd regression/override/linux/keyctl.h similarity index 100% rename from test cases/common/293 systemd regression/override/linux/keyctl.h rename to test cases/common/294 systemd regression/override/linux/keyctl.h diff --git a/test cases/common/293 systemd regression/override/sys/keyctl.h b/test cases/common/294 systemd regression/override/sys/keyctl.h similarity index 100% rename from test cases/common/293 systemd regression/override/sys/keyctl.h rename to test cases/common/294 systemd regression/override/sys/keyctl.h diff --git a/test cases/common/293 systemd regression/test.json b/test cases/common/294 systemd regression/test.json similarity index 100% rename from test cases/common/293 systemd regression/test.json rename to test cases/common/294 systemd regression/test.json diff --git a/test cases/common/293 systemd regression/uapi/linux/keyctl.h b/test cases/common/294 systemd regression/uapi/linux/keyctl.h similarity index 100% rename from test cases/common/293 systemd regression/uapi/linux/keyctl.h rename to test cases/common/294 systemd regression/uapi/linux/keyctl.h diff --git a/test cases/common/294 source set if_false deps/a.c b/test cases/common/295 source set if_false deps/a.c similarity index 100% rename from test cases/common/294 source set if_false deps/a.c rename to test cases/common/295 source set if_false deps/a.c diff --git a/test cases/common/294 source set if_false deps/all.h b/test cases/common/295 source set if_false deps/all.h similarity index 100% rename from test cases/common/294 source set if_false deps/all.h rename to test cases/common/295 source set if_false deps/all.h diff --git a/test cases/common/294 source set if_false deps/b.c b/test cases/common/295 source set if_false deps/b.c similarity index 100% rename from test cases/common/294 source set if_false deps/b.c rename to test cases/common/295 source set if_false deps/b.c diff --git a/test cases/common/294 source set if_false deps/main.c b/test cases/common/295 source set if_false deps/main.c similarity index 100% rename from test cases/common/294 source set if_false deps/main.c rename to test cases/common/295 source set if_false deps/main.c diff --git a/test cases/common/294 source set if_false deps/meson.build b/test cases/common/295 source set if_false deps/meson.build similarity index 100% rename from test cases/common/294 source set if_false deps/meson.build rename to test cases/common/295 source set if_false deps/meson.build diff --git a/test cases/common/295 depends in generator.process/generate_config.py b/test cases/common/296 depends in generator.process/generate_config.py similarity index 100% rename from test cases/common/295 depends in generator.process/generate_config.py rename to test cases/common/296 depends in generator.process/generate_config.py diff --git a/test cases/common/295 depends in generator.process/generate_main.py b/test cases/common/296 depends in generator.process/generate_main.py similarity index 100% rename from test cases/common/295 depends in generator.process/generate_main.py rename to test cases/common/296 depends in generator.process/generate_main.py diff --git a/test cases/common/295 depends in generator.process/main.config.ini b/test cases/common/296 depends in generator.process/main.config.ini similarity index 100% rename from test cases/common/295 depends in generator.process/main.config.ini rename to test cases/common/296 depends in generator.process/main.config.ini diff --git a/test cases/common/295 depends in generator.process/main.template b/test cases/common/296 depends in generator.process/main.template similarity index 100% rename from test cases/common/295 depends in generator.process/main.template rename to test cases/common/296 depends in generator.process/main.template diff --git a/test cases/common/295 depends in generator.process/meson.build b/test cases/common/296 depends in generator.process/meson.build similarity index 100% rename from test cases/common/295 depends in generator.process/meson.build rename to test cases/common/296 depends in generator.process/meson.build diff --git a/test cases/common/295 redundant version check/meson.build b/test cases/common/297 redundant version check/meson.build similarity index 100% rename from test cases/common/295 redundant version check/meson.build rename to test cases/common/297 redundant version check/meson.build diff --git a/test cases/common/295 redundant version check/test.json b/test cases/common/297 redundant version check/test.json similarity index 61% rename from test cases/common/295 redundant version check/test.json rename to test cases/common/297 redundant version check/test.json index 2d95f693b3c1..7182623fffc8 100644 --- a/test cases/common/295 redundant version check/test.json +++ b/test cases/common/297 redundant version check/test.json @@ -1,19 +1,19 @@ { "stdout": [ { - "line": "test cases/common/295 redundant version check/meson.build:3: WARNING: Conditional on version '>= 0.55.0' always evaluates to true" + "line": "test cases/common/297 redundant version check/meson.build:3: WARNING: Conditional on version '>= 0.55.0' always evaluates to true" }, { - "line": "test cases/common/295 redundant version check/meson.build:6: WARNING: Conditional on version '< 0.60.0' always evaluates to false" + "line": "test cases/common/297 redundant version check/meson.build:6: WARNING: Conditional on version '< 0.60.0' always evaluates to false" }, { - "line": "test cases/common/295 redundant version check/meson.build:11: WARNING: Conditional on version '>= 0.40' always evaluates to true" + "line": "test cases/common/297 redundant version check/meson.build:11: WARNING: Conditional on version '>= 0.40' always evaluates to true" }, { - "line": "test cases/common/295 redundant version check/meson.build:26: WARNING: Conditional on version '>= 0.40, < 99.0' always evaluates to true" + "line": "test cases/common/297 redundant version check/meson.build:26: WARNING: Conditional on version '>= 0.40, < 99.0' always evaluates to true" }, { - "line": "test cases/common/295 redundant version check/meson.build:29: WARNING: Conditional on version '>= 0.40, < 99.0' always evaluates to true" + "line": "test cases/common/297 redundant version check/meson.build:29: WARNING: Conditional on version '>= 0.40, < 99.0' always evaluates to true" } ] } diff --git a/test cases/common/296 pch link_language/f.cc b/test cases/common/298 pch link_language/f.cc similarity index 100% rename from test cases/common/296 pch link_language/f.cc rename to test cases/common/298 pch link_language/f.cc diff --git a/test cases/common/296 pch link_language/f.h b/test cases/common/298 pch link_language/f.h similarity index 100% rename from test cases/common/296 pch link_language/f.h rename to test cases/common/298 pch link_language/f.h diff --git a/test cases/common/296 pch link_language/meson.build b/test cases/common/298 pch link_language/meson.build similarity index 100% rename from test cases/common/296 pch link_language/meson.build rename to test cases/common/298 pch link_language/meson.build diff --git a/test cases/common/296 pch link_language/pch/f_pch.h b/test cases/common/298 pch link_language/pch/f_pch.h similarity index 100% rename from test cases/common/296 pch link_language/pch/f_pch.h rename to test cases/common/298 pch link_language/pch/f_pch.h diff --git a/test cases/common/296 program in kwargs/customtarget.py b/test cases/common/299 program in kwargs/customtarget.py similarity index 100% rename from test cases/common/296 program in kwargs/customtarget.py rename to test cases/common/299 program in kwargs/customtarget.py diff --git a/test cases/common/296 program in kwargs/foo.py b/test cases/common/299 program in kwargs/foo.py similarity index 100% rename from test cases/common/296 program in kwargs/foo.py rename to test cases/common/299 program in kwargs/foo.py diff --git a/test cases/common/296 program in kwargs/meson.build b/test cases/common/299 program in kwargs/meson.build similarity index 100% rename from test cases/common/296 program in kwargs/meson.build rename to test cases/common/299 program in kwargs/meson.build diff --git a/test cases/common/296 program in kwargs/subprojects/sub/meson.build b/test cases/common/299 program in kwargs/subprojects/sub/meson.build similarity index 100% rename from test cases/common/296 program in kwargs/subprojects/sub/meson.build rename to test cases/common/299 program in kwargs/subprojects/sub/meson.build diff --git a/test cases/common/296 program in kwargs/subprojects/sub/trivial.c b/test cases/common/299 program in kwargs/subprojects/sub/trivial.c similarity index 100% rename from test cases/common/296 program in kwargs/subprojects/sub/trivial.c rename to test cases/common/299 program in kwargs/subprojects/sub/trivial.c diff --git a/test cases/common/296 program in kwargs/test.py b/test cases/common/299 program in kwargs/test.py similarity index 100% rename from test cases/common/296 program in kwargs/test.py rename to test cases/common/299 program in kwargs/test.py diff --git a/test cases/common/299 built file by path/main.c b/test cases/common/300 built file by path/main.c similarity index 100% rename from test cases/common/299 built file by path/main.c rename to test cases/common/300 built file by path/main.c diff --git a/test cases/common/299 built file by path/meson.build b/test cases/common/300 built file by path/meson.build similarity index 100% rename from test cases/common/299 built file by path/meson.build rename to test cases/common/300 built file by path/meson.build diff --git a/test cases/common/299 built file by path/test.json b/test cases/common/300 built file by path/test.json similarity index 76% rename from test cases/common/299 built file by path/test.json rename to test cases/common/300 built file by path/test.json index 8fa2627bda13..e8483d8f2392 100644 --- a/test cases/common/299 built file by path/test.json +++ b/test cases/common/300 built file by path/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/common/299 built file by path/meson.build:12: WARNING: '.*/fullpath.c' is a generated file, and should be passed by reference instead of string name. This will become a hard error in Meson 2.0", + "line": "test cases/common/300 built file by path/meson.build:12: WARNING: '.*/fullpath.c' is a generated file, and should be passed by reference instead of string name. This will become a hard error in Meson 2.0", "match": "re" } ] diff --git a/test cases/common/300 default/meson.build b/test cases/common/301 default/meson.build similarity index 100% rename from test cases/common/300 default/meson.build rename to test cases/common/301 default/meson.build diff --git a/test cases/common/300 default/prog.cpp b/test cases/common/301 default/prog.cpp similarity index 100% rename from test cases/common/300 default/prog.cpp rename to test cases/common/301 default/prog.cpp diff --git a/test cases/failing/104 enter subdir twice/meson.build b/test cases/failing/103 enter subdir twice/meson.build similarity index 100% rename from test cases/failing/104 enter subdir twice/meson.build rename to test cases/failing/103 enter subdir twice/meson.build diff --git a/test cases/failing/104 enter subdir twice/sub/meson.build b/test cases/failing/103 enter subdir twice/sub/meson.build similarity index 100% rename from test cases/failing/104 enter subdir twice/sub/meson.build rename to test cases/failing/103 enter subdir twice/sub/meson.build diff --git a/test cases/failing/104 enter subdir twice/test.json b/test cases/failing/103 enter subdir twice/test.json similarity index 64% rename from test cases/failing/104 enter subdir twice/test.json rename to test cases/failing/103 enter subdir twice/test.json index 9d638899949a..8c4da6d68dff 100644 --- a/test cases/failing/104 enter subdir twice/test.json +++ b/test cases/failing/103 enter subdir twice/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/104 enter subdir twice/meson.build:3:0: ERROR: Tried to enter directory \"sub\", which has already been visited." + "line": "test cases/failing/103 enter subdir twice/meson.build:3:0: ERROR: Tried to enter directory \"sub\", which has already been visited." } ] } diff --git a/test cases/failing/105 invalid fstring/109 invalid fstring/meson.build b/test cases/failing/104 invalid fstring/109 invalid fstring/meson.build similarity index 100% rename from test cases/failing/105 invalid fstring/109 invalid fstring/meson.build rename to test cases/failing/104 invalid fstring/109 invalid fstring/meson.build diff --git a/test cases/failing/105 invalid fstring/109 invalid fstring/test.json b/test cases/failing/104 invalid fstring/109 invalid fstring/test.json similarity index 100% rename from test cases/failing/105 invalid fstring/109 invalid fstring/test.json rename to test cases/failing/104 invalid fstring/109 invalid fstring/test.json diff --git a/test cases/failing/105 invalid fstring/meson.build b/test cases/failing/104 invalid fstring/meson.build similarity index 100% rename from test cases/failing/105 invalid fstring/meson.build rename to test cases/failing/104 invalid fstring/meson.build diff --git a/test cases/failing/105 invalid fstring/test.json b/test cases/failing/104 invalid fstring/test.json similarity index 63% rename from test cases/failing/105 invalid fstring/test.json rename to test cases/failing/104 invalid fstring/test.json index ae2a53196c58..58e008cf8744 100644 --- a/test cases/failing/105 invalid fstring/test.json +++ b/test cases/failing/104 invalid fstring/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/105 invalid fstring/meson.build:3:4: ERROR: Identifier \"foo\" does not name a variable." + "line": "test cases/failing/104 invalid fstring/meson.build:3:4: ERROR: Identifier \"foo\" does not name a variable." } ] } diff --git a/test cases/failing/106 compiler argument checking/meson.build b/test cases/failing/105 compiler argument checking/meson.build similarity index 100% rename from test cases/failing/106 compiler argument checking/meson.build rename to test cases/failing/105 compiler argument checking/meson.build diff --git a/test cases/failing/106 compiler argument checking/test.json b/test cases/failing/105 compiler argument checking/test.json similarity index 66% rename from test cases/failing/106 compiler argument checking/test.json rename to test cases/failing/105 compiler argument checking/test.json index 262db69f3156..dcf02cde47c3 100644 --- a/test cases/failing/106 compiler argument checking/test.json +++ b/test cases/failing/105 compiler argument checking/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/106 compiler argument checking/meson.build:4:25: ERROR: Compiler for C does not support \"-meson-goober-arg-for-testing\"" + "line": "test cases/failing/105 compiler argument checking/meson.build:4:25: ERROR: Compiler for C does not support \"-meson-goober-arg-for-testing\"" } ] } diff --git a/test cases/failing/107 empty fallback/meson.build b/test cases/failing/106 empty fallback/meson.build similarity index 100% rename from test cases/failing/107 empty fallback/meson.build rename to test cases/failing/106 empty fallback/meson.build diff --git a/test cases/failing/107 empty fallback/subprojects/foo/meson.build b/test cases/failing/106 empty fallback/subprojects/foo/meson.build similarity index 100% rename from test cases/failing/107 empty fallback/subprojects/foo/meson.build rename to test cases/failing/106 empty fallback/subprojects/foo/meson.build diff --git a/test cases/failing/107 empty fallback/test.json b/test cases/failing/106 empty fallback/test.json similarity index 65% rename from test cases/failing/107 empty fallback/test.json rename to test cases/failing/106 empty fallback/test.json index 3d69a58f951c..5f5230def899 100644 --- a/test cases/failing/107 empty fallback/test.json +++ b/test cases/failing/106 empty fallback/test.json @@ -2,7 +2,7 @@ "stdout": [ { "match": "re", - "line": "test cases/failing/107 empty fallback/meson.build:6:0: ERROR: Dependency \"foo\" not found.*" + "line": "test cases/failing/106 empty fallback/meson.build:6:0: ERROR: Dependency \"foo\" not found.*" } ] } diff --git a/test cases/failing/108 cmake executable dependency/meson.build b/test cases/failing/107 cmake executable dependency/meson.build similarity index 100% rename from test cases/failing/108 cmake executable dependency/meson.build rename to test cases/failing/107 cmake executable dependency/meson.build diff --git a/test cases/failing/108 cmake executable dependency/subprojects/cmlib/CMakeLists.txt b/test cases/failing/107 cmake executable dependency/subprojects/cmlib/CMakeLists.txt similarity index 100% rename from test cases/failing/108 cmake executable dependency/subprojects/cmlib/CMakeLists.txt rename to test cases/failing/107 cmake executable dependency/subprojects/cmlib/CMakeLists.txt diff --git a/test cases/failing/108 cmake executable dependency/subprojects/cmlib/main.c b/test cases/failing/107 cmake executable dependency/subprojects/cmlib/main.c similarity index 100% rename from test cases/failing/108 cmake executable dependency/subprojects/cmlib/main.c rename to test cases/failing/107 cmake executable dependency/subprojects/cmlib/main.c diff --git a/test cases/failing/108 cmake executable dependency/test.json b/test cases/failing/107 cmake executable dependency/test.json similarity index 77% rename from test cases/failing/108 cmake executable dependency/test.json rename to test cases/failing/107 cmake executable dependency/test.json index 5a88b0dac130..47c14a50a740 100644 --- a/test cases/failing/108 cmake executable dependency/test.json +++ b/test cases/failing/107 cmake executable dependency/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/108 cmake executable dependency/meson.build:5:14: ERROR: main is an executable and does not support the dependency() method. Use target() instead." + "line": "test cases/failing/107 cmake executable dependency/meson.build:5:14: ERROR: main is an executable and does not support the dependency() method. Use target() instead." } ], "tools": { diff --git a/test cases/failing/109 allow_fallback with fallback/meson.build b/test cases/failing/108 allow_fallback with fallback/meson.build similarity index 100% rename from test cases/failing/109 allow_fallback with fallback/meson.build rename to test cases/failing/108 allow_fallback with fallback/meson.build diff --git a/test cases/failing/109 allow_fallback with fallback/test.json b/test cases/failing/108 allow_fallback with fallback/test.json similarity index 70% rename from test cases/failing/109 allow_fallback with fallback/test.json rename to test cases/failing/108 allow_fallback with fallback/test.json index 76917b1ab091..de0aabea511f 100644 --- a/test cases/failing/109 allow_fallback with fallback/test.json +++ b/test cases/failing/108 allow_fallback with fallback/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/109 allow_fallback with fallback/meson.build:3:0: ERROR: \"fallback\" and \"allow_fallback\" arguments are mutually exclusive" + "line": "test cases/failing/108 allow_fallback with fallback/meson.build:3:0: ERROR: \"fallback\" and \"allow_fallback\" arguments are mutually exclusive" } ] } diff --git a/test cases/failing/110 nonsensical bindgen/meson.build b/test cases/failing/109 nonsensical bindgen/meson.build similarity index 100% rename from test cases/failing/110 nonsensical bindgen/meson.build rename to test cases/failing/109 nonsensical bindgen/meson.build diff --git a/test cases/failing/110 nonsensical bindgen/src/header.h b/test cases/failing/109 nonsensical bindgen/src/header.h similarity index 100% rename from test cases/failing/110 nonsensical bindgen/src/header.h rename to test cases/failing/109 nonsensical bindgen/src/header.h diff --git a/test cases/failing/110 nonsensical bindgen/src/source.c b/test cases/failing/109 nonsensical bindgen/src/source.c similarity index 100% rename from test cases/failing/110 nonsensical bindgen/src/source.c rename to test cases/failing/109 nonsensical bindgen/src/source.c diff --git a/test cases/failing/110 nonsensical bindgen/test.json b/test cases/failing/109 nonsensical bindgen/test.json similarity index 69% rename from test cases/failing/110 nonsensical bindgen/test.json rename to test cases/failing/109 nonsensical bindgen/test.json index 43cfa3a9b9fe..490f6fbf8bb0 100644 --- a/test cases/failing/110 nonsensical bindgen/test.json +++ b/test cases/failing/109 nonsensical bindgen/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/110 nonsensical bindgen/meson.build:17:24: ERROR: bindgen source file must be a C header, not an object or build target" + "line": "test cases/failing/109 nonsensical bindgen/meson.build:17:24: ERROR: bindgen source file must be a C header, not an object or build target" } ] } diff --git a/test cases/failing/111 run_target in test/meson.build b/test cases/failing/110 run_target in test/meson.build similarity index 100% rename from test cases/failing/111 run_target in test/meson.build rename to test cases/failing/110 run_target in test/meson.build diff --git a/test cases/failing/111 run_target in test/test.json b/test cases/failing/110 run_target in test/test.json similarity index 78% rename from test cases/failing/111 run_target in test/test.json rename to test cases/failing/110 run_target in test/test.json index 278b168ae4e3..f4bf44672a7b 100644 --- a/test cases/failing/111 run_target in test/test.json +++ b/test cases/failing/110 run_target in test/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/111 run_target in test/meson.build:7:0: ERROR: test keyword argument 'args' was of type array[RunTarget] but should have been array[str | File | BuildTarget | CustomTarget | CustomTargetIndex | Program]" + "line": "test cases/failing/110 run_target in test/meson.build:7:0: ERROR: test keyword argument 'args' was of type array[RunTarget] but should have been array[str | File | BuildTarget | CustomTarget | CustomTargetIndex | Program]" } ] } diff --git a/test cases/failing/111 run_target in test/trivial.c b/test cases/failing/110 run_target in test/trivial.c similarity index 100% rename from test cases/failing/111 run_target in test/trivial.c rename to test cases/failing/110 run_target in test/trivial.c diff --git a/test cases/failing/112 run_target in add_install_script/meson.build b/test cases/failing/111 run_target in add_install_script/meson.build similarity index 100% rename from test cases/failing/112 run_target in add_install_script/meson.build rename to test cases/failing/111 run_target in add_install_script/meson.build diff --git a/test cases/failing/112 run_target in add_install_script/test.json b/test cases/failing/111 run_target in add_install_script/test.json similarity index 80% rename from test cases/failing/112 run_target in add_install_script/test.json rename to test cases/failing/111 run_target in add_install_script/test.json index 22908659e4c0..2816f338acd4 100644 --- a/test cases/failing/112 run_target in add_install_script/test.json +++ b/test cases/failing/111 run_target in add_install_script/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/112 run_target in add_install_script/meson.build:7:6: ERROR: meson.add_install_script argument 2 was of type \"RunTarget\" but should have been one of: \"str\", \"File\", \"BuildTarget\", \"CustomTarget\", \"CustomTargetIndex\", \"Program\"" + "line": "test cases/failing/111 run_target in add_install_script/meson.build:7:6: ERROR: meson.add_install_script argument 2 was of type \"RunTarget\" but should have been one of: \"str\", \"File\", \"BuildTarget\", \"CustomTarget\", \"CustomTargetIndex\", \"Program\"" } ] } diff --git a/test cases/failing/112 run_target in add_install_script/trivial.c b/test cases/failing/111 run_target in add_install_script/trivial.c similarity index 100% rename from test cases/failing/112 run_target in add_install_script/trivial.c rename to test cases/failing/111 run_target in add_install_script/trivial.c diff --git a/test cases/failing/113 pathsep in install_symlink/meson.build b/test cases/failing/112 pathsep in install_symlink/meson.build similarity index 100% rename from test cases/failing/113 pathsep in install_symlink/meson.build rename to test cases/failing/112 pathsep in install_symlink/meson.build diff --git a/test cases/failing/113 pathsep in install_symlink/test.json b/test cases/failing/112 pathsep in install_symlink/test.json similarity index 74% rename from test cases/failing/113 pathsep in install_symlink/test.json rename to test cases/failing/112 pathsep in install_symlink/test.json index bbf25e266180..f12ed903c6ca 100644 --- a/test cases/failing/113 pathsep in install_symlink/test.json +++ b/test cases/failing/112 pathsep in install_symlink/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/113 pathsep in install_symlink/meson.build:3:0: ERROR: Link name is \"foo/bar\", but link names cannot contain path separators. The dir part should be in install_dir." + "line": "test cases/failing/112 pathsep in install_symlink/meson.build:3:0: ERROR: Link name is \"foo/bar\", but link names cannot contain path separators. The dir part should be in install_dir." } ] } diff --git a/test cases/failing/114 subproject version conflict/meson.build b/test cases/failing/113 subproject version conflict/meson.build similarity index 100% rename from test cases/failing/114 subproject version conflict/meson.build rename to test cases/failing/113 subproject version conflict/meson.build diff --git a/test cases/failing/114 subproject version conflict/subprojects/A/meson.build b/test cases/failing/113 subproject version conflict/subprojects/A/meson.build similarity index 100% rename from test cases/failing/114 subproject version conflict/subprojects/A/meson.build rename to test cases/failing/113 subproject version conflict/subprojects/A/meson.build diff --git a/test cases/failing/114 subproject version conflict/subprojects/B/meson.build b/test cases/failing/113 subproject version conflict/subprojects/B/meson.build similarity index 100% rename from test cases/failing/114 subproject version conflict/subprojects/B/meson.build rename to test cases/failing/113 subproject version conflict/subprojects/B/meson.build diff --git a/test cases/failing/114 subproject version conflict/test.json b/test cases/failing/113 subproject version conflict/test.json similarity index 66% rename from test cases/failing/114 subproject version conflict/test.json rename to test cases/failing/113 subproject version conflict/test.json index 654897292550..c0e7e2b4a8b2 100644 --- a/test cases/failing/114 subproject version conflict/test.json +++ b/test cases/failing/113 subproject version conflict/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/114 subproject version conflict/meson.build:4:8: ERROR: Subproject B version is 100 but ['1'] required." + "line": "test cases/failing/113 subproject version conflict/meson.build:4:8: ERROR: Subproject B version is 100 but ['1'] required." } ] } diff --git a/test cases/failing/115 structured source empty string/main.rs b/test cases/failing/114 structured source empty string/main.rs similarity index 100% rename from test cases/failing/115 structured source empty string/main.rs rename to test cases/failing/114 structured source empty string/main.rs diff --git a/test cases/failing/115 structured source empty string/meson.build b/test cases/failing/114 structured source empty string/meson.build similarity index 100% rename from test cases/failing/115 structured source empty string/meson.build rename to test cases/failing/114 structured source empty string/meson.build diff --git a/test cases/failing/115 structured source empty string/test.json b/test cases/failing/114 structured source empty string/test.json similarity index 71% rename from test cases/failing/115 structured source empty string/test.json rename to test cases/failing/114 structured source empty string/test.json index 48c1c83dcd7b..081294439348 100644 --- a/test cases/failing/115 structured source empty string/test.json +++ b/test cases/failing/114 structured source empty string/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/115 structured source empty string/meson.build:9:2: ERROR: structured_sources: keys to dictionary argument may not be an empty string." + "line": "test cases/failing/114 structured source empty string/meson.build:9:2: ERROR: structured_sources: keys to dictionary argument may not be an empty string." } ] } diff --git a/test cases/failing/116 structured_sources conflicts/main.rs b/test cases/failing/115 structured_sources conflicts/main.rs similarity index 100% rename from test cases/failing/116 structured_sources conflicts/main.rs rename to test cases/failing/115 structured_sources conflicts/main.rs diff --git a/test cases/failing/116 structured_sources conflicts/meson.build b/test cases/failing/115 structured_sources conflicts/meson.build similarity index 100% rename from test cases/failing/116 structured_sources conflicts/meson.build rename to test cases/failing/115 structured_sources conflicts/meson.build diff --git a/test cases/failing/116 structured_sources conflicts/test.json b/test cases/failing/115 structured_sources conflicts/test.json similarity index 67% rename from test cases/failing/116 structured_sources conflicts/test.json rename to test cases/failing/115 structured_sources conflicts/test.json index 7b539231ccf7..3a0a5efd00c2 100644 --- a/test cases/failing/116 structured_sources conflicts/test.json +++ b/test cases/failing/115 structured_sources conflicts/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/116 structured_sources conflicts/meson.build:7:0: ERROR: Conflicting sources in structured sources: main.rs" + "line": "test cases/failing/115 structured_sources conflicts/meson.build:7:0: ERROR: Conflicting sources in structured sources: main.rs" } ] } diff --git a/test cases/failing/117 missing compiler/meson.build b/test cases/failing/116 missing compiler/meson.build similarity index 100% rename from test cases/failing/117 missing compiler/meson.build rename to test cases/failing/116 missing compiler/meson.build diff --git a/test cases/failing/117 missing compiler/subprojects/sub/main.c b/test cases/failing/116 missing compiler/subprojects/sub/main.c similarity index 100% rename from test cases/failing/117 missing compiler/subprojects/sub/main.c rename to test cases/failing/116 missing compiler/subprojects/sub/main.c diff --git a/test cases/failing/117 missing compiler/subprojects/sub/meson.build b/test cases/failing/116 missing compiler/subprojects/sub/meson.build similarity index 100% rename from test cases/failing/117 missing compiler/subprojects/sub/meson.build rename to test cases/failing/116 missing compiler/subprojects/sub/meson.build diff --git a/test cases/failing/117 missing compiler/test.json b/test cases/failing/116 missing compiler/test.json similarity index 68% rename from test cases/failing/117 missing compiler/test.json rename to test cases/failing/116 missing compiler/test.json index 525e92706b7e..d4ebec797f56 100644 --- a/test cases/failing/117 missing compiler/test.json +++ b/test cases/failing/116 missing compiler/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/117 missing compiler/subprojects/sub/meson.build:4:0: ERROR: No host machine compiler for 'subprojects/sub/main.c'" + "line": "test cases/failing/116 missing compiler/subprojects/sub/meson.build:4:0: ERROR: No host machine compiler for 'subprojects/sub/main.c'" } ] } diff --git a/test cases/failing/118 cmake subproject error/meson.build b/test cases/failing/117 cmake subproject error/meson.build similarity index 100% rename from test cases/failing/118 cmake subproject error/meson.build rename to test cases/failing/117 cmake subproject error/meson.build diff --git a/test cases/failing/118 cmake subproject error/subprojects/cmlib/CMakeLists.txt b/test cases/failing/117 cmake subproject error/subprojects/cmlib/CMakeLists.txt similarity index 100% rename from test cases/failing/118 cmake subproject error/subprojects/cmlib/CMakeLists.txt rename to test cases/failing/117 cmake subproject error/subprojects/cmlib/CMakeLists.txt diff --git a/test cases/failing/118 cmake subproject error/test.json b/test cases/failing/117 cmake subproject error/test.json similarity index 74% rename from test cases/failing/118 cmake subproject error/test.json rename to test cases/failing/117 cmake subproject error/test.json index 4e47f896c96c..4ef9296480e4 100644 --- a/test cases/failing/118 cmake subproject error/test.json +++ b/test cases/failing/117 cmake subproject error/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/118 cmake subproject error/meson.build:4:14: ERROR: Failed to configure the CMake subproject: Fancy error message" + "line": "test cases/failing/117 cmake subproject error/meson.build:4:14: ERROR: Failed to configure the CMake subproject: Fancy error message" } ], "tools": { diff --git a/test cases/failing/119 pkgconfig not relocatable outside prefix/meson.build b/test cases/failing/118 pkgconfig not relocatable outside prefix/meson.build similarity index 100% rename from test cases/failing/119 pkgconfig not relocatable outside prefix/meson.build rename to test cases/failing/118 pkgconfig not relocatable outside prefix/meson.build diff --git a/test cases/failing/119 pkgconfig not relocatable outside prefix/test.json b/test cases/failing/118 pkgconfig not relocatable outside prefix/test.json similarity index 79% rename from test cases/failing/119 pkgconfig not relocatable outside prefix/test.json rename to test cases/failing/118 pkgconfig not relocatable outside prefix/test.json index 24c4fe4effbe..1d17d9f7bca9 100644 --- a/test cases/failing/119 pkgconfig not relocatable outside prefix/test.json +++ b/test cases/failing/118 pkgconfig not relocatable outside prefix/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/119 pkgconfig not relocatable outside prefix/meson\\.build:17:5: ERROR: Pkgconfig prefix cannot be outside of the prefix when pkgconfig\\.relocatable=true. Pkgconfig prefix is (C:)?[\\\\/]opt[\\\\/]lib[\\\\/]pkgconfig.", + "line": "test cases/failing/118 pkgconfig not relocatable outside prefix/meson\\.build:17:5: ERROR: Pkgconfig prefix cannot be outside of the prefix when pkgconfig\\.relocatable=true. Pkgconfig prefix is (C:)?[\\\\/]opt[\\\\/]lib[\\\\/]pkgconfig.", "match": "re" } ] diff --git a/test cases/failing/120 subproject sandbox violation/meson.build b/test cases/failing/119 subproject sandbox violation/meson.build similarity index 100% rename from test cases/failing/120 subproject sandbox violation/meson.build rename to test cases/failing/119 subproject sandbox violation/meson.build diff --git a/test cases/failing/120 subproject sandbox violation/meson_options.txt b/test cases/failing/119 subproject sandbox violation/meson_options.txt similarity index 100% rename from test cases/failing/120 subproject sandbox violation/meson_options.txt rename to test cases/failing/119 subproject sandbox violation/meson_options.txt diff --git a/test cases/failing/120 subproject sandbox violation/subprojects/subproj1/file.txt b/test cases/failing/119 subproject sandbox violation/subprojects/subproj1/file.txt similarity index 100% rename from test cases/failing/120 subproject sandbox violation/subprojects/subproj1/file.txt rename to test cases/failing/119 subproject sandbox violation/subprojects/subproj1/file.txt diff --git a/test cases/failing/120 subproject sandbox violation/subprojects/subproj1/meson.build b/test cases/failing/119 subproject sandbox violation/subprojects/subproj1/meson.build similarity index 100% rename from test cases/failing/120 subproject sandbox violation/subprojects/subproj1/meson.build rename to test cases/failing/119 subproject sandbox violation/subprojects/subproj1/meson.build diff --git a/test cases/failing/120 subproject sandbox violation/subprojects/subproj1/nested/meson.build b/test cases/failing/119 subproject sandbox violation/subprojects/subproj1/nested/meson.build similarity index 100% rename from test cases/failing/120 subproject sandbox violation/subprojects/subproj1/nested/meson.build rename to test cases/failing/119 subproject sandbox violation/subprojects/subproj1/nested/meson.build diff --git a/test cases/failing/120 subproject sandbox violation/subprojects/subproj2/file.txt b/test cases/failing/119 subproject sandbox violation/subprojects/subproj2/file.txt similarity index 100% rename from test cases/failing/120 subproject sandbox violation/subprojects/subproj2/file.txt rename to test cases/failing/119 subproject sandbox violation/subprojects/subproj2/file.txt diff --git a/test cases/failing/120 subproject sandbox violation/subprojects/subproj2/meson.build b/test cases/failing/119 subproject sandbox violation/subprojects/subproj2/meson.build similarity index 100% rename from test cases/failing/120 subproject sandbox violation/subprojects/subproj2/meson.build rename to test cases/failing/119 subproject sandbox violation/subprojects/subproj2/meson.build diff --git a/test cases/failing/120 subproject sandbox violation/subprojects/subproj2/nested/meson.build b/test cases/failing/119 subproject sandbox violation/subprojects/subproj2/nested/meson.build similarity index 100% rename from test cases/failing/120 subproject sandbox violation/subprojects/subproj2/nested/meson.build rename to test cases/failing/119 subproject sandbox violation/subprojects/subproj2/nested/meson.build diff --git a/test cases/failing/120 subproject sandbox violation/subprojects/subproj3/file.txt b/test cases/failing/119 subproject sandbox violation/subprojects/subproj3/file.txt similarity index 100% rename from test cases/failing/120 subproject sandbox violation/subprojects/subproj3/file.txt rename to test cases/failing/119 subproject sandbox violation/subprojects/subproj3/file.txt diff --git a/test cases/failing/120 subproject sandbox violation/subprojects/subproj3/meson.build b/test cases/failing/119 subproject sandbox violation/subprojects/subproj3/meson.build similarity index 100% rename from test cases/failing/120 subproject sandbox violation/subprojects/subproj3/meson.build rename to test cases/failing/119 subproject sandbox violation/subprojects/subproj3/meson.build diff --git a/test cases/failing/120 subproject sandbox violation/test.json b/test cases/failing/119 subproject sandbox violation/test.json similarity index 82% rename from test cases/failing/120 subproject sandbox violation/test.json rename to test cases/failing/119 subproject sandbox violation/test.json index d506076089b8..d4a18a4d5cd2 100644 --- a/test cases/failing/120 subproject sandbox violation/test.json +++ b/test cases/failing/119 subproject sandbox violation/test.json @@ -10,7 +10,7 @@ }, "stdout": [ { - "line": "test cases/failing/120 subproject sandbox violation/meson.build:24:0: ERROR: Sandbox violation: Tried to grab file file.txt from a nested subproject." + "line": "test cases/failing/119 subproject sandbox violation/meson.build:24:0: ERROR: Sandbox violation: Tried to grab file file.txt from a nested subproject." } ] } diff --git a/test cases/failing/121 override and add_project_dependency/inc/lib.h b/test cases/failing/120 override and add_project_dependency/inc/lib.h similarity index 100% rename from test cases/failing/121 override and add_project_dependency/inc/lib.h rename to test cases/failing/120 override and add_project_dependency/inc/lib.h diff --git a/test cases/failing/121 override and add_project_dependency/lib.c b/test cases/failing/120 override and add_project_dependency/lib.c similarity index 100% rename from test cases/failing/121 override and add_project_dependency/lib.c rename to test cases/failing/120 override and add_project_dependency/lib.c diff --git a/test cases/failing/121 override and add_project_dependency/meson.build b/test cases/failing/120 override and add_project_dependency/meson.build similarity index 100% rename from test cases/failing/121 override and add_project_dependency/meson.build rename to test cases/failing/120 override and add_project_dependency/meson.build diff --git a/test cases/failing/121 override and add_project_dependency/subprojects/a/meson.build b/test cases/failing/120 override and add_project_dependency/subprojects/a/meson.build similarity index 100% rename from test cases/failing/121 override and add_project_dependency/subprojects/a/meson.build rename to test cases/failing/120 override and add_project_dependency/subprojects/a/meson.build diff --git a/test cases/failing/121 override and add_project_dependency/subprojects/a/prog.c b/test cases/failing/120 override and add_project_dependency/subprojects/a/prog.c similarity index 100% rename from test cases/failing/121 override and add_project_dependency/subprojects/a/prog.c rename to test cases/failing/120 override and add_project_dependency/subprojects/a/prog.c diff --git a/test cases/failing/121 override and add_project_dependency/test.json b/test cases/failing/120 override and add_project_dependency/test.json similarity index 66% rename from test cases/failing/121 override and add_project_dependency/test.json rename to test cases/failing/120 override and add_project_dependency/test.json index e762359f7785..1f9b4d25580c 100644 --- a/test cases/failing/121 override and add_project_dependency/test.json +++ b/test cases/failing/120 override and add_project_dependency/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/121 override and add_project_dependency/subprojects/a/meson.build:6:0: ERROR: Dependencies must be external dependencies" + "line": "test cases/failing/120 override and add_project_dependency/subprojects/a/meson.build:6:0: ERROR: Dependencies must be external dependencies" } ] } diff --git a/test cases/failing/122 targets before add_project_dependency/inc/lib.h b/test cases/failing/121 targets before add_project_dependency/inc/lib.h similarity index 100% rename from test cases/failing/122 targets before add_project_dependency/inc/lib.h rename to test cases/failing/121 targets before add_project_dependency/inc/lib.h diff --git a/test cases/failing/122 targets before add_project_dependency/lib.c b/test cases/failing/121 targets before add_project_dependency/lib.c similarity index 100% rename from test cases/failing/122 targets before add_project_dependency/lib.c rename to test cases/failing/121 targets before add_project_dependency/lib.c diff --git a/test cases/failing/122 targets before add_project_dependency/meson.build b/test cases/failing/121 targets before add_project_dependency/meson.build similarity index 100% rename from test cases/failing/122 targets before add_project_dependency/meson.build rename to test cases/failing/121 targets before add_project_dependency/meson.build diff --git a/test cases/failing/122 targets before add_project_dependency/test.json b/test cases/failing/121 targets before add_project_dependency/test.json similarity index 70% rename from test cases/failing/122 targets before add_project_dependency/test.json rename to test cases/failing/121 targets before add_project_dependency/test.json index 674467a107c9..af2e32981a39 100644 --- a/test cases/failing/122 targets before add_project_dependency/test.json +++ b/test cases/failing/121 targets before add_project_dependency/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/122 targets before add_project_dependency/meson.build:5:0: ERROR: Tried to use 'add_project_dependencies' after a build target has been declared." + "line": "test cases/failing/121 targets before add_project_dependency/meson.build:5:0: ERROR: Tried to use 'add_project_dependencies' after a build target has been declared." } ] } diff --git a/test cases/failing/123 extract from unity/meson.build b/test cases/failing/122 extract from unity/meson.build similarity index 100% rename from test cases/failing/123 extract from unity/meson.build rename to test cases/failing/122 extract from unity/meson.build diff --git a/test cases/failing/123 extract from unity/src1.c b/test cases/failing/122 extract from unity/src1.c similarity index 100% rename from test cases/failing/123 extract from unity/src1.c rename to test cases/failing/122 extract from unity/src1.c diff --git a/test cases/failing/123 extract from unity/src2.c b/test cases/failing/122 extract from unity/src2.c similarity index 100% rename from test cases/failing/123 extract from unity/src2.c rename to test cases/failing/122 extract from unity/src2.c diff --git a/test cases/failing/123 extract from unity/test.json b/test cases/failing/122 extract from unity/test.json similarity index 73% rename from test cases/failing/123 extract from unity/test.json rename to test cases/failing/122 extract from unity/test.json index ce9f1053ca97..b130fa680660 100644 --- a/test cases/failing/123 extract from unity/test.json +++ b/test cases/failing/122 extract from unity/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/123 extract from unity/meson.build:4:37: ERROR: Single object files cannot be extracted in Unity builds. You can only extract all the object files for each compiler at once." + "line": "test cases/failing/122 extract from unity/meson.build:4:37: ERROR: Single object files cannot be extracted in Unity builds. You can only extract all the object files for each compiler at once." } ] } diff --git a/test cases/failing/124 generator host binary/exe.c b/test cases/failing/123 generator host binary/exe.c similarity index 100% rename from test cases/failing/124 generator host binary/exe.c rename to test cases/failing/123 generator host binary/exe.c diff --git a/test cases/failing/124 generator host binary/lib.in b/test cases/failing/123 generator host binary/lib.in similarity index 100% rename from test cases/failing/124 generator host binary/lib.in rename to test cases/failing/123 generator host binary/lib.in diff --git a/test cases/failing/124 generator host binary/meson.build b/test cases/failing/123 generator host binary/meson.build similarity index 100% rename from test cases/failing/124 generator host binary/meson.build rename to test cases/failing/123 generator host binary/meson.build diff --git a/test cases/failing/124 generator host binary/test.json b/test cases/failing/123 generator host binary/test.json similarity index 100% rename from test cases/failing/124 generator host binary/test.json rename to test cases/failing/123 generator host binary/test.json diff --git a/test cases/failing/125 invalid ast/meson.build b/test cases/failing/124 invalid ast/meson.build similarity index 100% rename from test cases/failing/125 invalid ast/meson.build rename to test cases/failing/124 invalid ast/meson.build diff --git a/test cases/failing/125 invalid ast/test.json b/test cases/failing/124 invalid ast/test.json similarity index 67% rename from test cases/failing/125 invalid ast/test.json rename to test cases/failing/124 invalid ast/test.json index 7b659c11a8f9..767496363e89 100644 --- a/test cases/failing/125 invalid ast/test.json +++ b/test cases/failing/124 invalid ast/test.json @@ -2,7 +2,7 @@ "stdout": [ { "match": "re", - "line": "test cases/failing/125 invalid ast/meson.build:1:44: ERROR: Meson version is [0-9.]+(\\.rc[0-9]+)? but project requires 0.1.0" + "line": "test cases/failing/124 invalid ast/meson.build:1:44: ERROR: Meson version is [0-9.]+(\\.rc[0-9]+)? but project requires 0.1.0" } ] } diff --git a/test cases/failing/126 invalid project function/meson.build b/test cases/failing/125 invalid project function/meson.build similarity index 100% rename from test cases/failing/126 invalid project function/meson.build rename to test cases/failing/125 invalid project function/meson.build diff --git a/test cases/failing/126 invalid project function/test.json b/test cases/failing/125 invalid project function/test.json similarity index 69% rename from test cases/failing/126 invalid project function/test.json rename to test cases/failing/125 invalid project function/test.json index e826185586d7..7ae01e6eeabe 100644 --- a/test cases/failing/126 invalid project function/test.json +++ b/test cases/failing/125 invalid project function/test.json @@ -2,7 +2,7 @@ "stdout": [ { "match": "re", - "line": "test cases/failing/126 invalid project function/meson.build:1:67: ERROR: Meson version is [0-9.]+(\\.rc[0-9]+)? but project requires 0.1.0" + "line": "test cases/failing/125 invalid project function/meson.build:1:67: ERROR: Meson version is [0-9.]+(\\.rc[0-9]+)? but project requires 0.1.0" } ] } diff --git a/test cases/failing/127 utf8 with bom/meson.build b/test cases/failing/126 utf8 with bom/meson.build similarity index 100% rename from test cases/failing/127 utf8 with bom/meson.build rename to test cases/failing/126 utf8 with bom/meson.build diff --git a/test cases/failing/127 utf8 with bom/test.json b/test cases/failing/126 utf8 with bom/test.json similarity index 63% rename from test cases/failing/127 utf8 with bom/test.json rename to test cases/failing/126 utf8 with bom/test.json index 686077d9790a..e6940fef044c 100644 --- a/test cases/failing/127 utf8 with bom/test.json +++ b/test cases/failing/126 utf8 with bom/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/127 utf8 with bom/meson.build:0:0: ERROR: Builder file must be encoded in UTF-8 (with no BOM)" + "line": "test cases/failing/126 utf8 with bom/meson.build:0:0: ERROR: Builder file must be encoded in UTF-8 (with no BOM)" } ] } diff --git a/test cases/failing/128 utf8 with bom subdir/meson.build b/test cases/failing/127 utf8 with bom subdir/meson.build similarity index 100% rename from test cases/failing/128 utf8 with bom subdir/meson.build rename to test cases/failing/127 utf8 with bom subdir/meson.build diff --git a/test cases/failing/128 utf8 with bom subdir/subdir/meson.build b/test cases/failing/127 utf8 with bom subdir/subdir/meson.build similarity index 100% rename from test cases/failing/128 utf8 with bom subdir/subdir/meson.build rename to test cases/failing/127 utf8 with bom subdir/subdir/meson.build diff --git a/test cases/failing/128 utf8 with bom subdir/test.json b/test cases/failing/127 utf8 with bom subdir/test.json similarity index 66% rename from test cases/failing/128 utf8 with bom subdir/test.json rename to test cases/failing/127 utf8 with bom subdir/test.json index f73143c8afc4..77c1a2035cef 100644 --- a/test cases/failing/128 utf8 with bom subdir/test.json +++ b/test cases/failing/127 utf8 with bom subdir/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/128 utf8 with bom subdir/subdir/meson.build:0:0: ERROR: Builder file must be encoded in UTF-8 (with no BOM)" + "line": "test cases/failing/127 utf8 with bom subdir/subdir/meson.build:0:0: ERROR: Builder file must be encoded in UTF-8 (with no BOM)" } ] } diff --git a/test cases/failing/129 utf8 with bom options/meson.build b/test cases/failing/128 utf8 with bom options/meson.build similarity index 100% rename from test cases/failing/129 utf8 with bom options/meson.build rename to test cases/failing/128 utf8 with bom options/meson.build diff --git a/test cases/failing/129 utf8 with bom options/meson.options b/test cases/failing/128 utf8 with bom options/meson.options similarity index 100% rename from test cases/failing/129 utf8 with bom options/meson.options rename to test cases/failing/128 utf8 with bom options/meson.options diff --git a/test cases/failing/129 utf8 with bom options/test.json b/test cases/failing/128 utf8 with bom options/test.json similarity index 65% rename from test cases/failing/129 utf8 with bom options/test.json rename to test cases/failing/128 utf8 with bom options/test.json index 9662b932c54c..f74e0e0384b0 100644 --- a/test cases/failing/129 utf8 with bom options/test.json +++ b/test cases/failing/128 utf8 with bom options/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/129 utf8 with bom options/meson.options:0:0: ERROR: Builder file must be encoded in UTF-8 (with no BOM)" + "line": "test cases/failing/128 utf8 with bom options/meson.options:0:0: ERROR: Builder file must be encoded in UTF-8 (with no BOM)" } ] } diff --git a/test cases/failing/130 module use inside project decl/meson.build b/test cases/failing/129 module use inside project decl/meson.build similarity index 100% rename from test cases/failing/130 module use inside project decl/meson.build rename to test cases/failing/129 module use inside project decl/meson.build diff --git a/test cases/failing/130 module use inside project decl/test.json b/test cases/failing/129 module use inside project decl/test.json similarity index 72% rename from test cases/failing/130 module use inside project decl/test.json rename to test cases/failing/129 module use inside project decl/test.json index b886c2d7e980..a7854627dfa0 100644 --- a/test cases/failing/130 module use inside project decl/test.json +++ b/test cases/failing/129 module use inside project decl/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/130 module use inside project decl/meson.build:4:21: ERROR: Module methods (python.find_installation) cannot be invoked during project declaration." + "line": "test cases/failing/129 module use inside project decl/meson.build:4:21: ERROR: Module methods (python.find_installation) cannot be invoked during project declaration." } ] } diff --git a/test cases/failing/131 dub missing dependency/dub.json b/test cases/failing/130 dub missing dependency/dub.json similarity index 100% rename from test cases/failing/131 dub missing dependency/dub.json rename to test cases/failing/130 dub missing dependency/dub.json diff --git a/test cases/failing/131 dub missing dependency/dub.selections.json b/test cases/failing/130 dub missing dependency/dub.selections.json similarity index 100% rename from test cases/failing/131 dub missing dependency/dub.selections.json rename to test cases/failing/130 dub missing dependency/dub.selections.json diff --git a/test cases/failing/131 dub missing dependency/meson.build b/test cases/failing/130 dub missing dependency/meson.build similarity index 100% rename from test cases/failing/131 dub missing dependency/meson.build rename to test cases/failing/130 dub missing dependency/meson.build diff --git a/test cases/failing/131 dub missing dependency/source/app.d b/test cases/failing/130 dub missing dependency/source/app.d similarity index 100% rename from test cases/failing/131 dub missing dependency/source/app.d rename to test cases/failing/130 dub missing dependency/source/app.d diff --git a/test cases/failing/131 dub missing dependency/test.json b/test cases/failing/130 dub missing dependency/test.json similarity index 64% rename from test cases/failing/131 dub missing dependency/test.json rename to test cases/failing/130 dub missing dependency/test.json index d0cf2aaf5f57..e67a5f6150c6 100644 --- a/test cases/failing/131 dub missing dependency/test.json +++ b/test cases/failing/130 dub missing dependency/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/131 dub missing dependency/meson.build:17:6: ERROR: Dependency \"urld\" not found", + "line": "test cases/failing/130 dub missing dependency/meson.build:17:6: ERROR: Dependency \"urld\" not found", "line": "dub add urld" } ] diff --git a/test cases/failing/132 java sources in non jar target/Test.java b/test cases/failing/131 java sources in non jar target/Test.java similarity index 100% rename from test cases/failing/132 java sources in non jar target/Test.java rename to test cases/failing/131 java sources in non jar target/Test.java diff --git a/test cases/failing/132 java sources in non jar target/meson.build b/test cases/failing/131 java sources in non jar target/meson.build similarity index 100% rename from test cases/failing/132 java sources in non jar target/meson.build rename to test cases/failing/131 java sources in non jar target/meson.build diff --git a/test cases/failing/132 java sources in non jar target/test.json b/test cases/failing/131 java sources in non jar target/test.json similarity index 71% rename from test cases/failing/132 java sources in non jar target/test.json rename to test cases/failing/131 java sources in non jar target/test.json index 17f63456b005..a201bb969b54 100644 --- a/test cases/failing/132 java sources in non jar target/test.json +++ b/test cases/failing/131 java sources in non jar target/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/132 java sources in non jar target/meson.build:3:0: ERROR: Build target of type \"executable\" cannot build java source: \"Test.java\". Use \"jar\" instead." + "line": "test cases/failing/131 java sources in non jar target/meson.build:3:0: ERROR: Build target of type \"executable\" cannot build java source: \"Test.java\". Use \"jar\" instead." } ] } diff --git a/test cases/failing/133 rust link_language/f.rs b/test cases/failing/132 rust link_language/f.rs similarity index 100% rename from test cases/failing/133 rust link_language/f.rs rename to test cases/failing/132 rust link_language/f.rs diff --git a/test cases/failing/133 rust link_language/meson.build b/test cases/failing/132 rust link_language/meson.build similarity index 100% rename from test cases/failing/133 rust link_language/meson.build rename to test cases/failing/132 rust link_language/meson.build diff --git a/test cases/failing/133 rust link_language/test.json b/test cases/failing/132 rust link_language/test.json similarity index 66% rename from test cases/failing/133 rust link_language/test.json rename to test cases/failing/132 rust link_language/test.json index a67c18f5397e..a3389cfb122e 100644 --- a/test cases/failing/133 rust link_language/test.json +++ b/test cases/failing/132 rust link_language/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/133 rust link_language/meson.build:10:0: ERROR: cannot build Rust sources with a different link_language" + "line": "test cases/failing/132 rust link_language/meson.build:10:0: ERROR: cannot build Rust sources with a different link_language" } ] } diff --git a/test cases/failing/134 invalid build_subdir/existing-dir/config.h.in b/test cases/failing/133 invalid build_subdir/existing-dir/config.h.in similarity index 100% rename from test cases/failing/134 invalid build_subdir/existing-dir/config.h.in rename to test cases/failing/133 invalid build_subdir/existing-dir/config.h.in diff --git a/test cases/failing/134 invalid build_subdir/meson.build b/test cases/failing/133 invalid build_subdir/meson.build similarity index 100% rename from test cases/failing/134 invalid build_subdir/meson.build rename to test cases/failing/133 invalid build_subdir/meson.build diff --git a/test cases/failing/134 invalid build_subdir/test.json b/test cases/failing/133 invalid build_subdir/test.json similarity index 65% rename from test cases/failing/134 invalid build_subdir/test.json rename to test cases/failing/133 invalid build_subdir/test.json index 9196872c2a4e..c62d9afeb206 100644 --- a/test cases/failing/134 invalid build_subdir/test.json +++ b/test cases/failing/133 invalid build_subdir/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/134 invalid build_subdir/meson.build:18:0: ERROR: Build subdir \"existing-dir\" in \"config.h\" exists in source tree." + "line": "test cases/failing/133 invalid build_subdir/meson.build:18:0: ERROR: Build subdir \"existing-dir\" in \"config.h\" exists in source tree." } ] } diff --git a/test cases/failing/135 misspelled method kwargs/meson.build b/test cases/failing/136 misspelled method kwargs/meson.build similarity index 100% rename from test cases/failing/135 misspelled method kwargs/meson.build rename to test cases/failing/136 misspelled method kwargs/meson.build diff --git a/test cases/failing/135 misspelled method kwargs/test.json b/test cases/failing/136 misspelled method kwargs/test.json similarity index 71% rename from test cases/failing/135 misspelled method kwargs/test.json rename to test cases/failing/136 misspelled method kwargs/test.json index 95b0616d1ba8..b1dd9dae5b6e 100644 --- a/test cases/failing/135 misspelled method kwargs/test.json +++ b/test cases/failing/136 misspelled method kwargs/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/135 misspelled method kwargs/meson.build:4:11: ERROR: python.extension_module got unknown keyword arguments \"deps\", \"srcs\" (did you mean \"sources\"?)" + "line": "test cases/failing/136 misspelled method kwargs/meson.build:4:11: ERROR: python.extension_module got unknown keyword arguments \"deps\", \"srcs\" (did you mean \"sources\"?)" } ] } diff --git a/test cases/failing/136 cython using implicit c explicitly/foo.c b/test cases/failing/137 cython using implicit c explicitly/foo.c similarity index 100% rename from test cases/failing/136 cython using implicit c explicitly/foo.c rename to test cases/failing/137 cython using implicit c explicitly/foo.c diff --git a/test cases/failing/136 cython using implicit c explicitly/meson.build b/test cases/failing/137 cython using implicit c explicitly/meson.build similarity index 100% rename from test cases/failing/136 cython using implicit c explicitly/meson.build rename to test cases/failing/137 cython using implicit c explicitly/meson.build diff --git a/test cases/failing/136 cython using implicit c explicitly/test.json b/test cases/failing/137 cython using implicit c explicitly/test.json similarity index 62% rename from test cases/failing/136 cython using implicit c explicitly/test.json rename to test cases/failing/137 cython using implicit c explicitly/test.json index 31b5b30d2f3c..fe135335062d 100644 --- a/test cases/failing/136 cython using implicit c explicitly/test.json +++ b/test cases/failing/137 cython using implicit c explicitly/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/136 cython using implicit c explicitly/meson.build:14:0: ERROR: No host machine compiler for 'foo.c'" + "line": "test cases/failing/137 cython using implicit c explicitly/meson.build:14:0: ERROR: No host machine compiler for 'foo.c'" } ] } diff --git a/test cases/failing/136 misspelled function name/meson.build b/test cases/failing/138 misspelled function name/meson.build similarity index 100% rename from test cases/failing/136 misspelled function name/meson.build rename to test cases/failing/138 misspelled function name/meson.build diff --git a/test cases/failing/136 misspelled function name/test.json b/test cases/failing/138 misspelled function name/test.json similarity index 65% rename from test cases/failing/136 misspelled function name/test.json rename to test cases/failing/138 misspelled function name/test.json index a48d28027c93..f924220a34c8 100644 --- a/test cases/failing/136 misspelled function name/test.json +++ b/test cases/failing/138 misspelled function name/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/136 misspelled function name/meson.build:2:19: ERROR: Unknown function \"executabl\". Did you mean \"executable\"?" + "line": "test cases/failing/138 misspelled function name/meson.build:2:19: ERROR: Unknown function \"executabl\". Did you mean \"executable\"?" } ] } diff --git a/test cases/failing/137 misspelled method name/meson.build b/test cases/failing/139 misspelled method name/meson.build similarity index 100% rename from test cases/failing/137 misspelled method name/meson.build rename to test cases/failing/139 misspelled method name/meson.build diff --git a/test cases/failing/137 misspelled method name/test.json b/test cases/failing/139 misspelled method name/test.json similarity index 78% rename from test cases/failing/137 misspelled method name/test.json rename to test cases/failing/139 misspelled method name/test.json index 9ce08763379a..bbd97b4b5c18 100644 --- a/test cases/failing/137 misspelled method name/test.json +++ b/test cases/failing/139 misspelled method name/test.json @@ -2,7 +2,7 @@ "stdout": [ { "match": "re", - "line": "test cases/failing/137 misspelled method name/meson.build:2:16: ERROR: Unknown method \"versoin\" in object of type MesonMain. Did you mean \"version\"?" + "line": "test cases/failing/139 misspelled method name/meson.build:2:16: ERROR: Unknown method \"versoin\" in object of type MesonMain. Did you mean \"version\"?" } ] } diff --git a/test cases/failing/137 ~ in meson.build/meson.build b/test cases/failing/140 ~ in meson.build/meson.build similarity index 100% rename from test cases/failing/137 ~ in meson.build/meson.build rename to test cases/failing/140 ~ in meson.build/meson.build diff --git a/test cases/failing/137 ~ in meson.build/test.json b/test cases/failing/140 ~ in meson.build/test.json similarity index 55% rename from test cases/failing/137 ~ in meson.build/test.json rename to test cases/failing/140 ~ in meson.build/test.json index 0cf2308ea3f9..d2ccb2ea9088 100644 --- a/test cases/failing/137 ~ in meson.build/test.json +++ b/test cases/failing/140 ~ in meson.build/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/137 ~ in meson.build/meson.build:3:7: ERROR: lexer: unrecognized token '~'" + "line": "test cases/failing/140 ~ in meson.build/meson.build:3:7: ERROR: lexer: unrecognized token '~'" } ] } diff --git a/test cases/failing/138 misspelled module method name/meson.build b/test cases/failing/141 misspelled module method name/meson.build similarity index 100% rename from test cases/failing/138 misspelled module method name/meson.build rename to test cases/failing/141 misspelled module method name/meson.build diff --git a/test cases/failing/138 misspelled module method name/test.json b/test cases/failing/141 misspelled module method name/test.json similarity index 69% rename from test cases/failing/138 misspelled module method name/test.json rename to test cases/failing/141 misspelled module method name/test.json index 35bfae2876c0..8fca8d25c7d0 100644 --- a/test cases/failing/138 misspelled module method name/test.json +++ b/test cases/failing/141 misspelled module method name/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/138 misspelled module method name/meson.build:3:10: ERROR: Unknown method \"find_installlation\" in object. Did you mean \"find_installation\"?" + "line": "test cases/failing/141 misspelled module method name/meson.build:3:10: ERROR: Unknown method \"find_installlation\" in object. Did you mean \"find_installation\"?" } ] } diff --git a/test cases/failing/138 ~ in meson.options/meson.build b/test cases/failing/142 ~ in meson.options/meson.build similarity index 100% rename from test cases/failing/138 ~ in meson.options/meson.build rename to test cases/failing/142 ~ in meson.options/meson.build diff --git a/test cases/failing/138 ~ in meson.options/meson.options b/test cases/failing/142 ~ in meson.options/meson.options similarity index 100% rename from test cases/failing/138 ~ in meson.options/meson.options rename to test cases/failing/142 ~ in meson.options/meson.options diff --git a/test cases/failing/138 ~ in meson.options/test.json b/test cases/failing/142 ~ in meson.options/test.json similarity index 56% rename from test cases/failing/138 ~ in meson.options/test.json rename to test cases/failing/142 ~ in meson.options/test.json index 53f447ac2b96..88f9098fa1fb 100644 --- a/test cases/failing/138 ~ in meson.options/test.json +++ b/test cases/failing/142 ~ in meson.options/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/138 ~ in meson.options/meson.options:1:7: ERROR: lexer: unrecognized token '~'" + "line": "test cases/failing/142 ~ in meson.options/meson.options:1:7: ERROR: lexer: unrecognized token '~'" } ] } diff --git a/test cases/failing/139 misspelled variable name/meson.build b/test cases/failing/143 misspelled variable name/meson.build similarity index 100% rename from test cases/failing/139 misspelled variable name/meson.build rename to test cases/failing/143 misspelled variable name/meson.build diff --git a/test cases/failing/139 misspelled variable name/test.json b/test cases/failing/143 misspelled variable name/test.json similarity index 65% rename from test cases/failing/139 misspelled variable name/test.json rename to test cases/failing/143 misspelled variable name/test.json index fa1335ba1728..f10bac4e9f2f 100644 --- a/test cases/failing/139 misspelled variable name/test.json +++ b/test cases/failing/143 misspelled variable name/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/139 misspelled variable name/meson.build:3:26: ERROR: Unknown variable name \"versoin\". Did you mean \"version\"?" + "line": "test cases/failing/143 misspelled variable name/meson.build:3:26: ERROR: Unknown variable name \"versoin\". Did you mean \"version\"?" } ] } diff --git a/test cases/failing/139 ~ in meson.options value/meson.build b/test cases/failing/144 ~ in meson.options value/meson.build similarity index 100% rename from test cases/failing/139 ~ in meson.options value/meson.build rename to test cases/failing/144 ~ in meson.options value/meson.build diff --git a/test cases/failing/139 ~ in meson.options value/meson.options b/test cases/failing/144 ~ in meson.options value/meson.options similarity index 100% rename from test cases/failing/139 ~ in meson.options value/meson.options rename to test cases/failing/144 ~ in meson.options value/meson.options diff --git a/test cases/failing/139 ~ in meson.options value/test.json b/test cases/failing/144 ~ in meson.options value/test.json similarity index 58% rename from test cases/failing/139 ~ in meson.options value/test.json rename to test cases/failing/144 ~ in meson.options value/test.json index 42341afc80a2..c19ae85043f3 100644 --- a/test cases/failing/139 ~ in meson.options value/test.json +++ b/test cases/failing/144 ~ in meson.options value/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/139 ~ in meson.options value/meson.options:1:40: ERROR: lexer: unrecognized token '~'" + "line": "test cases/failing/144 ~ in meson.options value/meson.options:1:40: ERROR: lexer: unrecognized token '~'" } ] } diff --git a/test cases/failing/140 misspelled variable name get_variable/meson.build b/test cases/failing/145 misspelled variable name get_variable/meson.build similarity index 100% rename from test cases/failing/140 misspelled variable name get_variable/meson.build rename to test cases/failing/145 misspelled variable name get_variable/meson.build diff --git a/test cases/failing/140 misspelled variable name get_variable/test.json b/test cases/failing/145 misspelled variable name get_variable/test.json similarity index 68% rename from test cases/failing/140 misspelled variable name get_variable/test.json rename to test cases/failing/145 misspelled variable name get_variable/test.json index ca80826b5b85..8d455fb9c932 100644 --- a/test cases/failing/140 misspelled variable name get_variable/test.json +++ b/test cases/failing/145 misspelled variable name get_variable/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/140 misspelled variable name get_variable/meson.build:3:9: ERROR: Tried to get unknown variable \"jello\". Did you mean \"hello\"?" + "line": "test cases/failing/145 misspelled variable name get_variable/meson.build:3:9: ERROR: Tried to get unknown variable \"jello\". Did you mean \"hello\"?" } ] } diff --git a/test cases/failing/141 misspelled variable name unset_variable/meson.build b/test cases/failing/146 misspelled variable name unset_variable/meson.build similarity index 100% rename from test cases/failing/141 misspelled variable name unset_variable/meson.build rename to test cases/failing/146 misspelled variable name unset_variable/meson.build diff --git a/test cases/failing/141 misspelled variable name unset_variable/test.json b/test cases/failing/146 misspelled variable name unset_variable/test.json similarity index 68% rename from test cases/failing/141 misspelled variable name unset_variable/test.json rename to test cases/failing/146 misspelled variable name unset_variable/test.json index 5590efe54978..eaf6f0ee8156 100644 --- a/test cases/failing/141 misspelled variable name unset_variable/test.json +++ b/test cases/failing/146 misspelled variable name unset_variable/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/141 misspelled variable name unset_variable/meson.build:3:0: ERROR: Tried to unset unknown variable \"jello\". Did you mean \"hello\"?" + "line": "test cases/failing/146 misspelled variable name unset_variable/meson.build:3:0: ERROR: Tried to unset unknown variable \"jello\". Did you mean \"hello\"?" } ] } diff --git a/test cases/failing/142 misspelled variable name fstring/meson.build b/test cases/failing/147 misspelled variable name fstring/meson.build similarity index 100% rename from test cases/failing/142 misspelled variable name fstring/meson.build rename to test cases/failing/147 misspelled variable name fstring/meson.build diff --git a/test cases/failing/142 misspelled variable name fstring/test.json b/test cases/failing/147 misspelled variable name fstring/test.json similarity index 68% rename from test cases/failing/142 misspelled variable name fstring/test.json rename to test cases/failing/147 misspelled variable name fstring/test.json index 89c008e3ddf8..30b53b64bbb3 100644 --- a/test cases/failing/142 misspelled variable name fstring/test.json +++ b/test cases/failing/147 misspelled variable name fstring/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/142 misspelled variable name fstring/meson.build:3:4: ERROR: Identifier \"jello\" does not name a variable. Did you mean \"hello\"?" + "line": "test cases/failing/147 misspelled variable name fstring/meson.build:3:4: ERROR: Identifier \"jello\" does not name a variable. Did you mean \"hello\"?" } ] } diff --git a/test cases/failing/143 misspelled variable name subproject.get_variable/meson.build b/test cases/failing/148 misspelled variable name subproject.get_variable/meson.build similarity index 100% rename from test cases/failing/143 misspelled variable name subproject.get_variable/meson.build rename to test cases/failing/148 misspelled variable name subproject.get_variable/meson.build diff --git a/test cases/failing/143 misspelled variable name subproject.get_variable/subprojects/x/meson.build b/test cases/failing/148 misspelled variable name subproject.get_variable/subprojects/x/meson.build similarity index 100% rename from test cases/failing/143 misspelled variable name subproject.get_variable/subprojects/x/meson.build rename to test cases/failing/148 misspelled variable name subproject.get_variable/subprojects/x/meson.build diff --git a/test cases/failing/143 misspelled variable name subproject.get_variable/test.json b/test cases/failing/148 misspelled variable name subproject.get_variable/test.json similarity index 69% rename from test cases/failing/143 misspelled variable name subproject.get_variable/test.json rename to test cases/failing/148 misspelled variable name subproject.get_variable/test.json index ce3f67372d31..8edc77f52cb9 100644 --- a/test cases/failing/143 misspelled variable name subproject.get_variable/test.json +++ b/test cases/failing/148 misspelled variable name subproject.get_variable/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/143 misspelled variable name subproject.get_variable/meson.build:3:10: ERROR: Requested variable \"hello\" not found. Did you mean \"jello\"?" + "line": "test cases/failing/148 misspelled variable name subproject.get_variable/meson.build:3:10: ERROR: Requested variable \"hello\" not found. Did you mean \"jello\"?" } ] } diff --git a/test cases/failing/144 misspelled module name/meson.build b/test cases/failing/149 misspelled module name/meson.build similarity index 100% rename from test cases/failing/144 misspelled module name/meson.build rename to test cases/failing/149 misspelled module name/meson.build diff --git a/test cases/failing/144 misspelled module name/test.json b/test cases/failing/149 misspelled module name/test.json similarity index 66% rename from test cases/failing/144 misspelled module name/test.json rename to test cases/failing/149 misspelled module name/test.json index 513ef1459a73..5d8d4223bcb7 100644 --- a/test cases/failing/144 misspelled module name/test.json +++ b/test cases/failing/149 misspelled module name/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/144 misspelled module name/meson.build:2:5: ERROR: Module \"pythin\" does not exist. Did you mean \"python\"?" + "line": "test cases/failing/149 misspelled module name/meson.build:2:5: ERROR: Module \"pythin\" does not exist. Did you mean \"python\"?" } ] } diff --git a/test cases/failing/145 misspelled subproject name/meson.build b/test cases/failing/150 misspelled subproject name/meson.build similarity index 100% rename from test cases/failing/145 misspelled subproject name/meson.build rename to test cases/failing/150 misspelled subproject name/meson.build diff --git a/test cases/failing/145 misspelled subproject name/subprojects/help/meson.build b/test cases/failing/150 misspelled subproject name/subprojects/help/meson.build similarity index 100% rename from test cases/failing/145 misspelled subproject name/subprojects/help/meson.build rename to test cases/failing/150 misspelled subproject name/subprojects/help/meson.build diff --git a/test cases/failing/145 misspelled subproject name/test.json b/test cases/failing/150 misspelled subproject name/test.json similarity index 71% rename from test cases/failing/145 misspelled subproject name/test.json rename to test cases/failing/150 misspelled subproject name/test.json index b0ece7cbb213..a690898335c4 100644 --- a/test cases/failing/145 misspelled subproject name/test.json +++ b/test cases/failing/150 misspelled subproject name/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/145 misspelled subproject name/meson.build:2:6: ERROR: Neither a subproject directory nor a welp.wrap file was found. Did you mean \"help\"?" + "line": "test cases/failing/150 misspelled subproject name/meson.build:2:6: ERROR: Neither a subproject directory nor a welp.wrap file was found. Did you mean \"help\"?" } ] } diff --git a/test cases/unit/99 custom target name/file.txt.in b/test cases/unit/100 custom target name/file.txt.in similarity index 100% rename from test cases/unit/99 custom target name/file.txt.in rename to test cases/unit/100 custom target name/file.txt.in diff --git a/test cases/unit/99 custom target name/meson.build b/test cases/unit/100 custom target name/meson.build similarity index 100% rename from test cases/unit/99 custom target name/meson.build rename to test cases/unit/100 custom target name/meson.build diff --git a/test cases/unit/99 custom target name/subdir/meson.build b/test cases/unit/100 custom target name/subdir/meson.build similarity index 100% rename from test cases/unit/99 custom target name/subdir/meson.build rename to test cases/unit/100 custom target name/subdir/meson.build diff --git a/test cases/unit/100 relative find program/foo.py b/test cases/unit/101 relative find program/foo.py similarity index 100% rename from test cases/unit/100 relative find program/foo.py rename to test cases/unit/101 relative find program/foo.py diff --git a/test cases/unit/100 relative find program/meson.build b/test cases/unit/101 relative find program/meson.build similarity index 100% rename from test cases/unit/100 relative find program/meson.build rename to test cases/unit/101 relative find program/meson.build diff --git a/test cases/unit/100 relative find program/subdir/meson.build b/test cases/unit/101 relative find program/subdir/meson.build similarity index 100% rename from test cases/unit/100 relative find program/subdir/meson.build rename to test cases/unit/101 relative find program/subdir/meson.build diff --git a/test cases/unit/101 rlib linkage/lib2.rs b/test cases/unit/102 rlib linkage/lib2.rs similarity index 100% rename from test cases/unit/101 rlib linkage/lib2.rs rename to test cases/unit/102 rlib linkage/lib2.rs diff --git a/test cases/unit/101 rlib linkage/main.rs b/test cases/unit/102 rlib linkage/main.rs similarity index 100% rename from test cases/unit/101 rlib linkage/main.rs rename to test cases/unit/102 rlib linkage/main.rs diff --git a/test cases/unit/101 rlib linkage/meson.build b/test cases/unit/102 rlib linkage/meson.build similarity index 100% rename from test cases/unit/101 rlib linkage/meson.build rename to test cases/unit/102 rlib linkage/meson.build diff --git a/test cases/unit/102 python without pkgconfig/meson.build b/test cases/unit/103 python without pkgconfig/meson.build similarity index 100% rename from test cases/unit/102 python without pkgconfig/meson.build rename to test cases/unit/103 python without pkgconfig/meson.build diff --git a/test cases/unit/103 strip/lib.c b/test cases/unit/104 strip/lib.c similarity index 100% rename from test cases/unit/103 strip/lib.c rename to test cases/unit/104 strip/lib.c diff --git a/test cases/unit/103 strip/meson.build b/test cases/unit/104 strip/meson.build similarity index 100% rename from test cases/unit/103 strip/meson.build rename to test cases/unit/104 strip/meson.build diff --git a/test cases/unit/104 debug function/meson.build b/test cases/unit/105 debug function/meson.build similarity index 100% rename from test cases/unit/104 debug function/meson.build rename to test cases/unit/105 debug function/meson.build diff --git a/test cases/unit/105 pkgconfig relocatable with absolute path/meson.build b/test cases/unit/106 pkgconfig relocatable with absolute path/meson.build similarity index 100% rename from test cases/unit/105 pkgconfig relocatable with absolute path/meson.build rename to test cases/unit/106 pkgconfig relocatable with absolute path/meson.build diff --git a/test cases/unit/106 underspecified mtest/main.c b/test cases/unit/107 underspecified mtest/main.c similarity index 100% rename from test cases/unit/106 underspecified mtest/main.c rename to test cases/unit/107 underspecified mtest/main.c diff --git a/test cases/unit/106 underspecified mtest/meson.build b/test cases/unit/107 underspecified mtest/meson.build similarity index 100% rename from test cases/unit/106 underspecified mtest/meson.build rename to test cases/unit/107 underspecified mtest/meson.build diff --git a/test cases/unit/106 underspecified mtest/runner.py b/test cases/unit/107 underspecified mtest/runner.py similarity index 100% rename from test cases/unit/106 underspecified mtest/runner.py rename to test cases/unit/107 underspecified mtest/runner.py diff --git a/test cases/unit/107 subproject symlink/cp.py b/test cases/unit/108 subproject symlink/cp.py similarity index 100% rename from test cases/unit/107 subproject symlink/cp.py rename to test cases/unit/108 subproject symlink/cp.py diff --git a/test cases/unit/107 subproject symlink/main.c b/test cases/unit/108 subproject symlink/main.c similarity index 100% rename from test cases/unit/107 subproject symlink/main.c rename to test cases/unit/108 subproject symlink/main.c diff --git a/test cases/unit/107 subproject symlink/meson.build b/test cases/unit/108 subproject symlink/meson.build similarity index 100% rename from test cases/unit/107 subproject symlink/meson.build rename to test cases/unit/108 subproject symlink/meson.build diff --git a/test cases/unit/107 subproject symlink/symlinked_subproject/datadir/datafile b/test cases/unit/108 subproject symlink/symlinked_subproject/datadir/datafile similarity index 100% rename from test cases/unit/107 subproject symlink/symlinked_subproject/datadir/datafile rename to test cases/unit/108 subproject symlink/symlinked_subproject/datadir/datafile diff --git a/test cases/unit/107 subproject symlink/symlinked_subproject/datadir/meson.build b/test cases/unit/108 subproject symlink/symlinked_subproject/datadir/meson.build similarity index 100% rename from test cases/unit/107 subproject symlink/symlinked_subproject/datadir/meson.build rename to test cases/unit/108 subproject symlink/symlinked_subproject/datadir/meson.build diff --git a/test cases/unit/107 subproject symlink/symlinked_subproject/meson.build b/test cases/unit/108 subproject symlink/symlinked_subproject/meson.build similarity index 100% rename from test cases/unit/107 subproject symlink/symlinked_subproject/meson.build rename to test cases/unit/108 subproject symlink/symlinked_subproject/meson.build diff --git a/test cases/unit/107 subproject symlink/symlinked_subproject/src.c b/test cases/unit/108 subproject symlink/symlinked_subproject/src.c similarity index 100% rename from test cases/unit/107 subproject symlink/symlinked_subproject/src.c rename to test cases/unit/108 subproject symlink/symlinked_subproject/src.c diff --git a/test cases/unit/108 new subproject on reconfigure/meson.build b/test cases/unit/109 new subproject on reconfigure/meson.build similarity index 100% rename from test cases/unit/108 new subproject on reconfigure/meson.build rename to test cases/unit/109 new subproject on reconfigure/meson.build diff --git a/test cases/unit/108 new subproject on reconfigure/meson_options.txt b/test cases/unit/109 new subproject on reconfigure/meson_options.txt similarity index 100% rename from test cases/unit/108 new subproject on reconfigure/meson_options.txt rename to test cases/unit/109 new subproject on reconfigure/meson_options.txt diff --git a/test cases/unit/108 new subproject on reconfigure/subprojects/foo/foo.c b/test cases/unit/109 new subproject on reconfigure/subprojects/foo/foo.c similarity index 100% rename from test cases/unit/108 new subproject on reconfigure/subprojects/foo/foo.c rename to test cases/unit/109 new subproject on reconfigure/subprojects/foo/foo.c diff --git a/test cases/unit/108 new subproject on reconfigure/subprojects/foo/meson.build b/test cases/unit/109 new subproject on reconfigure/subprojects/foo/meson.build similarity index 100% rename from test cases/unit/108 new subproject on reconfigure/subprojects/foo/meson.build rename to test cases/unit/109 new subproject on reconfigure/subprojects/foo/meson.build diff --git a/test cases/unit/109 configure same noop/meson.build b/test cases/unit/110 configure same noop/meson.build similarity index 100% rename from test cases/unit/109 configure same noop/meson.build rename to test cases/unit/110 configure same noop/meson.build diff --git a/test cases/unit/109 configure same noop/meson_options.txt b/test cases/unit/110 configure same noop/meson_options.txt similarity index 100% rename from test cases/unit/109 configure same noop/meson_options.txt rename to test cases/unit/110 configure same noop/meson_options.txt diff --git a/test cases/unit/110 freeze/freeze.c b/test cases/unit/111 freeze/freeze.c similarity index 100% rename from test cases/unit/110 freeze/freeze.c rename to test cases/unit/111 freeze/freeze.c diff --git a/test cases/unit/110 freeze/meson.build b/test cases/unit/111 freeze/meson.build similarity index 100% rename from test cases/unit/110 freeze/meson.build rename to test cases/unit/111 freeze/meson.build diff --git a/test cases/unit/111 replace unencodable xml chars/meson.build b/test cases/unit/112 replace unencodable xml chars/meson.build similarity index 100% rename from test cases/unit/111 replace unencodable xml chars/meson.build rename to test cases/unit/112 replace unencodable xml chars/meson.build diff --git a/test cases/unit/111 replace unencodable xml chars/script.py b/test cases/unit/112 replace unencodable xml chars/script.py similarity index 100% rename from test cases/unit/111 replace unencodable xml chars/script.py rename to test cases/unit/112 replace unencodable xml chars/script.py diff --git a/test cases/unit/112 classpath/com/mesonbuild/Simple.java b/test cases/unit/113 classpath/com/mesonbuild/Simple.java similarity index 100% rename from test cases/unit/112 classpath/com/mesonbuild/Simple.java rename to test cases/unit/113 classpath/com/mesonbuild/Simple.java diff --git a/test cases/unit/112 classpath/meson.build b/test cases/unit/113 classpath/meson.build similarity index 100% rename from test cases/unit/112 classpath/meson.build rename to test cases/unit/113 classpath/meson.build diff --git a/test cases/unit/113 list build options/meson.build b/test cases/unit/114 list build options/meson.build similarity index 100% rename from test cases/unit/113 list build options/meson.build rename to test cases/unit/114 list build options/meson.build diff --git a/test cases/unit/113 list build options/meson_options.txt b/test cases/unit/114 list build options/meson_options.txt similarity index 100% rename from test cases/unit/113 list build options/meson_options.txt rename to test cases/unit/114 list build options/meson_options.txt diff --git a/test cases/unit/114 complex link cases/main.c b/test cases/unit/115 complex link cases/main.c similarity index 100% rename from test cases/unit/114 complex link cases/main.c rename to test cases/unit/115 complex link cases/main.c diff --git a/test cases/unit/114 complex link cases/meson.build b/test cases/unit/115 complex link cases/meson.build similarity index 100% rename from test cases/unit/114 complex link cases/meson.build rename to test cases/unit/115 complex link cases/meson.build diff --git a/test cases/unit/114 complex link cases/s1.c b/test cases/unit/115 complex link cases/s1.c similarity index 100% rename from test cases/unit/114 complex link cases/s1.c rename to test cases/unit/115 complex link cases/s1.c diff --git a/test cases/unit/114 complex link cases/s2.c b/test cases/unit/115 complex link cases/s2.c similarity index 100% rename from test cases/unit/114 complex link cases/s2.c rename to test cases/unit/115 complex link cases/s2.c diff --git a/test cases/unit/114 complex link cases/s3.c b/test cases/unit/115 complex link cases/s3.c similarity index 100% rename from test cases/unit/114 complex link cases/s3.c rename to test cases/unit/115 complex link cases/s3.c diff --git a/test cases/unit/115 c cpp stds/meson.build b/test cases/unit/116 c cpp stds/meson.build similarity index 100% rename from test cases/unit/115 c cpp stds/meson.build rename to test cases/unit/116 c cpp stds/meson.build diff --git a/test cases/unit/115 c cpp stds/meson.options b/test cases/unit/116 c cpp stds/meson.options similarity index 100% rename from test cases/unit/115 c cpp stds/meson.options rename to test cases/unit/116 c cpp stds/meson.options diff --git a/test cases/unit/116 empty project/expected_mods.json b/test cases/unit/117 empty project/expected_mods.json similarity index 100% rename from test cases/unit/116 empty project/expected_mods.json rename to test cases/unit/117 empty project/expected_mods.json diff --git a/test cases/unit/116 empty project/meson.build b/test cases/unit/117 empty project/meson.build similarity index 100% rename from test cases/unit/116 empty project/meson.build rename to test cases/unit/117 empty project/meson.build diff --git a/test cases/unit/117 genvslite/main.cpp b/test cases/unit/118 genvslite/main.cpp similarity index 100% rename from test cases/unit/117 genvslite/main.cpp rename to test cases/unit/118 genvslite/main.cpp diff --git a/test cases/unit/117 genvslite/meson.build b/test cases/unit/118 genvslite/meson.build similarity index 100% rename from test cases/unit/117 genvslite/meson.build rename to test cases/unit/118 genvslite/meson.build diff --git a/test cases/unit/118 meson package cache dir/cache_dir/bar/meson.build b/test cases/unit/119 meson package cache dir/cache_dir/bar/meson.build similarity index 100% rename from test cases/unit/118 meson package cache dir/cache_dir/bar/meson.build rename to test cases/unit/119 meson package cache dir/cache_dir/bar/meson.build diff --git a/test cases/unit/118 meson package cache dir/cache_dir/foo.zip b/test cases/unit/119 meson package cache dir/cache_dir/foo.zip similarity index 100% rename from test cases/unit/118 meson package cache dir/cache_dir/foo.zip rename to test cases/unit/119 meson package cache dir/cache_dir/foo.zip diff --git a/test cases/unit/118 meson package cache dir/meson.build b/test cases/unit/119 meson package cache dir/meson.build similarity index 100% rename from test cases/unit/118 meson package cache dir/meson.build rename to test cases/unit/119 meson package cache dir/meson.build diff --git a/test cases/unit/118 meson package cache dir/subprojects/bar.wrap b/test cases/unit/119 meson package cache dir/subprojects/bar.wrap similarity index 100% rename from test cases/unit/118 meson package cache dir/subprojects/bar.wrap rename to test cases/unit/119 meson package cache dir/subprojects/bar.wrap diff --git a/test cases/unit/118 meson package cache dir/subprojects/foo.wrap b/test cases/unit/119 meson package cache dir/subprojects/foo.wrap similarity index 100% rename from test cases/unit/118 meson package cache dir/subprojects/foo.wrap rename to test cases/unit/119 meson package cache dir/subprojects/foo.wrap diff --git a/test cases/unit/119 openssl cmake bug/meson.build b/test cases/unit/120 openssl cmake bug/meson.build similarity index 100% rename from test cases/unit/119 openssl cmake bug/meson.build rename to test cases/unit/120 openssl cmake bug/meson.build diff --git a/test cases/unit/119 openssl cmake bug/nativefile.ini b/test cases/unit/120 openssl cmake bug/nativefile.ini similarity index 100% rename from test cases/unit/119 openssl cmake bug/nativefile.ini rename to test cases/unit/120 openssl cmake bug/nativefile.ini diff --git a/test cases/unit/120 rewrite/meson.build b/test cases/unit/121 rewrite/meson.build similarity index 100% rename from test cases/unit/120 rewrite/meson.build rename to test cases/unit/121 rewrite/meson.build diff --git a/test cases/unit/121 executable suffix/main.c b/test cases/unit/122 executable suffix/main.c similarity index 100% rename from test cases/unit/121 executable suffix/main.c rename to test cases/unit/122 executable suffix/main.c diff --git a/test cases/unit/121 executable suffix/meson.build b/test cases/unit/122 executable suffix/meson.build similarity index 100% rename from test cases/unit/121 executable suffix/meson.build rename to test cases/unit/122 executable suffix/meson.build diff --git a/test cases/unit/122 persp options/meson.build b/test cases/unit/123 persp options/meson.build similarity index 100% rename from test cases/unit/122 persp options/meson.build rename to test cases/unit/123 persp options/meson.build diff --git a/test cases/unit/122 persp options/meson.options b/test cases/unit/123 persp options/meson.options similarity index 100% rename from test cases/unit/122 persp options/meson.options rename to test cases/unit/123 persp options/meson.options diff --git a/test cases/unit/122 persp options/subprojects/sub1/meson.build b/test cases/unit/123 persp options/subprojects/sub1/meson.build similarity index 100% rename from test cases/unit/122 persp options/subprojects/sub1/meson.build rename to test cases/unit/123 persp options/subprojects/sub1/meson.build diff --git a/test cases/unit/122 persp options/subprojects/sub1/meson.options b/test cases/unit/123 persp options/subprojects/sub1/meson.options similarity index 100% rename from test cases/unit/122 persp options/subprojects/sub1/meson.options rename to test cases/unit/123 persp options/subprojects/sub1/meson.options diff --git a/test cases/unit/122 persp options/subprojects/sub1/sub1.c b/test cases/unit/123 persp options/subprojects/sub1/sub1.c similarity index 100% rename from test cases/unit/122 persp options/subprojects/sub1/sub1.c rename to test cases/unit/123 persp options/subprojects/sub1/sub1.c diff --git a/test cases/unit/122 persp options/subprojects/sub2/meson.build b/test cases/unit/123 persp options/subprojects/sub2/meson.build similarity index 100% rename from test cases/unit/122 persp options/subprojects/sub2/meson.build rename to test cases/unit/123 persp options/subprojects/sub2/meson.build diff --git a/test cases/unit/122 persp options/subprojects/sub2/meson.options b/test cases/unit/123 persp options/subprojects/sub2/meson.options similarity index 100% rename from test cases/unit/122 persp options/subprojects/sub2/meson.options rename to test cases/unit/123 persp options/subprojects/sub2/meson.options diff --git a/test cases/unit/122 persp options/subprojects/sub2/sub2.c b/test cases/unit/123 persp options/subprojects/sub2/sub2.c similarity index 100% rename from test cases/unit/122 persp options/subprojects/sub2/sub2.c rename to test cases/unit/123 persp options/subprojects/sub2/sub2.c diff --git a/test cases/unit/122 persp options/toplevel.c b/test cases/unit/123 persp options/toplevel.c similarity index 100% rename from test cases/unit/122 persp options/toplevel.c rename to test cases/unit/123 persp options/toplevel.c diff --git a/test cases/unit/123 reconfigure base options/meson.build b/test cases/unit/124 reconfigure base options/meson.build similarity index 100% rename from test cases/unit/123 reconfigure base options/meson.build rename to test cases/unit/124 reconfigure base options/meson.build diff --git a/test cases/unit/123 reconfigure base options/subprojects/sub/meson.build b/test cases/unit/124 reconfigure base options/subprojects/sub/meson.build similarity index 100% rename from test cases/unit/123 reconfigure base options/subprojects/sub/meson.build rename to test cases/unit/124 reconfigure base options/subprojects/sub/meson.build diff --git a/test cases/unit/124 interactive tap/meson.build b/test cases/unit/125 interactive tap/meson.build similarity index 100% rename from test cases/unit/124 interactive tap/meson.build rename to test cases/unit/125 interactive tap/meson.build diff --git a/test cases/unit/124 interactive tap/script.py b/test cases/unit/125 interactive tap/script.py similarity index 100% rename from test cases/unit/124 interactive tap/script.py rename to test cases/unit/125 interactive tap/script.py diff --git a/test cases/unit/125 declare_dep var/meson.build b/test cases/unit/126 declare_dep var/meson.build similarity index 100% rename from test cases/unit/125 declare_dep var/meson.build rename to test cases/unit/126 declare_dep var/meson.build diff --git a/test cases/unit/125 declare_dep var/meson_options.txt b/test cases/unit/126 declare_dep var/meson_options.txt similarity index 100% rename from test cases/unit/125 declare_dep var/meson_options.txt rename to test cases/unit/126 declare_dep var/meson_options.txt diff --git a/test cases/unit/126 python extension/foo.c b/test cases/unit/127 python extension/foo.c similarity index 100% rename from test cases/unit/126 python extension/foo.c rename to test cases/unit/127 python extension/foo.c diff --git a/test cases/unit/126 python extension/meson.build b/test cases/unit/127 python extension/meson.build similarity index 100% rename from test cases/unit/126 python extension/meson.build rename to test cases/unit/127 python extension/meson.build diff --git a/test cases/unit/127 pkgsubproj/meson.build b/test cases/unit/128 pkgsubproj/meson.build similarity index 100% rename from test cases/unit/127 pkgsubproj/meson.build rename to test cases/unit/128 pkgsubproj/meson.build diff --git a/test cases/unit/127 pkgsubproj/subprojects/sub/meson.build b/test cases/unit/128 pkgsubproj/subprojects/sub/meson.build similarity index 100% rename from test cases/unit/127 pkgsubproj/subprojects/sub/meson.build rename to test cases/unit/128 pkgsubproj/subprojects/sub/meson.build diff --git a/test cases/unit/128 test slice/meson.build b/test cases/unit/129 test slice/meson.build similarity index 100% rename from test cases/unit/128 test slice/meson.build rename to test cases/unit/129 test slice/meson.build diff --git a/test cases/unit/128 test slice/test.py b/test cases/unit/129 test slice/test.py similarity index 100% rename from test cases/unit/128 test slice/test.py rename to test cases/unit/129 test slice/test.py diff --git a/test cases/unit/129 sanitizers/meson.build b/test cases/unit/130 sanitizers/meson.build similarity index 100% rename from test cases/unit/129 sanitizers/meson.build rename to test cases/unit/130 sanitizers/meson.build diff --git a/test cases/unit/130 long opt vs D/meson.build b/test cases/unit/131 long opt vs D/meson.build similarity index 100% rename from test cases/unit/130 long opt vs D/meson.build rename to test cases/unit/131 long opt vs D/meson.build diff --git a/test cases/unit/130 long opt vs D/meson_options.txt b/test cases/unit/131 long opt vs D/meson_options.txt similarity index 100% rename from test cases/unit/130 long opt vs D/meson_options.txt rename to test cases/unit/131 long opt vs D/meson_options.txt diff --git a/test cases/unit/131 vala internal glib/lib.vala b/test cases/unit/132 vala internal glib/lib.vala similarity index 100% rename from test cases/unit/131 vala internal glib/lib.vala rename to test cases/unit/132 vala internal glib/lib.vala diff --git a/test cases/unit/131 vala internal glib/meson.build b/test cases/unit/132 vala internal glib/meson.build similarity index 100% rename from test cases/unit/131 vala internal glib/meson.build rename to test cases/unit/132 vala internal glib/meson.build diff --git a/test cases/unit/131 vala internal glib/meson.options b/test cases/unit/132 vala internal glib/meson.options similarity index 100% rename from test cases/unit/131 vala internal glib/meson.options rename to test cases/unit/132 vala internal glib/meson.options diff --git a/test cases/unit/132 custom target index test/meson.build b/test cases/unit/133 custom target index test/meson.build similarity index 100% rename from test cases/unit/132 custom target index test/meson.build rename to test cases/unit/133 custom target index test/meson.build diff --git a/test cases/unit/133 nasm language only/meson.build b/test cases/unit/134 nasm language only/meson.build similarity index 100% rename from test cases/unit/133 nasm language only/meson.build rename to test cases/unit/134 nasm language only/meson.build diff --git a/test cases/unit/134 promote link_language/consumer.c b/test cases/unit/135 promote link_language/consumer.c similarity index 100% rename from test cases/unit/134 promote link_language/consumer.c rename to test cases/unit/135 promote link_language/consumer.c diff --git a/test cases/unit/134 promote link_language/header.h b/test cases/unit/135 promote link_language/header.h similarity index 100% rename from test cases/unit/134 promote link_language/header.h rename to test cases/unit/135 promote link_language/header.h diff --git a/test cases/unit/134 promote link_language/lib.cpp b/test cases/unit/135 promote link_language/lib.cpp similarity index 100% rename from test cases/unit/134 promote link_language/lib.cpp rename to test cases/unit/135 promote link_language/lib.cpp diff --git a/test cases/unit/134 promote link_language/meson.build b/test cases/unit/135 promote link_language/meson.build similarity index 100% rename from test cases/unit/134 promote link_language/meson.build rename to test cases/unit/135 promote link_language/meson.build diff --git a/test cases/unit/135 minimal bindgen/header.h b/test cases/unit/136 minimal bindgen/header.h similarity index 100% rename from test cases/unit/135 minimal bindgen/header.h rename to test cases/unit/136 minimal bindgen/header.h diff --git a/test cases/unit/135 minimal bindgen/meson.build b/test cases/unit/136 minimal bindgen/meson.build similarity index 100% rename from test cases/unit/135 minimal bindgen/meson.build rename to test cases/unit/136 minimal bindgen/meson.build diff --git a/test cases/unit/135 minimal bindgen/meson.options b/test cases/unit/136 minimal bindgen/meson.options similarity index 100% rename from test cases/unit/135 minimal bindgen/meson.options rename to test cases/unit/136 minimal bindgen/meson.options diff --git a/test cases/unit/136 native subproject introspect/meson.build b/test cases/unit/137 native subproject introspect/meson.build similarity index 100% rename from test cases/unit/136 native subproject introspect/meson.build rename to test cases/unit/137 native subproject introspect/meson.build diff --git a/test cases/unit/136 native subproject introspect/subprojects/recursive-both/lib.c b/test cases/unit/137 native subproject introspect/subprojects/recursive-both/lib.c similarity index 100% rename from test cases/unit/136 native subproject introspect/subprojects/recursive-both/lib.c rename to test cases/unit/137 native subproject introspect/subprojects/recursive-both/lib.c diff --git a/test cases/unit/136 native subproject introspect/subprojects/recursive-both/lib.def b/test cases/unit/137 native subproject introspect/subprojects/recursive-both/lib.def similarity index 100% rename from test cases/unit/136 native subproject introspect/subprojects/recursive-both/lib.def rename to test cases/unit/137 native subproject introspect/subprojects/recursive-both/lib.def diff --git a/test cases/unit/136 native subproject introspect/subprojects/recursive-both/meson.build b/test cases/unit/137 native subproject introspect/subprojects/recursive-both/meson.build similarity index 100% rename from test cases/unit/136 native subproject introspect/subprojects/recursive-both/meson.build rename to test cases/unit/137 native subproject introspect/subprojects/recursive-both/meson.build diff --git a/test cases/unit/136 native subproject introspect/subprojects/recursive-both/recursive-both.h.in b/test cases/unit/137 native subproject introspect/subprojects/recursive-both/recursive-both.h.in similarity index 100% rename from test cases/unit/136 native subproject introspect/subprojects/recursive-both/recursive-both.h.in rename to test cases/unit/137 native subproject introspect/subprojects/recursive-both/recursive-both.h.in diff --git a/test cases/unit/137 external property nonexisting/meson.build b/test cases/unit/138 external property nonexisting/meson.build similarity index 100% rename from test cases/unit/137 external property nonexisting/meson.build rename to test cases/unit/138 external property nonexisting/meson.build diff --git a/test cases/unit/137 external property nonexisting/meson_options.txt b/test cases/unit/138 external property nonexisting/meson_options.txt similarity index 100% rename from test cases/unit/137 external property nonexisting/meson_options.txt rename to test cases/unit/138 external property nonexisting/meson_options.txt diff --git a/test cases/unit/137 external property nonexisting/propfile b/test cases/unit/138 external property nonexisting/propfile similarity index 100% rename from test cases/unit/137 external property nonexisting/propfile rename to test cases/unit/138 external property nonexisting/propfile diff --git a/test cases/unit/91 install skip subprojects/foo.c b/test cases/unit/92 install skip subprojects/foo.c similarity index 100% rename from test cases/unit/91 install skip subprojects/foo.c rename to test cases/unit/92 install skip subprojects/foo.c diff --git a/test cases/unit/91 install skip subprojects/foo.dat b/test cases/unit/92 install skip subprojects/foo.dat similarity index 100% rename from test cases/unit/91 install skip subprojects/foo.dat rename to test cases/unit/92 install skip subprojects/foo.dat diff --git a/test cases/unit/91 install skip subprojects/foo.h b/test cases/unit/92 install skip subprojects/foo.h similarity index 100% rename from test cases/unit/91 install skip subprojects/foo.h rename to test cases/unit/92 install skip subprojects/foo.h diff --git a/test cases/unit/91 install skip subprojects/foo/foofile b/test cases/unit/92 install skip subprojects/foo/foofile similarity index 100% rename from test cases/unit/91 install skip subprojects/foo/foofile rename to test cases/unit/92 install skip subprojects/foo/foofile diff --git a/test cases/unit/91 install skip subprojects/meson.build b/test cases/unit/92 install skip subprojects/meson.build similarity index 100% rename from test cases/unit/91 install skip subprojects/meson.build rename to test cases/unit/92 install skip subprojects/meson.build diff --git a/test cases/unit/91 install skip subprojects/subprojects/bar/bar.c b/test cases/unit/92 install skip subprojects/subprojects/bar/bar.c similarity index 100% rename from test cases/unit/91 install skip subprojects/subprojects/bar/bar.c rename to test cases/unit/92 install skip subprojects/subprojects/bar/bar.c diff --git a/test cases/unit/91 install skip subprojects/subprojects/bar/bar.dat b/test cases/unit/92 install skip subprojects/subprojects/bar/bar.dat similarity index 100% rename from test cases/unit/91 install skip subprojects/subprojects/bar/bar.dat rename to test cases/unit/92 install skip subprojects/subprojects/bar/bar.dat diff --git a/test cases/unit/91 install skip subprojects/subprojects/bar/bar.h b/test cases/unit/92 install skip subprojects/subprojects/bar/bar.h similarity index 100% rename from test cases/unit/91 install skip subprojects/subprojects/bar/bar.h rename to test cases/unit/92 install skip subprojects/subprojects/bar/bar.h diff --git a/test cases/unit/91 install skip subprojects/subprojects/bar/bar/barfile b/test cases/unit/92 install skip subprojects/subprojects/bar/bar/barfile similarity index 100% rename from test cases/unit/91 install skip subprojects/subprojects/bar/bar/barfile rename to test cases/unit/92 install skip subprojects/subprojects/bar/bar/barfile diff --git a/test cases/unit/91 install skip subprojects/subprojects/bar/meson.build b/test cases/unit/92 install skip subprojects/subprojects/bar/meson.build similarity index 100% rename from test cases/unit/91 install skip subprojects/subprojects/bar/meson.build rename to test cases/unit/92 install skip subprojects/subprojects/bar/meson.build diff --git a/test cases/unit/92 new subproject in configured project/meson.build b/test cases/unit/93 new subproject in configured project/meson.build similarity index 100% rename from test cases/unit/92 new subproject in configured project/meson.build rename to test cases/unit/93 new subproject in configured project/meson.build diff --git a/test cases/unit/92 new subproject in configured project/meson_options.txt b/test cases/unit/93 new subproject in configured project/meson_options.txt similarity index 100% rename from test cases/unit/92 new subproject in configured project/meson_options.txt rename to test cases/unit/93 new subproject in configured project/meson_options.txt diff --git a/test cases/unit/92 new subproject in configured project/subprojects/sub/foo.c b/test cases/unit/93 new subproject in configured project/subprojects/sub/foo.c similarity index 100% rename from test cases/unit/92 new subproject in configured project/subprojects/sub/foo.c rename to test cases/unit/93 new subproject in configured project/subprojects/sub/foo.c diff --git a/test cases/unit/92 new subproject in configured project/subprojects/sub/meson.build b/test cases/unit/93 new subproject in configured project/subprojects/sub/meson.build similarity index 100% rename from test cases/unit/92 new subproject in configured project/subprojects/sub/meson.build rename to test cases/unit/93 new subproject in configured project/subprojects/sub/meson.build diff --git a/test cases/unit/93 clangformat/.clang-format b/test cases/unit/94 clangformat/.clang-format similarity index 100% rename from test cases/unit/93 clangformat/.clang-format rename to test cases/unit/94 clangformat/.clang-format diff --git a/test cases/unit/93 clangformat/.clang-format-ignore b/test cases/unit/94 clangformat/.clang-format-ignore similarity index 100% rename from test cases/unit/93 clangformat/.clang-format-ignore rename to test cases/unit/94 clangformat/.clang-format-ignore diff --git a/test cases/unit/93 clangformat/.clang-format-include b/test cases/unit/94 clangformat/.clang-format-include similarity index 100% rename from test cases/unit/93 clangformat/.clang-format-include rename to test cases/unit/94 clangformat/.clang-format-include diff --git a/test cases/unit/93 clangformat/meson.build b/test cases/unit/94 clangformat/meson.build similarity index 100% rename from test cases/unit/93 clangformat/meson.build rename to test cases/unit/94 clangformat/meson.build diff --git a/test cases/unit/93 clangformat/not-included/badformat.cpp b/test cases/unit/94 clangformat/not-included/badformat.cpp similarity index 100% rename from test cases/unit/93 clangformat/not-included/badformat.cpp rename to test cases/unit/94 clangformat/not-included/badformat.cpp diff --git a/test cases/unit/93 clangformat/src/badformat.c b/test cases/unit/94 clangformat/src/badformat.c similarity index 100% rename from test cases/unit/93 clangformat/src/badformat.c rename to test cases/unit/94 clangformat/src/badformat.c diff --git a/test cases/unit/93 clangformat/src/badformat.cpp b/test cases/unit/94 clangformat/src/badformat.cpp similarity index 100% rename from test cases/unit/93 clangformat/src/badformat.cpp rename to test cases/unit/94 clangformat/src/badformat.cpp diff --git a/test cases/unit/94 custominc/easytogrepfor/genh.py b/test cases/unit/95 custominc/easytogrepfor/genh.py similarity index 100% rename from test cases/unit/94 custominc/easytogrepfor/genh.py rename to test cases/unit/95 custominc/easytogrepfor/genh.py diff --git a/test cases/unit/94 custominc/easytogrepfor/meson.build b/test cases/unit/95 custominc/easytogrepfor/meson.build similarity index 100% rename from test cases/unit/94 custominc/easytogrepfor/meson.build rename to test cases/unit/95 custominc/easytogrepfor/meson.build diff --git a/test cases/unit/94 custominc/helper.c b/test cases/unit/95 custominc/helper.c similarity index 100% rename from test cases/unit/94 custominc/helper.c rename to test cases/unit/95 custominc/helper.c diff --git a/test cases/unit/94 custominc/meson.build b/test cases/unit/95 custominc/meson.build similarity index 100% rename from test cases/unit/94 custominc/meson.build rename to test cases/unit/95 custominc/meson.build diff --git a/test cases/unit/94 custominc/prog.c b/test cases/unit/95 custominc/prog.c similarity index 100% rename from test cases/unit/94 custominc/prog.c rename to test cases/unit/95 custominc/prog.c diff --git a/test cases/unit/94 custominc/prog2.c b/test cases/unit/95 custominc/prog2.c similarity index 100% rename from test cases/unit/94 custominc/prog2.c rename to test cases/unit/95 custominc/prog2.c diff --git a/test cases/unit/95 implicit force fallback/meson.build b/test cases/unit/96 implicit force fallback/meson.build similarity index 100% rename from test cases/unit/95 implicit force fallback/meson.build rename to test cases/unit/96 implicit force fallback/meson.build diff --git a/test cases/unit/95 implicit force fallback/subprojects/something/meson.build b/test cases/unit/96 implicit force fallback/subprojects/something/meson.build similarity index 100% rename from test cases/unit/95 implicit force fallback/subprojects/something/meson.build rename to test cases/unit/96 implicit force fallback/subprojects/something/meson.build diff --git a/test cases/unit/96 compiler.links file arg/meson.build b/test cases/unit/97 compiler.links file arg/meson.build similarity index 100% rename from test cases/unit/96 compiler.links file arg/meson.build rename to test cases/unit/97 compiler.links file arg/meson.build diff --git a/test cases/unit/96 compiler.links file arg/test.c b/test cases/unit/97 compiler.links file arg/test.c similarity index 100% rename from test cases/unit/96 compiler.links file arg/test.c rename to test cases/unit/97 compiler.links file arg/test.c diff --git a/test cases/unit/97 link full name/.gitignore b/test cases/unit/98 link full name/.gitignore similarity index 100% rename from test cases/unit/97 link full name/.gitignore rename to test cases/unit/98 link full name/.gitignore diff --git a/test cases/unit/97 link full name/libtestprovider/meson.build b/test cases/unit/98 link full name/libtestprovider/meson.build similarity index 100% rename from test cases/unit/97 link full name/libtestprovider/meson.build rename to test cases/unit/98 link full name/libtestprovider/meson.build diff --git a/test cases/unit/97 link full name/libtestprovider/provider.c b/test cases/unit/98 link full name/libtestprovider/provider.c similarity index 100% rename from test cases/unit/97 link full name/libtestprovider/provider.c rename to test cases/unit/98 link full name/libtestprovider/provider.c diff --git a/test cases/unit/97 link full name/proguser/meson.build b/test cases/unit/98 link full name/proguser/meson.build similarity index 100% rename from test cases/unit/97 link full name/proguser/meson.build rename to test cases/unit/98 link full name/proguser/meson.build diff --git a/test cases/unit/97 link full name/proguser/receiver.c b/test cases/unit/98 link full name/proguser/receiver.c similarity index 100% rename from test cases/unit/97 link full name/proguser/receiver.c rename to test cases/unit/98 link full name/proguser/receiver.c diff --git a/test cases/unit/98 install all targets/bar-custom.txt b/test cases/unit/99 install all targets/bar-custom.txt similarity index 100% rename from test cases/unit/98 install all targets/bar-custom.txt rename to test cases/unit/99 install all targets/bar-custom.txt diff --git a/test cases/unit/98 install all targets/bar-devel.h b/test cases/unit/99 install all targets/bar-devel.h similarity index 100% rename from test cases/unit/98 install all targets/bar-devel.h rename to test cases/unit/99 install all targets/bar-devel.h diff --git a/test cases/unit/98 install all targets/bar-notag.txt b/test cases/unit/99 install all targets/bar-notag.txt similarity index 100% rename from test cases/unit/98 install all targets/bar-notag.txt rename to test cases/unit/99 install all targets/bar-notag.txt diff --git a/test cases/unit/98 install all targets/custom_files/data.txt b/test cases/unit/99 install all targets/custom_files/data.txt similarity index 100% rename from test cases/unit/98 install all targets/custom_files/data.txt rename to test cases/unit/99 install all targets/custom_files/data.txt diff --git a/test cases/unit/98 install all targets/excludes/excluded.txt b/test cases/unit/99 install all targets/excludes/excluded.txt similarity index 100% rename from test cases/unit/98 install all targets/excludes/excluded.txt rename to test cases/unit/99 install all targets/excludes/excluded.txt diff --git a/test cases/unit/98 install all targets/excludes/excluded/placeholder.txt b/test cases/unit/99 install all targets/excludes/excluded/placeholder.txt similarity index 100% rename from test cases/unit/98 install all targets/excludes/excluded/placeholder.txt rename to test cases/unit/99 install all targets/excludes/excluded/placeholder.txt diff --git a/test cases/unit/98 install all targets/excludes/installed.txt b/test cases/unit/99 install all targets/excludes/installed.txt similarity index 100% rename from test cases/unit/98 install all targets/excludes/installed.txt rename to test cases/unit/99 install all targets/excludes/installed.txt diff --git a/test cases/unit/98 install all targets/foo.in b/test cases/unit/99 install all targets/foo.in similarity index 100% rename from test cases/unit/98 install all targets/foo.in rename to test cases/unit/99 install all targets/foo.in diff --git a/test cases/unit/98 install all targets/foo1-devel.h b/test cases/unit/99 install all targets/foo1-devel.h similarity index 100% rename from test cases/unit/98 install all targets/foo1-devel.h rename to test cases/unit/99 install all targets/foo1-devel.h diff --git a/test cases/unit/98 install all targets/lib.c b/test cases/unit/99 install all targets/lib.c similarity index 100% rename from test cases/unit/98 install all targets/lib.c rename to test cases/unit/99 install all targets/lib.c diff --git a/test cases/unit/98 install all targets/main.c b/test cases/unit/99 install all targets/main.c similarity index 100% rename from test cases/unit/98 install all targets/main.c rename to test cases/unit/99 install all targets/main.c diff --git a/test cases/unit/98 install all targets/meson.build b/test cases/unit/99 install all targets/meson.build similarity index 100% rename from test cases/unit/98 install all targets/meson.build rename to test cases/unit/99 install all targets/meson.build diff --git a/test cases/unit/98 install all targets/script.py b/test cases/unit/99 install all targets/script.py similarity index 100% rename from test cases/unit/98 install all targets/script.py rename to test cases/unit/99 install all targets/script.py diff --git a/test cases/unit/98 install all targets/subdir/bar2-devel.h b/test cases/unit/99 install all targets/subdir/bar2-devel.h similarity index 100% rename from test cases/unit/98 install all targets/subdir/bar2-devel.h rename to test cases/unit/99 install all targets/subdir/bar2-devel.h diff --git a/test cases/unit/98 install all targets/subdir/foo2.in b/test cases/unit/99 install all targets/subdir/foo2.in similarity index 100% rename from test cases/unit/98 install all targets/subdir/foo2.in rename to test cases/unit/99 install all targets/subdir/foo2.in diff --git a/test cases/unit/98 install all targets/subdir/foo3-devel.h b/test cases/unit/99 install all targets/subdir/foo3-devel.h similarity index 100% rename from test cases/unit/98 install all targets/subdir/foo3-devel.h rename to test cases/unit/99 install all targets/subdir/foo3-devel.h diff --git a/test cases/unit/98 install all targets/subdir/lib.c b/test cases/unit/99 install all targets/subdir/lib.c similarity index 100% rename from test cases/unit/98 install all targets/subdir/lib.c rename to test cases/unit/99 install all targets/subdir/lib.c diff --git a/test cases/unit/98 install all targets/subdir/main.c b/test cases/unit/99 install all targets/subdir/main.c similarity index 100% rename from test cases/unit/98 install all targets/subdir/main.c rename to test cases/unit/99 install all targets/subdir/main.c diff --git a/test cases/unit/98 install all targets/subdir/meson.build b/test cases/unit/99 install all targets/subdir/meson.build similarity index 100% rename from test cases/unit/98 install all targets/subdir/meson.build rename to test cases/unit/99 install all targets/subdir/meson.build diff --git a/test cases/unit/98 install all targets/subdir/script.py b/test cases/unit/99 install all targets/subdir/script.py similarity index 100% rename from test cases/unit/98 install all targets/subdir/script.py rename to test cases/unit/99 install all targets/subdir/script.py diff --git a/test cases/unit/98 install all targets/subprojects/subproject/aaa.txt b/test cases/unit/99 install all targets/subprojects/subproject/aaa.txt similarity index 100% rename from test cases/unit/98 install all targets/subprojects/subproject/aaa.txt rename to test cases/unit/99 install all targets/subprojects/subproject/aaa.txt diff --git a/test cases/unit/98 install all targets/subprojects/subproject/bbb.txt b/test cases/unit/99 install all targets/subprojects/subproject/bbb.txt similarity index 100% rename from test cases/unit/98 install all targets/subprojects/subproject/bbb.txt rename to test cases/unit/99 install all targets/subprojects/subproject/bbb.txt diff --git a/test cases/unit/98 install all targets/subprojects/subproject/meson.build b/test cases/unit/99 install all targets/subprojects/subproject/meson.build similarity index 100% rename from test cases/unit/98 install all targets/subprojects/subproject/meson.build rename to test cases/unit/99 install all targets/subprojects/subproject/meson.build diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index cbd817fa62e4..7c558243d58a 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -538,7 +538,7 @@ def test_replace_unencodable_xml_chars_unit(self): printable representation https://github.com/mesonbuild/meson/issues/9894 ''' - testdir = os.path.join(self.unit_test_dir, '111 replace unencodable xml chars') + testdir = os.path.join(self.unit_test_dir, '112 replace unencodable xml chars') self.init(testdir) tests_command_output = self.run_tests() junit_xml_logs = Path(self.logdir, 'testlog.junit.xml') @@ -699,7 +699,7 @@ def test_forcefallback(self): self.run_tests() def test_implicit_forcefallback(self): - testdir = os.path.join(self.unit_test_dir, '95 implicit force fallback') + testdir = os.path.join(self.unit_test_dir, '96 implicit force fallback') with self.assertRaises(subprocess.CalledProcessError): self.init(testdir) self.init(testdir, extra_args=['--wrap-mode=forcefallback']) @@ -1491,7 +1491,7 @@ def test_long_opt_vs_D(self): Test that conflicts between -D for builtin options and the corresponding long option are detected without false positives or negatives. ''' - testdir = os.path.join(self.unit_test_dir, '130 long opt vs D') + testdir = os.path.join(self.unit_test_dir, '131 long opt vs D') for opt in ['-Dsysconfdir=/etc', '-Dsysconfdir2=/etc']: exception_raised = False @@ -2235,7 +2235,7 @@ def check_pcfile(name, *, relocatable, levels=2): check_pcfile('libvartest2.pc', relocatable=False) self.wipe() - testdir_abs = os.path.join(self.unit_test_dir, '105 pkgconfig relocatable with absolute path') + testdir_abs = os.path.join(self.unit_test_dir, '106 pkgconfig relocatable with absolute path') self.init(testdir_abs) check_pcfile('libsimple.pc', relocatable=True, levels=3) @@ -2317,7 +2317,7 @@ def get_opt(): self.assertDictEqual(original, expected) def test_executable_names(self): - testdir = os.path.join(self.unit_test_dir, '121 executable suffix') + testdir = os.path.join(self.unit_test_dir, '122 executable suffix') self.init(testdir) self.build() exe1 = os.path.join(self.builddir, 'foo' + exe_suffix) @@ -2408,7 +2408,7 @@ def test_options_with_choices_changing(self) -> None: def test_options_listed_in_build_options(self) -> None: """Detect when changed options become listed in build options.""" - testdir = os.path.join(self.unit_test_dir, '113 list build options') + testdir = os.path.join(self.unit_test_dir, '114 list build options') out = self.init(testdir) for line in out.splitlines(): @@ -2727,7 +2727,7 @@ def test_check_module_linking(self): self.assertIn(msg, out) def test_mixed_language_linker_check(self): - testdir = os.path.join(self.unit_test_dir, '96 compiler.links file arg') + testdir = os.path.join(self.unit_test_dir, '97 compiler.links file arg') self.init(testdir) cmds = self.get_meson_log_compiler_checks() self.assertEqual(len(cmds), 5) @@ -3188,7 +3188,7 @@ def test_pkg_config_libdir(self): @skipIf(is_windows(), 'POSIX only') def test_python_build_config_extensions(self): testdir = os.path.join(self.unit_test_dir, - '126 python extension') + '127 python extension') VERSION_INFO_KEYS = ('major', 'minor', 'micro', 'releaselevel', 'serial') EXTENSION_SUFFIX = '.extension-suffix.so' @@ -3479,7 +3479,7 @@ def test_introspection_target_native_subproject(self): self.meson_cross_files = [os.path.join(self.builddir, "crossfile")] self.new_builddir() - testdir = os.path.join(self.unit_test_dir, '136 native subproject introspect') + testdir = os.path.join(self.unit_test_dir, '137 native subproject introspect') self.init(testdir) res = self.introspect('--targets') @@ -4663,7 +4663,7 @@ def test_custom_target_index_as_test_prereq(self): if self.backend is not Backend.ninja: raise SkipTest('ninja backend needed for "meson test" to build test dependencies') - testdir = os.path.join(self.unit_test_dir, '132 custom target index test') + testdir = os.path.join(self.unit_test_dir, '133 custom target index test') self.init(testdir) self.run_tests() @@ -4774,7 +4774,7 @@ def test_build_b_options(self) -> None: self.init(srcdir, extra_args=['-Dbuild.b_lto=true']) def test_install_skip_subprojects(self): - testdir = os.path.join(self.unit_test_dir, '91 install skip subprojects') + testdir = os.path.join(self.unit_test_dir, '92 install skip subprojects') self.init(testdir) self.build() @@ -4821,7 +4821,7 @@ def check_installed_files(extra_args, expected): check_installed_files(['--skip-subprojects', 'another'], all_expected) def test_adding_subproject_to_configure_project(self) -> None: - srcdir = os.path.join(self.unit_test_dir, '92 new subproject in configured project') + srcdir = os.path.join(self.unit_test_dir, '93 new subproject in configured project') self.init(srcdir) self.build() self.setconf('-Duse-sub=true') @@ -4874,7 +4874,7 @@ def test_clang_format_check(self): if self.backend is not Backend.ninja: raise SkipTest(f'Skipping clang-format tests with {self.backend.name} backend') - testdir = os.path.join(self.unit_test_dir, '93 clangformat') + testdir = os.path.join(self.unit_test_dir, '94 clangformat') newdir = os.path.join(self.builddir, 'testdir') shutil.copytree(testdir, newdir) self.new_builddir() @@ -4899,7 +4899,7 @@ def test_clang_format_check(self): self.build('clang-format-check') def test_custom_target_implicit_include(self): - testdir = os.path.join(self.unit_test_dir, '94 custominc') + testdir = os.path.join(self.unit_test_dir, '95 custominc') self.init(testdir) self.build() compdb = self.get_compdb() @@ -4935,7 +4935,7 @@ def test_env_flags_to_linker(self) -> None: self.assertEqual(sorted(link_args), sorted(['-flto'])) def test_install_tag(self) -> None: - testdir = os.path.join(self.unit_test_dir, '98 install all targets') + testdir = os.path.join(self.unit_test_dir, '99 install all targets') self.init(testdir) self.build() @@ -5106,7 +5106,7 @@ def test_install_script_dry_run(self): def test_introspect_install_plan(self): - testdir = os.path.join(self.unit_test_dir, '98 install all targets') + testdir = os.path.join(self.unit_test_dir, '99 install all targets') introfile = os.path.join(self.builddir, 'meson-info', 'intro-install_plan.json') self.init(testdir) self.assertPathExists(introfile) @@ -5441,7 +5441,7 @@ def test_rust_rlib_linkage(self) -> None: }} ''') - testdir = os.path.join(self.unit_test_dir, '101 rlib linkage') + testdir = os.path.join(self.unit_test_dir, '102 rlib linkage') gen_file = os.path.join(testdir, 'lib.rs') with open(gen_file, 'w', encoding='utf-8') as f: f.write(template.format(0)) @@ -5489,7 +5489,7 @@ def test_bindgen_drops_invalid(self) -> None: return def test_custom_target_name(self): - testdir = os.path.join(self.unit_test_dir, '99 custom target name') + testdir = os.path.join(self.unit_test_dir, '100 custom target name') self.init(testdir) out = self.build() if self.backend is Backend.ninja: @@ -5497,7 +5497,7 @@ def test_custom_target_name(self): self.assertIn('Generating subdir/file.txt with a custom command', out) def test_symlinked_subproject(self): - testdir = os.path.join(self.unit_test_dir, '107 subproject symlink') + testdir = os.path.join(self.unit_test_dir, '108 subproject symlink') subproject_dir = os.path.join(testdir, 'subprojects') subproject = os.path.join(testdir, 'symlinked_subproject') symlinked_subproject = os.path.join(testdir, 'subprojects', 'symlinked_subproject') @@ -5513,7 +5513,7 @@ def test_symlinked_subproject(self): self.build() def test_configure_same_noop(self): - testdir = os.path.join(self.unit_test_dir, '109 configure same noop') + testdir = os.path.join(self.unit_test_dir, '110 configure same noop') args = [ '-Dstring=val', '-Dboolean=true', @@ -5552,7 +5552,7 @@ def test_configure_same_noop(self): def test_link_language_promotion(self): if self.backend is Backend.vs: raise SkipTest('target introspection is lacking linker details') - testdir = os.path.join(self.unit_test_dir, '134 promote link_language') + testdir = os.path.join(self.unit_test_dir, '135 promote link_language') self.init(testdir) cintrospection = self.introspect('--compilers') clinker = cintrospection['host']['c']['linker_exelist'] @@ -5565,7 +5565,7 @@ def test_link_language_promotion(self): def __test_multi_stds(self, test_c: bool = True, test_objc: bool = False) -> None: assert test_c or test_objc, 'must test something' - testdir = os.path.join(self.unit_test_dir, '115 c cpp stds') + testdir = os.path.join(self.unit_test_dir, '116 c cpp stds') self.init(testdir, extra_args=[f'-Dwith-c={str(test_c).lower()}', f'-Dwith-objc={str(test_objc).lower()}']) # Invalid values should fail whatever compiler we have @@ -5624,7 +5624,7 @@ def test_c_cpp_objc_objcpp_stds(self) -> None: self.__test_multi_stds(test_objc=True) def test_slice(self): - testdir = os.path.join(self.unit_test_dir, '128 test slice') + testdir = os.path.join(self.unit_test_dir, '129 test slice') self.init(testdir) self.build() @@ -5664,7 +5664,7 @@ def test_rsp_support(self): self.assertEqual(cc.linker.get_accepts_rsp(), has_rsp) def test_nonexisting_bargs(self): - testdir = os.path.join(self.unit_test_dir, '116 empty project') + testdir = os.path.join(self.unit_test_dir, '117 empty project') args = ['-Db_ndebug=if_release'] self.init(testdir, extra_args=args) @@ -5674,7 +5674,7 @@ def test_wipe_with_args(self): self.init(testdir, extra_args=['--wipe']) def test_interactive_tap(self): - testdir = os.path.join(self.unit_test_dir, '124 interactive tap') + testdir = os.path.join(self.unit_test_dir, '125 interactive tap') self.init(testdir, extra_args=['--wrap-mode=forcefallback']) output = self._run(self.mtest_command + ['--interactive']) self.assertRegex(output, r'Ok:\s*0') diff --git a/unittests/linuxliketests.py b/unittests/linuxliketests.py index 2288e0d693aa..0863a041ded9 100644 --- a/unittests/linuxliketests.py +++ b/unittests/linuxliketests.py @@ -1414,7 +1414,7 @@ def test_link_arg_fullname(self): see: https://github.com/mesonbuild/meson/issues/9000 https://stackoverflow.com/questions/48532868/gcc-library-option-with-a-colon-llibevent-a ''' - testdir = os.path.join(self.unit_test_dir, '97 link full name','libtestprovider') + testdir = os.path.join(self.unit_test_dir, '98 link full name','libtestprovider') oldprefix = self.prefix # install into installdir without using DESTDIR installdir = self.installdir @@ -1427,7 +1427,7 @@ def test_link_arg_fullname(self): self.new_builddir() env = {'LIBRARY_PATH': os.path.join(installdir, self.libdir), 'PKG_CONFIG_PATH': _prepend_pkg_config_path(os.path.join(installdir, self.libdir, 'pkgconfig'))} - testdir = os.path.join(self.unit_test_dir, '97 link full name','proguser') + testdir = os.path.join(self.unit_test_dir, '98 link full name','proguser') self.init(testdir,override_envvars=env) # test for link with full path @@ -1832,7 +1832,7 @@ def test_cmake_multilib(self): @skipUnless(is_linux() or is_osx(), 'Test only applicable to Linux and macOS') def test_install_strip(self): - testdir = os.path.join(self.unit_test_dir, '103 strip') + testdir = os.path.join(self.unit_test_dir, '104 strip') self.init(testdir) self.build() @@ -1879,7 +1879,7 @@ def test_isystem_default_removal_with_symlink(self): self.assertFalse(cpp.compiler_args([f'-isystem{symlink}' for symlink in default_symlinks]).to_native()) def test_freezing(self): - testdir = os.path.join(self.unit_test_dir, '110 freeze') + testdir = os.path.join(self.unit_test_dir, '111 freeze') self.init(testdir) self.build() with self.assertRaises(subprocess.CalledProcessError) as e: @@ -1888,7 +1888,7 @@ def test_freezing(self): @skipUnless(is_linux(), "Ninja file differs on different platforms") def test_complex_link_cases(self): - testdir = os.path.join(self.unit_test_dir, '114 complex link cases') + testdir = os.path.join(self.unit_test_dir, '115 complex link cases') self.init(testdir) self.build() with open(os.path.join(self.builddir, 'build.ninja'), encoding='utf-8') as f: @@ -1909,11 +1909,11 @@ def test_complex_link_cases(self): self.assertIn('build t13-e1: c_LINKER t13-e1.p/main.c.o | libt12-s1.a libt13-s3.a\n', content) def test_top_options_in_sp(self): - testdir = os.path.join(self.unit_test_dir, '127 pkgsubproj') + testdir = os.path.join(self.unit_test_dir, '128 pkgsubproj') self.init(testdir) def test_unreadable_dir_in_declare_dep(self): - testdir = os.path.join(self.unit_test_dir, '125 declare_dep var') + testdir = os.path.join(self.unit_test_dir, '126 declare_dep var') tmpdir = Path(tempfile.mkdtemp()) self.addCleanup(windows_proof_rmtree, tmpdir) declaredepdir = tmpdir / 'test' @@ -1935,7 +1935,7 @@ def test_persp_options(self): if self.backend is not Backend.ninja: raise SkipTest(f'{self.backend.name!r} backend can\'t install files') - testdir = os.path.join(self.unit_test_dir, '122 persp options') + testdir = os.path.join(self.unit_test_dir, '123 persp options') with self.subTest('init'): self.init(testdir, extra_args='-Doptimization=1') @@ -2020,7 +2020,7 @@ def test_rust_staticlib_rlib_deps(self): self.assertNotIn('liblib.rlib', param) def test_sanitizers(self): - testdir = os.path.join(self.unit_test_dir, '129 sanitizers') + testdir = os.path.join(self.unit_test_dir, '130 sanitizers') with self.subTest('no b_sanitize value'): try: diff --git a/unittests/machinefiletests.py b/unittests/machinefiletests.py index b20b8172f9e4..f8d1c7414184 100644 --- a/unittests/machinefiletests.py +++ b/unittests/machinefiletests.py @@ -423,7 +423,7 @@ def test_java_compiler(self): def test_java_classpath(self): if self.backend is not Backend.ninja: raise SkipTest('Jar is only supported with Ninja') - testdir = os.path.join(self.unit_test_dir, '112 classpath') + testdir = os.path.join(self.unit_test_dir, '113 classpath') self.init(testdir) self.build() one_build_path = get_classpath(os.path.join(self.builddir, 'one.jar')) @@ -1015,13 +1015,13 @@ def test_for_build_env_vars(self) -> None: self.assertEqual(found, 2, 'Did not find all sections.') def test_external_property_build_machine_native(self): - testdir = os.path.join(self.unit_test_dir, '137 external property nonexisting') + testdir = os.path.join(self.unit_test_dir, '138 external property nonexisting') self.meson_native_files = [os.path.join(testdir, "propfile")] out = self.init(testdir, allow_fail=True, extra_args=['-Dnative=true']) self.assertIn('Unknown property for host machine: nonexisting', out) def test_external_property_host_machine_native(self): - testdir = os.path.join(self.unit_test_dir, '137 external property nonexisting') + testdir = os.path.join(self.unit_test_dir, '138 external property nonexisting') self.meson_native_files = [os.path.join(testdir, "propfile")] out = self.init(testdir, allow_fail=True, extra_args=['-Dnative=false']) self.assertIn('Unknown property for host machine: nonexisting', out) @@ -1040,14 +1040,14 @@ def test_external_property_build_machine_cross(self): crossfile = os.path.join(self.builddir, "crossfile") self.new_builddir() - testdir = os.path.join(self.unit_test_dir, '137 external property nonexisting') + testdir = os.path.join(self.unit_test_dir, '138 external property nonexisting') self.meson_native_files = [nativefile, os.path.join(testdir, "propfile")] self.meson_cross_files = [crossfile] out = self.init(testdir, allow_fail=True, extra_args=['-Dnative=true']) self.assertIn('Unknown property for build machine: nonexisting', out) self.new_builddir() - testdir = os.path.join(self.unit_test_dir, '137 external property nonexisting') + testdir = os.path.join(self.unit_test_dir, '138 external property nonexisting') self.meson_native_files = [nativefile] self.meson_cross_files = [crossfile, os.path.join(testdir, "propfile")] out = self.init(testdir, allow_fail=True, extra_args=['-Dnative=false']) @@ -1072,7 +1072,7 @@ def test_project_options_native_only(self) -> None: @skip_if_not_language('rust') @skipIfNoExecutable('bindgen') def test_bindgen_finds_target_in_clang_options(self) -> None: - testcase = os.path.join(self.unit_test_dir, '135 minimal bindgen') + testcase = os.path.join(self.unit_test_dir, '136 minimal bindgen') def check_target(include: T.Optional[str], exclude: T.Optional[str] = None) -> None: configuration = self.introspect('--targets') diff --git a/unittests/platformagnostictests.py b/unittests/platformagnostictests.py index fe565cffb851..6e27310af4af 100644 --- a/unittests/platformagnostictests.py +++ b/unittests/platformagnostictests.py @@ -33,7 +33,7 @@ def test_relative_find_program(self): Tests that find_program() with a relative path does not find the program in current workdir. ''' - testdir = os.path.join(self.unit_test_dir, '100 relative find program') + testdir = os.path.join(self.unit_test_dir, '101 relative find program') self.init(testdir, workdir=testdir) def test_invalid_option_names(self): @@ -92,11 +92,11 @@ def write_file(code: str): interp.process, fname) def test_python_dependency_without_pkgconfig(self): - testdir = os.path.join(self.unit_test_dir, '102 python without pkgconfig') + testdir = os.path.join(self.unit_test_dir, '103 python without pkgconfig') self.init(testdir, override_envvars={'PKG_CONFIG': 'notfound'}) def test_vala_target_with_internal_glib(self): - testdir = os.path.join(self.unit_test_dir, '131 vala internal glib') + testdir = os.path.join(self.unit_test_dir, '132 vala internal glib') for run in [{ 'version': '2.84.4', 'expected': '2.84'}, { 'version': '2.85.2', 'expected': '2.84' }]: self.new_builddir() self.init(testdir, extra_args=[f'-Dglib-version={run["version"]}']) @@ -113,7 +113,7 @@ def test_vala_target_with_internal_glib(self): self.skipTest('Current backend does not produce introspection data') def test_debug_function_outputs_to_meson_log(self): - testdir = os.path.join(self.unit_test_dir, '104 debug function') + testdir = os.path.join(self.unit_test_dir, '105 debug function') log_msg = 'This is an example debug output, should only end up in debug log' output = self.init(testdir) @@ -125,7 +125,7 @@ def test_debug_function_outputs_to_meson_log(self): self.assertIn(log_msg, mesonlog) def test_new_subproject_reconfigure(self): - testdir = os.path.join(self.unit_test_dir, '108 new subproject on reconfigure') + testdir = os.path.join(self.unit_test_dir, '109 new subproject on reconfigure') self.init(testdir) self.build() @@ -288,7 +288,7 @@ def test_setup_loaded_modules(self): thing to do as new features are added, but keeping track of them is good. ''' - testdir = os.path.join(self.unit_test_dir, '116 empty project') + testdir = os.path.join(self.unit_test_dir, '117 empty project') self.init(testdir) self._run(self.meson_command + ['--internal', 'regenerate', '--profile-self', testdir, self.builddir]) @@ -303,7 +303,7 @@ def test_setup_loaded_modules(self): def test_meson_package_cache_dir(self): # Copy testdir into temporary directory to not pollute meson source tree. - testdir = os.path.join(self.unit_test_dir, '118 meson package cache dir') + testdir = os.path.join(self.unit_test_dir, '119 meson package cache dir') srcdir = os.path.join(self.builddir, 'srctree') shutil.copytree(testdir, srcdir) builddir = os.path.join(srcdir, '_build') @@ -312,7 +312,7 @@ def test_meson_package_cache_dir(self): def test_cmake_openssl_not_found_bug(self): """Issue #12098""" - testdir = os.path.join(self.unit_test_dir, '119 openssl cmake bug') + testdir = os.path.join(self.unit_test_dir, '120 openssl cmake bug') self.meson_native_files.append(os.path.join(testdir, 'nativefile.ini')) out = self.init(testdir, allow_fail=True) self.assertNotIn('Unhandled python exception', out) @@ -422,7 +422,7 @@ def test_error_configuring_subdir(self): self.assertIn(f'Did you mean to run meson from the directory: "{testdir}"?', out) def test_reconfigure_base_options(self): - testdir = os.path.join(self.unit_test_dir, '123 reconfigure base options') + testdir = os.path.join(self.unit_test_dir, '124 reconfigure base options') out = self.init(testdir, extra_args=['-Db_ndebug=true']) self.assertIn('\nMessage: b_ndebug: true\n', out) self.assertIn('\nMessage: c_std: c89\n', out) @@ -548,7 +548,7 @@ def test_configure_new_option_subproject(self) -> None: self.assertEqual(self.getconf('subproject:new_option'), True) def test_mtest_rebuild_deps(self): - testdir = os.path.join(self.unit_test_dir, '106 underspecified mtest') + testdir = os.path.join(self.unit_test_dir, '107 underspecified mtest') self.init(testdir) with self.assertRaises(subprocess.CalledProcessError): diff --git a/unittests/rewritetests.py b/unittests/rewritetests.py index 4bf8283ec842..1d90847842f6 100644 --- a/unittests/rewritetests.py +++ b/unittests/rewritetests.py @@ -479,7 +479,7 @@ def test_tricky_dataflow(self): self.assertEqualIgnoreOrder(out, expected) def test_raw_printer_is_idempotent(self): - test_path = Path(self.unit_test_dir, '120 rewrite') + test_path = Path(self.unit_test_dir, '121 rewrite') meson_build_file = test_path / 'meson.build' # original_contents = meson_build_file.read_bytes() original_contents = meson_build_file.read_text(encoding='utf-8') @@ -533,6 +533,6 @@ def node_to_str(node): @skip_if_not_language('nasm') def test_nasm(self) -> None: - srcdir = os.path.join(self.unit_test_dir, '133 nasm language only') + srcdir = os.path.join(self.unit_test_dir, '134 nasm language only') self.rewrite_raw(srcdir, ['kwargs', 'info', 'project', '/']) diff --git a/unittests/windowstests.py b/unittests/windowstests.py index 18ea7d38ac6b..a6f9e264ffbb 100644 --- a/unittests/windowstests.py +++ b/unittests/windowstests.py @@ -185,7 +185,7 @@ def test_genvslite(self): if self.backend is not Backend.ninja: raise SkipTest('Test only applies when using the Ninja backend') - testdir = os.path.join(self.unit_test_dir, '117 genvslite') + testdir = os.path.join(self.unit_test_dir, '118 genvslite') env = get_fake_env(testdir, self.builddir, self.prefix) cc = detect_c_compiler(env, MachineChoice.HOST) From 0334fa4c86f5693006903f06980926d787369fd3 Mon Sep 17 00:00:00 2001 From: KO Myung-Hun Date: Sun, 5 Jul 2026 15:59:12 +0900 Subject: [PATCH 591/681] symbolextractor: check the validity of stderr output on OS/2 Stderr output may be 'NoneType'. --- mesonbuild/scripts/symbolextractor.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mesonbuild/scripts/symbolextractor.py b/mesonbuild/scripts/symbolextractor.py index ea03dc42119e..efe46dcd6b13 100644 --- a/mesonbuild/scripts/symbolextractor.py +++ b/mesonbuild/scripts/symbolextractor.py @@ -273,14 +273,16 @@ def os2_syms(impfilename: str, outfilename: str) -> None: result = [x for x in output.split('\n') if ' E ' in x] write_if_changed('\n'.join(result) + '\n', outfilename) return - all_stderr += e + if e: + all_stderr += e # Next try listomf.exe for omf .lib output, e = call_tool_nowarn(get_tool('listomf') + ['-d', impfilename]) if output: result = [x for x in output.split('\n') if ' IMPDEF ' in x] write_if_changed('\n'.join(result) + '\n', outfilename) return - all_stderr += e + if e: + all_stderr += e print_tool_warning(['nm', 'listomf'], 'do not work or were not found', all_stderr) def gen_symbols(libfilename: str, impfilename: str, outfilename: str, cross_host: str) -> None: From da76d46d9b54001710132326c7b0326b54460f6b Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 2 Jun 2026 10:18:58 -0700 Subject: [PATCH 592/681] tests: Set encoding for several `open()` calls --- test cases/common/277 generator custom_tgt subdir/meson.build | 2 +- test cases/common/278 custom target private dir/meson.build | 2 +- .../windows/12 resources with custom targets/res/gen-res.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test cases/common/277 generator custom_tgt subdir/meson.build b/test cases/common/277 generator custom_tgt subdir/meson.build index 38e9635b6f15..3648a82467f3 100644 --- a/test cases/common/277 generator custom_tgt subdir/meson.build +++ b/test cases/common/277 generator custom_tgt subdir/meson.build @@ -3,7 +3,7 @@ project('276 generator custom_tgt subdir') python = find_program('python3', required: true) args = [ '-c', - 'import sys; open(sys.argv[1], "w")', + 'import sys; open(sys.argv[1], "w", encoding="utf-8")', '@OUTPUT0@', ] diff --git a/test cases/common/278 custom target private dir/meson.build b/test cases/common/278 custom target private dir/meson.build index dc48d1affd31..017fb6da5ec8 100644 --- a/test cases/common/278 custom target private dir/meson.build +++ b/test cases/common/278 custom target private dir/meson.build @@ -7,7 +7,7 @@ custom_target( command: [ python, '-c', - 'import os, sys; os.chdir(sys.argv[1]); open(sys.argv[2], "w")', + 'import os, sys; os.chdir(sys.argv[1]); open(sys.argv[2], "w", encoding="utf-8")', '@PRIVATE_DIR@', '@OUTPUT@', ], diff --git a/test cases/windows/12 resources with custom targets/res/gen-res.py b/test cases/windows/12 resources with custom targets/res/gen-res.py index 71f9d00dec2f..b44205045e26 100755 --- a/test cases/windows/12 resources with custom targets/res/gen-res.py +++ b/test cases/windows/12 resources with custom targets/res/gen-res.py @@ -2,5 +2,5 @@ import sys -with open(sys.argv[1]) as infile, open(sys.argv[2], 'w', encoding='utf-8') as outfile: +with open(sys.argv[1], encoding='utf-8') as infile, open(sys.argv[2], 'w', encoding='utf-8') as outfile: outfile.write(infile.read().format(icon=sys.argv[3])) From 6a0453489759c32507b9b6045d5544832f227fbb Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 15 Apr 2024 10:54:47 -0700 Subject: [PATCH 593/681] rust: Add wrapper for cbindgen This is pretty minimal, but does work at least for basic tests. It also handles structured_sources. Has been tested against Mesa's use of cbindgen. Fixes: #13092 --- docs/markdown/Rust-module.md | 39 ++++++ docs/markdown/snippets/rust-cbindgen.md | 5 + mesonbuild/modules/rust.py | 142 ++++++++++++++++++++- test cases/rust/44 cbindgen/config.toml | 159 ++++++++++++++++++++++++ test cases/rust/44 cbindgen/lib.rs | 15 +++ test cases/rust/44 cbindgen/lib2.rs | 12 ++ test cases/rust/44 cbindgen/meson.build | 51 ++++++++ test cases/rust/44 cbindgen/sub.rs | 10 ++ test cases/rust/44 cbindgen/test1.c | 20 +++ test cases/rust/44 cbindgen/test1.cpp | 16 +++ 10 files changed, 466 insertions(+), 3 deletions(-) create mode 100644 docs/markdown/snippets/rust-cbindgen.md create mode 100644 test cases/rust/44 cbindgen/config.toml create mode 100644 test cases/rust/44 cbindgen/lib.rs create mode 100644 test cases/rust/44 cbindgen/lib2.rs create mode 100644 test cases/rust/44 cbindgen/meson.build create mode 100644 test cases/rust/44 cbindgen/sub.rs create mode 100644 test cases/rust/44 cbindgen/test1.c create mode 100644 test cases/rust/44 cbindgen/test1.cpp diff --git a/docs/markdown/Rust-module.md b/docs/markdown/Rust-module.md index 2b268e051ddc..54e93b4c5b78 100644 --- a/docs/markdown/Rust-module.md +++ b/docs/markdown/Rust-module.md @@ -143,6 +143,45 @@ were never turned on by Meson. bindgen_clang_arguments = ['--target', 'x86_64-linux-gnu'] ``` +## cbindgen() + +*Since 1.12.0* + +```meson +file_h = rustmod.cbindgen('infile.rs', 'outfile.h', config : 'cbindgen.toml') +``` + +This function wraps cbindgen to simplify creating C bindings for Rust targets. +It has several advantages over invoking a `custom_target` directly: + + - Correctly handles depfiles, adding them when supported + - Sets the profile to `debug` when Meson is configured with -Ddebug=true + - Automatically passes some useful options for use in Meson, such as `--quiet` + and `--cpp-compat` + - Handles structured_sources correctly + - Automatically detect language based on file extension (c or c++ only), + +The majority of the configuration should be handled inside the `config.toml` +file. + +It takes the following positional arguments: + + - `infile`: the root rust file to bind, or a [[@structured_src]] instance + - `outfile`: the header to generate + +It takes the following keyword arguments + + - `config`: The Path to a configuration toml. May be a File, str, or CustomTarget. + Customarily, this is called `cbindgen.toml`, but Meson does not require this. + - `language`: The language to bind for. If unset, Meson will choose a language + based on the file extension of the output file. Currently, auto detection only + works for C and C++. May be one of: `c`, `cpp`, `cython`. + - `depends`: An array of CustomTargets that this target depends on + - `depend_files`: An array of files that this target depends on + - `install`: A boolean controlling whether to install the generated header + - `install_dir`: Where to install the header. This is required if `install` is true. + + ### compiler_target() *Since 1.11.0* diff --git a/docs/markdown/snippets/rust-cbindgen.md b/docs/markdown/snippets/rust-cbindgen.md new file mode 100644 index 000000000000..f5fb0eb4ea8d --- /dev/null +++ b/docs/markdown/snippets/rust-cbindgen.md @@ -0,0 +1,5 @@ +## The Rust module includes a basic wrapper for cbindgen + +This will correctly track the config.toml, and generate a rust source from the +marked C files. This handles structured_sources correctly, and also handles +depfile generation transparently. diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index f949205fcc6e..6fe9e0b6982c 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -21,8 +21,8 @@ from ..interpreter.decorators import apply_machine_map from ..interpreter.type_checking import ( DEPENDENCIES_KW, LINK_WITH_KW, LINK_WHOLE_KW, SHARED_LIB_KWS, TEST_KWS, TEST_KWS_NO_ARGS, - NATIVE_KW, OUTPUT_KW, INCLUDE_DIRECTORIES, SOURCES_VARARGS, NoneType, in_set_validator, - EXECUTABLE_KWS, LIBRARY_KWS, SHARED_MOD_KWS, _BASE_LANG_KW + OUTPUT_KW, INCLUDE_DIRECTORIES, SOURCES_VARARGS, NATIVE_KW, NoneType, in_set_validator, + EXECUTABLE_KWS, LIBRARY_KWS, SHARED_MOD_KWS, _BASE_LANG_KW, DEPEND_FILES_KW, INSTALL_DIR_KW, INSTALL_KW, ) from ..interpreterbase import ContainerTypeInfo, InterpreterException, KwargInfo, typed_kwargs, typed_pos_args, noKwargs, noPosargs from ..interpreter.interpreterobjects import Doctest @@ -45,8 +45,9 @@ from ..interpreterbase import TYPE_kwargs from ..programs import Program from ..interpreter.type_checking import SourcesVarargsType + from ..utils.universal import FileOrString - from typing_extensions import TypedDict, Literal + from typing_extensions import Literal, TypedDict ArgsType = T.TypeVar('ArgsType') @@ -73,6 +74,15 @@ class FuncBindgen(TypedDict): language: T.Optional[Literal['c', 'cpp']] bindgen_version: T.List[str] + class FuncCBindgen(TypedDict): + + config: str | File | CustomTarget | CustomTargetIndex + language: T.Literal['c', 'cpp', 'cython'] | None + depends: list[TargetDepends] + depend_files: list[FileOrString] + install: bool + install_dir: str | None + class FuncSubproject(TypedDict): native: MachineChoice @@ -583,6 +593,12 @@ def dependency_method(self, state: ModuleState, args: T.List, kwargs: FuncDepend return state.overridden_dependency(depname, for_machine=self.for_machine) +def _cbindgen_config_validator(val: str) -> T.Optional[str]: + if os.path.splitext(val)[1] != '.toml': + return 'config file must be a .toml file' + return None + + class RustModule(ExtensionModule): """A module that holds helper functions for rust.""" @@ -600,6 +616,10 @@ def __init__(self, interpreter: Interpreter) -> None: else: self._bindgen_rust_target = None self._bindgen_set_std = False + + self._cbindgen_bin: Program | None = None + self._cbindgen_has_depfile = False + self.methods.update({ 'test': self.test, 'doctest': self.doctest, @@ -608,6 +628,7 @@ def __init__(self, interpreter: Interpreter) -> None: 'proc_macro': self.proc_macro, 'to_system_dependency': self.to_system_dependency, 'workspace': self.workspace, + 'cbindgen': self.cbindgen, }) def test_common(self, funcname: str, state: ModuleState, args: T.Tuple[str, BuildTarget], kwargs: FuncRustTest) -> T.Tuple[Executable, _kwargs.FuncTest]: @@ -1100,6 +1121,121 @@ def workspace(self, state: ModuleState, args: T.List, kwargs: FuncWorkspace) -> return RustWorkspace(self.interpreter, ws) + @FeatureNew('rust.cbindgen', '1.12.0') + @typed_pos_args('rust.cbindgen', (str, File, CustomTargetIndex, CustomTarget, StructuredSources), str) + @typed_kwargs( + 'rust.cbindgen', + KwargInfo('config', (str, File, CustomTarget, CustomTargetIndex), required=True, validator=_cbindgen_config_validator), + KwargInfo( + 'language', + (str, NoneType), + validator=in_set_validator({'c', 'cpp', 'cython'}), + ), + KwargInfo( + 'depends', + ContainerTypeInfo(list, (CustomTarget, CustomTargetIndex)), + default=[], + listify=True, + ), + DEPEND_FILES_KW, + INSTALL_KW, + INSTALL_DIR_KW, + ) + def cbindgen(self, state: ModuleState, + args: tuple[FileOrString | CustomTarget | CustomTargetIndex | StructuredSources, str], + kwargs: FuncCBindgen) -> ModuleReturnValue: + # TODO: should we allow GeneratedList here? + + if kwargs['install'] and not kwargs['install_dir']: + raise InterpreterException.from_node('cbindgen: When `install` is true `install_dir` must be set', + node=state.current_node) + + if self._cbindgen_bin is None: + self._cbindgen_bin = state.find_program('cbindgen') + self._cbindgen_has_depfile = mesonlib.version_compare( + self._cbindgen_bin.get_version(self.interpreter), '>= 0.25') + + _infile, outfile = args + infile = self.interpreter.source_strings_to_files([_infile])[0] + depend_files = self.interpreter.source_strings_to_files(kwargs['depend_files']) + depends = kwargs['depends'].copy() + + if isinstance(infile, StructuredSources): + infile, *_depends = infile.as_list() + if isinstance(infile, GeneratedList): + raise MesonException.from_node( + 'Using a GeneratedList as the main input for cbindgen is unsupported', + node=state.current_node) + for d in _depends: + if isinstance(d, File): + depend_files.append(d) + else: + depends.append(d) + + if isinstance(infile, File): + name = infile.fname + else: + if len(infile.get_outputs()) != 1: + raise mesonlib.MesonException.from_node( + 'Cannot pass a custom_target generating more than one output to rust.cbindgen, use custom_target[index] to select the output to generate bindings for', + node=state.current_node) + name = infile.get_outputs()[0] + + if os.path.dirname(outfile): + raise InvalidArguments.from_node( + 'outfile name must not contain a path segment', node=state.current_node) + + # Detect langauge from output file extension + language = kwargs['language'] + if language is None: + ext = os.path.splitext(outfile)[1][1:] + if ext in lang_suffixes['cpp']: + language = 'cpp' + elif ext == 'h': + language = 'c' + else: + raise InterpreterException.from_node( + f'Unknown file type extension for: {outfile}', node=state.current_node) + + # Convert Meson's `cpp` to cbindgen's `c++` + if language == 'cpp': + language = 'c++' + + # Set the --profile flag based on meson's debug option + debug = state.get_option('debug') + assert isinstance(debug, bool), 'for mypy' + + command: list[str | Program] = [ + self._cbindgen_bin, '--output', '@OUTPUT@', '--config', + '@INPUT0@', '--quiet', '--lang', language, + '--profile', 'debug' if debug else 'release', + ] + if language == 'c': + command.append('--cpp-compat') + if self._cbindgen_has_depfile: + command.extend(['--depfile', '@DEPFILE@']) + command.extend(['--', '@INPUT1@']) # must be last + + config_file = self.interpreter.source_strings_to_files([kwargs['config']]) + + target = CustomTarget( + f'rustmod-cbindgen-{outfile}', + state.subdir, + state.environment, + command, + [config_file[0], infile], + [outfile], + state.current_build_project, + depfile=f'{name}.d', + depend_files=depend_files, + extra_depends=depends, + install=kwargs['install'], + install_dir=[kwargs['install_dir']], + install_tag=['devel'], + ) + + return ModuleReturnValue(target, [target]) + def initialize(interp: Interpreter) -> RustModule: return RustModule(interp) diff --git a/test cases/rust/44 cbindgen/config.toml b/test cases/rust/44 cbindgen/config.toml new file mode 100644 index 000000000000..2c273f64faec --- /dev/null +++ b/test cases/rust/44 cbindgen/config.toml @@ -0,0 +1,159 @@ +# This is a template cbindgen.toml file with all of the default values. +# Some values are commented out because their absence is the real default. +# +# See https://github.com/mozilla/cbindgen/blob/master/docs.md#cbindgentoml +# for detailed documentation of every option here. + + + +language = "C++" + + + +############## Options for Wrapping the Contents of the Header ################# + +# header = "/* Text to put at the beginning of the generated file. Probably a license. */" +# trailer = "/* Text to put at the end of the generated file */" +# include_guard = "my_bindings_h" +pragma_once = true +# autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */" +include_version = false +# namespace = "my_namespace" +namespaces = [] +using_namespaces = [] +sys_includes = [] +includes = [] +no_includes = false +# cpp_compat = true +after_includes = "" + + + + +############################ Code Style Options ################################ + +braces = "SameLine" +line_length = 100 +tab_width = 2 +documentation = true +documentation_style = "auto" +documentation_length = "full" +line_endings = "LF" # also "CR", "CRLF", "Native" + + + + +############################# Codegen Options ################################## + +style = "both" +sort_by = "Name" # default for `fn.sort_by` and `const.sort_by` +usize_is_size_t = true + + + +[defines] +# "target_os = freebsd" = "DEFINE_FREEBSD" +# "feature = serde" = "DEFINE_SERDE" + + + +[export] +include = [] +exclude = [] +# prefix = "CAPI_" +item_types = [] +renaming_overrides_prefixing = false + + + +[export.rename] + + + +[export.body] + + +[export.mangle] + + +[fn] +rename_args = "None" +# must_use = "MUST_USE_FUNC" +# deprecated = "DEPRECATED_FUNC" +# deprecated_with_note = "DEPRECATED_FUNC_WITH_NOTE" +# no_return = "NO_RETURN" +# prefix = "START_FUNC" +# postfix = "END_FUNC" +args = "auto" +sort_by = "Name" + + + + +[struct] +rename_fields = "None" +# must_use = "MUST_USE_STRUCT" +# deprecated = "DEPRECATED_STRUCT" +# deprecated_with_note = "DEPRECATED_STRUCT_WITH_NOTE" +derive_constructor = false +derive_eq = false +derive_neq = false +derive_lt = false +derive_lte = false +derive_gt = false +derive_gte = false + + + + +[enum] +rename_variants = "None" +# must_use = "MUST_USE_ENUM" +# deprecated = "DEPRECATED_ENUM" +# deprecated_with_note = "DEPRECATED_ENUM_WITH_NOTE" +add_sentinel = false +prefix_with_name = false +derive_helper_methods = false +derive_const_casts = false +derive_mut_casts = false +# cast_assert_name = "ASSERT" +derive_tagged_enum_destructor = false +derive_tagged_enum_copy_constructor = false +enum_class = true +private_default_tagged_enum_constructor = false + + + + +[const] +allow_static_const = true +allow_constexpr = false +sort_by = "Name" + + + + +[macro_expansion] +bitflags = false + + + + + + +############## Options for How Your Rust library Should Be Parsed ############## + +[parse] +parse_deps = false +# include = [] +exclude = [] +clean = false +extra_bindings = [] + + + +[parse.expand] +crates = [] +all_features = false +default_features = true +features = [] diff --git a/test cases/rust/44 cbindgen/lib.rs b/test cases/rust/44 cbindgen/lib.rs new file mode 100644 index 000000000000..3f38265888d4 --- /dev/null +++ b/test cases/rust/44 cbindgen/lib.rs @@ -0,0 +1,15 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright © 2024 Intel Corporation + */ + +#[repr(C)] +pub struct MyStruct { + pub cost: i64, + pub power: u8, +} + +#[no_mangle] +pub extern "C" fn print(x: &MyStruct) { + println!("A thing has a cost of {} and a power of {}", x.cost, x.power); +} diff --git a/test cases/rust/44 cbindgen/lib2.rs b/test cases/rust/44 cbindgen/lib2.rs new file mode 100644 index 000000000000..14d82b4382c5 --- /dev/null +++ b/test cases/rust/44 cbindgen/lib2.rs @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright © 2024 Intel Corporation + */ + +mod sub; +use sub::MyStruct; + +#[no_mangle] +pub extern "C" fn print(x: &MyStruct) { + println!("A thing has a cost of {} and a power of {}", x.cost, x.power); +} diff --git a/test cases/rust/44 cbindgen/meson.build b/test cases/rust/44 cbindgen/meson.build new file mode 100644 index 000000000000..32cb08062876 --- /dev/null +++ b/test cases/rust/44 cbindgen/meson.build @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright © 2024 Intel Corporation + +project( + 'cbindgen', + 'rust', 'c', + meson_version : '>= 1.12', + default_options : {'cpp_std' : 'c++20'}, +) + +if not find_program('cbindgen', required : false).found() + error('MESON_SKIP_TEST: cbindgen was not found') +endif + +rust = import('rust') + +rlib = static_library('rlib', 'lib.rs', rust_abi : 'c') + +bound_h = rust.cbindgen('lib.rs', 'lib.h', config : 'config.toml') + +test( + 'uses bound rust', + executable('ctest', 'test1.c', bound_h, link_with : rlib) +) + +srcs = structured_sources( + 'lib2.rs', + { + 'sub' : 'sub.rs', + } +) + +rlib2 = static_library('lib2', srcs, rust_abi : 'c') + +bound_h = rust.cbindgen(srcs, 'lib2.h', config : 'config.toml') +test( + 'uses bound rust from structured_sources', + executable('ctest2', 'test1.c', bound_h, c_args : '-DUSE_LIB2', link_with : rlib2) +) + +# The requirement of C++20 for designated initializers means this may not be +# supported everywhere +if add_languages('cpp', native : false, required : false) + rlib3 = static_library('lib3', 'lib.rs', rust_abi : 'c') + + bound_h = rust.cbindgen('lib.rs', 'lib3.hpp', config : 'config.toml') + test( + 'generates a c++ header', + executable('cpptest1', 'test1.cpp', bound_h, link_with : rlib2) + ) +endif diff --git a/test cases/rust/44 cbindgen/sub.rs b/test cases/rust/44 cbindgen/sub.rs new file mode 100644 index 000000000000..4ee1295e9675 --- /dev/null +++ b/test cases/rust/44 cbindgen/sub.rs @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright © 2024 Intel Corporation + */ + +#[repr(C)] +pub struct MyStruct { + pub cost: i64, + pub power: u8, +} diff --git a/test cases/rust/44 cbindgen/test1.c b/test cases/rust/44 cbindgen/test1.c new file mode 100644 index 000000000000..1541bea671e3 --- /dev/null +++ b/test cases/rust/44 cbindgen/test1.c @@ -0,0 +1,20 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright © 2024 Intel Corporation + */ + +#ifdef USE_LIB2 +#include "lib2.h" +#else +#include "lib.h" +#endif + +static MyStruct thing = { + .cost = -5, + .power = 1, +}; + +int main() { + print(&thing); + return 0; +} diff --git a/test cases/rust/44 cbindgen/test1.cpp b/test cases/rust/44 cbindgen/test1.cpp new file mode 100644 index 000000000000..fe8e56a403b0 --- /dev/null +++ b/test cases/rust/44 cbindgen/test1.cpp @@ -0,0 +1,16 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright © 2024 Intel Corporation + */ + +#include "lib3.hpp" + +static MyStruct thing = { + .cost = -5, + .power = 1, +}; + +int main() { + print(&thing); + return 0; +} From f81fdea2ac81299b95cc3668a3180ab6db7ba6d5 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 6 Jul 2026 19:03:40 +0000 Subject: [PATCH 594/681] _typing: mark ImmutableListProtocol method arguemnts as position only PEP 570 (finalized in Python 3.8) added position only arguments to the Python language. These are separated from position-or-keyword arguments by adding `/` between them. I.e., `def func(pos_only: int, /, pos_or_kw: str)`. Many Python builtins in 3.8 were either changed to position only, or already were position only as this had always existed in the C API, but not in Python. Mypy does not complain about these without more strictness turned on, but pylance does, by default, complain that a `list[str]` cannot be assigned to `ImmutableListProtocol[str]` because of the position-only mismatch. --- mesonbuild/_typing.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/mesonbuild/_typing.py b/mesonbuild/_typing.py index 8336c46bd06d..2777d1983ac9 100644 --- a/mesonbuild/_typing.py +++ b/mesonbuild/_typing.py @@ -43,27 +43,27 @@ class ImmutableListProtocol(Protocol[T]): def __iter__(self) -> typing.Iterator[T]: ... @typing.overload - def __getitem__(self, index: int) -> T: ... + def __getitem__(self, index: int, /) -> T: ... @typing.overload - def __getitem__(self, index: slice) -> typing.List[T]: ... + def __getitem__(self, index: slice, /) -> typing.List[T]: ... - def __contains__(self, item: T) -> bool: ... + def __contains__(self, item: T, /) -> bool: ... def __reversed__(self) -> typing.Iterator[T]: ... def __len__(self) -> int: ... - def __add__(self, other: typing.List[T]) -> typing.List[T]: ... + def __add__(self, other: typing.List[T], /) -> typing.List[T]: ... - def __eq__(self, other: typing.Any) -> bool: ... - def __ne__(self, other: typing.Any) -> bool: ... - def __le__(self, other: typing.Any) -> bool: ... - def __lt__(self, other: typing.Any) -> bool: ... - def __gt__(self, other: typing.Any) -> bool: ... - def __ge__(self, other: typing.Any) -> bool: ... + def __eq__(self, other: typing.Any, /) -> bool: ... + def __ne__(self, other: typing.Any, /) -> bool: ... + def __le__(self, other: typing.Any, /) -> bool: ... + def __lt__(self, other: typing.Any, /) -> bool: ... + def __gt__(self, other: typing.Any, /) -> bool: ... + def __ge__(self, other: typing.Any, /) -> bool: ... - def count(self, item: T) -> int: ... + def count(self, item: T, /) -> int: ... - def index(self, item: T) -> int: ... + def index(self, item: T, /) -> int: ... def copy(self) -> typing.List[T]: ... From 89670f9c9524f18a4364dc3feafdc51174ff5feb Mon Sep 17 00:00:00 2001 From: KO Myung-Hun Date: Sun, 5 Jul 2026 18:49:14 +0900 Subject: [PATCH 595/681] platform: implement path_has_root() on OS/2 OS/2 has the same problem as Windows. '/path/to/f' is not treated as an absolute path. Especially, DESTDIR is always appended to the build dir. --- mesonbuild/utils/platform.py | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/mesonbuild/utils/platform.py b/mesonbuild/utils/platform.py index 08a193825395..31e90d56c2ba 100644 --- a/mesonbuild/utils/platform.py +++ b/mesonbuild/utils/platform.py @@ -76,18 +76,6 @@ def __exit__(self, *args: T.Any) -> None: msvcrt.locking(self.lockfile.fileno(), msvcrt.LK_UNLCK, 1) self.lockfile.close() - # os.path.isabs changed in Python 3.13 (wtf) and now excludes Windows - # path with a root component but no drive. However, checking - # whether something is from outside the source tree, or is installed - # outside the prefix fails this new test. - def path_has_root(path: str) -> bool: - # Check for root-relative paths (and also UNC paths, which is okay); - # unnecessary on Python pre-3.13, but not a problem to do it anyway - if path and path[0] in '/\\': - return True - # Check for paths including a drive - return os.path.isabs(path) - else: import fcntl @@ -128,5 +116,19 @@ def __exit__(self, *args: T.Any) -> None: fcntl.flock(self.lockfile, fcntl.LOCK_UN) self.lockfile.close() +if sys.platform in {'win32', 'os2knix'}: + # os.path.isabs changed in Python 3.13 (wtf) and now excludes Windows + # and OS/2 path with a root component but no drive. However, checking + # whether something is from outside the source tree, or is installed + # outside the prefix fails this new test. + def path_has_root(path: str) -> bool: + # Check for root-relative paths (and also UNC paths, which is okay); + # unnecessary on Python pre-3.13, but not a problem to do it anyway + if path and path[0] in '/\\': + return True + # Check for paths including a drive + return os.path.isabs(path) + +else: # on POSIX systems don't go through pathlib as it's slower - path_has_root = os.path.isabs + path_has_root = os.path.isabs # type: ignore[assignment] From f2d0d089b5c6084f9b0aa2c51166f90efc6d357d Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Mon, 6 Jul 2026 20:48:57 +0300 Subject: [PATCH 596/681] Link against the latest Boost Python version. --- mesonbuild/dependencies/boost.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/mesonbuild/dependencies/boost.py b/mesonbuild/dependencies/boost.py index fe3fade03a2c..f97605292132 100644 --- a/mesonbuild/dependencies/boost.py +++ b/mesonbuild/dependencies/boost.py @@ -636,6 +636,19 @@ def filter_libraries(self, libs: T.List[BoostLibraryFile], lib_vers: str) -> T.L abitag = libs[0].abitag libs = [x for x in libs if x.abitag == abitag] + # Assume that we are building against the latest Python version + # and that the other ones are only there for backwards compatibility. + # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1141440 + no_python_libs = [] + python_libs = [] + for l in libs: + if l.is_python_lib(): + python_libs.append(l) + else: + no_python_libs.append(l) + sorted_pylibs = sorted(python_libs, key=lambda l: l.name, reverse=True) + libs = no_python_libs + sorted_pylibs[:1] + return libs def detect_libraries(self, libdir: Path) -> T.List[BoostLibraryFile]: From b0c04131c77acbe0966751742ac7e4448f6c2295 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Tue, 7 Jul 2026 16:07:30 +0300 Subject: [PATCH 597/681] Bump version number for rc1. --- man/meson.1 | 2 +- mesonbuild/coredata.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/man/meson.1 b/man/meson.1 index b655e299d534..bab9b9d78746 100644 --- a/man/meson.1 +++ b/man/meson.1 @@ -1,4 +1,4 @@ -.TH MESON "1" "April 2026" "meson 1.11.0" "User Commands" +.TH MESON "1" "July 2026" "meson 1.12.0" "User Commands" .SH NAME meson - a high productivity build system .SH DESCRIPTION diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 87a1fe6c2aa1..ce3e4d7a3527 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -43,7 +43,7 @@ # # Pip requires that RCs are named like this: '0.1.0.rc1' # But the corresponding Git tag needs to be '0.1.0rc1' -version = '1.11.99' +version = '1.12.0.rc1' # The next stable version when we are in dev. This is used to allow projects to # require meson version >=1.2.0 when using 1.1.99. FeatureNew won't warn when From 2b4f5dcd1b37a4a8915ba507b9da250a2a64aa4c Mon Sep 17 00:00:00 2001 From: Aleksandr Kovalko Date: Tue, 7 Jul 2026 19:35:24 +0200 Subject: [PATCH 598/681] compilers/cpp: add vc++23 to ALL_STDS ClangClCPPCompiler.get_options() registers 'vc++23' as a valid cpp_std value (since commit 3b7c81d4, 'clang-cl: add c++23 support ...'), but 'vc++23' was never added to the ALL_STDS list that backs the cpp_std UserStdOption. UserStdOption.set_versions() therefore trips its assert all(std in self.all_stds for std in versions) as soon as a clang-cl C++ compiler is detected, aborting configuration with an AssertionError ("Unhandled python exception") for every C++ project built with clang-cl. MSVC is unaffected because it never registers the vc++23 standard. Add the missing 'vc++23' entry so the compiler's advertised stds are a subset of ALL_STDS again. --- mesonbuild/compilers/cpp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index 41915038a2b8..7927d4a80568 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -48,7 +48,7 @@ ALL_STDS = ['c++98', 'c++0x', 'c++03', 'c++1y', 'c++1z', 'c++11', 'c++14', 'c++17'] ALL_STDS += ['c++2a', 'c++2b', 'c++2c', 'c++20', 'c++23', 'c++26'] ALL_STDS += [f'gnu{std[1:]}' for std in ALL_STDS] -ALL_STDS += ['vc++11', 'vc++14', 'vc++17', 'vc++20', 'vc++latest', 'c++latest'] +ALL_STDS += ['vc++11', 'vc++14', 'vc++17', 'vc++20', 'vc++23', 'vc++latest', 'c++latest'] def non_msvc_eh_options(eh: str, args: T.List[str]) -> None: From 22675c6a6521d73fa4f44bbd0057c6f1f1c74f7f Mon Sep 17 00:00:00 2001 From: Maxandre Ogeret Date: Thu, 9 Jul 2026 03:10:13 +0300 Subject: [PATCH 599/681] mconf: fix crash printing list-valued option augments print_augments() formatted the raw value with a string width spec, which raises TypeError for list values such as c_args. Use the same stringify() helper as the regular option display. Fixes #15991. --- mesonbuild/mconf.py | 2 +- unittests/allplatformstests.py | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py index 2b02eadf76c1..1e2f63b646a7 100644 --- a/mesonbuild/mconf.py +++ b/mesonbuild/mconf.py @@ -355,7 +355,7 @@ def print_augments(self) -> None: if self.coredata.optstore.augments: mlog.log('\nCurrently set option augments:') for k, v in self.coredata.optstore.augments.items(): - mlog.log(f'{k!s:21}{v:10}') + mlog.log(f'{k!s:21}{stringify(v):10}') else: mlog.log('\nThere are no option augments.') diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 7c558243d58a..06749132b70e 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -4190,6 +4190,14 @@ def test_configure(self): self.init(testdir) self._run(self.mconf_command + [self.builddir]) + def test_configure_with_augments(self): + # A list-valued augment used to crash `meson configure `. + testdir = os.path.join(self.unit_test_dir, '47 reconfigure') + self.init(testdir, extra_args=['-Dsub1:c_args=-DFOO']) + out = self._run(self.mconf_command + [self.builddir]) + self.assertIn('sub1:c_args', out) + self.assertIn('[-DFOO]', out) + def test_summary(self): testdir = os.path.join(self.unit_test_dir, '71 summary') out = self.init(testdir, extra_args=['-Denabled_opt=enabled', f'-Dpython={sys.executable}']) From 802e55238332ef1248b9556986fe95daac7c6e5b Mon Sep 17 00:00:00 2001 From: Peter Harris Date: Fri, 26 Jun 2026 10:24:54 -0400 Subject: [PATCH 600/681] move /showIncludes from always_args to get_show_dep_args The /showIncludes flag is required by ninja, but not by msbuild. Since this is a backend-specific requirement, it makes sense to separate it from the rest of the always_args. Fixes a compile performance regression with the vs backend when using Visual Studio 2026 version 18.7 Fixes #15959 --- mesonbuild/backend/ninjabackend.py | 1 + mesonbuild/compilers/compilers.py | 4 ++++ mesonbuild/compilers/mixins/visualstudio.py | 9 ++++++--- unittests/internaltests.py | 13 +++++++++---- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index d0ff82375062..30d3f8a26d1d 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -3132,6 +3132,7 @@ def _generate_single_compile_target_args(self, target: build.BuildTarget, compil # Add compiler args and include paths from several sources; defaults, # build options, external dependencies, etc. commands = self.generate_basic_compiler_args(target, compiler) + commands += compiler.get_show_dep_args() # Add custom target dirs as includes automatically, but before # target-specific include directories. if target.implicit_include_directories: diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 1ba9d91d67a8..63e31aae21fc 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -1704,3 +1704,7 @@ def get_crt_static(self) -> bool: def get_target_libdir(self) -> str: """Where is the libdir for the current machine located""" raise EnvironmentException(f'{self.get_id()} does not support Rust target libdir') + + def get_show_dep_args(self) -> T.List[str]: + """Arguments for printing depfile information in MSVC compatible format""" + return [] diff --git a/mesonbuild/compilers/mixins/visualstudio.py b/mesonbuild/compilers/mixins/visualstudio.py index 87173fb6bd57..365421e100f8 100644 --- a/mesonbuild/compilers/mixins/visualstudio.py +++ b/mesonbuild/compilers/mixins/visualstudio.py @@ -87,13 +87,11 @@ class VisualStudioLikeCompiler(Compiler, metaclass=mesonlib.SimpleABC): 'mtd': ['/MTd'], } - # /showIncludes is needed for build dependency tracking in Ninja - # See: https://ninja-build.org/manual.html#_deps # Assume UTF-8 sources by default, but self.unix_args_to_native() removes it # if `/source-charset` is set too. # It is also dropped if Visual Studio 2013 or earlier is used, since it would # not be supported in that case. - always_args = ['/nologo', '/showIncludes', '/utf-8'] + always_args = ['/nologo', '/utf-8'] warn_args: T.Dict[str, T.List[str]] = { '0': [], '1': ['/W2'], @@ -354,6 +352,11 @@ def symbols_have_underscore_prefix(self) -> bool: def get_pie_args(self) -> T.List[str]: return [] + def get_show_dep_args(self) -> T.List[str]: + # /showIncludes is needed for build dependency tracking in Ninja + # See: https://ninja-build.org/manual.html#_deps + return ['/showIncludes'] + class MSVCCompiler(VisualStudioLikeCompiler): """Specific to the Microsoft Compilers.""" diff --git a/unittests/internaltests.py b/unittests/internaltests.py index 6c4663036cb0..5647a0e68a43 100644 --- a/unittests/internaltests.py +++ b/unittests/internaltests.py @@ -291,19 +291,24 @@ def test_compiler_args_class_visualstudio(self): cc = VisualStudioCPPCompiler([], [], '20.00', MachineChoice.HOST, env, 'x64', linker=linker) a = cc.compiler_args(cc.get_always_args()) - self.assertEqual(a.to_native(copy=True), ['/nologo', '/showIncludes', '/utf-8', '/Zc:__cplusplus']) + self.assertEqual(a.to_native(copy=True), ['/nologo', '/utf-8', '/Zc:__cplusplus']) # Ensure /source-charset: removes /utf-8 a.append('/source-charset:utf-8') - self.assertEqual(a.to_native(copy=True), ['/nologo', '/showIncludes', '/Zc:__cplusplus', '/source-charset:utf-8']) + self.assertEqual(a.to_native(copy=True), ['/nologo', '/Zc:__cplusplus', '/source-charset:utf-8']) # Ensure /execution-charset: removes /utf-8 a = cc.compiler_args(cc.get_always_args() + ['/execution-charset:utf-8']) - self.assertEqual(a.to_native(copy=True), ['/nologo', '/showIncludes', '/Zc:__cplusplus', '/execution-charset:utf-8']) + self.assertEqual(a.to_native(copy=True), ['/nologo', '/Zc:__cplusplus', '/execution-charset:utf-8']) # Ensure /validate-charset- removes /utf-8 a = cc.compiler_args(cc.get_always_args() + ['/validate-charset-']) - self.assertEqual(a.to_native(copy=True), ['/nologo', '/showIncludes', '/Zc:__cplusplus', '/validate-charset-']) + self.assertEqual(a.to_native(copy=True), ['/nologo', '/Zc:__cplusplus', '/validate-charset-']) + + # /showIncludes is needed for build dependency tracking in Ninja + # See: https://ninja-build.org/manual.html#_deps + a = cc.compiler_args(cc.get_show_dep_args()) + self.assertEqual(a.to_native(copy=True), ['/showIncludes']) def test_msvc_unix_args_to_native(self): From b8b951970765c720e889dc660d06984234e06951 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 13 Jul 2026 06:30:25 +0200 Subject: [PATCH 601/681] adjust minimum version for external program as DEPENDS and INPUT Commits 8035ea6f5c and 0fea57bd98 were not backported to 1.11.2, as was intended to happen when the commit was written. Change the version number in the KwargInfo and move them to the 1.12.0 release notes. Signed-off-by: Paolo Bonzini --- docs/markdown/Gnome-module.md | 2 +- docs/markdown/Release-notes-for-1.11.0.md | 12 ------------ docs/markdown/snippets/programs-in-more-places.md | 12 ++++++++++++ mesonbuild/interpreter/type_checking.py | 4 ++-- 4 files changed, 15 insertions(+), 15 deletions(-) create mode 100644 docs/markdown/snippets/programs-in-more-places.md diff --git a/docs/markdown/Gnome-module.md b/docs/markdown/Gnome-module.md index c89e0e96165c..f4ce77b42b27 100644 --- a/docs/markdown/Gnome-module.md +++ b/docs/markdown/Gnome-module.md @@ -132,7 +132,7 @@ Generates a marshal file using the `glib-genmarshal` tool. The first argument is the basename of the output files. * `depends` [](BuildTarget | CustomTarget | CustomTargetIndex | Program): - passed directly to CustomTarget (*since 0.61.0*; `program` *since 1.11.2*) + passed directly to CustomTarget (*since 0.61.0*; `program` *since 1.12.0*) * `depend_files` [](str | File): Passed directly to CustomTarget (*since 0.61.0*) * `extra_args`: (*Added 0.42.0*) additional command line arguments to pass * `install_dir`: directory to install header to diff --git a/docs/markdown/Release-notes-for-1.11.0.md b/docs/markdown/Release-notes-for-1.11.0.md index ed4bbfce5df0..da8700979337 100644 --- a/docs/markdown/Release-notes-for-1.11.0.md +++ b/docs/markdown/Release-notes-for-1.11.0.md @@ -203,15 +203,3 @@ as dependencies. now has an `implicit_include_directories` keyword argument to automatically add current build and source directories to the included paths when compiling a resource. - -## External programs as inputs and dependencies to custom targets - -Custom targets now allow specifying an external program in -the `input` and `depends` keyword arguments. This also applies -to several methods provided by modules, as they are lowered to -custom targets internally. (*Added in 1.11.2*). - -## External programs as dependencies to tests - -Tests now allow specifying an external program in -the `depends` keyword argument. (*Added in 1.11.2*). diff --git a/docs/markdown/snippets/programs-in-more-places.md b/docs/markdown/snippets/programs-in-more-places.md new file mode 100644 index 000000000000..e561cce9537e --- /dev/null +++ b/docs/markdown/snippets/programs-in-more-places.md @@ -0,0 +1,12 @@ +## External programs as inputs and dependencies to custom targets + +Custom targets now allow specifying an external program in +the `input` and `depends` keyword arguments. This also applies +to several methods provided by modules, as they are lowered to +custom targets internally. + +## External programs as dependencies to tests + +Tests now allow specifying an external program in +the `depends` keyword argument. + diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py index b029b98358db..9f2f9194155c 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -277,7 +277,7 @@ def _env_convertor(value: FullEnvInitValueType) -> EnvironmentVariables: ContainerTypeInfo(list, (BuildTarget, CustomTarget, CustomTargetIndex, Program)), listify=True, default=[], - since_values={CustomTargetIndex: '1.5.0', ExternalProgram: '1.11.2'}, + since_values={CustomTargetIndex: '1.5.0', ExternalProgram: '1.12.0'}, ) DEPEND_FILES_KW: KwargInfo[T.List[T.Union[str, File]]] = KwargInfo( @@ -367,7 +367,7 @@ def _local_program_convertor(raw: T.List[T.Union[str, File, BuildTarget, Generat listify=True, default=[], convertor=_local_program_convertor, - since_values={ExternalProgram: '1.11.2'}, + since_values={ExternalProgram: '1.12.0'}, ) CT_INSTALL_TAG_KW: KwargInfo[T.List[T.Union[str, bool]]] = KwargInfo( From afd2f89f61945f26eab0f7eed611c700e0795543 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 13 Jul 2026 06:38:13 +0200 Subject: [PATCH 602/681] interpreter: add FeatureNew for generated non-.pyx sources This was fixed as a result of the effort to clean up typing of the Ninja backend. Detect the situation and raise a FeatureNew for it, so that users get a notice that they are relying on something that never worked prior to commit 8e705ad. Signed-off-by: Paolo Bonzini --- mesonbuild/interpreter/interpreter.py | 11 ++++++++++ .../cython/2 generated sources/answer.c.in | 3 +++ .../cython/2 generated sources/genc.pyx.in | 9 ++++++++ .../cython/2 generated sources/meson.build | 21 +++++++++++++++++++ .../cython/2 generated sources/test.json | 7 +++++++ 5 files changed, 51 insertions(+) create mode 100644 test cases/cython/2 generated sources/answer.c.in create mode 100644 test cases/cython/2 generated sources/genc.pyx.in create mode 100644 test cases/cython/2 generated sources/test.json diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 2a5a2e97ba14..342ffacf0243 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -3501,6 +3501,17 @@ def add_target(self, name: str, tobj: build.Target) -> None: tobj.process_compilers_late() self.add_stdlib_info(tobj) + if tobj.uses_cython(): + for gensrc in tobj.get_generated_sources(): + # a generator producing a .pyx has always worked; feeding + # anything else from a generator into Cython triggered a bug + # in the ninja backend and was effectively a new feature. + if isinstance(gensrc, build.GeneratedList) and \ + any(not o.endswith('.pyx') for o in gensrc.get_outputs()): + FeatureNew.single_use('Generators for non-.pyx Cython sources', + '1.12.0', self.subproject, location=self.current_node) + break + self.build.targets[idname] = tobj # Only need to add executables to this set if isinstance(tobj, build.Executable): diff --git a/test cases/cython/2 generated sources/answer.c.in b/test cases/cython/2 generated sources/answer.c.in new file mode 100644 index 000000000000..76ef52d9b27e --- /dev/null +++ b/test cases/cython/2 generated sources/answer.c.in @@ -0,0 +1,3 @@ +const char *c_func(void) { + return "Hello, World!"; +} diff --git a/test cases/cython/2 generated sources/genc.pyx.in b/test cases/cython/2 generated sources/genc.pyx.in new file mode 100644 index 000000000000..05efc7b348c4 --- /dev/null +++ b/test cases/cython/2 generated sources/genc.pyx.in @@ -0,0 +1,9 @@ +cdef extern from *: + """ + const char *c_func(void); + """ + const char *c_func() + +cpdef func(): + cdef bytes b = c_func() + return b.decode('utf-8') diff --git a/test cases/cython/2 generated sources/meson.build b/test cases/cython/2 generated sources/meson.build index 3d2837d42509..a227c85ddacc 100644 --- a/test cases/cython/2 generated sources/meson.build +++ b/test cases/cython/2 generated sources/meson.build @@ -2,6 +2,7 @@ project( 'generated cython sources', ['cython', 'c'], default_options : ['buildtype=release'], + meson_version : '>= 1.10.0', ) if meson.backend() != 'ninja' @@ -74,6 +75,26 @@ test( env : ['PYTHONPATH=' + meson.current_build_dir()] ) +# A generator whose output is *not* a .pyx file, mixed with a Cython .pyx +# source in the same target. +gen_c = generator( + find_program('generator.py'), + arguments : ['@INPUT@', '@OUTPUT@'], + output : '@BASENAME@', +) + +genc_ext = py3.extension_module( + 'genc', + gen_c.process('genc.pyx.in', 'answer.c.in'), +) + +test( + 'generator with non-pyx output', + py3, + args : [files('test.py'), 'genc'], + env : ['PYTHONPATH=' + meson.current_build_dir()] +) + stuff_pxi = fs.copyfile( 'stuff.pxi.in', 'stuff.pxi' diff --git a/test cases/cython/2 generated sources/test.json b/test cases/cython/2 generated sources/test.json new file mode 100644 index 000000000000..6a4e66bc1dd9 --- /dev/null +++ b/test cases/cython/2 generated sources/test.json @@ -0,0 +1,7 @@ +{ + "stdout": [ + { + "line": "test cases/cython/2 generated sources/meson.build:86: WARNING: Project targets '>= 1.10.0' but uses feature introduced in '1.12.0': Generators for non-.pyx Cython sources." + } + ] +} From e4cc4d2a504ebee2e6230a26a4a61a54ba93dee3 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 13 Jul 2026 06:05:42 +0200 Subject: [PATCH 603/681] Revert "linkers: apple: do not pass -object_path_lto to dyld" This reverts commit 7d4c05a6ee53fb6970edf461bc221184748bc1f5. The correct fix is to check for the version number, since -object_path_lto was added in ld64 123.2. Signed-off-by: Paolo Bonzini --- mesonbuild/linkers/detect.py | 4 +--- mesonbuild/linkers/linkers.py | 9 --------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/mesonbuild/linkers/detect.py b/mesonbuild/linkers/detect.py index 10ccda159b93..c19f66cb2fe0 100644 --- a/mesonbuild/linkers/detect.py +++ b/mesonbuild/linkers/detect.py @@ -228,17 +228,15 @@ def guess_nix_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty cmd = compiler + comp_class.LINKER_OPTION_STYLE.wrap(['-v']) + extra_args _, newo, newerr = Popen_safe_logged(cmd, msg='Detecting Apple linker via') - is_dyld: bool for line in newerr.split('\n'): if 'PROJECT:ld' in line or 'PROJECT:dyld' in line: v = line.split('-')[1] - is_dyld = 'PROJECT:dyld' in line break else: __failed_to_detect_linker(compiler, check_args, o, e) linker = linkers.AppleDynamicLinker( compiler, env, for_machine, comp_class.LINKER_OPTION_STYLE, override, - system=system, version=v, is_dyld=is_dyld + system=system, version=v ) elif 'ld.exe: unrecognized option' in e or 'ld: unrecognized option' in e: linker = linkers.OS2AoutDynamicLinker( diff --git a/mesonbuild/linkers/linkers.py b/mesonbuild/linkers/linkers.py index fd90cddeb964..f12475dbaf8a 100644 --- a/mesonbuild/linkers/linkers.py +++ b/mesonbuild/linkers/linkers.py @@ -853,13 +853,6 @@ class AppleDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker): id = 'ld64' - def __init__(self, exelist: T.List[str], env: Environment, - for_machine: mesonlib.MachineChoice, prefix_arg: T.Optional[LinkerOptionStyle], - always_args: T.List[str], *, system: str, - version: str, is_dyld: bool = False): - super().__init__(exelist, env, for_machine, prefix_arg, always_args, system=system, version=version) - self.is_dyld = is_dyld - def get_asneeded_args(self) -> T.List[str]: return self._apply_prefix('-dead_strip_dylibs') @@ -966,8 +959,6 @@ def get_thinlto_cache_args(self, path: str) -> T.List[str]: def get_lto_obj_cache_path(self, path: str) -> T.List[str]: # https://clang.llvm.org/docs/CommandGuide/clang.html#cmdoption-flto - if self.is_dyld: - return [] return ["-Wl,-object_path_lto," + path] def export_dynamic_args(self) -> T.List[str]: From 97f29867866d7aac958df568472849505cc4b829 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 13 Jul 2026 06:09:19 +0200 Subject: [PATCH 604/681] linkers: apple: -object_path_lto was added in ld 123.2 Bound its inclusion using the same scheme as export_dynamic_args. Use _apply_prefix for consistency. Fixes: #15808 Signed-off-by: Paolo Bonzini --- mesonbuild/linkers/linkers.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mesonbuild/linkers/linkers.py b/mesonbuild/linkers/linkers.py index f12475dbaf8a..bc318a8f1bab 100644 --- a/mesonbuild/linkers/linkers.py +++ b/mesonbuild/linkers/linkers.py @@ -959,7 +959,9 @@ def get_thinlto_cache_args(self, path: str) -> T.List[str]: def get_lto_obj_cache_path(self, path: str) -> T.List[str]: # https://clang.llvm.org/docs/CommandGuide/clang.html#cmdoption-flto - return ["-Wl,-object_path_lto," + path] + if mesonlib.version_compare(self.version, '>=123.2'): + return self._apply_prefix(['-object_path_lto', path]) + return [] def export_dynamic_args(self) -> T.List[str]: if mesonlib.version_compare(self.version, '>=224.1'): From a60c47874086f4508cac68b9e58c8739bc2358d3 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 13 Jul 2026 06:22:48 +0200 Subject: [PATCH 605/681] linker: apple: fix xtools version detection For xtools ld64, Meson currently passes the xtools version (e.g. 2.2.4) instead of the linker version. Fix this so that all the features are available. Signed-off-by: Paolo Bonzini --- mesonbuild/linkers/detect.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/mesonbuild/linkers/detect.py b/mesonbuild/linkers/detect.py index c19f66cb2fe0..54c620c4e466 100644 --- a/mesonbuild/linkers/detect.py +++ b/mesonbuild/linkers/detect.py @@ -213,8 +213,14 @@ def guess_nix_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty compiler, env, for_machine, comp_class.LINKER_OPTION_STYLE, override, version=v) # detect xtools first, bug #10805 elif 'xtools-' in o.split('\n', maxsplit=1)[0]: - xtools = o.split(' ', maxsplit=1)[0] - v = xtools.split('-', maxsplit=2)[1] + # See https://github.com/iains/darwin-xtools/blob/darwin-xtools-3-3-0/ld64/src/ld/ld_vers.c.in + # for the format + for line in o.split('\n'): + if line.startswith('Based on Apple Inc. ld64-'): + v = line.split()[4][5:] + break + else: + __failed_to_detect_linker(compiler, check_args, o, e) linker = linkers.AppleDynamicLinker( compiler, env, for_machine, comp_class.LINKER_OPTION_STYLE, override, system=system, version=v From 16fe37ea83fdce5281906a8bc6fe7258995a23eb Mon Sep 17 00:00:00 2001 From: Jakub Jelen Date: Thu, 16 Jul 2026 11:03:10 +0200 Subject: [PATCH 606/681] Fix formatting for man page test --slice option Currently, the `--slice` option renders on the same line as the `--setup` due to wrong formatting character initializing the indented block. This is fixing the case of the formatting character and renders the manual page correctly. Signed-off-by: Jakub Jelen --- man/meson.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/meson.1 b/man/meson.1 index bab9b9d78746..a6907165ce0b 100644 --- a/man/meson.1 +++ b/man/meson.1 @@ -326,7 +326,7 @@ a multiplier to use for test timeout values (usually something like 100 for Valg .TP \fB\-\-setup\fR use the specified test setup -.Tp +.TP \fB\-\-slice SLICE/NUM_SLICES\fR Split tests into NUM_SLICES slices and execute slice number SLICE. (Since 1.8.0) From ce3543969d4341d0265de874964fd7be95e542d0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 17 Feb 2026 13:02:08 +0100 Subject: [PATCH 607/681] python: switch (again) default rust_abi for extension modules to C Adding `rust_abi: c` automatically was broken by commit b5f61587c ("modules/python: rework extension_module method for typing"), fix it again. While at it, be more careful - ensure that rust_abi is only added if the caller provided neither of rust_abi and rust_crate_type. Signed-off-by: Paolo Bonzini --- mesonbuild/modules/python.py | 3 +- .../python/12 rust extmodule/blaster.py | 11 +++++ .../python/12 rust extmodule/ext/meson.build | 6 +++ .../12 rust extmodule/ext/tachyon_module.c | 45 +++++++++++++++++++ .../12 rust extmodule/ext/tachyon_module.rs | 17 +++++++ .../python/12 rust extmodule/meson.build | 36 +++++++++++++++ 6 files changed, 117 insertions(+), 1 deletion(-) create mode 100755 test cases/python/12 rust extmodule/blaster.py create mode 100644 test cases/python/12 rust extmodule/ext/meson.build create mode 100644 test cases/python/12 rust extmodule/ext/tachyon_module.c create mode 100644 test cases/python/12 rust extmodule/ext/tachyon_module.rs create mode 100644 test cases/python/12 rust extmodule/meson.build diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py index e8c359529f95..313e90e6a978 100644 --- a/mesonbuild/modules/python.py +++ b/mesonbuild/modules/python.py @@ -157,6 +157,8 @@ def as_(obj: object, type_: T.Type[_T]) -> _T: @InterpreterObject.method('extension_module') def extension_module_method(self, args: T.Tuple[str, T.List[BuildTargetSource]], kwargs: ExtensionModuleKw) -> 'SharedModule': target_kwargs = T.cast('SharedModuleKw', {k: v for k, v in kwargs.items() if k not in {'install_dir', 'subdir', 'limited_api'}}) + if kwargs['rust_abi'] is None and kwargs['rust_crate_type'] is None: + target_kwargs['rust_abi'] = 'c' if kwargs['install_dir'] is not None: if kwargs['subdir'] is not None: @@ -247,7 +249,6 @@ def extension_module_method(self, args: T.Tuple[str, T.List[BuildTargetSource]], (self.is_pypy or mesonlib.version_compare(self.version, '>=3.9')): target_kwargs['gnu_symbol_visibility'] = 'inlineshidden' - kwargs.setdefault('rust_abi', 'c') return self.interpreter.build_target( self.current_node, T.cast('T.Tuple[str, SourcesVarargsType]', args), target_kwargs, SharedModule) diff --git a/test cases/python/12 rust extmodule/blaster.py b/test cases/python/12 rust extmodule/blaster.py new file mode 100755 index 000000000000..1f01876457ff --- /dev/null +++ b/test cases/python/12 rust extmodule/blaster.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python3 + +import tachyon + +result = tachyon.phaserize('shoot') + +if not isinstance(result, int): + raise SystemExit('Returned result not an integer.') + +if result != 1: + raise SystemExit('Returned result {} is not 1.'.format(result)) diff --git a/test cases/python/12 rust extmodule/ext/meson.build b/test cases/python/12 rust extmodule/ext/meson.build new file mode 100644 index 000000000000..907a06cc1c97 --- /dev/null +++ b/test cases/python/12 rust extmodule/ext/meson.build @@ -0,0 +1,6 @@ +pylib = py.extension_module('tachyon', + 'tachyon_module.rs', 'tachyon_module.c', + limited_api: '3.7', +) + +pypathdir = meson.current_build_dir() diff --git a/test cases/python/12 rust extmodule/ext/tachyon_module.c b/test cases/python/12 rust extmodule/ext/tachyon_module.c new file mode 100644 index 000000000000..d907cc517cea --- /dev/null +++ b/test cases/python/12 rust extmodule/ext/tachyon_module.c @@ -0,0 +1,45 @@ +/* + Copyright 2018 The Meson development team + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/* A very simple Python extension module. */ + +#include +#include + +static PyObject* phaserize(PyObject *self, PyObject *args) { + const char *message; + int result; + + if(!PyArg_ParseTuple(args, "s", &message)) + return NULL; + + result = strcmp(message, "shoot") ? 0 : 1; + return PyLong_FromLong(result); +} + +static PyMethodDef TachyonMethods[] = { + {"phaserize", phaserize, METH_VARARGS, + "Shoot tachyon cannons."}, + {NULL, NULL, 0, NULL} +}; + +struct PyModuleDef *tachyonmodule = &(struct PyModuleDef){ + PyModuleDef_HEAD_INIT, + "tachyon", + NULL, + -1, + TachyonMethods +}; diff --git a/test cases/python/12 rust extmodule/ext/tachyon_module.rs b/test cases/python/12 rust extmodule/ext/tachyon_module.rs new file mode 100644 index 000000000000..fe4350becec0 --- /dev/null +++ b/test cases/python/12 rust extmodule/ext/tachyon_module.rs @@ -0,0 +1,17 @@ +#![allow(improper_ctypes)] + +#[repr(C)] +pub struct PyModuleDef; +#[repr(C)] +pub struct PyObject; + +extern "C" { + pub static mut tachyonmodule: *mut PyModuleDef; + pub fn PyModule_Create2(def: *mut PyModuleDef, abiver: i32) -> *mut PyObject; +} + +#[allow(non_snake_case)] +#[no_mangle] +pub unsafe extern "C" fn PyInit_tachyon() -> *mut PyObject { + PyModule_Create2(tachyonmodule, 3) +} diff --git a/test cases/python/12 rust extmodule/meson.build b/test cases/python/12 rust extmodule/meson.build new file mode 100644 index 000000000000..6c4cec8a0707 --- /dev/null +++ b/test cases/python/12 rust extmodule/meson.build @@ -0,0 +1,36 @@ +project('Python extension module', 'c', + default_options : ['buildtype=release', 'werror=true', 'python.bytecompile=-1']) +# Because Windows Python ships only with optimized libs, +# we must build this project the same way. + +if meson.backend() != 'ninja' + error('MESON_SKIP_TEST: Ninja backend required') +endif +if not add_languages('rust', required: false, native: false) + error('MESON_SKIP_TEST: Rust compiler required') +endif + +py_mod = import('python') +py = py_mod.find_installation() +py_dep = py.dependency(required: false) + +if not py_dep.found() + error('MESON_SKIP_TEST: Python libraries not found.') +endif + +# Rust code cannot rely on #define PyModule_Create, so it calls +# PyModule_Create2. But it is also not possible to use has_function() +# because the function is in the Python executable, so just check +# for PyPy. +if run_command(py, '-c', 'import sys; sys.exit("__pypy__" in sys.builtin_module_names)', + check: false).returncode() == 1 + error('MESON_SKIP_TEST: Limited API not supported (PyPy?)') +endif + +subdir('ext') + +src = files('blaster.py') +test('extmod', + py, + args : src, + env : ['PYTHONPATH=' + pypathdir]) From 11f88f3c7de6caf49002715bb68287bde132a784 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 14 Jul 2026 13:13:22 +0200 Subject: [PATCH 608/681] vs2010backend: use Build.get_global_args Continue the job started by commit 687cb9790 ("backends: use Build.get_project_args", 2026-01-16). Signed-off-by: Paolo Bonzini --- mesonbuild/backend/vs2010backend.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index cccd5912176e..909930776391 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -1063,15 +1063,11 @@ def get_args_defines_and_inc_dirs( file_args[l] += comp.get_option_std_args( target, target.subproject) - # Add compile args added using add_project_arguments() + # Add compile args added using add_project_arguments() and add_global_arguments() for l, comp in target.compilers.items(): file_args[l] += self.build.get_project_args(comp, target) + file_args[l] += self.build.get_global_args(comp, target.for_machine) - # Add compile args added using add_global_arguments() - # These override per-project arguments - for l, args in self.build.global_args[target.for_machine].items(): - if l in file_args: - file_args[l] += args # Compile args added from the env or cross file: CFLAGS/CXXFLAGS, etc. We want these # to override all the defaults, but not the per-target compile args. for lang in file_args.keys(): From ef574de6ac40e10d34d3abf9038a10253369dc92 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 14 Jul 2026 08:21:17 +0200 Subject: [PATCH 609/681] build: pass target to get_global_*args Make this more consistent with get_project_*args. Either way the arguments are only used together with a build target. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/backends.py | 2 +- mesonbuild/backend/ninjabackend.py | 6 +++--- mesonbuild/backend/vs2010backend.py | 2 +- mesonbuild/build.py | 8 ++++---- mesonbuild/compilers/compilers.py | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 21cc8bb0a65d..4aeed370b614 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -970,7 +970,7 @@ def generate_basic_compiler_args(self, target: build.BuildTarget, compiler: 'Com commands += self.build.get_project_args(compiler, target) # Add compile args added using add_global_arguments() # These override per-project arguments - commands += self.build.get_global_args(compiler, target.for_machine) + commands += self.build.get_global_args(compiler, target) # Compile args added from the env: CFLAGS/CXXFLAGS, etc, or the cross # file. We want these to override all the defaults, but not the # per-target compile args. diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 30d3f8a26d1d..0a52d87686c6 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1896,7 +1896,7 @@ def generate_cython_transpile(self, target: build.BuildTarget) -> \ args += cython.get_optimization_args(optimization) args += cython.get_option_compile_args(target, target.subproject) args += cython.get_option_std_args(target, target.subproject) - args += self.build.get_global_args(cython, target.for_machine) + args += self.build.get_global_args(cython, target) args += self.build.get_project_args(cython, target) args += target.get_extra_args('cython') @@ -2416,7 +2416,7 @@ def generate_swift_target(self, target: build.BuildTarget) -> None: compile_args += swiftc.get_module_args(module_name) compile_args += swiftc.get_cxx_interoperability_args(target) compile_args += self.build.get_project_args(swiftc, target) - compile_args += self.build.get_global_args(swiftc, target.for_machine) + compile_args += self.build.get_global_args(swiftc, target) if isinstance(target, (build.StaticLibrary, build.SharedLibrary)): # swiftc treats modules with a single source file, and the main.swift file in multi-source file modules # as top-level code. This is undesirable in library targets since it emits a main function. Add the @@ -2430,7 +2430,7 @@ def generate_swift_target(self, target: build.BuildTarget) -> None: compile_args += target.get_extra_args('swift') link_args = swiftc.get_output_args(os.path.join(self.environment.get_build_dir(), self.get_target_filename(target))) link_args += self.build.get_project_link_args(swiftc, target) - link_args += self.build.get_global_link_args(swiftc, target.for_machine) + link_args += self.build.get_global_link_args(swiftc, target) rundir = self.get_target_private_dir(target) out_module_name = self.swift_module_file_name(target) in_module_files = self.determine_swift_dep_modules(target) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 909930776391..924764e05fec 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -1066,7 +1066,7 @@ def get_args_defines_and_inc_dirs( # Add compile args added using add_project_arguments() and add_global_arguments() for l, comp in target.compilers.items(): file_args[l] += self.build.get_project_args(comp, target) - file_args[l] += self.build.get_global_args(comp, target.for_machine) + file_args[l] += self.build.get_global_args(comp, target) # Compile args added from the env or cross file: CFLAGS/CXXFLAGS, etc. We want these # to override all the defaults, but not the per-target compile args. diff --git a/mesonbuild/build.py b/mesonbuild/build.py index d9fa10414f6d..0099787416e4 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -476,8 +476,8 @@ def get_emptydir(self) -> T.List['EmptyDir']: def get_install_subdirs(self) -> T.List['InstallDir']: return self.install_dirs - def get_global_args(self, compiler: 'Compiler', for_machine: 'MachineChoice') -> T.List[str]: - d = self.global_args[for_machine] + def get_global_args(self, compiler: 'Compiler', target: BuildTarget) -> T.List[str]: + d = self.global_args[target.for_machine] return d.get(compiler.get_language(), []) def get_project_args(self, compiler: 'Compiler', target: BuildTarget) -> T.List[str]: @@ -487,8 +487,8 @@ def get_project_args(self, compiler: 'Compiler', target: BuildTarget) -> T.List[ return [] return args.get(compiler.get_language(), []) - def get_global_link_args(self, compiler: 'Compiler', for_machine: 'MachineChoice') -> T.List[str]: - d = self.global_link_args[for_machine] + def get_global_link_args(self, compiler: 'Compiler', target: BuildTarget) -> T.List[str]: + d = self.global_link_args[target.for_machine] return d.get(compiler.get_language(), []) def get_project_link_args(self, compiler: 'Compiler', target: BuildTarget) -> T.List[str]: diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 63e31aae21fc..518ea0deb496 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -1196,7 +1196,7 @@ def get_build_link_args(self, target: BuildTarget, build: build.Build) -> T.List # per-project link arguments. Link args added from the env (LDFLAGS) # override all the defaults but not the per-target link args. return build.get_project_link_args(self, target) \ - + build.get_global_link_args(self, self.for_machine) \ + + build.get_global_link_args(self, target) \ + self.environment.coredata.get_external_link_args(self.for_machine, self.get_language()) def get_target_link_args(self, target: 'BuildTarget') -> T.List[str]: From 9c03a12e009c1c18dbeaf82636c7753472b9e61b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 14 Jul 2026 08:36:09 +0200 Subject: [PATCH 610/681] build, interpreter: handle per-machine project arguments in BuildTarget Per-machine compiler arguments (whether global or project) are separated for "native: true" and "native: false" targets. This means that it is incorrect to simply drop them for "native: true" targets when not cross compiling. Instead, the BuildTarget needs to know the *original* value of native and use it to apply the correct set of arguments. Start by adding all the infrastructure and handling project arguments. Global arguments need more care; they are not yet fully fixed. --- mesonbuild/ast/introspection.py | 4 +++- mesonbuild/build.py | 34 ++++++++++++++------------- mesonbuild/interpreter/interpreter.py | 30 +++++++---------------- unittests/allplatformstests.py | 5 ++-- 4 files changed, 33 insertions(+), 40 deletions(-) diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py index 1cc80ad843a2..2abf94f92709 100644 --- a/mesonbuild/ast/introspection.py +++ b/mesonbuild/ast/introspection.py @@ -254,12 +254,14 @@ def build_target(self, node: BaseNode, args: T.List[TYPE_var], kwargs_raw: T.Dic assert extraf_nodes is None extraf_nodes = v + for_machine = MachineChoice.BUILD if kwargs.get('native', False) else MachineChoice.HOST + # Make sure nothing can crash when creating the build class _kwargs_reduced = {k: v for k, v in kwargs.items() if k in _TARGET_KWARGS[targetclass.typename] and k in {'install', 'build_by_default', 'build_always', 'name_prefix'}} _kwargs_reduced = {k: v.value if isinstance(v, ElementaryNode) else v for k, v in _kwargs_reduced.items()} _kwargs_reduced = {k: v for k, v in _kwargs_reduced.items() if not isinstance(v, (BaseNode, UnknownValue))} + _kwargs_reduced['native'] = for_machine kwargs_reduced = T.cast('BuildTargetKeywordArguments', _kwargs_reduced) - for_machine = MachineChoice.BUILD if kwargs.get('native', False) else MachineChoice.HOST objects: T.List[T.Any] = [] empty_sources: T.List[T.Any] = [] # Passing the unresolved sources list causes errors diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 0099787416e4..bce9162235c6 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -477,23 +477,23 @@ def get_install_subdirs(self) -> T.List['InstallDir']: return self.install_dirs def get_global_args(self, compiler: 'Compiler', target: BuildTarget) -> T.List[str]: - d = self.global_args[target.for_machine] + d = self.global_args[target.orig_for_machine] return d.get(compiler.get_language(), []) def get_project_args(self, compiler: 'Compiler', target: BuildTarget) -> T.List[str]: d = target.build_project - args = d.project_args[target.for_machine] + args = d.project_args[target.orig_for_machine] if not args: return [] return args.get(compiler.get_language(), []) def get_global_link_args(self, compiler: 'Compiler', target: BuildTarget) -> T.List[str]: - d = self.global_link_args[target.for_machine] + d = self.global_link_args[target.orig_for_machine] return d.get(compiler.get_language(), []) def get_project_link_args(self, compiler: 'Compiler', target: BuildTarget) -> T.List[str]: d = target.build_project - link_args = d.project_link_args[target.for_machine] + link_args = d.project_link_args[target.orig_for_machine] if not link_args: return [] @@ -796,7 +796,7 @@ def __init__( self, name: str, subdir: str, - for_machine: MachineChoice, + orig_for_machine: MachineChoice, sources: T.List['SourceOutputs'], structured_sources: T.Optional[StructuredSources], objects: T.Sequence[ObjectTypes | GeneratedTypes], @@ -805,10 +805,11 @@ def __init__( build_project: BuildProject, kwargs: BuildTargetKeywordArguments): super().__init__(name, subdir, kwargs.get('build_by_default', True), - for_machine, environment, + kwargs['native'], environment, install=kwargs.get('install', False), build_subdir=kwargs.get('build_subdir', ''), build_project=build_project) + self.orig_for_machine = orig_for_machine self.original_kwargs = kwargs # all_compilers is a reference to Interpreter.compilers, as such we # cannot mutate it inside build. Use a Mapping to get help from the @@ -2192,7 +2193,7 @@ def __init__( self, name: str, subdir: str, - for_machine: MachineChoice, + orig_for_machine: MachineChoice, sources: T.List['SourceOutputs'], structured_sources: T.Optional[StructuredSources], objects: T.Sequence[ObjectTypes | GeneratedTypes], @@ -2202,7 +2203,7 @@ def __init__( kwargs: ExecutableKeywordArguments): self.export_dynamic = kwargs.get('export_dynamic', False) self.rust_crate_type = kwargs.get('rust_crate_type', 'bin') - super().__init__(name, subdir, for_machine, sources, structured_sources, objects, + super().__init__(name, subdir, orig_for_machine, sources, structured_sources, objects, environment, compilers, build_project, kwargs) self.win_subsystem = kwargs.get('win_subsystem') or 'console' self.pie = self._extract_pic_pie(kwargs, 'pie', 'b_pie') @@ -2325,7 +2326,7 @@ def __init__( self, name: str, subdir: str, - for_machine: MachineChoice, + orig_for_machine: MachineChoice, sources: T.List['SourceOutputs'], structured_sources: T.Optional[StructuredSources], objects: T.Sequence[ObjectTypes | GeneratedTypes], @@ -2335,7 +2336,7 @@ def __init__( kwargs: StaticLibraryKeywordArguments): self.prelink = kwargs.get('prelink', False) self.rust_crate_type = kwargs.get('rust_crate_type', 'rlib') - super().__init__(name, subdir, for_machine, sources, structured_sources, objects, + super().__init__(name, subdir, orig_for_machine, sources, structured_sources, objects, environment, compilers, build_project, kwargs) self.pic = self._extract_pic_pie(kwargs, 'pic', 'b_staticpic') if not self.pic: @@ -2507,7 +2508,7 @@ def __init__( self, name: str, subdir: str, - for_machine: MachineChoice, + orig_for_machine: MachineChoice, sources: T.List['SourceOutputs'], structured_sources: T.Optional[StructuredSources], objects: T.Sequence[ObjectTypes | GeneratedTypes], @@ -2515,7 +2516,7 @@ def __init__( compilers: CompilerDict, build_project: BuildProject, kwargs: SharedLibraryKeywordArguments): - super().__init__(name, subdir, for_machine, sources, structured_sources, objects, + super().__init__(name, subdir, orig_for_machine, sources, structured_sources, objects, environment, compilers, build_project, kwargs) # Max length 2, first element is compatibility_version, second is current_version self.darwin_versions: T.Optional[T.Tuple[str, str]] = None @@ -2845,7 +2846,7 @@ def __init__( self, name: str, subdir: str, - for_machine: MachineChoice, + orig_for_machine: MachineChoice, sources: T.List['SourceOutputs'], structured_sources: T.Optional[StructuredSources], objects: T.Sequence[ObjectTypes | GeneratedTypes], @@ -2853,7 +2854,7 @@ def __init__( compilers: CompilerDict, build_project: BuildProject, kwargs: SharedModuleKeywordArguments): - super().__init__(name, subdir, for_machine, sources, + super().__init__(name, subdir, orig_for_machine, sources, structured_sources, objects, environment, compilers, build_project, # SharedModuleKeywordArguments is a subclass, it's annoying mypy can't figure this out T.cast('SharedLibraryKeywordArguments', kwargs)) @@ -3238,6 +3239,7 @@ def __init__(self, 'language_args': defaultdict(list, [(compiler.language, compile_args)]), 'include_directories': include_directories, 'dependencies': dependencies, + 'native': compiler.for_machine, } super().__init__(name, subdir, compiler.for_machine, sources, None, [], environment, compilers, @@ -3338,11 +3340,11 @@ class Jar(BuildTarget): typename = 'jar' rust_crate_type = '' # type: ignore[assignment] - def __init__(self, name: str, subdir: str, for_machine: MachineChoice, + def __init__(self, name: str, subdir: str, orig_for_machine: MachineChoice, sources: T.List[SourceOutputs], structured_sources: T.Optional['StructuredSources'], objects: T.Sequence[ObjectTypes | GeneratedTypes], environment: Environment, compilers: CompilerDict, build_project: BuildProject, kwargs: JarKeywordArguments): - super().__init__(name, subdir, for_machine, sources, structured_sources, objects, + super().__init__(name, subdir, orig_for_machine, sources, structured_sources, objects, environment, compilers, build_project, kwargs) for s in self.sources: if not s.endswith('.java'): diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 342ffacf0243..341281503db9 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -3089,26 +3089,22 @@ def func_add_test_setup(self, node: mparser.BaseNode, args: T.Tuple[str], kwargs @typed_pos_args('add_global_arguments', varargs=str) @typed_kwargs('add_global_arguments', NATIVE_KW, LANGUAGE_KW) - @apply_machine_map def func_add_global_arguments(self, node: mparser.FunctionNode, args: T.Tuple[T.List[str]], kwargs: 'kwtypes.FuncAddProjectArgs') -> None: self._add_global_arguments(node, self.build.global_args[kwargs['native']], args[0], kwargs) @typed_pos_args('add_global_link_arguments', varargs=str) @typed_kwargs('add_global_link_arguments', NATIVE_KW, LANGUAGE_KW) - @apply_machine_map def func_add_global_link_arguments(self, node: mparser.FunctionNode, args: T.Tuple[T.List[str]], kwargs: 'kwtypes.FuncAddProjectArgs') -> None: self._add_global_arguments(node, self.build.global_link_args[kwargs['native']], args[0], kwargs) @typed_pos_args('add_project_arguments', varargs=str) @typed_kwargs('add_project_arguments', NATIVE_KW, LANGUAGE_KW) - @apply_machine_map def func_add_project_arguments(self, node: mparser.FunctionNode, args: T.Tuple[T.List[str]], kwargs: 'kwtypes.FuncAddProjectArgs') -> None: self._add_project_arguments(node, self.current_build_project().project_args[kwargs['native']], args[0], kwargs) @typed_pos_args('add_project_link_arguments', varargs=str) @typed_kwargs('add_project_link_arguments', NATIVE_KW, LANGUAGE_KW) - @apply_machine_map def func_add_project_link_arguments(self, node: mparser.FunctionNode, args: T.Tuple[T.List[str]], kwargs: 'kwtypes.FuncAddProjectArgs') -> None: self._add_project_arguments(node, self.current_build_project().project_link_args[kwargs['native']], args[0], kwargs) @@ -3120,7 +3116,6 @@ def current_build_project(self) -> build.BuildProject: @FeatureNew('add_project_dependencies', '0.63.0') @typed_pos_args('add_project_dependencies', varargs=dependencies.Dependency) @typed_kwargs('add_project_dependencies', NATIVE_KW, LANGUAGE_KW) - @apply_machine_map def func_add_project_dependencies(self, node: mparser.FunctionNode, args: T.Tuple[T.List[dependencies.Dependency]], kwargs: 'kwtypes.FuncAddProjectArgs') -> None: for_machine = kwargs['native'] for lang in kwargs['language']: @@ -3186,18 +3181,8 @@ def _add_project_arguments(self, node: mparser.FunctionNode, argsdict: T.Dict[La args: T.List[str], kwargs: 'kwtypes.FuncAddProjectArgs') -> None: self._add_arguments(node, argsdict, self.project_args_frozen, args, kwargs) - def is_dup_machine(self, for_machine: MachineChoice) -> bool: - # Return whether applying the caller's effect to the build and - # host machine would duplicate the effect - return \ - for_machine is not MachineChoice.HOST and \ - self.build.machine_map[for_machine] is self.build.machine_map.host - def _add_arguments(self, node: mparser.FunctionNode, argsdict: T.Dict[Language, T.List[str]], args_frozen: bool, args: T.List[str], kwargs: 'kwtypes.FuncAddProjectArgs') -> None: - if self.is_dup_machine(kwargs['native']): - return - if args_frozen: msg = f'Tried to use \'{node.func_name.value}\' after a build target has been declared.\n' \ 'This is not permitted. Please declare all arguments before your targets.' @@ -3897,7 +3882,8 @@ def __convert_jar_kwargs(self, node: mparser.BaseNode, kwargs: kwtypes.Jar) -> b # 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'): + 'link_with', 'java_args', 'java_resources', 'main_class', + 'native'): final[arg] = kwargs[arg] # this is not accessable from the DSL, so we need to initialize it @@ -3950,6 +3936,8 @@ def build_target(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargs mlog.debug('Unknown target type:', str(targetclass)) raise RuntimeError('Unreachable code') + # preserved for global and project arguments + orig_for_machine = kwargs['native'] self.apply_machine_map_to_kwargs(kwargs) for_machine = kwargs['native'] @@ -4023,29 +4011,29 @@ def build_target(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargs if targetclass is build.Executable: nkwargs = self.__convert_executable_kwargs( node, T.cast('kwtypes.Executable', kwargs)) - target = build.Executable(name, self.subdir, for_machine, srcs, struct, objs, + target = build.Executable(name, self.subdir, orig_for_machine, srcs, struct, objs, self.environment, self.compilers[for_machine], self.current_build_project(), nkwargs) elif targetclass is build.StaticLibrary: nkwargs = self.__convert_static_library_kwargs( node, T.cast('kwtypes.StaticLibrary', kwargs)) - target = build.StaticLibrary(name, self.subdir, for_machine, srcs, struct, objs, + target = build.StaticLibrary(name, self.subdir, orig_for_machine, srcs, struct, objs, self.environment, self.compilers[for_machine], self.current_build_project(), nkwargs) elif targetclass is build.SharedLibrary: nkwargs = self.__convert_shared_library_kwargs( node, T.cast('kwtypes.SharedLibrary', kwargs)) - target = build.SharedLibrary(name, self.subdir, for_machine, srcs, struct, objs, + target = build.SharedLibrary(name, self.subdir, orig_for_machine, srcs, struct, objs, self.environment, self.compilers[for_machine], self.current_build_project(), nkwargs) target.shared_library_only = shared_library_only elif targetclass is build.SharedModule: nkwargs = self.__convert_shared_module_kwargs( node, T.cast('kwtypes.SharedModule', kwargs)) - target = build.SharedModule(name, self.subdir, for_machine, srcs, struct, objs, + target = build.SharedModule(name, self.subdir, orig_for_machine, srcs, struct, objs, self.environment, self.compilers[for_machine], self.current_build_project(), nkwargs) target.shared_library_only = shared_library_only else: nkwargs = self.__convert_jar_kwargs( node, T.cast('kwtypes.Jar', kwargs)) - target = build.Jar(name, self.subdir, for_machine, srcs, struct, objs, + target = build.Jar(name, self.subdir, orig_for_machine, srcs, struct, objs, self.environment, self.compilers[for_machine], self.current_build_project(), nkwargs) if objs and target.uses_rust(): diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 06749132b70e..19bbf6d673d4 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -5125,10 +5125,11 @@ def test_introspect_install_plan(self): def output_name(name, type_): target = type_(name=name, subdir='', - for_machine=MachineChoice.HOST, sources=[], + orig_for_machine=MachineChoice.HOST, sources=[], structured_sources=None, objects=[], environment=env, compilers=env.coredata.compilers[MachineChoice.HOST], - build_project=BuildProject('', '', SubProject(''), MachineChoice.HOST), kwargs={}) + build_project=BuildProject('', '', SubProject(''), MachineChoice.HOST), + kwargs={'native': MachineChoice.HOST}) target.process_compilers_late() return target.filename From 743ad95145ea2ccc5dcd3cfe235a5186b7ba4e36 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 13 Jul 2026 20:42:02 +0200 Subject: [PATCH 611/681] add testcase for 1.12 regression - add_project_arguments version Reduced from QEMU. Reported-by: Katalin Rebhan Signed-off-by: Paolo Bonzini --- .../include/stdio.h | 3 +++ .../12 add_project_arguments native true/meson.build | 10 ++++++++++ .../native/12 add_project_arguments native true/test.c | 7 +++++++ 3 files changed, 20 insertions(+) create mode 100644 test cases/native/12 add_project_arguments native true/include/stdio.h create mode 100644 test cases/native/12 add_project_arguments native true/meson.build create mode 100644 test cases/native/12 add_project_arguments native true/test.c diff --git a/test cases/native/12 add_project_arguments native true/include/stdio.h b/test cases/native/12 add_project_arguments native true/include/stdio.h new file mode 100644 index 000000000000..3b5e695bf269 --- /dev/null +++ b/test cases/native/12 add_project_arguments native true/include/stdio.h @@ -0,0 +1,3 @@ +#define USING_LOCAL_STDIO 1 + +#include_next diff --git a/test cases/native/12 add_project_arguments native true/meson.build b/test cases/native/12 add_project_arguments native true/meson.build new file mode 100644 index 000000000000..76121948f67a --- /dev/null +++ b/test cases/native/12 add_project_arguments native true/meson.build @@ -0,0 +1,10 @@ +project('test include path', 'c') + +if meson.get_compiler('c').get_id() not in ['gcc', 'clang'] + error('MESON_SKIP_TEST: test needs #include_next <>') +endif + +add_project_arguments('-iquote', meson.current_source_dir() / 'include', language: ['c']) + +e = executable('executable', 'test.c', native: true) +test('executable', e) diff --git a/test cases/native/12 add_project_arguments native true/test.c b/test cases/native/12 add_project_arguments native true/test.c new file mode 100644 index 000000000000..e2e0a402582c --- /dev/null +++ b/test cases/native/12 add_project_arguments native true/test.c @@ -0,0 +1,7 @@ +#include "stdio.h" + +#ifdef USING_LOCAL_STDIO +#error unexpected argument for native: true +#endif + +int main(void) {} From a1f12bb56cf641810165d413c92c4ad973117471 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 14 Jul 2026 10:48:12 +0200 Subject: [PATCH 612/681] build: store original "native: true" setting for subprojects --- mesonbuild/ast/introspection.py | 2 +- mesonbuild/build.py | 3 +++ mesonbuild/interpreter/interpreter.py | 2 +- unittests/allplatformstests.py | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py index 2abf94f92709..1802f8e1bb6b 100644 --- a/mesonbuild/ast/introspection.py +++ b/mesonbuild/ast/introspection.py @@ -76,7 +76,7 @@ def __init__(self, self.cross_file = cross_file self.backend = backend - self.build_project = BuildProject(subproject, '', subproject, MachineChoice.HOST) + self.build_project = BuildProject(subproject, '', subproject, MachineChoice.HOST, MachineChoice.HOST) self.project_data: T.Dict[str, T.Any] = {} self.targets: T.List[IntrospectionBuildTarget] = [] self.dependencies: T.List[IntrospectionDependency] = [] diff --git a/mesonbuild/build.py b/mesonbuild/build.py index bce9162235c6..fa49e9fee8c8 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -307,6 +307,7 @@ class BuildProject: name: str version: str subproject: SubProject + orig_for_machine: MachineChoice for_machine: MachineChoice project_args: PerMachine[T.Dict[Language, T.List[str]]] = field(default_factory=lambda: PerMachine({}, {})) project_link_args: PerMachine[T.Dict[Language, T.List[str]]] = field(default_factory=lambda: PerMachine({}, {})) @@ -323,6 +324,7 @@ class Build: """ def __init__(self, environment: Environment): + self.for_machine = MachineChoice.HOST self.version = coredata.version self._def_files: T.Optional[T.List[str]] = None self.project_name = 'name of master project' @@ -414,6 +416,7 @@ def copy_for_build_machine(self) -> Build: new = self.copy() new.machine_map = MachineMap(self.machine_map.build, self.machine_map.build, ThreeMachineChoice(self.machine_map.host)) + new.for_machine = MachineChoice.BUILD return new def merge(self, other: Build) -> None: diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 341281503db9..1ffc34ff475d 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1389,7 +1389,7 @@ def func_project(self, node: mparser.FunctionNode, args: T.Tuple[str, T.List[str if self.cargo is None: self.load_root_cargo_lock_file() - build_project = build.BuildProject(proj_name, self.project_version, self.subproject, for_machine) + build_project = build.BuildProject(proj_name, self.project_version, self.subproject, self.build.for_machine, for_machine) self.build.projects[for_machine][self.subproject] = build_project extra_args: T.List[mlog.TV_Loggable] = [] diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 19bbf6d673d4..431de8e8fbec 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -5128,7 +5128,7 @@ def output_name(name, type_): orig_for_machine=MachineChoice.HOST, sources=[], structured_sources=None, objects=[], environment=env, compilers=env.coredata.compilers[MachineChoice.HOST], - build_project=BuildProject('', '', SubProject(''), MachineChoice.HOST), + build_project=BuildProject('', '', SubProject(''), MachineChoice.HOST, MachineChoice.HOST), kwargs={'native': MachineChoice.HOST}) target.process_compilers_late() return target.filename From 79837e7825b33179df8e8c6a723f64ab5147b751 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 14 Jul 2026 08:36:09 +0200 Subject: [PATCH 613/681] build, interpreter: handle per-machine global arguments in BuildTarget Per-machine compiler arguments (whether global or project) are separated for "native: true" and "native: false" targets. This means that it is incorrect to simply drop them for "native: true" targets when not cross compiling. For global arguments, the BuildTarget needs to whether "native: true" appeared in the *original* value of native as well as in any subproject call. --- mesonbuild/build.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index fa49e9fee8c8..dc490188608d 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -307,6 +307,13 @@ class BuildProject: name: str version: str subproject: SubProject + # orig_for_machine in BuildProject and BuildTarget is used to choose + # between "native: true" and "native: false" global arguments + # proj.o_f_m target.o_f_m global arguments used + # BUILD HOST BUILD ("native: true") + # BUILD BUILD BUILD ("native: true") + # HOST HOST HOST ("native: false") + # HOST BUILD BUILD ("native: true") orig_for_machine: MachineChoice for_machine: MachineChoice project_args: PerMachine[T.Dict[Language, T.List[str]]] = field(default_factory=lambda: PerMachine({}, {})) @@ -480,7 +487,14 @@ def get_install_subdirs(self) -> T.List['InstallDir']: return self.install_dirs def get_global_args(self, compiler: 'Compiler', target: BuildTarget) -> T.List[str]: - d = self.global_args[target.orig_for_machine] + # for build-machine subprojects, even "native: false" targets use + # the global build-machine arguments (using the host machine would + # make no sense when cross compiling) + args_machine = MachineChoice.BUILD \ + if target.build_project.orig_for_machine is MachineChoice.BUILD \ + else target.orig_for_machine + + d = self.global_args[args_machine] return d.get(compiler.get_language(), []) def get_project_args(self, compiler: 'Compiler', target: BuildTarget) -> T.List[str]: @@ -491,7 +505,11 @@ def get_project_args(self, compiler: 'Compiler', target: BuildTarget) -> T.List[ return args.get(compiler.get_language(), []) def get_global_link_args(self, compiler: 'Compiler', target: BuildTarget) -> T.List[str]: - d = self.global_link_args[target.orig_for_machine] + args_machine = MachineChoice.BUILD \ + if target.build_project.orig_for_machine is MachineChoice.BUILD \ + else target.orig_for_machine + + d = self.global_link_args[args_machine] return d.get(compiler.get_language(), []) def get_project_link_args(self, compiler: 'Compiler', target: BuildTarget) -> T.List[str]: From 93879150951676b1e742c901db6c98dcc45fe9f9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 14 Jul 2026 10:48:26 +0200 Subject: [PATCH 614/681] add testcase for 1.12 regression - add_global_arguments version --- .../include/stdio.h | 3 +++ .../meson.build | 17 +++++++++++++++++ .../subprojects/buildsub/meson.build | 4 ++++ .../subprojects/buildsub/test.c | 7 +++++++ .../subprojects/hostsub/meson.build | 4 ++++ .../subprojects/hostsub/test.c | 7 +++++++ .../13 add_global_arguments native true/test.c | 7 +++++++ 7 files changed, 49 insertions(+) create mode 100644 test cases/native/13 add_global_arguments native true/include/stdio.h create mode 100644 test cases/native/13 add_global_arguments native true/meson.build create mode 100644 test cases/native/13 add_global_arguments native true/subprojects/buildsub/meson.build create mode 100644 test cases/native/13 add_global_arguments native true/subprojects/buildsub/test.c create mode 100644 test cases/native/13 add_global_arguments native true/subprojects/hostsub/meson.build create mode 100644 test cases/native/13 add_global_arguments native true/subprojects/hostsub/test.c create mode 100644 test cases/native/13 add_global_arguments native true/test.c diff --git a/test cases/native/13 add_global_arguments native true/include/stdio.h b/test cases/native/13 add_global_arguments native true/include/stdio.h new file mode 100644 index 000000000000..3b5e695bf269 --- /dev/null +++ b/test cases/native/13 add_global_arguments native true/include/stdio.h @@ -0,0 +1,3 @@ +#define USING_LOCAL_STDIO 1 + +#include_next diff --git a/test cases/native/13 add_global_arguments native true/meson.build b/test cases/native/13 add_global_arguments native true/meson.build new file mode 100644 index 000000000000..22bb32fab4b0 --- /dev/null +++ b/test cases/native/13 add_global_arguments native true/meson.build @@ -0,0 +1,17 @@ +project('test include path', 'c') + +if meson.get_compiler('c').get_id() not in ['gcc', 'clang'] + error('MESON_SKIP_TEST: test needs #include_next <>') +endif + +add_global_arguments('-iquote', meson.current_source_dir() / 'include', language: ['c']) + +e = executable('executable', 'test.c', native: true) +test('executable', e) + +subproject('hostsub') + +# run the same test in a build-side subproject. Even if that subproject +# uses "native: false", it has to use the build-side global arguments +# because nothing else would make sense in a cross compilation scenario. +subproject('buildsub', native: true) diff --git a/test cases/native/13 add_global_arguments native true/subprojects/buildsub/meson.build b/test cases/native/13 add_global_arguments native true/subprojects/buildsub/meson.build new file mode 100644 index 000000000000..a25ff5a3cb90 --- /dev/null +++ b/test cases/native/13 add_global_arguments native true/subprojects/buildsub/meson.build @@ -0,0 +1,4 @@ +project('test include path subproject', 'c') + +e = executable('executable', 'test.c') +test('executable', e) diff --git a/test cases/native/13 add_global_arguments native true/subprojects/buildsub/test.c b/test cases/native/13 add_global_arguments native true/subprojects/buildsub/test.c new file mode 100644 index 000000000000..e2e0a402582c --- /dev/null +++ b/test cases/native/13 add_global_arguments native true/subprojects/buildsub/test.c @@ -0,0 +1,7 @@ +#include "stdio.h" + +#ifdef USING_LOCAL_STDIO +#error unexpected argument for native: true +#endif + +int main(void) {} diff --git a/test cases/native/13 add_global_arguments native true/subprojects/hostsub/meson.build b/test cases/native/13 add_global_arguments native true/subprojects/hostsub/meson.build new file mode 100644 index 000000000000..f953f70d22cc --- /dev/null +++ b/test cases/native/13 add_global_arguments native true/subprojects/hostsub/meson.build @@ -0,0 +1,4 @@ +project('test include path subproject', 'c') + +e = executable('executable', 'test.c', native: true) +test('executable', e) diff --git a/test cases/native/13 add_global_arguments native true/subprojects/hostsub/test.c b/test cases/native/13 add_global_arguments native true/subprojects/hostsub/test.c new file mode 100644 index 000000000000..e2e0a402582c --- /dev/null +++ b/test cases/native/13 add_global_arguments native true/subprojects/hostsub/test.c @@ -0,0 +1,7 @@ +#include "stdio.h" + +#ifdef USING_LOCAL_STDIO +#error unexpected argument for native: true +#endif + +int main(void) {} diff --git a/test cases/native/13 add_global_arguments native true/test.c b/test cases/native/13 add_global_arguments native true/test.c new file mode 100644 index 000000000000..e2e0a402582c --- /dev/null +++ b/test cases/native/13 add_global_arguments native true/test.c @@ -0,0 +1,7 @@ +#include "stdio.h" + +#ifdef USING_LOCAL_STDIO +#error unexpected argument for native: true +#endif + +int main(void) {} From dde29f27fe2605a9e7af58ebbaf4abe691414a86 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 15 Jul 2026 08:55:32 +0200 Subject: [PATCH 615/681] python: fix application of machine map Only apply machine map in the leaf functions, which are _dependency_method_impl and Interpreter.build_target. dependency_method was not applying it while extension_module_method was doing it too early (resulting in double application later in build_target). Signed-off-by: Paolo Bonzini --- mesonbuild/modules/python.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py index 313e90e6a978..697382de60a6 100644 --- a/mesonbuild/modules/python.py +++ b/mesonbuild/modules/python.py @@ -14,7 +14,6 @@ from ..dependencies.detect import get_dep_identifier, find_external_dependency from ..dependencies.python import BasicPythonExternalProgram, python_factory, _PythonDependencyBase from ..interpreter import extract_required_kwarg, primitives as P_OBJ -from ..interpreter.decorators import apply_machine_map from ..interpreter.interpreterobjects import ProgramHolder from ..interpreter.type_checking import NoneType, DEPENDENCY_KWS, PRESERVE_PATH_KW, REQUIRED_KW, SHARED_MOD_KWS from ..interpreterbase import ( @@ -153,7 +152,6 @@ def as_(obj: object, type_: T.Type[_T]) -> _T: _LIMITED_API_KW, KwargInfo('install_dir', (str, bool, NoneType)), ) - @apply_machine_map @InterpreterObject.method('extension_module') def extension_module_method(self, args: T.Tuple[str, T.List[BuildTargetSource]], kwargs: ExtensionModuleKw) -> 'SharedModule': target_kwargs = T.cast('SharedModuleKw', {k: v for k, v in kwargs.items() if k not in {'install_dir', 'subdir', 'limited_api'}}) @@ -204,7 +202,8 @@ def extension_module_method(self, args: T.Tuple[str, T.List[BuildTargetSource]], target_kwargs['cpp_args'] = new_cpp_args # On Windows, the limited API DLL is python3.dll, not python3X.dll. - for_machine = kwargs['native'] + # FIXME: pydep.for_machine is nicer, but InternalDependency does not have the attribute + for_machine = self.interpreter.build.machine_map[kwargs['native']] if self.interpreter.environment.machines[for_machine].is_windows(): pydep_copy = copy.copy(pydep) if isinstance(pydep_copy, _PythonDependencyBase): @@ -270,7 +269,9 @@ def _convert_api_version_to_py_version_hex(self, api_version: str, detected_vers return '0x{:02x}{:02x}0000'.format(major, minor) def _dependency_method_impl(self, kwargs: DependencyObjectKWs) -> Dependency: - for_machine = kwargs['native'] + for_machine = self.interpreter.build.machine_map[kwargs['native']] + kwargs['native'] = for_machine + identifier = get_dep_identifier(self._full_path(), kwargs) dep = self.interpreter.coredata.deps[for_machine].get(identifier) From 14143beea86f8232d79b1f02327971e7890dba3d Mon Sep 17 00:00:00 2001 From: Sam James Date: Sun, 19 Jul 2026 21:34:02 +0100 Subject: [PATCH 616/681] tests: common: respect PKG_CONFIG in handwritten configure script This led to much confusion when attempting to bisect the linked bug. Bug: https://github.com/mesonbuild/meson/issues/16019 --- test cases/common/230 external project/libfoo/configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test cases/common/230 external project/libfoo/configure b/test cases/common/230 external project/libfoo/configure index 853b6f173dc9..c4e9d0aebd84 100755 --- a/test cases/common/230 external project/libfoo/configure +++ b/test cases/common/230 external project/libfoo/configure @@ -44,7 +44,7 @@ case $i in esac done -DEP_ARGS=$(pkg-config --cflags --libs somelib) +DEP_ARGS=$(${PKG_CONFIG:-pkg-config} --cflags --libs somelib) cat > Makefile << EOL all: libfoo.$LIBEXT From 8a95b319733a8cf0fbe34e0d3b52f6bffd94ddf7 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Mon, 20 Jul 2026 23:35:32 +0300 Subject: [PATCH 617/681] Condense directory names for rc2. --- run_format_tests.py | 2 +- .../foo.py | 0 .../meson.build | 0 .../subdir/meson.build | 0 .../lib2.rs | 0 .../main.rs | 0 .../meson.build | 0 .../meson.build | 0 .../unit/{104 strip => 103 strip}/lib.c | 0 .../unit/{104 strip => 103 strip}/meson.build | 0 .../meson.build | 0 .../meson.build | 0 .../main.c | 0 .../meson.build | 0 .../runner.py | 0 .../cp.py | 0 .../main.c | 0 .../meson.build | 0 .../symlinked_subproject/datadir/datafile | 0 .../symlinked_subproject/datadir/meson.build | 0 .../symlinked_subproject/meson.build | 0 .../symlinked_subproject/src.c | 0 .../meson.build | 0 .../meson_options.txt | 0 .../subprojects/foo/foo.c | 0 .../subprojects/foo/meson.build | 0 .../meson.build | 0 .../meson_options.txt | 0 .../unit/{111 freeze => 110 freeze}/freeze.c | 0 .../{111 freeze => 110 freeze}/meson.build | 0 .../meson.build | 0 .../script.py | 0 .../com/mesonbuild/Simple.java | 0 .../meson.build | 0 .../meson.build | 0 .../meson_options.txt | 0 .../main.c | 0 .../meson.build | 0 .../s1.c | 0 .../s2.c | 0 .../s3.c | 0 .../meson.build | 0 .../meson.options | 0 .../expected_mods.json | 0 .../meson.build | 0 .../{118 genvslite => 117 genvslite}/main.cpp | 0 .../meson.build | 0 .../cache_dir/bar/meson.build | 0 .../cache_dir/foo.zip | Bin .../meson.build | 0 .../subprojects/bar.wrap | 0 .../subprojects/foo.wrap | 0 .../meson.build | 0 .../nativefile.ini | 0 .../{121 rewrite => 120 rewrite}/meson.build | 0 .../main.c | 0 .../meson.build | 0 .../meson.build | 0 .../meson.options | 0 .../subprojects/sub1/meson.build | 0 .../subprojects/sub1/meson.options | 0 .../subprojects/sub1/sub1.c | 0 .../subprojects/sub2/meson.build | 0 .../subprojects/sub2/meson.options | 0 .../subprojects/sub2/sub2.c | 0 .../toplevel.c | 0 .../meson.build | 0 .../subprojects/sub/meson.build | 0 .../meson.build | 0 .../script.py | 0 .../meson.build | 0 .../meson_options.txt | 0 .../foo.c | 0 .../meson.build | 0 .../meson.build | 0 .../subprojects/sub/meson.build | 0 .../meson.build | 0 .../test.py | 0 .../meson.build | 0 .../meson.build | 0 .../meson_options.txt | 0 .../lib.vala | 0 .../meson.build | 0 .../meson.options | 0 .../meson.build | 0 .../meson.build | 0 .../consumer.c | 0 .../header.h | 0 .../lib.cpp | 0 .../meson.build | 0 .../header.h | 0 .../meson.build | 0 .../meson.options | 0 .../meson.build | 0 .../subprojects/recursive-both/lib.c | 0 .../subprojects/recursive-both/lib.def | 0 .../subprojects/recursive-both/meson.build | 0 .../recursive-both/recursive-both.h.in | 0 .../meson.build | 0 .../meson_options.txt | 0 .../propfile | 0 .../foo.c | 0 .../foo.dat | 0 .../foo.h | 0 .../foo/foofile | 0 .../meson.build | 0 .../subprojects/bar/bar.c | 0 .../subprojects/bar/bar.dat | 0 .../subprojects/bar/bar.h | 0 .../subprojects/bar/bar/barfile | 0 .../subprojects/bar/meson.build | 0 .../meson.build | 0 .../meson_options.txt | 0 .../subprojects/sub/foo.c | 0 .../subprojects/sub/meson.build | 0 .../.clang-format | 0 .../.clang-format-ignore | 0 .../.clang-format-include | 0 .../meson.build | 0 .../not-included/badformat.cpp | 0 .../src/badformat.c | 0 .../src/badformat.cpp | 0 .../easytogrepfor/genh.py | 0 .../easytogrepfor/meson.build | 0 .../{95 custominc => 94 custominc}/helper.c | 0 .../meson.build | 0 .../{95 custominc => 94 custominc}/prog.c | 0 .../{95 custominc => 94 custominc}/prog2.c | 0 .../meson.build | 0 .../subprojects/something/meson.build | 0 .../meson.build | 0 .../test.c | 0 .../.gitignore | 0 .../libtestprovider/meson.build | 0 .../libtestprovider/provider.c | 0 .../proguser/meson.build | 0 .../proguser/receiver.c | 0 .../bar-custom.txt | 0 .../bar-devel.h | 0 .../bar-notag.txt | 0 .../custom_files/data.txt | 0 .../excludes/excluded.txt | 0 .../excludes/excluded/placeholder.txt | 0 .../excludes/installed.txt | 0 .../foo.in | 0 .../foo1-devel.h | 0 .../lib.c | 0 .../main.c | 0 .../meson.build | 0 .../script.py | 0 .../subdir/bar2-devel.h | 0 .../subdir/foo2.in | 0 .../subdir/foo3-devel.h | 0 .../subdir/lib.c | 0 .../subdir/main.c | 0 .../subdir/meson.build | 0 .../subdir/script.py | 0 .../subprojects/subproject/aaa.txt | 0 .../subprojects/subproject/bbb.txt | 0 .../subprojects/subproject/meson.build | 0 .../file.txt.in | 0 .../meson.build | 0 .../subdir/meson.build | 0 unittests/allplatformstests.py | 50 +++++++++--------- unittests/linuxliketests.py | 18 +++---- unittests/machinefiletests.py | 12 ++--- unittests/platformagnostictests.py | 20 +++---- unittests/rewritetests.py | 4 +- unittests/windowstests.py | 2 +- 169 files changed, 54 insertions(+), 54 deletions(-) rename test cases/unit/{101 relative find program => 100 relative find program}/foo.py (100%) rename test cases/unit/{101 relative find program => 100 relative find program}/meson.build (100%) rename test cases/unit/{101 relative find program => 100 relative find program}/subdir/meson.build (100%) rename test cases/unit/{102 rlib linkage => 101 rlib linkage}/lib2.rs (100%) rename test cases/unit/{102 rlib linkage => 101 rlib linkage}/main.rs (100%) rename test cases/unit/{102 rlib linkage => 101 rlib linkage}/meson.build (100%) rename test cases/unit/{103 python without pkgconfig => 102 python without pkgconfig}/meson.build (100%) rename test cases/unit/{104 strip => 103 strip}/lib.c (100%) rename test cases/unit/{104 strip => 103 strip}/meson.build (100%) rename test cases/unit/{105 debug function => 104 debug function}/meson.build (100%) rename test cases/unit/{106 pkgconfig relocatable with absolute path => 105 pkgconfig relocatable with absolute path}/meson.build (100%) rename test cases/unit/{107 underspecified mtest => 106 underspecified mtest}/main.c (100%) rename test cases/unit/{107 underspecified mtest => 106 underspecified mtest}/meson.build (100%) rename test cases/unit/{107 underspecified mtest => 106 underspecified mtest}/runner.py (100%) rename test cases/unit/{108 subproject symlink => 107 subproject symlink}/cp.py (100%) rename test cases/unit/{108 subproject symlink => 107 subproject symlink}/main.c (100%) rename test cases/unit/{108 subproject symlink => 107 subproject symlink}/meson.build (100%) rename test cases/unit/{108 subproject symlink => 107 subproject symlink}/symlinked_subproject/datadir/datafile (100%) rename test cases/unit/{108 subproject symlink => 107 subproject symlink}/symlinked_subproject/datadir/meson.build (100%) rename test cases/unit/{108 subproject symlink => 107 subproject symlink}/symlinked_subproject/meson.build (100%) rename test cases/unit/{108 subproject symlink => 107 subproject symlink}/symlinked_subproject/src.c (100%) rename test cases/unit/{109 new subproject on reconfigure => 108 new subproject on reconfigure}/meson.build (100%) rename test cases/unit/{109 new subproject on reconfigure => 108 new subproject on reconfigure}/meson_options.txt (100%) rename test cases/unit/{109 new subproject on reconfigure => 108 new subproject on reconfigure}/subprojects/foo/foo.c (100%) rename test cases/unit/{109 new subproject on reconfigure => 108 new subproject on reconfigure}/subprojects/foo/meson.build (100%) rename test cases/unit/{110 configure same noop => 109 configure same noop}/meson.build (100%) rename test cases/unit/{110 configure same noop => 109 configure same noop}/meson_options.txt (100%) rename test cases/unit/{111 freeze => 110 freeze}/freeze.c (100%) rename test cases/unit/{111 freeze => 110 freeze}/meson.build (100%) rename test cases/unit/{112 replace unencodable xml chars => 111 replace unencodable xml chars}/meson.build (100%) rename test cases/unit/{112 replace unencodable xml chars => 111 replace unencodable xml chars}/script.py (100%) rename test cases/unit/{113 classpath => 112 classpath}/com/mesonbuild/Simple.java (100%) rename test cases/unit/{113 classpath => 112 classpath}/meson.build (100%) rename test cases/unit/{114 list build options => 113 list build options}/meson.build (100%) rename test cases/unit/{114 list build options => 113 list build options}/meson_options.txt (100%) rename test cases/unit/{115 complex link cases => 114 complex link cases}/main.c (100%) rename test cases/unit/{115 complex link cases => 114 complex link cases}/meson.build (100%) rename test cases/unit/{115 complex link cases => 114 complex link cases}/s1.c (100%) rename test cases/unit/{115 complex link cases => 114 complex link cases}/s2.c (100%) rename test cases/unit/{115 complex link cases => 114 complex link cases}/s3.c (100%) rename test cases/unit/{116 c cpp stds => 115 c cpp stds}/meson.build (100%) rename test cases/unit/{116 c cpp stds => 115 c cpp stds}/meson.options (100%) rename test cases/unit/{117 empty project => 116 empty project}/expected_mods.json (100%) rename test cases/unit/{117 empty project => 116 empty project}/meson.build (100%) rename test cases/unit/{118 genvslite => 117 genvslite}/main.cpp (100%) rename test cases/unit/{118 genvslite => 117 genvslite}/meson.build (100%) rename test cases/unit/{119 meson package cache dir => 118 meson package cache dir}/cache_dir/bar/meson.build (100%) rename test cases/unit/{119 meson package cache dir => 118 meson package cache dir}/cache_dir/foo.zip (100%) rename test cases/unit/{119 meson package cache dir => 118 meson package cache dir}/meson.build (100%) rename test cases/unit/{119 meson package cache dir => 118 meson package cache dir}/subprojects/bar.wrap (100%) rename test cases/unit/{119 meson package cache dir => 118 meson package cache dir}/subprojects/foo.wrap (100%) rename test cases/unit/{120 openssl cmake bug => 119 openssl cmake bug}/meson.build (100%) rename test cases/unit/{120 openssl cmake bug => 119 openssl cmake bug}/nativefile.ini (100%) rename test cases/unit/{121 rewrite => 120 rewrite}/meson.build (100%) rename test cases/unit/{122 executable suffix => 121 executable suffix}/main.c (100%) rename test cases/unit/{122 executable suffix => 121 executable suffix}/meson.build (100%) rename test cases/unit/{123 persp options => 122 persp options}/meson.build (100%) rename test cases/unit/{123 persp options => 122 persp options}/meson.options (100%) rename test cases/unit/{123 persp options => 122 persp options}/subprojects/sub1/meson.build (100%) rename test cases/unit/{123 persp options => 122 persp options}/subprojects/sub1/meson.options (100%) rename test cases/unit/{123 persp options => 122 persp options}/subprojects/sub1/sub1.c (100%) rename test cases/unit/{123 persp options => 122 persp options}/subprojects/sub2/meson.build (100%) rename test cases/unit/{123 persp options => 122 persp options}/subprojects/sub2/meson.options (100%) rename test cases/unit/{123 persp options => 122 persp options}/subprojects/sub2/sub2.c (100%) rename test cases/unit/{123 persp options => 122 persp options}/toplevel.c (100%) rename test cases/unit/{124 reconfigure base options => 123 reconfigure base options}/meson.build (100%) rename test cases/unit/{124 reconfigure base options => 123 reconfigure base options}/subprojects/sub/meson.build (100%) rename test cases/unit/{125 interactive tap => 124 interactive tap}/meson.build (100%) rename test cases/unit/{125 interactive tap => 124 interactive tap}/script.py (100%) rename test cases/unit/{126 declare_dep var => 125 declare_dep var}/meson.build (100%) rename test cases/unit/{126 declare_dep var => 125 declare_dep var}/meson_options.txt (100%) rename test cases/unit/{127 python extension => 126 python extension}/foo.c (100%) rename test cases/unit/{127 python extension => 126 python extension}/meson.build (100%) rename test cases/unit/{128 pkgsubproj => 127 pkgsubproj}/meson.build (100%) rename test cases/unit/{128 pkgsubproj => 127 pkgsubproj}/subprojects/sub/meson.build (100%) rename test cases/unit/{129 test slice => 128 test slice}/meson.build (100%) rename test cases/unit/{129 test slice => 128 test slice}/test.py (100%) rename test cases/unit/{130 sanitizers => 129 sanitizers}/meson.build (100%) rename test cases/unit/{131 long opt vs D => 130 long opt vs D}/meson.build (100%) rename test cases/unit/{131 long opt vs D => 130 long opt vs D}/meson_options.txt (100%) rename test cases/unit/{132 vala internal glib => 131 vala internal glib}/lib.vala (100%) rename test cases/unit/{132 vala internal glib => 131 vala internal glib}/meson.build (100%) rename test cases/unit/{132 vala internal glib => 131 vala internal glib}/meson.options (100%) rename test cases/unit/{133 custom target index test => 132 custom target index test}/meson.build (100%) rename test cases/unit/{134 nasm language only => 133 nasm language only}/meson.build (100%) rename test cases/unit/{135 promote link_language => 134 promote link_language}/consumer.c (100%) rename test cases/unit/{135 promote link_language => 134 promote link_language}/header.h (100%) rename test cases/unit/{135 promote link_language => 134 promote link_language}/lib.cpp (100%) rename test cases/unit/{135 promote link_language => 134 promote link_language}/meson.build (100%) rename test cases/unit/{136 minimal bindgen => 135 minimal bindgen}/header.h (100%) rename test cases/unit/{136 minimal bindgen => 135 minimal bindgen}/meson.build (100%) rename test cases/unit/{136 minimal bindgen => 135 minimal bindgen}/meson.options (100%) rename test cases/unit/{137 native subproject introspect => 136 native subproject introspect}/meson.build (100%) rename test cases/unit/{137 native subproject introspect => 136 native subproject introspect}/subprojects/recursive-both/lib.c (100%) rename test cases/unit/{137 native subproject introspect => 136 native subproject introspect}/subprojects/recursive-both/lib.def (100%) rename test cases/unit/{137 native subproject introspect => 136 native subproject introspect}/subprojects/recursive-both/meson.build (100%) rename test cases/unit/{137 native subproject introspect => 136 native subproject introspect}/subprojects/recursive-both/recursive-both.h.in (100%) rename test cases/unit/{138 external property nonexisting => 137 external property nonexisting}/meson.build (100%) rename test cases/unit/{138 external property nonexisting => 137 external property nonexisting}/meson_options.txt (100%) rename test cases/unit/{138 external property nonexisting => 137 external property nonexisting}/propfile (100%) rename test cases/unit/{92 install skip subprojects => 91 install skip subprojects}/foo.c (100%) rename test cases/unit/{92 install skip subprojects => 91 install skip subprojects}/foo.dat (100%) rename test cases/unit/{92 install skip subprojects => 91 install skip subprojects}/foo.h (100%) rename test cases/unit/{92 install skip subprojects => 91 install skip subprojects}/foo/foofile (100%) rename test cases/unit/{92 install skip subprojects => 91 install skip subprojects}/meson.build (100%) rename test cases/unit/{92 install skip subprojects => 91 install skip subprojects}/subprojects/bar/bar.c (100%) rename test cases/unit/{92 install skip subprojects => 91 install skip subprojects}/subprojects/bar/bar.dat (100%) rename test cases/unit/{92 install skip subprojects => 91 install skip subprojects}/subprojects/bar/bar.h (100%) rename test cases/unit/{92 install skip subprojects => 91 install skip subprojects}/subprojects/bar/bar/barfile (100%) rename test cases/unit/{92 install skip subprojects => 91 install skip subprojects}/subprojects/bar/meson.build (100%) rename test cases/unit/{93 new subproject in configured project => 92 new subproject in configured project}/meson.build (100%) rename test cases/unit/{93 new subproject in configured project => 92 new subproject in configured project}/meson_options.txt (100%) rename test cases/unit/{93 new subproject in configured project => 92 new subproject in configured project}/subprojects/sub/foo.c (100%) rename test cases/unit/{93 new subproject in configured project => 92 new subproject in configured project}/subprojects/sub/meson.build (100%) rename test cases/unit/{94 clangformat => 93 clangformat}/.clang-format (100%) rename test cases/unit/{94 clangformat => 93 clangformat}/.clang-format-ignore (100%) rename test cases/unit/{94 clangformat => 93 clangformat}/.clang-format-include (100%) rename test cases/unit/{94 clangformat => 93 clangformat}/meson.build (100%) rename test cases/unit/{94 clangformat => 93 clangformat}/not-included/badformat.cpp (100%) rename test cases/unit/{94 clangformat => 93 clangformat}/src/badformat.c (100%) rename test cases/unit/{94 clangformat => 93 clangformat}/src/badformat.cpp (100%) rename test cases/unit/{95 custominc => 94 custominc}/easytogrepfor/genh.py (100%) rename test cases/unit/{95 custominc => 94 custominc}/easytogrepfor/meson.build (100%) rename test cases/unit/{95 custominc => 94 custominc}/helper.c (100%) rename test cases/unit/{95 custominc => 94 custominc}/meson.build (100%) rename test cases/unit/{95 custominc => 94 custominc}/prog.c (100%) rename test cases/unit/{95 custominc => 94 custominc}/prog2.c (100%) rename test cases/unit/{96 implicit force fallback => 95 implicit force fallback}/meson.build (100%) rename test cases/unit/{96 implicit force fallback => 95 implicit force fallback}/subprojects/something/meson.build (100%) rename test cases/unit/{97 compiler.links file arg => 96 compiler.links file arg}/meson.build (100%) rename test cases/unit/{97 compiler.links file arg => 96 compiler.links file arg}/test.c (100%) rename test cases/unit/{98 link full name => 97 link full name}/.gitignore (100%) rename test cases/unit/{98 link full name => 97 link full name}/libtestprovider/meson.build (100%) rename test cases/unit/{98 link full name => 97 link full name}/libtestprovider/provider.c (100%) rename test cases/unit/{98 link full name => 97 link full name}/proguser/meson.build (100%) rename test cases/unit/{98 link full name => 97 link full name}/proguser/receiver.c (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/bar-custom.txt (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/bar-devel.h (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/bar-notag.txt (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/custom_files/data.txt (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/excludes/excluded.txt (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/excludes/excluded/placeholder.txt (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/excludes/installed.txt (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/foo.in (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/foo1-devel.h (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/lib.c (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/main.c (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/meson.build (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/script.py (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/subdir/bar2-devel.h (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/subdir/foo2.in (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/subdir/foo3-devel.h (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/subdir/lib.c (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/subdir/main.c (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/subdir/meson.build (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/subdir/script.py (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/subprojects/subproject/aaa.txt (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/subprojects/subproject/bbb.txt (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/subprojects/subproject/meson.build (100%) rename test cases/unit/{100 custom target name => 99 custom target name}/file.txt.in (100%) rename test cases/unit/{100 custom target name => 99 custom target name}/meson.build (100%) rename test cases/unit/{100 custom target name => 99 custom target name}/subdir/meson.build (100%) diff --git a/run_format_tests.py b/run_format_tests.py index dee787b332ae..30c975882d9b 100755 --- a/run_format_tests.py +++ b/run_format_tests.py @@ -51,7 +51,7 @@ def check_format() -> None: 'work area', '.eggs', '_cache', # e.g. .mypy_cache 'venv', # virtualenvs have DOS line endings - '121 rewrite', # we explicitly test for tab in meson.build file + '120 rewrite', # we explicitly test for tab in meson.build file '3 editorconfig', } for (root, _, filenames) in os.walk('.'): diff --git a/test cases/unit/101 relative find program/foo.py b/test cases/unit/100 relative find program/foo.py similarity index 100% rename from test cases/unit/101 relative find program/foo.py rename to test cases/unit/100 relative find program/foo.py diff --git a/test cases/unit/101 relative find program/meson.build b/test cases/unit/100 relative find program/meson.build similarity index 100% rename from test cases/unit/101 relative find program/meson.build rename to test cases/unit/100 relative find program/meson.build diff --git a/test cases/unit/101 relative find program/subdir/meson.build b/test cases/unit/100 relative find program/subdir/meson.build similarity index 100% rename from test cases/unit/101 relative find program/subdir/meson.build rename to test cases/unit/100 relative find program/subdir/meson.build diff --git a/test cases/unit/102 rlib linkage/lib2.rs b/test cases/unit/101 rlib linkage/lib2.rs similarity index 100% rename from test cases/unit/102 rlib linkage/lib2.rs rename to test cases/unit/101 rlib linkage/lib2.rs diff --git a/test cases/unit/102 rlib linkage/main.rs b/test cases/unit/101 rlib linkage/main.rs similarity index 100% rename from test cases/unit/102 rlib linkage/main.rs rename to test cases/unit/101 rlib linkage/main.rs diff --git a/test cases/unit/102 rlib linkage/meson.build b/test cases/unit/101 rlib linkage/meson.build similarity index 100% rename from test cases/unit/102 rlib linkage/meson.build rename to test cases/unit/101 rlib linkage/meson.build diff --git a/test cases/unit/103 python without pkgconfig/meson.build b/test cases/unit/102 python without pkgconfig/meson.build similarity index 100% rename from test cases/unit/103 python without pkgconfig/meson.build rename to test cases/unit/102 python without pkgconfig/meson.build diff --git a/test cases/unit/104 strip/lib.c b/test cases/unit/103 strip/lib.c similarity index 100% rename from test cases/unit/104 strip/lib.c rename to test cases/unit/103 strip/lib.c diff --git a/test cases/unit/104 strip/meson.build b/test cases/unit/103 strip/meson.build similarity index 100% rename from test cases/unit/104 strip/meson.build rename to test cases/unit/103 strip/meson.build diff --git a/test cases/unit/105 debug function/meson.build b/test cases/unit/104 debug function/meson.build similarity index 100% rename from test cases/unit/105 debug function/meson.build rename to test cases/unit/104 debug function/meson.build diff --git a/test cases/unit/106 pkgconfig relocatable with absolute path/meson.build b/test cases/unit/105 pkgconfig relocatable with absolute path/meson.build similarity index 100% rename from test cases/unit/106 pkgconfig relocatable with absolute path/meson.build rename to test cases/unit/105 pkgconfig relocatable with absolute path/meson.build diff --git a/test cases/unit/107 underspecified mtest/main.c b/test cases/unit/106 underspecified mtest/main.c similarity index 100% rename from test cases/unit/107 underspecified mtest/main.c rename to test cases/unit/106 underspecified mtest/main.c diff --git a/test cases/unit/107 underspecified mtest/meson.build b/test cases/unit/106 underspecified mtest/meson.build similarity index 100% rename from test cases/unit/107 underspecified mtest/meson.build rename to test cases/unit/106 underspecified mtest/meson.build diff --git a/test cases/unit/107 underspecified mtest/runner.py b/test cases/unit/106 underspecified mtest/runner.py similarity index 100% rename from test cases/unit/107 underspecified mtest/runner.py rename to test cases/unit/106 underspecified mtest/runner.py diff --git a/test cases/unit/108 subproject symlink/cp.py b/test cases/unit/107 subproject symlink/cp.py similarity index 100% rename from test cases/unit/108 subproject symlink/cp.py rename to test cases/unit/107 subproject symlink/cp.py diff --git a/test cases/unit/108 subproject symlink/main.c b/test cases/unit/107 subproject symlink/main.c similarity index 100% rename from test cases/unit/108 subproject symlink/main.c rename to test cases/unit/107 subproject symlink/main.c diff --git a/test cases/unit/108 subproject symlink/meson.build b/test cases/unit/107 subproject symlink/meson.build similarity index 100% rename from test cases/unit/108 subproject symlink/meson.build rename to test cases/unit/107 subproject symlink/meson.build diff --git a/test cases/unit/108 subproject symlink/symlinked_subproject/datadir/datafile b/test cases/unit/107 subproject symlink/symlinked_subproject/datadir/datafile similarity index 100% rename from test cases/unit/108 subproject symlink/symlinked_subproject/datadir/datafile rename to test cases/unit/107 subproject symlink/symlinked_subproject/datadir/datafile diff --git a/test cases/unit/108 subproject symlink/symlinked_subproject/datadir/meson.build b/test cases/unit/107 subproject symlink/symlinked_subproject/datadir/meson.build similarity index 100% rename from test cases/unit/108 subproject symlink/symlinked_subproject/datadir/meson.build rename to test cases/unit/107 subproject symlink/symlinked_subproject/datadir/meson.build diff --git a/test cases/unit/108 subproject symlink/symlinked_subproject/meson.build b/test cases/unit/107 subproject symlink/symlinked_subproject/meson.build similarity index 100% rename from test cases/unit/108 subproject symlink/symlinked_subproject/meson.build rename to test cases/unit/107 subproject symlink/symlinked_subproject/meson.build diff --git a/test cases/unit/108 subproject symlink/symlinked_subproject/src.c b/test cases/unit/107 subproject symlink/symlinked_subproject/src.c similarity index 100% rename from test cases/unit/108 subproject symlink/symlinked_subproject/src.c rename to test cases/unit/107 subproject symlink/symlinked_subproject/src.c diff --git a/test cases/unit/109 new subproject on reconfigure/meson.build b/test cases/unit/108 new subproject on reconfigure/meson.build similarity index 100% rename from test cases/unit/109 new subproject on reconfigure/meson.build rename to test cases/unit/108 new subproject on reconfigure/meson.build diff --git a/test cases/unit/109 new subproject on reconfigure/meson_options.txt b/test cases/unit/108 new subproject on reconfigure/meson_options.txt similarity index 100% rename from test cases/unit/109 new subproject on reconfigure/meson_options.txt rename to test cases/unit/108 new subproject on reconfigure/meson_options.txt diff --git a/test cases/unit/109 new subproject on reconfigure/subprojects/foo/foo.c b/test cases/unit/108 new subproject on reconfigure/subprojects/foo/foo.c similarity index 100% rename from test cases/unit/109 new subproject on reconfigure/subprojects/foo/foo.c rename to test cases/unit/108 new subproject on reconfigure/subprojects/foo/foo.c diff --git a/test cases/unit/109 new subproject on reconfigure/subprojects/foo/meson.build b/test cases/unit/108 new subproject on reconfigure/subprojects/foo/meson.build similarity index 100% rename from test cases/unit/109 new subproject on reconfigure/subprojects/foo/meson.build rename to test cases/unit/108 new subproject on reconfigure/subprojects/foo/meson.build diff --git a/test cases/unit/110 configure same noop/meson.build b/test cases/unit/109 configure same noop/meson.build similarity index 100% rename from test cases/unit/110 configure same noop/meson.build rename to test cases/unit/109 configure same noop/meson.build diff --git a/test cases/unit/110 configure same noop/meson_options.txt b/test cases/unit/109 configure same noop/meson_options.txt similarity index 100% rename from test cases/unit/110 configure same noop/meson_options.txt rename to test cases/unit/109 configure same noop/meson_options.txt diff --git a/test cases/unit/111 freeze/freeze.c b/test cases/unit/110 freeze/freeze.c similarity index 100% rename from test cases/unit/111 freeze/freeze.c rename to test cases/unit/110 freeze/freeze.c diff --git a/test cases/unit/111 freeze/meson.build b/test cases/unit/110 freeze/meson.build similarity index 100% rename from test cases/unit/111 freeze/meson.build rename to test cases/unit/110 freeze/meson.build diff --git a/test cases/unit/112 replace unencodable xml chars/meson.build b/test cases/unit/111 replace unencodable xml chars/meson.build similarity index 100% rename from test cases/unit/112 replace unencodable xml chars/meson.build rename to test cases/unit/111 replace unencodable xml chars/meson.build diff --git a/test cases/unit/112 replace unencodable xml chars/script.py b/test cases/unit/111 replace unencodable xml chars/script.py similarity index 100% rename from test cases/unit/112 replace unencodable xml chars/script.py rename to test cases/unit/111 replace unencodable xml chars/script.py diff --git a/test cases/unit/113 classpath/com/mesonbuild/Simple.java b/test cases/unit/112 classpath/com/mesonbuild/Simple.java similarity index 100% rename from test cases/unit/113 classpath/com/mesonbuild/Simple.java rename to test cases/unit/112 classpath/com/mesonbuild/Simple.java diff --git a/test cases/unit/113 classpath/meson.build b/test cases/unit/112 classpath/meson.build similarity index 100% rename from test cases/unit/113 classpath/meson.build rename to test cases/unit/112 classpath/meson.build diff --git a/test cases/unit/114 list build options/meson.build b/test cases/unit/113 list build options/meson.build similarity index 100% rename from test cases/unit/114 list build options/meson.build rename to test cases/unit/113 list build options/meson.build diff --git a/test cases/unit/114 list build options/meson_options.txt b/test cases/unit/113 list build options/meson_options.txt similarity index 100% rename from test cases/unit/114 list build options/meson_options.txt rename to test cases/unit/113 list build options/meson_options.txt diff --git a/test cases/unit/115 complex link cases/main.c b/test cases/unit/114 complex link cases/main.c similarity index 100% rename from test cases/unit/115 complex link cases/main.c rename to test cases/unit/114 complex link cases/main.c diff --git a/test cases/unit/115 complex link cases/meson.build b/test cases/unit/114 complex link cases/meson.build similarity index 100% rename from test cases/unit/115 complex link cases/meson.build rename to test cases/unit/114 complex link cases/meson.build diff --git a/test cases/unit/115 complex link cases/s1.c b/test cases/unit/114 complex link cases/s1.c similarity index 100% rename from test cases/unit/115 complex link cases/s1.c rename to test cases/unit/114 complex link cases/s1.c diff --git a/test cases/unit/115 complex link cases/s2.c b/test cases/unit/114 complex link cases/s2.c similarity index 100% rename from test cases/unit/115 complex link cases/s2.c rename to test cases/unit/114 complex link cases/s2.c diff --git a/test cases/unit/115 complex link cases/s3.c b/test cases/unit/114 complex link cases/s3.c similarity index 100% rename from test cases/unit/115 complex link cases/s3.c rename to test cases/unit/114 complex link cases/s3.c diff --git a/test cases/unit/116 c cpp stds/meson.build b/test cases/unit/115 c cpp stds/meson.build similarity index 100% rename from test cases/unit/116 c cpp stds/meson.build rename to test cases/unit/115 c cpp stds/meson.build diff --git a/test cases/unit/116 c cpp stds/meson.options b/test cases/unit/115 c cpp stds/meson.options similarity index 100% rename from test cases/unit/116 c cpp stds/meson.options rename to test cases/unit/115 c cpp stds/meson.options diff --git a/test cases/unit/117 empty project/expected_mods.json b/test cases/unit/116 empty project/expected_mods.json similarity index 100% rename from test cases/unit/117 empty project/expected_mods.json rename to test cases/unit/116 empty project/expected_mods.json diff --git a/test cases/unit/117 empty project/meson.build b/test cases/unit/116 empty project/meson.build similarity index 100% rename from test cases/unit/117 empty project/meson.build rename to test cases/unit/116 empty project/meson.build diff --git a/test cases/unit/118 genvslite/main.cpp b/test cases/unit/117 genvslite/main.cpp similarity index 100% rename from test cases/unit/118 genvslite/main.cpp rename to test cases/unit/117 genvslite/main.cpp diff --git a/test cases/unit/118 genvslite/meson.build b/test cases/unit/117 genvslite/meson.build similarity index 100% rename from test cases/unit/118 genvslite/meson.build rename to test cases/unit/117 genvslite/meson.build diff --git a/test cases/unit/119 meson package cache dir/cache_dir/bar/meson.build b/test cases/unit/118 meson package cache dir/cache_dir/bar/meson.build similarity index 100% rename from test cases/unit/119 meson package cache dir/cache_dir/bar/meson.build rename to test cases/unit/118 meson package cache dir/cache_dir/bar/meson.build diff --git a/test cases/unit/119 meson package cache dir/cache_dir/foo.zip b/test cases/unit/118 meson package cache dir/cache_dir/foo.zip similarity index 100% rename from test cases/unit/119 meson package cache dir/cache_dir/foo.zip rename to test cases/unit/118 meson package cache dir/cache_dir/foo.zip diff --git a/test cases/unit/119 meson package cache dir/meson.build b/test cases/unit/118 meson package cache dir/meson.build similarity index 100% rename from test cases/unit/119 meson package cache dir/meson.build rename to test cases/unit/118 meson package cache dir/meson.build diff --git a/test cases/unit/119 meson package cache dir/subprojects/bar.wrap b/test cases/unit/118 meson package cache dir/subprojects/bar.wrap similarity index 100% rename from test cases/unit/119 meson package cache dir/subprojects/bar.wrap rename to test cases/unit/118 meson package cache dir/subprojects/bar.wrap diff --git a/test cases/unit/119 meson package cache dir/subprojects/foo.wrap b/test cases/unit/118 meson package cache dir/subprojects/foo.wrap similarity index 100% rename from test cases/unit/119 meson package cache dir/subprojects/foo.wrap rename to test cases/unit/118 meson package cache dir/subprojects/foo.wrap diff --git a/test cases/unit/120 openssl cmake bug/meson.build b/test cases/unit/119 openssl cmake bug/meson.build similarity index 100% rename from test cases/unit/120 openssl cmake bug/meson.build rename to test cases/unit/119 openssl cmake bug/meson.build diff --git a/test cases/unit/120 openssl cmake bug/nativefile.ini b/test cases/unit/119 openssl cmake bug/nativefile.ini similarity index 100% rename from test cases/unit/120 openssl cmake bug/nativefile.ini rename to test cases/unit/119 openssl cmake bug/nativefile.ini diff --git a/test cases/unit/121 rewrite/meson.build b/test cases/unit/120 rewrite/meson.build similarity index 100% rename from test cases/unit/121 rewrite/meson.build rename to test cases/unit/120 rewrite/meson.build diff --git a/test cases/unit/122 executable suffix/main.c b/test cases/unit/121 executable suffix/main.c similarity index 100% rename from test cases/unit/122 executable suffix/main.c rename to test cases/unit/121 executable suffix/main.c diff --git a/test cases/unit/122 executable suffix/meson.build b/test cases/unit/121 executable suffix/meson.build similarity index 100% rename from test cases/unit/122 executable suffix/meson.build rename to test cases/unit/121 executable suffix/meson.build diff --git a/test cases/unit/123 persp options/meson.build b/test cases/unit/122 persp options/meson.build similarity index 100% rename from test cases/unit/123 persp options/meson.build rename to test cases/unit/122 persp options/meson.build diff --git a/test cases/unit/123 persp options/meson.options b/test cases/unit/122 persp options/meson.options similarity index 100% rename from test cases/unit/123 persp options/meson.options rename to test cases/unit/122 persp options/meson.options diff --git a/test cases/unit/123 persp options/subprojects/sub1/meson.build b/test cases/unit/122 persp options/subprojects/sub1/meson.build similarity index 100% rename from test cases/unit/123 persp options/subprojects/sub1/meson.build rename to test cases/unit/122 persp options/subprojects/sub1/meson.build diff --git a/test cases/unit/123 persp options/subprojects/sub1/meson.options b/test cases/unit/122 persp options/subprojects/sub1/meson.options similarity index 100% rename from test cases/unit/123 persp options/subprojects/sub1/meson.options rename to test cases/unit/122 persp options/subprojects/sub1/meson.options diff --git a/test cases/unit/123 persp options/subprojects/sub1/sub1.c b/test cases/unit/122 persp options/subprojects/sub1/sub1.c similarity index 100% rename from test cases/unit/123 persp options/subprojects/sub1/sub1.c rename to test cases/unit/122 persp options/subprojects/sub1/sub1.c diff --git a/test cases/unit/123 persp options/subprojects/sub2/meson.build b/test cases/unit/122 persp options/subprojects/sub2/meson.build similarity index 100% rename from test cases/unit/123 persp options/subprojects/sub2/meson.build rename to test cases/unit/122 persp options/subprojects/sub2/meson.build diff --git a/test cases/unit/123 persp options/subprojects/sub2/meson.options b/test cases/unit/122 persp options/subprojects/sub2/meson.options similarity index 100% rename from test cases/unit/123 persp options/subprojects/sub2/meson.options rename to test cases/unit/122 persp options/subprojects/sub2/meson.options diff --git a/test cases/unit/123 persp options/subprojects/sub2/sub2.c b/test cases/unit/122 persp options/subprojects/sub2/sub2.c similarity index 100% rename from test cases/unit/123 persp options/subprojects/sub2/sub2.c rename to test cases/unit/122 persp options/subprojects/sub2/sub2.c diff --git a/test cases/unit/123 persp options/toplevel.c b/test cases/unit/122 persp options/toplevel.c similarity index 100% rename from test cases/unit/123 persp options/toplevel.c rename to test cases/unit/122 persp options/toplevel.c diff --git a/test cases/unit/124 reconfigure base options/meson.build b/test cases/unit/123 reconfigure base options/meson.build similarity index 100% rename from test cases/unit/124 reconfigure base options/meson.build rename to test cases/unit/123 reconfigure base options/meson.build diff --git a/test cases/unit/124 reconfigure base options/subprojects/sub/meson.build b/test cases/unit/123 reconfigure base options/subprojects/sub/meson.build similarity index 100% rename from test cases/unit/124 reconfigure base options/subprojects/sub/meson.build rename to test cases/unit/123 reconfigure base options/subprojects/sub/meson.build diff --git a/test cases/unit/125 interactive tap/meson.build b/test cases/unit/124 interactive tap/meson.build similarity index 100% rename from test cases/unit/125 interactive tap/meson.build rename to test cases/unit/124 interactive tap/meson.build diff --git a/test cases/unit/125 interactive tap/script.py b/test cases/unit/124 interactive tap/script.py similarity index 100% rename from test cases/unit/125 interactive tap/script.py rename to test cases/unit/124 interactive tap/script.py diff --git a/test cases/unit/126 declare_dep var/meson.build b/test cases/unit/125 declare_dep var/meson.build similarity index 100% rename from test cases/unit/126 declare_dep var/meson.build rename to test cases/unit/125 declare_dep var/meson.build diff --git a/test cases/unit/126 declare_dep var/meson_options.txt b/test cases/unit/125 declare_dep var/meson_options.txt similarity index 100% rename from test cases/unit/126 declare_dep var/meson_options.txt rename to test cases/unit/125 declare_dep var/meson_options.txt diff --git a/test cases/unit/127 python extension/foo.c b/test cases/unit/126 python extension/foo.c similarity index 100% rename from test cases/unit/127 python extension/foo.c rename to test cases/unit/126 python extension/foo.c diff --git a/test cases/unit/127 python extension/meson.build b/test cases/unit/126 python extension/meson.build similarity index 100% rename from test cases/unit/127 python extension/meson.build rename to test cases/unit/126 python extension/meson.build diff --git a/test cases/unit/128 pkgsubproj/meson.build b/test cases/unit/127 pkgsubproj/meson.build similarity index 100% rename from test cases/unit/128 pkgsubproj/meson.build rename to test cases/unit/127 pkgsubproj/meson.build diff --git a/test cases/unit/128 pkgsubproj/subprojects/sub/meson.build b/test cases/unit/127 pkgsubproj/subprojects/sub/meson.build similarity index 100% rename from test cases/unit/128 pkgsubproj/subprojects/sub/meson.build rename to test cases/unit/127 pkgsubproj/subprojects/sub/meson.build diff --git a/test cases/unit/129 test slice/meson.build b/test cases/unit/128 test slice/meson.build similarity index 100% rename from test cases/unit/129 test slice/meson.build rename to test cases/unit/128 test slice/meson.build diff --git a/test cases/unit/129 test slice/test.py b/test cases/unit/128 test slice/test.py similarity index 100% rename from test cases/unit/129 test slice/test.py rename to test cases/unit/128 test slice/test.py diff --git a/test cases/unit/130 sanitizers/meson.build b/test cases/unit/129 sanitizers/meson.build similarity index 100% rename from test cases/unit/130 sanitizers/meson.build rename to test cases/unit/129 sanitizers/meson.build diff --git a/test cases/unit/131 long opt vs D/meson.build b/test cases/unit/130 long opt vs D/meson.build similarity index 100% rename from test cases/unit/131 long opt vs D/meson.build rename to test cases/unit/130 long opt vs D/meson.build diff --git a/test cases/unit/131 long opt vs D/meson_options.txt b/test cases/unit/130 long opt vs D/meson_options.txt similarity index 100% rename from test cases/unit/131 long opt vs D/meson_options.txt rename to test cases/unit/130 long opt vs D/meson_options.txt diff --git a/test cases/unit/132 vala internal glib/lib.vala b/test cases/unit/131 vala internal glib/lib.vala similarity index 100% rename from test cases/unit/132 vala internal glib/lib.vala rename to test cases/unit/131 vala internal glib/lib.vala diff --git a/test cases/unit/132 vala internal glib/meson.build b/test cases/unit/131 vala internal glib/meson.build similarity index 100% rename from test cases/unit/132 vala internal glib/meson.build rename to test cases/unit/131 vala internal glib/meson.build diff --git a/test cases/unit/132 vala internal glib/meson.options b/test cases/unit/131 vala internal glib/meson.options similarity index 100% rename from test cases/unit/132 vala internal glib/meson.options rename to test cases/unit/131 vala internal glib/meson.options diff --git a/test cases/unit/133 custom target index test/meson.build b/test cases/unit/132 custom target index test/meson.build similarity index 100% rename from test cases/unit/133 custom target index test/meson.build rename to test cases/unit/132 custom target index test/meson.build diff --git a/test cases/unit/134 nasm language only/meson.build b/test cases/unit/133 nasm language only/meson.build similarity index 100% rename from test cases/unit/134 nasm language only/meson.build rename to test cases/unit/133 nasm language only/meson.build diff --git a/test cases/unit/135 promote link_language/consumer.c b/test cases/unit/134 promote link_language/consumer.c similarity index 100% rename from test cases/unit/135 promote link_language/consumer.c rename to test cases/unit/134 promote link_language/consumer.c diff --git a/test cases/unit/135 promote link_language/header.h b/test cases/unit/134 promote link_language/header.h similarity index 100% rename from test cases/unit/135 promote link_language/header.h rename to test cases/unit/134 promote link_language/header.h diff --git a/test cases/unit/135 promote link_language/lib.cpp b/test cases/unit/134 promote link_language/lib.cpp similarity index 100% rename from test cases/unit/135 promote link_language/lib.cpp rename to test cases/unit/134 promote link_language/lib.cpp diff --git a/test cases/unit/135 promote link_language/meson.build b/test cases/unit/134 promote link_language/meson.build similarity index 100% rename from test cases/unit/135 promote link_language/meson.build rename to test cases/unit/134 promote link_language/meson.build diff --git a/test cases/unit/136 minimal bindgen/header.h b/test cases/unit/135 minimal bindgen/header.h similarity index 100% rename from test cases/unit/136 minimal bindgen/header.h rename to test cases/unit/135 minimal bindgen/header.h diff --git a/test cases/unit/136 minimal bindgen/meson.build b/test cases/unit/135 minimal bindgen/meson.build similarity index 100% rename from test cases/unit/136 minimal bindgen/meson.build rename to test cases/unit/135 minimal bindgen/meson.build diff --git a/test cases/unit/136 minimal bindgen/meson.options b/test cases/unit/135 minimal bindgen/meson.options similarity index 100% rename from test cases/unit/136 minimal bindgen/meson.options rename to test cases/unit/135 minimal bindgen/meson.options diff --git a/test cases/unit/137 native subproject introspect/meson.build b/test cases/unit/136 native subproject introspect/meson.build similarity index 100% rename from test cases/unit/137 native subproject introspect/meson.build rename to test cases/unit/136 native subproject introspect/meson.build diff --git a/test cases/unit/137 native subproject introspect/subprojects/recursive-both/lib.c b/test cases/unit/136 native subproject introspect/subprojects/recursive-both/lib.c similarity index 100% rename from test cases/unit/137 native subproject introspect/subprojects/recursive-both/lib.c rename to test cases/unit/136 native subproject introspect/subprojects/recursive-both/lib.c diff --git a/test cases/unit/137 native subproject introspect/subprojects/recursive-both/lib.def b/test cases/unit/136 native subproject introspect/subprojects/recursive-both/lib.def similarity index 100% rename from test cases/unit/137 native subproject introspect/subprojects/recursive-both/lib.def rename to test cases/unit/136 native subproject introspect/subprojects/recursive-both/lib.def diff --git a/test cases/unit/137 native subproject introspect/subprojects/recursive-both/meson.build b/test cases/unit/136 native subproject introspect/subprojects/recursive-both/meson.build similarity index 100% rename from test cases/unit/137 native subproject introspect/subprojects/recursive-both/meson.build rename to test cases/unit/136 native subproject introspect/subprojects/recursive-both/meson.build diff --git a/test cases/unit/137 native subproject introspect/subprojects/recursive-both/recursive-both.h.in b/test cases/unit/136 native subproject introspect/subprojects/recursive-both/recursive-both.h.in similarity index 100% rename from test cases/unit/137 native subproject introspect/subprojects/recursive-both/recursive-both.h.in rename to test cases/unit/136 native subproject introspect/subprojects/recursive-both/recursive-both.h.in diff --git a/test cases/unit/138 external property nonexisting/meson.build b/test cases/unit/137 external property nonexisting/meson.build similarity index 100% rename from test cases/unit/138 external property nonexisting/meson.build rename to test cases/unit/137 external property nonexisting/meson.build diff --git a/test cases/unit/138 external property nonexisting/meson_options.txt b/test cases/unit/137 external property nonexisting/meson_options.txt similarity index 100% rename from test cases/unit/138 external property nonexisting/meson_options.txt rename to test cases/unit/137 external property nonexisting/meson_options.txt diff --git a/test cases/unit/138 external property nonexisting/propfile b/test cases/unit/137 external property nonexisting/propfile similarity index 100% rename from test cases/unit/138 external property nonexisting/propfile rename to test cases/unit/137 external property nonexisting/propfile diff --git a/test cases/unit/92 install skip subprojects/foo.c b/test cases/unit/91 install skip subprojects/foo.c similarity index 100% rename from test cases/unit/92 install skip subprojects/foo.c rename to test cases/unit/91 install skip subprojects/foo.c diff --git a/test cases/unit/92 install skip subprojects/foo.dat b/test cases/unit/91 install skip subprojects/foo.dat similarity index 100% rename from test cases/unit/92 install skip subprojects/foo.dat rename to test cases/unit/91 install skip subprojects/foo.dat diff --git a/test cases/unit/92 install skip subprojects/foo.h b/test cases/unit/91 install skip subprojects/foo.h similarity index 100% rename from test cases/unit/92 install skip subprojects/foo.h rename to test cases/unit/91 install skip subprojects/foo.h diff --git a/test cases/unit/92 install skip subprojects/foo/foofile b/test cases/unit/91 install skip subprojects/foo/foofile similarity index 100% rename from test cases/unit/92 install skip subprojects/foo/foofile rename to test cases/unit/91 install skip subprojects/foo/foofile diff --git a/test cases/unit/92 install skip subprojects/meson.build b/test cases/unit/91 install skip subprojects/meson.build similarity index 100% rename from test cases/unit/92 install skip subprojects/meson.build rename to test cases/unit/91 install skip subprojects/meson.build diff --git a/test cases/unit/92 install skip subprojects/subprojects/bar/bar.c b/test cases/unit/91 install skip subprojects/subprojects/bar/bar.c similarity index 100% rename from test cases/unit/92 install skip subprojects/subprojects/bar/bar.c rename to test cases/unit/91 install skip subprojects/subprojects/bar/bar.c diff --git a/test cases/unit/92 install skip subprojects/subprojects/bar/bar.dat b/test cases/unit/91 install skip subprojects/subprojects/bar/bar.dat similarity index 100% rename from test cases/unit/92 install skip subprojects/subprojects/bar/bar.dat rename to test cases/unit/91 install skip subprojects/subprojects/bar/bar.dat diff --git a/test cases/unit/92 install skip subprojects/subprojects/bar/bar.h b/test cases/unit/91 install skip subprojects/subprojects/bar/bar.h similarity index 100% rename from test cases/unit/92 install skip subprojects/subprojects/bar/bar.h rename to test cases/unit/91 install skip subprojects/subprojects/bar/bar.h diff --git a/test cases/unit/92 install skip subprojects/subprojects/bar/bar/barfile b/test cases/unit/91 install skip subprojects/subprojects/bar/bar/barfile similarity index 100% rename from test cases/unit/92 install skip subprojects/subprojects/bar/bar/barfile rename to test cases/unit/91 install skip subprojects/subprojects/bar/bar/barfile diff --git a/test cases/unit/92 install skip subprojects/subprojects/bar/meson.build b/test cases/unit/91 install skip subprojects/subprojects/bar/meson.build similarity index 100% rename from test cases/unit/92 install skip subprojects/subprojects/bar/meson.build rename to test cases/unit/91 install skip subprojects/subprojects/bar/meson.build diff --git a/test cases/unit/93 new subproject in configured project/meson.build b/test cases/unit/92 new subproject in configured project/meson.build similarity index 100% rename from test cases/unit/93 new subproject in configured project/meson.build rename to test cases/unit/92 new subproject in configured project/meson.build diff --git a/test cases/unit/93 new subproject in configured project/meson_options.txt b/test cases/unit/92 new subproject in configured project/meson_options.txt similarity index 100% rename from test cases/unit/93 new subproject in configured project/meson_options.txt rename to test cases/unit/92 new subproject in configured project/meson_options.txt diff --git a/test cases/unit/93 new subproject in configured project/subprojects/sub/foo.c b/test cases/unit/92 new subproject in configured project/subprojects/sub/foo.c similarity index 100% rename from test cases/unit/93 new subproject in configured project/subprojects/sub/foo.c rename to test cases/unit/92 new subproject in configured project/subprojects/sub/foo.c diff --git a/test cases/unit/93 new subproject in configured project/subprojects/sub/meson.build b/test cases/unit/92 new subproject in configured project/subprojects/sub/meson.build similarity index 100% rename from test cases/unit/93 new subproject in configured project/subprojects/sub/meson.build rename to test cases/unit/92 new subproject in configured project/subprojects/sub/meson.build diff --git a/test cases/unit/94 clangformat/.clang-format b/test cases/unit/93 clangformat/.clang-format similarity index 100% rename from test cases/unit/94 clangformat/.clang-format rename to test cases/unit/93 clangformat/.clang-format diff --git a/test cases/unit/94 clangformat/.clang-format-ignore b/test cases/unit/93 clangformat/.clang-format-ignore similarity index 100% rename from test cases/unit/94 clangformat/.clang-format-ignore rename to test cases/unit/93 clangformat/.clang-format-ignore diff --git a/test cases/unit/94 clangformat/.clang-format-include b/test cases/unit/93 clangformat/.clang-format-include similarity index 100% rename from test cases/unit/94 clangformat/.clang-format-include rename to test cases/unit/93 clangformat/.clang-format-include diff --git a/test cases/unit/94 clangformat/meson.build b/test cases/unit/93 clangformat/meson.build similarity index 100% rename from test cases/unit/94 clangformat/meson.build rename to test cases/unit/93 clangformat/meson.build diff --git a/test cases/unit/94 clangformat/not-included/badformat.cpp b/test cases/unit/93 clangformat/not-included/badformat.cpp similarity index 100% rename from test cases/unit/94 clangformat/not-included/badformat.cpp rename to test cases/unit/93 clangformat/not-included/badformat.cpp diff --git a/test cases/unit/94 clangformat/src/badformat.c b/test cases/unit/93 clangformat/src/badformat.c similarity index 100% rename from test cases/unit/94 clangformat/src/badformat.c rename to test cases/unit/93 clangformat/src/badformat.c diff --git a/test cases/unit/94 clangformat/src/badformat.cpp b/test cases/unit/93 clangformat/src/badformat.cpp similarity index 100% rename from test cases/unit/94 clangformat/src/badformat.cpp rename to test cases/unit/93 clangformat/src/badformat.cpp diff --git a/test cases/unit/95 custominc/easytogrepfor/genh.py b/test cases/unit/94 custominc/easytogrepfor/genh.py similarity index 100% rename from test cases/unit/95 custominc/easytogrepfor/genh.py rename to test cases/unit/94 custominc/easytogrepfor/genh.py diff --git a/test cases/unit/95 custominc/easytogrepfor/meson.build b/test cases/unit/94 custominc/easytogrepfor/meson.build similarity index 100% rename from test cases/unit/95 custominc/easytogrepfor/meson.build rename to test cases/unit/94 custominc/easytogrepfor/meson.build diff --git a/test cases/unit/95 custominc/helper.c b/test cases/unit/94 custominc/helper.c similarity index 100% rename from test cases/unit/95 custominc/helper.c rename to test cases/unit/94 custominc/helper.c diff --git a/test cases/unit/95 custominc/meson.build b/test cases/unit/94 custominc/meson.build similarity index 100% rename from test cases/unit/95 custominc/meson.build rename to test cases/unit/94 custominc/meson.build diff --git a/test cases/unit/95 custominc/prog.c b/test cases/unit/94 custominc/prog.c similarity index 100% rename from test cases/unit/95 custominc/prog.c rename to test cases/unit/94 custominc/prog.c diff --git a/test cases/unit/95 custominc/prog2.c b/test cases/unit/94 custominc/prog2.c similarity index 100% rename from test cases/unit/95 custominc/prog2.c rename to test cases/unit/94 custominc/prog2.c diff --git a/test cases/unit/96 implicit force fallback/meson.build b/test cases/unit/95 implicit force fallback/meson.build similarity index 100% rename from test cases/unit/96 implicit force fallback/meson.build rename to test cases/unit/95 implicit force fallback/meson.build diff --git a/test cases/unit/96 implicit force fallback/subprojects/something/meson.build b/test cases/unit/95 implicit force fallback/subprojects/something/meson.build similarity index 100% rename from test cases/unit/96 implicit force fallback/subprojects/something/meson.build rename to test cases/unit/95 implicit force fallback/subprojects/something/meson.build diff --git a/test cases/unit/97 compiler.links file arg/meson.build b/test cases/unit/96 compiler.links file arg/meson.build similarity index 100% rename from test cases/unit/97 compiler.links file arg/meson.build rename to test cases/unit/96 compiler.links file arg/meson.build diff --git a/test cases/unit/97 compiler.links file arg/test.c b/test cases/unit/96 compiler.links file arg/test.c similarity index 100% rename from test cases/unit/97 compiler.links file arg/test.c rename to test cases/unit/96 compiler.links file arg/test.c diff --git a/test cases/unit/98 link full name/.gitignore b/test cases/unit/97 link full name/.gitignore similarity index 100% rename from test cases/unit/98 link full name/.gitignore rename to test cases/unit/97 link full name/.gitignore diff --git a/test cases/unit/98 link full name/libtestprovider/meson.build b/test cases/unit/97 link full name/libtestprovider/meson.build similarity index 100% rename from test cases/unit/98 link full name/libtestprovider/meson.build rename to test cases/unit/97 link full name/libtestprovider/meson.build diff --git a/test cases/unit/98 link full name/libtestprovider/provider.c b/test cases/unit/97 link full name/libtestprovider/provider.c similarity index 100% rename from test cases/unit/98 link full name/libtestprovider/provider.c rename to test cases/unit/97 link full name/libtestprovider/provider.c diff --git a/test cases/unit/98 link full name/proguser/meson.build b/test cases/unit/97 link full name/proguser/meson.build similarity index 100% rename from test cases/unit/98 link full name/proguser/meson.build rename to test cases/unit/97 link full name/proguser/meson.build diff --git a/test cases/unit/98 link full name/proguser/receiver.c b/test cases/unit/97 link full name/proguser/receiver.c similarity index 100% rename from test cases/unit/98 link full name/proguser/receiver.c rename to test cases/unit/97 link full name/proguser/receiver.c diff --git a/test cases/unit/99 install all targets/bar-custom.txt b/test cases/unit/98 install all targets/bar-custom.txt similarity index 100% rename from test cases/unit/99 install all targets/bar-custom.txt rename to test cases/unit/98 install all targets/bar-custom.txt diff --git a/test cases/unit/99 install all targets/bar-devel.h b/test cases/unit/98 install all targets/bar-devel.h similarity index 100% rename from test cases/unit/99 install all targets/bar-devel.h rename to test cases/unit/98 install all targets/bar-devel.h diff --git a/test cases/unit/99 install all targets/bar-notag.txt b/test cases/unit/98 install all targets/bar-notag.txt similarity index 100% rename from test cases/unit/99 install all targets/bar-notag.txt rename to test cases/unit/98 install all targets/bar-notag.txt diff --git a/test cases/unit/99 install all targets/custom_files/data.txt b/test cases/unit/98 install all targets/custom_files/data.txt similarity index 100% rename from test cases/unit/99 install all targets/custom_files/data.txt rename to test cases/unit/98 install all targets/custom_files/data.txt diff --git a/test cases/unit/99 install all targets/excludes/excluded.txt b/test cases/unit/98 install all targets/excludes/excluded.txt similarity index 100% rename from test cases/unit/99 install all targets/excludes/excluded.txt rename to test cases/unit/98 install all targets/excludes/excluded.txt diff --git a/test cases/unit/99 install all targets/excludes/excluded/placeholder.txt b/test cases/unit/98 install all targets/excludes/excluded/placeholder.txt similarity index 100% rename from test cases/unit/99 install all targets/excludes/excluded/placeholder.txt rename to test cases/unit/98 install all targets/excludes/excluded/placeholder.txt diff --git a/test cases/unit/99 install all targets/excludes/installed.txt b/test cases/unit/98 install all targets/excludes/installed.txt similarity index 100% rename from test cases/unit/99 install all targets/excludes/installed.txt rename to test cases/unit/98 install all targets/excludes/installed.txt diff --git a/test cases/unit/99 install all targets/foo.in b/test cases/unit/98 install all targets/foo.in similarity index 100% rename from test cases/unit/99 install all targets/foo.in rename to test cases/unit/98 install all targets/foo.in diff --git a/test cases/unit/99 install all targets/foo1-devel.h b/test cases/unit/98 install all targets/foo1-devel.h similarity index 100% rename from test cases/unit/99 install all targets/foo1-devel.h rename to test cases/unit/98 install all targets/foo1-devel.h diff --git a/test cases/unit/99 install all targets/lib.c b/test cases/unit/98 install all targets/lib.c similarity index 100% rename from test cases/unit/99 install all targets/lib.c rename to test cases/unit/98 install all targets/lib.c diff --git a/test cases/unit/99 install all targets/main.c b/test cases/unit/98 install all targets/main.c similarity index 100% rename from test cases/unit/99 install all targets/main.c rename to test cases/unit/98 install all targets/main.c diff --git a/test cases/unit/99 install all targets/meson.build b/test cases/unit/98 install all targets/meson.build similarity index 100% rename from test cases/unit/99 install all targets/meson.build rename to test cases/unit/98 install all targets/meson.build diff --git a/test cases/unit/99 install all targets/script.py b/test cases/unit/98 install all targets/script.py similarity index 100% rename from test cases/unit/99 install all targets/script.py rename to test cases/unit/98 install all targets/script.py diff --git a/test cases/unit/99 install all targets/subdir/bar2-devel.h b/test cases/unit/98 install all targets/subdir/bar2-devel.h similarity index 100% rename from test cases/unit/99 install all targets/subdir/bar2-devel.h rename to test cases/unit/98 install all targets/subdir/bar2-devel.h diff --git a/test cases/unit/99 install all targets/subdir/foo2.in b/test cases/unit/98 install all targets/subdir/foo2.in similarity index 100% rename from test cases/unit/99 install all targets/subdir/foo2.in rename to test cases/unit/98 install all targets/subdir/foo2.in diff --git a/test cases/unit/99 install all targets/subdir/foo3-devel.h b/test cases/unit/98 install all targets/subdir/foo3-devel.h similarity index 100% rename from test cases/unit/99 install all targets/subdir/foo3-devel.h rename to test cases/unit/98 install all targets/subdir/foo3-devel.h diff --git a/test cases/unit/99 install all targets/subdir/lib.c b/test cases/unit/98 install all targets/subdir/lib.c similarity index 100% rename from test cases/unit/99 install all targets/subdir/lib.c rename to test cases/unit/98 install all targets/subdir/lib.c diff --git a/test cases/unit/99 install all targets/subdir/main.c b/test cases/unit/98 install all targets/subdir/main.c similarity index 100% rename from test cases/unit/99 install all targets/subdir/main.c rename to test cases/unit/98 install all targets/subdir/main.c diff --git a/test cases/unit/99 install all targets/subdir/meson.build b/test cases/unit/98 install all targets/subdir/meson.build similarity index 100% rename from test cases/unit/99 install all targets/subdir/meson.build rename to test cases/unit/98 install all targets/subdir/meson.build diff --git a/test cases/unit/99 install all targets/subdir/script.py b/test cases/unit/98 install all targets/subdir/script.py similarity index 100% rename from test cases/unit/99 install all targets/subdir/script.py rename to test cases/unit/98 install all targets/subdir/script.py diff --git a/test cases/unit/99 install all targets/subprojects/subproject/aaa.txt b/test cases/unit/98 install all targets/subprojects/subproject/aaa.txt similarity index 100% rename from test cases/unit/99 install all targets/subprojects/subproject/aaa.txt rename to test cases/unit/98 install all targets/subprojects/subproject/aaa.txt diff --git a/test cases/unit/99 install all targets/subprojects/subproject/bbb.txt b/test cases/unit/98 install all targets/subprojects/subproject/bbb.txt similarity index 100% rename from test cases/unit/99 install all targets/subprojects/subproject/bbb.txt rename to test cases/unit/98 install all targets/subprojects/subproject/bbb.txt diff --git a/test cases/unit/99 install all targets/subprojects/subproject/meson.build b/test cases/unit/98 install all targets/subprojects/subproject/meson.build similarity index 100% rename from test cases/unit/99 install all targets/subprojects/subproject/meson.build rename to test cases/unit/98 install all targets/subprojects/subproject/meson.build diff --git a/test cases/unit/100 custom target name/file.txt.in b/test cases/unit/99 custom target name/file.txt.in similarity index 100% rename from test cases/unit/100 custom target name/file.txt.in rename to test cases/unit/99 custom target name/file.txt.in diff --git a/test cases/unit/100 custom target name/meson.build b/test cases/unit/99 custom target name/meson.build similarity index 100% rename from test cases/unit/100 custom target name/meson.build rename to test cases/unit/99 custom target name/meson.build diff --git a/test cases/unit/100 custom target name/subdir/meson.build b/test cases/unit/99 custom target name/subdir/meson.build similarity index 100% rename from test cases/unit/100 custom target name/subdir/meson.build rename to test cases/unit/99 custom target name/subdir/meson.build diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 431de8e8fbec..7af2f0ffed64 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -538,7 +538,7 @@ def test_replace_unencodable_xml_chars_unit(self): printable representation https://github.com/mesonbuild/meson/issues/9894 ''' - testdir = os.path.join(self.unit_test_dir, '112 replace unencodable xml chars') + testdir = os.path.join(self.unit_test_dir, '111 replace unencodable xml chars') self.init(testdir) tests_command_output = self.run_tests() junit_xml_logs = Path(self.logdir, 'testlog.junit.xml') @@ -699,7 +699,7 @@ def test_forcefallback(self): self.run_tests() def test_implicit_forcefallback(self): - testdir = os.path.join(self.unit_test_dir, '96 implicit force fallback') + testdir = os.path.join(self.unit_test_dir, '95 implicit force fallback') with self.assertRaises(subprocess.CalledProcessError): self.init(testdir) self.init(testdir, extra_args=['--wrap-mode=forcefallback']) @@ -1491,7 +1491,7 @@ def test_long_opt_vs_D(self): Test that conflicts between -D for builtin options and the corresponding long option are detected without false positives or negatives. ''' - testdir = os.path.join(self.unit_test_dir, '131 long opt vs D') + testdir = os.path.join(self.unit_test_dir, '130 long opt vs D') for opt in ['-Dsysconfdir=/etc', '-Dsysconfdir2=/etc']: exception_raised = False @@ -2235,7 +2235,7 @@ def check_pcfile(name, *, relocatable, levels=2): check_pcfile('libvartest2.pc', relocatable=False) self.wipe() - testdir_abs = os.path.join(self.unit_test_dir, '106 pkgconfig relocatable with absolute path') + testdir_abs = os.path.join(self.unit_test_dir, '105 pkgconfig relocatable with absolute path') self.init(testdir_abs) check_pcfile('libsimple.pc', relocatable=True, levels=3) @@ -2317,7 +2317,7 @@ def get_opt(): self.assertDictEqual(original, expected) def test_executable_names(self): - testdir = os.path.join(self.unit_test_dir, '122 executable suffix') + testdir = os.path.join(self.unit_test_dir, '121 executable suffix') self.init(testdir) self.build() exe1 = os.path.join(self.builddir, 'foo' + exe_suffix) @@ -2408,7 +2408,7 @@ def test_options_with_choices_changing(self) -> None: def test_options_listed_in_build_options(self) -> None: """Detect when changed options become listed in build options.""" - testdir = os.path.join(self.unit_test_dir, '114 list build options') + testdir = os.path.join(self.unit_test_dir, '113 list build options') out = self.init(testdir) for line in out.splitlines(): @@ -2727,7 +2727,7 @@ def test_check_module_linking(self): self.assertIn(msg, out) def test_mixed_language_linker_check(self): - testdir = os.path.join(self.unit_test_dir, '97 compiler.links file arg') + testdir = os.path.join(self.unit_test_dir, '96 compiler.links file arg') self.init(testdir) cmds = self.get_meson_log_compiler_checks() self.assertEqual(len(cmds), 5) @@ -3188,7 +3188,7 @@ def test_pkg_config_libdir(self): @skipIf(is_windows(), 'POSIX only') def test_python_build_config_extensions(self): testdir = os.path.join(self.unit_test_dir, - '127 python extension') + '126 python extension') VERSION_INFO_KEYS = ('major', 'minor', 'micro', 'releaselevel', 'serial') EXTENSION_SUFFIX = '.extension-suffix.so' @@ -3479,7 +3479,7 @@ def test_introspection_target_native_subproject(self): self.meson_cross_files = [os.path.join(self.builddir, "crossfile")] self.new_builddir() - testdir = os.path.join(self.unit_test_dir, '137 native subproject introspect') + testdir = os.path.join(self.unit_test_dir, '136 native subproject introspect') self.init(testdir) res = self.introspect('--targets') @@ -4671,7 +4671,7 @@ def test_custom_target_index_as_test_prereq(self): if self.backend is not Backend.ninja: raise SkipTest('ninja backend needed for "meson test" to build test dependencies') - testdir = os.path.join(self.unit_test_dir, '133 custom target index test') + testdir = os.path.join(self.unit_test_dir, '132 custom target index test') self.init(testdir) self.run_tests() @@ -4782,7 +4782,7 @@ def test_build_b_options(self) -> None: self.init(srcdir, extra_args=['-Dbuild.b_lto=true']) def test_install_skip_subprojects(self): - testdir = os.path.join(self.unit_test_dir, '92 install skip subprojects') + testdir = os.path.join(self.unit_test_dir, '91 install skip subprojects') self.init(testdir) self.build() @@ -4829,7 +4829,7 @@ def check_installed_files(extra_args, expected): check_installed_files(['--skip-subprojects', 'another'], all_expected) def test_adding_subproject_to_configure_project(self) -> None: - srcdir = os.path.join(self.unit_test_dir, '93 new subproject in configured project') + srcdir = os.path.join(self.unit_test_dir, '92 new subproject in configured project') self.init(srcdir) self.build() self.setconf('-Duse-sub=true') @@ -4882,7 +4882,7 @@ def test_clang_format_check(self): if self.backend is not Backend.ninja: raise SkipTest(f'Skipping clang-format tests with {self.backend.name} backend') - testdir = os.path.join(self.unit_test_dir, '94 clangformat') + testdir = os.path.join(self.unit_test_dir, '93 clangformat') newdir = os.path.join(self.builddir, 'testdir') shutil.copytree(testdir, newdir) self.new_builddir() @@ -4907,7 +4907,7 @@ def test_clang_format_check(self): self.build('clang-format-check') def test_custom_target_implicit_include(self): - testdir = os.path.join(self.unit_test_dir, '95 custominc') + testdir = os.path.join(self.unit_test_dir, '94 custominc') self.init(testdir) self.build() compdb = self.get_compdb() @@ -4943,7 +4943,7 @@ def test_env_flags_to_linker(self) -> None: self.assertEqual(sorted(link_args), sorted(['-flto'])) def test_install_tag(self) -> None: - testdir = os.path.join(self.unit_test_dir, '99 install all targets') + testdir = os.path.join(self.unit_test_dir, '98 install all targets') self.init(testdir) self.build() @@ -5114,7 +5114,7 @@ def test_install_script_dry_run(self): def test_introspect_install_plan(self): - testdir = os.path.join(self.unit_test_dir, '99 install all targets') + testdir = os.path.join(self.unit_test_dir, '98 install all targets') introfile = os.path.join(self.builddir, 'meson-info', 'intro-install_plan.json') self.init(testdir) self.assertPathExists(introfile) @@ -5450,7 +5450,7 @@ def test_rust_rlib_linkage(self) -> None: }} ''') - testdir = os.path.join(self.unit_test_dir, '102 rlib linkage') + testdir = os.path.join(self.unit_test_dir, '101 rlib linkage') gen_file = os.path.join(testdir, 'lib.rs') with open(gen_file, 'w', encoding='utf-8') as f: f.write(template.format(0)) @@ -5498,7 +5498,7 @@ def test_bindgen_drops_invalid(self) -> None: return def test_custom_target_name(self): - testdir = os.path.join(self.unit_test_dir, '100 custom target name') + testdir = os.path.join(self.unit_test_dir, '99 custom target name') self.init(testdir) out = self.build() if self.backend is Backend.ninja: @@ -5506,7 +5506,7 @@ def test_custom_target_name(self): self.assertIn('Generating subdir/file.txt with a custom command', out) def test_symlinked_subproject(self): - testdir = os.path.join(self.unit_test_dir, '108 subproject symlink') + testdir = os.path.join(self.unit_test_dir, '107 subproject symlink') subproject_dir = os.path.join(testdir, 'subprojects') subproject = os.path.join(testdir, 'symlinked_subproject') symlinked_subproject = os.path.join(testdir, 'subprojects', 'symlinked_subproject') @@ -5522,7 +5522,7 @@ def test_symlinked_subproject(self): self.build() def test_configure_same_noop(self): - testdir = os.path.join(self.unit_test_dir, '110 configure same noop') + testdir = os.path.join(self.unit_test_dir, '109 configure same noop') args = [ '-Dstring=val', '-Dboolean=true', @@ -5561,7 +5561,7 @@ def test_configure_same_noop(self): def test_link_language_promotion(self): if self.backend is Backend.vs: raise SkipTest('target introspection is lacking linker details') - testdir = os.path.join(self.unit_test_dir, '135 promote link_language') + testdir = os.path.join(self.unit_test_dir, '134 promote link_language') self.init(testdir) cintrospection = self.introspect('--compilers') clinker = cintrospection['host']['c']['linker_exelist'] @@ -5574,7 +5574,7 @@ def test_link_language_promotion(self): def __test_multi_stds(self, test_c: bool = True, test_objc: bool = False) -> None: assert test_c or test_objc, 'must test something' - testdir = os.path.join(self.unit_test_dir, '116 c cpp stds') + testdir = os.path.join(self.unit_test_dir, '115 c cpp stds') self.init(testdir, extra_args=[f'-Dwith-c={str(test_c).lower()}', f'-Dwith-objc={str(test_objc).lower()}']) # Invalid values should fail whatever compiler we have @@ -5633,7 +5633,7 @@ def test_c_cpp_objc_objcpp_stds(self) -> None: self.__test_multi_stds(test_objc=True) def test_slice(self): - testdir = os.path.join(self.unit_test_dir, '129 test slice') + testdir = os.path.join(self.unit_test_dir, '128 test slice') self.init(testdir) self.build() @@ -5673,7 +5673,7 @@ def test_rsp_support(self): self.assertEqual(cc.linker.get_accepts_rsp(), has_rsp) def test_nonexisting_bargs(self): - testdir = os.path.join(self.unit_test_dir, '117 empty project') + testdir = os.path.join(self.unit_test_dir, '116 empty project') args = ['-Db_ndebug=if_release'] self.init(testdir, extra_args=args) @@ -5683,7 +5683,7 @@ def test_wipe_with_args(self): self.init(testdir, extra_args=['--wipe']) def test_interactive_tap(self): - testdir = os.path.join(self.unit_test_dir, '125 interactive tap') + testdir = os.path.join(self.unit_test_dir, '124 interactive tap') self.init(testdir, extra_args=['--wrap-mode=forcefallback']) output = self._run(self.mtest_command + ['--interactive']) self.assertRegex(output, r'Ok:\s*0') diff --git a/unittests/linuxliketests.py b/unittests/linuxliketests.py index 0863a041ded9..2288e0d693aa 100644 --- a/unittests/linuxliketests.py +++ b/unittests/linuxliketests.py @@ -1414,7 +1414,7 @@ def test_link_arg_fullname(self): see: https://github.com/mesonbuild/meson/issues/9000 https://stackoverflow.com/questions/48532868/gcc-library-option-with-a-colon-llibevent-a ''' - testdir = os.path.join(self.unit_test_dir, '98 link full name','libtestprovider') + testdir = os.path.join(self.unit_test_dir, '97 link full name','libtestprovider') oldprefix = self.prefix # install into installdir without using DESTDIR installdir = self.installdir @@ -1427,7 +1427,7 @@ def test_link_arg_fullname(self): self.new_builddir() env = {'LIBRARY_PATH': os.path.join(installdir, self.libdir), 'PKG_CONFIG_PATH': _prepend_pkg_config_path(os.path.join(installdir, self.libdir, 'pkgconfig'))} - testdir = os.path.join(self.unit_test_dir, '98 link full name','proguser') + testdir = os.path.join(self.unit_test_dir, '97 link full name','proguser') self.init(testdir,override_envvars=env) # test for link with full path @@ -1832,7 +1832,7 @@ def test_cmake_multilib(self): @skipUnless(is_linux() or is_osx(), 'Test only applicable to Linux and macOS') def test_install_strip(self): - testdir = os.path.join(self.unit_test_dir, '104 strip') + testdir = os.path.join(self.unit_test_dir, '103 strip') self.init(testdir) self.build() @@ -1879,7 +1879,7 @@ def test_isystem_default_removal_with_symlink(self): self.assertFalse(cpp.compiler_args([f'-isystem{symlink}' for symlink in default_symlinks]).to_native()) def test_freezing(self): - testdir = os.path.join(self.unit_test_dir, '111 freeze') + testdir = os.path.join(self.unit_test_dir, '110 freeze') self.init(testdir) self.build() with self.assertRaises(subprocess.CalledProcessError) as e: @@ -1888,7 +1888,7 @@ def test_freezing(self): @skipUnless(is_linux(), "Ninja file differs on different platforms") def test_complex_link_cases(self): - testdir = os.path.join(self.unit_test_dir, '115 complex link cases') + testdir = os.path.join(self.unit_test_dir, '114 complex link cases') self.init(testdir) self.build() with open(os.path.join(self.builddir, 'build.ninja'), encoding='utf-8') as f: @@ -1909,11 +1909,11 @@ def test_complex_link_cases(self): self.assertIn('build t13-e1: c_LINKER t13-e1.p/main.c.o | libt12-s1.a libt13-s3.a\n', content) def test_top_options_in_sp(self): - testdir = os.path.join(self.unit_test_dir, '128 pkgsubproj') + testdir = os.path.join(self.unit_test_dir, '127 pkgsubproj') self.init(testdir) def test_unreadable_dir_in_declare_dep(self): - testdir = os.path.join(self.unit_test_dir, '126 declare_dep var') + testdir = os.path.join(self.unit_test_dir, '125 declare_dep var') tmpdir = Path(tempfile.mkdtemp()) self.addCleanup(windows_proof_rmtree, tmpdir) declaredepdir = tmpdir / 'test' @@ -1935,7 +1935,7 @@ def test_persp_options(self): if self.backend is not Backend.ninja: raise SkipTest(f'{self.backend.name!r} backend can\'t install files') - testdir = os.path.join(self.unit_test_dir, '123 persp options') + testdir = os.path.join(self.unit_test_dir, '122 persp options') with self.subTest('init'): self.init(testdir, extra_args='-Doptimization=1') @@ -2020,7 +2020,7 @@ def test_rust_staticlib_rlib_deps(self): self.assertNotIn('liblib.rlib', param) def test_sanitizers(self): - testdir = os.path.join(self.unit_test_dir, '130 sanitizers') + testdir = os.path.join(self.unit_test_dir, '129 sanitizers') with self.subTest('no b_sanitize value'): try: diff --git a/unittests/machinefiletests.py b/unittests/machinefiletests.py index f8d1c7414184..b20b8172f9e4 100644 --- a/unittests/machinefiletests.py +++ b/unittests/machinefiletests.py @@ -423,7 +423,7 @@ def test_java_compiler(self): def test_java_classpath(self): if self.backend is not Backend.ninja: raise SkipTest('Jar is only supported with Ninja') - testdir = os.path.join(self.unit_test_dir, '113 classpath') + testdir = os.path.join(self.unit_test_dir, '112 classpath') self.init(testdir) self.build() one_build_path = get_classpath(os.path.join(self.builddir, 'one.jar')) @@ -1015,13 +1015,13 @@ def test_for_build_env_vars(self) -> None: self.assertEqual(found, 2, 'Did not find all sections.') def test_external_property_build_machine_native(self): - testdir = os.path.join(self.unit_test_dir, '138 external property nonexisting') + testdir = os.path.join(self.unit_test_dir, '137 external property nonexisting') self.meson_native_files = [os.path.join(testdir, "propfile")] out = self.init(testdir, allow_fail=True, extra_args=['-Dnative=true']) self.assertIn('Unknown property for host machine: nonexisting', out) def test_external_property_host_machine_native(self): - testdir = os.path.join(self.unit_test_dir, '138 external property nonexisting') + testdir = os.path.join(self.unit_test_dir, '137 external property nonexisting') self.meson_native_files = [os.path.join(testdir, "propfile")] out = self.init(testdir, allow_fail=True, extra_args=['-Dnative=false']) self.assertIn('Unknown property for host machine: nonexisting', out) @@ -1040,14 +1040,14 @@ def test_external_property_build_machine_cross(self): crossfile = os.path.join(self.builddir, "crossfile") self.new_builddir() - testdir = os.path.join(self.unit_test_dir, '138 external property nonexisting') + testdir = os.path.join(self.unit_test_dir, '137 external property nonexisting') self.meson_native_files = [nativefile, os.path.join(testdir, "propfile")] self.meson_cross_files = [crossfile] out = self.init(testdir, allow_fail=True, extra_args=['-Dnative=true']) self.assertIn('Unknown property for build machine: nonexisting', out) self.new_builddir() - testdir = os.path.join(self.unit_test_dir, '138 external property nonexisting') + testdir = os.path.join(self.unit_test_dir, '137 external property nonexisting') self.meson_native_files = [nativefile] self.meson_cross_files = [crossfile, os.path.join(testdir, "propfile")] out = self.init(testdir, allow_fail=True, extra_args=['-Dnative=false']) @@ -1072,7 +1072,7 @@ def test_project_options_native_only(self) -> None: @skip_if_not_language('rust') @skipIfNoExecutable('bindgen') def test_bindgen_finds_target_in_clang_options(self) -> None: - testcase = os.path.join(self.unit_test_dir, '136 minimal bindgen') + testcase = os.path.join(self.unit_test_dir, '135 minimal bindgen') def check_target(include: T.Optional[str], exclude: T.Optional[str] = None) -> None: configuration = self.introspect('--targets') diff --git a/unittests/platformagnostictests.py b/unittests/platformagnostictests.py index 6e27310af4af..fe565cffb851 100644 --- a/unittests/platformagnostictests.py +++ b/unittests/platformagnostictests.py @@ -33,7 +33,7 @@ def test_relative_find_program(self): Tests that find_program() with a relative path does not find the program in current workdir. ''' - testdir = os.path.join(self.unit_test_dir, '101 relative find program') + testdir = os.path.join(self.unit_test_dir, '100 relative find program') self.init(testdir, workdir=testdir) def test_invalid_option_names(self): @@ -92,11 +92,11 @@ def write_file(code: str): interp.process, fname) def test_python_dependency_without_pkgconfig(self): - testdir = os.path.join(self.unit_test_dir, '103 python without pkgconfig') + testdir = os.path.join(self.unit_test_dir, '102 python without pkgconfig') self.init(testdir, override_envvars={'PKG_CONFIG': 'notfound'}) def test_vala_target_with_internal_glib(self): - testdir = os.path.join(self.unit_test_dir, '132 vala internal glib') + testdir = os.path.join(self.unit_test_dir, '131 vala internal glib') for run in [{ 'version': '2.84.4', 'expected': '2.84'}, { 'version': '2.85.2', 'expected': '2.84' }]: self.new_builddir() self.init(testdir, extra_args=[f'-Dglib-version={run["version"]}']) @@ -113,7 +113,7 @@ def test_vala_target_with_internal_glib(self): self.skipTest('Current backend does not produce introspection data') def test_debug_function_outputs_to_meson_log(self): - testdir = os.path.join(self.unit_test_dir, '105 debug function') + testdir = os.path.join(self.unit_test_dir, '104 debug function') log_msg = 'This is an example debug output, should only end up in debug log' output = self.init(testdir) @@ -125,7 +125,7 @@ def test_debug_function_outputs_to_meson_log(self): self.assertIn(log_msg, mesonlog) def test_new_subproject_reconfigure(self): - testdir = os.path.join(self.unit_test_dir, '109 new subproject on reconfigure') + testdir = os.path.join(self.unit_test_dir, '108 new subproject on reconfigure') self.init(testdir) self.build() @@ -288,7 +288,7 @@ def test_setup_loaded_modules(self): thing to do as new features are added, but keeping track of them is good. ''' - testdir = os.path.join(self.unit_test_dir, '117 empty project') + testdir = os.path.join(self.unit_test_dir, '116 empty project') self.init(testdir) self._run(self.meson_command + ['--internal', 'regenerate', '--profile-self', testdir, self.builddir]) @@ -303,7 +303,7 @@ def test_setup_loaded_modules(self): def test_meson_package_cache_dir(self): # Copy testdir into temporary directory to not pollute meson source tree. - testdir = os.path.join(self.unit_test_dir, '119 meson package cache dir') + testdir = os.path.join(self.unit_test_dir, '118 meson package cache dir') srcdir = os.path.join(self.builddir, 'srctree') shutil.copytree(testdir, srcdir) builddir = os.path.join(srcdir, '_build') @@ -312,7 +312,7 @@ def test_meson_package_cache_dir(self): def test_cmake_openssl_not_found_bug(self): """Issue #12098""" - testdir = os.path.join(self.unit_test_dir, '120 openssl cmake bug') + testdir = os.path.join(self.unit_test_dir, '119 openssl cmake bug') self.meson_native_files.append(os.path.join(testdir, 'nativefile.ini')) out = self.init(testdir, allow_fail=True) self.assertNotIn('Unhandled python exception', out) @@ -422,7 +422,7 @@ def test_error_configuring_subdir(self): self.assertIn(f'Did you mean to run meson from the directory: "{testdir}"?', out) def test_reconfigure_base_options(self): - testdir = os.path.join(self.unit_test_dir, '124 reconfigure base options') + testdir = os.path.join(self.unit_test_dir, '123 reconfigure base options') out = self.init(testdir, extra_args=['-Db_ndebug=true']) self.assertIn('\nMessage: b_ndebug: true\n', out) self.assertIn('\nMessage: c_std: c89\n', out) @@ -548,7 +548,7 @@ def test_configure_new_option_subproject(self) -> None: self.assertEqual(self.getconf('subproject:new_option'), True) def test_mtest_rebuild_deps(self): - testdir = os.path.join(self.unit_test_dir, '107 underspecified mtest') + testdir = os.path.join(self.unit_test_dir, '106 underspecified mtest') self.init(testdir) with self.assertRaises(subprocess.CalledProcessError): diff --git a/unittests/rewritetests.py b/unittests/rewritetests.py index 1d90847842f6..4bf8283ec842 100644 --- a/unittests/rewritetests.py +++ b/unittests/rewritetests.py @@ -479,7 +479,7 @@ def test_tricky_dataflow(self): self.assertEqualIgnoreOrder(out, expected) def test_raw_printer_is_idempotent(self): - test_path = Path(self.unit_test_dir, '121 rewrite') + test_path = Path(self.unit_test_dir, '120 rewrite') meson_build_file = test_path / 'meson.build' # original_contents = meson_build_file.read_bytes() original_contents = meson_build_file.read_text(encoding='utf-8') @@ -533,6 +533,6 @@ def node_to_str(node): @skip_if_not_language('nasm') def test_nasm(self) -> None: - srcdir = os.path.join(self.unit_test_dir, '134 nasm language only') + srcdir = os.path.join(self.unit_test_dir, '133 nasm language only') self.rewrite_raw(srcdir, ['kwargs', 'info', 'project', '/']) diff --git a/unittests/windowstests.py b/unittests/windowstests.py index a6f9e264ffbb..18ea7d38ac6b 100644 --- a/unittests/windowstests.py +++ b/unittests/windowstests.py @@ -185,7 +185,7 @@ def test_genvslite(self): if self.backend is not Backend.ninja: raise SkipTest('Test only applies when using the Ninja backend') - testdir = os.path.join(self.unit_test_dir, '118 genvslite') + testdir = os.path.join(self.unit_test_dir, '117 genvslite') env = get_fake_env(testdir, self.builddir, self.prefix) cc = detect_c_compiler(env, MachineChoice.HOST) From 1ad63886b559e397925c50d111a2516ea5b3c8b2 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Tue, 21 Jul 2026 00:25:04 +0300 Subject: [PATCH 618/681] Update version number for rc2. --- mesonbuild/coredata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index ce3e4d7a3527..5ae0c76af064 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -43,7 +43,7 @@ # # Pip requires that RCs are named like this: '0.1.0.rc1' # But the corresponding Git tag needs to be '0.1.0rc1' -version = '1.12.0.rc1' +version = '1.12.0.rc2' # The next stable version when we are in dev. This is used to allow projects to # require meson version >=1.2.0 when using 1.1.99. FeatureNew won't warn when From b5305c74b3ecccbc614d8f3dea1c47beda44ff0a Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Mon, 20 Jul 2026 10:17:51 -0600 Subject: [PATCH 619/681] cmake: omit CMAKE__COMPILER_LOADED from generated toolchain files When probing the compiler state, Meson captures the variables CMake sets in its CMakeCompiler.cmake files and re-emits them into the toolchain files it generates. Including CMAKE__COMPILER_LOADED there triggers a regression in CMake 4.4.0, so drop it before writing the variables out. See https://gitlab.kitware.com/cmake/cmake/-/work_items/27971 Signed-off-by: Richard Berger --- mesonbuild/cmake/toolchain.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mesonbuild/cmake/toolchain.py b/mesonbuild/cmake/toolchain.py index 60b061557533..eb3db359981d 100644 --- a/mesonbuild/cmake/toolchain.py +++ b/mesonbuild/cmake/toolchain.py @@ -275,5 +275,6 @@ def update_cmake_compiler_state(self) -> None: lang_cmake = language_map.get(lang, lang.upper()) file_name = f'CMake{lang_cmake}Compiler.cmake' vars = vars_by_file.setdefault(file_name, {}) + vars.pop(f'CMAKE_{lang_cmake}_COMPILER_LOADED', None) vars[f'CMAKE_{lang_cmake}_COMPILER_FORCED'] = ['1'] self.cmakestate.update(lang, vars) From d60a50934e881cfd5463c06626b92f8f0b37bfdb Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 22 Jul 2026 15:50:33 +0200 Subject: [PATCH 620/681] test cases: disable werror=true for python/12 rust extmodule This fails on macOS due to Rust adding -mmacosx-version-min=... automatically, and ld reporting a mismatch between the .c and .rs objects. Signed-off-by: Paolo Bonzini --- test cases/python/12 rust extmodule/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test cases/python/12 rust extmodule/meson.build b/test cases/python/12 rust extmodule/meson.build index 6c4cec8a0707..03ba48715afd 100644 --- a/test cases/python/12 rust extmodule/meson.build +++ b/test cases/python/12 rust extmodule/meson.build @@ -1,5 +1,5 @@ project('Python extension module', 'c', - default_options : ['buildtype=release', 'werror=true', 'python.bytecompile=-1']) + default_options : ['buildtype=release', 'python.bytecompile=-1']) # Because Windows Python ships only with optimized libs, # we must build this project the same way. From bb531f03cd7be40558cf26bdc8f2d311343a1b53 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 22 Jul 2026 15:02:45 +0200 Subject: [PATCH 621/681] type_checking: mark first available version for vs_module_defs in build_target() Issue #16031 was fixed in commit c9f5036d48aec3f6b5e7592e60c1d06437dd5c8e, but the descriptor was not properly marked with since='1.12.0'. Signed-off-by: Paolo Bonzini --- mesonbuild/interpreter/type_checking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py index 9f2f9194155c..7c42c2f86e5e 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -1039,7 +1039,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, + _VS_MODULE_DEFS_KW.evolve(since='1.12.0'), _WIN_SUBSYSTEM_KW.evolve(since='1.12.0'), 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()') From 9c868d3b6f712d9192d50d79d6105d5d7ed9704a Mon Sep 17 00:00:00 2001 From: Tristan Partin Date: Mon, 27 Jul 2026 19:44:57 +0000 Subject: [PATCH 622/681] Unset IFS in external project test We set IFS to do some parsing, but never unset it. This is a problem in our Windows setups because paths have a colon, which separates the drive letter from the rest of the path. /d/a/meson/meson/test cases/common/230 external project/libfoo/configure: line 47: D: command not found Signed-off-by: Tristan Partin --- test cases/common/230 external project/libfoo/configure | 1 + 1 file changed, 1 insertion(+) diff --git a/test cases/common/230 external project/libfoo/configure b/test cases/common/230 external project/libfoo/configure index c4e9d0aebd84..0ca647009a2e 100755 --- a/test cases/common/230 external project/libfoo/configure +++ b/test cases/common/230 external project/libfoo/configure @@ -18,6 +18,7 @@ then exit 1 fi done +unset IFS for i in "$@" do From 0b5b32e284709eb5b23ed30207fe978362d30a3d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 29 Jul 2026 10:30:25 +0200 Subject: [PATCH 623/681] ast/printer: fix list of nonassociative operators The nonassociative operators were still listed as their operation name rather than the symbol, which is used since 1.11.0. As a result, the AstPrinter would incorrectly leave out some parentheses. Note however that this is very unlikely to happen in practice. There are no existing testcases for AstPrinter. Signed-off-by: Paolo Bonzini --- mesonbuild/ast/printer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/ast/printer.py b/mesonbuild/ast/printer.py index 60aeef1008f1..689751a77963 100644 --- a/mesonbuild/ast/printer.py +++ b/mesonbuild/ast/printer.py @@ -156,7 +156,7 @@ def visit_ArithmeticNode(self, node: mparser.ArithmeticNode) -> None: self.maybe_parentheses(node, node.left, prec > prec_left) self.append_padded(node.operator.value, node) node.lineno = self.curr_line or node.lineno - self.maybe_parentheses(node, node.right, prec > prec_right or (prec == prec_right and node.operation in {'sub', 'div', 'mod'})) + self.maybe_parentheses(node, node.right, prec > prec_right or (prec == prec_right and node.operation in {'-', '/', '%'})) def visit_NotNode(self, node: mparser.NotNode) -> None: node.lineno = self.curr_line or node.lineno From cd852f14eca0a1bfdbcb402d074fece591ea3eed Mon Sep 17 00:00:00 2001 From: Sam James Date: Mon, 3 Aug 2026 20:48:24 +0100 Subject: [PATCH 624/681] cargo: use new endpoint for downloads Use the CDN rather than the API endpoint: https://blog.rust-lang.org/2024/03/11/crates-io-download-changes/ We already set a User-Agent fortunately. Signed-off-by: Sam James --- mesonbuild/cargo/interpreter.py | 2 +- unittests/cargotests.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index 62bc71e4ce0b..ddfc664074fd 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -872,7 +872,7 @@ def load_cargo_lock(filename: str, subproject_dir: str) -> T.Optional[CargoLock] checksum = package.checksum if checksum is None: checksum = cargolock.metadata[f'checksum {package.name} {package.version} ({package.source})'] - url = f'https://crates.io/api/v1/crates/{package.name}/{package.version}/download' + url = f'https://static.crates.io/crates/{package.name}/{package.version}/download' directory = f'{package.name}-{package.version}' name = SubProject(meson_depname) wrap_type = 'file' diff --git a/unittests/cargotests.py b/unittests/cargotests.py index e34615e93c04..af511a1cb131 100644 --- a/unittests/cargotests.py +++ b/unittests/cargotests.py @@ -283,7 +283,7 @@ def test_wraps(self) -> None: self.assertEqual(wraps['foo-0.1-rs'].directory, 'foo-0.1') self.assertEqual(wraps['foo-0.1-rs'].type, 'file') self.assertEqual(wraps['foo-0.1-rs'].get('method'), 'cargo') - self.assertEqual(wraps['foo-0.1-rs'].get('source_url'), 'https://crates.io/api/v1/crates/foo/0.1/download') + self.assertEqual(wraps['foo-0.1-rs'].get('source_url'), 'https://static.crates.io/crates/foo/0.1/download') self.assertEqual(wraps['foo-0.1-rs'].get('source_hash'), '8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb') self.assertEqual(wraps['gtk-rs-core-0.19'].name, 'gtk-rs-core-0.19') self.assertEqual(wraps['gtk-rs-core-0.19'].directory, 'gtk-rs-core-0.19') From 3bf88eb945b95928fc953dff507dc2c5a4e12329 Mon Sep 17 00:00:00 2001 From: Sam Clegg Date: Sun, 2 Aug 2026 16:16:03 -0700 Subject: [PATCH 625/681] Update example cross files for emscripten/wasm - Use more specific name (since not all wasm is emscripten-built) - Add a 64-bit version - Remove -sEXPORT_ALL (this is not flag that most user should need/want) See https://github.com/emscripten-core/emscripten/pull/27478 --- cross/{wasm.txt => wasm32-emscripten.txt} | 8 +------- cross/wasm64-emscripten.txt | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 7 deletions(-) rename cross/{wasm.txt => wasm32-emscripten.txt} (58%) create mode 100644 cross/wasm64-emscripten.txt diff --git a/cross/wasm.txt b/cross/wasm32-emscripten.txt similarity index 58% rename from cross/wasm.txt rename to cross/wasm32-emscripten.txt index 1b589d61ba33..f57ddba84188 100644 --- a/cross/wasm.txt +++ b/cross/wasm32-emscripten.txt @@ -2,16 +2,10 @@ c = 'emcc' cpp = 'em++' ar = 'emar' +ranlib = 'emranlib' exe_wrapper = 'node' -[built-in options] -c_args = [] -c_link_args = ['-sEXPORT_ALL=1'] -cpp_args = [] -cpp_link_args = ['-sEXPORT_ALL=1'] - [host_machine] - system = 'emscripten' cpu_family = 'wasm32' cpu = 'wasm32' diff --git a/cross/wasm64-emscripten.txt b/cross/wasm64-emscripten.txt new file mode 100644 index 000000000000..ea55f39376cd --- /dev/null +++ b/cross/wasm64-emscripten.txt @@ -0,0 +1,18 @@ +[binaries] +c = 'emcc' +cpp = 'em++' +ar = 'emar' +ranlib = 'emranlib' +exe_wrapper = 'node' + +[built-in options] +c_args = ['-m64'] +c_link_args = ['-m64'] +cpp_args = ['-m64'] +cpp_link_args = ['-m64'] + +[host_machine] +system = 'emscripten' +cpu_family = 'wasm64' +cpu = 'wasm64' +endian = 'little' From f004d10e758073a5aa15b195e7047f5c662f5581 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Tue, 4 Aug 2026 18:56:32 +0300 Subject: [PATCH 626/681] Bump version number for rc3. --- man/meson.1 | 2 +- mesonbuild/coredata.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/man/meson.1 b/man/meson.1 index a6907165ce0b..c1f092a8e36b 100644 --- a/man/meson.1 +++ b/man/meson.1 @@ -1,4 +1,4 @@ -.TH MESON "1" "July 2026" "meson 1.12.0" "User Commands" +.TH MESON "1" "August 2026" "meson 1.12.0" "User Commands" .SH NAME meson - a high productivity build system .SH DESCRIPTION diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 5ae0c76af064..551684b00992 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -43,7 +43,7 @@ # # Pip requires that RCs are named like this: '0.1.0.rc1' # But the corresponding Git tag needs to be '0.1.0rc1' -version = '1.12.0.rc2' +version = '1.12.0.rc3' # The next stable version when we are in dev. This is used to allow projects to # require meson version >=1.2.0 when using 1.1.99. FeatureNew won't warn when From 01c123cce7fb6c3dd05ea85740d0c519699aa3ce Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 9 Aug 2026 07:48:52 +0200 Subject: [PATCH 627/681] qt: use source_strings_to_files for the Qt module's generators Commit 1217faedc ("build: never pass str to Generator.process_files", 2026-06-15) replaced Generator.process_files's own input normalization with _qt.py's _source_to_files(). The two functions do different things for GeneratedList inputs, with _source_to_files() *not* adding a dependency from the input generator to the new one and also not wrapping the output into FileInTargetPrivateDir. So, after the commit a GeneratedList passed to moc never depends on the input GeneratedList, and also has the wrong path. The remaining _source_to_files uses are for .qrc and qmldir generation, and they existed even before commit 1217faedc. Fixes: #16060 --- mesonbuild/modules/_qt.py | 8 ++++---- test cases/frameworks/4 qt/copyfile.py | 6 ++++++ test cases/frameworks/4 qt/manualinclude.cpp | 2 +- test cases/frameworks/4 qt/meson.build | 10 +++++++++- 4 files changed, 20 insertions(+), 6 deletions(-) create mode 100644 test cases/frameworks/4 qt/copyfile.py diff --git a/mesonbuild/modules/_qt.py b/mesonbuild/modules/_qt.py index b29e6648fa2d..5f5ff65a7160 100644 --- a/mesonbuild/modules/_qt.py +++ b/mesonbuild/modules/_qt.py @@ -525,7 +525,7 @@ def _compile_ui_impl(self, state: ModuleState, kwargs: UICompilerKwArgs) -> buil kwargs['extra_args'] + ['-o', '@OUTPUT@', '@INPUT@'], ['ui_@BASENAME@.h'], name=f'Qt{self.qt_version} ui') - sources = self._source_to_files(state, kwargs['sources']) + sources = self.interpreter.source_strings_to_files(kwargs['sources']) return gen.process_files(sources, state.subdir, preserve_path_from) @FeatureNew('qt.compile_moc', '0.59.0') @@ -606,7 +606,7 @@ def _compile_moc_impl(self, state: ModuleState, kwargs: MocCompilerKwArgs) -> T. depends=depends, depfile='moc_@BASENAME@.cpp.d', name=f'Qt{self.qt_version} moc header') - headers = self._source_to_files(state, kwargs['headers']) + headers = self.interpreter.source_strings_to_files(kwargs['headers']) output.append(moc_gen.process_files(headers, state.subdir, preserve_path_from)) if kwargs['sources']: source_gen_output: T.List[str] = ['@BASENAME@.moc'] @@ -617,7 +617,7 @@ def _compile_moc_impl(self, state: ModuleState, kwargs: MocCompilerKwArgs) -> T. self.tools['moc'], arguments, source_gen_output, depfile='@BASENAME@.moc.d', name=f'Qt{self.qt_version} moc source') - sources = self._source_to_files(state, kwargs['sources']) + sources = self.interpreter.source_strings_to_files(kwargs['sources']) output.append(moc_gen.process_files(sources, state.subdir, preserve_path_from)) return output @@ -926,7 +926,7 @@ def _gen_qml_cachegen(self, state: ModuleState, kwargs: GenQmlCachegenKwArgs) -> name=f'Qml cache generation for {target_name}') output: T.List[T.Union[build.CustomTarget, build.GeneratedList]] = [] - qml_sources = self._source_to_files(state, kwargs['qml_sources']) + qml_sources = self.interpreter.source_strings_to_files(kwargs['qml_sources']) output.append(cache_gen.process_files(qml_sources, state.subdir)) cachegen_inputs: T.List[str] = [] diff --git a/test cases/frameworks/4 qt/copyfile.py b/test cases/frameworks/4 qt/copyfile.py new file mode 100644 index 000000000000..7e44c48dd1a5 --- /dev/null +++ b/test cases/frameworks/4 qt/copyfile.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python3 + +import sys +import shutil + +shutil.copy(sys.argv[1], sys.argv[2]) diff --git a/test cases/frameworks/4 qt/manualinclude.cpp b/test cases/frameworks/4 qt/manualinclude.cpp index ef151a44aad6..32d74fb61c2c 100644 --- a/test cases/frameworks/4 qt/manualinclude.cpp +++ b/test cases/frameworks/4 qt/manualinclude.cpp @@ -24,4 +24,4 @@ int main(int argc, char **argv) { return 0; } -#include"manualinclude.moc" +#include"manualincludeGen.moc" diff --git a/test cases/frameworks/4 qt/meson.build b/test cases/frameworks/4 qt/meson.build index dd82fddddcf6..14711b1302ea 100644 --- a/test cases/frameworks/4 qt/meson.build +++ b/test cases/frameworks/4 qt/meson.build @@ -9,8 +9,16 @@ fs = import('fs') qt5_modules = ['Widgets'] qt6_modules = ['Widgets'] +copyfile = find_program('copyfile.py') +copyfile_gen = generator(copyfile, + output: '@BASENAME@Gen.cpp', + arguments : ['@INPUT@', '@OUTPUT@']) + +manualinclude_cpp_gen = copyfile_gen.process('manualinclude.cpp') + manualinclude_cpp = fs.copyfile('manualinclude.cpp') manualinclude_h = fs.copyfile('manualinclude.h') + foreach qt : ['qt4', 'qt5', 'qt6'] qt_modules = ['Core', 'Gui'] if qt == 'qt5' @@ -143,7 +151,7 @@ foreach qt : ['qt4', 'qt5', 'qt6'] manpreprocessed = qtmodule.compile_moc( extra_args : ['-DMOC_EXTRA_FLAG'], # This is just a random macro to test `extra_arguments` - sources : 'manualinclude.cpp', + sources : manualinclude_cpp_gen, headers : 'manualinclude.h', method : get_option('method'), dependencies: mocdep, From ab4508e6abe338a3bd2e6eb5da1d18b8cf55dc93 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Mon, 10 Aug 2026 23:56:48 +0300 Subject: [PATCH 628/681] Bump version number for release. --- docs/markdown/Release-notes-for-1.12.0.md | 255 ++++++++++++++++++ .../snippets/atomic-now-supports-msvc.md | 4 - docs/markdown/snippets/cachedir_tag.md | 5 - docs/markdown/snippets/clippy-json.md | 7 - docs/markdown/snippets/default.md | 13 - .../snippets/delayed_install_script_errors.md | 7 - .../snippets/find_program-forcefallback.md | 5 - .../snippets/fs-copyfile-build-subdir.md | 4 - .../snippets/generator_process_depends.md | 4 - docs/markdown/snippets/i18n_msgfmt_args.md | 12 - .../snippets/include_directories_to_list.md | 10 - .../markdown/snippets/int-base-conversions.md | 10 - .../snippets/meson_wrap_install_multiple.md | 6 - .../snippets/mformat-recursive-check-diff.md | 5 - .../snippets/mformat-recursive-subprojects.md | 4 - docs/markdown/snippets/ohos-support.md | 20 -- .../snippets/programs-in-more-places.md | 12 - docs/markdown/snippets/python-310-required.md | 8 - docs/markdown/snippets/qt-preprocess-gen.md | 5 - docs/markdown/snippets/rust-cbindgen.md | 5 - .../snippets/rust-workspace-non-default.md | 6 - .../subproject_and_override_program_native.md | 16 -- docs/markdown/snippets/tests-exclude.md | 6 - docs/markdown/snippets/vala-cross.md | 7 - docs/markdown/snippets/werror-linker.md | 6 - .../snippets/win_subsystem_for_more.md | 10 - .../xgettext-include-private-dependencies.md | 24 -- docs/sitemap.txt | 1 + mesonbuild/coredata.py | 2 +- 29 files changed, 257 insertions(+), 222 deletions(-) create mode 100644 docs/markdown/Release-notes-for-1.12.0.md delete mode 100644 docs/markdown/snippets/atomic-now-supports-msvc.md delete mode 100644 docs/markdown/snippets/cachedir_tag.md delete mode 100644 docs/markdown/snippets/clippy-json.md delete mode 100644 docs/markdown/snippets/default.md delete mode 100644 docs/markdown/snippets/delayed_install_script_errors.md delete mode 100644 docs/markdown/snippets/find_program-forcefallback.md delete mode 100644 docs/markdown/snippets/fs-copyfile-build-subdir.md delete mode 100644 docs/markdown/snippets/generator_process_depends.md delete mode 100644 docs/markdown/snippets/i18n_msgfmt_args.md delete mode 100644 docs/markdown/snippets/include_directories_to_list.md delete mode 100644 docs/markdown/snippets/int-base-conversions.md delete mode 100644 docs/markdown/snippets/meson_wrap_install_multiple.md delete mode 100644 docs/markdown/snippets/mformat-recursive-check-diff.md delete mode 100644 docs/markdown/snippets/mformat-recursive-subprojects.md delete mode 100644 docs/markdown/snippets/ohos-support.md delete mode 100644 docs/markdown/snippets/programs-in-more-places.md delete mode 100644 docs/markdown/snippets/python-310-required.md delete mode 100644 docs/markdown/snippets/qt-preprocess-gen.md delete mode 100644 docs/markdown/snippets/rust-cbindgen.md delete mode 100644 docs/markdown/snippets/rust-workspace-non-default.md delete mode 100644 docs/markdown/snippets/subproject_and_override_program_native.md delete mode 100644 docs/markdown/snippets/tests-exclude.md delete mode 100644 docs/markdown/snippets/vala-cross.md delete mode 100644 docs/markdown/snippets/werror-linker.md delete mode 100644 docs/markdown/snippets/win_subsystem_for_more.md delete mode 100644 docs/markdown/snippets/xgettext-include-private-dependencies.md diff --git a/docs/markdown/Release-notes-for-1.12.0.md b/docs/markdown/Release-notes-for-1.12.0.md new file mode 100644 index 000000000000..d07f3b0d7148 --- /dev/null +++ b/docs/markdown/Release-notes-for-1.12.0.md @@ -0,0 +1,255 @@ +--- +title: Release 1.12.0 +short-description: Release notes for 1.12.0 +... + +# New features + +Meson 1.12.0 was released on 10 August 2026 +## Custom dependency for atomic now works on MSVC + +`dependency('atomic')` now works on MSVC >=19.35.32124. +It requires `c_std=c11` or later, otherwise the dependency will return not found. + +## Support for CACHEDIR.TAG specification + +A CACHEDIR.TAG file (https://bford.info/cachedir/) is now created in the build +directory as part of the setup and regenerate phases, allowing backup software +to ignore the whole directory. + +## New "clippy-json" Ninja Target For rust-analyzer + +A new `clippy-json` ninja target will now be generated for rust projects. + +rust-analyzer supports non-cargo based projects so long as you provide it with a `rust-project.json` file and a custom "check command" to provide compiler errors. Meson already for some time has generated a `rust-project.json` file for rust-analyzer, but had no way to hook up its rustc/clippy output to rust-analyzer. + +To use the new feature, you need to override the rust-analyzer check command as shown [in the rust-analyzer documentation](https://rust-analyzer.github.io/book/non_cargo_based_projects.html), and set it to a `ninja invocation along the lines of `ninja clippy-json -C build`. + +## Add a new `default()` function and object + +This function and object allow setting a keyword argument to it's default value. +This is especially useful for cases when a non-default value is wanted in some +cases but not all. + +For example: +```meson +library( + ... + name_prefix : host_machine.system() == 'windows' ? '' : default(), +) +``` + +## Delay install script errors to install time + +Previously, `gnome.post_install` might've caused a configure to fail +due to tools such as `update-desktop-database` not being installed or +due to an `exe_wrapper` missing when cross-building glib. Those +errors will now be delayed to install time, or not emitted at all +if installing with `DESTDIR` set, since then they'll be skipped. + +## find_program() now respects --force-fallback-for + +If a subproject listed in --force-fallback-for provides a particular +program (via program_names in its wrapfile), find_program will use the +program from the subproject instead of looking it up from the system. + +## `fs.copyfile()` now has a `build_subdir` argument + +`fs.copyfile()`'s new `build_subdir` argument allows creating a file +inside a subdirectory of the current build directory. + +## Added `depends` kwarg to `generator.process()` + +The [[generator.process]] method now supports specifying `depends` as a kwarg +to extend the list of dependencies for generated files. + +## Added `msgfmt_args` arg to `i18n.gettext` + +`i18n.gettext` now supports the `msgfmt_args` argument. +This argument can be used to pass additional arguments to +`msgfmt` when building the translations. + +```meson +i18n = import('i18n') +i18n.gettext('mycatalog', + args : ['-k_', '-kN_'], + msgfmt_args : ['--use-fuzzy']) +``` + +## include_directories object now have a to_list() method + +The [[@inc]] object returned by [[include_directories]] now has a `to_list()` method that +returns a list of strings of absolute paths. Unless the include path is a +system path, there will be a path for the source root and one for the build root. + +``` +inc = include_directories('include') +include_paths = inc.to_list() +``` + +## Added integer base conversions to `str.to_int()` and `int.to_string()` + +Meson strings can now be converted from hexadecimal, octal, and binary +integer literals with `str.to_int()`. Integers can also be formatted back to +strings in those bases with `int.to_string(format:)`. + +```meson +assert('0xff'.to_int() == 255) +assert(255.to_string(format: 'hex') == '0xff') +``` + +## Multiple positional arguments for `meson wrap install` + +Previously `meson wrap install` only accepted a single wrap name to install. +This limitation has now been lifted, and it is now possible to specify +multiple wrap names for installation. The exit code will be non-zero +if *any* of the wraps fail to install. + +## `meson format` allows `--recursive` with `--check-diff` + +Previously, the `--recursive` option to `meson format` required +either `--inplace` or `--output`. With this version, `--check-diff` +is allowed to. + +## New option `--subprojects` for `meson format` + +A new option `--subprojects`, to be specified together with `--recursive`, +tells `meson format` to also recurse into subprojects. + +## OpenHarmony (OHOS) is now recognized as an Android subsystem + +Meson now recognizes OpenHarmony / HarmonyOS (OHOS). OHOS behaves like +Android -- applications are shared libraries, shared libraries are not +versioned, and so on -- but uses musl instead of Bionic. It is therefore +modelled as an Android subsystem rather than a separate system. + +Machine files select it in the `[host_machine]` section with: + +```ini +[host_machine] +system = 'android' +subsystem = 'ohos' +``` + +With this, `host_machine.system()` is `'android'` (so all Android handling +applies, including unversioned `libfoo.so` output) while +`host_machine.subsystem()` is `'ohos'`. When cross-compiling a CMake +subproject, the toolchain file generated by the CMake module sets +`CMAKE_SYSTEM_NAME = OHOS`. + +## External programs as inputs and dependencies to custom targets + +Custom targets now allow specifying an external program in +the `input` and `depends` keyword arguments. This also applies +to several methods provided by modules, as they are lowered to +custom targets internally. + +## External programs as dependencies to tests + +Tests now allow specifying an external program in +the `depends` keyword argument. + + +## Support for Python 3.7, 3.8, and 3.9 dropped + +Meson 1.12 is the first version to require Python version 3.10 or greater. + +Support for older versions of Python is maintained with bug fixes only for +some LTS releases. See the [FAQ +entry](FAQ.md#Do-you-at-least-support-my-ancient-python-install) for more +information. + +## More types of generated files allowed by `qt.preprocess` + +`qt.preprocess` now allows custom target indexes or generators +in addition to custom targets for `ui_files`, `moc_sources` +and `moc_headers`. + +## The Rust module includes a basic wrapper for cbindgen + +This will correctly track the config.toml, and generate a rust source from the +marked C files. This handles structured_sources correctly, and also handles +depfile generation transparently. + +## Non-default members of Cargo workspaces can now be built + +The new keyword argument `extra_members` to the `workspace()` method +allows configuring non-default members of a Cargo workspace. Previously, +non-default members were never used for dependency resolution and +could not be built. + +## subproject() and meson.override_find_program() now support the native keyword argument + +Subprojects may now be built for the host or build machine (or both). Therefore, +in a cross compile setup, build-time dependencies can be built for the machine +running the build. When a subproject is run for the build machine it will act +just like a normal build == host setup, except that no targets will be installed. + +This necessarily means that `meson.override_find_program()` must differentiate +between programs for the host and those for the build machine, as you may need +two versions of the same program, which have different outputs based on the +machine they are for. For backwards compatibility reasons the default is for the +host machine. Inside a native subproject the host and build machine will both be +the build machine. + +Cargo workspaces use this feature automatically when building procedural macro +crates and their dependencies. + +## `meson test` now accepts `--exclude` + +`meson test` has a new `--exclude` argument to allow skipping named +tests. It takes a full test name and can be specified repeatedly. This +should help distributions that need to skip tests irrelevant for them +or known to be buggy. + +## Vala cross compilation changes + +Traditionally Meson has used the same Vala compiler for cross and +native compilation. It turned out that even though the Vala compiler +generated C code, the output and its dependencies are platform +dependent. Now Meson uses Vala in the same way as all other cross +compilers and it can be defined in the cross file. + +## `werror=true` now applies to the linker as well + +When `werror=true` is set, Meson now passes the appropriate +fatal-warnings flag to the linker (for example `--fatal-warnings` +for GNU ld, `-fatal_warnings` for Apple ld, `/WX` for MSVC link). +Previously, `werror=true` only affected compiler warnings. + +## Added `win_subsystem` to `shared_library()` and `shared_module()` + +Synonym for the one found in `executable()`. + +Mostly useful for setting the subsystem version in PE headers. +This can affects Windows's ShimEngine, but not SxS lookups. Usually you still +want to provide a manifest when targeting modern Windows. +Sometime also useful for targeting other subsystems. + +Visual Studio backends now also properly supports setting subsystem versions. + +## i18n.xgettext recursive option now includes "private" dependencies + +Suppose we have: + +``` +libA.dll -> libB.dll -> libC.dll +``` + +Here, `libA` links with `libB`, and `libB` links with `libC`, but `libA` does +not link with `libC` directly. So, `libC` is a "private" dependency of `libB`. +If we collect strings to translate using: + +``` +i18n.xgettext(libC) +i18n.xgettext(libB) +pot_file = i18n.xgettext(libA, recursive: true) +``` + +Previously, strings from `libC` would not be included in `pot_file`, since +`libC` is not a direct link dependency of `libA`. This has been fixed: when +the `recursive: true` option is used, `xgettext` now recursively includes +translations from all dependencies, including those of dependencies. This is +more logical, as even if `libA` does not directly link with `libC`, it may +still need translated strings from `libC`. + diff --git a/docs/markdown/snippets/atomic-now-supports-msvc.md b/docs/markdown/snippets/atomic-now-supports-msvc.md deleted file mode 100644 index 8d3d30044b5b..000000000000 --- a/docs/markdown/snippets/atomic-now-supports-msvc.md +++ /dev/null @@ -1,4 +0,0 @@ -## Custom dependency for atomic now works on MSVC - -`dependency('atomic')` now works on MSVC >=19.35.32124. -It requires `c_std=c11` or later, otherwise the dependency will return not found. diff --git a/docs/markdown/snippets/cachedir_tag.md b/docs/markdown/snippets/cachedir_tag.md deleted file mode 100644 index 470203933bc1..000000000000 --- a/docs/markdown/snippets/cachedir_tag.md +++ /dev/null @@ -1,5 +0,0 @@ -## Support for CACHEDIR.TAG specification - -A CACHEDIR.TAG file (https://bford.info/cachedir/) is now created in the build -directory as part of the setup and regenerate phases, allowing backup software -to ignore the whole directory. diff --git a/docs/markdown/snippets/clippy-json.md b/docs/markdown/snippets/clippy-json.md deleted file mode 100644 index 7e591453d9e5..000000000000 --- a/docs/markdown/snippets/clippy-json.md +++ /dev/null @@ -1,7 +0,0 @@ -## New "clippy-json" Ninja Target For rust-analyzer - -A new `clippy-json` ninja target will now be generated for rust projects. - -rust-analyzer supports non-cargo based projects so long as you provide it with a `rust-project.json` file and a custom "check command" to provide compiler errors. Meson already for some time has generated a `rust-project.json` file for rust-analyzer, but had no way to hook up its rustc/clippy output to rust-analyzer. - -To use the new feature, you need to override the rust-analyzer check command as shown [in the rust-analyzer documentation](https://rust-analyzer.github.io/book/non_cargo_based_projects.html), and set it to a `ninja invocation along the lines of `ninja clippy-json -C build`. diff --git a/docs/markdown/snippets/default.md b/docs/markdown/snippets/default.md deleted file mode 100644 index f2e4b7351f28..000000000000 --- a/docs/markdown/snippets/default.md +++ /dev/null @@ -1,13 +0,0 @@ -## Add a new `default()` function and object - -This function and object allow setting a keyword argument to it's default value. -This is especially useful for cases when a non-default value is wanted in some -cases but not all. - -For example: -```meson -library( - ... - name_prefix : host_machine.system() == 'windows' ? '' : default(), -) -``` diff --git a/docs/markdown/snippets/delayed_install_script_errors.md b/docs/markdown/snippets/delayed_install_script_errors.md deleted file mode 100644 index 7c81f22a01ee..000000000000 --- a/docs/markdown/snippets/delayed_install_script_errors.md +++ /dev/null @@ -1,7 +0,0 @@ -## Delay install script errors to install time - -Previously, `gnome.post_install` might've caused a configure to fail -due to tools such as `update-desktop-database` not being installed or -due to an `exe_wrapper` missing when cross-building glib. Those -errors will now be delayed to install time, or not emitted at all -if installing with `DESTDIR` set, since then they'll be skipped. diff --git a/docs/markdown/snippets/find_program-forcefallback.md b/docs/markdown/snippets/find_program-forcefallback.md deleted file mode 100644 index 5ed99de8a785..000000000000 --- a/docs/markdown/snippets/find_program-forcefallback.md +++ /dev/null @@ -1,5 +0,0 @@ -## find_program() now respects --force-fallback-for - -If a subproject listed in --force-fallback-for provides a particular -program (via program_names in its wrapfile), find_program will use the -program from the subproject instead of looking it up from the system. diff --git a/docs/markdown/snippets/fs-copyfile-build-subdir.md b/docs/markdown/snippets/fs-copyfile-build-subdir.md deleted file mode 100644 index f1664256a42c..000000000000 --- a/docs/markdown/snippets/fs-copyfile-build-subdir.md +++ /dev/null @@ -1,4 +0,0 @@ -## `fs.copyfile()` now has a `build_subdir` argument - -`fs.copyfile()`'s new `build_subdir` argument allows creating a file -inside a subdirectory of the current build directory. diff --git a/docs/markdown/snippets/generator_process_depends.md b/docs/markdown/snippets/generator_process_depends.md deleted file mode 100644 index c9c98b72a64f..000000000000 --- a/docs/markdown/snippets/generator_process_depends.md +++ /dev/null @@ -1,4 +0,0 @@ -## Added `depends` kwarg to `generator.process()` - -The [[generator.process]] method now supports specifying `depends` as a kwarg -to extend the list of dependencies for generated files. diff --git a/docs/markdown/snippets/i18n_msgfmt_args.md b/docs/markdown/snippets/i18n_msgfmt_args.md deleted file mode 100644 index d7106d4ae282..000000000000 --- a/docs/markdown/snippets/i18n_msgfmt_args.md +++ /dev/null @@ -1,12 +0,0 @@ -## Added `msgfmt_args` arg to `i18n.gettext` - -`i18n.gettext` now supports the `msgfmt_args` argument. -This argument can be used to pass additional arguments to -`msgfmt` when building the translations. - -```meson -i18n = import('i18n') -i18n.gettext('mycatalog', - args : ['-k_', '-kN_'], - msgfmt_args : ['--use-fuzzy']) -``` diff --git a/docs/markdown/snippets/include_directories_to_list.md b/docs/markdown/snippets/include_directories_to_list.md deleted file mode 100644 index 2b2d7dbf4984..000000000000 --- a/docs/markdown/snippets/include_directories_to_list.md +++ /dev/null @@ -1,10 +0,0 @@ -## include_directories object now have a to_list() method - -The [[@inc]] object returned by [[include_directories]] now has a `to_list()` method that -returns a list of strings of absolute paths. Unless the include path is a -system path, there will be a path for the source root and one for the build root. - -``` -inc = include_directories('include') -include_paths = inc.to_list() -``` diff --git a/docs/markdown/snippets/int-base-conversions.md b/docs/markdown/snippets/int-base-conversions.md deleted file mode 100644 index 4b1cbf2b968f..000000000000 --- a/docs/markdown/snippets/int-base-conversions.md +++ /dev/null @@ -1,10 +0,0 @@ -## Added integer base conversions to `str.to_int()` and `int.to_string()` - -Meson strings can now be converted from hexadecimal, octal, and binary -integer literals with `str.to_int()`. Integers can also be formatted back to -strings in those bases with `int.to_string(format:)`. - -```meson -assert('0xff'.to_int() == 255) -assert(255.to_string(format: 'hex') == '0xff') -``` diff --git a/docs/markdown/snippets/meson_wrap_install_multiple.md b/docs/markdown/snippets/meson_wrap_install_multiple.md deleted file mode 100644 index 4d226c3a7a69..000000000000 --- a/docs/markdown/snippets/meson_wrap_install_multiple.md +++ /dev/null @@ -1,6 +0,0 @@ -## Multiple positional arguments for `meson wrap install` - -Previously `meson wrap install` only accepted a single wrap name to install. -This limitation has now been lifted, and it is now possible to specify -multiple wrap names for installation. The exit code will be non-zero -if *any* of the wraps fail to install. diff --git a/docs/markdown/snippets/mformat-recursive-check-diff.md b/docs/markdown/snippets/mformat-recursive-check-diff.md deleted file mode 100644 index 0752098fca6a..000000000000 --- a/docs/markdown/snippets/mformat-recursive-check-diff.md +++ /dev/null @@ -1,5 +0,0 @@ -## `meson format` allows `--recursive` with `--check-diff` - -Previously, the `--recursive` option to `meson format` required -either `--inplace` or `--output`. With this version, `--check-diff` -is allowed to. diff --git a/docs/markdown/snippets/mformat-recursive-subprojects.md b/docs/markdown/snippets/mformat-recursive-subprojects.md deleted file mode 100644 index 73416d2ef45e..000000000000 --- a/docs/markdown/snippets/mformat-recursive-subprojects.md +++ /dev/null @@ -1,4 +0,0 @@ -## New option `--subprojects` for `meson format` - -A new option `--subprojects`, to be specified together with `--recursive`, -tells `meson format` to also recurse into subprojects. diff --git a/docs/markdown/snippets/ohos-support.md b/docs/markdown/snippets/ohos-support.md deleted file mode 100644 index 4331a8709c51..000000000000 --- a/docs/markdown/snippets/ohos-support.md +++ /dev/null @@ -1,20 +0,0 @@ -## OpenHarmony (OHOS) is now recognized as an Android subsystem - -Meson now recognizes OpenHarmony / HarmonyOS (OHOS). OHOS behaves like -Android -- applications are shared libraries, shared libraries are not -versioned, and so on -- but uses musl instead of Bionic. It is therefore -modelled as an Android subsystem rather than a separate system. - -Machine files select it in the `[host_machine]` section with: - -```ini -[host_machine] -system = 'android' -subsystem = 'ohos' -``` - -With this, `host_machine.system()` is `'android'` (so all Android handling -applies, including unversioned `libfoo.so` output) while -`host_machine.subsystem()` is `'ohos'`. When cross-compiling a CMake -subproject, the toolchain file generated by the CMake module sets -`CMAKE_SYSTEM_NAME = OHOS`. diff --git a/docs/markdown/snippets/programs-in-more-places.md b/docs/markdown/snippets/programs-in-more-places.md deleted file mode 100644 index e561cce9537e..000000000000 --- a/docs/markdown/snippets/programs-in-more-places.md +++ /dev/null @@ -1,12 +0,0 @@ -## External programs as inputs and dependencies to custom targets - -Custom targets now allow specifying an external program in -the `input` and `depends` keyword arguments. This also applies -to several methods provided by modules, as they are lowered to -custom targets internally. - -## External programs as dependencies to tests - -Tests now allow specifying an external program in -the `depends` keyword argument. - diff --git a/docs/markdown/snippets/python-310-required.md b/docs/markdown/snippets/python-310-required.md deleted file mode 100644 index 31a1de69ce4d..000000000000 --- a/docs/markdown/snippets/python-310-required.md +++ /dev/null @@ -1,8 +0,0 @@ -## Support for Python 3.7, 3.8, and 3.9 dropped - -Meson 1.12 is the first version to require Python version 3.10 or greater. - -Support for older versions of Python is maintained with bug fixes only for -some LTS releases. See the [FAQ -entry](FAQ.md#Do-you-at-least-support-my-ancient-python-install) for more -information. diff --git a/docs/markdown/snippets/qt-preprocess-gen.md b/docs/markdown/snippets/qt-preprocess-gen.md deleted file mode 100644 index 3ac05486cf52..000000000000 --- a/docs/markdown/snippets/qt-preprocess-gen.md +++ /dev/null @@ -1,5 +0,0 @@ -## More types of generated files allowed by `qt.preprocess` - -`qt.preprocess` now allows custom target indexes or generators -in addition to custom targets for `ui_files`, `moc_sources` -and `moc_headers`. diff --git a/docs/markdown/snippets/rust-cbindgen.md b/docs/markdown/snippets/rust-cbindgen.md deleted file mode 100644 index f5fb0eb4ea8d..000000000000 --- a/docs/markdown/snippets/rust-cbindgen.md +++ /dev/null @@ -1,5 +0,0 @@ -## The Rust module includes a basic wrapper for cbindgen - -This will correctly track the config.toml, and generate a rust source from the -marked C files. This handles structured_sources correctly, and also handles -depfile generation transparently. diff --git a/docs/markdown/snippets/rust-workspace-non-default.md b/docs/markdown/snippets/rust-workspace-non-default.md deleted file mode 100644 index deb74f73863a..000000000000 --- a/docs/markdown/snippets/rust-workspace-non-default.md +++ /dev/null @@ -1,6 +0,0 @@ -## Non-default members of Cargo workspaces can now be built - -The new keyword argument `extra_members` to the `workspace()` method -allows configuring non-default members of a Cargo workspace. Previously, -non-default members were never used for dependency resolution and -could not be built. diff --git a/docs/markdown/snippets/subproject_and_override_program_native.md b/docs/markdown/snippets/subproject_and_override_program_native.md deleted file mode 100644 index 75142524b5b7..000000000000 --- a/docs/markdown/snippets/subproject_and_override_program_native.md +++ /dev/null @@ -1,16 +0,0 @@ -## subproject() and meson.override_find_program() now support the native keyword argument - -Subprojects may now be built for the host or build machine (or both). Therefore, -in a cross compile setup, build-time dependencies can be built for the machine -running the build. When a subproject is run for the build machine it will act -just like a normal build == host setup, except that no targets will be installed. - -This necessarily means that `meson.override_find_program()` must differentiate -between programs for the host and those for the build machine, as you may need -two versions of the same program, which have different outputs based on the -machine they are for. For backwards compatibility reasons the default is for the -host machine. Inside a native subproject the host and build machine will both be -the build machine. - -Cargo workspaces use this feature automatically when building procedural macro -crates and their dependencies. diff --git a/docs/markdown/snippets/tests-exclude.md b/docs/markdown/snippets/tests-exclude.md deleted file mode 100644 index daf282ef59a6..000000000000 --- a/docs/markdown/snippets/tests-exclude.md +++ /dev/null @@ -1,6 +0,0 @@ -## `meson test` now accepts `--exclude` - -`meson test` has a new `--exclude` argument to allow skipping named -tests. It takes a full test name and can be specified repeatedly. This -should help distributions that need to skip tests irrelevant for them -or known to be buggy. diff --git a/docs/markdown/snippets/vala-cross.md b/docs/markdown/snippets/vala-cross.md deleted file mode 100644 index c3abc50741b1..000000000000 --- a/docs/markdown/snippets/vala-cross.md +++ /dev/null @@ -1,7 +0,0 @@ -## Vala cross compilation changes - -Traditionally Meson has used the same Vala compiler for cross and -native compilation. It turned out that even though the Vala compiler -generated C code, the output and its dependencies are platform -dependent. Now Meson uses Vala in the same way as all other cross -compilers and it can be defined in the cross file. diff --git a/docs/markdown/snippets/werror-linker.md b/docs/markdown/snippets/werror-linker.md deleted file mode 100644 index 3976c26c544e..000000000000 --- a/docs/markdown/snippets/werror-linker.md +++ /dev/null @@ -1,6 +0,0 @@ -## `werror=true` now applies to the linker as well - -When `werror=true` is set, Meson now passes the appropriate -fatal-warnings flag to the linker (for example `--fatal-warnings` -for GNU ld, `-fatal_warnings` for Apple ld, `/WX` for MSVC link). -Previously, `werror=true` only affected compiler warnings. diff --git a/docs/markdown/snippets/win_subsystem_for_more.md b/docs/markdown/snippets/win_subsystem_for_more.md deleted file mode 100644 index e89784639cb5..000000000000 --- a/docs/markdown/snippets/win_subsystem_for_more.md +++ /dev/null @@ -1,10 +0,0 @@ -## Added `win_subsystem` to `shared_library()` and `shared_module()` - -Synonym for the one found in `executable()`. - -Mostly useful for setting the subsystem version in PE headers. -This can affects Windows's ShimEngine, but not SxS lookups. Usually you still -want to provide a manifest when targeting modern Windows. -Sometime also useful for targeting other subsystems. - -Visual Studio backends now also properly supports setting subsystem versions. diff --git a/docs/markdown/snippets/xgettext-include-private-dependencies.md b/docs/markdown/snippets/xgettext-include-private-dependencies.md deleted file mode 100644 index 81f97c654689..000000000000 --- a/docs/markdown/snippets/xgettext-include-private-dependencies.md +++ /dev/null @@ -1,24 +0,0 @@ -## i18n.xgettext recursive option now includes "private" dependencies - -Suppose we have: - -``` -libA.dll -> libB.dll -> libC.dll -``` - -Here, `libA` links with `libB`, and `libB` links with `libC`, but `libA` does -not link with `libC` directly. So, `libC` is a "private" dependency of `libB`. -If we collect strings to translate using: - -``` -i18n.xgettext(libC) -i18n.xgettext(libB) -pot_file = i18n.xgettext(libA, recursive: true) -``` - -Previously, strings from `libC` would not be included in `pot_file`, since -`libC` is not a direct link dependency of `libA`. This has been fixed: when -the `recursive: true` option is used, `xgettext` now recursively includes -translations from all dependencies, including those of dependencies. This is -more logical, as even if `libA` does not directly link with `libC`, it may -still need translated strings from `libC`. diff --git a/docs/sitemap.txt b/docs/sitemap.txt index 6f0672b76644..c6bbb10ddf76 100644 --- a/docs/sitemap.txt +++ b/docs/sitemap.txt @@ -90,6 +90,7 @@ index.md Wrap-best-practices-and-tips.md Shipping-prebuilt-binaries-as-wraps.md Release-notes.md + Release-notes-for-1.12.0.md Release-notes-for-1.11.0.md Release-notes-for-1.10.0.md Release-notes-for-1.9.0.md diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 551684b00992..c02f6f676309 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -43,7 +43,7 @@ # # Pip requires that RCs are named like this: '0.1.0.rc1' # But the corresponding Git tag needs to be '0.1.0rc1' -version = '1.12.0.rc3' +version = '1.12.0' # The next stable version when we are in dev. This is used to allow projects to # require meson version >=1.2.0 when using 1.1.99. FeatureNew won't warn when From b04f7f023b9d5530d854a53ca8b4c581338b1216 Mon Sep 17 00:00:00 2001 From: Ricci Adams Date: Tue, 4 Aug 2026 09:23:03 -0700 Subject: [PATCH 629/681] Add 'objc' and 'objcpp' suffixes to fix #16063 (cherry picked from commit 055bbc8ebdfc904701b5aa36e3e67f003da32c9f) --- mesonbuild/scripts/run_tool.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mesonbuild/scripts/run_tool.py b/mesonbuild/scripts/run_tool.py index 4b3878c78e5d..349102e63e28 100644 --- a/mesonbuild/scripts/run_tool.py +++ b/mesonbuild/scripts/run_tool.py @@ -17,6 +17,9 @@ from ..mtest import complete_all import typing as T +if T.TYPE_CHECKING: + from ..compilers.compilers import Language + Info = T.TypeVar("Info") async def run_with_buffered_output(cmdlist: T.List[str], env: T.Optional[T.Dict[str, str]] = None) -> int: @@ -110,7 +113,8 @@ def all_clike_files(name: str, srcdir: Path, builddir: Path) -> T.Iterable[Path] patterns = parse_pattern_file(srcdir / f'.{name}-ignore') ignore = [str(builddir / '*')] ignore.extend([str(srcdir / p) for p in patterns]) - suffixes = set(lang_suffixes['c']).union(set(lang_suffixes['cpp'])) + langs: tuple[Language, ...] = ('c', 'cpp', 'objc', 'objcpp') + suffixes = {s for lang in langs for s in lang_suffixes[lang]} suffixes.add('h') suffixes = {f'.{s}' for s in suffixes} for f in itertools.chain.from_iterable(globs): From e4cf933213d0d20a193265098fd81da8854fd97f Mon Sep 17 00:00:00 2001 From: Enno Boland Date: Mon, 10 Aug 2026 09:07:18 +0200 Subject: [PATCH 630/681] options.py: fix f-string formatting in warning message. (cherry picked from commit 8f1b1579fdad67934cc90e50ca3545e2e4f8d1f1) --- mesonbuild/options.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 5ad5992d3040..147a442f038c 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -1371,7 +1371,7 @@ def initialize_from_subproject_call(self, for key, valstr in options.items(): if key.subproject != subproject: if key.subproject in self.subprojects and not self.option_has_value(key, valstr): - mlog.warning('option {key} is set in subproject {subproject} but has already been processed') + mlog.warning(f'option {key} is set in subproject {subproject} but has already been processed') continue # Subproject options from project() will be processed when the subproject is found From 712b3f3e7e8b95518cb41085856ff1531d81bae1 Mon Sep 17 00:00:00 2001 From: Lucas Baudin Date: Wed, 5 Aug 2026 17:55:52 +0200 Subject: [PATCH 631/681] cargo: Also allow proc_macro in addition to proc-macro both names are allowed by Cargo. Co-authored-by: Titouan Real (cherry picked from commit 76219245862b17517ed42956cc9ddc9ce8f4b489) --- mesonbuild/cargo/manifest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/cargo/manifest.py b/mesonbuild/cargo/manifest.py index 3f3e2db7731f..e2c041676f3b 100644 --- a/mesonbuild/cargo/manifest.py +++ b/mesonbuild/cargo/manifest.py @@ -386,9 +386,9 @@ class Library(BuildTarget): def from_raw(cls, raw: raw.LibTarget, pkg: Package) -> Self: name = raw.get('name', fixup_meson_varname(pkg.name)) # If proc_macro is True, it takes precedence and sets crate_type to proc-macro - proc_macro = raw.get('proc-macro', False) + proc_macro = raw.get('proc-macro', False) or raw.get('proc_macro', False) return _raw_to_dataclass(raw, cls, f'Library entry {name}', - ignored_fields=['proc-macro'], + ignored_fields=['proc-macro', 'proc_macro'], name=DefaultValue(name), path=DefaultValue('src/lib.rs'), edition=DefaultValue(pkg.edition), From 80ef2fdb7008d7bd7e6df138ce1ab7cd949d520a Mon Sep 17 00:00:00 2001 From: Srushti750 Date: Tue, 4 Aug 2026 19:18:12 +0530 Subject: [PATCH 632/681] gnome: Fix dependency between gir and sources (cherry picked from commit 2a67d560f04cd04110f17b6ddcde528eccff2778) --- mesonbuild/modules/gnome.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 3776d3927f30..7be632517b5a 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -989,6 +989,7 @@ def _make_gir_target( girfile: str, scan_command: T.Sequence[T.Union['FileOrString', Executable, Program]], generated_files: T.Sequence[build.GeneratedTypes], + depend_files: T.List[mesonlib.File], depends: T.Sequence[TargetDepends], env_flags: T.Sequence[str], kwargs: GenerateGir) -> GirTarget: @@ -1029,6 +1030,7 @@ def _make_gir_target( generated_files, [girfile], state.current_build_project, + depend_files=depend_files, build_by_default=kwargs['build_by_default'], extra_depends=depends, install=install, @@ -1262,10 +1264,16 @@ def generate_gir(self, state: 'ModuleState', args: T.Tuple[T.List[T.Union[Execut if kwargs['fatal_warnings']: scan_command.append('--warn-error') - generated_files = [f for f in libsources if isinstance(f, (GeneratedList, CustomTarget, CustomTargetIndex))] + generated_files: list[build.GeneratedTypes] = [] + depend_files: list[mesonlib.File] = [] + for f in libsources: + if isinstance(f, mesonlib.File): + depend_files.append(f) + elif isinstance(f, (GeneratedList, CustomTarget, CustomTargetIndex)): + generated_files.append(f) scan_target = self._make_gir_target( - state, girfile, scan_command, generated_files, depends, + state, girfile, scan_command, generated_files, depend_files, depends, scan_env_ldflags, kwargs) typelib_output = f'{ns}-{nsversion}.typelib' From fcdb7ac1e7fa927e640bf06494c29b7887e20c65 Mon Sep 17 00:00:00 2001 From: Luna Smith Date: Sun, 26 Jul 2026 11:03:31 +1000 Subject: [PATCH 633/681] Handle invalid CMake skip compiler test values (cherry picked from commit b319fd0906ccdb23a37b09ac14751da43351c5c2) --- mesonbuild/envconfig.py | 1 - unittests/internaltests.py | 9 ++++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/mesonbuild/envconfig.py b/mesonbuild/envconfig.py index 7c5aa88505b1..65d082345369 100644 --- a/mesonbuild/envconfig.py +++ b/mesonbuild/envconfig.py @@ -222,7 +222,6 @@ def get_cmake_skip_compiler_test(self) -> CMakeSkipCompilerTest: if 'cmake_skip_compiler_test' not in self.properties: return CMakeSkipCompilerTest.DEP_ONLY raw = self.properties['cmake_skip_compiler_test'] - assert isinstance(raw, str) try: return CMakeSkipCompilerTest(raw) except ValueError: diff --git a/unittests/internaltests.py b/unittests/internaltests.py index 5647a0e68a43..f623a98e53cb 100644 --- a/unittests/internaltests.py +++ b/unittests/internaltests.py @@ -38,7 +38,7 @@ from mesonbuild.interpreterbase import typed_pos_args, InvalidArguments, typed_kwargs, ContainerTypeInfo, KwargInfo from mesonbuild.mesonlib import ( LibType, MachineChoice, PerMachine, SimpleABC, Version, is_windows, is_osx, - is_cygwin, is_openbsd, search_version, MesonException, python_command, + is_cygwin, is_openbsd, search_version, MesonException, EnvironmentException, python_command, version_check_to_range, ) from mesonbuild.options import OptionKey @@ -54,6 +54,13 @@ class InternalTests(unittest.TestCase): + def test_cmake_skip_compiler_test_invalid(self): + properties = mesonbuild.envconfig.Properties({'cmake_skip_compiler_test': True}) + with self.assertRaisesRegex( + EnvironmentException, + '"True" is not a valid value for cmake_skip_compiler_test'): + properties.get_cmake_skip_compiler_test() + def test_machine_info_is_ohos(self): def machine(system: str, subsystem: str) -> mesonbuild.envconfig.MachineInfo: return mesonbuild.envconfig.MachineInfo( From 94c02552deb32e4bcf9679af38a190203b315bd1 Mon Sep 17 00:00:00 2001 From: mayank-dev-15 Date: Tue, 23 Jun 2026 20:37:14 +0530 Subject: [PATCH 634/681] mtest: warn on TAP unknown lines instead of erroring The TAP spec says unknown lines must not be treated as errors. Meson was marking them as ERROR for version <= 13. The test still passed if all actual test results are OK. Now they are warnings, matching the spec. Fixes #6810 [@bonzini: while at it, make the formatting the same as mlog] (cherry picked from commit 936be4e24e8bd5e98e983479488cdd189a78aa17) --- mesonbuild/mtest.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index 0661fa529d29..8b96ff0c69db 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -1179,15 +1179,15 @@ async def parse(self, harness: 'TestHarness', lines: T.AsyncIterator[str]) -> No res = TestResult.ERROR if warnings: - unknown = str(mlog.yellow('UNKNOWN')) + unknown = str(mlog.yellow('UNKNOWN:')) width = len(str(max(i.lineno for i in warnings))) for w in warnings: - self.warnings.append(f'stdout: {w.lineno:{width}}: {unknown}: {w.message}') + self.warnings.append(f'stdout: {w.lineno:{width}}: {unknown} {w.message}') if version > 13: self.warnings.append('Unknown TAP output lines have been ignored. Please open a feature request to\n' 'implement them, or prefix them with a # if they are not TAP syntax.') else: - self.warnings.append(str(mlog.red('ERROR')) + ': Unknown TAP output lines for a supported TAP version.\n' + self.warnings.append(str(mlog.yellow('WARNING:')) + ' Unknown TAP output lines for a supported TAP version.\n' 'This is probably a bug in the test; if they are not TAP syntax, prefix them with a #') if all(t.result is TestResult.SKIP for t in self.results): # This includes the case where self.results is empty From e9f87084c6919d48a7b495fc115b9c545ce4367f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 6 Aug 2026 09:11:20 +0200 Subject: [PATCH 635/681] introspection: drop dependencies with unknown name from output Fixes: #16046 Signed-off-by: Paolo Bonzini (cherry picked from commit 270de1272dccf633ab5e1936ed7d00510f595662) --- mesonbuild/ast/introspection.py | 3 ++- .../unit/139 introspection unknown dep/meson.build | 6 ++++++ .../139 introspection unknown dep/meson.options | 1 + unittests/allplatformstests.py | 14 -------------- unittests/platformagnostictests.py | 6 ++++++ 5 files changed, 15 insertions(+), 15 deletions(-) create mode 100644 test cases/unit/139 introspection unknown dep/meson.build create mode 100644 test cases/unit/139 introspection unknown dep/meson.options diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py index 1802f8e1bb6b..f4960231fbfa 100644 --- a/mesonbuild/ast/introspection.py +++ b/mesonbuild/ast/introspection.py @@ -218,7 +218,8 @@ def func_dependency(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict has_fallback=has_fallback, conditional=node.condition_level > 0, node=node) - self.dependencies += [newdep] + if not isinstance(name, UnknownValue): + self.dependencies += [newdep] return newdep def build_target(self, node: BaseNode, args: T.List[TYPE_var], kwargs_raw: T.Dict[str, TYPE_var], targetclass: T.Type[BuildTarget]) -> T.Union[IntrospectionBuildTarget, UnknownValue]: diff --git a/test cases/unit/139 introspection unknown dep/meson.build b/test cases/unit/139 introspection unknown dep/meson.build new file mode 100644 index 000000000000..d342bb68ae5d --- /dev/null +++ b/test cases/unit/139 introspection unknown dep/meson.build @@ -0,0 +1,6 @@ +project('t', 'c') +logind = get_option('logind') +enable_logind = (logind != 'none') +if enable_logind + logind_dep = dependency(logind, version: '>= 209') +endif diff --git a/test cases/unit/139 introspection unknown dep/meson.options b/test cases/unit/139 introspection unknown dep/meson.options new file mode 100644 index 000000000000..ec18c89f5568 --- /dev/null +++ b/test cases/unit/139 introspection unknown dep/meson.options @@ -0,0 +1 @@ +option('logind', type: 'string') diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 7af2f0ffed64..7bfe5951ff40 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -4139,20 +4139,6 @@ def test_introspect_dependencies_from_source(self): 'has_fallback': True, 'conditional': True }, - { - 'conditional': False, - 'has_fallback': False, - 'name': 'unknown', - 'required': False, - 'version': 'unknown' - }, - { - 'conditional': False, - 'has_fallback': False, - 'name': 'unknown', - 'required': False, - 'version': 'unknown' - }, ] self.maxDiff = None self.assertListEqual(res_nb, expected) diff --git a/unittests/platformagnostictests.py b/unittests/platformagnostictests.py index fe565cffb851..7e232af3dc0c 100644 --- a/unittests/platformagnostictests.py +++ b/unittests/platformagnostictests.py @@ -589,6 +589,12 @@ def test_buildtype_debug_optimization_override(self) -> None: self.assertEqual(self.getconf('debug', opts), True) self.assertEqual(self.getconf('optimization', opts), '3') + def test_introspect_dependency_unknown_name(self): + testdir = os.path.join(self.unit_test_dir, '139 introspection unknown dep') + testfile = os.path.join(testdir, 'meson.build') + res = self.introspect_directory(testfile, ['--dependencies'] + self.meson_args) + self.assertListEqual(res, []) + def test_setup_mixed_long_short_options(self) -> None: """Mixing unity and unity_size as long and short options should work.""" testdir = self.copy_srcdir(os.path.join(self.common_test_dir, '1 trivial')) From e4c1e78fae87bc061f3195979795eeb494b2c9ba Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 10 Aug 2026 11:01:04 +0200 Subject: [PATCH 636/681] wrap, mdist: require a resolver for get_directory This makes it possible to retrieve parameters such as the directory from sources other than wrap files. Signed-off-by: Paolo Bonzini (cherry picked from commit 5d5dbd355e4dfcc76fe305abcdbe742e506d3fde) --- mesonbuild/mdist.py | 4 ++-- mesonbuild/wrap/wrap.py | 11 ++++------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/mesonbuild/mdist.py b/mesonbuild/mdist.py index 72ef1731b87a..6ab7f8b4efdf 100644 --- a/mesonbuild/mdist.py +++ b/mesonbuild/mdist.py @@ -397,10 +397,10 @@ def run(options: argparse.Namespace) -> int: subprojects: T.Dict[SubProject, str] = {} extra_meson_args = [] if options.include_subprojects: - subproject_dir = os.path.join(src_root, b.subproject_dir) + resolver = wrap.Resolver(src_root, b.subproject_dir, silent=True) for sub in set(itertools.chain(b.projects.host, b.projects.build)): if sub: - directory = wrap.get_directory(subproject_dir, sub) + directory = resolver.get_directory(sub) subprojects[sub] = os.path.join(b.subproject_dir, directory) extra_meson_args.append('-Dwrap_mode=nodownload') diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py index e35aa4ce0a84..9abc4c057b15 100644 --- a/mesonbuild/wrap/wrap.py +++ b/mesonbuild/wrap/wrap.py @@ -346,13 +346,6 @@ def update_hash_cache(self, subproject_directory: str) -> None: def add_provided_dep(self, name: str) -> None: self.provided_deps[name] = None -def get_directory(subdir_root: str, packagename: str) -> str: - fname = os.path.join(subdir_root, packagename + '.wrap') - if os.path.isfile(fname): - wrap = PackageDefinition.from_wrap_file(fname) - return wrap.directory - return packagename - def verbose_git(cmd: T.List[str], workingdir: str, check: bool = False) -> bool: ''' Wrapper to convert GitException to WrapException caught in interpreter. @@ -487,6 +480,10 @@ def load_and_merge(self, subdir: str, subproject: SubProject) -> None: self.merge_wraps(other_resolver.wraps) self.loaded_dirs.add(subdir) + def get_directory(self, packagename: str) -> str: + wrap = self.wraps.get(packagename) + return wrap.directory if wrap else packagename + def find_dep_provider(self, packagename: str) -> T.Tuple[T.Optional[str], T.Optional[str]]: # Python's ini parser converts all key values to lowercase. # Thus the query name must also be in lower case. From 0700e6bcc6925e697de28cdcc36d59cb99ebe6d9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 10 Aug 2026 11:10:18 +0200 Subject: [PATCH 637/681] wrap: load Cargo.lock when Resolver is created Enable the usage of commands like `meson subprojects purge`, as well as of `Resolver.get_directory` for `meson dist --include-subprojects`, even for wraps that are declared via Cargo.lock. Signed-off-by: Paolo Bonzini (cherry picked from commit 13f30e7c85e4f590414a5ef359513b6bfa0a291d) --- mesonbuild/cargo/interpreter.py | 4 +--- mesonbuild/interpreter/interpreter.py | 7 +++--- mesonbuild/wrap/wrap.py | 34 +++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index ddfc664074fd..ffe57def305d 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -301,10 +301,8 @@ def __init__(self, env: Environment, subdir: str, subprojects_dir: str) -> None: self.build_def_files: T.List[str] = [] # Cargo packages filename = os.path.join(self.environment.get_source_dir(), subdir, 'Cargo.lock') - subprojects_dir = os.path.join(self.environment.get_source_dir(), subdir, subprojects_dir) - self.cargolock = load_cargo_lock(filename, subprojects_dir) + self.cargolock = self.environment.wrap_resolver.get_cargo_lock(subdir) if self.cargolock: - self.environment.wrap_resolver.merge_wraps(self.cargolock.wraps) self.build_def_files.append(filename) @property diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 1ffc34ff475d..90a2f101b043 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -335,9 +335,10 @@ def load_root_cargo_lock_file(self) -> None: from .. import cargo try: self.cargo = cargo.Interpreter(self.environment, self.subdir, self.subproject_dir) - except cargo.TomlImplementationMissing as e: - # error delayed to actual usage of a Cargo subproject - mlog.warning(f'cannot load Cargo.lock: {e}', fatal=False) + except cargo.TomlImplementationMissing: + # Error delayed to actual usage of a Cargo subproject. The warning + # has already been printed by Resolver.load_wraps(). + pass def _redetect_machines(self) -> None: # Re-initialize machine descriptions. We can do a better job now because we diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py index 9abc4c057b15..c2f709041029 100644 --- a/mesonbuild/wrap/wrap.py +++ b/mesonbuild/wrap/wrap.py @@ -41,6 +41,8 @@ import http.client from typing_extensions import Literal + from ..cargo.manifest import CargoLock + Method = Literal['meson', 'cmake', 'cargo'] try: @@ -367,6 +369,7 @@ class Resolver: def __post_init__(self) -> None: self.subdir_root = os.path.join(self.source_dir, self.subdir) + self.project_subdir = os.path.relpath(os.path.dirname(self.subdir_root), self.source_dir) self.cachedir = os.environ.get('MESON_PACKAGE_CACHE_DIR') or os.path.join(self.subdir_root, 'packagecache') self.wraps: T.Dict[str, PackageDefinition] = {} self.netrc: T.Optional[netrc] = None @@ -376,6 +379,7 @@ def __post_init__(self) -> None: self.wrapdb_provided_deps: T.Dict[str, str] = {} self.wrapdb_provided_programs: T.Dict[str, SubProject] = {} self.loaded_dirs: T.Set[str] = set() + self.cargolocks: T.Dict[str, T.Optional[CargoLock]] = {} self.load_wraps() self.load_netrc() self.load_wrapdb() @@ -411,8 +415,37 @@ def load_wraps(self) -> None: # Add provided deps and programs into our lookup tables for wrap in self.wraps.values(): self.add_wrap(wrap) + self.get_cargo_lock(self.project_subdir, warn_only=True) self.loaded_dirs.add(self.subdir) + def get_cargo_lock(self, project_subdir: str, warn_only: bool = False) -> T.Optional[CargoLock]: + project_subdir = os.path.normpath(os.path.join(self.source_dir, project_subdir)) + filename = os.path.join(project_subdir, 'Cargo.lock') + if filename in self.cargolocks: + return self.cargolocks[filename] + + if not os.path.exists(filename): + self.cargolocks[filename] = None + return None + + from ..cargo.interpreter import load_cargo_lock + from ..cargo.toml import TomlImplementationMissing + subdir_root = os.path.join(project_subdir, os.path.basename(self.subdir)) + try: + cargolock = load_cargo_lock(filename, subdir_root) + except TomlImplementationMissing as e: + if not warn_only: + raise + # Error delayed to the actual usage of a Cargo subproject; do not + # cache the failure, so that it can be reported properly then. + mlog.warning(f'cannot load Cargo.lock: {e}', fatal=False, once=True) + return None + + self.cargolocks[filename] = cargolock + if cargolock: + self.merge_wraps(cargolock.wraps) + return cargolock + def add_wrap(self, wrap: PackageDefinition, ignore_dups: bool = False) -> None: for k in wrap.provided_deps.keys(): if k not in self.provided_deps: @@ -478,6 +511,7 @@ def load_and_merge(self, subdir: str, subproject: SubProject) -> None: if self.wrap_mode != WrapMode.nopromote and subdir not in self.loaded_dirs: other_resolver = Resolver(self.source_dir, subdir, subproject, self.wrap_mode, self.wrap_frontend, self.allow_insecure, self.silent) self.merge_wraps(other_resolver.wraps) + self.cargolocks.update(other_resolver.cargolocks) self.loaded_dirs.add(subdir) def get_directory(self, packagename: str) -> str: From c3a585657ac49ed34108a28d47d413336bb92ff5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 10 Aug 2026 11:16:41 +0200 Subject: [PATCH 638/681] unittests: add test for --include-subprojects with Cargo.lock Signed-off-by: Paolo Bonzini (cherry picked from commit 7f59d7de274ba6af9a80871e32e836c131881622) --- unittests/allplatformstests.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 7bfe5951ff40..e951e6c41112 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -1655,6 +1655,26 @@ def test_dist_git_script(self): # fails sometimes. pass + @skipIfNoExecutable('git') + @skip_if_not_language('rust') + def test_dist_subprojects_cargo(self): + if self.backend is not Backend.ninja: + raise SkipTest('Dist is only supported with Ninja') + + try: + with tempfile.TemporaryDirectory() as tmpdir: + project_dir = os.path.join(tmpdir, 'a') + shutil.copytree(os.path.join(self.rust_test_dir, '25 cargo lock'), + project_dir) + git_init(project_dir) + self.init(project_dir) + self._run(self.meson_command + ['dist', '--include-subprojects'], workdir=self.builddir) + except PermissionError: + # When run under Windows CI, something (virus scanner?) + # holds on to the git files so cleaning up the dir + # fails sometimes. + pass + def create_dummy_subproject(self, project_dir, name): path = os.path.join(project_dir, 'subprojects', name) os.makedirs(path) From 5cdd1b4ca4c55bac1ce2d9f26e84b711a42228c6 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 11 Aug 2026 11:16:41 +0200 Subject: [PATCH 639/681] cargo: remove Optional from load_cargo_lock return value Signed-off-by: Paolo Bonzini (cherry picked from commit a7b2a14044f2a9f2e2360c92516fec95175083ec) --- mesonbuild/cargo/interpreter.py | 92 ++++++++++++++++----------------- mesonbuild/wrap/wrap.py | 3 +- 2 files changed, 46 insertions(+), 49 deletions(-) diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index ffe57def305d..f8d2a1fd3ec3 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -849,55 +849,53 @@ def _parse_git_url(url: str, branch: T.Optional[str] = None) -> T.Tuple[str, str return url, revision, directory -def load_cargo_lock(filename: str, subproject_dir: str) -> T.Optional[CargoLock]: +def load_cargo_lock(filename: str, subproject_dir: str) -> CargoLock: """ Convert Cargo.lock into a list of wraps """ # Map directory -> PackageDefinition, to avoid duplicates. Multiple packages # can have the same source URL, in that case we have a single wrap that # provides multiple dependency names. - if os.path.exists(filename): - toml = load_toml(filename) - raw_cargolock = T.cast('raw.CargoLock', toml) - cargolock = CargoLock.from_raw(raw_cargolock) - packagefiles_dir = os.path.join(subproject_dir, 'packagefiles') - wraps: T.Dict[str, PackageDefinition] = {} - for package in cargolock.package: - meson_depname = _dependency_name(package.name, version.api(package.version)) - if package.source is None: - # This is project's package, or one of its workspace members. - continue - elif package.source == 'registry+https://github.com/rust-lang/crates.io-index': - checksum = package.checksum - if checksum is None: - checksum = cargolock.metadata[f'checksum {package.name} {package.version} ({package.source})'] - url = f'https://static.crates.io/crates/{package.name}/{package.version}/download' - directory = f'{package.name}-{package.version}' - name = SubProject(meson_depname) - wrap_type = 'file' - cfg = { - 'directory': directory, - 'source_url': url, - 'source_filename': f'{directory}.tar.gz', - 'source_hash': checksum, - 'method': 'cargo', - } - elif package.source.startswith('git+'): - url, revision, directory = _parse_git_url(package.source) - name = SubProject(directory) - wrap_type = 'git' - cfg = { - 'url': url, - 'revision': revision, - 'method': 'cargo', - } - else: - mlog.warning(f'Unsupported source URL in {filename}: {package.source}') - continue - if os.path.isdir(os.path.join(packagefiles_dir, name)): - cfg['patch_directory'] = name - if directory not in wraps: - wraps[directory] = PackageDefinition.from_values(name, subproject_dir, wrap_type, cfg) - wraps[directory].add_provided_dep(meson_depname) - cargolock.wraps = {w.name: w for w in wraps.values()} - return cargolock - return None + toml = load_toml(filename) + raw_cargolock = T.cast('raw.CargoLock', toml) + cargolock = CargoLock.from_raw(raw_cargolock) + packagefiles_dir = os.path.join(subproject_dir, 'packagefiles') + wraps: T.Dict[str, PackageDefinition] = {} + for package in cargolock.package: + meson_depname = _dependency_name(package.name, version.api(package.version)) + if package.source is None: + # This is project's package, or one of its workspace members. + continue + elif package.source == 'registry+https://github.com/rust-lang/crates.io-index': + checksum = package.checksum + if checksum is None: + checksum = cargolock.metadata[f'checksum {package.name} {package.version} ({package.source})'] + url = f'https://static.crates.io/crates/{package.name}/{package.version}/download' + directory = f'{package.name}-{package.version}' + name = SubProject(meson_depname) + wrap_type = 'file' + cfg = { + 'directory': directory, + 'source_url': url, + 'source_filename': f'{directory}.tar.gz', + 'source_hash': checksum, + 'method': 'cargo', + } + elif package.source.startswith('git+'): + url, revision, directory = _parse_git_url(package.source) + name = SubProject(directory) + wrap_type = 'git' + cfg = { + 'url': url, + 'revision': revision, + 'method': 'cargo', + } + else: + mlog.warning(f'Unsupported source URL in {filename}: {package.source}') + continue + if os.path.isdir(os.path.join(packagefiles_dir, name)): + cfg['patch_directory'] = name + if directory not in wraps: + wraps[directory] = PackageDefinition.from_values(name, subproject_dir, wrap_type, cfg) + wraps[directory].add_provided_dep(meson_depname) + cargolock.wraps = {w.name: w for w in wraps.values()} + return cargolock diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py index c2f709041029..3281aa98c758 100644 --- a/mesonbuild/wrap/wrap.py +++ b/mesonbuild/wrap/wrap.py @@ -442,8 +442,7 @@ def get_cargo_lock(self, project_subdir: str, warn_only: bool = False) -> T.Opti return None self.cargolocks[filename] = cargolock - if cargolock: - self.merge_wraps(cargolock.wraps) + self.merge_wraps(cargolock.wraps) return cargolock def add_wrap(self, wrap: PackageDefinition, ignore_dups: bool = False) -> None: From aec31df80dda693b2c5bf5478da9095e6fa8f01c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 11 Aug 2026 11:17:10 +0200 Subject: [PATCH 640/681] Add enum WrapType We were using raw strings, which don't lend themselves to type safety. Signed-off-by: Tristan Partin (cherry picked from commit 097b497d24a51f5311f0297b4c1ca0fd2026dd25) --- mesonbuild/cargo/interpreter.py | 6 +++--- mesonbuild/msubprojects.py | 6 +++--- mesonbuild/wrap/wrap.py | 31 +++++++++++++++++++------------ 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index f8d2a1fd3ec3..7b33dcc60c7c 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -32,7 +32,7 @@ PerMachine, unique_list, SubProject, ) from .. import coredata, mlog -from ..wrap.wrap import PackageDefinition +from ..wrap.wrap import PackageDefinition, WrapType if T.TYPE_CHECKING: from . import raw @@ -872,7 +872,7 @@ def load_cargo_lock(filename: str, subproject_dir: str) -> CargoLock: url = f'https://static.crates.io/crates/{package.name}/{package.version}/download' directory = f'{package.name}-{package.version}' name = SubProject(meson_depname) - wrap_type = 'file' + wrap_type = WrapType.FILE cfg = { 'directory': directory, 'source_url': url, @@ -883,7 +883,7 @@ def load_cargo_lock(filename: str, subproject_dir: str) -> CargoLock: elif package.source.startswith('git+'): url, revision, directory = _parse_git_url(package.source) name = SubProject(directory) - wrap_type = 'git' + wrap_type = WrapType.GIT cfg = { 'url': url, 'revision': revision, diff --git a/mesonbuild/msubprojects.py b/mesonbuild/msubprojects.py index f4b4405634b2..9db9587a21ee 100755 --- a/mesonbuild/msubprojects.py +++ b/mesonbuild/msubprojects.py @@ -17,7 +17,7 @@ from . import mlog from .ast import IntrospectionInterpreter from .mesonlib import quiet_git, GitException, Popen_safe, MesonException, windows_proof_rmtree -from .wrap.wrap import (Resolver, WrapException, ALL_TYPES, +from .wrap.wrap import (Resolver, WrapException, WrapType, parse_patch_url, update_wrap_file, get_releases) if T.TYPE_CHECKING: @@ -59,7 +59,7 @@ class PackagefilesArguments(Arguments): apply: bool save: bool -ALL_TYPES_STRING = ', '.join(ALL_TYPES) +ALL_TYPES_STRING = ', '.join(WrapType) if sys.version_info >= (3, 14): tarfile.TarFile.extraction_filter = staticmethod(tarfile.fully_trusted_filter) @@ -739,7 +739,7 @@ def run(options: 'Arguments') -> int: wraps = list(r.wraps.values()) types = [t.strip() for t in options.types.split(',')] if options.types else [] for t in types: - if t not in ALL_TYPES: + if t not in tuple(WrapType): raise MesonException(f'Unknown subproject type {t!r}, supported types are: {ALL_TYPES_STRING}') tasks: T.List[T.Awaitable[bool]] = [] task_names: T.List[str] = [] diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py index 3281aa98c758..77c1e8f12728 100644 --- a/mesonbuild/wrap/wrap.py +++ b/mesonbuild/wrap/wrap.py @@ -24,6 +24,7 @@ import gzip from base64 import b64encode +from enum import Enum from netrc import netrc from pathlib import Path, PurePath from functools import lru_cache @@ -56,8 +57,6 @@ REQ_TIMEOUT = 30.0 WHITELIST_SUBDOMAIN = 'wrapdb.mesonbuild.com' -ALL_TYPES = ['file', 'git', 'hg', 'svn', 'redirect'] - if sys.version_info >= (3, 14): import tarfile tarfile.TarFile.extraction_filter = staticmethod(tarfile.fully_trusted_filter) @@ -191,6 +190,14 @@ def parse_patch_url(patch_url: str) -> T.Tuple[str, str]: raise WrapException(f'Invalid wrapdb URL {patch_url}') +class WrapType(str, Enum): + FILE = 'file' + GIT = 'git' + HG = 'hg' + SVN = 'svn' + REDIRECT = 'redirect' + + class WrapException(MesonException): pass @@ -198,7 +205,7 @@ class WrapNotFoundException(WrapException): pass class PackageDefinition: - def __init__(self, name: SubProject, subprojects_dir: str, type_: T.Optional[str] = None, values: T.Optional[T.Dict[str, str]] = None): + def __init__(self, name: SubProject, subprojects_dir: str, type_: T.Optional[WrapType] = None, values: T.Optional[T.Dict[str, str]] = None): self.name = name self.subprojects_dir = subprojects_dir self.type = type_ @@ -225,7 +232,7 @@ def __init__(self, name: SubProject, subprojects_dir: str, type_: T.Optional[str self.provided_deps[self.name.lower()] = None @staticmethod - def from_values(name: SubProject, subprojects_dir: str, type_: str, values: T.Dict[str, str]) -> PackageDefinition: + def from_values(name: SubProject, subprojects_dir: str, type_: WrapType, values: T.Dict[str, str]) -> PackageDefinition: return PackageDefinition(name, subprojects_dir, type_, values) @staticmethod @@ -247,7 +254,7 @@ def from_wrap_file(filename: str, subproject: SubProject = mesonlib.ROOT_SUBPROJ subprojects_dir = os.path.dirname(filename) - if type_ == 'redirect': + if type_ is WrapType.REDIRECT: # [wrap-redirect] have a `filename` value pointing to the real wrap # file we should parse instead. It must be relative to the current # wrap file location and must be in the form foo/subprojects/bar.wrap. @@ -289,7 +296,7 @@ def from_wrap_file(filename: str, subproject: SubProject = mesonlib.ROOT_SUBPROJ return wrap @staticmethod - def _parse_wrap(filename: str) -> T.Tuple[configparser.ConfigParser, str, T.Dict[str, str]]: + def _parse_wrap(filename: str) -> T.Tuple[configparser.ConfigParser, WrapType, T.Dict[str, str]]: try: config = configparser.ConfigParser(interpolation=None) config.read(filename, encoding='utf-8') @@ -301,10 +308,10 @@ def _parse_wrap(filename: str) -> T.Tuple[configparser.ConfigParser, str, T.Dict if not wrap_section.startswith('wrap-'): raise WrapException(f'{wrap_section!r} is not a valid first section in {filename}') type_ = wrap_section[5:] - if type_ not in ALL_TYPES: + if type_ not in tuple(WrapType): raise WrapException(f'Unknown wrap type {type_!r}') values = dict(config[wrap_section]) - return config, type_, values + return config, WrapType(type_), values def parse_provide_section(self, config: configparser.ConfigParser) -> None: if config.has_section('provides'): @@ -620,15 +627,15 @@ def has_buildfile() -> bool: cached_directory = os.path.join(self.cachedir, self.directory) if os.path.isdir(cached_directory): self.copy_tree(cached_directory, self.dirname) - elif self.wrap.type == 'file': + elif self.wrap.type is WrapType.FILE: self._get_file(packagename) else: self.check_can_download() - if self.wrap.type == 'git': + if self.wrap.type is WrapType.GIT: self._get_git(packagename) - elif self.wrap.type == "hg": + elif self.wrap.type is WrapType.HG: self._get_hg() - elif self.wrap.type == "svn": + elif self.wrap.type is WrapType.SVN: self._get_svn() else: raise WrapException(f'Unknown wrap type {self.wrap.type!r}') From 1ebf9db5ae9fc74721c02fe09f52af3c9725329f Mon Sep 17 00:00:00 2001 From: Tristan Partin Date: Tue, 11 Aug 2026 09:26:47 +0200 Subject: [PATCH 641/681] Fix meson dist --include-subprojects with redirected wraps When redirected wraps were used, mdist was searching for the source directory in the subprojects directory of the root project. However, the source for redirected wraps is actually located in some child subprojects directory. Subsequently, this resulted in a FileNotFoundError. By skipping redirected wraps as we collect subprojects, we can effectively prevent the issue from occuring because the authoritative subproject will be picked up and included in the dist. Fixes: https://github.com/mesonbuild/meson/issues/12489 Signed-off-by: Tristan Partin (cherry picked from commit 66ab168e6fc118f8a429d4817296671855e68f4d) --- mesonbuild/mdist.py | 7 ++- mesonbuild/wrap/wrap.py | 6 ++- unittests/allplatformstests.py | 82 ++++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 4 deletions(-) diff --git a/mesonbuild/mdist.py b/mesonbuild/mdist.py index 6ab7f8b4efdf..2f4fead6fecd 100644 --- a/mesonbuild/mdist.py +++ b/mesonbuild/mdist.py @@ -399,8 +399,11 @@ def run(options: argparse.Namespace) -> int: if options.include_subprojects: resolver = wrap.Resolver(src_root, b.subproject_dir, silent=True) for sub in set(itertools.chain(b.projects.host, b.projects.build)): - if sub: - directory = resolver.get_directory(sub) + if not sub: + continue + + directory = resolver.get_directory(sub) + if directory is not None: subprojects[sub] = os.path.join(b.subproject_dir, directory) extra_meson_args.append('-Dwrap_mode=nodownload') diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py index 77c1e8f12728..51e962d4fc26 100644 --- a/mesonbuild/wrap/wrap.py +++ b/mesonbuild/wrap/wrap.py @@ -520,9 +520,11 @@ def load_and_merge(self, subdir: str, subproject: SubProject) -> None: self.cargolocks.update(other_resolver.cargolocks) self.loaded_dirs.add(subdir) - def get_directory(self, packagename: str) -> str: + def get_directory(self, packagename: str) -> T.Optional[str]: wrap = self.wraps.get(packagename) - return wrap.directory if wrap else packagename + if wrap: + return None if wrap.redirected else wrap.directory + return packagename def find_dep_provider(self, packagename: str) -> T.Tuple[T.Optional[str], T.Optional[str]]: # Python's ini parser converts all key values to lowercase. diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index e951e6c41112..ab9e66242c7a 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -1675,6 +1675,88 @@ def test_dist_subprojects_cargo(self): # fails sometimes. pass + @skipIfNoExecutable('git') + def test_dist_nested_promoted_subproject(self): + ''' + https://github.com/mesonbuild/meson/issues/12489 + + A subproject (e.g. glib) can itself depend on a nested subproject + (e.g. gvdb) whose sources are bundled inside the outer subproject's + own "subprojects" directory rather than the main project's. Meson + promotes the wrap file for such a nested subproject to the main + project's subprojects directory (writing a [wrap-redirect] wrap + there), but the actual source directory stays nested. `meson dist + --include-subprojects` used to look for the nested subproject + directly under the main project's subprojects directory (following + the promoted wrap file naively) and crashed with a FileNotFoundError + because that directory did not exist there. + ''' + if self.backend is not Backend.ninja: + raise SkipTest('Dist is only supported with Ninja') + + with tempfile.TemporaryDirectory() as project_dir: + with open(os.path.join(project_dir, 'meson.build'), 'w', encoding='utf-8') as f: + f.write(textwrap.dedent('''\ + project('gtk', version : '1.0') + subproject('glib') + ''')) + + glib_dir = os.path.join(project_dir, 'subprojects', 'glib') + gvdb_dir = os.path.join(glib_dir, 'subprojects', 'gvdb') + os.makedirs(gvdb_dir) + + # glib is a normal subproject with its own wrap file + with open(os.path.join(project_dir, 'subprojects', 'glib.wrap'), 'w', encoding='utf-8') as f: + f.write(textwrap.dedent('''\ + [wrap-file] + directory = glib + ''')) + + # Only the main project and glib's wrap file are tracked by git; + # glib itself is *not* a git repository, mimicking a subproject + # fetched/extracted from a tarball + git_init(project_dir) + + with open(os.path.join(glib_dir, 'meson.build'), 'w', encoding='utf-8') as f: + f.write(textwrap.dedent('''\ + project('glib', version : '1.0') + subproject('gvdb') + ''')) + + # gvdb is bundled inside glib's own subprojects directory, with + # its sources already extracted there + with open(os.path.join(glib_dir, 'subprojects', 'gvdb.wrap'), 'w', encoding='utf-8') as f: + f.write(textwrap.dedent('''\ + [wrap-file] + directory = gvdb + ''')) + with open(os.path.join(gvdb_dir, 'meson.build'), 'w', encoding='utf-8') as f: + f.write(textwrap.dedent('''\ + project('gvdb', version : '1.0') + ''')) + + self.init(project_dir) + # Configuring writes the promoted [wrap-redirect] wrap file + redirect_wrap = os.path.join(project_dir, 'subprojects', 'gvdb.wrap') + self.assertPathExists(redirect_wrap) + with open(redirect_wrap, encoding='utf-8') as f: + self.assertIn('[wrap-redirect]', f.read()) + + self._run(self.meson_command + ['dist', '--include-subprojects', '--no-tests', + '--formats', 'zip'], + workdir=self.builddir) + + zip_distfile = os.path.join(self.distdir, 'gtk-1.0.zip') + self.assertPathExists(zip_distfile) + z = zipfile.ZipFile(zip_distfile) + names = set(z.namelist()) + # gvdb must end up nested inside glib, where it really lives... + self.assertIn('gtk-1.0/subprojects/glib/subprojects/gvdb/meson.build', names) + # ... and must not be duplicated (or looked up) directly under the + # main project's subprojects directory. + self.assertNotIn('gtk-1.0/subprojects/gvdb/meson.build', names) + self.assertNotIn('gtk-1.0/subprojects/gvdb.wrap', names) + def create_dummy_subproject(self, project_dir, name): path = os.path.join(project_dir, 'subprojects', name) os.makedirs(path) From c54d67c7be7c75419e93c2948fd0ac9df879648f Mon Sep 17 00:00:00 2001 From: moi15moi Date: Tue, 4 Aug 2026 19:33:32 -0400 Subject: [PATCH 642/681] baseplatformtests: Don't specify "/" in os.path.join This is needed because otherwise, if while MSYSTEM= python run_unittests.py PlatformAgnosticTests.test_error_configuring_subdir in msys2, I was getting this error: ``` $ MSYSTEM= python run_unittests.py PlatformAgnosticTests.test_error_configuring_subdir Meson build system 1.12.0.rc2 Unit Tests pytest not found, using unittest instead F Stdout: $ C:\msys64\mingw64\bin\python.exe C:\Users\moi15moi\Documents\GitHub\meson\meson.py setup --backend=ninja --prefix /usr --libdir lib C:\Users\moi15moi\Documents\GitHub\meson\tmp2judbrrm "C:\Users\moi15moi\Documents\GitHub\meson\test cases/common\152 index customtarget\subdir" stdout: The Meson build system Version: 1.12.0.rc2 Source dir: C:\Users\moi15moi\Documents\GitHub\meson\test cases\common\152 index customtarget\subdir Build dir: C:\Users\moi15moi\Documents\GitHub\meson\tmp2judbrrm Build type: native build ERROR: Not the project root: first statement must be a call to project() Did you mean to run meson from the directory: "C:\Users\moi15moi\Documents\GitHub\meson\test cases\common\152 index customtarget"? A full log can be found at C:\Users\moi15moi\Documents\GitHub\meson\tmp2judbrrm\meson-logs\meson-log.txt ====================================================================== FAIL: test_error_configuring_subdir (unittests.platformagnostictests.PlatformAgnosticTests.test_error_configuring_subdir) ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\Users\moi15moi\Documents\GitHub\meson\unittests\platformagnostictests.py", line 422, in test_error_configuring_subdir self.assertIn(f'Did you mean to run meson from the directory: "{testdir}"?', out) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ AssertionError: 'Did you mean to run meson from the directory: "C:\\Users\\moi15moi\\Documents\\GitHub\\meson\\test cases/common\\152 index customtarget"?' not found in 'Build started at 2026-08-04T19:25:58.484065\nMain binary: C:\\msys64\\mingw64\\bin\\python.exe\nBuild Options: -Dbackend=ninja -Dprefix=/usr -Dlibdir=lib\nPython system: Window s\nThe Meson build system\nVersion: 1.12.0.rc2\nSource dir: C:\\Users\\moi15moi\\Documents\\GitHub\\meson\\test cases\\common\\152 index customtarget\\subdir\nBuild dir: C:\\Users\\moi15moi\\Documents\\GitHub\\meson\\tmp2judbrrm\nBuild type: native build\n\nERROR: Not the project root: first statement must be a call to project()\n\nDid you mean to run meson from the directory: "C:\\Users\\moi15moi\\Documents\\GitHub\\meson\\test cases\\common\\152 index customtarget"?\n' Stdout: $ C:\msys64\mingw64\bin\python.exe C:\Users\moi15moi\Documents\GitHub\meson\meson.py setup --backend=ninja --prefix /usr --libdir lib C:\Users\moi15moi\Documents\GitHub\meson\tmp2judbrrm "C:\Users\moi15moi\Documents\GitHub\meson\test cases/common\152 index customtarget\subdir" stdout: The Meson build system Version: 1.12.0.rc2 Source dir: C:\Users\moi15moi\Documents\GitHub\meson\test cases\common\152 index customtarget\subdir Build dir: C:\Users\moi15moi\Documents\GitHub\meson\tmp2judbrrm Build type: native build ERROR: Not the project root: first statement must be a call to project() Did you mean to run meson from the directory: "C:\Users\moi15moi\Documents\GitHub\meson\test cases\common\152 index customtarget"? A full log can be found at C:\Users\moi15moi\Documents\GitHub\meson\tmp2judbrrm\meson-logs\meson-log.txt ---------------------------------------------------------------------- Ran 1 test in 0.479s FAILED (failures=1) Total time: 0.787 seconds ``` The msys2 CI doesn't catch this issue currently because PlatformAgnosticTests are skipped (https://github.com/mesonbuild/meson/blob/f004d10e758073a5aa15b195e7047f5c662f5581/unittests/platformagnostictests.py#L25) Part-of: https://github.com/mesonbuild/meson/pull/16073 Closes: https://github.com/mesonbuild/meson/pull/16073 Signed-off-by: Sam James (cherry picked from commit ee72484e8f594a709e20e4ca06a9246c36862b32) --- unittests/baseplatformtests.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/unittests/baseplatformtests.py b/unittests/baseplatformtests.py index f00384baa2e5..f431536035f0 100644 --- a/unittests/baseplatformtests.py +++ b/unittests/baseplatformtests.py @@ -83,18 +83,18 @@ def setUpClass(cls) -> None: cls.build_command, cls.clean_command, cls.test_command, cls.install_command, \ cls.uninstall_command = get_backend_commands(cls.backend) # Test directories - cls.common_test_dir = os.path.join(src_root, 'test cases/common') - cls.python_test_dir = os.path.join(src_root, 'test cases/python') - cls.rust_test_dir = os.path.join(src_root, 'test cases/rust') - cls.vala_test_dir = os.path.join(src_root, 'test cases/vala') - cls.framework_test_dir = os.path.join(src_root, 'test cases/frameworks') - cls.unit_test_dir = os.path.join(src_root, 'test cases/unit') - cls.rewrite_test_dir = os.path.join(src_root, 'test cases/rewrite') - cls.linuxlike_test_dir = os.path.join(src_root, 'test cases/linuxlike') - cls.objc_test_dir = os.path.join(src_root, 'test cases/objc') - cls.objcpp_test_dir = os.path.join(src_root, 'test cases/objcpp') - cls.darwin_test_dir = os.path.join(src_root, 'test cases/darwin') - cls.fortran_test_dir = os.path.join(src_root, 'test cases/fortran') + cls.common_test_dir = os.path.join(src_root, 'test cases', 'common') + cls.python_test_dir = os.path.join(src_root, 'test cases', 'python') + cls.rust_test_dir = os.path.join(src_root, 'test cases', 'rust') + cls.vala_test_dir = os.path.join(src_root, 'test cases', 'vala') + cls.framework_test_dir = os.path.join(src_root, 'test cases', 'frameworks') + cls.unit_test_dir = os.path.join(src_root, 'test cases', 'unit') + cls.rewrite_test_dir = os.path.join(src_root, 'test cases', 'rewrite') + cls.linuxlike_test_dir = os.path.join(src_root, 'test cases', 'linuxlike') + cls.objc_test_dir = os.path.join(src_root, 'test cases', 'objc') + cls.objcpp_test_dir = os.path.join(src_root, 'test cases', 'objcpp') + cls.darwin_test_dir = os.path.join(src_root, 'test cases', 'darwin') + cls.fortran_test_dir = os.path.join(src_root, 'test cases', 'fortran') # Misc stuff if cls.backend is Backend.ninja: From 616040cf5a6d581bc0807c44d18e1841ff6595cf Mon Sep 17 00:00:00 2001 From: Sam James Date: Wed, 11 Mar 2026 05:58:51 +0000 Subject: [PATCH 643/681] Build a shared library for find_library link tests Build a shared library (as I promised before) so that undefined references are tolerated. We also make sure to use PIC if we can because linking non-PIC against a PIC object will fail, while linking PIC against a non-PIC object should be fine. There are some conditions: * Only do this if get_std_shared_lib_link_args gives us some non-empty argument because some platforms only support static linking. * Don't do this when `static` is passed to `find_library`. If `static` is passed, we don't perform any link check at all because we assume the user knows what they're doing, e.g. it may be a runtime toolchain component which requires special options to link with, or must be used in combination with another library. We don't try to handle the case (i.e. we allow and let it fail later when building) where we have some real dummy static library that is corrupt or for the wrong architecture or whathaveyou because the attempts in #15601 to find a reliable method failed: we hit I think two different riscv ld bugs there. Unfortunately, the PIC change wasn't enough to fix the RTEMS case, so we have to punt on static libaries overall still. The real motivating case for this work was a broken linker script (libatomic.so) anyway. We may still want to add a toggle for people to override this for a toolchain, possibly for non-ELF targets where I'm not sure the shared library behaviour is as common in linkers. If we add such a toggle, we could look at dropping the static requirement again. In summary, what changed: * Use a more robust method for testing (build a shared library) rather than a shortcut (linker option that doesn't work if the target is itself not a shared library). * Be (very) lenient for static libraries, as we were in the past. (cherry picked from commit bc071a42af9b9ca83c499ced5d26bf5b179ed240) --- mesonbuild/compilers/mixins/clike.py | 35 +++++++++++- unittests/internaltests.py | 84 ++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+), 1 deletion(-) diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py index db440c30b4fb..a50007c13625 100644 --- a/mesonbuild/compilers/mixins/clike.py +++ b/mesonbuild/compilers/mixins/clike.py @@ -1149,11 +1149,44 @@ def _find_library_real(self, libname: str, extra_dirs: T.List[str], code: str, l for trial in trials: if not os.path.isfile(trial): continue + + # Check if an argument to find_library is usable: + # * user requested a static library specifically + # => assume they know what they're doing, no link test + # as some runtime libraries may be provided in such + # a way that they must be used together with another + # library, or require special options. + # + # * user requested a library without `static: true` + # => build a shared library and link against it. Shared + # libraries (on ELF platforms) can be underlinked + # so it's better than an executable, as we'll only fail + # if it's unusable for another reason. + + # If we were asked specifically for a static library, don't perform + # a link test, because it might be a special toolchain library. + skip_link_check |= (libtype == LibType.STATIC) + + # Don't bother fishing for arguments if we've already + # been told not to perform a test. + if not skip_link_check: + shared_link_args = self.get_std_shared_lib_link_args() + extra_args = [trial] + lcargs + shared_link_args + + # Try to build the link test as PIC. We don't know if + # the target library is itself PIC: if it's non-PIC, + # PIC is fine, and if it is PIC, then PIC is needed, + # so always use it. + extra_args += self.get_pic_args() + + # Don't bother with a link check if we don't know how to build + # a shared library. + skip_link_check |= (not shared_link_args) + # When skip_link_check is True (e.g. for pkg-config # libraries which are trusted to be linkable), skip the # potentially expensive link check and just verify that the # file exists. - extra_args = [trial] + lcargs if skip_link_check or self.links(code, extra_args=extra_args, disable_cache=True)[0]: trial_result = trial break diff --git a/unittests/internaltests.py b/unittests/internaltests.py index f623a98e53cb..6f4334f4d392 100644 --- a/unittests/internaltests.py +++ b/unittests/internaltests.py @@ -728,6 +728,90 @@ def test_find_library_patterns(self): env.machines.host.system = 'windows' self._test_all_naming(cc, patterns, 'windows-mingw') + self._test_find_library_undefined(cc) + + def _test_find_library_undefined(self, cc): + ''' + find_library checks if its argument both exists and can be + linked against, but it must tolerate underlinked static + libraries. + + https://github.com/mesonbuild/meson/issues/15601 + ''' + def create_static_lib_with_undefined(name): + src = name.with_suffix('.c') + out = name.with_suffix('.o') + with src.open('w', encoding='utf-8') as f: + f.write('extern int get_cookie();') + f.write('int meson_foobar (void) { return get_cookie(); }') + # use of x86_64 is hardcoded in run_tests.py:get_fake_env() + if is_osx(): + subprocess.check_call(['clang', '-c', str(src), '-o', str(out), '-arch', 'x86_64']) + else: + subprocess.check_call(['gcc', '-c', str(src), '-o', str(out)]) + subprocess.check_call(['ar', 'csr', str(name), str(out)]) + + def create_static_lib_empty(name): + with name.open('w', encoding='utf-8') as f: + f.write("garbage") + + # The test relies on some open-coded toolchain invocations for + # library creation in create_static_lib_with_undefined. + if is_windows() or is_cygwin(): + return + + with tempfile.TemporaryDirectory() as tmpdir: + p1 = Path(tmpdir) + create_static_lib_with_undefined(p1 / 'libfoo.a') + + code = 'int meson_foobar (void); int main(void) { return meson_foobar(); }' + + # Check that we always tolerate undefined references in a static + # library regardless of the library type. + for type in (LibType.STATIC, LibType.PREFER_STATIC, LibType.PREFER_SHARED): + found = cc._find_library_real('foo', [tmpdir], + code, + type, lib_prefix_warning=True, ignore_system_dirs=False) + self.assertEqual(os.path.basename(found[0]), 'libfoo.a') + + # Check that we reject broken static libraries unless we were + # told to only find a static library. + create_static_lib_empty(p1 / 'libbar.a') + + # We have a broken static library *and* we indicated we want a static + # library, so we don't perform a link test. + found = cc._find_library_real('bar', [tmpdir], + code, + LibType.STATIC, lib_prefix_warning=True, ignore_system_dirs=False) + self.assertEqual(os.path.basename(found[0]), 'libbar.a') + + # We have a broken static library we're testing against but we only said + # we'd prefer static, not that it must be static: the heuristic + # says it likely isn't a special toolchain library, so we perform a + # link test. + found = cc._find_library_real('bar', [tmpdir], + code, + LibType.PREFER_STATIC, lib_prefix_warning=True, ignore_system_dirs=False) + self.assertIsNone(found, 'Unexpectedly found a library with PREFER_STATIC, link test expected to reject it') + + # We have a broken static library we're testing against but we only said + # we'd prefer shared, not that it must be shared: the heuristic + # says it likely isn't a special toolchain library, so we perform a + # link test. + found = cc._find_library_real('bar', [tmpdir], + code, + LibType.PREFER_SHARED, lib_prefix_warning=True, ignore_system_dirs=False) + self.assertIsNone(found, 'Unexpectedly found a library with PREFER_SHARED, link test expected to reject it') + + # We asked for a shared library and we only got a broken + # static one. We only tolerate them being broken if people + # explicitly ask for it w/ static: true, so we perform a link + # test. + found = cc._find_library_real('bar', [tmpdir], + code, + LibType.SHARED, lib_prefix_warning=True, ignore_system_dirs=False) + self.assertIsNone(found, 'Unexpectedly found a library with SHARED') + @skipIfNoPkgconfig def test_pkgconfig_parse_libs(self): ''' From 0d33866d7d60b35301ca28874304981cb382db7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Marcos=20Costa?= Date: Thu, 2 Jul 2026 10:50:41 +0200 Subject: [PATCH 644/681] universal.py: search_version: make the version regex a bit more robust MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change basically adds another check: the version number should not be followed by something other than a space. As an example, this is the output of 'clang --version' in a custom LLVM toolchain: (FooBar LLVM-Linux 5.0.0) clang version 21.9.0 Target: riscv64-unknown-linux-gnu Thread model: posix (...) where "5.0.0" is an internal tag, unrelated to clang's version itself. With the current regex, meson detects that clang's version is "5.0.0", and this breaks the build of some packages (e.g., appstream): | ../sources/AppStream-1.0.6/meson.build:1:0: ERROR: None of values ['gnu17'] are supported by the C compiler. Possible values for option "c_std" are ['none', 'c89', 'c99', 'c11', 'gnu89', 'gnu99', 'gnu11'] While I understand the root cause is this spurious internal version number being present left to clang's version, I belive the current regex would be more robust if it also checks what comes next after the 'X.Y.Z' pattern. Signed-off-by: João Marcos Costa (cherry picked from commit b43e7ed27e82f4243c24a7bf36f2ff571b17dfd7) --- mesonbuild/utils/universal.py | 3 ++- unittests/internaltests.py | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index 41f0b9c59c94..976c7ac7b000 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -1222,7 +1222,8 @@ def search_version(text: str) -> str: ( -[a-zA-Z0-9]+ # Hyphen and one or more alphanumeric )? # Zero or one occurrence - ) # One occurrence + ) + (?!\S) # Must not be followed by non-space char """, re.VERBOSE) match = version_regex.search(text) if match: diff --git a/unittests/internaltests.py b/unittests/internaltests.py index 6f4334f4d392..0848b75abfe7 100644 --- a/unittests/internaltests.py +++ b/unittests/internaltests.py @@ -91,6 +91,10 @@ def test_version_number(self): self.assertEqual(search_version('2016.x'), 'unknown version') self.assertEqual(search_version(r'something version is \033[32;2m1.2.0\033[0m.'), '1.2.0') + self.assertEqual(search_version(r'''(FooBar LLVM-Linux 5.0.0) clang version 21.9.0 +Target: riscv64-unknown-linux-gnu +Thread model: posix'''), '21.9.0') + # Literal output of mvn self.assertEqual(search_version(r'''\ \033[1mApache Maven 3.8.1 (05c21c65bdfed0f71a2f2ada8b84da59348c4c5d)\033[0m From ff17e7ee09fec0cc74122dce19bfd658ada791c8 Mon Sep 17 00:00:00 2001 From: Charles Brunet Date: Tue, 11 Aug 2026 10:37:18 -0400 Subject: [PATCH 645/681] Fix dep dependency prefix detection Fix regression introduced by 1e50fe1. It is possible that the reported relative path has not ./ prefix at all: e.g. "Note: including file: incdetect2". This fixes the regex for that case. Part-of: https://github.com/mesonbuild/meson/pull/16098 Closes: https://github.com/mesonbuild/meson/pull/16098 (cherry picked from commit afb7e6dc3c00209b5398e70615009af6150239c9) --- mesonbuild/backend/ninjabackend.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 0a52d87686c6..846e6ab7f22e 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -653,7 +653,7 @@ def detect_vs_dep_prefix(self, tempfilename: str) -> T.TextIO: # 'Note: including file: d:\build\meson-private\incdetect2', however # different locales have different messages with a different # number of colons. Match up to the drive name 'd:\' or - # a relative path '.\'. + # a relative path '.\', or no path at all. # When used in cross compilation, the path separator is a # forward slash rather than a backslash so handle both; i.e. # the path is /build/meson-private/incdetect or ./incdetect2. @@ -661,7 +661,7 @@ def detect_vs_dep_prefix(self, tempfilename: str) -> T.TextIO: # use backslashes, but without the leading drive name, so # allow the path to start with any path separator, i.e. # \build\meson-private\incdetect2 - matchre = re.compile(rb"^(.*\s)([a-zA-Z]:[\\/]|\.?[\\/]).*incdetect2$") + matchre = re.compile(rb"^(.*\s)([a-zA-Z]:[\\/].*|\.?[\\/].*|)incdetect2$") def detect_prefix(out: bytes) -> T.TextIO: for line in re.split(rb'\r?\n', out): From 14933d4bba945e1e0ba6f674014b7c906284f941 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Thu, 13 Aug 2026 13:52:23 +0300 Subject: [PATCH 646/681] Ensure coverage info is not empty. This seems to be caused by a change in lcov. Originally detected in Debian: https: //bugs.debian.org/cgi-bin/bugreport.cgi?bug=1144176 (cherry picked from commit aa397a895ea18e409377293c3c172c695c6817a0) --- test cases/common/1 trivial/trivial.c | 7 ++++++- test cases/common/243 escape++/test.c | 8 +++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/test cases/common/1 trivial/trivial.c b/test cases/common/1 trivial/trivial.c index 96612d48b4e8..ad19a4a6097e 100644 --- a/test cases/common/1 trivial/trivial.c +++ b/test cases/common/1 trivial/trivial.c @@ -1,6 +1,11 @@ #include -int main(void) { +int main(int argc, char **argv) { + /* Have a non-elidable branch to make sure coverage info is not empty. */ + if(argc > 1) { + printf("A command line argument was passed to the program.\n"); + (void)argv; + } printf("Trivial test is working.\n"); return 0; } diff --git a/test cases/common/243 escape++/test.c b/test cases/common/243 escape++/test.c index 9b6bdc2ec2f0..4a82fd3f6d9a 100644 --- a/test cases/common/243 escape++/test.c +++ b/test cases/common/243 escape++/test.c @@ -1,3 +1,9 @@ -int main(void) { +#include + +int main(int argc, char **argv) { + if(argc > 1) { + printf("A command line argument was passed to this program.\n"); + (void)argv; + } return 0; } From f5dda9a183bb511c2cfa8d8857d9fefc91d01c1a Mon Sep 17 00:00:00 2001 From: Adrien Dorsaz Date: Thu, 20 Aug 2026 20:32:14 +0200 Subject: [PATCH 647/681] msetup: fix misleading error message about directories `validate_core_dirs` method is used to find out which directory contains the sources and which one will be used as build directory. Its error message misleads the user when the user follows the order of positional arguments of `meson setup` (the first one should be the build directory and the second one the source directory). In case of invalid configuration, e.g. when neither directory contained a `meson.build` file, the error message was: > $ meson setup ./build ./src > ERROR: Neither source directory './build' nor build directory './src' contain > a build file meson.build. In this case, a user might waste time trying to figure out why their build directory is being reported as the source directory. Since the purpose of this method is to determine what each directory corresponds, the best possible error message should make no assumptions about the directories. (cherry picked from commit fb0c38e959a582600548e7c29f03a40b6b0e8f3e) --- mesonbuild/msetup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/msetup.py b/mesonbuild/msetup.py index f80ef0b6f4bc..002ade5719de 100644 --- a/mesonbuild/msetup.py +++ b/mesonbuild/msetup.py @@ -119,7 +119,7 @@ def has_build_file(self, dirname: str) -> bool: return os.path.exists(fname) def validate_core_dirs(self, dir1: T.Optional[str], dir2: T.Optional[str]) -> T.Tuple[str, str]: - invalid_msg_prefix = f'Neither source directory {dir1!r} nor build directory {dir2!r}' + invalid_msg_prefix = f'Neither directory {dir1!r} nor directory {dir2!r}' if dir1 is None: if dir2 is None: if not self.has_build_file('.') and self.has_build_file('..'): From 8f83a4677dc96f84a97ee9fd58030a9d6049bb85 Mon Sep 17 00:00:00 2001 From: mattip Date: Sat, 15 Aug 2026 21:35:25 +0300 Subject: [PATCH 648/681] python module: support pypy without distutils Co-authored-by: Daniele Nicolodi (cherry picked from commit 7ce2ff534732df7a1ec48319ee9bac8ff912a1e7) --- mesonbuild/scripts/python_info.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/mesonbuild/scripts/python_info.py b/mesonbuild/scripts/python_info.py index b006bc38acb0..dc92779f49d8 100755 --- a/mesonbuild/scripts/python_info.py +++ b/mesonbuild/scripts/python_info.py @@ -65,11 +65,13 @@ def get_install_paths(): def links_against_libpython(): # on versions supporting python-embed.pc, this is the non-embed lib - # - # PyPy is not yet up to 3.12 and work is still pending to export the - # relevant information (it doesn't automatically provide arbitrary - # Makefile vars) - if sys.version_info >= (3, 8) and not is_pypy: + if is_pypy: + # PyPy work is still pending to export the relevant information (it + # doesn't automatically provide arbitrary Makefile vars). However, + # the only PyPy supported platform that requires linking to libpython + # is Windows, thus it is easy enough to hardcode the answer. + return sysconfig.get_platform() == "win-amd64" + elif sys.version_info >= (3, 8): variables = sysconfig.get_config_vars() return bool(variables.get('LIBPYTHON', 'yes')) else: From f93e40a702483cec872ef427a3836c7f7b36b63a Mon Sep 17 00:00:00 2001 From: Gleb Popov <6yearold@gmail.com> Date: Thu, 20 Aug 2026 21:59:00 +0300 Subject: [PATCH 649/681] ElbrusCompiler: Remove the lchmod hack With recent e2k glibc this hack is actually wrong and makes the find_function test fail. (cherry picked from commit 4ab745f3a5819a22119dc68ee40e52d9b191a9de) --- mesonbuild/compilers/c.py | 4 ---- mesonbuild/compilers/cpp.py | 4 ---- 2 files changed, 8 deletions(-) diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index 2d88082beb80..827a81296bdb 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -346,13 +346,9 @@ def get_options(self) -> 'MutableKeyedOptionDictType': std_opt.set_versions(stds) return opts - # Elbrus C compiler does not have lchmod, but there is only linker warning, not compiler error. - # So we should explicitly fail at this case. def has_function(self, funcname: str, prefix: str, *, extra_args: T.Optional[T.List[str]] = None, dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]: - if funcname == 'lchmod': - return False, False return super().has_function(funcname, prefix, extra_args=extra_args, dependencies=dependencies) diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index 7927d4a80568..620c1fa5126c 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -640,13 +640,9 @@ def get_options(self) -> 'MutableKeyedOptionDictType': std_opt.set_versions(cpp_stds, gnu=True) return opts - # Elbrus C++ compiler does not have lchmod, but there is only linker warning, not compiler error. - # So we should explicitly fail at this case. def has_function(self, funcname: str, prefix: str, *, extra_args: T.Optional[T.List[str]] = None, dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]: - if funcname == 'lchmod': - return False, False return super().has_function(funcname, prefix, extra_args=extra_args, dependencies=dependencies) # Elbrus C++ compiler does not support RTTI, so don't check for it. From 978ba5ea5f725ba435cc037072ac1ea02a073fd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Apitzsch?= Date: Tue, 25 Aug 2026 00:31:36 +0200 Subject: [PATCH 650/681] interpreterobjects: Fix typo in error message When trying to enable a disabled feature, the error should say ' cannot be enabled' and not ' cannot be disabled'. Similar, when trying to disable an enabled feature. (cherry picked from commit 6db2b663af92cd5eb95fb93c35f38d1e94e409c1) --- mesonbuild/interpreter/interpreterobjects.py | 4 ++-- test cases/failing/101 feature require/test.json | 2 +- test cases/failing/102 feature require.bis/test.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index 441285ce65d5..76f2b394ba29 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -149,7 +149,7 @@ def _disable_if(self, condition: bool, message: T.Optional[str]) -> Feature: return self.held_object if self.held_object.is_enabled(): - err_msg = f'Feature {self.held_object.name} cannot be enabled' + err_msg = f'Feature {self.held_object.name} cannot be disabled' if message: err_msg += f': {message}' raise InterpreterException(err_msg) @@ -187,7 +187,7 @@ def enable_if_method(self, args: T.Tuple[bool], kwargs: 'kwargs.FeatureOptionReq return self.held_object if self.held_object.is_disabled(): - err_msg = f'Feature {self.held_object.name} cannot be disabled' + err_msg = f'Feature {self.held_object.name} cannot be enabled' if kwargs['error_message']: err_msg += f': {kwargs["error_message"]}' raise InterpreterException(err_msg) diff --git a/test cases/failing/101 feature require/test.json b/test cases/failing/101 feature require/test.json index 2da54948fc28..cb8c0e68ec02 100644 --- a/test cases/failing/101 feature require/test.json +++ b/test cases/failing/101 feature require/test.json @@ -2,7 +2,7 @@ "stdout": [ { "match": "re", - "line": ".*/meson\\.build:2:31: ERROR: Feature reqfeature cannot be enabled: frobnicator not available" + "line": ".*/meson\\.build:2:31: ERROR: Feature reqfeature cannot be disabled: frobnicator not available" } ] } diff --git a/test cases/failing/102 feature require.bis/test.json b/test cases/failing/102 feature require.bis/test.json index ed488affd456..cdb007d17e40 100644 --- a/test cases/failing/102 feature require.bis/test.json +++ b/test cases/failing/102 feature require.bis/test.json @@ -2,7 +2,7 @@ "stdout": [ { "match": "re", - "line": ".*/meson\\.build:2:31: ERROR: Feature reqfeature cannot be enabled" + "line": ".*/meson\\.build:2:31: ERROR: Feature reqfeature cannot be disabled" } ] } From 69e5964389e8b6db38c1cb692e04c298745cf11b Mon Sep 17 00:00:00 2001 From: Tristan Partin Date: Tue, 25 Aug 2026 16:45:31 +0000 Subject: [PATCH 651/681] Stop depending on libwmf-config to run test Debian has stopped shipping libwmf-config in the default PATH. Therefore, we were skipping the test. Debian is shipping a pkg-config file that we can use instead to make sure that the test is still run. Signed-off-by: Tristan Partin Part-of: https://github.com/mesonbuild/meson/pull/16149 Closes: https://github.com/mesonbuild/meson/pull/16149 Signed-off-by: Sam James (cherry picked from commit 39ab648c832e91a1e0f25c3190f8113a3651f5a7) --- test cases/frameworks/21 libwmf/meson.build | 24 +++++++++++---------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/test cases/frameworks/21 libwmf/meson.build b/test cases/frameworks/21 libwmf/meson.build index 5892932a70cc..16370bdd44b8 100644 --- a/test cases/frameworks/21 libwmf/meson.build +++ b/test cases/frameworks/21 libwmf/meson.build @@ -1,11 +1,10 @@ project('libwmf test', 'c') -wm = find_program('libwmf-config', required : false) -if not wm.found() or meson.is_cross_build() - error('MESON_SKIP_TEST: libwmf-config not installed') +libwmf_dep = dependency('libwmf', version : '>= 0.2.8', required : false) +if not libwmf_dep.found() + error('MESON_SKIP_TEST: libwmf not installed') endif -libwmf_dep = dependency('libwmf', version : '>= 0.2.8') libwmf_ver = libwmf_dep.version() assert(libwmf_ver.split('.').length() > 1, 'libwmf version is "@0@"'.format(libwmf_ver)) message('libwmf version is "@0@"'.format(libwmf_ver)) @@ -17,11 +16,14 @@ e = executable('libwmf_prog', 'libwmf_prog.c', dependencies : [libwmf_dep, ft_de test('libwmftest', e) -# Test using the method keyword: - -dependency('libwmf', method : 'config-tool') -dependency('libwmf', method : 'libwmf-config') - # Check we can apply a version constraint -dependency('libwmf', version: '>=@0@'.format(libwmf_dep.version()), method: 'pkg-config', required: false) -dependency('libwmf', version: '>=@0@'.format(libwmf_dep.version()), method: 'config-tool') +dependency('libwmf', version: '>=@0@'.format(libwmf_ver), method: 'pkg-config', required: false) + +# Test using the method keyword. Only exercise the config-tool method when +# libwmf-config is actually available on PATH. +libwmf_config = find_program('libwmf-config', required : false) +if libwmf_config.found() and not meson.is_cross_build() + dependency('libwmf', method : 'config-tool') + dependency('libwmf', method : 'libwmf-config') + dependency('libwmf', version: '>=@0@'.format(libwmf_ver), method: 'config-tool') +endif From 8d13bffaafc7c67cd9a52f9ee14833ec448956c7 Mon Sep 17 00:00:00 2001 From: Tristan Partin Date: Mon, 24 Aug 2026 18:36:21 +0000 Subject: [PATCH 652/681] Fix racy wine prefix initialization in ubuntu-rolling cross CI Wine wants to initialize the prefix on first use. In our CI, we were initializing the prefix lazily when the tests actually ran. Since we run tests in parallel, this became contentious and resulted in the following failure: wine client error:0: recvmsg: Connection reset by peer Sometimes the CI job would pass and other times it would fail. Signed-off-by: Tristan Partin (cherry picked from commit 0efde72d426738e04ea4b36e2f2bd1a93f20aeef) --- ci/ciimage/ubuntu-rolling/install.sh | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ci/ciimage/ubuntu-rolling/install.sh b/ci/ciimage/ubuntu-rolling/install.sh index 6551107e0cb9..6ffea7041fc3 100755 --- a/ci/ciimage/ubuntu-rolling/install.sh +++ b/ci/ciimage/ubuntu-rolling/install.sh @@ -49,6 +49,18 @@ eatmydata apt-get -y build-dep meson eatmydata apt-get -y install "${pkgs[@]}" "${transitivepkgs[@]}" eatmydata apt-get -y install --no-install-recommends wine # Wine is special +# Initialize the wine prefix now, at image-build time, while nothing else is +# using wine concurrently. If left uninitialized, `wine` lazily creates +# ~/.wine on first use. We run tests concurrently , and several of them can race +# to bootstrap ~/.wine for the first time simultaneously, causing wine to +# intermittently fail with: +# +# wine client error:0: recvmsg: Connection reset by peer +# +# causing spurious "Executables created by are not runnable" +# sanity check failures. +WINEDEBUG=-all wine wineboot --init + # HACK: build hotdoc from git repo since current sdist is broken on modern compilers # change back to 'hotdoc' once it's fixed install_python_packages git+https://github.com/hotdoc/hotdoc From ab28057cb7444829cedea9cd743781d64f132386 Mon Sep 17 00:00:00 2001 From: Tristan Partin Date: Wed, 26 Aug 2026 21:25:21 -0500 Subject: [PATCH 653/681] Migrate envcheck.py test to C In Valgrind test setups, we were running the Python interpreter under Valgrind. The Python interpreter is not code that we control, meaning that if something changes upstream that causes a Valgrind failure, we are victims of it. By migrating to C, we can easily control the outcome. Signed-off-by: Tristan Partin (cherry picked from commit 5a41285e6142c75f7a299267fabcb8642eea42e7) --- test cases/unit/2 testsetups/envcheck.c | 10 ++++++++++ test cases/unit/2 testsetups/envcheck.py | 5 ----- test cases/unit/2 testsetups/meson.build | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) create mode 100644 test cases/unit/2 testsetups/envcheck.c delete mode 100755 test cases/unit/2 testsetups/envcheck.py diff --git a/test cases/unit/2 testsetups/envcheck.c b/test cases/unit/2 testsetups/envcheck.c new file mode 100644 index 000000000000..02257c858b4a --- /dev/null +++ b/test cases/unit/2 testsetups/envcheck.c @@ -0,0 +1,10 @@ +#include + +int +main(void) +{ + if (getenv("PATH") == NULL) + return 1; + + return 0; +} diff --git a/test cases/unit/2 testsetups/envcheck.py b/test cases/unit/2 testsetups/envcheck.py deleted file mode 100755 index e41a715a0405..000000000000 --- a/test cases/unit/2 testsetups/envcheck.py +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env python3 - -import os - -assert 'PATH' in os.environ diff --git a/test cases/unit/2 testsetups/meson.build b/test cases/unit/2 testsetups/meson.build index 91a5fbe8e0e6..cfac3befc63d 100644 --- a/test cases/unit/2 testsetups/meson.build +++ b/test cases/unit/2 testsetups/meson.build @@ -21,7 +21,7 @@ add_test_setup('valgrind', buggy = executable('buggy', 'buggy.c', 'impl.c') test('Test buggy', buggy, suite: ['buggy']) -envcheck = find_program('envcheck.py') +envcheck = executable('envcheck', 'envcheck.c') test('test-env', envcheck) add_test_setup('empty') From fa6a193d89e5409160689048acfb2d9e9d75822b Mon Sep 17 00:00:00 2001 From: Tristan Partin Date: Tue, 25 Aug 2026 17:18:38 +0000 Subject: [PATCH 654/681] Detect Java home directory by asking the compiler The Java compiler can tell use the java.home system property when we pass -J-XshowSettings:properties to it. Theoretically, this is a much more reliable method to detect the Java home directory, so now we prioritize it before falling back to other methods. Maybe in the future we can even remove the other methods entirely. Signed-off-by: Tristan Partin Part-of: https://github.com/mesonbuild/meson/pull/16150 Signed-off-by: Sam James --- mesonbuild/dependencies/dev.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/mesonbuild/dependencies/dev.py b/mesonbuild/dependencies/dev.py index 4b043a07e82f..55d41ac8f136 100644 --- a/mesonbuild/dependencies/dev.py +++ b/mesonbuild/dependencies/dev.py @@ -587,6 +587,8 @@ def __init__(self, name: str, environment: 'Environment', kwargs: DependencyObje return self.java_home = environment.properties[self.for_machine].get_java_home() + if not self.java_home: + self.java_home = self.__detect_java_home_from_compiler() if not self.java_home: self.java_home = pathlib.Path(shutil.which(self.javac.get_exe())).resolve().parents[1] if m.is_darwin(): @@ -643,6 +645,30 @@ def __init__(self, name: str, environment: 'Environment', kwargs: DependencyObje self.is_found = True + def __detect_java_home_from_compiler(self) -> T.Optional[pathlib.Path]: + ''' + Ask the compiler for its home directory by parsing the `java.home` + system property. + + This is necessary because not every OS wires `javac` up as a plain + symlink (or chain of symlinks) pointing directly at the real JDK + installation. + ''' + try: + p, _, e = mesonlib.Popen_safe(self.javac.exelist + ['-J-XshowSettings:properties', '-version']) + except (OSError, subprocess.SubprocessError): + return None + if p.returncode != 0: + return None + # -XshowSettings writes to stderr, not stdout. + for line in e.splitlines(): + name, separator, value = line.partition('=') + if separator and name.strip() == 'java.home': + value = value.strip() + if value: + return pathlib.Path(value) + return None + @staticmethod def __cpu_translate(cpu: str) -> str: ''' From 49a852fecf1346ac308ecfcebfc3ed961f43500e Mon Sep 17 00:00:00 2001 From: Tristan Partin Date: Wed, 26 Aug 2026 21:47:57 -0500 Subject: [PATCH 655/681] Account for pre-JDK9 java.home behavior Prior to JDK9, the java.home property pointed to the JRE directory inside the JDK, not the JDK root itself. Signed-off-by: Tristan Partin (cherry picked from commit d0fc67c5795606c4388cea39546329b22a4bd73c) --- mesonbuild/dependencies/dev.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mesonbuild/dependencies/dev.py b/mesonbuild/dependencies/dev.py index 55d41ac8f136..e0c27fe5a715 100644 --- a/mesonbuild/dependencies/dev.py +++ b/mesonbuild/dependencies/dev.py @@ -666,7 +666,13 @@ def __detect_java_home_from_compiler(self) -> T.Optional[pathlib.Path]: if separator and name.strip() == 'java.home': value = value.strip() if value: - return pathlib.Path(value) + home = pathlib.Path(value) + # Pre-JDK9 (JEP 220), java.home points at the JRE directory + # bundled inside the JDK, not the JDK root itself, so we + # must go up one level. + if version_compare(self.version, '<= 1.8.0'): + home = home.parent + return home return None @staticmethod From 62d3bd5fd878dc225e356c84b730310a7ba77a94 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 25 Aug 2026 16:11:38 +0200 Subject: [PATCH 656/681] interpreter: allow attaching a feature check to a relaxation This provides a way to influence feature reporting from elsewhere in the code. Signed-off-by: Paolo Bonzini (cherry picked from commit 93996eba1042c99c1539f6bae2106d7e34856369) --- mesonbuild/interpreter/interpreter.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 90a2f101b043..9b12ece7c98b 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -122,6 +122,7 @@ from . import kwargs as kwtypes from ..backend.backends import Backend from ..compilers.compilers import CompilerDict, Language + from ..interpreterbase import FeatureCheckBase from ..interpreterbase.baseobjects import InterpreterObject, TYPE_var, TYPE_kwargs from ..options import OptionDict from ..mesonlib import InstallScript, SubProject @@ -284,7 +285,7 @@ def __init__( subproject_dir: str = 'subprojects', invoker_method_default_options: T.Optional[OptionDict] = None, ast: T.Optional[mparser.CodeBlockNode] = None, - relaxations: T.Optional[T.Set[InterpreterRuleRelaxation]] = None, + relaxations: T.Optional[T.Dict[InterpreterRuleRelaxation, T.Optional[FeatureCheckBase]]] = None, user_defined_options: T.Optional[SharedCMDOptions] = None, cargo: T.Optional[cargo.Interpreter] = None, ) -> None: @@ -296,7 +297,7 @@ def __init__( self.cargo = cargo self.summary: T.Dict[str, 'Summary'] = {} self.modules: T.Dict[str, NewExtensionModule] = {} - self.relaxations = relaxations or set() + self.relaxations = relaxations or {} if ast is None: self.load_root_meson_file() else: @@ -328,6 +329,16 @@ def __init__( def __getnewargs_ex__(self) -> T.Tuple[T.Tuple[object], T.Dict[str, object]]: raise MesonBugException('This class is unpicklable') + def relaxed(self, relaxation: InterpreterRuleRelaxation) -> bool: + '''Check if a relaxation is in place, and if so whether it should warn + as a broken or deprecated feature.''' + if relaxation not in self.relaxations: + return False + feature = self.relaxations[relaxation] + if feature is not None: + feature.use(self.subproject, location=self.current_node) + return True + def load_root_cargo_lock_file(self) -> None: cargo_lock = os.path.join(self.source_root, self.subdir, 'Cargo.lock') if not os.path.isfile(cargo_lock): @@ -1054,7 +1065,7 @@ def _do_subproject_meson(self, subp_name: SubProject, subdir: str, kwargs: kwtypes.DoSubproject, ast: T.Optional[mparser.CodeBlockNode] = None, build_def_files: T.Optional[T.List[str]] = None, - relaxations: T.Optional[T.Set[InterpreterRuleRelaxation]] = None, + relaxations: T.Optional[T.Dict[InterpreterRuleRelaxation, T.Optional[FeatureCheckBase]]] = None, cargo: T.Optional[cargo.Interpreter] = None) -> SubprojectHolder: for_machine = kwargs['for_machine'] if for_machine is MachineChoice.BUILD: @@ -1125,7 +1136,7 @@ def _do_subproject_cmake(self, subp_name: SubProject, subdir: str, kwargs, ast, [str(f) for f in cm_int.bs_files], relaxations={ - InterpreterRuleRelaxation.ALLOW_BUILD_DIR_FILE_REFERENCES, + InterpreterRuleRelaxation.ALLOW_BUILD_DIR_FILE_REFERENCES: None, } ) result.cm_interpreter = cm_int @@ -1153,7 +1164,7 @@ def _do_subproject_cargo(self, subp_name: SubProject, subdir: str, return self._do_subproject_meson( subp_name, subdir, default_options, kwargs, ast, - relaxations={InterpreterRuleRelaxation.CARGO_SUBDIR} if ast is not None else None, + relaxations={InterpreterRuleRelaxation.CARGO_SUBDIR: None} if ast is not None else None, cargo=cargo_int) @typed_pos_args('get_option', str) @@ -2616,7 +2627,7 @@ def func_subdir(self, node: mparser.BaseNode, args: T.Tuple[str], kwargs: 'kwtyp self.create_build_subdir(subdir) - if InterpreterRuleRelaxation.CARGO_SUBDIR in self.relaxations and \ + if self.relaxed(InterpreterRuleRelaxation.CARGO_SUBDIR) and \ os.path.exists(os.path.join(self.environment.get_source_dir(), subdir, 'Cargo.toml')): codeblock = self.cargo.interpret(subdir, self.root_subdir) self._save_ast(subdir, codeblock) @@ -3400,7 +3411,7 @@ def source_strings_to_files(self, self.validate_within_subproject(self.subdir, s) except BuiltFileByNameError as e: # In Meson 2.0 this should just raise - if InterpreterRuleRelaxation.ALLOW_BUILD_DIR_FILE_REFERENCES not in self.relaxations: + if not self.relaxed(InterpreterRuleRelaxation.ALLOW_BUILD_DIR_FILE_REFERENCES): #raise mlog.warning(str(e), location=self.current_node) if path_has_root(s): From 881a5f2c1bc94ee94f5420297fd3138f7d5d10ea Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 25 Aug 2026 16:14:10 +0200 Subject: [PATCH 657/681] interpreter: allow sandbox violations for custom target depend_files These were unwittingly forbidden in Meson 1.12.0 via commit d8af51917 ("interpreter: fix depend_files type from func_custom_target", 2026-05-18). Catch the unfortunate situation and insta-deprecate it, but while allowing it in previous versions. Signed-off-by: Paolo Bonzini (cherry picked from commit da5e788616965daeac058b3637deacd8297cd6e4) --- mesonbuild/interpreter/interpreter.py | 28 +++++++++++++++++-- .../meson.build | 3 ++ .../shared.py | 2 ++ .../subprojects/sub/meson.build | 8 ++++++ .../subprojects/sub/touch.py | 3 ++ .../test.json | 7 +++++ 6 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 test cases/warning/10 depend_files sandbox violation/meson.build create mode 100644 test cases/warning/10 depend_files sandbox violation/shared.py create mode 100644 test cases/warning/10 depend_files sandbox violation/subprojects/sub/meson.build create mode 100644 test cases/warning/10 depend_files sandbox violation/subprojects/sub/touch.py create mode 100644 test cases/warning/10 depend_files sandbox violation/test.json diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 9b12ece7c98b..995502ad4d2a 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -4,6 +4,7 @@ from __future__ import annotations +import contextlib import io, sys, traceback import dataclasses import functools @@ -267,6 +268,7 @@ class InterpreterRuleRelaxation(Enum): ALLOW_BUILD_DIR_FILE_REFERENCES = 1 CARGO_SUBDIR = 2 + ALLOW_SANDBOX_VIOLATION = 4 implicit_check_false_warning = """You should add the boolean check kwarg to the run_command call. It currently defaults to false, @@ -329,6 +331,19 @@ def __init__( def __getnewargs_ex__(self) -> T.Tuple[T.Tuple[object], T.Dict[str, object]]: raise MesonBugException('This class is unpicklable') + @contextlib.contextmanager + def relaxing(self, relaxation: InterpreterRuleRelaxation, feature: FeatureCheckBase) -> T.Iterator[None]: + '''Temporarily add a rule relaxation to the interpreter, with a + warning if it is encountered.''' + if relaxation in self.relaxations: + yield + else: + try: + self.relaxations[relaxation] = feature + yield + finally: + del self.relaxations[relaxation] + def relaxed(self, relaxation: InterpreterRuleRelaxation) -> bool: '''Check if a relaxation is in place, and if so whether it should warn as a broken or deprecated feature.''' @@ -2272,6 +2287,10 @@ def func_custom_target(self, node: mparser.FunctionNode, args: T.Tuple[str], self._validate_custom_target_outputs(len(inputs) > 1, kwargs['output'], "custom_target") + with self.relaxing(InterpreterRuleRelaxation.ALLOW_SANDBOX_VIOLATION, + FeatureBroken('grabbing custom target depend_files from outside the current project', '1.12.0')): + depend_files = self.source_strings_to_files(kwargs['depend_files']) + tg = build.CustomTarget( name, self.subdir, @@ -2284,7 +2303,7 @@ def func_custom_target(self, node: mparser.FunctionNode, args: T.Tuple[str], build_by_default=build_by_default, capture=kwargs['capture'], console=kwargs['console'], - depend_files=self.source_strings_to_files(kwargs['depend_files']), + depend_files=depend_files, depfile=kwargs['depfile'], extra_depends=kwargs['depends'], env=kwargs['env'], @@ -3425,8 +3444,11 @@ def source_strings_to_files(self, results.append(mesonlib.File.from_built_relative(rel)) else: results.append(mesonlib.File.from_built_file(self.subdir, s)) - else: - results.append(mesonlib.File.from_source_file(self.environment.source_dir, self.subdir, s)) + continue + except SandboxViolationError: + if not self.relaxed(InterpreterRuleRelaxation.ALLOW_SANDBOX_VIOLATION): + raise + results.append(mesonlib.File.from_source_file(self.environment.source_dir, self.subdir, s)) elif isinstance(s, (mesonlib.File, build.GeneratedList, Program, build.BuildTarget, build.CustomTargetIndex, build.CustomTarget, build.ExtractedObjects, build.StructuredSources)): diff --git a/test cases/warning/10 depend_files sandbox violation/meson.build b/test cases/warning/10 depend_files sandbox violation/meson.build new file mode 100644 index 000000000000..bbde9afaea88 --- /dev/null +++ b/test cases/warning/10 depend_files sandbox violation/meson.build @@ -0,0 +1,3 @@ +project('subproject depend_files sandbox violation') + +subproject('sub') diff --git a/test cases/warning/10 depend_files sandbox violation/shared.py b/test cases/warning/10 depend_files sandbox violation/shared.py new file mode 100644 index 000000000000..2dc46fbc9ac7 --- /dev/null +++ b/test cases/warning/10 depend_files sandbox violation/shared.py @@ -0,0 +1,2 @@ +#! /usr/bin/env python3 +print('what were you looking for?') diff --git a/test cases/warning/10 depend_files sandbox violation/subprojects/sub/meson.build b/test cases/warning/10 depend_files sandbox violation/subprojects/sub/meson.build new file mode 100644 index 000000000000..fd33a8663a7b --- /dev/null +++ b/test cases/warning/10 depend_files sandbox violation/subprojects/sub/meson.build @@ -0,0 +1,8 @@ +project('sub') + +custom_target('t', + output: 'out', + command: [find_program('touch.py'), '@OUTPUT@'], + depend_files: '../../shared.py', + build_by_default: true, +) diff --git a/test cases/warning/10 depend_files sandbox violation/subprojects/sub/touch.py b/test cases/warning/10 depend_files sandbox violation/subprojects/sub/touch.py new file mode 100644 index 000000000000..bd92092c5191 --- /dev/null +++ b/test cases/warning/10 depend_files sandbox violation/subprojects/sub/touch.py @@ -0,0 +1,3 @@ +#! /usr/bin/env python3 +import sys +open(sys.argv[1], "w").close() diff --git a/test cases/warning/10 depend_files sandbox violation/test.json b/test cases/warning/10 depend_files sandbox violation/test.json new file mode 100644 index 000000000000..5e61027a6ed6 --- /dev/null +++ b/test cases/warning/10 depend_files sandbox violation/test.json @@ -0,0 +1,7 @@ +{ + "stdout": [ + { + "line": "sub| test cases/warning/10 depend_files sandbox violation/subprojects/sub/meson.build:3: DEPRECATION: Project uses feature that was always broken, and is now deprecated since '1.12.0': grabbing custom target depend_files from outside the current project." + } + ] +} From 3fa2726831cc1aba9f1291d81c5fb02db4cb7ed7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 25 Aug 2026 09:23:48 +0200 Subject: [PATCH 658/681] build, backends: move get_all_linked_targets to Backend The get_all_linked_targets() method is incomplete, in that it does not check other targets that provide objects for the target. Because information about objects is not available in Build, move the function to Backend. Note that this drops the lru_cache; it is not necessary because the function is called in two places and each of them runs at most once per target. Signed-off-by: Paolo Bonzini (cherry picked from commit 88c5b5def007e0844316a8bf9b4936428c6e1dd3) --- mesonbuild/backend/backends.py | 24 ++++++++++++++++++++ mesonbuild/backend/ninjabackend.py | 4 ++-- mesonbuild/build.py | 36 ------------------------------ 3 files changed, 26 insertions(+), 38 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 4aeed370b614..6465615a8d08 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -3,6 +3,7 @@ from __future__ import annotations +from collections import deque from dataclasses import dataclass, InitVar from functools import lru_cache from itertools import chain @@ -487,6 +488,29 @@ def relpath(todir: str | os.PathLike[str], fromdir: str | os.PathLike[str]) -> s return os.path.relpath(os.path.join('dummyprefixdir', todir), os.path.join('dummyprefixdir', fromdir)) + def get_all_linked_targets(self, target: build.BuildTarget) -> T.Iterator[build.BuildTargetTypes]: + """Get all targets that have been linked with this one, including internal and + indirect static libraries unlike :method:`build.BuildTarget.get_all_link_deps` + and :method:`build.all_dependencies_recurse`. + + This is useful for cases where we need to analyze these links, such as + for module information. + """ + seen: T.Set[build.BuildTarget] = set() + + stack: T.Deque[build.BuildTargetTypes] = deque() + stack.extendleft(target.link_targets) + stack.extendleft(target.link_whole_targets) + while stack: + t = stack.pop() + if t in seen or not isinstance(t, build.BuildTarget): + continue + seen.add(t) + stack.extendleft(t.link_targets) + stack.extendleft(t.link_whole_targets) + yield t + assert target not in seen, 'should not have self' + def flatten_object_list(self, target: build.BuildTarget, proj_dir_to_build_root: str = '' ) -> T.Tuple[T.List[str], T.List[build.BuildTarget]]: obj_list, deps = self._flatten_object_list(target, target.get_objects(), proj_dir_to_build_root) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 846e6ab7f22e..ce45d74b6b87 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1121,7 +1121,7 @@ def generate_target(self, target: T.Union[build.Target]) -> None: # add the private directories of all transitive dependencies, which # are needed for their mod files fc = target.compilers['fortran'] - for t in target.get_all_linked_targets(): + for t in self.get_all_linked_targets(target): fortran_inc_args.extend(fc.get_include_args( self.get_target_private_dir(t), False)) @@ -1269,7 +1269,7 @@ def generate_dependency_scan_target(self, target: build.BuildTarget, self.add_build(elem) infiles: T.Set[str] = set() - for t in target.get_all_linked_targets(): + for t in self.get_all_linked_targets(target): if self.should_use_dyndeps_for_target(t): assert isinstance(t, build.BuildTarget) infiles.add(self.get_dep_scan_file_for(t)[0]) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index dc490188608d..71ee4d45cb2b 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -1323,39 +1323,6 @@ def get_all_link_deps(self) -> ImmutableListProtocol[BuildTargetTypes]: stack.extendleft((t2 for t2 in t.link_whole_targets if t2 not in nonresults)) return list(result) - @lru_cache(maxsize=None) - def get_all_linked_targets(self) -> ImmutableListProtocol[BuildTargetTypes]: - """Get all targets that have been linked with this one. - - This is useful for cases where we need to analyze these links, such as - for module information. - - This includes static libraries and static libraries linked with static - libraries. This differs from :method:`get_all_link_deps` in that it does - add static libs, and differs from `:method:`get_dependencies`, which - does not look for targets that are not directly linked, such as those - that are added with `link_whole`. - - :returns: An immutable list of BuildTargets - """ - result: OrderedSet[BuildTargetTypes] = OrderedSet() - stack: T.Deque[BuildTargetTypes] = deque() - stack.extendleft(self.link_targets) - stack.extendleft(self.link_whole_targets) - while stack: - t = stack.pop() - if t in result: - continue - if isinstance(t, CustomTargetIndex): - stack.appendleft(t.target) - continue - if isinstance(t, BuildTarget): - result.add(t) - stack.extendleft(t.link_targets) - stack.extendleft(t.link_whole_targets) - assert self not in result, 'should not have self' - return list(result) - def get_link_deps_mapping(self, prefix: str) -> T.Mapping[str, str]: return self.get_transitive_link_deps_mapping(prefix) @@ -2988,9 +2955,6 @@ def get_internal_static_libraries(self) -> OrderedSet[StaticTargetTypes]: def get_internal_static_libraries_recurse(self, result: OrderedSet[StaticTargetTypes]) -> None: pass - def get_all_linked_targets(self) -> ImmutableListProtocol[BuildTargetTypes]: - return [] - def get(self, lib_type: _LibraryType, recursive: bool = False) -> LinkableTargetTypes: """Base case used by BothLibraries""" assert isinstance(self, (CustomTarget, CustomTargetIndex)) From 21913cdfa9fb10957160c58c17e5c0f8e40b910e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 25 Aug 2026 09:30:12 +0200 Subject: [PATCH 659/681] backends: include objects of linked targets into the depscan phase The get_all_linked_targets() method is incomplete, in that it does not check other targets that provide objects for the target. While ninjabackend.py does use `flatten_object_list` on the target itself, it does not check any link_with or link_whole targets. And because the static library in a `BothLibraries` object might actually have no sources and have just an `ExtractedObjects` entry instead, using `link_with: foo.get_static_lib()` in a Fortran program is broken. Signed-off-by: Paolo Bonzini (cherry picked from commit bb575c20e39779aabac9efbdc43b23babe10b635) --- mesonbuild/backend/backends.py | 14 ++++++++++---- test cases/fortran/24 both_libraries/main.f90 | 5 +++++ test cases/fortran/24 both_libraries/meson.build | 6 ++++++ test cases/fortran/24 both_libraries/modsrc.f90 | 4 ++++ 4 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 test cases/fortran/24 both_libraries/main.f90 create mode 100644 test cases/fortran/24 both_libraries/meson.build create mode 100644 test cases/fortran/24 both_libraries/modsrc.f90 diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 6465615a8d08..64b5e5da8f56 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -491,14 +491,21 @@ def relpath(todir: str | os.PathLike[str], fromdir: str | os.PathLike[str]) -> s def get_all_linked_targets(self, target: build.BuildTarget) -> T.Iterator[build.BuildTargetTypes]: """Get all targets that have been linked with this one, including internal and indirect static libraries unlike :method:`build.BuildTarget.get_all_link_deps` - and :method:`build.all_dependencies_recurse`. + and :method:`build.all_dependencies_recurse`, and targets whose objects + are borrowed by this one or any returned target. This is useful for cases where we need to analyze these links, such as for module information. """ seen: T.Set[build.BuildTarget] = set() - stack: T.Deque[build.BuildTargetTypes] = deque() + + def add_linked_targets(t: build.BuildTarget) -> None: + stack.extendleft(t.link_targets) + stack.extendleft(t.link_whole_targets) + _, od = self.flatten_object_list(t) + stack.extendleft(od) + stack.extendleft(target.link_targets) stack.extendleft(target.link_whole_targets) while stack: @@ -506,8 +513,7 @@ def get_all_linked_targets(self, target: build.BuildTarget) -> T.Iterator[build. if t in seen or not isinstance(t, build.BuildTarget): continue seen.add(t) - stack.extendleft(t.link_targets) - stack.extendleft(t.link_whole_targets) + add_linked_targets(t) yield t assert target not in seen, 'should not have self' diff --git a/test cases/fortran/24 both_libraries/main.f90 b/test cases/fortran/24 both_libraries/main.f90 new file mode 100644 index 000000000000..bc2a09fea14d --- /dev/null +++ b/test cases/fortran/24 both_libraries/main.f90 @@ -0,0 +1,5 @@ +program main + use mymod, only: myval + implicit none + print *, myval +end program main diff --git a/test cases/fortran/24 both_libraries/meson.build b/test cases/fortran/24 both_libraries/meson.build new file mode 100644 index 000000000000..c90d7e72b62e --- /dev/null +++ b/test cases/fortran/24 both_libraries/meson.build @@ -0,0 +1,6 @@ +project('fmodbug', 'fortran', default_options: ['default_library=both']) + +lib = library('mylib', 'modsrc.f90') +# CI also runs with other default_library settings +lib_linked = get_option('default_library') == 'both' ? lib.get_static_lib() : lib +exe = executable('myexe', 'main.f90', link_with: lib_linked) diff --git a/test cases/fortran/24 both_libraries/modsrc.f90 b/test cases/fortran/24 both_libraries/modsrc.f90 new file mode 100644 index 000000000000..e4d25869e6f4 --- /dev/null +++ b/test cases/fortran/24 both_libraries/modsrc.f90 @@ -0,0 +1,4 @@ +module mymod + implicit none + integer, parameter :: myval = 42 +end module mymod From a77806c25840fdf8675a91d7955b3695460a48b6 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 25 Aug 2026 09:37:15 +0200 Subject: [PATCH 660/681] backends: include target's own object sources in get_all_linked_targets This removes duplicate code from the ninja backend. Signed-off-by: Paolo Bonzini (cherry picked from commit 495f6d71d3fe2d88c6707f7b62c2dbe864fc79a3) --- mesonbuild/backend/backends.py | 3 +-- mesonbuild/backend/ninjabackend.py | 17 +++++++---------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 64b5e5da8f56..8742e7aa8f20 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -506,8 +506,7 @@ def add_linked_targets(t: build.BuildTarget) -> None: _, od = self.flatten_object_list(t) stack.extendleft(od) - stack.extendleft(target.link_targets) - stack.extendleft(target.link_whole_targets) + add_linked_targets(target) while stack: t = stack.pop() if t in seen or not isinstance(t, build.BuildTarget): diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index ce45d74b6b87..33d3aa140525 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1115,15 +1115,14 @@ def generate_target(self, target: T.Union[build.Target]) -> None: fortran_inc_args: T.List[str] = [] if target.uses_fortran(): - fortran_inc_args = mesonlib.listify([target.compilers['fortran'].get_include_args( - self.get_target_private_dir(t), is_system=False) for t in od if t.uses_fortran()]) - - # add the private directories of all transitive dependencies, which - # are needed for their mod files + # Add the private directories of the target itself, of all + # transitive dependencies, and of anywhere their mod files may + # have actually been compiled (see get_all_linked_targets). fc = target.compilers['fortran'] - for t in self.get_all_linked_targets(target): - fortran_inc_args.extend(fc.get_include_args( - self.get_target_private_dir(t), False)) + fortran_inc_args = mesonlib.listify([fc.get_include_args( + self.get_target_private_dir(t), is_system=False) + for t in self.get_all_linked_targets(target) + if t.uses_fortran()]) # Generate compilation targets for sources generated by transpilers. # @@ -1273,8 +1272,6 @@ def generate_dependency_scan_target(self, target: build.BuildTarget, if self.should_use_dyndeps_for_target(t): assert isinstance(t, build.BuildTarget) infiles.add(self.get_dep_scan_file_for(t)[0]) - _, od = self.flatten_object_list(target) - infiles.update({self.get_dep_scan_file_for(t)[0] for t in od if t.uses_fortran()}) elem = NinjaBuildElement(self.all_outputs, depscan_file, 'depaccumulate', [json_file] + sorted(infiles)) elem.add_item('name', target.name) From f86395001f43df49adbc90c6039a22148fb15693 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 25 Aug 2026 11:33:55 +0200 Subject: [PATCH 661/681] build: ExtractedObjects are non-recursive by default Because extract_objects() will only look for sources, and not for anything passed in `objects` for the target, it is by definition non-recursive. Only extract_all_objects() can return recursive ExtractedObjects. This ensures that all the recursive calls of _flatten_object_list() receive exactly the same objlist argument for a given target, instead of also getting recursive calls with empty objlists. Signed-off-by: Paolo Bonzini --- mesonbuild/build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 71ee4d45cb2b..6b502e809e62 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -592,7 +592,7 @@ class ExtractedObjects(HoldableObject): srclist: T.List[File] = field(default_factory=list) genlist: T.List['GeneratedTypes'] = field(default_factory=list) objlist: T.List[ObjectTypes] = field(default_factory=list) - recursive: bool = True + recursive: bool = False pch: bool = False def __repr__(self) -> str: From 55a69fc3ba878f7da8a7744690afb45aa66afa4e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 25 Aug 2026 11:26:04 +0200 Subject: [PATCH 662/681] backends: remove unused argument --- mesonbuild/backend/backends.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 8742e7aa8f20..83c0ba417847 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -518,15 +518,14 @@ def add_linked_targets(t: build.BuildTarget) -> None: def flatten_object_list(self, target: build.BuildTarget, proj_dir_to_build_root: str = '' ) -> T.Tuple[T.List[str], T.List[build.BuildTarget]]: - obj_list, deps = self._flatten_object_list(target, target.get_objects(), proj_dir_to_build_root) + obj_list, deps = self._flatten_object_list(target.get_objects(), proj_dir_to_build_root) return unique_list(obj_list), deps def determine_ext_objs(self, objects: build.ExtractedObjects) -> T.List[str]: - obj_list, _ = self._flatten_object_list(objects.target, [objects], '') + obj_list, _ = self._flatten_object_list([objects], '') return unique_list(obj_list) - def _flatten_object_list(self, target: build.BuildTarget, - objects: T.Sequence[build.ObjectTypes], + def _flatten_object_list(self, objects: T.Sequence[build.ObjectTypes], proj_dir_to_build_root: str) -> T.Tuple[T.List[str], T.List[build.BuildTarget]]: obj_list: T.List[str] = [] deps: T.List[build.BuildTarget] = [] @@ -542,7 +541,7 @@ def _flatten_object_list(self, target: build.BuildTarget, obj_list.append(obj.rel_to_builddir(o)) elif isinstance(obj, build.ExtractedObjects): if obj.recursive: - objs, d = self._flatten_object_list(obj.target, obj.objlist, proj_dir_to_build_root) + objs, d = self._flatten_object_list(obj.objlist, proj_dir_to_build_root) obj_list.extend(objs) deps.extend(d) new_objs = self._determine_ext_objs(obj) From 854409c5daf24b105a1d98718d76b53fbcfdf112 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 25 Aug 2026 11:26:54 +0200 Subject: [PATCH 663/681] remove exponential worst case in _flatten_object_list _flatten_object_list can reach the same target through multiple ExtractObjects, which causes an exponential blowup in the number of recursive calls. Introduce a set of visited dependencies (which has to be returned anyway) and use it to ensure that each distinct target is visited exactly once. Depth-first order (aka post-order) ensures that each target using extract_all_objects(recursive: true) is visited after the target providing the objects, so perform a DFS and flatten each target one by one: later visits will find the result of earlier visits already. --- mesonbuild/backend/backends.py | 80 ++++++++++++------- mesonbuild/backend/ninjabackend.py | 7 +- .../common/302 repeated objects/meson.build | 22 +++++ test cases/common/302 repeated objects/src.c | 1 + .../common/302 repeated objects/test.json | 5 ++ 5 files changed, 84 insertions(+), 31 deletions(-) create mode 100644 test cases/common/302 repeated objects/meson.build create mode 100644 test cases/common/302 repeated objects/src.c create mode 100644 test cases/common/302 repeated objects/test.json diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 83c0ba417847..46f63b7c6410 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -517,7 +517,7 @@ def add_linked_targets(t: build.BuildTarget) -> None: assert target not in seen, 'should not have self' def flatten_object_list(self, target: build.BuildTarget, proj_dir_to_build_root: str = '' - ) -> T.Tuple[T.List[str], T.List[build.BuildTarget]]: + ) -> T.Tuple[T.List[str], T.Iterable[build.BuildTarget]]: obj_list, deps = self._flatten_object_list(target.get_objects(), proj_dir_to_build_root) return unique_list(obj_list), deps @@ -526,34 +526,58 @@ def determine_ext_objs(self, objects: build.ExtractedObjects) -> T.List[str]: return unique_list(obj_list) def _flatten_object_list(self, objects: T.Sequence[build.ObjectTypes], - proj_dir_to_build_root: str) -> T.Tuple[T.List[str], T.List[build.BuildTarget]]: - obj_list: T.List[str] = [] - deps: T.List[build.BuildTarget] = [] - for obj in objects: - if isinstance(obj, mesonlib.File): - if obj.is_built: - o = os.path.join(proj_dir_to_build_root, - obj.rel_to_builddir(self.build_to_src)) - obj_list.append(o) - else: - o = os.path.join(proj_dir_to_build_root, - self.build_to_src) - obj_list.append(obj.rel_to_builddir(o)) - elif isinstance(obj, build.ExtractedObjects): - if obj.recursive: - objs, d = self._flatten_object_list(obj.objlist, proj_dir_to_build_root) - obj_list.extend(objs) - deps.extend(d) - new_objs = self._determine_ext_objs(obj) - if proj_dir_to_build_root: - for o in new_objs: - obj_list.append(os.path.join(proj_dir_to_build_root, o)) + proj_dir_to_build_root: str) -> T.Tuple[T.List[str], T.Iterable[build.BuildTarget]]: + # The same target can be reached through multiple ExtractObjects, so + # ensure each distinct target is visited exactly once. + seen: T.Set[build.BuildTarget] = set() + deps: OrderedSet[build.BuildTarget] = OrderedSet() + result: T.Dict[build.BuildTarget, T.List[str]] = {} + + def visit_dfs(o: build.ExtractedObjects) -> T.Iterator[build.BuildTarget]: + t = o.target + deps.add(t) + if o.recursive: + if t in seen: + return + seen.add(t) + for obj in t.get_objects(): + if isinstance(obj, build.ExtractedObjects): + yield from visit_dfs(obj) + yield t + + def flatten_one(objs: T.Sequence[build.ObjectTypes]) -> T.List[str]: + obj_list: T.List[str] = [] + for obj in objs: + if isinstance(obj, mesonlib.File): + if obj.is_built: + o = os.path.join(proj_dir_to_build_root, + obj.rel_to_builddir(self.build_to_src)) + obj_list.append(o) + else: + o = os.path.join(proj_dir_to_build_root, + self.build_to_src) + obj_list.append(obj.rel_to_builddir(o)) + elif isinstance(obj, build.ExtractedObjects): + # Whatever obj.target recursively depends on has already + # been yielded and flattened. + if obj.recursive: + obj_list.extend(result[obj.target]) + new_objs = self._determine_ext_objs(obj) + if proj_dir_to_build_root: + for o in new_objs: + obj_list.append(os.path.join(proj_dir_to_build_root, o)) + else: + obj_list.extend(new_objs) else: - obj_list.extend(new_objs) - deps.append(obj.target) - else: - raise MesonException('Unknown data type in object list.') - return obj_list, deps + raise MesonBugException('Unknown data type in object list.') + return obj_list + + for obj in objects: + if isinstance(obj, build.ExtractedObjects): + for t in visit_dfs(obj): + result[t] = flatten_one(t.get_objects()) + + return flatten_one(objects), deps @staticmethod def is_swift_target(target: build.BuildTargetTypes) -> bool: diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 33d3aa140525..5b4b070a7d63 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -2673,15 +2673,16 @@ def use_dyndeps_for_fortran(self) -> bool: minimum version is bumped to 1.10.''' return self.ninja_has_dyndeps - def get_fortran_order_deps(self, deps: T.List[build.BuildTarget]) -> T.List[File]: + def get_fortran_order_deps(self, deps: T.Iterable[build.BuildTarget]) -> T.List[File]: # We don't need this order dep if we're using dyndeps, as the # depscanner will handle this for us, which produces a better dependency # graph if self.use_dyndeps_for_fortran(): return [] - return [File(True, *os.path.split(self.get_target_filename(t))) for t in deps - if t.uses_fortran()] + files: T.List[str] = [self.get_target_filename(t) for t in deps if t.uses_fortran()] + files.sort() + return [File(True, *os.path.split(f)) for f in files] def generate_fortran_dep_hack(self, crstr: str) -> None: if self.use_dyndeps_for_fortran(): diff --git a/test cases/common/302 repeated objects/meson.build b/test cases/common/302 repeated objects/meson.build new file mode 100644 index 000000000000..ed78ae27df01 --- /dev/null +++ b/test cases/common/302 repeated objects/meson.build @@ -0,0 +1,22 @@ +# this takes exponential time unless _flatten_object_list uses a worklist +project('repeated objects', 'c') +l0 = static_library('l0', 'src.c', install: true) +l1 = static_library('l1', objects: [l0.extract_all_objects(recursive: true), l0.extract_all_objects(recursive: true)]) +l2 = static_library('l2', objects: [l1.extract_all_objects(recursive: true), l1.extract_all_objects(recursive: true)]) +l3 = static_library('l3', objects: [l2.extract_all_objects(recursive: true), l2.extract_all_objects(recursive: true)]) +l4 = static_library('l4', objects: [l3.extract_all_objects(recursive: true), l3.extract_all_objects(recursive: true)]) +l5 = static_library('l5', objects: [l4.extract_all_objects(recursive: true), l4.extract_all_objects(recursive: true)]) +l6 = static_library('l6', objects: [l5.extract_all_objects(recursive: true), l5.extract_all_objects(recursive: true)]) +l7 = static_library('l7', objects: [l6.extract_all_objects(recursive: true), l6.extract_all_objects(recursive: true)]) +l8 = static_library('l8', objects: [l7.extract_all_objects(recursive: true), l7.extract_all_objects(recursive: true)]) +l9 = static_library('l9', objects: [l8.extract_all_objects(recursive: true), l8.extract_all_objects(recursive: true)]) +l10 = static_library('l10', objects: [l9.extract_all_objects(recursive: true), l9.extract_all_objects(recursive: true)]) +l11 = static_library('l11', objects: [l10.extract_all_objects(recursive: true), l10.extract_all_objects(recursive: true)]) +l12 = static_library('l12', objects: [l11.extract_all_objects(recursive: true), l11.extract_all_objects(recursive: true)]) +l13 = static_library('l13', objects: [l12.extract_all_objects(recursive: true), l12.extract_all_objects(recursive: true)]) +l14 = static_library('l14', objects: [l13.extract_all_objects(recursive: true), l13.extract_all_objects(recursive: true)]) +l15 = static_library('l15', objects: [l14.extract_all_objects(recursive: true), l14.extract_all_objects(recursive: true)]) +l16 = static_library('l16', objects: [l15.extract_all_objects(recursive: true), l15.extract_all_objects(recursive: true)]) +l17 = static_library('l17', objects: [l16.extract_all_objects(recursive: true), l16.extract_all_objects(recursive: true)]) +l18 = static_library('l18', objects: [l17.extract_all_objects(recursive: true), l17.extract_all_objects(recursive: true)]) +l19 = static_library('l19', objects: [l18.extract_all_objects(recursive: true), l18.extract_all_objects(recursive: true)]) diff --git a/test cases/common/302 repeated objects/src.c b/test cases/common/302 repeated objects/src.c new file mode 100644 index 000000000000..35844c07a2fe --- /dev/null +++ b/test cases/common/302 repeated objects/src.c @@ -0,0 +1 @@ +int f(void) { return 0; } diff --git a/test cases/common/302 repeated objects/test.json b/test cases/common/302 repeated objects/test.json new file mode 100644 index 000000000000..d5568f83f392 --- /dev/null +++ b/test cases/common/302 repeated objects/test.json @@ -0,0 +1,5 @@ +{ + "installed": [ + { "type": "file", "file": "usr/lib/libl0.a" } + ] +} From 881da62166fe14c35d4d1e6e85961e6005a7a0ff Mon Sep 17 00:00:00 2001 From: Tristan Partin Date: Fri, 28 Aug 2026 03:38:42 +0000 Subject: [PATCH 664/681] Add libclang-rt-dev to ubuntu-rolling Debian/Ubuntu have changed their packaging such that libclang-dev no longer includes libclang-rt-dev. We require the libraries in libclang-rt-dev to run some of our tests. Signed-off-by: Tristan Partin Part-of: https://github.com/mesonbuild/meson/pull/16165 Closes: https://github.com/mesonbuild/meson/pull/16165 Signed-off-by: Sam James (cherry picked from commit 34767edec979ba4cc4ea4b6671f0d1aa8845db5c) --- ci/ciimage/ubuntu-rolling/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/ciimage/ubuntu-rolling/install.sh b/ci/ciimage/ubuntu-rolling/install.sh index 6ffea7041fc3..ef6d776b8065 100755 --- a/ci/ciimage/ubuntu-rolling/install.sh +++ b/ci/ciimage/ubuntu-rolling/install.sh @@ -17,7 +17,7 @@ pkgs=( llvm lcov dub ldc mingw-w64 mingw-w64-tools libz-mingw-w64-dev - libclang-dev + libclang-dev libclang-rt-dev libgcrypt20-dev libgpgme-dev libhdf5-dev From 9375d10196ef5e0a08a71ff91e8e3431b00d71bd Mon Sep 17 00:00:00 2001 From: Lucas Colley Date: Fri, 28 Aug 2026 09:55:56 +0100 Subject: [PATCH 665/681] compilers(MSVC): use `/Fe` for `.dll` too (cherry picked from commit f3b78ee4e0994f7cb6eba4220d9d5b6a607de7ea) --- mesonbuild/compilers/mixins/visualstudio.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mesonbuild/compilers/mixins/visualstudio.py b/mesonbuild/compilers/mixins/visualstudio.py index 365421e100f8..be9bf11d2bd4 100644 --- a/mesonbuild/compilers/mixins/visualstudio.py +++ b/mesonbuild/compilers/mixins/visualstudio.py @@ -171,7 +171,8 @@ def sanitizer_compile_args(self, target: T.Optional[BuildTarget], value: T.List[ def get_output_args(self, outputname: str) -> T.List[str]: if self.mode == 'PREPROCESSOR': return ['/Fi' + outputname] - if outputname.endswith('.exe'): + # hack: this list is not exhaustive + if outputname.endswith(('.exe', '.dll')): return ['/Fe' + outputname] return ['/Fo' + outputname] From d46cea436ab8c8500c6d592394e3bc8bbdbd2567 Mon Sep 17 00:00:00 2001 From: Tristan Partin Date: Fri, 28 Aug 2026 14:46:40 +0000 Subject: [PATCH 666/681] Add LLVM 23 version suffixes Signed-off-by: Tristan Partin (cherry picked from commit 570963b9ea08edaefc3e6b3cf5bb48ca56759594) --- mesonbuild/tooldetect.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mesonbuild/tooldetect.py b/mesonbuild/tooldetect.py index cc8e8ddf6af3..967d7ef5e783 100644 --- a/mesonbuild/tooldetect.py +++ b/mesonbuild/tooldetect.py @@ -140,6 +140,8 @@ def get_llvm_tool_names(tool: str) -> T.List[str]: # unless it becomes a stable release. suffixes = [ '', # base (no suffix) + '-23.1', '23.1', + '-23', '23', '-22.1', '22.1', '-22', '22', '-21.1', '21.1', From 11a4ffa9f07d7bc155c4abec5212790815ffc7a8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 31 Aug 2026 09:57:55 +0200 Subject: [PATCH 667/681] Revert "cargo: Use --env-set when we have nightly rustc" This reverts commit c63cfd95b3b93a25f1c8ac490603c37124ba20f0. --env-set is being dropped from rustc. It can be reapplied once Meson starts supporting environment arguments natively. (cherry picked from commit cb59331b47aa1e502d5b49d92e7c75bdf7829e61) --- mesonbuild/cargo/interpreter.py | 13 ------------- mesonbuild/compilers/rust.py | 9 --------- 2 files changed, 22 deletions(-) diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index 7b33dcc60c7c..c063a6d0c5cd 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -177,18 +177,6 @@ def get_lint_args(self, rustc: RustCompiler) -> T.List[str]: return args - def get_env_args(self, rustc: RustCompiler, environment: Environment, subdir: str) -> T.List[str]: - """Get environment variable arguments for rustc.""" - enable_env_set_args = rustc.enable_env_set_args() - if enable_env_set_args is None: - return [] - - env_dict = self.get_env_dict(environment, subdir) - env_args = list(enable_env_set_args) - for k, v in env_dict.items(): - env_args.extend(['--env-set', f'{k}={v}']) - return env_args - def get_rustc_args(self, environment: Environment, subdir: str, machine: MachineChoice) -> T.List[str]: """Get rustc arguments for this package.""" if not environment.is_cross_build(): @@ -200,7 +188,6 @@ def get_rustc_args(self, environment: Environment, subdir: str, machine: Machine args: T.List[str] = [] args.extend(self.get_lint_args(rustc)) args.extend(cfg.get_features_args()) - args.extend(self.get_env_args(rustc, environment, subdir)) return args def supported_abis(self) -> T.Set[RUST_ABI]: diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index b6109c798c9e..9f618778be14 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -567,15 +567,6 @@ def get_rustdoc(self) -> T.Optional[RustdocTestCompiler]: full_version=self.full_version, linker=self.linker, rustc=self) - def enable_env_set_args(self) -> T.Optional[T.List[str]]: - '''Extra arguments to enable --env-set support in rustc. - Returns None if not supported. - ''' - if version_compare(self.version, '>= 1.76') and self.allow_nightly: - return ['-Z', 'unstable-options'] - return None - - class ClippyRustCompiler(RustCompiler): """Clippy is a linter that wraps Rustc. From 594b12f7cbdcc45bf7b9f0f3f4ad9c36641ba760 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 4 Sep 2026 18:39:14 +0000 Subject: [PATCH 668/681] backends/vs: fix potential infinite loop We would just run a loop of `x = y while x exists { x = f'{y}0' }` which... means if `f'{y}0` exists we'll loop forever. (cherry picked from commit 4d5484cedfd694a363377c9b4140e0fd2f84738b) --- mesonbuild/backend/vs2010backend.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 924764e05fec..ee42934fa624 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -2180,7 +2180,8 @@ def nonexistent_file(prefix: str) -> str: i = 0 file = prefix while os.path.exists(file): - file = '%s%d' % (prefix, i) + file = f'{prefix}{i}' + i += 1 return file def generate_debug_information(self, link: ET.Element) -> None: From b66a1a81121abeb40bce9fd1650a6e7cb8db4cd2 Mon Sep 17 00:00:00 2001 From: Tristan Partin Date: Mon, 24 Aug 2026 20:48:09 +0000 Subject: [PATCH 669/681] Stop passing link arguments to compile-only sanity checks There is no need to pass link-only arguments where we are only compiling. This has led to real world issues: Clang will emit a warning when unused arguments are passed on the command line: clang: warning: argument unused during compilation: '-L/tmp/opencode' [-Wunused-command-line-argument] This is a bit of a hard warning to trigger. The following is required: 1. Clang or Clang-derived compiler (clang-cl) 2. A cross build 3. No exe wrapper configured 4. c_link_args is non-empty When (2) and (3) hold, the compile mode becomes CompileCheckMode.COMPILE, which means we pass -c to clang's sanity check command. When -c is passed to clang, it ignores linker arguments like -L. Signed-off-by: Tristan Partin Part-of: https://github.com/mesonbuild/meson/pull/16142 Closes: https://github.com/mesonbuild/meson/pull/16142 Signed-off-by: Sam James (cherry picked from commit c9bb03c2d1236ead370772f101891c670c2e54cf) --- mesonbuild/compilers/mixins/clike.py | 11 ++++++++++- unittests/internaltests.py | 21 ++++++++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py index a50007c13625..f0a2392bf49e 100644 --- a/mesonbuild/compilers/mixins/clike.py +++ b/mesonbuild/compilers/mixins/clike.py @@ -281,8 +281,17 @@ def _sanity_check_compile_args(self, sourcename: str, binname: str # Cross-compiling is hard. For example, you might need -nostdlib, or to pass --target, etc. mode = CompileCheckMode.COMPILE if self.is_cross and not self.environment.has_exe_wrapper() else CompileCheckMode.LINK cargs, b_largs = self._get_basic_compiler_args(mode) - largs = self.linker_to_compiler_args(b_largs) s_args, s_largs = super()._sanity_check_compile_args(sourcename, binname) + if mode is CompileCheckMode.COMPILE: + # We aren't linking in this invocation (and can't run the result + # without an exe wrapper anyway), so don't pass any link-only + # arguments to a compile-only command. Some compilers add fixed + # flags (e.g. MSVC-style compilers always prepend /link) even + # when there is nothing to link, which would otherwise end up + # unused/misplaced in a compile-only invocation. + return s_args + cargs, [] + + largs = self.linker_to_compiler_args(b_largs) return s_args + cargs, s_largs + largs def check_header(self, hname: str, prefix: str, *, diff --git a/unittests/internaltests.py b/unittests/internaltests.py index 0848b75abfe7..94cf5424f48d 100644 --- a/unittests/internaltests.py +++ b/unittests/internaltests.py @@ -28,7 +28,7 @@ import mesonbuild.scripts.env2mfile from mesonbuild import coredata from mesonbuild.compilers.c import ClangCCompiler, GnuCCompiler -from mesonbuild.compilers.compilers import ManyInOneLinkerOptionStyle +from mesonbuild.compilers.compilers import Compiler, ManyInOneLinkerOptionStyle from mesonbuild.compilers.cpp import VisualStudioCPPCompiler from mesonbuild.compilers.d import DmdDCompiler from mesonbuild.compilers.detect import detect_c_compiler @@ -322,6 +322,25 @@ def test_compiler_args_class_visualstudio(self): self.assertEqual(a.to_native(copy=True), ['/showIncludes']) + def test_clike_sanity_check_drops_link_only_args_when_compile_only(self): + # When cross-compiling without an exe wrapper, CLikeCompiler's sanity + # check only compiles and never links because we can't run the + # executable. Therefore, it doesn't make sense for link-only arguments + # to be added on the command line. Some compilers warn about unused + # command line arguments. + env = get_fake_env() + linker = linkers.MoldDynamicLinker([], env, MachineChoice.HOST, '-Wl,', []) + cc = ClangCCompiler([], [], '14.0.0', MachineChoice.HOST, env, linker=linker) + cc.is_cross = True + + with mock.patch.object(env, 'has_exe_wrapper', return_value=False), \ + mock.patch.object(cc, '_get_basic_compiler_args', return_value=([], ['-fake-cross-link-arg'])), \ + mock.patch.object(Compiler, '_sanity_check_compile_args', return_value=([], ['-Lfake-ldflags-arg'])): + _, largs = cc._sanity_check_compile_args('foo.c', 'foo.exe') + + self.assertEqual(largs, []) + + def test_msvc_unix_args_to_native(self): # joined self.assertEqual(MSVCCompiler.unix_args_to_native(['-isystemfoo']), ['/Ifoo']) From c74b7b114b1b96f20e15b8a9e90e03ca44b63dbd Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 28 Aug 2026 12:24:22 +0200 Subject: [PATCH 670/681] unittests: check that linker always args reach compile checks Commit 300d7c39fca1 ("compilers: add link always args if CompileCheckMode.LINK") made has_function() and friends pass the linker's always args, so that e.g. -Zomf is not missing on OS/2 when a dependency is used. Before changing where those arguments are added, add a test. Signed-off-by: Paolo Bonzini --- unittests/internaltests.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/unittests/internaltests.py b/unittests/internaltests.py index 94cf5424f48d..f2380d740447 100644 --- a/unittests/internaltests.py +++ b/unittests/internaltests.py @@ -28,7 +28,7 @@ import mesonbuild.scripts.env2mfile from mesonbuild import coredata from mesonbuild.compilers.c import ClangCCompiler, GnuCCompiler -from mesonbuild.compilers.compilers import Compiler, ManyInOneLinkerOptionStyle +from mesonbuild.compilers.compilers import Compiler, CompileCheckMode, ManyInOneLinkerOptionStyle from mesonbuild.compilers.cpp import VisualStudioCPPCompiler from mesonbuild.compilers.d import DmdDCompiler from mesonbuild.compilers.detect import detect_c_compiler @@ -373,6 +373,22 @@ def test_clangcl_unix_args_to_native(self): self.assertEqual(ClangClCompiler.unix_args_to_native(['-idirafter', 'foo']), ['/clang:-idirafterfoo']) self.assertEqual(ClangClCompiler.unix_args_to_native(['-iquote', 'foo']), ['/clang:-iquotefoo']) + def _fake_gnu_cc(self, cflags='-DCFLAG', ldflags='-Wl,-O1', + linker_cls=linkers.GnuBFDDynamicLinker): + with mock.patch.dict(os.environ, {'CFLAGS': cflags, 'LDFLAGS': ldflags}): + env = get_fake_env() + env.add_lang_args('c', GnuCCompiler, MachineChoice.HOST) + linker = linker_cls([], env, MachineChoice.HOST, + ManyInOneLinkerOptionStyle('-Wl,', ','), []) + return GnuCCompiler([], [], 'fake', MachineChoice.HOST, env, linker=linker) + + def test_compiler_check_args_os2(self): + # the linker's always args (-Zomf on OS/2) must reach link mode checks + cc = self._fake_gnu_cc(linker_cls=linkers.OS2OmfDynamicLinker) + args = cc.build_wrapper_args(None, None, CompileCheckMode.LINK) + self.assertEqual(list(args).count('-Zomf'), 1, args) + self.assertNotIn('-Zomf', cc.build_wrapper_args(None, None, CompileCheckMode.COMPILE)) + def test_compiler_args_class_gnuld(self): ## Test --start/end-group env = get_fake_env() From 59129dce68ea359adb85e5e5a1cb361d249182b4 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 28 Aug 2026 12:00:29 +0200 Subject: [PATCH 671/681] compilers: introduce _sanity_check_mode Commit c9bb03c2d ("Stop passing link arguments to compile-only sanity checks", 2026-08-28) dropped link arguments from compile-only checks for CLikeCompiler. Introduce Compiler._sanity_check_mode() to move this logic up to Compiler and so that it applies also to the super() call of CLikeCompiler._sanity_check_compile_args(). Signed-off-by: Paolo Bonzini --- mesonbuild/compilers/compilers.py | 11 ++++++++++- mesonbuild/compilers/cuda.py | 18 ++++++++++++------ mesonbuild/compilers/cython.py | 7 ++++++- mesonbuild/compilers/mixins/clike.py | 9 +++++++-- mesonbuild/compilers/vala.py | 5 +++++ 5 files changed, 40 insertions(+), 10 deletions(-) diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 518ea0deb496..4a5854bcafd9 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -1428,6 +1428,10 @@ def _transpiled_sanity_check_compile_args( """ return compiler._sanity_check_compile_args(sourcename, binname) + def _sanity_check_mode(self) -> CompileCheckMode: + """Whether the sanity check links a binary, or only compiles one.""" + return CompileCheckMode.LINK + def _sanity_check_compile_args(self, sourcename: str, binname: str ) -> T.Tuple[T.List[str], T.List[str]]: """Get arguments to run compiler for sanity check. @@ -1440,9 +1444,14 @@ def _sanity_check_compile_args(self, sourcename: str, binname: str :return: a tuple of arguments, the first is the executable and compiler arguments, the second is linker arguments """ + mode = self._sanity_check_mode() + cargs = list(self.environment.coredata.get_external_args(self.for_machine, self.language)) + cargs = self.exelist_no_ccache + self.get_always_args() + self.get_output_args(binname) + [sourcename] + cargs + if mode is CompileCheckMode.COMPILE: + return cargs, [] largs = list(self.environment.coredata.get_external_link_args(self.for_machine, self.language)) - return self.exelist_no_ccache + self.get_always_args() + self.get_output_args(binname) + [sourcename] + cargs, largs + return cargs, largs @abc.abstractmethod def _sanity_check_source_code(self) -> str: diff --git a/mesonbuild/compilers/cuda.py b/mesonbuild/compilers/cuda.py index 4f29498a3987..a5f8ff27222a 100644 --- a/mesonbuild/compilers/cuda.py +++ b/mesonbuild/compilers/cuda.py @@ -528,6 +528,16 @@ def _sanity_check_source_code(self) -> str: } ''' + def _sanity_check_mode(self) -> CompileCheckMode: + # Linking cross built apps is painful. You can't really + # tell if you should use -nostdlib or not and for example + # on OSX the compiler binary is the same but you need + # a ton of compiler flags to differentiate between + # arm and x86_64. So just compile. + if self.is_cross and not self.environment.has_exe_wrapper(): + return CompileCheckMode.COMPILE + return CompileCheckMode.LINK + def _sanity_check_compile_args(self, sourcename: str, binname: str ) -> T.Tuple[T.List[str], T.List[str]]: # Disable warnings, compile with statically-linked runtime for minimum @@ -540,12 +550,8 @@ def _sanity_check_compile_args(self, sourcename: str, binname: str flags += self._get_ccbin_args(None, '') # If cross-compiling, we can't run the sanity check, only compile it. - if self.is_cross and not self.environment.has_exe_wrapper(): - # Linking cross built apps is painful. You can't really - # tell if you should use -nostdlib or not and for example - # on OSX the compiler binary is the same but you need - # a ton of compiler flags to differentiate between - # arm and x86_64. So just compile. + mode = self._sanity_check_mode() + if mode is CompileCheckMode.COMPILE: flags += self.get_compile_only_args() flags += self.get_output_args(binname) diff --git a/mesonbuild/compilers/cython.py b/mesonbuild/compilers/cython.py index e3e5a3ef601f..58654ae03380 100644 --- a/mesonbuild/compilers/cython.py +++ b/mesonbuild/compilers/cython.py @@ -10,7 +10,7 @@ from .. import options from .. import mlog from ..mesonlib import version_compare, EnvironmentException -from .compilers import Compiler +from .compilers import CompileCheckMode, Compiler if T.TYPE_CHECKING: from ..options import MutableKeyedOptionDictType @@ -89,6 +89,11 @@ def _transpiled_sanity_check_compile_args( largs.extend(compiler.get_allow_undefined_link_args()) return args, largs + def _sanity_check_mode(self) -> CompileCheckMode: + # the sanity check only transpiles, the generated source is compiled + # and linked by _transpiled_sanity_check_compile_args() + return CompileCheckMode.COMPILE + def _sanity_check_compile_args(self, sourcename: str, binname: str ) -> T.Tuple[T.List[str], T.List[str]]: args, largs = super()._sanity_check_compile_args(sourcename, binname) diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py index f0a2392bf49e..c34a3db566ea 100644 --- a/mesonbuild/compilers/mixins/clike.py +++ b/mesonbuild/compilers/mixins/clike.py @@ -276,10 +276,15 @@ def gen_export_dynamic_link_args(self) -> T.List[str]: def gen_import_library_args(self, implibname: str) -> T.List[str]: return self.linker.import_library_args(implibname) + def _sanity_check_mode(self) -> CompileCheckMode: + # Cross-compiling is hard. For example, you might need -nostdlib, or to pass --target, etc. + if self.is_cross and not self.environment.has_exe_wrapper(): + return CompileCheckMode.COMPILE + return CompileCheckMode.LINK + def _sanity_check_compile_args(self, sourcename: str, binname: str ) -> T.Tuple[T.List[str], T.List[str]]: - # Cross-compiling is hard. For example, you might need -nostdlib, or to pass --target, etc. - mode = CompileCheckMode.COMPILE if self.is_cross and not self.environment.has_exe_wrapper() else CompileCheckMode.LINK + mode = self._sanity_check_mode() cargs, b_largs = self._get_basic_compiler_args(mode) s_args, s_largs = super()._sanity_check_compile_args(sourcename, binname) if mode is CompileCheckMode.COMPILE: diff --git a/mesonbuild/compilers/vala.py b/mesonbuild/compilers/vala.py index be5c1b0b44a5..4b24ee2ea6b2 100644 --- a/mesonbuild/compilers/vala.py +++ b/mesonbuild/compilers/vala.py @@ -110,6 +110,11 @@ def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str], def _sanity_check_source_code(self) -> str: return 'public static int main() { return 0; }' + def _sanity_check_mode(self) -> CompileCheckMode: + # the sanity check only transpiles, the generated source is compiled + # and linked by _transpiled_sanity_check_compile_args() + return CompileCheckMode.COMPILE + def _sanity_check_compile_args(self, sourcename: str, binname: str ) -> T.Tuple[T.List[str], T.List[str]]: args, largs = super()._sanity_check_compile_args(sourcename, binname) From 991ee94fe1544dc372c7d72908e7e0db630ec872 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 28 Aug 2026 12:54:31 +0200 Subject: [PATCH 672/681] clike: do not add external arguments twice in the sanity check Commit 797decc3e4782 ("compilers: pass external args to sanity check") made Compiler._sanity_check_compile_args() add {c,cpp}_args and {c,cpp}_link_args, assuming the extra copies on the CLike path were harmless because "arglist deduplicates them", but the sanity check builds a plain list so nothing is deduplicated. Furthermore, the copy of linker arguments in s_largs would end up in front of the /link that linker_to_compiler_args() inserts for MSVC-style compilers. For example, with c_link_args=['/SUBSYSTEM:CONSOLE'] the probe became cl ... /c /SUBSYSTEM:CONSOLE /link /SUBSYSTEM:CONSOLE (the first from super()._sanity_check_compile_args, the second from self._get_basic_compiler_args) instead of cl ... /c /link /SUBSYSTEM:CONSOLE Stop using super()._sanity_check_compile_args() to remove the duplication while keeping control of the position of the linker arguments. Note that get_always_args() was also duplicated because _get_basic_compiler_args() returns it via get_compiler_args_for_mode(), so drop it as well. The effect is to remove the copy of get_always_args() that was previously in front of the output argument, and only keep the one after the source file, but this is harmless. Fixes: #16111 Fixes: 797decc3e4782 ("compilers: pass external args to sanity check") Signed-off-by: Paolo Bonzini --- mesonbuild/compilers/mixins/clike.py | 12 ++++++++---- unittests/internaltests.py | 8 ++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py index c34a3db566ea..ba0d7c81090e 100644 --- a/mesonbuild/compilers/mixins/clike.py +++ b/mesonbuild/compilers/mixins/clike.py @@ -284,9 +284,13 @@ def _sanity_check_mode(self) -> CompileCheckMode: def _sanity_check_compile_args(self, sourcename: str, binname: str ) -> T.Tuple[T.List[str], T.List[str]]: + # _get_basic_compiler_args() already adds c_args/c_link_args (or + # similar). Calling super()._sanity_check_compile_args() would + # duplicate them and, for MSVC-like compilers, place the link + # arguments before the /link that linker_to_compiler_args() inserts. mode = self._sanity_check_mode() - cargs, b_largs = self._get_basic_compiler_args(mode) - s_args, s_largs = super()._sanity_check_compile_args(sourcename, binname) + b_cargs, b_largs = self._get_basic_compiler_args(mode) + cargs = self.exelist_no_ccache + self.get_output_args(binname) + [sourcename] + b_cargs if mode is CompileCheckMode.COMPILE: # We aren't linking in this invocation (and can't run the result # without an exe wrapper anyway), so don't pass any link-only @@ -294,10 +298,10 @@ def _sanity_check_compile_args(self, sourcename: str, binname: str # flags (e.g. MSVC-style compilers always prepend /link) even # when there is nothing to link, which would otherwise end up # unused/misplaced in a compile-only invocation. - return s_args + cargs, [] + return cargs, [] largs = self.linker_to_compiler_args(b_largs) - return s_args + cargs, s_largs + largs + return cargs, largs def check_header(self, hname: str, prefix: str, *, extra_args: T.Union[None, T.List[str], T.Callable[['CompileCheckMode'], T.List[str]]] = None, diff --git a/unittests/internaltests.py b/unittests/internaltests.py index f2380d740447..0d0fb96f7a3a 100644 --- a/unittests/internaltests.py +++ b/unittests/internaltests.py @@ -382,6 +382,14 @@ def _fake_gnu_cc(self, cflags='-DCFLAG', ldflags='-Wl,-O1', ManyInOneLinkerOptionStyle('-Wl,', ','), []) return GnuCCompiler([], [], 'fake', MachineChoice.HOST, env, linker=linker) + def test_sanity_check_args_gnu(self): + cc = self._fake_gnu_cc() + args, largs = cc._sanity_check_compile_args('t.c', 't.exe') + # external args must reach the probe, but only once + self.assertEqual(args.count('-DCFLAG'), 1, args) + self.assertEqual((args + largs).count('-Wl,-O1'), 1, (args, largs)) + self.assertIn('-Wl,-O1', largs) + def test_compiler_check_args_os2(self): # the linker's always args (-Zomf on OS/2) must reach link mode checks cc = self._fake_gnu_cc(linker_cls=linkers.OS2OmfDynamicLinker) From 1a2843272a0551cae419e7b8ae78a6b3b95430d5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 28 Aug 2026 15:27:17 +0200 Subject: [PATCH 673/681] unittests: add test that find_library receives the link_always_args Right now they are passed twice, but this will be fixed. Add a partial test for now. Signed-off-by: Paolo Bonzini --- unittests/internaltests.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/unittests/internaltests.py b/unittests/internaltests.py index 0d0fb96f7a3a..2a4cd1476e93 100644 --- a/unittests/internaltests.py +++ b/unittests/internaltests.py @@ -27,7 +27,7 @@ import mesonbuild.modules.gnome import mesonbuild.scripts.env2mfile from mesonbuild import coredata -from mesonbuild.compilers.c import ClangCCompiler, GnuCCompiler +from mesonbuild.compilers.c import ClangCCompiler, GnuCCompiler, VisualStudioCCompiler from mesonbuild.compilers.compilers import Compiler, CompileCheckMode, ManyInOneLinkerOptionStyle from mesonbuild.compilers.cpp import VisualStudioCPPCompiler from mesonbuild.compilers.d import DmdDCompiler @@ -373,6 +373,13 @@ def test_clangcl_unix_args_to_native(self): self.assertEqual(ClangClCompiler.unix_args_to_native(['-idirafter', 'foo']), ['/clang:-idirafterfoo']) self.assertEqual(ClangClCompiler.unix_args_to_native(['-iquote', 'foo']), ['/clang:-iquotefoo']) + def _fake_msvc_cc(self, cflags='-DCFLAG', ldflags='/SUBSYSTEM:CONSOLE'): + with mock.patch.dict(os.environ, {'CFLAGS': cflags, 'LDFLAGS': ldflags}): + env = get_fake_env() + env.add_lang_args('c', VisualStudioCCompiler, MachineChoice.HOST) + linker = linkers.MSVCDynamicLinker(env, MachineChoice.HOST, []) + return VisualStudioCCompiler([], [], '20.00', MachineChoice.HOST, env, 'x64', linker=linker) + def _fake_gnu_cc(self, cflags='-DCFLAG', ldflags='-Wl,-O1', linker_cls=linkers.GnuBFDDynamicLinker): with mock.patch.dict(os.environ, {'CFLAGS': cflags, 'LDFLAGS': ldflags}): @@ -397,6 +404,17 @@ def test_compiler_check_args_os2(self): self.assertEqual(list(args).count('-Zomf'), 1, args) self.assertNotIn('-Zomf', cc.build_wrapper_args(None, None, CompileCheckMode.COMPILE)) + def test_find_library_args_msvc(self): + cc = self._fake_msvc_cc() + + def fake_links(code, *, extra_args=None, **kwargs): + args = cc.build_wrapper_args(extra_args, None, CompileCheckMode.LINK).to_native() + self.assertIn('/release', args) + return (True, False) + + with mock.patch.object(cc, 'links', fake_links): + cc.find_library('foo', []) + def test_compiler_args_class_gnuld(self): ## Test --start/end-group env = get_fake_env() From ade542a4bf6d3a7e1ef2f24d1310bde8001e5d9c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 28 Aug 2026 13:17:19 +0200 Subject: [PATCH 674/681] compilers: pass linker always args as linker, not compiler arguments Commit 300d7c39f ("compilers: add link always args if CompileCheckMode.LINK", 2026-01-07) added get_linker_always_args() to get_compiler_args_for_mode(), but that function returns *compiler* arguments, and it is applied twice to a compile check: once by CLikeCompiler._get_basic_compiler_args() and once by Compiler.compile(). For MSVC this puts linker-only flags on the compiler command line twice *and* in the wrong place: cl ... /release /nologo ... /release /nologo ... /link foo.lib Add them to the linker argument list that is passed through linker_to_compiler_args() instead of going through get_compiler_args_for_mode(). While that appears twice (in build_wrapper_args() and in the sanity check), it is not necessary anymore to add linker always args in find_library() now that build_wrapper_args() adds them. Note that this does not include the Ninja backend which has a call to get_linker_always_args() of its own. Fixes: #16086 Fixes: 300d7c39f ("compilers: add link always args if CompileCheckMode.LINK", 2026-01-07) Signed-off-by: Paolo Bonzini --- mesonbuild/compilers/compilers.py | 2 +- mesonbuild/compilers/mixins/clike.py | 6 ++++-- unittests/internaltests.py | 32 +++++++++++++++++++++++++++- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 4a5854bcafd9..76ecae5bbff2 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -909,7 +909,6 @@ def get_compiler_args_for_mode(self, mode: CompileCheckMode) -> T.List[str]: args += self.get_preprocess_only_args() else: assert mode is CompileCheckMode.LINK - args += self.get_linker_always_args() return args def compiler_args(self, args: T.Optional[T.Iterable[str]] = None) -> CompilerArgs: @@ -1598,6 +1597,7 @@ def build_wrapper_args(self, # Add DFLAGS from the env args += self.environment.coredata.get_external_args(self.for_machine, self.language) elif mode is CompileCheckMode.LINK: + args += self.get_linker_always_args() # Add LDFLAGS from the env args += self.environment.coredata.get_external_link_args(self.for_machine, self.language) # extra_args must override all other arguments, so we add them last diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py index ba0d7c81090e..5d72eae875cc 100644 --- a/mesonbuild/compilers/mixins/clike.py +++ b/mesonbuild/compilers/mixins/clike.py @@ -368,6 +368,8 @@ def _get_basic_compiler_args(self, mode: CompileCheckMode) -> T.Tuple[T.List[str cargs += cleaned_sys_args if mode is CompileCheckMode.LINK: + largs += self.get_linker_always_args() + ld_value = self.environment.lookup_binary_entry(self.for_machine, self.language + '_ld') if ld_value is not None: cargs += self.use_linker_args(ld_value[0], self.version) @@ -1133,7 +1135,7 @@ def _find_library_real(self, libname: str, extra_dirs: T.List[str], code: str, l if ((not extra_dirs and libtype is LibType.PREFER_SHARED) or libname in self.internal_libs): cargs = ['-l' + libname] - largs = self.get_linker_always_args() + self.get_allow_undefined_link_args() + largs = self.get_allow_undefined_link_args() extra_args = cargs + self.linker_to_compiler_args(largs) if self.links(code, extra_args=extra_args, disable_cache=True)[0]: @@ -1155,7 +1157,7 @@ def _find_library_real(self, libname: str, extra_dirs: T.List[str], code: str, l except (mesonlib.MesonException, KeyError): # TODO evaluate if catching KeyError is wanted here elf_class = 0 # Search in the specified dirs, and then in the system libraries - largs = self.get_linker_always_args() + self.get_allow_undefined_link_args() + largs = self.get_allow_undefined_link_args() lcargs = self.linker_to_compiler_args(largs) for d in itertools.chain(extra_dirs, [] if ignore_system_dirs else self.get_library_dirs(elf_class)): for p in patterns: diff --git a/unittests/internaltests.py b/unittests/internaltests.py index 2a4cd1476e93..d3368a7d52ce 100644 --- a/unittests/internaltests.py +++ b/unittests/internaltests.py @@ -389,6 +389,26 @@ def _fake_gnu_cc(self, cflags='-DCFLAG', ldflags='-Wl,-O1', ManyInOneLinkerOptionStyle('-Wl,', ','), []) return GnuCCompiler([], [], 'fake', MachineChoice.HOST, env, linker=linker) + def assertAfterLink(self, args: T.List[str], flag: str) -> None: + '''Assert that a linker-only flag is passed exactly once, after /link.''' + self.assertEqual(args.count(flag), 1, f'{flag} not passed exactly once in {args}') + self.assertIn('/link', args) + self.assertLess(args.index('/link'), args.index(flag), f'{flag} passed before /link in {args}') + + def test_sanity_check_args_msvc(self): + cc = self._fake_msvc_cc() + args, largs = cc._sanity_check_compile_args('t.c', 't.exe') + # external link args are passed once, and after /link + self.assertAfterLink(largs, '/SUBSYSTEM:CONSOLE') + self.assertNotIn('/SUBSYSTEM:CONSOLE', args) + # so are the linker's own always args + self.assertAfterLink(largs, '/release') + self.assertNotIn('/release', args) + # external compile args are passed to the compiler, not to the linker + self.assertNotIn('-DCFLAG', largs) + self.assertEqual(args.count('-DCFLAG'), 1, args) + self.assertIn('/Fet.exe', args) + def test_sanity_check_args_gnu(self): cc = self._fake_gnu_cc() args, largs = cc._sanity_check_compile_args('t.c', 't.exe') @@ -397,6 +417,16 @@ def test_sanity_check_args_gnu(self): self.assertEqual((args + largs).count('-Wl,-O1'), 1, (args, largs)) self.assertIn('-Wl,-O1', largs) + def test_compiler_check_args_msvc(self): + cc = self._fake_msvc_cc() + args = cc.build_wrapper_args(None, None, CompileCheckMode.LINK).to_native() + # linker-only flags belong after /link, not on the compiler command line + self.assertAfterLink(args, '/release') + self.assertAfterLink(args, '/SUBSYSTEM:CONSOLE') + args = cc.build_wrapper_args(None, None, CompileCheckMode.COMPILE).to_native() + self.assertNotIn('/release', args) + self.assertNotIn('/link', args) + def test_compiler_check_args_os2(self): # the linker's always args (-Zomf on OS/2) must reach link mode checks cc = self._fake_gnu_cc(linker_cls=linkers.OS2OmfDynamicLinker) @@ -409,7 +439,7 @@ def test_find_library_args_msvc(self): def fake_links(code, *, extra_args=None, **kwargs): args = cc.build_wrapper_args(extra_args, None, CompileCheckMode.LINK).to_native() - self.assertIn('/release', args) + self.assertAfterLink(args, '/release') return (True, False) with mock.patch.object(cc, 'links', fake_links): From 3bc8ff445e4b2542e0cf2a7de6c94510eb065690 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 28 Aug 2026 16:14:56 +0200 Subject: [PATCH 675/681] d: pass link arguments through unix_args_to_native Commit decc3e assumed the sanity check goes through CompilerArgs, but it does not. Add to _sanity_check_compile_args() the missing call to DCompiler.unix_args_to_native(). Fixes: #16115 Signed-off-by: Paolo Bonzini --- mesonbuild/compilers/d.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mesonbuild/compilers/d.py b/mesonbuild/compilers/d.py index 367355828738..6cab9e8fc45f 100644 --- a/mesonbuild/compilers/d.py +++ b/mesonbuild/compilers/d.py @@ -402,6 +402,7 @@ def _sanity_check_source_code(self) -> str: def _sanity_check_compile_args(self, sourcename: str, binname: str ) -> T.Tuple[T.List[str], T.List[str]]: args, largs = super()._sanity_check_compile_args(sourcename, binname) + largs = self.unix_args_to_native(largs) largs.extend(self._get_target_arch_args()) return args, largs From 18b394b620d8bc384693757ce75d78a6021e2b95 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 28 Aug 2026 18:27:54 +0200 Subject: [PATCH 676/681] compilers: add get_external_compile/link_args Avoid a repeated get_value_for() idiom. Rust wraps the linker arguments with -Clink-arg as usual; this is a bug fix for cc.links(), cc.has_function() and friends which previously received the raw arguments. Signed-off-by: Paolo Bonzini --- mesonbuild/compilers/compilers.py | 17 +++++++++++------ mesonbuild/compilers/fortran.py | 4 ++-- mesonbuild/compilers/mixins/clike.py | 8 +++----- mesonbuild/compilers/rust.py | 3 +++ mesonbuild/compilers/swift.py | 2 +- mesonbuild/compilers/vala.py | 7 +++---- 6 files changed, 23 insertions(+), 18 deletions(-) diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 76ecae5bbff2..df624c84ca42 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -1431,6 +1431,12 @@ def _sanity_check_mode(self) -> CompileCheckMode: """Whether the sanity check links a binary, or only compiles one.""" return CompileCheckMode.LINK + def get_external_compile_args(self) -> T.List[str]: + return list(self.environment.coredata.get_external_args(self.for_machine, self.language)) + + def get_external_link_args(self) -> T.List[str]: + return list(self.environment.coredata.get_external_link_args(self.for_machine, self.language)) + def _sanity_check_compile_args(self, sourcename: str, binname: str ) -> T.Tuple[T.List[str], T.List[str]]: """Get arguments to run compiler for sanity check. @@ -1444,12 +1450,11 @@ def _sanity_check_compile_args(self, sourcename: str, binname: str arguments, the second is linker arguments """ mode = self._sanity_check_mode() - - cargs = list(self.environment.coredata.get_external_args(self.for_machine, self.language)) - cargs = self.exelist_no_ccache + self.get_always_args() + self.get_output_args(binname) + [sourcename] + cargs + cargs = self.exelist_no_ccache + self.get_always_args() + self.get_output_args(binname) + [sourcename] + \ + self.get_external_compile_args() if mode is CompileCheckMode.COMPILE: return cargs, [] - largs = list(self.environment.coredata.get_external_link_args(self.for_machine, self.language)) + largs = self.get_external_link_args() return cargs, largs @abc.abstractmethod @@ -1595,11 +1600,11 @@ def build_wrapper_args(self, if mode is CompileCheckMode.COMPILE: # Add DFLAGS from the env - args += self.environment.coredata.get_external_args(self.for_machine, self.language) + args += self.get_external_compile_args() elif mode is CompileCheckMode.LINK: args += self.get_linker_always_args() # Add LDFLAGS from the env - args += self.environment.coredata.get_external_link_args(self.for_machine, self.language) + args += self.get_external_link_args() # extra_args must override all other arguments, so we add them last args += extra_args return args diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py index 6ad4a6b59bcd..a58667f18a29 100644 --- a/mesonbuild/compilers/fortran.py +++ b/mesonbuild/compilers/fortran.py @@ -57,8 +57,8 @@ def has_function(self, funcname: str, prefix: str, *, 'that example is to see if the compiler has Fortran 2008 Block element.') def _get_basic_compiler_args(self, mode: CompileCheckMode) -> T.Tuple[T.List[str], T.List[str]]: - cargs = self.environment.coredata.get_external_args(self.for_machine, self.language) - largs = self.environment.coredata.get_external_link_args(self.for_machine, self.language) + cargs = self.get_external_compile_args() + largs = self.get_external_link_args() return cargs, largs def _sanity_check_source_code(self) -> str: diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py index 5d72eae875cc..bc6f619b6016 100644 --- a/mesonbuild/compilers/mixins/clike.py +++ b/mesonbuild/compilers/mixins/clike.py @@ -357,9 +357,7 @@ def _get_basic_compiler_args(self, mode: CompileCheckMode) -> T.Tuple[T.List[str pass # Add CFLAGS/CXXFLAGS/OBJCFLAGS/OBJCXXFLAGS and CPPFLAGS from the env - sys_args = self.environment.coredata.get_external_args(self.for_machine, self.language) - if isinstance(sys_args, str): - sys_args = [sys_args] + sys_args = self.get_external_compile_args() # Apparently it is a thing to inject linker flags both # via CFLAGS _and_ LDFLAGS, even though the former are # also used during linking. These flags can break @@ -375,7 +373,7 @@ def _get_basic_compiler_args(self, mode: CompileCheckMode) -> T.Tuple[T.List[str cargs += self.use_linker_args(ld_value[0], self.version) # Add LDFLAGS from the env - sys_ld_args = self.environment.coredata.get_external_link_args(self.for_machine, self.language) + sys_ld_args = self.get_external_link_args() # CFLAGS and CXXFLAGS go to both linking and compiling, but we want them # to only appear on the command line once. Remove dupes. largs += [x for x in sys_ld_args if x not in cleaned_sys_args] @@ -1255,7 +1253,7 @@ def find_framework_paths(self) -> T.List[str]: commands = self.get_exelist(ccache=False) + ['-v', '-E', '-'] commands += self.get_always_args() # Add CFLAGS/CXXFLAGS/OBJCFLAGS/OBJCXXFLAGS from the env - commands += self.environment.coredata.get_external_args(self.for_machine, self.language) + commands += self.get_external_compile_args() mlog.debug('Finding framework path by running: ', ' '.join(commands), '\n') os_env = os.environ.copy() os_env['LC_ALL'] = 'C' diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index 9f618778be14..f725693f95f7 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -410,6 +410,9 @@ def get_colorout_args(self, colortype: str) -> T.List[str]: return [f'--color={colortype}'] raise MesonException(f'Invalid color type for rust {colortype}') + def get_external_link_args(self) -> T.List[str]: + return rustc_link_args(super().get_external_link_args()) + @functools.lru_cache(maxsize=None) def get_linker_always_args(self) -> T.List[str]: return rustc_link_args(super().get_linker_always_args()) + ['-Cdefault-linker-libraries'] diff --git a/mesonbuild/compilers/swift.py b/mesonbuild/compilers/swift.py index 105258027e30..64241942682e 100644 --- a/mesonbuild/compilers/swift.py +++ b/mesonbuild/compilers/swift.py @@ -184,7 +184,7 @@ def _sanity_check_compile_args(self, sourcename: str, binname: str if self.is_cross: args.extend(self.get_compile_only_args()) else: - largs.extend(self.environment.coredata.get_external_link_args(self.for_machine, self.language)) + largs.extend(self.get_external_link_args()) args.extend(self.get_output_args(binname)) args.append(sourcename) diff --git a/mesonbuild/compilers/vala.py b/mesonbuild/compilers/vala.py index 4b24ee2ea6b2..d04247bea62d 100644 --- a/mesonbuild/compilers/vala.py +++ b/mesonbuild/compilers/vala.py @@ -157,8 +157,7 @@ def find_library(self, libname: str, extra_dirs: T.List[str], libtype: LibType = # no extra dirs are specified. if not extra_dirs: code = 'class MesonFindLibrary : Object { }' - args: T.List[str] = [] - args += self.environment.coredata.get_external_args(self.for_machine, self.language) + args: T.List[str] = self.get_external_compile_args() vapi_args = ['--pkg', libname] args += vapi_args with self.cached_compile(code, extra_args=args, mode=CompileCheckMode.COMPILE) as p: @@ -217,10 +216,10 @@ def build_wrapper_args(self, if mode is CompileCheckMode.COMPILE: # Add DFLAGS from the env - args += self.environment.coredata.get_external_args(self.for_machine, self.language) + args += self.get_external_compile_args() elif mode is CompileCheckMode.LINK: # Add LDFLAGS from the env - args += self.environment.coredata.get_external_link_args(self.for_machine, self.language) + args += self.get_external_link_args() # extra_args must override all other arguments, so we add them last args += extra_args return args From aadc006f5fac81464ba9358a348871c9701d1009 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 28 Aug 2026 16:18:20 +0200 Subject: [PATCH 677/681] rust: pass rust_args and rust_link_args to sanity check rustc's sanity check passes no external args at all. Fix it for consistency with other languages and to ensure that failures are detected early. Signed-off-by: Paolo Bonzini --- mesonbuild/compilers/rust.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index f725693f95f7..48a8cdc44af6 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -165,12 +165,9 @@ def needs_static_linker(self) -> bool: def _sanity_check_compile_args(self, sourcename: str, binname: str ) -> T.Tuple[T.List[str], T.List[str]]: - cmdlist = self.exelist.copy() - largs: T.List[str] = [] + cmdlist, largs = super()._sanity_check_compile_args(sourcename, binname) if self.info.kernel == 'none' and 'ld.' in self.get_linker_id(): largs.extend(rustc_link_args(['-nostartfiles'])) - cmdlist.extend(self.get_output_args(binname)) - cmdlist.append(sourcename) return cmdlist, largs def _sanity_check_source_code(self) -> str: From 3c4fb2b15162618981ed070e6a69612713063ef8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 10 Sep 2026 19:54:00 +0200 Subject: [PATCH 678/681] compilers: move -fpermissive from CPPCompiler to individual superclasses or mixins This is indicated as a problem in comments, and it causes problem with recent changes to the sanity check command lines. Get -fpermissive from a separate method, and implement it where it was used before. Fixes: #16203 Signed-off-by: Paolo Bonzini --- mesonbuild/compilers/cpp.py | 21 +++++++++------------ mesonbuild/compilers/mixins/arm.py | 3 +++ mesonbuild/compilers/mixins/gnu.py | 3 +++ mesonbuild/compilers/mixins/pgi.py | 3 +++ 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index 620c1fa5126c..d7ad0c05e837 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -91,11 +91,13 @@ def get_cpp_modules_args(self) -> T.List[str]: def _sanity_check_source_code(self) -> str: return '#include \nclass breakCCompiler;int main(void) { return 0; }\n' + def get_cpp_permissive_args(self) -> T.List[str]: + return [] + def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]: - # -fpermissive allows non-conforming code to compile which is necessary - # for many C++ checks. Particularly, the has_header_symbol check is - # too strict without this and always fails. - return super().get_compiler_check_args(mode) + ['-fpermissive'] + # Compiler checks must accept non-conforming code. Particularly, the + # has_header_symbol check is too strict without this and always fails. + return super().get_compiler_check_args(mode) + self.get_cpp_permissive_args() def has_header_symbol(self, hname: str, symbol: str, prefix: str, *, extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]] = None, @@ -859,10 +861,6 @@ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = args.append('/permissive-') return args - def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]: - # XXX: this is a hack because so much GnuLike stuff is in the base CPPCompiler class. - return Compiler.get_compiler_check_args(self, mode) - class CPP11AsCPP14Mixin(CompilerMixinBase): """Mixin class for VisualStudio and ClangCl to replace C++11 std with C++14. @@ -996,10 +994,6 @@ def get_options(self) -> 'MutableKeyedOptionDictType': cpp_stds += ['c++20'] return self._get_options_impl(super().get_options(), cpp_stds) - def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]: - # XXX: this is a hack because so much GnuLike stuff is in the base CPPCompiler class. - return IntelVisualStudioLikeCompiler.get_compiler_check_args(self, mode) - class IntelLLVMClCPPCompiler(IntelClCPPCompiler): @@ -1089,6 +1083,9 @@ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = def get_always_args(self) -> T.List[str]: return [] + def get_cpp_permissive_args(self) -> T.List[str]: + return ['-fpermissive'] + def get_option_link_args(self, target: 'BuildTarget', subproject: T.Optional[str] = None) -> T.List[str]: return [] diff --git a/mesonbuild/compilers/mixins/arm.py b/mesonbuild/compilers/mixins/arm.py index e1212a2805fe..e2bdd8636ed7 100644 --- a/mesonbuild/compilers/mixins/arm.py +++ b/mesonbuild/compilers/mixins/arm.py @@ -140,6 +140,9 @@ def get_pic_args(self) -> T.List[str]: def get_colorout_args(self, colortype: str) -> T.List[str]: return clang_color_args[colortype][:] + def get_cpp_permissive_args(self) -> T.List[str]: + return ['-fpermissive'] + def get_pch_suffix(self) -> str: return 'gch' diff --git a/mesonbuild/compilers/mixins/gnu.py b/mesonbuild/compilers/mixins/gnu.py index 25208796ea30..5aee5edce006 100644 --- a/mesonbuild/compilers/mixins/gnu.py +++ b/mesonbuild/compilers/mixins/gnu.py @@ -381,6 +381,9 @@ def get_pic_args(self) -> T.List[str]: def get_pie_args(self) -> T.List[str]: return ['-fPIE'] + def get_cpp_permissive_args(self) -> T.List[str]: + return ['-fpermissive'] + @abc.abstractmethod def get_optimization_args(self, optimization_level: str) -> T.List[str]: pass diff --git a/mesonbuild/compilers/mixins/pgi.py b/mesonbuild/compilers/mixins/pgi.py index 7bb78a8ee59f..bda10b7b2a37 100644 --- a/mesonbuild/compilers/mixins/pgi.py +++ b/mesonbuild/compilers/mixins/pgi.py @@ -50,6 +50,9 @@ def get_pic_args(self) -> T.List[str]: return ['-fPIC'] return [] + def get_cpp_permissive_args(self) -> T.List[str]: + return ['-fpermissive'] + def openmp_flags(self) -> T.List[str]: return ['-mp'] From f1b329de11efb7faad7b617b98e8a529873cefee Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 10 Sep 2026 19:54:38 +0200 Subject: [PATCH 679/681] compilers: add /permissive to cl/clang-cl compiler checks Match what GNU-like compiler does, helping with c++NN std. Signed-off-by: Paolo Bonzini --- mesonbuild/compilers/cpp.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index d7ad0c05e837..43897d093495 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -861,6 +861,9 @@ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = args.append('/permissive-') return args + def get_cpp_permissive_args(self) -> T.List[str]: + return ['/permissive'] + class CPP11AsCPP14Mixin(CompilerMixinBase): """Mixin class for VisualStudio and ClangCl to replace C++11 std with C++14. From 01ad3d09fb3718379b9ebf82498800adf9f75ad5 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Wed, 23 Sep 2026 02:39:01 +0530 Subject: [PATCH 680/681] Bump versions to 1.12.1 for stable release Milestone: https://github.com/mesonbuild/meson/milestone/139?closed=1 --- man/meson.1 | 2 +- mesonbuild/coredata.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/man/meson.1 b/man/meson.1 index c1f092a8e36b..b20d9b5f8d7c 100644 --- a/man/meson.1 +++ b/man/meson.1 @@ -1,4 +1,4 @@ -.TH MESON "1" "August 2026" "meson 1.12.0" "User Commands" +.TH MESON "1" "September 2026" "meson 1.12.1" "User Commands" .SH NAME meson - a high productivity build system .SH DESCRIPTION diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index c02f6f676309..fbfced64f040 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -43,7 +43,7 @@ # # Pip requires that RCs are named like this: '0.1.0.rc1' # But the corresponding Git tag needs to be '0.1.0rc1' -version = '1.12.0' +version = '1.12.1' # The next stable version when we are in dev. This is used to allow projects to # require meson version >=1.2.0 when using 1.1.99. FeatureNew won't warn when From 70598bb7825f0f804fb31ec2795de5e79e7692a0 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Wed, 23 Sep 2026 02:49:55 +0530 Subject: [PATCH 681/681] ci: Move fastjsonschema to pip for msys2 jobs Package seems to be unavailable for the gccx86ninja job's architecture. --- .github/workflows/msys2.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/msys2.yml b/.github/workflows/msys2.yml index 4cd3954270ab..9c0b8ae9a2e4 100644 --- a/.github/workflows/msys2.yml +++ b/.github/workflows/msys2.yml @@ -83,14 +83,13 @@ jobs: mingw-w64-${{ matrix.MSYS2_ARCH }}-python-lxml mingw-w64-${{ matrix.MSYS2_ARCH }}-python-setuptools mingw-w64-${{ matrix.MSYS2_ARCH }}-python-pip - mingw-w64-${{ matrix.MSYS2_ARCH }}-python-fastjsonschema mingw-w64-${{ matrix.MSYS2_ARCH }}-${{ matrix.MSYSTEM != 'MINGW32' && 'objfw' || 'ninja' }} mingw-w64-${{ matrix.MSYS2_ARCH }}-${{ matrix.MSYSTEM != 'MINGW32' && 'llvm' || 'ninja' }} mingw-w64-${{ matrix.MSYS2_ARCH }}-${{ matrix.TOOLCHAIN }} - name: Install dependencies run: | - python3 -m pip --disable-pip-version-check install gcovr pefile pytest pytest-subtests pytest-xdist + python3 -m pip --disable-pip-version-check install fastjsonschema gcovr pefile pytest pytest-subtests pytest-xdist - name: Install pypy3 on x86_64 run: |