summaryrefslogtreecommitdiff
path: root/mesonbuild
diff options
context:
space:
mode:
authorXavier Claessens <xclaessens@netflix.com>2025-10-13 15:49:24 -0400
committerXavier Claessens <xclaesse@gmail.com>2025-10-15 12:15:39 -0400
commit569fe981b08f8fa38ff3533651ceff414decadf4 (patch)
tree75dc96f5ba8fce8bbc8da7d0ce027b573ed20bce /mesonbuild
parentbd33265b04ca609afe6bb895453c5757bbbbb27d (diff)
downloadmeson-569fe981b08f8fa38ff3533651ceff414decadf4.tar.gz
Add common ABC for ExternalProgram and LocalProgram
Diffstat (limited to 'mesonbuild')
-rw-r--r--mesonbuild/build.py21
-rw-r--r--mesonbuild/interpreter/__init__.py4
-rw-r--r--mesonbuild/interpreter/interpreter.py5
-rw-r--r--mesonbuild/interpreter/interpreterobjects.py11
-rw-r--r--mesonbuild/modules/python.py6
-rw-r--r--mesonbuild/modules/rust.py5
-rw-r--r--mesonbuild/programs.py29
7 files changed, 39 insertions, 42 deletions
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index 3fee807d4..e1d7e7fbe 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -2255,10 +2255,6 @@ 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"
@@ -2281,21 +2277,6 @@ 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
@@ -3353,7 +3334,7 @@ class ConfigurationData(HoldableObject):
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 e2ccce479..5186b842b 100644
--- a/mesonbuild/interpreter/__init__.py
+++ b/mesonbuild/interpreter/__init__.py
@@ -20,7 +20,6 @@ __all__ = [
'SubprojectHolder',
'DependencyHolder',
'GeneratedListHolder',
- 'ExternalProgramHolder',
'extract_required_kwarg',
'ArrayHolder',
@@ -35,8 +34,7 @@ from .compiler import CompilerHolder
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 552e1331b..7cf9ecc9c 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
+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
@@ -440,7 +440,6 @@ class Interpreter(InterpreterBase, HoldableObject):
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,
@@ -470,7 +469,7 @@ class Interpreter(InterpreterBase, HoldableObject):
'''
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 431086669..addc7d3db 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
+from ..programs import ExternalProgram, BaseProgram
from ..mesonlib import HoldableObject, listify, Popen_safe
import typing as T
@@ -605,9 +605,9 @@ 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=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)
@@ -656,11 +656,6 @@ class _BaseProgramHolder(ObjectHolder[_BASEPROG]):
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 3ec446267..c637e5f5e 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 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 @@ _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(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 3d0ad771e..2c56a860e 100644
--- a/mesonbuild/modules/rust.py
+++ b/mesonbuild/modules/rust.py
@@ -376,10 +376,7 @@ 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:
- # 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 <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 80acb98c8..16c12c85a 100644
--- a/mesonbuild/programs.py
+++ b/mesonbuild/programs.py
@@ -13,6 +13,7 @@ import sys
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 @@ if T.TYPE_CHECKING:
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