diff --git a/mesonbuild/dependencies/pkgconfig.py b/mesonbuild/dependencies/pkgconfig.py index 4fea86c12a33..f8d57a6d18b3 100644 --- a/mesonbuild/dependencies/pkgconfig.py +++ b/mesonbuild/dependencies/pkgconfig.py @@ -6,7 +6,8 @@ from pathlib import Path from .base import ExternalDependency, DependencyException, sort_libpaths, DependencyTypeName -from ..mesonlib import EnvironmentVariables, OrderedSet, PerMachine, Popen_safe, Popen_safe_logged, MachineChoice, join_args +from ..mesonlib import (EnvironmentVariables, OrderedSet, PerMachine, Popen_safe, Popen_safe_logged, MachineChoice, + join_args, MesonException) from ..options import OptionKey from ..programs import find_external_program, ExternalProgram from .. import mlog @@ -30,6 +31,14 @@ class PkgConfigInterface: class_impl: PerMachine[T.Union[Literal[False], T.Optional[PkgConfigInterface]]] = PerMachine(False, False) class_cli_impl: PerMachine[T.Union[Literal[False], T.Optional[PkgConfigCLI]]] = PerMachine(False, False) + pkg_bin_per_machine: PerMachine[T.Optional[ExternalProgram]] = PerMachine(None, None) + + @staticmethod + def set_program_override(pkg_bin: ExternalProgram, for_machine: MachineChoice) -> None: + if PkgConfigInterface.class_impl[for_machine]: + raise MesonException(f'Tried to override pkg-config for machine {for_machine} but it was already initialized.\n' + 'pkg-config must be overridden before it\'s used.') + PkgConfigInterface.pkg_bin_per_machine[for_machine] = pkg_bin @staticmethod def instance(env: Environment, for_machine: MachineChoice, silent: bool) -> T.Optional[PkgConfigInterface]: @@ -37,7 +46,7 @@ def instance(env: Environment, for_machine: MachineChoice, silent: bool) -> T.Op for_machine = for_machine if env.is_cross_build() else MachineChoice.HOST impl = PkgConfigInterface.class_impl[for_machine] if impl is False: - impl = PkgConfigCLI(env, for_machine, silent) + impl = PkgConfigCLI(env, for_machine, silent, PkgConfigInterface.pkg_bin_per_machine[for_machine]) if not impl.found(): impl = None if not impl and not silent: @@ -57,7 +66,7 @@ def _cli(env: Environment, for_machine: MachineChoice, silent: bool = False) -> if impl and not isinstance(impl, PkgConfigCLI): impl = PkgConfigInterface.class_cli_impl[for_machine] if impl is False: - impl = PkgConfigCLI(env, for_machine, silent) + impl = PkgConfigCLI(env, for_machine, silent, PkgConfigInterface.pkg_bin_per_machine[for_machine]) if not impl.found(): impl = None PkgConfigInterface.class_cli_impl[for_machine] = impl @@ -113,9 +122,9 @@ def list_all(self) -> ImmutableListProtocol[str]: class PkgConfigCLI(PkgConfigInterface): '''pkg-config CLI implementation''' - def __init__(self, env: Environment, for_machine: MachineChoice, silent: bool) -> None: + def __init__(self, env: Environment, for_machine: MachineChoice, silent: bool,pkgbin: T.Optional[ExternalProgram] = None) -> None: super().__init__(env, for_machine) - self._detect_pkgbin() + self._detect_pkgbin(pkgbin) if self.pkgbin and not silent: mlog.log('Found pkg-config:', mlog.green('YES'), mlog.bold(f'({self.pkgbin.get_path()})'), mlog.blue(self.pkgbin_version)) @@ -200,14 +209,27 @@ def _split_args(cmd: str) -> T.List[str]: # output using shlex.split rather than mesonlib.split_args return shlex.split(cmd) - def _detect_pkgbin(self) -> None: - for potential_pkgbin in find_external_program( - self.env, self.for_machine, 'pkg-config', 'Pkg-config', - self.env.default_pkgconfig, allow_default_for_cross=False): + def _detect_pkgbin(self, pkgbin: T.Optional[ExternalProgram] = None) -> None: + def validate(potential_pkgbin: ExternalProgram) -> bool: version_if_ok = self._check_pkgconfig(potential_pkgbin) if version_if_ok: self.pkgbin = potential_pkgbin self.pkgbin_version = version_if_ok + return True + return False + + if pkgbin and validate(pkgbin): + return + + for potential_pkgbin in find_external_program( + self.env, + self.for_machine, + "pkg-config", + "Pkg-config", + self.env.default_pkgconfig, + allow_default_for_cross=False, + ): + if validate(potential_pkgbin): return self.pkgbin = None @@ -274,7 +296,8 @@ def _call_pkgbin(self, args: T.List[str], env: T.Optional[EnvironOrDict] = None) class PkgConfigDependency(ExternalDependency): - def __init__(self, name: str, environment: Environment, kwargs: T.Dict[str, T.Any], language: T.Optional[str] = None) -> None: + def __init__(self, name: str, environment: Environment, kwargs: T.Dict[str, T.Any], + language: T.Optional[str] = None) -> None: super().__init__(DependencyTypeName('pkgconfig'), environment, kwargs, language=language) self.name = name self.is_libtool = False diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index aa839da361f4..46a79d6c9a9d 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1638,6 +1638,9 @@ def add_find_program_override(self, name: str, exe: T.Union[build.Executable, Ex if name in self.build.find_overrides: raise InterpreterException(f'Tried to override executable "{name}" which has already been overridden.') self.build.find_overrides[name] = exe + if name == 'pkg-config' and isinstance(exe, ExternalProgram): + from ..dependencies.pkgconfig import PkgConfigInterface + PkgConfigInterface.set_program_override(exe, MachineChoice.HOST) def notfound_program(self, args: T.List[mesonlib.FileOrString]) -> ExternalProgram: return NonExistingExternalProgram(' '.join( diff --git a/run_project_tests.py b/run_project_tests.py index 831b947ccca4..9f1e1a5958e3 100755 --- a/run_project_tests.py +++ b/run_project_tests.py @@ -551,9 +551,14 @@ def validate_output(test: TestDef, stdo: str, stde: str) -> str: def clear_internal_caches() -> None: import mesonbuild.interpreterbase from mesonbuild.dependencies.cmake import CMakeDependency + from mesonbuild.dependencies.pkgconfig import PkgConfigInterface from mesonbuild.mesonlib import PerMachine mesonbuild.interpreterbase.FeatureNew.feature_registry = {} CMakeDependency.class_cmakeinfo = PerMachine(None, None) + PkgConfigInterface.class_impl = PerMachine(False, False) + PkgConfigInterface.class_cli_impl = PerMachine(False, False) + PkgConfigInterface.pkg_bin_per_machine = PerMachine(None, None) + def run_test_inprocess(testdir: str) -> T.Tuple[int, str, str, str]: old_stdout = sys.stdout diff --git a/test cases/common/279 pkgconfig override/meson.build b/test cases/common/279 pkgconfig override/meson.build new file mode 100644 index 000000000000..90298288bda1 --- /dev/null +++ b/test cases/common/279 pkgconfig override/meson.build @@ -0,0 +1,8 @@ +project('override pkg-config', 'c') + +subproject('pkg-config') + +pkgconfig = find_program('pkg-config') + +# This dependency can only be found if pkg-config is overridden with our custom pkg-config.py +gobj = dependency('test-package-0.0', version : '= 0.0.0') diff --git a/test cases/common/279 pkgconfig override/subprojects/pkg-config.wrap b/test cases/common/279 pkgconfig override/subprojects/pkg-config.wrap new file mode 100644 index 000000000000..ef7a52ed2037 --- /dev/null +++ b/test cases/common/279 pkgconfig override/subprojects/pkg-config.wrap @@ -0,0 +1,5 @@ +[wrap-file] +directory = pkg-config + +[provide] +program_names = pkg-config diff --git a/test cases/common/279 pkgconfig override/subprojects/pkg-config/bin/pkg-config.py b/test cases/common/279 pkgconfig override/subprojects/pkg-config/bin/pkg-config.py new file mode 100755 index 000000000000..7dc28c849415 --- /dev/null +++ b/test cases/common/279 pkgconfig override/subprojects/pkg-config/bin/pkg-config.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python3 + +import sys + +if len(sys.argv) > 1: + if sys.argv[1] == "--modversion": + if sys.argv[2] == "test-package-0.0": + print("0.0.0") + else: + exit(-1) + elif sys.argv[1] == "--version": + print("0.0.0") + exit(0) diff --git a/test cases/common/279 pkgconfig override/subprojects/pkg-config/meson.build b/test cases/common/279 pkgconfig override/subprojects/pkg-config/meson.build new file mode 100644 index 000000000000..af526f9345a9 --- /dev/null +++ b/test cases/common/279 pkgconfig override/subprojects/pkg-config/meson.build @@ -0,0 +1,4 @@ +project('pkg-config') + +pkgconfig = find_program(meson.project_source_root() / 'bin' / 'pkg-config.py') +meson.override_find_program('pkg-config', pkgconfig)