From c8920ed4bb3ca9458eddb2931fb387108fdd0415 Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Tue, 25 Nov 2025 07:58:40 -0500 Subject: [PATCH 01/12] Add LocalProgram object --- mesonbuild/build.py | 45 ++++++++++++++++++++ mesonbuild/interpreter/interpreter.py | 1 + mesonbuild/interpreter/interpreterobjects.py | 13 ++++++ 3 files changed, 59 insertions(+) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 138f1ae03233..0053fb37a43c 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -3354,6 +3354,51 @@ def __getattr__(self, name: str) -> T.Any: def get_version(self, interpreter: T.Optional[Interpreter] = None) -> str: return self._version +class LocalProgram(HoldableObject): + ''' A wrapper for a program that may have build dependencies.''' + def __init__(self, program: T.Union[programs.ExternalProgram, Executable, CustomTarget, CustomTargetIndex], version: str, + depends: T.Optional[T.List[T.Union[BuildTarget, CustomTarget]]] = None, + depend_files: T.Optional[T.List[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.') + self.name = program.name + self.for_machine = program.for_machine + self.program = program + self.depends = list(depends or []) + self.depend_files = list(depend_files or []) + self.version = version + + def found(self) -> bool: + return True + + def get_version(self, interpreter: T.Optional[Interpreter] = None) -> str: + return self.version + + def get_command(self) -> T.List[str]: + if isinstance(self.program, (Executable, CustomTarget, CustomTargetIndex)): + return [os.path.join(self.program.subdir, self.program.get_filename())] + return self.program.get_command() + + def get_path(self) -> str: + if isinstance(self.program, (Executable, CustomTarget, CustomTargetIndex)): + return os.path.join(self.program.subdir, self.program.get_filename()) + return self.program.get_path() + + def description(self) -> str: + if isinstance(self.program, Executable): + return self.program.name + if isinstance(self.program, (CustomTarget, CustomTargetIndex)): + return self.program.get_filename() + return self.program.description() + + def run_program(self) -> T.Optional[programs.ExternalProgram]: + ''' Returns an ExternalProgram if it can be run at configure time.''' + if isinstance(self.program, programs.ExternalProgram) and not self.depends: + return self.program + return None + # A bit poorly named, but this represents plain data files to copy # during install. @dataclass(eq=False) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 56659460a844..3e013a64d4af 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -420,6 +420,7 @@ def build_holder_map(self) -> None: build.GeneratedList: OBJ.GeneratedListHolder, build.ExtractedObjects: OBJ.GeneratedObjectsHolder, build.OverrideExecutable: OBJ.OverrideExecutableHolder, + build.LocalProgram: OBJ.LocalProgramHolder, build.RunTarget: OBJ.RunTargetHolder, build.AliasTarget: OBJ.AliasTargetHolder, build.Headers: OBJ.HeadersHolder, diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index 3bdbcb93eab8..63de63b569a0 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -1212,3 +1212,16 @@ class OverrideExecutableHolder(BuildTargetHolder[build.OverrideExecutable]): @InterpreterObject.method('version') def version_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: return self.held_object.get_version(self.interpreter) + +class LocalProgramHolder(ObjectHolder[build.LocalProgram]): + @noPosargs + @noKwargs + @InterpreterObject.method('version') + def version_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: + return self.held_object.version + + @noPosargs + @noKwargs + @InterpreterObject.method('found') + def found_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool: + return True From 9e0852b85ff582722b219c8f8f69ff873701a5f6 Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Sat, 11 Oct 2025 16:36:50 -0400 Subject: [PATCH 02/12] Add support for LocalProgram to custom_target() and run_target() --- mesonbuild/build.py | 10 +++++++--- mesonbuild/interpreter/kwargs.py | 5 +++-- mesonbuild/interpreter/type_checking.py | 7 ++++--- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 0053fb37a43c..0fef5c5e25a5 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2781,11 +2781,15 @@ class CommandBase: dependencies: T.List[T.Union[BuildTarget, 'CustomTarget']] subproject: str - def flatten_command(self, cmd: T.Sequence[T.Union[str, File, programs.ExternalProgram, BuildTargetTypes]]) -> \ + def flatten_command(self, cmd: T.Sequence[T.Union[str, File, programs.ExternalProgram, BuildTargetTypes, LocalProgram]]) -> \ T.List[T.Union[str, File, BuildTarget, CustomTarget, programs.ExternalProgram]]: cmd = listify(cmd) final_cmd: T.List[T.Union[str, File, BuildTarget, 'CustomTarget']] = [] for c in cmd: + if isinstance(c, LocalProgram): + self.dependencies.extend(c.depends) + self.depend_files.extend(c.depend_files) + c = c.program if isinstance(c, str): final_cmd.append(c) elif isinstance(c, File): @@ -2851,7 +2855,7 @@ def __init__(self, environment: Environment, command: T.Sequence[T.Union[ str, BuildTargetTypes, GeneratedList, - programs.ExternalProgram, File]], + programs.ExternalProgram, File, LocalProgram]], sources: T.Sequence[T.Union[ str, File, BuildTargetTypes, ExtractedObjects, GeneratedList, programs.ExternalProgram]], @@ -3112,7 +3116,7 @@ class RunTarget(Target, CommandBase): typename = 'run' def __init__(self, name: str, - command: T.Sequence[T.Union[str, File, BuildTargetTypes, programs.ExternalProgram]], + command: T.Sequence[T.Union[str, File, BuildTargetTypes, programs.ExternalProgram, LocalProgram]], dependencies: T.Sequence[AnyTargetType], subdir: str, subproject: str, diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index 4935973d95d4..357d4deb597c 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -171,7 +171,8 @@ class FuncAddLanguages(ExtractRequired): class RunTarget(TypedDict): - command: T.List[T.Union[str, build.BuildTarget, build.CustomTarget, ExternalProgram, File]] + command: T.List[T.Union[str, build.BuildTarget, build.CustomTarget, ExternalProgram, + File, build.LocalProgram]] depends: T.List[T.Union[build.BuildTarget, build.CustomTarget]] env: EnvironmentVariables @@ -183,7 +184,7 @@ class CustomTarget(TypedDict): build_by_default: T.Optional[bool] build_subdir: str capture: bool - command: T.List[T.Union[str, build.BuildTargetTypes, ExternalProgram, File]] + command: T.List[T.Union[str, build.BuildTargetTypes, ExternalProgram, File, build.LocalProgram]] console: bool depend_files: T.List[FileOrString] depends: T.List[T.Union[build.BuildTarget, build.CustomTarget]] diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py index 28280a996538..3bf6e5d59b22 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -10,7 +10,8 @@ from .. import compilers from ..build import (CustomTarget, BuildTarget, CustomTargetIndex, ExtractedObjects, GeneratedList, IncludeDirs, - BothLibraries, SharedLibrary, StaticLibrary, Jar, Executable, StructuredSources) + BothLibraries, SharedLibrary, StaticLibrary, Jar, Executable, StructuredSources, + LocalProgram) from ..options import OptionKey, UserFeatureOption from ..dependencies import Dependency, DependencyMethods, InternalDependency from ..interpreterbase.decorators import KwargInfo, ContainerTypeInfo, FeatureBroken, FeatureDeprecated @@ -285,9 +286,9 @@ def _env_convertor(value: _FullEnvInitValueType) -> EnvironmentVariables: default=[], ) -COMMAND_KW: KwargInfo[T.List[T.Union[str, BuildTargetTypes, ExternalProgram, File]]] = KwargInfo( +COMMAND_KW: KwargInfo[T.List[T.Union[str, BuildTargetTypes, ExternalProgram, File, LocalProgram]]] = KwargInfo( 'command', - ContainerTypeInfo(list, (str, BuildTarget, CustomTarget, CustomTargetIndex, ExternalProgram, File), allow_empty=False), + ContainerTypeInfo(list, (str, BuildTarget, CustomTarget, CustomTargetIndex, ExternalProgram, File, LocalProgram), allow_empty=False), required=True, listify=True, default=[], From 79acee37d57c2ffe730883c5dca9fe20964cc542 Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Sat, 11 Oct 2025 17:16:42 -0400 Subject: [PATCH 03/12] Add support for LocalProgram to configure_file() and run_command() --- mesonbuild/interpreter/interpreter.py | 52 ++++++++++++++++----------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 3e013a64d4af..a5af9cd31e49 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -748,8 +748,8 @@ def validate_arguments(self, args, argcount, arg_types): # better error messages when overridden @typed_pos_args( 'run_command', - (build.Executable, ExternalProgram, compilers.Compiler, mesonlib.File, str), - varargs=(build.Executable, ExternalProgram, compilers.Compiler, mesonlib.File, str)) + (build.Executable, ExternalProgram, compilers.Compiler, mesonlib.File, str, build.LocalProgram), + varargs=(build.Executable, ExternalProgram, compilers.Compiler, mesonlib.File, str, build.LocalProgram)) @typed_kwargs( 'run_command', KwargInfo('check', (bool, NoneType), since='0.47.0'), @@ -757,14 +757,21 @@ def validate_arguments(self, args, argcount, arg_types): ENV_KW.evolve(since='0.50.0'), ) def func_run_command(self, node: mparser.BaseNode, - args: T.Tuple[T.Union[build.Executable, ExternalProgram, compilers.Compiler, mesonlib.File, str], - T.List[T.Union[build.Executable, ExternalProgram, compilers.Compiler, mesonlib.File, str]]], + args: T.Tuple[T.Union[build.Executable, ExternalProgram, compilers.Compiler, mesonlib.File, str, build.LocalProgram], + T.List[T.Union[build.Executable, ExternalProgram, compilers.Compiler, mesonlib.File, str, build.LocalProgram]]], kwargs: 'kwtypes.RunCommand') -> RunProcess: return self.run_command_impl(args, kwargs) + def _compiled_exe_error(self, cmd: T.Union[build.LocalProgram, 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') + raise InterpreterException(f'Program {descr!r} is a compiled executable and therefore cannot be used during configuration') + def run_command_impl(self, - args: T.Tuple[T.Union[build.Executable, ExternalProgram, compilers.Compiler, mesonlib.File, str], - T.List[T.Union[build.Executable, ExternalProgram, compilers.Compiler, mesonlib.File, str]]], + args: T.Tuple[T.Union[build.Executable, ExternalProgram, compilers.Compiler, mesonlib.File, str, build.LocalProgram], + T.List[T.Union[build.Executable, ExternalProgram, compilers.Compiler, mesonlib.File, str, build.LocalProgram]]], kwargs: 'kwtypes.RunCommand', in_builddir: bool = False) -> RunProcess: cmd, cargs = args @@ -778,19 +785,17 @@ def run_command_impl(self, mlog.warning(implicit_check_false_warning, once=True) check = False - overridden_msg = ('Program {!r} was overridden with the compiled ' - 'executable {!r} and therefore cannot be used during ' - 'configuration') expanded_args: T.List[str] = [] - if isinstance(cmd, build.Executable): - for name, exe in self.build.find_overrides.items(): - if cmd == exe: - progname = name - break - else: - raise InterpreterException(f'Program {cmd.description()!r} is a compiled executable and therefore cannot be used during configuration') - raise InterpreterException(overridden_msg.format(progname, cmd.description())) - if isinstance(cmd, ExternalProgram): + if isinstance(cmd, build.LocalProgram): + prog = cmd.run_program() + if prog is None: + self._compiled_exe_error(cmd) + for f in cmd.depend_files: + self.add_build_def_file(f) + cmd = prog + elif isinstance(cmd, build.Executable): + self._compiled_exe_error(cmd) + elif isinstance(cmd, ExternalProgram): if not cmd.found(): raise InterpreterException(f'command {cmd.get_name()!r} not found or not executable') elif isinstance(cmd, compilers.Compiler): @@ -822,8 +827,15 @@ def run_command_impl(self, if not prog.found(): raise InterpreterException(f'Program {cmd!r} not found or not executable') expanded_args.append(prog.get_path()) + elif isinstance(a, build.LocalProgram): + prog = a.run_program() + if prog is None: + self._compiled_exe_error(a) + for f in a.depend_files: + self.add_build_def_file(f) + expanded_args.append(prog.get_path()) else: - raise InterpreterException(overridden_msg.format(a.name, cmd.description())) + self._compiled_exe_error(a) # If any file that was used as an argument to the command # changes, we must re-run the configuration step. @@ -2605,7 +2617,7 @@ def validate_build_subdir(self, build_subdir: str, target: str): KwargInfo('capture', bool, default=False, since='0.41.0'), KwargInfo( 'command', - (ContainerTypeInfo(list, (build.Executable, ExternalProgram, compilers.Compiler, mesonlib.File, str), allow_empty=False), NoneType), + (ContainerTypeInfo(list, (build.Executable, ExternalProgram, compilers.Compiler, mesonlib.File, str, build.LocalProgram), allow_empty=False), NoneType), listify=True, ), KwargInfo( From ae2531b80ba2e82f3c81a4a02d06dc6d63c4948b Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Sat, 11 Oct 2025 17:48:52 -0400 Subject: [PATCH 04/12] Add support for LocalProgram to test() and benchmark() --- mesonbuild/interpreter/interpreter.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index a5af9cd31e49..3865c09ae766 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -2220,17 +2220,17 @@ def func_generator(self, node: mparser.FunctionNode, return build.Generator(self.environment, args[0], **kwargs) - @typed_pos_args('benchmark', str, (build.Executable, build.Jar, ExternalProgram, mesonlib.File, build.CustomTarget, build.CustomTargetIndex)) + @typed_pos_args('benchmark', str, (build.Executable, build.Jar, ExternalProgram, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.LocalProgram)) @typed_kwargs('benchmark', *TEST_KWS) def func_benchmark(self, node: mparser.BaseNode, - args: T.Tuple[str, T.Union[build.Executable, build.Jar, ExternalProgram, mesonlib.File]], + args: T.Tuple[str, T.Union[build.Executable, build.Jar, ExternalProgram, mesonlib.File, build.LocalProgram]], kwargs: 'kwtypes.FuncBenchmark') -> None: self.add_test(node, args, kwargs, False) - @typed_pos_args('test', str, (build.Executable, build.Jar, ExternalProgram, mesonlib.File, build.CustomTarget, build.CustomTargetIndex)) + @typed_pos_args('test', str, (build.Executable, build.Jar, ExternalProgram, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.LocalProgram)) @typed_kwargs('test', *TEST_KWS, KwargInfo('is_parallel', bool, default=True)) def func_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, ExternalProgram, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.LocalProgram]], kwargs: 'kwtypes.FuncTest') -> None: self.add_test(node, args, kwargs, True) @@ -2244,7 +2244,7 @@ def unpack_env_kwarg(self, kwargs: T.Union[EnvironmentVariables, T.Dict[str, 'TY return ENV_KW.convertor(envlist) def make_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, ExternalProgram, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.LocalProgram]], kwargs: 'kwtypes.BaseTest', klass: T.Type[TestClass] = Test) -> TestClass: name = args[0] @@ -2253,15 +2253,20 @@ def make_test(self, node: mparser.BaseNode, location=node) name = name.replace(':', '_') exe = args[1] - if isinstance(exe, ExternalProgram): + depends = list(kwargs['depends'] or []) + if isinstance(exe, build.LocalProgram): + # FIXME: tests does not have depend_files? + depends.extend(exe.depends) + exe = exe.program + elif isinstance(exe, ExternalProgram): if 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]) elif isinstance(exe, build.CustomTarget): - kwargs.setdefault('depends', []).append(exe) + depends.append(exe) elif isinstance(exe, build.CustomTargetIndex): - kwargs.setdefault('depends', []).append(exe.target) + depends.append(exe.target) env = self.unpack_env_kwarg(kwargs) @@ -2280,7 +2285,7 @@ def make_test(self, node: mparser.BaseNode, prj, suite, exe, - kwargs['depends'], + depends, kwargs.get('is_parallel', False), kwargs['args'], env, From 9435f52941aae2c96495138965a1bc6644e5d917 Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Sat, 11 Oct 2025 17:54:00 -0400 Subject: [PATCH 05/12] Add support for LocalProgram to generator() --- mesonbuild/build.py | 10 +++++++--- mesonbuild/interpreter/interpreter.py | 4 ++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 0fef5c5e25a5..c226e8fd4a39 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -1980,7 +1980,7 @@ def __str__(self) -> str: class Generator(HoldableObject): def __init__(self, env: Environment, - exe: T.Union['Executable', programs.ExternalProgram], + exe: T.Union[Executable, programs.ExternalProgram, LocalProgram, CustomTarget, CustomTargetIndex], arguments: T.List[str], output: T.List[str], # how2dataclass @@ -1990,10 +1990,14 @@ def __init__(self, env: Environment, depends: T.Optional[T.List[BuildTargetTypes]] = None, name: str = 'Generator'): self.environment = env + self.depends = list(depends or []) + if isinstance(exe, LocalProgram): + # FIXME: Generator does not have depend_files? + self.depends.extend(exe.depends) + exe = exe.program self.exe = exe self.depfile = depfile self.capture = capture - self.depends: T.List[BuildTargetTypes] = depends or [] self.arglist = arguments self.outputs = output self.name = name @@ -2002,7 +2006,7 @@ def __repr__(self) -> str: repr_str = "<{0}: {1}>" return repr_str.format(self.__class__.__name__, self.exe) - def get_exe(self) -> T.Union['Executable', programs.ExternalProgram]: + def get_exe(self) -> T.Union[Executable, programs.ExternalProgram, CustomTarget, CustomTargetIndex]: return self.exe def get_base_outnames(self, inname: str) -> T.List[str]: diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 3865c09ae766..e4443b2554b6 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -2196,7 +2196,7 @@ def func_alias_target(self, node: mparser.BaseNode, args: T.Tuple[str, T.List[T. self.add_target(name, tg) return tg - @typed_pos_args('generator', (build.Executable, ExternalProgram)) + @typed_pos_args('generator', (build.Executable, ExternalProgram, build.LocalProgram)) @typed_kwargs( 'generator', KwargInfo('arguments', ContainerTypeInfo(list, str, allow_empty=False), required=True, listify=True), @@ -2206,7 +2206,7 @@ def func_alias_target(self, node: mparser.BaseNode, args: T.Tuple[str, T.List[T. KwargInfo('capture', bool, default=False, since='0.43.0'), ) def func_generator(self, node: mparser.FunctionNode, - args: T.Tuple[T.Union[build.Executable, ExternalProgram]], + args: T.Tuple[T.Union[build.Executable, ExternalProgram, build.LocalProgram]], kwargs: 'kwtypes.FuncGenerator') -> build.Generator: for rule in kwargs['output']: if '@BASENAME@' not in rule and '@PLAINNAME@' not in rule: From 4e39473a438c894002e2aec885c25d7fb787517a Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Sat, 11 Oct 2025 18:03:31 -0400 Subject: [PATCH 06/12] Add support for LocalProgram to override_find_program() --- mesonbuild/backend/backends.py | 10 +++++--- mesonbuild/build.py | 2 +- mesonbuild/interpreter/interpreter.py | 10 ++++---- mesonbuild/interpreter/mesonmain.py | 6 ++--- mesonbuild/modules/__init__.py | 6 ++--- mesonbuild/modules/_qt.py | 34 ++++++++++++++++++++------- mesonbuild/modules/dlang.py | 4 ++-- mesonbuild/modules/gnome.py | 19 +++++++-------- mesonbuild/modules/i18n.py | 2 +- mesonbuild/modules/icestorm.py | 2 +- mesonbuild/modules/rust.py | 8 +++---- mesonbuild/modules/wayland.py | 4 ++-- 12 files changed, 63 insertions(+), 44 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index dddcf67d8dc7..60366d0c9bfe 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -535,7 +535,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.ExternalProgram, build.BuildTarget, build.CustomTarget, File, str]], + self, cmd: T.Sequence[T.Union[programs.ExternalProgram, build.BuildTarget, build.CustomTarget, build.CustomTargetIndex, File, str, build.LocalProgram]], workdir: T.Optional[str] = None, extra_bdeps: T.Optional[T.List[build.BuildTarget]] = None, capture: T.Optional[str] = None, @@ -548,13 +548,15 @@ def get_executable_serialisation( # 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): + exe = exe.program if isinstance(exe, programs.ExternalProgram): exe_cmd = exe.get_command() exe_for_machine = exe.for_machine elif isinstance(exe, build.BuildTarget): exe_cmd = [self.get_target_filename_abs(exe)] exe_for_machine = exe.for_machine - elif isinstance(exe, build.CustomTarget): + elif isinstance(exe, (build.CustomTarget, build.CustomTargetIndex)): # The output of a custom target can either be directly runnable # or not, that is, a script, a native binary or a cross compiled # binary when exe wrapper is available and when it is not. @@ -571,9 +573,11 @@ def get_executable_serialisation( cmd_args: T.List[str] = [] for c in raw_cmd_args: + if isinstance(c, build.LocalProgram): + c = c.program if isinstance(c, programs.ExternalProgram): cmd_args += c.get_command() - elif isinstance(c, (build.BuildTarget, build.CustomTarget)): + elif isinstance(c, (build.BuildTarget, build.CustomTarget, build.CustomTargetIndex)): cmd_args.append(self.get_target_filename_abs(c)) elif isinstance(c, mesonlib.File): cmd_args.append(c.rel_to_builddir(self.environment.source_dir)) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index c226e8fd4a39..6aebd5c32949 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -363,7 +363,7 @@ def __init__(self, environment: Environment): self.stdlibs = PerMachine({}, {}) self.test_setups: T.Dict[str, TestSetup] = {} self.test_setup_default_name = None - self.find_overrides: T.Dict[str, T.Union['OverrideExecutable', programs.ExternalProgram, programs.OverrideProgram]] = {} + self.find_overrides: T.Dict[str, T.Union[OverrideExecutable, programs.ExternalProgram, programs.OverrideProgram, LocalProgram]] = {} 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 e4443b2554b6..9fbcb805b661 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1598,7 +1598,7 @@ def program_from_system(self, args: T.List[mesonlib.FileOrString], search_dirs: def program_from_overrides(self, command_names: T.List[mesonlib.FileOrString], extra_info: T.List['mlog.TV_Loggable'] - ) -> T.Optional[T.Union[ExternalProgram, OverrideProgram, build.OverrideExecutable]]: + ) -> T.Optional[T.Union[ExternalProgram, OverrideProgram, build.OverrideExecutable, build.LocalProgram]]: for name in command_names: if not isinstance(name, str): continue @@ -1613,7 +1613,7 @@ def store_name_lookups(self, command_names: T.List[mesonlib.FileOrString]) -> No if isinstance(name, str): self.build.searched_programs.add(name) - def add_find_program_override(self, name: str, exe: T.Union[build.OverrideExecutable, ExternalProgram, 'OverrideProgram']) -> None: + def add_find_program_override(self, name: str, exe: T.Union[build.OverrideExecutable, ExternalProgram, OverrideProgram, build.LocalProgram]) -> 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.build.find_overrides: @@ -1638,7 +1638,7 @@ def find_program_impl(self, args: T.List[mesonlib.FileOrString], search_dirs: T.Optional[T.List[str]] = None, version_arg: T.Optional[str] = '', version_func: T.Optional[ProgramVersionFunc] = None - ) -> T.Union['ExternalProgram', 'build.OverrideExecutable', 'OverrideProgram']: + ) -> T.Union[ExternalProgram, build.OverrideExecutable, OverrideProgram, build.LocalProgram]: args = mesonlib.listify(args) extra_info: T.List[mlog.TV_Loggable] = [] @@ -1670,7 +1670,7 @@ def program_lookup(self, args: T.List[mesonlib.FileOrString], for_machine: Machi version_arg: T.Optional[str], version_func: T.Optional[ProgramVersionFunc], extra_info: T.List[mlog.TV_Loggable] - ) -> T.Optional[T.Union[ExternalProgram, build.Executable, OverrideProgram]]: + ) -> T.Optional[T.Union[ExternalProgram, build.Executable, OverrideProgram, build.LocalProgram]]: progobj = self.program_from_overrides(args, extra_info) if progobj: return progobj @@ -1706,7 +1706,7 @@ def program_lookup(self, args: T.List[mesonlib.FileOrString], for_machine: Machi return progobj - def check_program_version(self, progobj: T.Union[ExternalProgram, build.Executable, OverrideProgram], + def check_program_version(self, progobj: T.Union[ExternalProgram, build.Executable, OverrideProgram, build.LocalProgram], wanted: T.Union[str, T.List[str]], version_func: T.Optional[ProgramVersionFunc], extra_info: T.List[mlog.TV_Loggable]) -> bool: diff --git a/mesonbuild/interpreter/mesonmain.py b/mesonbuild/interpreter/mesonmain.py index 067d5ffb3f30..da74fe0d0d27 100644 --- a/mesonbuild/interpreter/mesonmain.py +++ b/mesonbuild/interpreter/mesonmain.py @@ -60,7 +60,7 @@ def __init__(self, build: 'build.Build', interpreter: 'Interpreter'): def _find_source_script( self, name: str, prog: T.Union[str, mesonlib.File, build.Executable, ExternalProgram], args: T.List[str]) -> 'ExecutableSerialisation': - largs: T.List[T.Union[str, build.Executable, ExternalProgram]] = [] + largs: T.List[T.Union[str, build.Executable, ExternalProgram, build.LocalProgram]] = [] if isinstance(prog, (build.Executable, ExternalProgram)): FeatureNew.single_use(f'Passing executable/found program object to script parameter of {name}', @@ -313,10 +313,10 @@ def install_dependency_manifest_method(self, args: T.Tuple[str], kwargs: 'TYPE_k self.build.dep_manifest_name = args[0] @FeatureNew('meson.override_find_program', '0.46.0') - @typed_pos_args('meson.override_find_program', str, (mesonlib.File, ExternalProgram, build.Executable)) + @typed_pos_args('meson.override_find_program', str, (mesonlib.File, ExternalProgram, build.Executable, build.LocalProgram)) @noKwargs @InterpreterObject.method('override_find_program') - def override_find_program_method(self, args: T.Tuple[str, T.Union[mesonlib.File, ExternalProgram, build.Executable]], kwargs: 'TYPE_kwargs') -> None: + def override_find_program_method(self, args: T.Tuple[str, T.Union[mesonlib.File, ExternalProgram, build.Executable, build.LocalProgram]], kwargs: 'TYPE_kwargs') -> None: name, exe = args if isinstance(exe, mesonlib.File): abspath = exe.absolute_path(self.interpreter.environment.source_dir, diff --git a/mesonbuild/modules/__init__.py b/mesonbuild/modules/__init__.py index 3ff9368d907f..38f8e0aaf38e 100644 --- a/mesonbuild/modules/__init__.py +++ b/mesonbuild/modules/__init__.py @@ -75,14 +75,14 @@ def find_program(self, prog: T.Union[mesonlib.FileOrString, T.List[mesonlib.File required: bool = True, version_func: T.Optional[ProgramVersionFunc] = None, wanted: T.Union[str, T.List[str]] = '', silent: bool = False, - for_machine: MachineChoice = MachineChoice.HOST) -> T.Union[ExternalProgram, build.OverrideExecutable, OverrideProgram]: + for_machine: MachineChoice = MachineChoice.HOST) -> T.Union[ExternalProgram, build.OverrideExecutable, OverrideProgram, build.LocalProgram]: if not isinstance(prog, list): prog = [prog] return self._interpreter.find_program_impl(prog, required=required, version_func=version_func, wanted=wanted, silent=silent, for_machine=for_machine) def find_tool(self, name: str, depname: str, varname: str, required: bool = True, - wanted: T.Optional[str] = None) -> T.Union[build.OverrideExecutable, ExternalProgram, 'OverrideProgram']: + wanted: T.Optional[str] = None) -> T.Union[build.OverrideExecutable, ExternalProgram, OverrideProgram, build.LocalProgram]: # Look in overrides in case it's built as subproject progobj = self._interpreter.program_from_overrides([name], []) if progobj is not None: @@ -118,7 +118,7 @@ def dependency(self, depname: str, native: bool = False, required: bool = True, # implementations of meson functions anyway. return self._interpreter.func_dependency(self.current_node, [depname], kwargs) # type: ignore - def test(self, args: T.Tuple[str, T.Union[build.Executable, build.Jar, 'ExternalProgram', mesonlib.File]], + def test(self, args: T.Tuple[str, T.Union[build.Executable, build.Jar, ExternalProgram, build.LocalProgram, mesonlib.File]], workdir: T.Optional[str] = None, env: T.Union[T.List[str], T.Dict[str, str], str] = None, depends: T.List[T.Union[build.CustomTarget, build.BuildTarget]] = None) -> None: diff --git a/mesonbuild/modules/_qt.py b/mesonbuild/modules/_qt.py index b75169e4b2b0..11b2a4c90086 100644 --- a/mesonbuild/modules/_qt.py +++ b/mesonbuild/modules/_qt.py @@ -209,7 +209,7 @@ def __init__(self, interpreter: Interpreter, qt_version: int = 5): self.qt_version = qt_version # It is important that this list does not change order as the order of # the returned ExternalPrograms will change as well - self.tools: T.Dict[str, T.Union[ExternalProgram, build.Executable]] = { + self.tools: T.Dict[str, T.Union[ExternalProgram, build.Executable, build.LocalProgram]] = { tool: NonExistingExternalProgram(tool) for tool in self._set_of_qt_tools } self.methods.update({ @@ -446,12 +446,17 @@ def _compile_resources_impl(self, state: 'ModuleState', kwargs: 'ResourceCompile for s in sources: qrc_deps.extend(self._parse_qrc_deps(state, s)) + cmd: T.List[T.Union[ExternalProgram, build.Executable, build.LocalProgram, str]] + cmd = [self.tools['rcc'], '-name', name, '-o', '@OUTPUT@'] + cmd.extend(extra_args) + cmd.append('@INPUT@') + cmd.extend(DEPFILE_ARGS) res_target = build.CustomTarget( name, state.subdir, state.subproject, state.environment, - self.tools['rcc'].get_command() + ['-name', name, '-o', '@OUTPUT@'] + extra_args + ['@INPUT@'] + DEPFILE_ARGS, + cmd, sources, [f'{name}.cpp'], depend_files=qrc_deps, @@ -467,12 +472,16 @@ def _compile_resources_impl(self, state: 'ModuleState', kwargs: 'ResourceCompile else: basename = os.path.basename(rcc_file.fname) name = f'qt{self.qt_version}-{basename.replace(".", "_")}' + cmd = [self.tools['rcc'], '-name', '@BASENAME@', '-o', '@OUTPUT@'] + cmd.extend(extra_args) + cmd.append('@INPUT@') + cmd.extend(DEPFILE_ARGS) res_target = build.CustomTarget( name, state.subdir, state.subproject, state.environment, - self.tools['rcc'].get_command() + ['-name', '@BASENAME@', '-o', '@OUTPUT@'] + extra_args + ['@INPUT@'] + DEPFILE_ARGS, + cmd, [rcc_file], [f'{name}.cpp'], depend_files=qrc_deps, @@ -727,7 +736,7 @@ def compile_translations(self, state: ModuleState, args: T.Tuple, kwargs: Compil ts = os.path.basename(ts) else: outdir = state.subdir - cmd: T.List[T.Union[ExternalProgram, build.Executable, str]] = [self.tools['lrelease'], '@INPUT@', '-qm', '@OUTPUT@'] + cmd: T.List[T.Union[ExternalProgram, build.Executable, build.LocalProgram, str]] = [self.tools['lrelease'], '@INPUT@', '-qm', '@OUTPUT@'] lrelease_target = build.CustomTarget( f'qt{self.qt_version}-compile-{ts}', outdir, @@ -867,12 +876,15 @@ def _moc_json_collect(self, state: ModuleState, kwargs: MocJsonCollectKwArgs) -> input_args.append(f'@INPUT{input_counter}@') input_counter += 1 + cmd: T.List[T.Union[ExternalProgram, build.Executable, build.LocalProgram, str]] + cmd = [self.tools['moc'], '--collect-json', '-o', '@OUTPUT@'] + cmd.extend(input_args) return build.CustomTarget( f'moc_collect_json_{target_name}', state.subdir, state.subproject, state.environment, - self.tools['moc'].get_command() + ['--collect-json', '-o', '@OUTPUT@'] + input_args, + cmd, moc_json, [f'{target_name}_json_collect.json'], description=f'Collecting json type information for {target_name}', @@ -912,12 +924,17 @@ def _gen_qml_cachegen(self, state: ModuleState, kwargs: GenQmlCachegenKwArgs) -> ressource_path = os.path.join('/', kwargs['module_prefix'], source_basename) cachegen_inputs.append(ressource_path) + cmd: T.List[T.Union[ExternalProgram, build.Executable, build.LocalProgram, str]] + cmd = [self.tools['qmlcachegen'], '-o', '@OUTPUT@', '--resource-name', f'qmlcache_{target_name}'] + cmd.extend(kwargs['extra_args']) + cmd.append('--resource=@INPUT@') + cmd.extend(cachegen_inputs) cacheloader_target = build.CustomTarget( f'cacheloader_{target_name}', state.subdir, state.subproject, state.environment, - self.tools['qmlcachegen'].get_command() + ['-o', '@OUTPUT@'] + ['--resource-name', f'qmlcache_{target_name}'] + kwargs['extra_args'] + ['--resource=@INPUT@'] + cachegen_inputs, + cmd, [kwargs['qml_qrc']], #output name format matters here [f'{target_name}_qmlcache_loader.cpp'], @@ -945,11 +962,12 @@ def _qml_type_registrar(self, state: ModuleState, kwargs: GenQmlTypeRegistrarKwA install_dir: T.List[T.Union[str, Literal[False]]] = [False] install_tag: T.List[T.Union[str, None]] = [None] - cmd = self.tools['qmltyperegistrar'].get_command() + [ + cmd = [ + self.tools['qmltyperegistrar'], '--import-name', import_name, '--major-version', major_version, '--minor-version', minor_version, - '-o', '@OUTPUT0@', + '-o', '@OUTPUT0@' ] cmd.extend(kwargs['extra_args']) diff --git a/mesonbuild/modules/dlang.py b/mesonbuild/modules/dlang.py index 35ce86be81f1..6d35e30acfd1 100644 --- a/mesonbuild/modules/dlang.py +++ b/mesonbuild/modules/dlang.py @@ -12,7 +12,7 @@ from . import ExtensionModule, ModuleInfo from .. import mlog -from ..build import InvalidArguments +from ..build import InvalidArguments, LocalProgram from ..dependencies import Dependency from ..dependencies.dub import DubDependency from ..interpreterbase import typed_pos_args @@ -27,7 +27,7 @@ from ..interpreterbase.baseobjects import TYPE_kwargs from ..programs import ExternalProgram, OverrideProgram - _AnyProgram: TypeAlias = T.Union[OverrideExecutable, ExternalProgram, OverrideProgram] + _AnyProgram: TypeAlias = T.Union[OverrideExecutable, ExternalProgram, OverrideProgram, LocalProgram] _JSONTypes: TypeAlias = T.Union[str, int, bool, None, T.List['_JSONTypes'], T.Dict[str, '_JSONTypes']] diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 0f522c106ba6..6a032596935d 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -22,7 +22,7 @@ from .. import interpreter from .. import mesonlib from .. import mlog -from ..build import CustomTarget, CustomTargetIndex, Executable, GeneratedList, InvalidArguments +from ..build import CustomTarget, CustomTargetIndex, Executable, GeneratedList, InvalidArguments, LocalProgram from ..dependencies import Dependency, InternalDependency from ..dependencies.pkgconfig import PkgConfigDependency, PkgConfigInterface from ..interpreter.type_checking import DEPENDS_KW, DEPEND_FILES_KW, ENV_KW, INSTALL_DIR_KW, INSTALL_KW, NoneType, DEPENDENCY_SOURCES_KW, in_set_validator @@ -198,7 +198,7 @@ class MkEnums(_MkEnumsCommon): vtail: T.Optional[str] depends: T.List[T.Union[BuildTarget, CustomTarget, CustomTargetIndex]] - ToolType: TypeAlias = T.Union[Executable, ExternalProgram, OverrideProgram] + ToolType: TypeAlias = T.Union[Executable, ExternalProgram, LocalProgram] # Differs from the CustomTarget version in that it straight defaults to True @@ -789,8 +789,7 @@ def postconf_hook(self, b: build.Build) -> None: if self.devenv is not None: b.devenv.append(self.devenv) - def _get_gir_dep(self, state: 'ModuleState') -> T.Tuple[Dependency, T.Union[Executable, 'ExternalProgram', 'OverrideProgram'], - T.Union[Executable, 'ExternalProgram', 'OverrideProgram']]: + def _get_gir_dep(self, state: 'ModuleState') -> T.Tuple[Dependency, ToolType, ToolType]: if not self.gir_dep: self.gir_dep = state.dependency('gobject-introspection-1.0') self.giscanner = self._find_tool(state, 'g-ir-scanner') @@ -971,7 +970,7 @@ def _make_gir_target( self, state: 'ModuleState', girfile: str, - scan_command: T.Sequence[T.Union['FileOrString', Executable, ExternalProgram, OverrideProgram]], + scan_command: T.Sequence[T.Union['FileOrString', Executable, ToolType]], 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], @@ -1020,7 +1019,7 @@ def _make_gir_target( @staticmethod def _make_typelib_target(state: 'ModuleState', typelib_output: str, - typelib_cmd: T.Sequence[T.Union[str, Executable, ExternalProgram, CustomTarget]], + typelib_cmd: T.Sequence[T.Union[str, CustomTarget, ToolType]], generated_files: T.Sequence[T.Union[str, mesonlib.File, build.GeneratedTypes]], kwargs: T.Dict[str, T.Any]) -> TypelibTarget: install = kwargs['install_typelib'] @@ -1194,7 +1193,7 @@ def generate_gir(self, state: 'ModuleState', args: T.Tuple[T.List[T.Union[Execut gir_inc_dirs: T.List[str] = [] - scan_command: T.List[T.Union[str, Executable, 'ExternalProgram', 'OverrideProgram']] = [giscanner] + scan_command: T.List[T.Union[str, ToolType]] = [giscanner] scan_command += ['--quiet'] scan_command += ['--no-libtool'] scan_command += ['--namespace=' + ns, '--nsversion=' + nsversion] @@ -1347,7 +1346,7 @@ def yelp(self, state: 'ModuleState', args: T.Tuple[str, T.List[str]], kwargs: 'Y pot_file = os.path.join('@SOURCE_ROOT@', state.subdir, 'C', project_id + '.pot') pot_sources = [os.path.join('@SOURCE_ROOT@', state.subdir, 'C', s) for s in sources] - pot_args: T.List[T.Union[ExternalProgram, Executable, OverrideProgram, str]] = [itstool, '-o', pot_file] + pot_args: T.List[T.Union[ToolType, str]] = [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, @@ -1379,7 +1378,7 @@ def yelp(self, state: 'ModuleState', args: T.Tuple[str, T.List[str]], kwargs: 'Y targets.append(l_data) po_file = l + '.po' - po_args: T.List[T.Union[ExternalProgram, Executable, OverrideProgram, str]] = [ + po_args: T.List[T.Union[ToolType, str]] = [ msgmerge, '-q', '-o', os.path.join('@SOURCE_ROOT@', l_subdir, po_file), os.path.join('@SOURCE_ROOT@', l_subdir, po_file), pot_file] @@ -2242,7 +2241,7 @@ def generate_vapi(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'Gener build_dir = os.path.join(state.environment.get_build_dir(), state.subdir) source_dir = os.path.join(state.environment.get_source_dir(), state.subdir) pkg_cmd, vapi_depends, vapi_packages, vapi_includes, packages = self._extract_vapi_packages(state, kwargs['packages']) - cmd: T.List[T.Union[ExternalProgram, Executable, OverrideProgram, str]] + cmd: T.List[T.Union[ToolType, str]] cmd = [state.find_program('vapigen'), '--quiet', f'--library={library}', f'--directory={build_dir}'] cmd.extend([f'--vapidir={d}' for d in kwargs['vapi_dirs']]) cmd.extend([f'--metadatadir={d}' for d in kwargs['metadata_dirs']]) diff --git a/mesonbuild/modules/i18n.py b/mesonbuild/modules/i18n.py index 2d8d04d3ec30..b3779f968244 100644 --- a/mesonbuild/modules/i18n.py +++ b/mesonbuild/modules/i18n.py @@ -259,7 +259,7 @@ def __init__(self, interpreter: 'Interpreter'): 'itstool_join': self.itstool_join, 'xgettext': self.xgettext, }) - self.tools: T.Dict[str, T.Optional[T.Union[ExternalProgram, build.Executable]]] = { + self.tools: T.Dict[str, T.Optional[T.Union[ExternalProgram, build.Executable, build.LocalProgram]]] = { 'itstool': None, 'msgfmt': None, 'msginit': None, diff --git a/mesonbuild/modules/icestorm.py b/mesonbuild/modules/icestorm.py index 18bf0e2022c8..a2a947d7f0b6 100644 --- a/mesonbuild/modules/icestorm.py +++ b/mesonbuild/modules/icestorm.py @@ -29,7 +29,7 @@ class IceStormModule(ExtensionModule): def __init__(self, interpreter: Interpreter) -> None: super().__init__(interpreter) - self.tools: T.Dict[str, T.Union[ExternalProgram, build.Executable]] = {} + self.tools: T.Dict[str, T.Union[ExternalProgram, build.Executable, build.LocalProgram]] = {} self.methods.update({ 'project': self.project, }) diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index a8fcc86a06b6..8cd3373d4a24 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -13,7 +13,7 @@ from . import ExtensionModule, ModuleReturnValue, ModuleInfo from .. import mesonlib, mlog from ..build import (BothLibraries, BuildTarget, CustomTargetIndex, Executable, ExtractedObjects, GeneratedList, - CustomTarget, InvalidArguments, Jar, StructuredSources, SharedLibrary, StaticLibrary) + CustomTarget, InvalidArguments, Jar, LocalProgram, StructuredSources, SharedLibrary, StaticLibrary) from ..compilers.compilers import are_asserts_disabled_for_subproject, lang_suffixes from ..interpreter.type_checking import ( DEPENDENCIES_KW, LINK_WITH_KW, LINK_WHOLE_KW, SHARED_LIB_KWS, TEST_KWS, TEST_KWS_NO_ARGS, @@ -91,7 +91,7 @@ class RustModule(ExtensionModule): def __init__(self, interpreter: Interpreter) -> None: super().__init__(interpreter) - self._bindgen_bin: T.Optional[T.Union[ExternalProgram, Executable, OverrideProgram]] = None + self._bindgen_bin: T.Optional[T.Union[ExternalProgram, Executable, OverrideProgram, LocalProgram]] = None if 'rust' in interpreter.compilers.host: rustc = T.cast('RustCompiler', interpreter.compilers.host['rust']) self._bindgen_rust_target = 'nightly' if rustc.is_nightly else rustc.version @@ -386,9 +386,7 @@ def bindgen(self, state: ModuleState, args: T.List, kwargs: FuncBindgen) -> Modu if 'Got an invalid' in err or 'is not a valid Rust target' in err: self._bindgen_rust_target = None - # TODO: Executable needs to learn about get_version - if isinstance(self._bindgen_bin, ExternalProgram): - self._bindgen_set_std = mesonlib.version_compare(self._bindgen_bin.get_version(), '>= 0.71') + self._bindgen_set_std = mesonlib.version_compare(self._bindgen_bin.get_version(), '>= 0.71') name: str if isinstance(header, File): diff --git a/mesonbuild/modules/wayland.py b/mesonbuild/modules/wayland.py index 94c6f819db5e..458a0a78d4da 100644 --- a/mesonbuild/modules/wayland.py +++ b/mesonbuild/modules/wayland.py @@ -6,7 +6,7 @@ import typing as T from . import ExtensionModule, ModuleReturnValue, ModuleInfo -from ..build import CustomTarget +from ..build import CustomTarget, LocalProgram from ..interpreter.type_checking import NoneType, in_set_validator from ..interpreterbase import typed_pos_args, typed_kwargs, KwargInfo, FeatureNew from ..mesonlib import File, MesonException @@ -42,7 +42,7 @@ def __init__(self, interpreter: Interpreter) -> None: self.protocols_dep: T.Optional[Dependency] = None self.pkgdatadir: T.Optional[str] = None - self.scanner_bin: T.Optional[T.Union[ExternalProgram, Executable]] = None + self.scanner_bin: T.Optional[T.Union[ExternalProgram, Executable, LocalProgram]] = None self.methods.update({ 'scan_xml': self.scan_xml, From 457fb3f688517c384079e83e52711466b8f2164d Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Sun, 12 Oct 2025 10:20:47 -0400 Subject: [PATCH 07/12] Replace OverrideExecutable and OverrideProgram with LocalProgram --- mesonbuild/build.py | 14 +------ mesonbuild/interpreter/interpreter.py | 18 ++++---- mesonbuild/interpreter/interpreterobjects.py | 44 +++++++------------- mesonbuild/interpreter/mesonmain.py | 7 ++-- mesonbuild/modules/__init__.py | 5 +-- mesonbuild/modules/_qt.py | 12 +++--- mesonbuild/modules/codegen.py | 5 +-- mesonbuild/modules/dlang.py | 5 +-- mesonbuild/modules/gnome.py | 7 ++-- mesonbuild/modules/i18n.py | 2 +- mesonbuild/modules/icestorm.py | 2 +- mesonbuild/modules/python.py | 6 +-- mesonbuild/modules/rust.py | 3 +- mesonbuild/modules/wayland.py | 3 +- mesonbuild/programs.py | 11 ----- 15 files changed, 49 insertions(+), 95 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 6aebd5c32949..ba6570dcf6e6 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -363,7 +363,7 @@ def __init__(self, environment: Environment): self.stdlibs = PerMachine({}, {}) self.test_setups: T.Dict[str, TestSetup] = {} self.test_setup_default_name = None - self.find_overrides: T.Dict[str, T.Union[OverrideExecutable, programs.ExternalProgram, programs.OverrideProgram, LocalProgram]] = {} + self.find_overrides: T.Dict[str, T.Union[programs.ExternalProgram, LocalProgram]] = {} 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 @@ -3350,18 +3350,6 @@ def get(self, name: str) -> T.Tuple[T.Union[str, int, bool], T.Optional[str]]: def keys(self) -> T.Iterator[str]: return self.values.keys() -class OverrideExecutable(Executable): - def __init__(self, executable: Executable, version: str): - self._executable = executable - self._version = version - - def __getattr__(self, name: str) -> T.Any: - _executable = object.__getattribute__(self, '_executable') - return getattr(_executable, name) - - def get_version(self, interpreter: T.Optional[Interpreter] = None) -> str: - return self._version - class LocalProgram(HoldableObject): ''' A wrapper for a program that may have build dependencies.''' def __init__(self, program: T.Union[programs.ExternalProgram, Executable, CustomTarget, CustomTargetIndex], version: str, diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 9fbcb805b661..8c094e4d4f9f 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -119,7 +119,6 @@ from ..backend.backends import Backend from ..interpreterbase.baseobjects import InterpreterObject, TYPE_var, TYPE_kwargs from ..options import OptionDict - from ..programs import OverrideProgram from .type_checking import SourcesVarargsType # Input source types passed to Targets @@ -131,7 +130,7 @@ BuildTargetSource = T.Union[mesonlib.FileOrString, build.GeneratedTypes, build.StructuredSources] - ProgramVersionFunc = T.Callable[[T.Union[ExternalProgram, build.Executable, OverrideProgram]], str] + ProgramVersionFunc = T.Callable[[T.Union[ExternalProgram, build.LocalProgram]], str] TestClass = T.TypeVar('TestClass', bound=Test) @@ -419,7 +418,6 @@ def build_holder_map(self) -> None: build.Generator: OBJ.GeneratorHolder, build.GeneratedList: OBJ.GeneratedListHolder, build.ExtractedObjects: OBJ.GeneratedObjectsHolder, - build.OverrideExecutable: OBJ.OverrideExecutableHolder, build.LocalProgram: OBJ.LocalProgramHolder, build.RunTarget: OBJ.RunTargetHolder, build.AliasTarget: OBJ.AliasTargetHolder, @@ -1598,7 +1596,7 @@ def program_from_system(self, args: T.List[mesonlib.FileOrString], search_dirs: def program_from_overrides(self, command_names: T.List[mesonlib.FileOrString], extra_info: T.List['mlog.TV_Loggable'] - ) -> T.Optional[T.Union[ExternalProgram, OverrideProgram, build.OverrideExecutable, build.LocalProgram]]: + ) -> T.Optional[T.Union[ExternalProgram, build.LocalProgram]]: for name in command_names: if not isinstance(name, str): continue @@ -1613,7 +1611,7 @@ def store_name_lookups(self, command_names: T.List[mesonlib.FileOrString]) -> No if isinstance(name, str): self.build.searched_programs.add(name) - def add_find_program_override(self, name: str, exe: T.Union[build.OverrideExecutable, ExternalProgram, OverrideProgram, build.LocalProgram]) -> None: + def add_find_program_override(self, name: str, exe: T.Union[ExternalProgram, build.LocalProgram]) -> 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.build.find_overrides: @@ -1638,7 +1636,7 @@ def find_program_impl(self, args: T.List[mesonlib.FileOrString], search_dirs: T.Optional[T.List[str]] = None, version_arg: T.Optional[str] = '', version_func: T.Optional[ProgramVersionFunc] = None - ) -> T.Union[ExternalProgram, build.OverrideExecutable, OverrideProgram, build.LocalProgram]: + ) -> T.Union[ExternalProgram, build.LocalProgram]: args = mesonlib.listify(args) extra_info: T.List[mlog.TV_Loggable] = [] @@ -1670,7 +1668,7 @@ def program_lookup(self, args: T.List[mesonlib.FileOrString], for_machine: Machi version_arg: T.Optional[str], version_func: T.Optional[ProgramVersionFunc], extra_info: T.List[mlog.TV_Loggable] - ) -> T.Optional[T.Union[ExternalProgram, build.Executable, OverrideProgram, build.LocalProgram]]: + ) -> T.Optional[T.Union[ExternalProgram, build.LocalProgram]]: progobj = self.program_from_overrides(args, extra_info) if progobj: return progobj @@ -1706,7 +1704,7 @@ def program_lookup(self, args: T.List[mesonlib.FileOrString], for_machine: Machi return progobj - def check_program_version(self, progobj: T.Union[ExternalProgram, build.Executable, OverrideProgram, build.LocalProgram], + def check_program_version(self, progobj: T.Union[ExternalProgram, build.LocalProgram], wanted: T.Union[str, T.List[str]], version_func: T.Optional[ProgramVersionFunc], extra_info: T.List[mlog.TV_Loggable]) -> bool: @@ -1733,7 +1731,7 @@ def check_program_version(self, progobj: T.Union[ExternalProgram, build.Executab def find_program_fallback(self, fallback: str, args: T.List[mesonlib.FileOrString], default_options: OptionDict, required: bool, extra_info: T.List[mlog.TV_Loggable] - ) -> T.Optional[T.Union[ExternalProgram, build.Executable, OverrideProgram]]: + ) -> T.Optional[T.Union[ExternalProgram, build.LocalProgram]]: mlog.log('Fallback to subproject', mlog.bold(fallback), 'which provides program', mlog.bold(' '.join(args))) sp_kwargs: kwtypes.DoSubproject = { @@ -1760,7 +1758,7 @@ def find_program_fallback(self, fallback: str, args: T.List[mesonlib.FileOrStrin @disablerIfNotFound def func_find_program(self, node: mparser.BaseNode, args: T.Tuple[T.List[mesonlib.FileOrString]], kwargs: 'kwtypes.FindProgram', - ) -> T.Union['build.Executable', ExternalProgram, 'OverrideProgram']: + ) -> T.Union[ExternalProgram, build.LocalProgram]: 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') diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index 63de63b569a0..7fcd94e0d7ec 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -633,10 +633,10 @@ def as_shared_method(self, args: T.List[TYPE_var], kwargs: InternalDependencyAsK raise InterpreterException('as_shared method is only supported on declare_dependency() objects') return self.held_object.get_as_shared(kwargs['recursive']) -_EXTPROG = T.TypeVar('_EXTPROG', bound=ExternalProgram) +_BASEPROG = T.TypeVar('_BASEPROG', bound=T.Union[ExternalProgram, build.LocalProgram]) -class _ExternalProgramHolder(ObjectHolder[_EXTPROG]): - def __init__(self, ep: _EXTPROG, interpreter: 'Interpreter') -> None: +class _BaseProgramHolder(ObjectHolder[_BASEPROG]): + def __init__(self, ep: _BASEPROG, interpreter: 'Interpreter') -> None: super().__init__(ep, interpreter) @noPosargs @@ -647,15 +647,15 @@ def found_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool: @noPosargs @noKwargs - @FeatureDeprecated('ExternalProgram.path', '0.55.0', - 'use ExternalProgram.full_path() instead') + @FeatureDeprecated('Program.path', '0.55.0', + 'use Program.full_path() instead') @InterpreterObject.method('path') def path_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: return self._full_path() @noPosargs @noKwargs - @FeatureNew('ExternalProgram.full_path', '0.55.0') + @FeatureNew('Program.full_path', '0.55.0') @InterpreterObject.method('full_path') def full_path_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: return self._full_path() @@ -669,7 +669,7 @@ def _full_path(self) -> str: @noPosargs @noKwargs - @FeatureNew('ExternalProgram.cmd_array', '1.10.0') + @FeatureNew('Program.cmd_array', '1.10.0') @InterpreterObject.method('cmd_array') def cmd_array_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.List[str]: if not self.found(): @@ -680,9 +680,11 @@ def cmd_array_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.Lis @noPosargs @noKwargs - @FeatureNew('ExternalProgram.version', '0.62.0') + @FeatureNew('Program.version', '0.62.0') @InterpreterObject.method('version') def version_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: + if isinstance(self.held_object, build.LocalProgram) and isinstance(self.held_object.program, build.Executable): + FeatureNew.single_use('Program.version with an executable', '1.9.0', subproject=self.subproject, location=self.current_node) if not self.found(): raise InterpreterException('Unable to get the version of a not-found external program') try: @@ -693,7 +695,10 @@ def version_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: def found(self) -> bool: return self.held_object.found() -class ExternalProgramHolder(_ExternalProgramHolder[ExternalProgram]): +class ExternalProgramHolder(_BaseProgramHolder[ExternalProgram]): + pass + +class LocalProgramHolder(_BaseProgramHolder[build.LocalProgram]): pass class ExternalLibraryHolder(ObjectHolder[ExternalLibrary]): @@ -1204,24 +1209,3 @@ class StructuredSourcesHolder(ObjectHolder[build.StructuredSources]): def __init__(self, sources: build.StructuredSources, interp: 'Interpreter'): super().__init__(sources, interp) - -class OverrideExecutableHolder(BuildTargetHolder[build.OverrideExecutable]): - @noPosargs - @noKwargs - @FeatureNew('OverrideExecutable.version', '1.9.0') - @InterpreterObject.method('version') - def version_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: - return self.held_object.get_version(self.interpreter) - -class LocalProgramHolder(ObjectHolder[build.LocalProgram]): - @noPosargs - @noKwargs - @InterpreterObject.method('version') - def version_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: - return self.held_object.version - - @noPosargs - @noKwargs - @InterpreterObject.method('found') - def found_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool: - return True diff --git a/mesonbuild/interpreter/mesonmain.py b/mesonbuild/interpreter/mesonmain.py index da74fe0d0d27..e45af6ff6391 100644 --- a/mesonbuild/interpreter/mesonmain.py +++ b/mesonbuild/interpreter/mesonmain.py @@ -14,7 +14,7 @@ from ..mesonlib import MachineChoice from ..options import OptionKey -from ..programs import OverrideProgram, ExternalProgram +from ..programs import ExternalProgram from ..interpreter.type_checking import ENV_KW, ENV_METHOD_KW, ENV_SEPARATOR_KW, env_convertor_with_method from ..interpreterbase import (MesonInterpreterObject, FeatureNew, FeatureDeprecated, typed_pos_args, noArgsFlattening, noPosargs, noKwargs, @@ -323,9 +323,10 @@ def override_find_program_method(self, args: T.Tuple[str, T.Union[mesonlib.File, self.interpreter.environment.build_dir) if not os.path.exists(abspath): raise InterpreterException(f'Tried to override {name} with a file that does not exist.') - exe = OverrideProgram(name, self.interpreter.project_version, command=[abspath]) + prog = ExternalProgram(name, command=[abspath], silent=True) + exe = build.LocalProgram(prog, self.interpreter.project_version) elif isinstance(exe, build.Executable): - exe = build.OverrideExecutable(exe, self.interpreter.project_version) + exe = build.LocalProgram(exe, self.interpreter.project_version) self.interpreter.add_find_program_override(name, exe) @typed_kwargs( diff --git a/mesonbuild/modules/__init__.py b/mesonbuild/modules/__init__.py index 38f8e0aaf38e..9fceca576af0 100644 --- a/mesonbuild/modules/__init__.py +++ b/mesonbuild/modules/__init__.py @@ -18,7 +18,6 @@ from ..interpreter import Interpreter from ..interpreter.interpreter import ProgramVersionFunc from ..interpreterbase import TYPE_var, TYPE_kwargs - from ..programs import OverrideProgram from ..dependencies import Dependency from ..options import ElementaryOptionValues @@ -75,14 +74,14 @@ def find_program(self, prog: T.Union[mesonlib.FileOrString, T.List[mesonlib.File required: bool = True, version_func: T.Optional[ProgramVersionFunc] = None, wanted: T.Union[str, T.List[str]] = '', silent: bool = False, - for_machine: MachineChoice = MachineChoice.HOST) -> T.Union[ExternalProgram, build.OverrideExecutable, OverrideProgram, build.LocalProgram]: + for_machine: MachineChoice = MachineChoice.HOST) -> T.Union[ExternalProgram, build.LocalProgram]: if not isinstance(prog, list): prog = [prog] return self._interpreter.find_program_impl(prog, required=required, version_func=version_func, wanted=wanted, silent=silent, for_machine=for_machine) def find_tool(self, name: str, depname: str, varname: str, required: bool = True, - wanted: T.Optional[str] = None) -> T.Union[build.OverrideExecutable, ExternalProgram, OverrideProgram, build.LocalProgram]: + wanted: T.Optional[str] = None) -> T.Union[ExternalProgram, build.LocalProgram]: # Look in overrides in case it's built as subproject progobj = self._interpreter.program_from_overrides([name], []) if progobj is not None: diff --git a/mesonbuild/modules/_qt.py b/mesonbuild/modules/_qt.py index 11b2a4c90086..2f24aa142d35 100644 --- a/mesonbuild/modules/_qt.py +++ b/mesonbuild/modules/_qt.py @@ -209,7 +209,7 @@ def __init__(self, interpreter: Interpreter, qt_version: int = 5): self.qt_version = qt_version # It is important that this list does not change order as the order of # the returned ExternalPrograms will change as well - self.tools: T.Dict[str, T.Union[ExternalProgram, build.Executable, build.LocalProgram]] = { + self.tools: T.Dict[str, T.Union[ExternalProgram, build.LocalProgram]] = { tool: NonExistingExternalProgram(tool) for tool in self._set_of_qt_tools } self.methods.update({ @@ -251,7 +251,7 @@ def gen_bins() -> T.Generator[T.Tuple[str, str], None, None]: arg = ['-v'] # Ensure that the version of qt and each tool are the same - def get_version(p: T.Union[ExternalProgram, build.Executable]) -> str: + def get_version(p: T.Union[ExternalProgram, build.LocalProgram]) -> str: _, out, err = Popen_safe(p.get_command() + arg) if name == 'lrelease' or not qt_dep.version.startswith('4'): care = out @@ -446,7 +446,7 @@ def _compile_resources_impl(self, state: 'ModuleState', kwargs: 'ResourceCompile for s in sources: qrc_deps.extend(self._parse_qrc_deps(state, s)) - cmd: T.List[T.Union[ExternalProgram, build.Executable, build.LocalProgram, str]] + cmd: T.List[T.Union[ExternalProgram, build.LocalProgram, str]] cmd = [self.tools['rcc'], '-name', name, '-o', '@OUTPUT@'] cmd.extend(extra_args) cmd.append('@INPUT@') @@ -736,7 +736,7 @@ def compile_translations(self, state: ModuleState, args: T.Tuple, kwargs: Compil ts = os.path.basename(ts) else: outdir = state.subdir - cmd: T.List[T.Union[ExternalProgram, build.Executable, build.LocalProgram, str]] = [self.tools['lrelease'], '@INPUT@', '-qm', '@OUTPUT@'] + cmd: T.List[T.Union[ExternalProgram, build.LocalProgram, str]] = [self.tools['lrelease'], '@INPUT@', '-qm', '@OUTPUT@'] lrelease_target = build.CustomTarget( f'qt{self.qt_version}-compile-{ts}', outdir, @@ -876,7 +876,7 @@ def _moc_json_collect(self, state: ModuleState, kwargs: MocJsonCollectKwArgs) -> input_args.append(f'@INPUT{input_counter}@') input_counter += 1 - cmd: T.List[T.Union[ExternalProgram, build.Executable, build.LocalProgram, str]] + cmd: T.List[T.Union[ExternalProgram, build.LocalProgram, str]] cmd = [self.tools['moc'], '--collect-json', '-o', '@OUTPUT@'] cmd.extend(input_args) return build.CustomTarget( @@ -924,7 +924,7 @@ def _gen_qml_cachegen(self, state: ModuleState, kwargs: GenQmlCachegenKwArgs) -> ressource_path = os.path.join('/', kwargs['module_prefix'], source_basename) cachegen_inputs.append(ressource_path) - cmd: T.List[T.Union[ExternalProgram, build.Executable, build.LocalProgram, str]] + cmd: T.List[T.Union[ExternalProgram, build.LocalProgram, str]] cmd = [self.tools['qmlcachegen'], '-o', '@OUTPUT@', '--resource-name', f'qmlcache_{target_name}'] cmd.extend(kwargs['extra_args']) cmd.append('--resource=@INPUT@') diff --git a/mesonbuild/modules/codegen.py b/mesonbuild/modules/codegen.py index f37f964c4255..171f572713f2 100644 --- a/mesonbuild/modules/codegen.py +++ b/mesonbuild/modules/codegen.py @@ -25,14 +25,13 @@ from . import ModuleState from .._typing import ImmutableListProtocol - from ..build import Executable + from ..build import LocalProgram from ..interpreter import Interpreter from ..interpreter.kwargs import ExtractRequired from ..interpreterbase import TYPE_var, TYPE_kwargs from ..mesonlib import MachineChoice - from ..programs import OverrideProgram - Program: TypeAlias = T.Union[Executable, ExternalProgram, OverrideProgram] + Program: TypeAlias = T.Union[ExternalProgram, LocalProgram] LexImpls = Literal['lex', 'flex', 'reflex', 'win_flex'] YaccImpls = Literal['yacc', 'byacc', 'bison', 'win_bison'] diff --git a/mesonbuild/modules/dlang.py b/mesonbuild/modules/dlang.py index 6d35e30acfd1..860a62481cef 100644 --- a/mesonbuild/modules/dlang.py +++ b/mesonbuild/modules/dlang.py @@ -22,12 +22,11 @@ from typing_extensions import Literal, TypeAlias from . import ModuleState - from ..build import OverrideExecutable from ..interpreter.interpreter import Interpreter from ..interpreterbase.baseobjects import TYPE_kwargs - from ..programs import ExternalProgram, OverrideProgram + from ..programs import ExternalProgram - _AnyProgram: TypeAlias = T.Union[OverrideExecutable, ExternalProgram, OverrideProgram, LocalProgram] + _AnyProgram: TypeAlias = T.Union[ExternalProgram, LocalProgram] _JSONTypes: TypeAlias = T.Union[str, int, bool, None, T.List['_JSONTypes'], T.Dict[str, '_JSONTypes']] diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 6a032596935d..d8a4b2fac663 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -33,7 +33,6 @@ MachineChoice, MesonException, OrderedSet, Popen_safe, join_args, quote_arg ) from ..options import OptionKey -from ..programs import OverrideProgram from ..scripts.gettext import read_linguas if T.TYPE_CHECKING: @@ -198,7 +197,7 @@ class MkEnums(_MkEnumsCommon): vtail: T.Optional[str] depends: T.List[T.Union[BuildTarget, CustomTarget, CustomTargetIndex]] - ToolType: TypeAlias = T.Union[Executable, ExternalProgram, LocalProgram] + ToolType: TypeAlias = T.Union[ExternalProgram, LocalProgram] # Differs from the CustomTarget version in that it straight defaults to True @@ -809,7 +808,7 @@ def _giscanner_version_compare(self, state: 'ModuleState', cmp: str) -> bool: @functools.lru_cache(maxsize=None) def _gir_has_option(self, option: str) -> bool: exe = self.giscanner - if isinstance(exe, (Executable, OverrideProgram)): + if isinstance(exe, LocalProgram): # Handle overridden g-ir-scanner assert option in {'--extra-library', '--sources-top-dirs'} return True @@ -1193,7 +1192,7 @@ def generate_gir(self, state: 'ModuleState', args: T.Tuple[T.List[T.Union[Execut gir_inc_dirs: T.List[str] = [] - scan_command: T.List[T.Union[str, ToolType]] = [giscanner] + scan_command: T.List[T.Union[str, ToolType, Executable]] = [giscanner] scan_command += ['--quiet'] scan_command += ['--no-libtool'] scan_command += ['--namespace=' + ns, '--nsversion=' + nsversion] diff --git a/mesonbuild/modules/i18n.py b/mesonbuild/modules/i18n.py index b3779f968244..06e891714d24 100644 --- a/mesonbuild/modules/i18n.py +++ b/mesonbuild/modules/i18n.py @@ -259,7 +259,7 @@ def __init__(self, interpreter: 'Interpreter'): 'itstool_join': self.itstool_join, 'xgettext': self.xgettext, }) - self.tools: T.Dict[str, T.Optional[T.Union[ExternalProgram, build.Executable, build.LocalProgram]]] = { + self.tools: T.Dict[str, T.Optional[T.Union[ExternalProgram, build.LocalProgram]]] = { 'itstool': None, 'msgfmt': None, 'msginit': None, diff --git a/mesonbuild/modules/icestorm.py b/mesonbuild/modules/icestorm.py index a2a947d7f0b6..86af78d8b73c 100644 --- a/mesonbuild/modules/icestorm.py +++ b/mesonbuild/modules/icestorm.py @@ -29,7 +29,7 @@ class IceStormModule(ExtensionModule): def __init__(self, interpreter: Interpreter) -> None: super().__init__(interpreter) - self.tools: T.Dict[str, T.Union[ExternalProgram, build.Executable, build.LocalProgram]] = {} + self.tools: T.Dict[str, T.Union[ExternalProgram, build.LocalProgram]] = {} self.methods.update({ 'project': self.project, }) diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py index 99602c05ad4c..d9044f8182da 100644 --- a/mesonbuild/modules/python.py +++ b/mesonbuild/modules/python.py @@ -15,7 +15,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.interpreterobjects import _ExternalProgramHolder +from ..interpreter.interpreterobjects import _BaseProgramHolder from ..interpreter.type_checking import NoneType, DEPENDENCY_KWS, PRESERVE_PATH_KW, SHARED_MOD_KWS from ..interpreterbase import ( noPosargs, noKwargs, permittedKwargs, ContainerTypeInfo, @@ -109,9 +109,9 @@ def _get_path(self, state: T.Optional['ModuleState'], key: str) -> str: _LIMITED_API_KW = KwargInfo('limited_api', str, default='', since='1.3.0') _DEFAULTABLE_SUBDIR_KW = KwargInfo('subdir', (str, NoneType)) -class PythonInstallation(_ExternalProgramHolder['PythonExternalProgram']): +class PythonInstallation(_BaseProgramHolder['PythonExternalProgram']): def __init__(self, python: 'PythonExternalProgram', interpreter: 'Interpreter'): - _ExternalProgramHolder.__init__(self, python, interpreter) + _BaseProgramHolder.__init__(self, python, interpreter) info = python.info prefix = self.interpreter.environment.coredata.optstore.get_value_for(OptionKey('prefix')) assert isinstance(prefix, str), 'for mypy' diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index 8cd3373d4a24..46e95e80bb21 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -33,7 +33,6 @@ from ..interpreter import kwargs as _kwargs from ..interpreter.interpreter import SourceInputs, SourceOutputs from ..interpreter.interpreterobjects import Test - from ..programs import OverrideProgram from ..interpreter.type_checking import SourcesVarargsType from typing_extensions import TypedDict, Literal @@ -91,7 +90,7 @@ class RustModule(ExtensionModule): def __init__(self, interpreter: Interpreter) -> None: super().__init__(interpreter) - self._bindgen_bin: T.Optional[T.Union[ExternalProgram, Executable, OverrideProgram, LocalProgram]] = None + self._bindgen_bin: T.Optional[T.Union[ExternalProgram, LocalProgram]] = None if 'rust' in interpreter.compilers.host: rustc = T.cast('RustCompiler', interpreter.compilers.host['rust']) self._bindgen_rust_target = 'nightly' if rustc.is_nightly else rustc.version diff --git a/mesonbuild/modules/wayland.py b/mesonbuild/modules/wayland.py index 458a0a78d4da..675a6d9740e0 100644 --- a/mesonbuild/modules/wayland.py +++ b/mesonbuild/modules/wayland.py @@ -15,7 +15,6 @@ from typing_extensions import Literal, TypedDict from . import ModuleState - from ..build import Executable from ..dependencies import Dependency from ..interpreter import Interpreter from ..programs import ExternalProgram @@ -42,7 +41,7 @@ def __init__(self, interpreter: Interpreter) -> None: self.protocols_dep: T.Optional[Dependency] = None self.pkgdatadir: T.Optional[str] = None - self.scanner_bin: T.Optional[T.Union[ExternalProgram, Executable, LocalProgram]] = None + self.scanner_bin: T.Optional[T.Union[ExternalProgram, LocalProgram]] = None self.methods.update({ 'scan_xml': self.scan_xml, diff --git a/mesonbuild/programs.py b/mesonbuild/programs.py index a1cfc06e98a2..d06a8bb2966b 100644 --- a/mesonbuild/programs.py +++ b/mesonbuild/programs.py @@ -362,17 +362,6 @@ def found(self) -> bool: return False -class OverrideProgram(ExternalProgram): - - """A script overriding a program.""" - - def __init__(self, name: str, version: str, command: T.Optional[T.List[str]] = None, - silent: bool = False, search_dirs: T.Optional[T.List[T.Optional[str]]] = None, - exclude_paths: T.Optional[T.List[str]] = None): - super().__init__(name, command=command, silent=silent, - search_dirs=search_dirs, exclude_paths=exclude_paths) - self.cached_version = version - def find_external_program(env: 'Environment', for_machine: MachineChoice, name: str, display_name: str, default_names: T.List[str], allow_default_for_cross: bool = True, From 8834ae6699e9acb6c549a7fa3c6947eb64c2501b Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Mon, 13 Oct 2025 15:49:24 -0400 Subject: [PATCH 08/12] Add common ABC for ExternalProgram and LocalProgram --- mesonbuild/build.py | 21 +------------- mesonbuild/interpreter/__init__.py | 4 +-- mesonbuild/interpreter/interpreter.py | 5 ++-- mesonbuild/interpreter/interpreterobjects.py | 11 ++------ mesonbuild/modules/python.py | 6 ++-- mesonbuild/modules/rust.py | 5 +--- mesonbuild/programs.py | 29 +++++++++++++++++++- 7 files changed, 39 insertions(+), 42 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index ba6570dcf6e6..f3591797cce8 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2245,10 +2245,6 @@ def post_init(self) -> None: def get_default_install_dir(self) -> T.Union[T.Tuple[str, str], T.Tuple[None, None]]: return self.environment.get_bindir(), '{bindir}' - def description(self): - '''Human friendly description of the executable''' - return self.name - def type_suffix(self): return "@exe" @@ -2271,21 +2267,6 @@ def get_debug_filename(self) -> T.Optional[str]: def is_linkable_target(self) -> bool: return self.is_linkwithable - def get_command(self) -> 'ImmutableListProtocol[str]': - """Provides compatibility with ExternalProgram. - - Since you can override ExternalProgram instances with Executables. - """ - return self.outputs - - def get_path(self) -> str: - """Provides compatibility with ExternalProgram.""" - return os.path.join(self.subdir, self.filename) - - def found(self) -> bool: - """Provides compatibility with ExternalProgram.""" - return True - class StaticLibrary(BuildTarget): known_kwargs = known_stlib_kwargs @@ -3350,7 +3331,7 @@ def get(self, name: str) -> T.Tuple[T.Union[str, int, bool], T.Optional[str]]: def keys(self) -> T.Iterator[str]: return self.values.keys() -class LocalProgram(HoldableObject): +class LocalProgram(programs.BaseProgram): ''' A wrapper for a program that may have build dependencies.''' def __init__(self, program: T.Union[programs.ExternalProgram, Executable, CustomTarget, CustomTargetIndex], version: str, depends: T.Optional[T.List[T.Union[BuildTarget, CustomTarget]]] = None, diff --git a/mesonbuild/interpreter/__init__.py b/mesonbuild/interpreter/__init__.py index 600798f1a5b8..72f0178a5c4d 100644 --- a/mesonbuild/interpreter/__init__.py +++ b/mesonbuild/interpreter/__init__.py @@ -19,7 +19,6 @@ 'SubprojectHolder', 'DependencyHolder', 'GeneratedListHolder', - 'ExternalProgramHolder', 'extract_required_kwarg', 'ArrayHolder', @@ -34,8 +33,7 @@ from .interpreterobjects import (ExecutableHolder, BuildTargetHolder, CustomTargetHolder, CustomTargetIndexHolder, MachineHolder, Test, ConfigurationDataHolder, SubprojectHolder, DependencyHolder, - GeneratedListHolder, ExternalProgramHolder, - extract_required_kwarg) + GeneratedListHolder, extract_required_kwarg) from .primitives import ( ArrayHolder, diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 8c094e4d4f9f..b29d44169f40 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -21,7 +21,7 @@ FileMode, MachineChoice, is_parent_path, listify, extract_as_list, has_path_sep, path_is_in_root, PerMachine) from ..options import OptionKey -from ..programs import ExternalProgram, NonExistingExternalProgram +from ..programs import ExternalProgram, NonExistingExternalProgram, BaseProgram from ..dependencies import Dependency from ..depfile import DepFile from ..interpreterbase import ContainerTypeInfo, InterpreterBase, KwargInfo, typed_kwargs, typed_pos_args @@ -418,7 +418,6 @@ def build_holder_map(self) -> None: build.Generator: OBJ.GeneratorHolder, build.GeneratedList: OBJ.GeneratedListHolder, build.ExtractedObjects: OBJ.GeneratedObjectsHolder, - build.LocalProgram: OBJ.LocalProgramHolder, build.RunTarget: OBJ.RunTargetHolder, build.AliasTarget: OBJ.AliasTargetHolder, build.Headers: OBJ.HeadersHolder, @@ -448,7 +447,7 @@ def build_holder_map(self) -> None: ''' self.bound_holder_map.update({ dependencies.Dependency: OBJ.DependencyHolder, - ExternalProgram: OBJ.ExternalProgramHolder, + BaseProgram: OBJ.BaseProgramHolder, compilers.Compiler: compilerOBJ.CompilerHolder, ModuleObject: OBJ.ModuleObjectHolder, MutableModuleObject: OBJ.MutableModuleObjectHolder, diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index 7fcd94e0d7ec..7dd974e8bc49 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -23,7 +23,7 @@ flatten, resolve_second_level_holders, 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 +from ..programs import ExternalProgram, BaseProgram from ..mesonlib import HoldableObject, listify, Popen_safe import typing as T @@ -633,9 +633,9 @@ def as_shared_method(self, args: T.List[TYPE_var], kwargs: InternalDependencyAsK raise InterpreterException('as_shared method is only supported on declare_dependency() objects') return self.held_object.get_as_shared(kwargs['recursive']) -_BASEPROG = T.TypeVar('_BASEPROG', bound=T.Union[ExternalProgram, build.LocalProgram]) +_BASEPROG = T.TypeVar('_BASEPROG', bound=BaseProgram) -class _BaseProgramHolder(ObjectHolder[_BASEPROG]): +class BaseProgramHolder(ObjectHolder[_BASEPROG]): def __init__(self, ep: _BASEPROG, interpreter: 'Interpreter') -> None: super().__init__(ep, interpreter) @@ -695,11 +695,6 @@ def version_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: def found(self) -> bool: return self.held_object.found() -class ExternalProgramHolder(_BaseProgramHolder[ExternalProgram]): - pass - -class LocalProgramHolder(_BaseProgramHolder[build.LocalProgram]): - pass class ExternalLibraryHolder(ObjectHolder[ExternalLibrary]): def __init__(self, el: ExternalLibrary, interpreter: 'Interpreter'): diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py index d9044f8182da..e0d57a66c9e9 100644 --- a/mesonbuild/modules/python.py +++ b/mesonbuild/modules/python.py @@ -15,7 +15,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.interpreterobjects import _BaseProgramHolder +from ..interpreter.interpreterobjects import BaseProgramHolder from ..interpreter.type_checking import NoneType, DEPENDENCY_KWS, PRESERVE_PATH_KW, SHARED_MOD_KWS from ..interpreterbase import ( noPosargs, noKwargs, permittedKwargs, ContainerTypeInfo, @@ -109,9 +109,9 @@ def _get_path(self, state: T.Optional['ModuleState'], key: str) -> str: _LIMITED_API_KW = KwargInfo('limited_api', str, default='', since='1.3.0') _DEFAULTABLE_SUBDIR_KW = KwargInfo('subdir', (str, NoneType)) -class PythonInstallation(_BaseProgramHolder['PythonExternalProgram']): +class PythonInstallation(BaseProgramHolder['PythonExternalProgram']): def __init__(self, python: 'PythonExternalProgram', interpreter: 'Interpreter'): - _BaseProgramHolder.__init__(self, python, interpreter) + BaseProgramHolder.__init__(self, python, interpreter) info = python.info prefix = self.interpreter.environment.coredata.optstore.get_value_for(OptionKey('prefix')) assert isinstance(prefix, str), 'for mypy' diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index 46e95e80bb21..43a7ff4efcb1 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -374,10 +374,7 @@ def bindgen(self, state: ModuleState, args: T.List, kwargs: FuncBindgen) -> Modu if self._bindgen_bin is None: self._bindgen_bin = state.find_program('bindgen', wanted=kwargs['bindgen_version']) if self._bindgen_rust_target is not None: - # ExternalCommand.command's type is bonkers - _, _, err = mesonlib.Popen_safe( - T.cast('T.List[str]', self._bindgen_bin.get_command()) + - ['--rust-target', self._bindgen_rust_target]) + _, _, err = mesonlib.Popen_safe(self._bindgen_bin.get_command() + ['--rust-target', self._bindgen_rust_target]) # < 0.71: Sometimes this is "invalid Rust target" and # sometimes "invalid # rust target" # >= 0.71: error: invalid value '...' for '--rust-target ': "..." is not a valid Rust target, accepted values are of the form ... diff --git a/mesonbuild/programs.py b/mesonbuild/programs.py index d06a8bb2966b..7bcec59456ba 100644 --- a/mesonbuild/programs.py +++ b/mesonbuild/programs.py @@ -13,6 +13,7 @@ import re import typing as T from pathlib import Path +from abc import ABCMeta, abstractmethod from . import mesonlib from . import mlog @@ -23,7 +24,33 @@ from .interpreter import Interpreter -class ExternalProgram(mesonlib.HoldableObject): +class BaseProgram(mesonlib.HoldableObject, metaclass=ABCMeta): + ''' A base class for LocalProgram and ExternalProgram.''' + + name: str + + @abstractmethod + def found(self) -> bool: + pass + + @abstractmethod + def get_version(self, interpreter: T.Optional[Interpreter] = None) -> str: + pass + + @abstractmethod + def get_command(self) -> T.List[str]: + pass + + @abstractmethod + def get_path(self) -> T.Optional[str]: + pass + + @abstractmethod + def description(self) -> str: + '''Human friendly description of the command''' + + +class ExternalProgram(BaseProgram): """A program that is found on the system. :param name: The name of the program From 8b0a37735fe848a0c70eb36347f6bbd05ff433fd Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Wed, 15 Oct 2025 09:36:08 -0400 Subject: [PATCH 09/12] Add support for LocalProgram to add_*_script() --- mesonbuild/interpreter/mesonmain.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/mesonbuild/interpreter/mesonmain.py b/mesonbuild/interpreter/mesonmain.py index e45af6ff6391..a766f788c077 100644 --- a/mesonbuild/interpreter/mesonmain.py +++ b/mesonbuild/interpreter/mesonmain.py @@ -58,21 +58,23 @@ def __init__(self, build: 'build.Build', interpreter: 'Interpreter'): self.interpreter = interpreter def _find_source_script( - self, name: str, prog: T.Union[str, mesonlib.File, build.Executable, ExternalProgram], - args: T.List[str]) -> 'ExecutableSerialisation': + self, name: str, prog: T.Union[str, mesonlib.File, build.Executable, ExternalProgram, build.LocalProgram], + args: T.List[str], allow_built_program: bool = False) -> 'ExecutableSerialisation': largs: T.List[T.Union[str, build.Executable, ExternalProgram, build.LocalProgram]] = [] if isinstance(prog, (build.Executable, ExternalProgram)): FeatureNew.single_use(f'Passing executable/found program object to script parameter of {name}', '0.55.0', self.subproject, location=self.current_node) - largs.append(prog) - else: + elif isinstance(prog, (str, mesonlib.File)): if isinstance(prog, mesonlib.File): FeatureNew.single_use(f'Passing file object to script parameter of {name}', '0.57.0', self.subproject, location=self.current_node) - found = self.interpreter.find_program_impl([prog]) - largs.append(found) + prog = self.interpreter.find_program_impl([prog]) + + if isinstance(prog, build.LocalProgram) and not allow_built_program and not prog.run_program(): + self.interpreter._compiled_exe_error(prog) + largs.append(prog) largs.extend(args) es = self.interpreter.backend.get_executable_serialisation(largs, verbose=True) es.subproject = self.interpreter.subproject @@ -117,7 +119,7 @@ def _process_script_args( @typed_pos_args( 'meson.add_install_script', - (str, mesonlib.File, build.Executable, ExternalProgram), + (str, mesonlib.File, build.Executable, ExternalProgram, build.LocalProgram), varargs=(str, mesonlib.File, build.BuildTarget, build.CustomTarget, build.CustomTargetIndex, ExternalProgram) ) @typed_kwargs( @@ -133,7 +135,7 @@ def add_install_script_method( T.List[T.Union[str, mesonlib.File, build.BuildTargetTypes, ExternalProgram]]], 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) + script = self._find_source_script('add_install_script', args[0], script_args, allow_built_program=True) script.skip_if_destdir = kwargs['skip_if_destdir'] script.tag = kwargs['install_tag'] script.dry_run = kwargs['dry_run'] @@ -148,7 +150,7 @@ def add_install_script_method( @InterpreterObject.method('add_postconf_script') def add_postconf_script_method( self, - args: T.Tuple[T.Union[str, mesonlib.File, ExternalProgram], + args: T.Tuple[T.Union[str, mesonlib.File, ExternalProgram, build.LocalProgram], T.List[T.Union[str, mesonlib.File, ExternalProgram]]], kwargs: 'TYPE_kwargs') -> None: script_args = self._process_script_args('add_postconf_script', args[1]) @@ -165,7 +167,7 @@ def add_postconf_script_method( @InterpreterObject.method('add_dist_script') def add_dist_script_method( self, - args: T.Tuple[T.Union[str, mesonlib.File, ExternalProgram], + args: T.Tuple[T.Union[str, mesonlib.File, ExternalProgram, build.LocalProgram], T.List[T.Union[str, mesonlib.File, ExternalProgram]]], kwargs: 'TYPE_kwargs') -> None: if args[1]: From 8933a3d03c45fe25feba0a7eb7851d3bdf1440d9 Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Tue, 25 Nov 2025 14:52:02 -0500 Subject: [PATCH 10/12] LocalProgram: fix full_path() and cmd_array() --- mesonbuild/build.py | 6 ++++-- mesonbuild/interpreter/interpreterobjects.py | 4 ++++ test cases/common/26 find program/main.c | 4 ++++ test cases/common/26 find program/meson.build | 11 +++++++++-- 4 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 test cases/common/26 find program/main.c diff --git a/mesonbuild/build.py b/mesonbuild/build.py index f3591797cce8..2c576a005edd 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -3355,12 +3355,14 @@ def get_version(self, interpreter: T.Optional[Interpreter] = None) -> str: def get_command(self) -> T.List[str]: if isinstance(self.program, (Executable, CustomTarget, CustomTargetIndex)): - return [os.path.join(self.program.subdir, self.program.get_filename())] + # Only the backend knows the actual path to the build program. + raise MesonBugException('Cannot call get_command() on program that is a build target.') return self.program.get_command() def get_path(self) -> str: if isinstance(self.program, (Executable, CustomTarget, CustomTargetIndex)): - return os.path.join(self.program.subdir, self.program.get_filename()) + # Only the backend knows the actual path to the build program. + raise MesonBugException('Cannot call get_path() on program that is a build target.') return self.program.get_path() def description(self) -> str: diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index 7dd974e8bc49..4226bc6886b1 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -663,6 +663,8 @@ def full_path_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: def _full_path(self) -> str: if not self.found(): raise InterpreterException('Unable to get the path of a not-found external program') + if isinstance(self.held_object, build.LocalProgram) and isinstance(self.held_object.program, (build.Executable, build.CustomTarget, 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 @@ -674,6 +676,8 @@ def _full_path(self) -> str: def cmd_array_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.List[str]: if not self.found(): raise InterpreterException('Unable to get the path of a not-found external program') + if isinstance(self.held_object, build.LocalProgram) and isinstance(self.held_object.program, (build.Executable, build.CustomTarget, build.CustomTargetIndex)): + return [self.interpreter.backend.get_target_filename_abs(self.held_object.program)] cmd = self.held_object.get_command() assert cmd is not None return cmd diff --git a/test cases/common/26 find program/main.c b/test cases/common/26 find program/main.c new file mode 100644 index 000000000000..58fe69254d01 --- /dev/null +++ b/test cases/common/26 find program/main.c @@ -0,0 +1,4 @@ +int main(void) +{ + return 0; +} diff --git a/test cases/common/26 find program/meson.build b/test cases/common/26 find program/meson.build index 72d311c41faa..78d66253742e 100644 --- a/test cases/common/26 find program/meson.build +++ b/test cases/common/26 find program/meson.build @@ -1,4 +1,4 @@ -project('find program', meson_version: '>= 1.10.0',) +project('find program', 'c', meson_version: '>= 1.10.0',) if build_machine.system() == 'windows' # Things Windows does not provide: @@ -57,4 +57,11 @@ if build_machine.system() == 'windows' else prog = find_program('ld') assert(prog.cmd_array() == [prog.full_path()]) -endif \ No newline at end of file +endif + +exe = executable('app', 'main.c') +meson.override_find_program('foo', exe) +prog = find_program('foo') +assert(prog.found()) +assert(prog.full_path() == exe.full_path()) +assert(prog.cmd_array() == [exe.full_path()]) From 0160a226ba4ebbae21fe693967eb58699c4c4477 Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Tue, 25 Nov 2025 14:53:04 -0500 Subject: [PATCH 11/12] BaseProgram: Define for_machine --- mesonbuild/programs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mesonbuild/programs.py b/mesonbuild/programs.py index 7bcec59456ba..865750bea6b3 100644 --- a/mesonbuild/programs.py +++ b/mesonbuild/programs.py @@ -28,6 +28,7 @@ class BaseProgram(mesonlib.HoldableObject, metaclass=ABCMeta): ''' A base class for LocalProgram and ExternalProgram.''' name: str + for_machine: MachineChoice @abstractmethod def found(self) -> bool: From 02e8054bf5a811dbed650113075da89e39e85fdf Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Tue, 25 Nov 2025 14:47:46 -0500 Subject: [PATCH 12/12] Rename BaseProgram to Program and use it where possible When both ExternalProgram and LocalProgram are accepted, use Program instead. --- mesonbuild/backend/backends.py | 8 ++-- mesonbuild/build.py | 12 ++--- mesonbuild/interpreter/interpreter.py | 48 ++++++++++---------- mesonbuild/interpreter/interpreterobjects.py | 6 +-- mesonbuild/interpreter/kwargs.py | 7 ++- mesonbuild/interpreter/mesonmain.py | 16 +++---- mesonbuild/interpreter/type_checking.py | 9 ++-- mesonbuild/modules/__init__.py | 7 +-- mesonbuild/modules/_qt.py | 16 +++---- mesonbuild/modules/codegen.py | 5 +- mesonbuild/modules/dlang.py | 11 ++--- mesonbuild/modules/gnome.py | 40 ++++++++-------- mesonbuild/modules/i18n.py | 3 +- mesonbuild/modules/icestorm.py | 4 +- mesonbuild/modules/python.py | 6 +-- mesonbuild/modules/rust.py | 5 +- mesonbuild/modules/wayland.py | 8 ++-- mesonbuild/programs.py | 4 +- 18 files changed, 106 insertions(+), 109 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 60366d0c9bfe..906af855d5bb 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -535,7 +535,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.ExternalProgram, build.BuildTarget, build.CustomTarget, build.CustomTargetIndex, File, str, build.LocalProgram]], + self, cmd: T.Sequence[T.Union[programs.Program, build.BuildTarget, build.CustomTarget, build.CustomTargetIndex, File, str]], workdir: T.Optional[str] = None, extra_bdeps: T.Optional[T.List[build.BuildTarget]] = None, capture: T.Optional[str] = None, @@ -550,7 +550,7 @@ def get_executable_serialisation( exe, *raw_cmd_args = cmd if isinstance(exe, build.LocalProgram): exe = exe.program - if isinstance(exe, programs.ExternalProgram): + if isinstance(exe, programs.Program): exe_cmd = exe.get_command() exe_for_machine = exe.for_machine elif isinstance(exe, build.BuildTarget): @@ -575,7 +575,7 @@ def get_executable_serialisation( for c in raw_cmd_args: if isinstance(c, build.LocalProgram): c = c.program - if isinstance(c, programs.ExternalProgram): + if isinstance(c, programs.Program): cmd_args += c.get_command() elif isinstance(c, (build.BuildTarget, build.CustomTarget, build.CustomTargetIndex)): cmd_args.append(self.get_target_filename_abs(c)) @@ -1128,7 +1128,7 @@ def extract_dll_paths(cls, target: build.BuildTarget) -> T.Set[str]: return results def determine_windows_extra_paths( - self, target: T.Union[build.BuildTargetTypes, programs.ExternalProgram, mesonlib.File, str], + self, target: T.Union[build.BuildTargetTypes, programs.Program, mesonlib.File, str], extra_bdeps: T.Sequence[build.BuildTargetTypes]) -> T.List[str]: """On Windows there is no such thing as an rpath. diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 2c576a005edd..eb539e2b2d6c 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -363,7 +363,7 @@ def __init__(self, environment: Environment): self.stdlibs = PerMachine({}, {}) self.test_setups: T.Dict[str, TestSetup] = {} self.test_setup_default_name = None - self.find_overrides: T.Dict[str, T.Union[programs.ExternalProgram, LocalProgram]] = {} + 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 @@ -1980,7 +1980,7 @@ def __str__(self) -> str: class Generator(HoldableObject): def __init__(self, env: Environment, - exe: T.Union[Executable, programs.ExternalProgram, LocalProgram, CustomTarget, CustomTargetIndex], + exe: T.Union[Executable, programs.Program, CustomTarget, CustomTargetIndex], arguments: T.List[str], output: T.List[str], # how2dataclass @@ -2766,7 +2766,7 @@ class CommandBase: dependencies: T.List[T.Union[BuildTarget, 'CustomTarget']] subproject: str - def flatten_command(self, cmd: T.Sequence[T.Union[str, File, programs.ExternalProgram, BuildTargetTypes, LocalProgram]]) -> \ + def flatten_command(self, cmd: T.Sequence[T.Union[str, File, programs.Program, BuildTargetTypes]]) -> \ T.List[T.Union[str, File, BuildTarget, CustomTarget, programs.ExternalProgram]]: cmd = listify(cmd) final_cmd: T.List[T.Union[str, File, BuildTarget, 'CustomTarget']] = [] @@ -2840,7 +2840,7 @@ def __init__(self, environment: Environment, command: T.Sequence[T.Union[ str, BuildTargetTypes, GeneratedList, - programs.ExternalProgram, File, LocalProgram]], + programs.Program, File]], sources: T.Sequence[T.Union[ str, File, BuildTargetTypes, ExtractedObjects, GeneratedList, programs.ExternalProgram]], @@ -3101,7 +3101,7 @@ class RunTarget(Target, CommandBase): typename = 'run' def __init__(self, name: str, - command: T.Sequence[T.Union[str, File, BuildTargetTypes, programs.ExternalProgram, LocalProgram]], + command: T.Sequence[T.Union[str, File, BuildTargetTypes, programs.Program]], dependencies: T.Sequence[AnyTargetType], subdir: str, subproject: str, @@ -3331,7 +3331,7 @@ def get(self, name: str) -> T.Tuple[T.Union[str, int, bool], T.Optional[str]]: def keys(self) -> T.Iterator[str]: return self.values.keys() -class LocalProgram(programs.BaseProgram): +class LocalProgram(programs.Program): ''' A wrapper for a program that may have build dependencies.''' def __init__(self, program: T.Union[programs.ExternalProgram, Executable, CustomTarget, CustomTargetIndex], version: str, depends: T.Optional[T.List[T.Union[BuildTarget, CustomTarget]]] = None, diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index b29d44169f40..c0e2e5b69127 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -21,7 +21,7 @@ FileMode, MachineChoice, is_parent_path, listify, extract_as_list, has_path_sep, path_is_in_root, PerMachine) from ..options import OptionKey -from ..programs import ExternalProgram, NonExistingExternalProgram, BaseProgram +from ..programs import ExternalProgram, NonExistingExternalProgram, Program from ..dependencies import Dependency from ..depfile import DepFile from ..interpreterbase import ContainerTypeInfo, InterpreterBase, KwargInfo, typed_kwargs, typed_pos_args @@ -130,7 +130,7 @@ BuildTargetSource = T.Union[mesonlib.FileOrString, build.GeneratedTypes, build.StructuredSources] - ProgramVersionFunc = T.Callable[[T.Union[ExternalProgram, build.LocalProgram]], str] + ProgramVersionFunc = T.Callable[[Program], str] TestClass = T.TypeVar('TestClass', bound=Test) @@ -447,7 +447,7 @@ def build_holder_map(self) -> None: ''' self.bound_holder_map.update({ dependencies.Dependency: OBJ.DependencyHolder, - BaseProgram: OBJ.BaseProgramHolder, + Program: OBJ.ProgramHolder, compilers.Compiler: compilerOBJ.CompilerHolder, ModuleObject: OBJ.ModuleObjectHolder, MutableModuleObject: OBJ.MutableModuleObjectHolder, @@ -745,8 +745,8 @@ def validate_arguments(self, args, argcount, arg_types): # better error messages when overridden @typed_pos_args( 'run_command', - (build.Executable, ExternalProgram, compilers.Compiler, mesonlib.File, str, build.LocalProgram), - varargs=(build.Executable, ExternalProgram, compilers.Compiler, mesonlib.File, str, build.LocalProgram)) + (build.Executable, Program, compilers.Compiler, mesonlib.File, str), + varargs=(build.Executable, Program, compilers.Compiler, mesonlib.File, str)) @typed_kwargs( 'run_command', KwargInfo('check', (bool, NoneType), since='0.47.0'), @@ -754,8 +754,8 @@ def validate_arguments(self, args, argcount, arg_types): ENV_KW.evolve(since='0.50.0'), ) def func_run_command(self, node: mparser.BaseNode, - args: T.Tuple[T.Union[build.Executable, ExternalProgram, compilers.Compiler, mesonlib.File, str, build.LocalProgram], - T.List[T.Union[build.Executable, ExternalProgram, compilers.Compiler, mesonlib.File, str, build.LocalProgram]]], + args: T.Tuple[T.Union[build.Executable, Program, compilers.Compiler, mesonlib.File, str], + T.List[T.Union[build.Executable, Program, compilers.Compiler, mesonlib.File, str]]], kwargs: 'kwtypes.RunCommand') -> RunProcess: return self.run_command_impl(args, kwargs) @@ -767,8 +767,8 @@ def _compiled_exe_error(self, cmd: T.Union[build.LocalProgram, build.Executable] raise InterpreterException(f'Program {descr!r} is a compiled executable and therefore cannot be used during configuration') def run_command_impl(self, - args: T.Tuple[T.Union[build.Executable, ExternalProgram, compilers.Compiler, mesonlib.File, str, build.LocalProgram], - T.List[T.Union[build.Executable, ExternalProgram, compilers.Compiler, mesonlib.File, str, build.LocalProgram]]], + args: T.Tuple[T.Union[build.Executable, Program, compilers.Compiler, mesonlib.File, str], + T.List[T.Union[build.Executable, Program, compilers.Compiler, mesonlib.File, str]]], kwargs: 'kwtypes.RunCommand', in_builddir: bool = False) -> RunProcess: cmd, cargs = args @@ -1595,7 +1595,7 @@ def program_from_system(self, args: T.List[mesonlib.FileOrString], search_dirs: def program_from_overrides(self, command_names: T.List[mesonlib.FileOrString], extra_info: T.List['mlog.TV_Loggable'] - ) -> T.Optional[T.Union[ExternalProgram, build.LocalProgram]]: + ) -> T.Optional[Program]: for name in command_names: if not isinstance(name, str): continue @@ -1610,7 +1610,7 @@ def store_name_lookups(self, command_names: T.List[mesonlib.FileOrString]) -> No if isinstance(name, str): self.build.searched_programs.add(name) - def add_find_program_override(self, name: str, exe: T.Union[ExternalProgram, build.LocalProgram]) -> None: + 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.build.find_overrides: @@ -1635,7 +1635,7 @@ def find_program_impl(self, args: T.List[mesonlib.FileOrString], search_dirs: T.Optional[T.List[str]] = None, version_arg: T.Optional[str] = '', version_func: T.Optional[ProgramVersionFunc] = None - ) -> T.Union[ExternalProgram, build.LocalProgram]: + ) -> Program: args = mesonlib.listify(args) extra_info: T.List[mlog.TV_Loggable] = [] @@ -1667,7 +1667,7 @@ def program_lookup(self, args: T.List[mesonlib.FileOrString], for_machine: Machi version_arg: T.Optional[str], version_func: T.Optional[ProgramVersionFunc], extra_info: T.List[mlog.TV_Loggable] - ) -> T.Optional[T.Union[ExternalProgram, build.LocalProgram]]: + ) -> T.Optional[Program]: progobj = self.program_from_overrides(args, extra_info) if progobj: return progobj @@ -1703,7 +1703,7 @@ def program_lookup(self, args: T.List[mesonlib.FileOrString], for_machine: Machi return progobj - def check_program_version(self, progobj: T.Union[ExternalProgram, build.LocalProgram], + def check_program_version(self, progobj: Program, wanted: T.Union[str, T.List[str]], version_func: T.Optional[ProgramVersionFunc], extra_info: T.List[mlog.TV_Loggable]) -> bool: @@ -1730,7 +1730,7 @@ def check_program_version(self, progobj: T.Union[ExternalProgram, build.LocalPro def find_program_fallback(self, fallback: str, args: T.List[mesonlib.FileOrString], default_options: OptionDict, required: bool, extra_info: T.List[mlog.TV_Loggable] - ) -> T.Optional[T.Union[ExternalProgram, build.LocalProgram]]: + ) -> T.Optional[Program]: mlog.log('Fallback to subproject', mlog.bold(fallback), 'which provides program', mlog.bold(' '.join(args))) sp_kwargs: kwtypes.DoSubproject = { @@ -1757,7 +1757,7 @@ def find_program_fallback(self, fallback: str, args: T.List[mesonlib.FileOrStrin @disablerIfNotFound def func_find_program(self, node: mparser.BaseNode, args: T.Tuple[T.List[mesonlib.FileOrString]], kwargs: 'kwtypes.FindProgram', - ) -> T.Union[ExternalProgram, build.LocalProgram]: + ) -> 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') @@ -2193,7 +2193,7 @@ def func_alias_target(self, node: mparser.BaseNode, args: T.Tuple[str, T.List[T. self.add_target(name, tg) return tg - @typed_pos_args('generator', (build.Executable, ExternalProgram, build.LocalProgram)) + @typed_pos_args('generator', (build.Executable, Program)) @typed_kwargs( 'generator', KwargInfo('arguments', ContainerTypeInfo(list, str, allow_empty=False), required=True, listify=True), @@ -2203,7 +2203,7 @@ def func_alias_target(self, node: mparser.BaseNode, args: T.Tuple[str, T.List[T. KwargInfo('capture', bool, default=False, since='0.43.0'), ) def func_generator(self, node: mparser.FunctionNode, - args: T.Tuple[T.Union[build.Executable, ExternalProgram, build.LocalProgram]], + args: T.Tuple[T.Union[build.Executable, Program]], kwargs: 'kwtypes.FuncGenerator') -> build.Generator: for rule in kwargs['output']: if '@BASENAME@' not in rule and '@PLAINNAME@' not in rule: @@ -2217,17 +2217,17 @@ def func_generator(self, node: mparser.FunctionNode, return build.Generator(self.environment, args[0], **kwargs) - @typed_pos_args('benchmark', str, (build.Executable, build.Jar, ExternalProgram, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.LocalProgram)) + @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, ExternalProgram, mesonlib.File, build.LocalProgram]], + args: T.Tuple[str, T.Union[build.Executable, build.Jar, Program, mesonlib.File]], kwargs: 'kwtypes.FuncBenchmark') -> None: self.add_test(node, args, kwargs, False) - @typed_pos_args('test', str, (build.Executable, build.Jar, ExternalProgram, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.LocalProgram)) + @typed_pos_args('test', str, (build.Executable, build.Jar, Program, mesonlib.File, build.CustomTarget, build.CustomTargetIndex)) @typed_kwargs('test', *TEST_KWS, KwargInfo('is_parallel', bool, default=True)) def func_test(self, node: mparser.BaseNode, - args: T.Tuple[str, T.Union[build.Executable, build.Jar, ExternalProgram, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.LocalProgram]], + args: T.Tuple[str, T.Union[build.Executable, build.Jar, Program, mesonlib.File]], kwargs: 'kwtypes.FuncTest') -> None: self.add_test(node, args, kwargs, True) @@ -2241,7 +2241,7 @@ def unpack_env_kwarg(self, kwargs: T.Union[EnvironmentVariables, T.Dict[str, 'TY return ENV_KW.convertor(envlist) def make_test(self, node: mparser.BaseNode, - args: T.Tuple[str, T.Union[build.Executable, build.Jar, ExternalProgram, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.LocalProgram]], + args: T.Tuple[str, T.Union[build.Executable, build.Jar, Program, mesonlib.File, build.CustomTarget, build.CustomTargetIndex]], kwargs: 'kwtypes.BaseTest', klass: T.Type[TestClass] = Test) -> TestClass: name = args[0] @@ -2619,7 +2619,7 @@ def validate_build_subdir(self, build_subdir: str, target: str): KwargInfo('capture', bool, default=False, since='0.41.0'), KwargInfo( 'command', - (ContainerTypeInfo(list, (build.Executable, ExternalProgram, compilers.Compiler, mesonlib.File, str, build.LocalProgram), allow_empty=False), NoneType), + (ContainerTypeInfo(list, (build.Executable, Program, compilers.Compiler, mesonlib.File, str)), NoneType), listify=True, ), KwargInfo( diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index 4226bc6886b1..d6e31e74ca7e 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -23,7 +23,7 @@ flatten, resolve_second_level_holders, 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, BaseProgram +from ..programs import ExternalProgram, Program from ..mesonlib import HoldableObject, listify, Popen_safe import typing as T @@ -633,9 +633,9 @@ def as_shared_method(self, args: T.List[TYPE_var], kwargs: InternalDependencyAsK raise InterpreterException('as_shared method is only supported on declare_dependency() objects') return self.held_object.get_as_shared(kwargs['recursive']) -_BASEPROG = T.TypeVar('_BASEPROG', bound=BaseProgram) +_BASEPROG = T.TypeVar('_BASEPROG', bound=Program) -class BaseProgramHolder(ObjectHolder[_BASEPROG]): +class ProgramHolder(ObjectHolder[_BASEPROG]): def __init__(self, ep: _BASEPROG, interpreter: 'Interpreter') -> None: super().__init__(ep, interpreter) diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index 357d4deb597c..6000b7be571e 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -16,7 +16,7 @@ from ..mesonlib import EnvironmentVariables, MachineChoice, File, FileMode, FileOrString from ..options import OptionKey from ..modules.cmake import CMakeSubprojectOptions -from ..programs import ExternalProgram +from ..programs import ExternalProgram, Program from .type_checking import PkgConfigDefineType, SourcesVarargsType TestArgs = T.Union[str, File, build.Target, ExternalProgram] @@ -171,8 +171,7 @@ class FuncAddLanguages(ExtractRequired): class RunTarget(TypedDict): - command: T.List[T.Union[str, build.BuildTarget, build.CustomTarget, ExternalProgram, - File, build.LocalProgram]] + command: T.List[T.Union[str, build.BuildTarget, build.CustomTarget, Program, File]] depends: T.List[T.Union[build.BuildTarget, build.CustomTarget]] env: EnvironmentVariables @@ -184,7 +183,7 @@ class CustomTarget(TypedDict): build_by_default: T.Optional[bool] build_subdir: str capture: bool - command: T.List[T.Union[str, build.BuildTargetTypes, ExternalProgram, File, build.LocalProgram]] + 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]] diff --git a/mesonbuild/interpreter/mesonmain.py b/mesonbuild/interpreter/mesonmain.py index a766f788c077..c8c1be34eccd 100644 --- a/mesonbuild/interpreter/mesonmain.py +++ b/mesonbuild/interpreter/mesonmain.py @@ -14,7 +14,7 @@ from ..mesonlib import MachineChoice from ..options import OptionKey -from ..programs import ExternalProgram +from ..programs import ExternalProgram, Program from ..interpreter.type_checking import ENV_KW, ENV_METHOD_KW, ENV_SEPARATOR_KW, env_convertor_with_method from ..interpreterbase import (MesonInterpreterObject, FeatureNew, FeatureDeprecated, typed_pos_args, noArgsFlattening, noPosargs, noKwargs, @@ -58,9 +58,9 @@ def __init__(self, build: 'build.Build', interpreter: 'Interpreter'): self.interpreter = interpreter def _find_source_script( - self, name: str, prog: T.Union[str, mesonlib.File, build.Executable, ExternalProgram, build.LocalProgram], + self, name: str, prog: T.Union[str, mesonlib.File, build.Executable, Program], args: T.List[str], allow_built_program: bool = False) -> 'ExecutableSerialisation': - largs: T.List[T.Union[str, build.Executable, ExternalProgram, build.LocalProgram]] = [] + largs: T.List[T.Union[str, build.Executable, Program]] = [] if isinstance(prog, (build.Executable, ExternalProgram)): FeatureNew.single_use(f'Passing executable/found program object to script parameter of {name}', @@ -119,7 +119,7 @@ def _process_script_args( @typed_pos_args( 'meson.add_install_script', - (str, mesonlib.File, build.Executable, ExternalProgram, build.LocalProgram), + (str, mesonlib.File, build.Executable, Program), varargs=(str, mesonlib.File, build.BuildTarget, build.CustomTarget, build.CustomTargetIndex, ExternalProgram) ) @typed_kwargs( @@ -150,7 +150,7 @@ def add_install_script_method( @InterpreterObject.method('add_postconf_script') def add_postconf_script_method( self, - args: T.Tuple[T.Union[str, mesonlib.File, ExternalProgram, build.LocalProgram], + args: T.Tuple[T.Union[str, mesonlib.File, Program], T.List[T.Union[str, mesonlib.File, ExternalProgram]]], kwargs: 'TYPE_kwargs') -> None: script_args = self._process_script_args('add_postconf_script', args[1]) @@ -167,7 +167,7 @@ def add_postconf_script_method( @InterpreterObject.method('add_dist_script') def add_dist_script_method( self, - args: T.Tuple[T.Union[str, mesonlib.File, ExternalProgram, build.LocalProgram], + args: T.Tuple[T.Union[str, mesonlib.File, Program], T.List[T.Union[str, mesonlib.File, ExternalProgram]]], kwargs: 'TYPE_kwargs') -> None: if args[1]: @@ -315,10 +315,10 @@ def install_dependency_manifest_method(self, args: T.Tuple[str], kwargs: 'TYPE_k self.build.dep_manifest_name = args[0] @FeatureNew('meson.override_find_program', '0.46.0') - @typed_pos_args('meson.override_find_program', str, (mesonlib.File, ExternalProgram, build.Executable, build.LocalProgram)) + @typed_pos_args('meson.override_find_program', str, (mesonlib.File, Program, build.Executable)) @noKwargs @InterpreterObject.method('override_find_program') - def override_find_program_method(self, args: T.Tuple[str, T.Union[mesonlib.File, ExternalProgram, build.Executable, build.LocalProgram]], kwargs: 'TYPE_kwargs') -> None: + def override_find_program_method(self, args: T.Tuple[str, T.Union[mesonlib.File, Program, build.Executable]], kwargs: 'TYPE_kwargs') -> None: name, exe = args if isinstance(exe, mesonlib.File): abspath = exe.absolute_path(self.interpreter.environment.source_dir, diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py index 3bf6e5d59b22..20cc6506ab7f 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -10,14 +10,13 @@ from .. import compilers from ..build import (CustomTarget, BuildTarget, CustomTargetIndex, ExtractedObjects, GeneratedList, IncludeDirs, - BothLibraries, SharedLibrary, StaticLibrary, Jar, Executable, StructuredSources, - LocalProgram) + BothLibraries, SharedLibrary, StaticLibrary, Jar, Executable, StructuredSources) from ..options import OptionKey, UserFeatureOption from ..dependencies import Dependency, DependencyMethods, InternalDependency from ..interpreterbase.decorators import KwargInfo, ContainerTypeInfo, FeatureBroken, FeatureDeprecated from ..mesonlib import (File, FileMode, MachineChoice, has_path_sep, listify, stringlistify, EnvironmentVariables) -from ..programs import ExternalProgram +from ..programs import ExternalProgram, Program # Helper definition for type checks that are `Optional[T]` NoneType: T.Type[None] = type(None) @@ -286,9 +285,9 @@ def _env_convertor(value: _FullEnvInitValueType) -> EnvironmentVariables: default=[], ) -COMMAND_KW: KwargInfo[T.List[T.Union[str, BuildTargetTypes, ExternalProgram, File, LocalProgram]]] = KwargInfo( +COMMAND_KW: KwargInfo[T.List[T.Union[str, BuildTargetTypes, Program, File]]] = KwargInfo( 'command', - ContainerTypeInfo(list, (str, BuildTarget, CustomTarget, CustomTargetIndex, ExternalProgram, File, LocalProgram), allow_empty=False), + ContainerTypeInfo(list, (str, BuildTarget, CustomTarget, CustomTargetIndex, Program, File), allow_empty=False), required=True, listify=True, default=[], diff --git a/mesonbuild/modules/__init__.py b/mesonbuild/modules/__init__.py index 9fceca576af0..96a845d8732d 100644 --- a/mesonbuild/modules/__init__.py +++ b/mesonbuild/modules/__init__.py @@ -20,6 +20,7 @@ from ..interpreterbase import TYPE_var, TYPE_kwargs from ..dependencies import Dependency from ..options import ElementaryOptionValues + from ..programs import Program class ModuleState: """Object passed to all module methods. @@ -74,14 +75,14 @@ def find_program(self, prog: T.Union[mesonlib.FileOrString, T.List[mesonlib.File required: bool = True, version_func: T.Optional[ProgramVersionFunc] = None, wanted: T.Union[str, T.List[str]] = '', silent: bool = False, - for_machine: MachineChoice = MachineChoice.HOST) -> T.Union[ExternalProgram, build.LocalProgram]: + for_machine: MachineChoice = MachineChoice.HOST) -> Program: if not isinstance(prog, list): prog = [prog] return self._interpreter.find_program_impl(prog, required=required, version_func=version_func, wanted=wanted, silent=silent, for_machine=for_machine) def find_tool(self, name: str, depname: str, varname: str, required: bool = True, - wanted: T.Optional[str] = None) -> T.Union[ExternalProgram, build.LocalProgram]: + wanted: T.Optional[str] = None) -> Program: # Look in overrides in case it's built as subproject progobj = self._interpreter.program_from_overrides([name], []) if progobj is not None: @@ -117,7 +118,7 @@ def dependency(self, depname: str, native: bool = False, required: bool = True, # implementations of meson functions anyway. return self._interpreter.func_dependency(self.current_node, [depname], kwargs) # type: ignore - def test(self, args: T.Tuple[str, T.Union[build.Executable, build.Jar, ExternalProgram, build.LocalProgram, mesonlib.File]], + def test(self, args: T.Tuple[str, T.Union[build.Executable, build.Jar, Program, mesonlib.File]], workdir: T.Optional[str] = None, env: T.Union[T.List[str], T.Dict[str, str], str] = None, depends: T.List[T.Union[build.CustomTarget, build.BuildTarget]] = None) -> None: diff --git a/mesonbuild/modules/_qt.py b/mesonbuild/modules/_qt.py index 2f24aa142d35..79ceacc0e5b5 100644 --- a/mesonbuild/modules/_qt.py +++ b/mesonbuild/modules/_qt.py @@ -28,7 +28,7 @@ from ..interpreter import Interpreter from ..interpreter import kwargs from ..mesonlib import FileOrString - from ..programs import ExternalProgram + from ..programs import Program from typing_extensions import Literal QtDependencyType = T.Union[QtPkgConfigDependency, QmakeQtDependency] @@ -209,7 +209,7 @@ def __init__(self, interpreter: Interpreter, qt_version: int = 5): self.qt_version = qt_version # It is important that this list does not change order as the order of # the returned ExternalPrograms will change as well - self.tools: T.Dict[str, T.Union[ExternalProgram, build.LocalProgram]] = { + self.tools: T.Dict[str, Program] = { tool: NonExistingExternalProgram(tool) for tool in self._set_of_qt_tools } self.methods.update({ @@ -251,7 +251,7 @@ def gen_bins() -> T.Generator[T.Tuple[str, str], None, None]: arg = ['-v'] # Ensure that the version of qt and each tool are the same - def get_version(p: T.Union[ExternalProgram, build.LocalProgram]) -> str: + def get_version(p: Program) -> str: _, out, err = Popen_safe(p.get_command() + arg) if name == 'lrelease' or not qt_dep.version.startswith('4'): care = out @@ -446,7 +446,7 @@ def _compile_resources_impl(self, state: 'ModuleState', kwargs: 'ResourceCompile for s in sources: qrc_deps.extend(self._parse_qrc_deps(state, s)) - cmd: T.List[T.Union[ExternalProgram, build.LocalProgram, str]] + cmd: T.List[T.Union[Program, str]] cmd = [self.tools['rcc'], '-name', name, '-o', '@OUTPUT@'] cmd.extend(extra_args) cmd.append('@INPUT@') @@ -736,7 +736,7 @@ def compile_translations(self, state: ModuleState, args: T.Tuple, kwargs: Compil ts = os.path.basename(ts) else: outdir = state.subdir - cmd: T.List[T.Union[ExternalProgram, build.LocalProgram, str]] = [self.tools['lrelease'], '@INPUT@', '-qm', '@OUTPUT@'] + cmd: T.List[T.Union[Program, str]] = [self.tools['lrelease'], '@INPUT@', '-qm', '@OUTPUT@'] lrelease_target = build.CustomTarget( f'qt{self.qt_version}-compile-{ts}', outdir, @@ -876,7 +876,7 @@ def _moc_json_collect(self, state: ModuleState, kwargs: MocJsonCollectKwArgs) -> input_args.append(f'@INPUT{input_counter}@') input_counter += 1 - cmd: T.List[T.Union[ExternalProgram, build.LocalProgram, str]] + cmd: T.List[T.Union[Program, str]] cmd = [self.tools['moc'], '--collect-json', '-o', '@OUTPUT@'] cmd.extend(input_args) return build.CustomTarget( @@ -924,7 +924,7 @@ def _gen_qml_cachegen(self, state: ModuleState, kwargs: GenQmlCachegenKwArgs) -> ressource_path = os.path.join('/', kwargs['module_prefix'], source_basename) cachegen_inputs.append(ressource_path) - cmd: T.List[T.Union[ExternalProgram, build.LocalProgram, str]] + cmd: T.List[T.Union[Program, str]] cmd = [self.tools['qmlcachegen'], '-o', '@OUTPUT@', '--resource-name', f'qmlcache_{target_name}'] cmd.extend(kwargs['extra_args']) cmd.append('--resource=@INPUT@') @@ -962,7 +962,7 @@ def _qml_type_registrar(self, state: ModuleState, kwargs: GenQmlTypeRegistrarKwA install_dir: T.List[T.Union[str, Literal[False]]] = [False] install_tag: T.List[T.Union[str, None]] = [None] - cmd = [ + cmd: T.List[T.Union[Program, str]] = [ self.tools['qmltyperegistrar'], '--import-name', import_name, '--major-version', major_version, diff --git a/mesonbuild/modules/codegen.py b/mesonbuild/modules/codegen.py index 171f572713f2..be8cdc8a8c60 100644 --- a/mesonbuild/modules/codegen.py +++ b/mesonbuild/modules/codegen.py @@ -21,17 +21,16 @@ from .. import mlog if T.TYPE_CHECKING: - from typing_extensions import Literal, TypeAlias, TypedDict + from typing_extensions import Literal, TypedDict from . import ModuleState from .._typing import ImmutableListProtocol - from ..build import LocalProgram from ..interpreter import Interpreter from ..interpreter.kwargs import ExtractRequired from ..interpreterbase import TYPE_var, TYPE_kwargs from ..mesonlib import MachineChoice + from ..programs import Program - Program: TypeAlias = T.Union[ExternalProgram, LocalProgram] LexImpls = Literal['lex', 'flex', 'reflex', 'win_flex'] YaccImpls = Literal['yacc', 'byacc', 'bison', 'win_bison'] diff --git a/mesonbuild/modules/dlang.py b/mesonbuild/modules/dlang.py index 860a62481cef..9ef099d0f164 100644 --- a/mesonbuild/modules/dlang.py +++ b/mesonbuild/modules/dlang.py @@ -12,7 +12,7 @@ from . import ExtensionModule, ModuleInfo from .. import mlog -from ..build import InvalidArguments, LocalProgram +from ..build import InvalidArguments from ..dependencies import Dependency from ..dependencies.dub import DubDependency from ..interpreterbase import typed_pos_args @@ -24,17 +24,16 @@ from . import ModuleState from ..interpreter.interpreter import Interpreter from ..interpreterbase.baseobjects import TYPE_kwargs - from ..programs import ExternalProgram + from ..programs import Program - _AnyProgram: TypeAlias = T.Union[ExternalProgram, LocalProgram] _JSONTypes: TypeAlias = T.Union[str, int, bool, None, T.List['_JSONTypes'], T.Dict[str, '_JSONTypes']] class DlangModule(ExtensionModule): - class_dubbin: T.Union[_AnyProgram, Literal[False], None] = None + class_dubbin: T.Union[Program, Literal[False], None] = None init_dub = False - dubbin: T.Union[_AnyProgram, Literal[False], None] + dubbin: T.Union[Program, Literal[False], None] INFO = ModuleInfo('dlang', '0.48.0') @@ -121,7 +120,7 @@ def _call_dubbin(self, args: T.List[str], env: T.Optional[T.Mapping[str, str]] = p, out = Popen_safe(self.dubbin.get_command() + args, env=env)[0:2] return p.returncode, out.strip() - def check_dub(self, state: ModuleState) -> T.Union[_AnyProgram, Literal[False]]: + def check_dub(self, state: ModuleState) -> T.Union[Program, Literal[False]]: dubbin = state.find_program('dub', silent=True) if dubbin.found(): try: diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index d8a4b2fac663..1f1c2afb830b 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -36,7 +36,7 @@ from ..scripts.gettext import read_linguas if T.TYPE_CHECKING: - from typing_extensions import Literal, TypeAlias, TypedDict + from typing_extensions import Literal, TypedDict from . import ModuleState from ..build import BuildTarget @@ -44,7 +44,7 @@ from ..interpreter import Interpreter from ..interpreterbase import TYPE_var, TYPE_kwargs from ..mesonlib import FileOrString - from ..programs import ExternalProgram + from ..programs import Program class PostInstall(TypedDict): glib_compile_schemas: bool @@ -197,8 +197,6 @@ class MkEnums(_MkEnumsCommon): vtail: T.Optional[str] depends: T.List[T.Union[BuildTarget, CustomTarget, CustomTargetIndex]] - ToolType: TypeAlias = T.Union[ExternalProgram, LocalProgram] - # Differs from the CustomTarget version in that it straight defaults to True _BUILD_BY_DEFAULT: KwargInfo[bool] = KwargInfo( @@ -255,8 +253,8 @@ class GnomeModule(ExtensionModule): def __init__(self, interpreter: 'Interpreter') -> None: super().__init__(interpreter) self.gir_dep: T.Optional[Dependency] = None - self.giscanner: T.Optional[ToolType] = None - self.gicompiler: T.Optional[ToolType] = None + self.giscanner: T.Optional[Program] = None + self.gicompiler: T.Optional[Program] = None self.install_glib_compile_schemas = False self.install_gio_querymodules: T.List[str] = [] self.install_gtk_update_icon_cache = False @@ -308,7 +306,7 @@ def _print_gdbus_warning() -> None: once=True, fatal=False) @staticmethod - def _find_tool(state: 'ModuleState', tool: str) -> 'ToolType': + def _find_tool(state: 'ModuleState', tool: str) -> 'Program': tool_map = { 'gio-querymodules': 'gio-2.0', 'glib-compile-schemas': 'gio-2.0', @@ -397,7 +395,7 @@ def compile_resources(self, state: 'ModuleState', args: T.Tuple[str, 'FileOrStri glib_version = self._get_native_glib_version(state) glib_compile_resources = self._find_tool(state, 'glib-compile-resources') - cmd: T.List[T.Union['ToolType', str]] = [glib_compile_resources, '@INPUT@'] + cmd: T.List[T.Union['Program', str]] = [glib_compile_resources, '@INPUT@'] source_dirs = kwargs['source_dir'] dependencies = kwargs['dependencies'] @@ -495,7 +493,7 @@ def compile_resources(self, state: 'ModuleState', args: T.Tuple[str, 'FileOrStri raise MesonException('GResource header is installed yet export is not enabled') depfile: T.Optional[str] = None - target_cmd: T.List[T.Union['ToolType', str]] + target_cmd: T.List[T.Union['Program', str]] if not mesonlib.version_compare(glib_version, gresource_dep_needed_version): # This will eventually go out of sync if dependencies are added target_cmd = cmd @@ -788,7 +786,7 @@ def postconf_hook(self, b: build.Build) -> None: if self.devenv is not None: b.devenv.append(self.devenv) - def _get_gir_dep(self, state: 'ModuleState') -> T.Tuple[Dependency, ToolType, ToolType]: + def _get_gir_dep(self, state: 'ModuleState') -> T.Tuple[Dependency, Program, Program]: if not self.gir_dep: self.gir_dep = state.dependency('gobject-introspection-1.0') self.giscanner = self._find_tool(state, 'g-ir-scanner') @@ -969,7 +967,7 @@ def _make_gir_target( self, state: 'ModuleState', girfile: str, - scan_command: T.Sequence[T.Union['FileOrString', Executable, ToolType]], + scan_command: T.Sequence[T.Union['FileOrString', Executable, Program]], 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], @@ -1018,7 +1016,7 @@ def _make_gir_target( @staticmethod def _make_typelib_target(state: 'ModuleState', typelib_output: str, - typelib_cmd: T.Sequence[T.Union[str, CustomTarget, ToolType]], + 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: install = kwargs['install_typelib'] @@ -1192,7 +1190,7 @@ def generate_gir(self, state: 'ModuleState', args: T.Tuple[T.List[T.Union[Execut gir_inc_dirs: T.List[str] = [] - scan_command: T.List[T.Union[str, ToolType, Executable]] = [giscanner] + scan_command: T.List[T.Union[str, Program, Executable]] = [giscanner] scan_command += ['--quiet'] scan_command += ['--no-libtool'] scan_command += ['--namespace=' + ns, '--nsversion=' + nsversion] @@ -1245,7 +1243,7 @@ def generate_gir(self, state: 'ModuleState', args: T.Tuple[T.List[T.Union[Execut T.cast('T.Dict[str, T.Any]', kwargs)) typelib_output = f'{ns}-{nsversion}.typelib' - typelib_cmd = [gicompiler, scan_target, '--output', '@OUTPUT@'] + typelib_cmd: T.List[T.Union[str, CustomTarget, Program]] = [gicompiler, scan_target, '--output', '@OUTPUT@'] typelib_cmd += state.get_include_args(gir_inc_dirs, prefix='--includedir=') for incdir in typelib_includes: @@ -1265,7 +1263,7 @@ def compile_schemas(self, state: 'ModuleState', args: T.List['TYPE_var'], kwargs srcdir = os.path.join(state.build_to_src, state.subdir) outdir = state.subdir - cmd: T.List[T.Union['ToolType', str]] = [self._find_tool(state, 'glib-compile-schemas'), '--targetdir', outdir, srcdir] + cmd: T.List[T.Union['Program', str]] = [self._find_tool(state, 'glib-compile-schemas'), '--targetdir', outdir, srcdir] if state.subdir == '': targetname = 'gsettings-compile' else: @@ -1345,7 +1343,7 @@ def yelp(self, state: 'ModuleState', args: T.Tuple[str, T.List[str]], kwargs: 'Y pot_file = os.path.join('@SOURCE_ROOT@', state.subdir, 'C', project_id + '.pot') pot_sources = [os.path.join('@SOURCE_ROOT@', state.subdir, 'C', s) for s in sources] - pot_args: T.List[T.Union[ToolType, str]] = [itstool, '-o', pot_file] + pot_args: T.List[T.Union[Program, str]] = [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, @@ -1377,7 +1375,7 @@ def yelp(self, state: 'ModuleState', args: T.Tuple[str, T.List[str]], kwargs: 'Y targets.append(l_data) po_file = l + '.po' - po_args: T.List[T.Union[ToolType, str]] = [ + po_args: T.List[T.Union[Program, str]] = [ msgmerge, '-q', '-o', os.path.join('@SOURCE_ROOT@', l_subdir, po_file), os.path.join('@SOURCE_ROOT@', l_subdir, po_file), pot_file] @@ -1642,7 +1640,7 @@ def gdbus_codegen(self, state: 'ModuleState', args: T.Tuple[str, T.Optional[T.Un kwargs: 'GdbusCodegen') -> ModuleReturnValue: namebase = args[0] xml_files: T.List[T.Union['FileOrString', build.GeneratedTypes]] = [args[1]] if args[1] else [] - cmd: T.List[T.Union['ToolType', str]] = [self._find_tool(state, 'gdbus-codegen')] + cmd: T.List[T.Union['Program', str]] = [self._find_tool(state, 'gdbus-codegen')] cmd.extend(kwargs['extra_args']) # Autocleanup supported? @@ -2047,7 +2045,7 @@ def _make_mkenum_impl( install_dir: T.Optional[T.Sequence[T.Union[str, bool]]] = None, depends: T.Optional[T.Sequence[build.BuildTargetTypes]] = None ) -> build.CustomTarget: - real_cmd: T.List[T.Union[str, 'ToolType']] = [self._find_tool(state, 'glib-mkenums')] + real_cmd: T.List[T.Union[str, 'Program']] = [self._find_tool(state, 'glib-mkenums')] real_cmd.extend(cmd) _install_dir = install_dir or state.environment.coredata.optstore.get_value_for(OptionKey('includedir')) assert isinstance(_install_dir, str), 'for mypy' @@ -2093,7 +2091,7 @@ def genmarshal(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'GenMarsh new_genmarshal = mesonlib.version_compare(self._get_native_glib_version(state), '>= 2.53.3') - cmd: T.List[T.Union['ToolType', str]] = [self._find_tool(state, 'glib-genmarshal'), '--quiet'] + cmd: T.List[T.Union['Program', str]] = [self._find_tool(state, 'glib-genmarshal'), '--quiet'] if kwargs['prefix']: cmd.extend(['--prefix', kwargs['prefix']]) if kwargs['extra_args']: @@ -2240,7 +2238,7 @@ def generate_vapi(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'Gener build_dir = os.path.join(state.environment.get_build_dir(), state.subdir) source_dir = os.path.join(state.environment.get_source_dir(), state.subdir) pkg_cmd, vapi_depends, vapi_packages, vapi_includes, packages = self._extract_vapi_packages(state, kwargs['packages']) - cmd: T.List[T.Union[ToolType, str]] + cmd: T.List[T.Union[Program, str]] cmd = [state.find_program('vapigen'), '--quiet', f'--library={library}', f'--directory={build_dir}'] cmd.extend([f'--vapidir={d}' for d in kwargs['vapi_dirs']]) cmd.extend([f'--metadatadir={d}' for d in kwargs['metadata_dirs']]) diff --git a/mesonbuild/modules/i18n.py b/mesonbuild/modules/i18n.py index 06e891714d24..df029dd5828f 100644 --- a/mesonbuild/modules/i18n.py +++ b/mesonbuild/modules/i18n.py @@ -27,6 +27,7 @@ from ..build import Target from ..interpreter import Interpreter from ..interpreterbase import TYPE_var + from ..programs import Program class MergeFile(TypedDict): @@ -259,7 +260,7 @@ def __init__(self, interpreter: 'Interpreter'): 'itstool_join': self.itstool_join, 'xgettext': self.xgettext, }) - self.tools: T.Dict[str, T.Optional[T.Union[ExternalProgram, build.LocalProgram]]] = { + self.tools: T.Dict[str, T.Optional[Program]] = { 'itstool': None, 'msgfmt': None, 'msginit': None, diff --git a/mesonbuild/modules/icestorm.py b/mesonbuild/modules/icestorm.py index 86af78d8b73c..c89acd38b08b 100644 --- a/mesonbuild/modules/icestorm.py +++ b/mesonbuild/modules/icestorm.py @@ -16,7 +16,7 @@ from . import ModuleState from ..interpreter import Interpreter - from ..programs import ExternalProgram + from ..programs import Program class ProjectKwargs(TypedDict): @@ -29,7 +29,7 @@ class IceStormModule(ExtensionModule): def __init__(self, interpreter: Interpreter) -> None: super().__init__(interpreter) - self.tools: T.Dict[str, T.Union[ExternalProgram, build.LocalProgram]] = {} + self.tools: T.Dict[str, Program] = {} self.methods.update({ 'project': self.project, }) diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py index e0d57a66c9e9..c6dabdf48040 100644 --- a/mesonbuild/modules/python.py +++ b/mesonbuild/modules/python.py @@ -15,7 +15,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.interpreterobjects import BaseProgramHolder +from ..interpreter.interpreterobjects import ProgramHolder from ..interpreter.type_checking import NoneType, DEPENDENCY_KWS, PRESERVE_PATH_KW, SHARED_MOD_KWS from ..interpreterbase import ( noPosargs, noKwargs, permittedKwargs, ContainerTypeInfo, @@ -109,9 +109,9 @@ def _get_path(self, state: T.Optional['ModuleState'], key: str) -> str: _LIMITED_API_KW = KwargInfo('limited_api', str, default='', since='1.3.0') _DEFAULTABLE_SUBDIR_KW = KwargInfo('subdir', (str, NoneType)) -class PythonInstallation(BaseProgramHolder['PythonExternalProgram']): +class PythonInstallation(ProgramHolder['PythonExternalProgram']): def __init__(self, python: 'PythonExternalProgram', interpreter: 'Interpreter'): - BaseProgramHolder.__init__(self, python, interpreter) + ProgramHolder.__init__(self, python, interpreter) info = python.info prefix = self.interpreter.environment.coredata.optstore.get_value_for(OptionKey('prefix')) assert isinstance(prefix, str), 'for mypy' diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index 43a7ff4efcb1..fc4fbb06c135 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -13,7 +13,7 @@ from . import ExtensionModule, ModuleReturnValue, ModuleInfo from .. import mesonlib, mlog from ..build import (BothLibraries, BuildTarget, CustomTargetIndex, Executable, ExtractedObjects, GeneratedList, - CustomTarget, InvalidArguments, Jar, LocalProgram, StructuredSources, SharedLibrary, StaticLibrary) + CustomTarget, InvalidArguments, Jar, StructuredSources, SharedLibrary, StaticLibrary) from ..compilers.compilers import are_asserts_disabled_for_subproject, lang_suffixes from ..interpreter.type_checking import ( DEPENDENCIES_KW, LINK_WITH_KW, LINK_WHOLE_KW, SHARED_LIB_KWS, TEST_KWS, TEST_KWS_NO_ARGS, @@ -34,6 +34,7 @@ from ..interpreter.interpreter import SourceInputs, SourceOutputs from ..interpreter.interpreterobjects import Test from ..interpreter.type_checking import SourcesVarargsType + from ..programs import Program from typing_extensions import TypedDict, Literal @@ -90,7 +91,7 @@ class RustModule(ExtensionModule): def __init__(self, interpreter: Interpreter) -> None: super().__init__(interpreter) - self._bindgen_bin: T.Optional[T.Union[ExternalProgram, LocalProgram]] = None + self._bindgen_bin: T.Optional[Program] = None if 'rust' in interpreter.compilers.host: rustc = T.cast('RustCompiler', interpreter.compilers.host['rust']) self._bindgen_rust_target = 'nightly' if rustc.is_nightly else rustc.version diff --git a/mesonbuild/modules/wayland.py b/mesonbuild/modules/wayland.py index 675a6d9740e0..3152550ecfd5 100644 --- a/mesonbuild/modules/wayland.py +++ b/mesonbuild/modules/wayland.py @@ -6,7 +6,7 @@ import typing as T from . import ExtensionModule, ModuleReturnValue, ModuleInfo -from ..build import CustomTarget, LocalProgram +from ..build import CustomTarget from ..interpreter.type_checking import NoneType, in_set_validator from ..interpreterbase import typed_pos_args, typed_kwargs, KwargInfo, FeatureNew from ..mesonlib import File, MesonException @@ -17,8 +17,8 @@ from . import ModuleState from ..dependencies import Dependency from ..interpreter import Interpreter - from ..programs import ExternalProgram from ..mesonlib import FileOrString + from ..programs import Program class ScanXML(TypedDict): @@ -41,7 +41,7 @@ def __init__(self, interpreter: Interpreter) -> None: self.protocols_dep: T.Optional[Dependency] = None self.pkgdatadir: T.Optional[str] = None - self.scanner_bin: T.Optional[T.Union[ExternalProgram, LocalProgram]] = None + self.scanner_bin: T.Optional[Program] = None self.methods.update({ 'scan_xml': self.scan_xml, @@ -88,7 +88,7 @@ def scan_xml(self, state: ModuleState, args: T.Tuple[T.List[FileOrString]], kwar targets.append(code) for side in sides: - command = [self.scanner_bin, f'{side}-header', '@INPUT@', '@OUTPUT@'] + command: T.List[T.Union[str, Program]] = [self.scanner_bin, f'{side}-header', '@INPUT@', '@OUTPUT@'] if kwargs['include_core_only']: command.append('--include-core-only') diff --git a/mesonbuild/programs.py b/mesonbuild/programs.py index 865750bea6b3..f91f34febad2 100644 --- a/mesonbuild/programs.py +++ b/mesonbuild/programs.py @@ -24,7 +24,7 @@ from .interpreter import Interpreter -class BaseProgram(mesonlib.HoldableObject, metaclass=ABCMeta): +class Program(mesonlib.HoldableObject, metaclass=ABCMeta): ''' A base class for LocalProgram and ExternalProgram.''' name: str @@ -51,7 +51,7 @@ def description(self) -> str: '''Human friendly description of the command''' -class ExternalProgram(BaseProgram): +class ExternalProgram(Program): """A program that is found on the system. :param name: The name of the program