Skip to content

Commit

Permalink
pkgconfig: add support to override pkg-config
Browse files Browse the repository at this point in the history
  • Loading branch information
ylatuya authored and nirbheek committed Oct 18, 2024
1 parent 5f0bd8f commit 5cc511b
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 10 deletions.
38 changes: 28 additions & 10 deletions mesonbuild/dependencies/pkgconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -30,14 +31,22 @@ 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]:
'''Return a pkg-config implementation singleton'''
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:
Expand All @@ -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
Expand Down Expand Up @@ -113,9 +122,10 @@ 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))

Expand Down Expand Up @@ -200,14 +210,21 @@ 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

Expand Down Expand Up @@ -274,7 +291,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
Expand Down
3 changes: 3 additions & 0 deletions mesonbuild/interpreter/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -1640,6 +1640,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(
Expand Down
5 changes: 5 additions & 0 deletions run_project_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions test cases/common/279 pkgconfig override/meson.build
Original file line number Diff line number Diff line change
@@ -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')
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[wrap-file]
directory = pkg-config

[provide]
program_names = pkg-config
Original file line number Diff line number Diff line change
@@ -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)
Original file line number Diff line number Diff line change
@@ -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)

0 comments on commit 5cc511b

Please sign in to comment.