summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEli Schwartz <eschwartz93@gmail.com>2025-10-15 21:49:10 -0400
committerEli Schwartz <eschwartz93@gmail.com>2025-10-15 23:01:36 -0400
commit5c0aad57f92d2a6bebc1cb17655dd8a56f4bcd3f (patch)
tree69a228a10f425b33392df576f79bd8b7c5402104
parent1177e77c2893891cb35144b8033786cb8f75c7cd (diff)
downloadmeson-5c0aad57f92d2a6bebc1cb17655dd8a56f4bcd3f.tar.gz
revert local_program()
This reverts https://github.com/mesonbuild/meson/pull/15107 Explicit objections regarding the design were raised and not answered, so it shouldn't have been merged. It needs to be discussed and revisited.
-rw-r--r--data/syntax-highlighting/vim/syntax/meson.vim1
-rw-r--r--docs/markdown/snippets/local_program.md13
-rw-r--r--docs/yaml/functions/local_program.yaml43
-rw-r--r--mesonbuild/ast/interpreter.py1
-rw-r--r--mesonbuild/backend/backends.py10
-rw-r--r--mesonbuild/build.py86
-rw-r--r--mesonbuild/interpreter/__init__.py4
-rw-r--r--mesonbuild/interpreter/interpreter.py140
-rw-r--r--mesonbuild/interpreter/interpreterobjects.py28
-rw-r--r--mesonbuild/interpreter/kwargs.py12
-rw-r--r--mesonbuild/interpreter/mesonmain.py40
-rw-r--r--mesonbuild/interpreter/type_checking.py7
-rw-r--r--mesonbuild/modules/__init__.py7
-rw-r--r--mesonbuild/modules/_qt.py36
-rw-r--r--mesonbuild/modules/dlang.py7
-rw-r--r--mesonbuild/modules/gnome.py22
-rw-r--r--mesonbuild/modules/i18n.py2
-rw-r--r--mesonbuild/modules/icestorm.py2
-rw-r--r--mesonbuild/modules/python.py6
-rw-r--r--mesonbuild/modules/rust.py14
-rw-r--r--mesonbuild/modules/wayland.py5
-rw-r--r--mesonbuild/programs.py40
-rw-r--r--test cases/common/285 local program/meson.build56
-rwxr-xr-xtest cases/common/285 local program/prog.py5
-rw-r--r--test cases/common/285 local program/pymod.py.in2
25 files changed, 187 insertions, 402 deletions
diff --git a/data/syntax-highlighting/vim/syntax/meson.vim b/data/syntax-highlighting/vim/syntax/meson.vim
index c4181c3f1..905e9fe64 100644
--- a/data/syntax-highlighting/vim/syntax/meson.vim
+++ b/data/syntax-highlighting/vim/syntax/meson.vim
@@ -93,7 +93,6 @@ syn keyword mesonBuiltin
\ executable
\ files
\ find_program
- \ local_program
\ generator
\ get_option
\ get_variable
diff --git a/docs/markdown/snippets/local_program.md b/docs/markdown/snippets/local_program.md
deleted file mode 100644
index e671efb2c..000000000
--- a/docs/markdown/snippets/local_program.md
+++ /dev/null
@@ -1,13 +0,0 @@
-## New [[local_program]] function
-
-Similar to [[find_program]], but only work with a program that exists in
-source tree, or a built target. Meson will not look for the program in the
-system or in a subproject.
-
-In addition, `depends` keyword argument can be specified in case the program
-depends on built targets, for example a Python script could require a compiled
-C module. If any such dependency is present, the program can only be used in
-build-time commands (e.g. [[custom_target]]).
-
-The program can be passed to [[meson.override_find_program]] and used in
-subprojects.
diff --git a/docs/yaml/functions/local_program.yaml b/docs/yaml/functions/local_program.yaml
deleted file mode 100644
index 07fafcf3a..000000000
--- a/docs/yaml/functions/local_program.yaml
+++ /dev/null
@@ -1,43 +0,0 @@
-name: local_program
-returns: external_program
-since: 1.10.0
-description: |
- Similar to [[find_program]], but only work with a program that exists in
- source tree, or a built target. Meson will not look for the program in the
- system or in a subproject.
-
- In addition, `depends` keyword argument can be specified in case the program
- depends on built targets, for example a Python script could require a compiled
- C module. If any such dependency is present, the program can only be used in
- build-time commands (e.g. [[custom_target]]).
-
- The program can be passed to [[meson.override_find_program]] and used in
- subprojects.
-
-posargs:
- program:
- type: str | file | exe | custom_tgt | custom_idx
- description: |
- A [[@file]] object or the name of a program in the current source directory.
-
-kwargs:
- depend_files:
- type: array[str | file]
- description: |
- files ([[@str]],
- [[@file]], or the return value of [[configure_file]] that
- this target depends on. Useful for adding regen dependencies.
-
- depends:
- type: array[build_tgt | custom_tgt | custom_idx]
- description: |
- Specifies that this target depends on the specified
- target(s). If specified, this program can only be used at build time,
- after those targets have been built.
-
- interpreter:
- type: external_program
- description: |
- When the program is a [[@custom_tgt]], Meson cannot derive the interpreter
- from the file's "shebang" (`#!`) line before it's built. If needed, this
- argument allows specifying an interpreter for the script.
diff --git a/mesonbuild/ast/interpreter.py b/mesonbuild/ast/interpreter.py
index 17ca15dd7..2f82a4a84 100644
--- a/mesonbuild/ast/interpreter.py
+++ b/mesonbuild/ast/interpreter.py
@@ -200,7 +200,6 @@ class AstInterpreter(InterpreterBase):
'configuration_data': self.func_do_nothing,
'configure_file': self.func_do_nothing,
'find_program': self.func_do_nothing,
- 'local_program': self.func_do_nothing,
'include_directories': self.func_do_nothing,
'add_global_arguments': self.func_do_nothing,
'add_global_link_arguments': self.func_do_nothing,
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index a322c9882..a5995e6b1 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -529,7 +529,7 @@ class Backend:
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.ExternalProgram, build.BuildTarget, build.CustomTarget, File, str]],
workdir: T.Optional[str] = None,
extra_bdeps: T.Optional[T.List[build.BuildTarget]] = None,
capture: T.Optional[str] = None,
@@ -542,15 +542,13 @@ class Backend:
# 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, build.CustomTargetIndex)):
+ elif isinstance(exe, build.CustomTarget):
# 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.
@@ -567,11 +565,9 @@ class Backend:
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, build.CustomTargetIndex)):
+ elif isinstance(c, (build.BuildTarget, build.CustomTarget)):
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 8f294c2ba..41ba32b63 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -363,7 +363,7 @@ class Build:
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, T.Union['OverrideExecutable', programs.ExternalProgram, programs.OverrideProgram]] = {}
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
@@ -1995,7 +1995,7 @@ class FileMaybeInTargetPrivateDir:
return self.fname
class Generator(HoldableObject):
- def __init__(self, exe: T.Union[Executable, programs.ExternalProgram, LocalProgram, CustomTarget, CustomTargetIndex],
+ def __init__(self, exe: T.Union['Executable', programs.ExternalProgram],
arguments: T.List[str],
output: T.List[str],
# how2dataclass
@@ -2004,14 +2004,10 @@ class Generator(HoldableObject):
capture: bool = False,
depends: T.Optional[T.List[BuildTargetTypes]] = None,
name: str = 'Generator'):
- 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
@@ -2020,7 +2016,7 @@ class Generator(HoldableObject):
repr_str = "<{0}: {1}>"
return repr_str.format(self.__class__.__name__, self.exe)
- def get_exe(self) -> T.Union[Executable, programs.ExternalProgram, CustomTarget, CustomTargetIndex]:
+ def get_exe(self) -> T.Union['Executable', programs.ExternalProgram]:
return self.exe
def get_base_outnames(self, inname: str) -> T.List[str]:
@@ -2260,6 +2256,10 @@ class Executable(BuildTarget):
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"
@@ -2282,6 +2282,21 @@ class Executable(BuildTarget):
def is_linkable_target(self):
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
@@ -2785,15 +2800,11 @@ 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.ExternalProgram, BuildTargetTypes]]) -> \
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):
@@ -2859,7 +2870,7 @@ class CustomTarget(Target, CustomTargetBase, CommandBase):
environment: environment.Environment,
command: T.Sequence[T.Union[
str, BuildTargetTypes, GeneratedList,
- programs.ExternalProgram, File, LocalProgram]],
+ programs.ExternalProgram, File]],
sources: T.Sequence[T.Union[
str, File, BuildTargetTypes, ExtractedObjects,
GeneratedList, programs.ExternalProgram]],
@@ -3119,7 +3130,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.ExternalProgram]],
dependencies: T.Sequence[AnyTargetType],
subdir: str,
subproject: str,
@@ -3342,46 +3353,17 @@ class ConfigurationData(HoldableObject):
def keys(self) -> T.Iterator[str]:
return self.values.keys()
-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,
- depend_files: T.Optional[T.List[File]] = None) -> None:
- super().__init__()
- self.name = program.name
- self.program = program
- self.depends = list(depends or [])
- self.depend_files = list(depend_files or [])
- self.version = version
+class OverrideExecutable(Executable):
+ def __init__(self, executable: Executable, version: str):
+ self._executable = executable
+ self._version = version
- def found(self) -> bool:
- return True
+ 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
-
- 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
+ return self._version
# A bit poorly named, but this represents plain data files to copy
# during install.
diff --git a/mesonbuild/interpreter/__init__.py b/mesonbuild/interpreter/__init__.py
index 5186b842b..e2ccce479 100644
--- a/mesonbuild/interpreter/__init__.py
+++ b/mesonbuild/interpreter/__init__.py
@@ -20,6 +20,7 @@ __all__ = [
'SubprojectHolder',
'DependencyHolder',
'GeneratedListHolder',
+ 'ExternalProgramHolder',
'extract_required_kwarg',
'ArrayHolder',
@@ -34,7 +35,8 @@ from .compiler import CompilerHolder
from .interpreterobjects import (ExecutableHolder, BuildTargetHolder, CustomTargetHolder,
CustomTargetIndexHolder, MachineHolder, Test,
ConfigurationDataHolder, SubprojectHolder, DependencyHolder,
- GeneratedListHolder, extract_required_kwarg)
+ GeneratedListHolder, ExternalProgramHolder,
+ extract_required_kwarg)
from .primitives import (
ArrayHolder,
diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py
index 51671bfc5..8481f36bd 100644
--- a/mesonbuild/interpreter/interpreter.py
+++ b/mesonbuild/interpreter/interpreter.py
@@ -21,7 +21,7 @@ from ..mesonlib import (EnvironmentVariables, ExecutableSerialisation, MesonBugE
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
from ..dependencies import Dependency
from ..depfile import DepFile
from ..interpreterbase import ContainerTypeInfo, InterpreterBase, KwargInfo, typed_kwargs, typed_pos_args
@@ -118,6 +118,7 @@ if T.TYPE_CHECKING:
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
@@ -129,7 +130,7 @@ if T.TYPE_CHECKING:
BuildTargetSource = T.Union[mesonlib.FileOrString, build.GeneratedTypes, build.StructuredSources]
- ProgramVersionFunc = T.Callable[[T.Union[ExternalProgram, build.LocalProgram]], str]
+ ProgramVersionFunc = T.Callable[[T.Union[ExternalProgram, build.Executable, OverrideProgram]], str]
TestClass = T.TypeVar('TestClass', bound=Test)
@@ -368,7 +369,6 @@ class Interpreter(InterpreterBase, HoldableObject):
'executable': self.func_executable,
'files': self.func_files,
'find_program': self.func_find_program,
- 'local_program': self.func_local_program,
'generator': self.func_generator,
'get_option': self.func_get_option,
'get_variable': self.func_get_variable,
@@ -440,6 +440,7 @@ class Interpreter(InterpreterBase, HoldableObject):
build.Generator: OBJ.GeneratorHolder,
build.GeneratedList: OBJ.GeneratedListHolder,
build.ExtractedObjects: OBJ.GeneratedObjectsHolder,
+ build.OverrideExecutable: OBJ.OverrideExecutableHolder,
build.RunTarget: OBJ.RunTargetHolder,
build.AliasTarget: OBJ.AliasTargetHolder,
build.Headers: OBJ.HeadersHolder,
@@ -469,7 +470,7 @@ class Interpreter(InterpreterBase, HoldableObject):
'''
self.bound_holder_map.update({
dependencies.Dependency: OBJ.DependencyHolder,
- BaseProgram: OBJ.BaseProgramHolder,
+ ExternalProgram: OBJ.ExternalProgramHolder,
compilers.Compiler: compilerOBJ.CompilerHolder,
ModuleObject: OBJ.ModuleObjectHolder,
MutableModuleObject: OBJ.MutableModuleObjectHolder,
@@ -768,8 +769,8 @@ class Interpreter(InterpreterBase, HoldableObject):
# 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, ExternalProgram, compilers.Compiler, mesonlib.File, str),
+ varargs=(build.Executable, ExternalProgram, compilers.Compiler, mesonlib.File, str))
@typed_kwargs(
'run_command',
KwargInfo('check', (bool, NoneType), since='0.47.0'),
@@ -777,21 +778,14 @@ class Interpreter(InterpreterBase, HoldableObject):
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, ExternalProgram, compilers.Compiler, mesonlib.File, str],
+ T.List[T.Union[build.Executable, ExternalProgram, compilers.Compiler, mesonlib.File, str]]],
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, build.LocalProgram],
- T.List[T.Union[build.Executable, ExternalProgram, compilers.Compiler, mesonlib.File, str, build.LocalProgram]]],
+ 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]]],
kwargs: 'kwtypes.RunCommand',
in_builddir: bool = False) -> RunProcess:
cmd, cargs = args
@@ -805,17 +799,19 @@ class Interpreter(InterpreterBase, HoldableObject):
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.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 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 not cmd.found():
raise InterpreterException(f'command {cmd.get_name()!r} not found or not executable')
elif isinstance(cmd, compilers.Compiler):
@@ -847,15 +843,8 @@ class Interpreter(InterpreterBase, HoldableObject):
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:
- self._compiled_exe_error(a)
+ raise InterpreterException(overridden_msg.format(a.name, cmd.description()))
# If any file that was used as an argument to the command
# changes, we must re-run the configuration step.
@@ -1620,7 +1609,7 @@ class Interpreter(InterpreterBase, HoldableObject):
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[T.Union[ExternalProgram, OverrideProgram, build.OverrideExecutable]]:
for name in command_names:
if not isinstance(name, str):
continue
@@ -1635,7 +1624,7 @@ class Interpreter(InterpreterBase, HoldableObject):
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: T.Union[build.OverrideExecutable, ExternalProgram, 'OverrideProgram']) -> 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:
@@ -1660,7 +1649,7 @@ class Interpreter(InterpreterBase, HoldableObject):
search_dirs: T.Optional[T.List[str]] = None,
version_arg: T.Optional[str] = '',
version_func: T.Optional[ProgramVersionFunc] = None
- ) -> T.Union[ExternalProgram, build.LocalProgram]:
+ ) -> T.Union['ExternalProgram', 'build.OverrideExecutable', 'OverrideProgram']:
args = mesonlib.listify(args)
extra_info: T.List[mlog.TV_Loggable] = []
@@ -1692,7 +1681,7 @@ class Interpreter(InterpreterBase, HoldableObject):
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[T.Union[ExternalProgram, build.Executable, OverrideProgram]]:
progobj = self.program_from_overrides(args, extra_info)
if progobj:
return progobj
@@ -1728,7 +1717,7 @@ class Interpreter(InterpreterBase, HoldableObject):
return progobj
- def check_program_version(self, progobj: T.Union[ExternalProgram, build.LocalProgram],
+ def check_program_version(self, progobj: T.Union[ExternalProgram, build.Executable, OverrideProgram],
wanted: T.Union[str, T.List[str]],
version_func: T.Optional[ProgramVersionFunc],
extra_info: T.List[mlog.TV_Loggable]) -> bool:
@@ -1755,7 +1744,7 @@ class Interpreter(InterpreterBase, HoldableObject):
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[T.Union[ExternalProgram, build.Executable, OverrideProgram]]:
mlog.log('Fallback to subproject', mlog.bold(fallback), 'which provides program',
mlog.bold(' '.join(args)))
sp_kwargs: kwtypes.DoSubproject = {
@@ -1782,7 +1771,7 @@ class Interpreter(InterpreterBase, HoldableObject):
@disablerIfNotFound
def func_find_program(self, node: mparser.BaseNode, args: T.Tuple[T.List[mesonlib.FileOrString]],
kwargs: 'kwtypes.FindProgram',
- ) -> T.Union[ExternalProgram, build.LocalProgram]:
+ ) -> T.Union['build.Executable', ExternalProgram, 'OverrideProgram']:
disabled, required, feature = extract_required_kwarg(kwargs, self.subproject)
if disabled:
assert feature, 'for mypy'
@@ -1795,44 +1784,6 @@ class Interpreter(InterpreterBase, HoldableObject):
silent=False, wanted=kwargs['version'], version_arg=kwargs['version_argument'],
search_dirs=search_dirs)
- @FeatureNew('local_program', '1.10.0')
- @typed_pos_args('local_program', (str, mesonlib.File, build.Executable, build.CustomTarget, build.CustomTargetIndex))
- @typed_kwargs(
- 'local_program',
- DEPENDS_KW,
- DEPEND_FILES_KW,
- KwargInfo('interpreter', (ExternalProgram, NoneType), default=None),
- )
- def func_local_program(self, node: mparser.BaseNode, args: T.Tuple[T.Union[mesonlib.FileOrString, build.Executable, build.CustomTarget, build.CustomTargetIndex]],
- kwargs: kwtypes.LocalProgram) -> build.LocalProgram:
- return self._local_program_impl(args[0], kwargs['depends'], kwargs['depend_files'], kwargs['interpreter'])
-
- def _local_program_impl(self, exe: T.Union[mesonlib.FileOrString, build.Executable, build.CustomTarget, build.CustomTargetIndex],
- depends_: T.Optional[T.List[T.Union[build.BuildTarget, build.CustomTarget, build.CustomTargetIndex]]] = None,
- depend_files_: T.Optional[T.List[mesonlib.FileOrString]] = None,
- interpreter: T.Optional[ExternalProgram] = None) -> build.LocalProgram:
- if isinstance(exe, build.CustomTarget):
- if len(exe.outputs) != 1:
- raise InvalidArguments('CustomTarget used as LocalProgram must have exactly one output.')
- depends = [d.target if isinstance(d, build.CustomTargetIndex) else d for d in (depends_ or [])]
- depend_files = self.source_strings_to_files(depend_files_ or [])
- if isinstance(exe, (str, mesonlib.File)):
- file = self.source_strings_to_files([exe])[0]
- abspath = file.absolute_path(self.environment.source_dir, self.environment.build_dir)
- prog = ExternalProgram(file.fname, command=[abspath], silent=True)
- return build.LocalProgram(prog, self.project_version, depends, depend_files)
- if interpreter:
- if not isinstance(exe, (build.CustomTarget, build.CustomTargetIndex)):
- raise InvalidArguments('The "interpreter" argument can only be used when the first argument is a custom target.')
- if not interpreter.found():
- raise InvalidArguments(f'Specified interpreter program {interpreter.description()!r} not found.')
- target = exe.target if isinstance(exe, build.CustomTargetIndex) else exe
- depends.append(target)
- cmd = interpreter.get_command() + [self.backend.get_target_filename(exe)]
- prog = ExternalProgram(exe.name, command=cmd, silent=True)
- return build.LocalProgram(prog, self.project_version, depends, depend_files)
- return build.LocalProgram(exe, self.project_version, depends, depend_files)
-
# When adding kwargs, please check if they make sense in dependencies.get_dep_identifier()
@FeatureNewKwargs('dependency', '0.57.0', ['cmake_package_version'])
@FeatureNewKwargs('dependency', '0.56.0', ['allow_fallback'])
@@ -2255,7 +2206,7 @@ class Interpreter(InterpreterBase, HoldableObject):
self.add_target(name, tg)
return tg
- @typed_pos_args('generator', (build.Executable, ExternalProgram, build.LocalProgram))
+ @typed_pos_args('generator', (build.Executable, ExternalProgram))
@typed_kwargs(
'generator',
KwargInfo('arguments', ContainerTypeInfo(list, str, allow_empty=False), required=True, listify=True),
@@ -2265,7 +2216,7 @@ class Interpreter(InterpreterBase, HoldableObject):
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, ExternalProgram]],
kwargs: 'kwtypes.FuncGenerator') -> build.Generator:
for rule in kwargs['output']:
if '@BASENAME@' not in rule and '@PLAINNAME@' not in rule:
@@ -2279,17 +2230,17 @@ class Interpreter(InterpreterBase, HoldableObject):
return build.Generator(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, ExternalProgram, 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, ExternalProgram, 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, ExternalProgram, 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, ExternalProgram, mesonlib.File, build.CustomTarget, build.CustomTargetIndex]],
kwargs: 'kwtypes.FuncTest') -> None:
self.add_test(node, args, kwargs, True)
@@ -2303,7 +2254,7 @@ class Interpreter(InterpreterBase, HoldableObject):
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, ExternalProgram, mesonlib.File, build.CustomTarget, build.CustomTargetIndex]],
kwargs: 'kwtypes.BaseTest',
klass: T.Type[TestClass] = Test) -> TestClass:
name = args[0]
@@ -2312,20 +2263,15 @@ class Interpreter(InterpreterBase, HoldableObject):
location=node)
name = name.replace(':', '_')
exe = args[1]
- 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 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):
- depends.append(exe)
+ kwargs.setdefault('depends', []).append(exe)
elif isinstance(exe, build.CustomTargetIndex):
- depends.append(exe.target)
+ kwargs.setdefault('depends', []).append(exe.target)
env = self.unpack_env_kwarg(kwargs)
@@ -2344,7 +2290,7 @@ class Interpreter(InterpreterBase, HoldableObject):
prj,
suite,
exe,
- depends,
+ kwargs['depends'],
kwargs.get('is_parallel', False),
kwargs['args'],
env,
@@ -2669,7 +2615,7 @@ class Interpreter(InterpreterBase, HoldableObject):
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, ExternalProgram, compilers.Compiler, mesonlib.File, str), allow_empty=False), NoneType),
listify=True,
),
KwargInfo(
diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py
index addc7d3db..86e8957bc 100644
--- a/mesonbuild/interpreter/interpreterobjects.py
+++ b/mesonbuild/interpreter/interpreterobjects.py
@@ -23,7 +23,7 @@ from ..interpreterbase import (
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
from ..mesonlib import HoldableObject, listify, Popen_safe
import typing as T
@@ -605,10 +605,10 @@ class DependencyHolder(ObjectHolder[Dependency]):
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)
+_EXTPROG = T.TypeVar('_EXTPROG', bound=ExternalProgram)
-class BaseProgramHolder(ObjectHolder[_BASEPROG]):
- def __init__(self, ep: _BASEPROG, interpreter: 'Interpreter') -> None:
+class _ExternalProgramHolder(ObjectHolder[_EXTPROG]):
+ def __init__(self, ep: _EXTPROG, interpreter: 'Interpreter') -> None:
super().__init__(ep, interpreter)
@noPosargs
@@ -619,15 +619,15 @@ class BaseProgramHolder(ObjectHolder[_BASEPROG]):
@noPosargs
@noKwargs
- @FeatureDeprecated('Program.path', '0.55.0',
- 'use Program.full_path() instead')
+ @FeatureDeprecated('ExternalProgram.path', '0.55.0',
+ 'use ExternalProgram.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('Program.full_path', '0.55.0')
+ @FeatureNew('ExternalProgram.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()
@@ -641,11 +641,9 @@ class BaseProgramHolder(ObjectHolder[_BASEPROG]):
@noPosargs
@noKwargs
- @FeatureNew('Program.version', '0.62.0')
+ @FeatureNew('ExternalProgram.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:
@@ -656,6 +654,8 @@ class BaseProgramHolder(ObjectHolder[_BASEPROG]):
def found(self) -> bool:
return self.held_object.found()
+class ExternalProgramHolder(_ExternalProgramHolder[ExternalProgram]):
+ pass
class ExternalLibraryHolder(ObjectHolder[ExternalLibrary]):
def __init__(self, el: ExternalLibrary, interpreter: 'Interpreter'):
@@ -1163,3 +1163,11 @@ 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)
diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py
index 22dde255d..e95d47341 100644
--- a/mesonbuild/interpreter/kwargs.py
+++ b/mesonbuild/interpreter/kwargs.py
@@ -171,8 +171,7 @@ class FuncAddLanguages(ExtractRequired):
class RunTarget(TypedDict):
- command: T.List[T.Union[str, build.BuildTarget, build.CustomTarget, ExternalProgram,
- File, LocalProgram]]
+ command: T.List[T.Union[str, build.BuildTarget, build.CustomTarget, ExternalProgram, File]]
depends: T.List[T.Union[build.BuildTarget, build.CustomTarget]]
env: EnvironmentVariables
@@ -183,7 +182,7 @@ class CustomTarget(TypedDict):
build_always_stale: T.Optional[bool]
build_by_default: T.Optional[bool]
capture: bool
- command: T.List[T.Union[str, build.BuildTargetTypes, ExternalProgram, File, LocalProgram]]
+ command: T.List[T.Union[str, build.BuildTargetTypes, ExternalProgram, File]]
console: bool
depend_files: T.List[FileOrString]
depends: T.List[T.Union[build.BuildTarget, build.CustomTarget]]
@@ -247,13 +246,6 @@ class FindProgram(ExtractRequired, ExtractSearchDirs):
version: T.List[str]
-class LocalProgram(TypedDict):
-
- depend_files: T.List[FileOrString]
- depends: T.List[T.Union[build.BuildTarget, build.CustomTarget, build.CustomTargetIndex]]
- interpreter: T.Optional[ExternalProgram]
-
-
class RunCommand(TypedDict):
check: bool
diff --git a/mesonbuild/interpreter/mesonmain.py b/mesonbuild/interpreter/mesonmain.py
index 3afeb8647..d22d36bf0 100644
--- a/mesonbuild/interpreter/mesonmain.py
+++ b/mesonbuild/interpreter/mesonmain.py
@@ -14,7 +14,7 @@ from .. import mlog, coredata
from ..mesonlib import MachineChoice
from ..options import OptionKey
-from ..programs import ExternalProgram
+from ..programs import OverrideProgram, 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,
@@ -57,23 +57,21 @@ class MesonMain(MesonInterpreterObject):
self.interpreter = interpreter
def _find_source_script(
- 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]] = []
+ 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]] = []
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)
- elif isinstance(prog, (str, mesonlib.File)):
+ largs.append(prog)
+ else:
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)
- 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)
+ found = self.interpreter.find_program_impl([prog])
+ largs.append(found)
- largs.append(prog)
largs.extend(args)
es = self.interpreter.backend.get_executable_serialisation(largs, verbose=True)
es.subproject = self.interpreter.subproject
@@ -118,7 +116,7 @@ class MesonMain(MesonInterpreterObject):
@typed_pos_args(
'meson.add_install_script',
- (str, mesonlib.File, build.Executable, ExternalProgram, build.LocalProgram),
+ (str, mesonlib.File, build.Executable, ExternalProgram),
varargs=(str, mesonlib.File, build.BuildTarget, build.CustomTarget, build.CustomTargetIndex, ExternalProgram)
)
@typed_kwargs(
@@ -134,7 +132,7 @@ class MesonMain(MesonInterpreterObject):
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, allow_built_program=True)
+ script = self._find_source_script('add_install_script', args[0], script_args)
script.skip_if_destdir = kwargs['skip_if_destdir']
script.tag = kwargs['install_tag']
script.dry_run = kwargs['dry_run']
@@ -149,7 +147,7 @@ class MesonMain(MesonInterpreterObject):
@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, ExternalProgram],
T.List[T.Union[str, mesonlib.File, ExternalProgram]]],
kwargs: 'TYPE_kwargs') -> None:
script_args = self._process_script_args('add_postconf_script', args[1])
@@ -166,7 +164,7 @@ class MesonMain(MesonInterpreterObject):
@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, ExternalProgram],
T.List[T.Union[str, mesonlib.File, ExternalProgram]]],
kwargs: 'TYPE_kwargs') -> None:
if args[1]:
@@ -314,13 +312,19 @@ class MesonMain(MesonInterpreterObject):
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, ExternalProgram, 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, ExternalProgram, build.Executable]], kwargs: 'TYPE_kwargs') -> None:
name, exe = args
- if not isinstance(exe, (ExternalProgram, build.LocalProgram)):
- exe = self.interpreter._local_program_impl(exe)
+ if isinstance(exe, mesonlib.File):
+ abspath = exe.absolute_path(self.interpreter.environment.source_dir,
+ 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])
+ elif isinstance(exe, build.Executable):
+ exe = build.OverrideExecutable(exe, self.interpreter.project_version)
self.interpreter.add_find_program_override(name, exe)
@typed_kwargs(
diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py
index a54a69ff0..4f961bb60 100644
--- a/mesonbuild/interpreter/type_checking.py
+++ b/mesonbuild/interpreter/type_checking.py
@@ -10,8 +10,7 @@ import typing as T
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, InternalDependency
from ..interpreterbase.decorators import KwargInfo, ContainerTypeInfo, FeatureBroken
@@ -286,9 +285,9 @@ DEPEND_FILES_KW: KwargInfo[T.List[T.Union[str, File]]] = KwargInfo(
default=[],
)
-COMMAND_KW: KwargInfo[T.List[T.Union[str, BuildTargetTypes, ExternalProgram, File, LocalProgram]]] = KwargInfo(
+COMMAND_KW: KwargInfo[T.List[T.Union[str, BuildTargetTypes, ExternalProgram, File]]] = KwargInfo(
'command',
- ContainerTypeInfo(list, (str, BuildTarget, CustomTarget, CustomTargetIndex, ExternalProgram, File, LocalProgram), allow_empty=False),
+ ContainerTypeInfo(list, (str, BuildTarget, CustomTarget, CustomTargetIndex, ExternalProgram, File), allow_empty=False),
required=True,
listify=True,
default=[],
diff --git a/mesonbuild/modules/__init__.py b/mesonbuild/modules/__init__.py
index 5f088e9ed..87892e6d7 100644
--- a/mesonbuild/modules/__init__.py
+++ b/mesonbuild/modules/__init__.py
@@ -18,6 +18,7 @@ if T.TYPE_CHECKING:
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
@@ -74,14 +75,14 @@ class ModuleState:
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) -> T.Union[ExternalProgram, build.OverrideExecutable, OverrideProgram]:
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) -> T.Union[build.OverrideExecutable, ExternalProgram, 'OverrideProgram']:
# 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 @@ class ModuleState:
# 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, 'ExternalProgram', 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 0f6d2ecb2..675c174a7 100644
--- a/mesonbuild/modules/_qt.py
+++ b/mesonbuild/modules/_qt.py
@@ -208,7 +208,7 @@ class QtBaseModule(ExtensionModule):
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, T.Union[ExternalProgram, build.Executable]] = {
tool: NonExistingExternalProgram(tool) for tool in self._set_of_qt_tools
}
self.methods.update({
@@ -250,7 +250,7 @@ class QtBaseModule(ExtensionModule):
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: T.Union[ExternalProgram, build.Executable]) -> str:
_, out, err = Popen_safe(p.get_command() + arg)
if name == 'lrelease' or not qt_dep.version.startswith('4'):
care = out
@@ -445,17 +445,12 @@ class QtBaseModule(ExtensionModule):
for s in sources:
qrc_deps.extend(self._parse_qrc_deps(state, s))
- cmd: T.List[T.Union[ExternalProgram, 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,
- cmd,
+ self.tools['rcc'].get_command() + ['-name', name, '-o', '@OUTPUT@'] + extra_args + ['@INPUT@'] + DEPFILE_ARGS,
sources,
[f'{name}.cpp'],
depend_files=qrc_deps,
@@ -471,16 +466,12 @@ class QtBaseModule(ExtensionModule):
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,
- cmd,
+ self.tools['rcc'].get_command() + ['-name', '@BASENAME@', '-o', '@OUTPUT@'] + extra_args + ['@INPUT@'] + DEPFILE_ARGS,
[rcc_file],
[f'{name}.cpp'],
depend_files=qrc_deps,
@@ -733,7 +724,7 @@ class QtBaseModule(ExtensionModule):
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[ExternalProgram, build.Executable, str]] = [self.tools['lrelease'], '@INPUT@', '-qm', '@OUTPUT@']
lrelease_target = build.CustomTarget(
f'qt{self.qt_version}-compile-{ts}',
outdir,
@@ -873,15 +864,12 @@ class QtBaseModule(ExtensionModule):
input_args.append(f'@INPUT{input_counter}@')
input_counter += 1
- cmd: T.List[T.Union[ExternalProgram, 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,
- cmd,
+ self.tools['moc'].get_command() + ['--collect-json', '-o', '@OUTPUT@'] + input_args,
moc_json,
[f'{target_name}_json_collect.json'],
description=f'Collecting json type information for {target_name}',
@@ -920,17 +908,12 @@ class QtBaseModule(ExtensionModule):
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 = [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,
- cmd,
+ self.tools['qmlcachegen'].get_command() + ['-o', '@OUTPUT@'] + ['--resource-name', f'qmlcache_{target_name}'] + kwargs['extra_args'] + ['--resource=@INPUT@'] + cachegen_inputs,
[kwargs['qml_qrc']],
#output name format matters here
[f'{target_name}_qmlcache_loader.cpp'],
@@ -958,12 +941,11 @@ class QtBaseModule(ExtensionModule):
install_dir: T.List[T.Union[str, Literal[False]]] = [False]
install_tag: T.List[T.Union[str, None]] = [None]
- cmd = [
- self.tools['qmltyperegistrar'],
+ cmd = self.tools['qmltyperegistrar'].get_command() + [
'--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 860a62481..35ce86be8 100644
--- a/mesonbuild/modules/dlang.py
+++ b/mesonbuild/modules/dlang.py
@@ -12,7 +12,7 @@ import typing as T
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
@@ -22,11 +22,12 @@ if T.TYPE_CHECKING:
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
+ from ..programs import ExternalProgram, OverrideProgram
- _AnyProgram: TypeAlias = T.Union[ExternalProgram, LocalProgram]
+ _AnyProgram: TypeAlias = T.Union[OverrideExecutable, ExternalProgram, OverrideProgram]
_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 6219e4ce0..9e525601e 100644
--- a/mesonbuild/modules/gnome.py
+++ b/mesonbuild/modules/gnome.py
@@ -22,7 +22,7 @@ from .. import build
from .. import interpreter
from .. import mesonlib
from .. import mlog
-from ..build import CustomTarget, CustomTargetIndex, Executable, GeneratedList, InvalidArguments, LocalProgram
+from ..build import CustomTarget, CustomTargetIndex, Executable, GeneratedList, InvalidArguments
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
@@ -33,6 +33,7 @@ from ..mesonlib import (
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:
@@ -197,7 +198,7 @@ if T.TYPE_CHECKING:
vtail: T.Optional[str]
depends: T.List[T.Union[BuildTarget, CustomTarget, CustomTargetIndex]]
- ToolType: TypeAlias = T.Union[ExternalProgram, LocalProgram]
+ ToolType: TypeAlias = T.Union[Executable, ExternalProgram, OverrideProgram]
# Differs from the CustomTarget version in that it straight defaults to True
@@ -788,7 +789,8 @@ class GnomeModule(ExtensionModule):
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, T.Union[Executable, 'ExternalProgram', 'OverrideProgram'],
+ T.Union[Executable, 'ExternalProgram', 'OverrideProgram']]:
if not self.gir_dep:
self.gir_dep = state.dependency('gobject-introspection-1.0')
self.giscanner = self._find_tool(state, 'g-ir-scanner')
@@ -808,7 +810,7 @@ class GnomeModule(ExtensionModule):
@functools.lru_cache(maxsize=None)
def _gir_has_option(self, option: str) -> bool:
exe = self.giscanner
- if isinstance(exe, LocalProgram):
+ if isinstance(exe, (Executable, OverrideProgram)):
# Handle overridden g-ir-scanner
assert option in {'--extra-library', '--sources-top-dirs'}
return True
@@ -969,7 +971,7 @@ class GnomeModule(ExtensionModule):
self,
state: 'ModuleState',
girfile: str,
- scan_command: T.Sequence[T.Union['FileOrString', Executable, ToolType]],
+ scan_command: T.Sequence[T.Union['FileOrString', Executable, ExternalProgram, OverrideProgram]],
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 +1020,7 @@ class GnomeModule(ExtensionModule):
@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, Executable, ExternalProgram, CustomTarget]],
generated_files: T.Sequence[T.Union[str, mesonlib.File, build.GeneratedTypes]],
kwargs: T.Dict[str, T.Any]) -> TypelibTarget:
install = kwargs['install_typelib']
@@ -1192,7 +1194,7 @@ class GnomeModule(ExtensionModule):
gir_inc_dirs: T.List[str] = []
- scan_command: T.List[T.Union[str, ToolType, Executable]] = [giscanner]
+ scan_command: T.List[T.Union[str, Executable, 'ExternalProgram', 'OverrideProgram']] = [giscanner]
scan_command += ['--quiet']
scan_command += ['--no-libtool']
scan_command += ['--namespace=' + ns, '--nsversion=' + nsversion]
@@ -1345,7 +1347,7 @@ class GnomeModule(ExtensionModule):
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[ExternalProgram, Executable, OverrideProgram, 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 +1379,7 @@ class GnomeModule(ExtensionModule):
targets.append(l_data)
po_file = l + '.po'
- po_args: T.List[T.Union[ToolType, str]] = [
+ po_args: T.List[T.Union[ExternalProgram, Executable, OverrideProgram, str]] = [
msgmerge, '-q', '-o',
os.path.join('@SOURCE_ROOT@', l_subdir, po_file),
os.path.join('@SOURCE_ROOT@', l_subdir, po_file), pot_file]
@@ -2240,7 +2242,7 @@ class GnomeModule(ExtensionModule):
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[ExternalProgram, Executable, OverrideProgram, 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 06e891714..2d8d04d3e 100644
--- a/mesonbuild/modules/i18n.py
+++ b/mesonbuild/modules/i18n.py
@@ -259,7 +259,7 @@ class I18nModule(ExtensionModule):
'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[T.Union[ExternalProgram, build.Executable]]] = {
'itstool': None,
'msgfmt': None,
'msginit': None,
diff --git a/mesonbuild/modules/icestorm.py b/mesonbuild/modules/icestorm.py
index 86af78d8b..18bf0e202 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.LocalProgram]] = {}
+ self.tools: T.Dict[str, T.Union[ExternalProgram, build.Executable]] = {}
self.methods.update({
'project': self.project,
})
diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py
index c637e5f5e..6f5a63a0b 100644
--- a/mesonbuild/modules/python.py
+++ b/mesonbuild/modules/python.py
@@ -15,7 +15,7 @@ from ..dependencies import NotFoundDependency
from ..dependencies.detect import get_dep_identifier, find_external_dependency
from ..dependencies.python import BasicPythonExternalProgram, python_factory, _PythonDependencyBase
from ..interpreter import extract_required_kwarg, permitted_dependency_kwargs, primitives as P_OBJ
-from ..interpreter.interpreterobjects import BaseProgramHolder
+from ..interpreter.interpreterobjects import _ExternalProgramHolder
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 @@ _SUBDIR_KW = KwargInfo('subdir', str, default='')
_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(_ExternalProgramHolder['PythonExternalProgram']):
def __init__(self, python: 'PythonExternalProgram', interpreter: 'Interpreter'):
- BaseProgramHolder.__init__(self, python, interpreter)
+ _ExternalProgramHolder.__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 73094f571..89c20230b 100644
--- a/mesonbuild/modules/rust.py
+++ b/mesonbuild/modules/rust.py
@@ -13,7 +13,7 @@ from mesonbuild.interpreterbase.decorators import FeatureNew
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,
@@ -33,6 +33,7 @@ if T.TYPE_CHECKING:
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
@@ -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[T.Union[ExternalProgram, Executable, OverrideProgram]] = 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
@@ -374,7 +375,10 @@ class RustModule(ExtensionModule):
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:
- _, _, err = mesonlib.Popen_safe(self._bindgen_bin.get_command() + ['--rust-target', self._bindgen_rust_target])
+ # 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])
# < 0.71: Sometimes this is "invalid Rust target" and
# sometimes "invalid # rust target"
# >= 0.71: error: invalid value '...' for '--rust-target <RUST_TARGET>': "..." is not a valid Rust target, accepted values are of the form ...
@@ -382,7 +386,9 @@ class RustModule(ExtensionModule):
if 'Got an invalid' in err or 'is not a valid Rust target' in err:
self._bindgen_rust_target = None
- self._bindgen_set_std = mesonlib.version_compare(self._bindgen_bin.get_version(), '>= 0.71')
+ # 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')
name: str
if isinstance(header, File):
diff --git a/mesonbuild/modules/wayland.py b/mesonbuild/modules/wayland.py
index 675a6d974..94c6f819d 100644
--- a/mesonbuild/modules/wayland.py
+++ b/mesonbuild/modules/wayland.py
@@ -6,7 +6,7 @@ import os
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
@@ -15,6 +15,7 @@ if T.TYPE_CHECKING:
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
@@ -41,7 +42,7 @@ class WaylandModule(ExtensionModule):
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[T.Union[ExternalProgram, Executable]] = None
self.methods.update({
'scan_xml': self.scan_xml,
diff --git a/mesonbuild/programs.py b/mesonbuild/programs.py
index 16c12c85a..0abf09720 100644
--- a/mesonbuild/programs.py
+++ b/mesonbuild/programs.py
@@ -13,7 +13,6 @@ import sys
import re
import typing as T
from pathlib import Path
-from abc import ABCMeta, abstractmethod
from . import mesonlib
from . import mlog
@@ -24,33 +23,7 @@ if T.TYPE_CHECKING:
from .interpreter import Interpreter
-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):
+class ExternalProgram(mesonlib.HoldableObject):
"""A program that is found on the system.
:param name: The name of the program
@@ -384,6 +357,17 @@ class NonExistingExternalProgram(ExternalProgram): # lgtm [py/missing-call-to-i
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,
diff --git a/test cases/common/285 local program/meson.build b/test cases/common/285 local program/meson.build
deleted file mode 100644
index fba59e8e5..000000000
--- a/test cases/common/285 local program/meson.build
+++ /dev/null
@@ -1,56 +0,0 @@
-project('local program', version: '2.0')
-
-python3 = find_program('python3')
-
-# A module imported by prog but only available at build time.
-pymod = custom_target(
- input: 'pymod.py.in',
- output: 'pymod.py',
- command: [python3, '-c', 'import shutil,sys;shutil.copy(sys.argv[1], sys.argv[2])', '@INPUT@', '@OUTPUT@'],
- build_by_default: false,
-)
-
-# Copy into builddir to have the same location as pymod.py
-prog = configure_file(
- input: 'prog.py',
- output: 'prog.py',
- copy: true,
-)
-
-# Without the dependency it can't be run, but it should have the project version.
-prog1 = local_program(prog)
-assert(prog1.version() == '2.0')
-assert(prog1.found())
-
-prog2 = local_program(prog, depends: pymod)
-assert(prog2.version() == '2.0')
-assert(prog2.found())
-
-meson.override_find_program('prog', prog2)
-prog3 = find_program('prog')
-assert(prog3.version() == '2.0')
-assert(prog3.found())
-
-# This should have the pymod dependency
-custom_target(
- output: 'out.txt',
- command: [prog3],
- capture: true,
- build_by_default: true,
-)
-
-test('test-prog3', prog3)
-
-# Custom target as local program. Meson cannot parse the shebang at configure time,
-# so we need to specify it otherwise it won't run on Windows.
-prog_ct = custom_target(
- input: 'prog.py',
- output: 'prog-ct.py',
- command: [python3, '-c', 'import shutil,sys;shutil.copy(sys.argv[1], sys.argv[2])', '@INPUT@', '@OUTPUT@'],
- depends: pymod,
-)
-meson.override_find_program('prog4', local_program(prog_ct, interpreter: python3))
-prog4 = find_program('prog4')
-assert(prog4.version() == '2.0')
-assert(prog4.found())
-test('test-prog4', prog4)
diff --git a/test cases/common/285 local program/prog.py b/test cases/common/285 local program/prog.py
deleted file mode 100755
index bd1042a2f..000000000
--- a/test cases/common/285 local program/prog.py
+++ /dev/null
@@ -1,5 +0,0 @@
-#! /usr/bin/env python3
-
-import pymod
-
-raise SystemExit(pymod.foo())
diff --git a/test cases/common/285 local program/pymod.py.in b/test cases/common/285 local program/pymod.py.in
deleted file mode 100644
index 3cc1da0c3..000000000
--- a/test cases/common/285 local program/pymod.py.in
+++ /dev/null
@@ -1,2 +0,0 @@
-def foo() -> None:
- return 0