diff --git a/mesonbuild/dependencies/python.py b/mesonbuild/dependencies/python.py index 0e40d481faf5..3b02bad663f9 100644 --- a/mesonbuild/dependencies/python.py +++ b/mesonbuild/dependencies/python.py @@ -15,9 +15,10 @@ from .framework import ExtraFrameworkDependency from .pkgconfig import PkgConfigDependency from ..envconfig import detect_cpu_family -from ..mesonlib import MachineChoice +from ..mesonlib import MachineChoice, path_is_in_root from ..programs import ExternalProgram from ..options import OptionKey +from ..scripts import destdir_join if T.TYPE_CHECKING: from typing_extensions import Final, TypedDict @@ -302,8 +303,9 @@ def find_libpy(self, environment: 'Environment') -> None: path = self.build_config['libpython'].get('dynamic') if not path: raise DependencyException('Python does not provide a dynamic libpython library') - sysroot = environment.properties[self.for_machine].get_sys_root() or '' - path = sysroot + path + sysroot = environment.properties[self.for_machine].get_sys_root() + if sysroot and not path_is_in_root(Path(path), Path(sysroot)): + path = destdir_join(sysroot, path) if not os.path.isfile(path): raise DependencyException('Python dynamic library does not exist or is not a file') self.link_args = [path] @@ -356,8 +358,11 @@ def get_windows_link_args(self, limited_api: bool, environment: 'Environment') - key = 'dynamic-stableabi' else: key = 'dynamic' - sysroot = environment.properties[self.for_machine].get_sys_root() or '' - return [sysroot + self.build_config['libpython'][key]] + sysroot = environment.properties[self.for_machine].get_sys_root() + path = self.build_config['libpython'][key] + if sysroot and not path_is_in_root(Path(path), Path(sysroot)): + path = destdir_join(sysroot, path) + return [path] if self.platform.startswith('win'): vernum = self.variables.get('py_version_nodot') @@ -479,8 +484,9 @@ def __init__(self, name: str, environment: Environment, kwargs: DependencyObject return for_machine = kwargs['native'] - sysroot = environment.properties[for_machine].get_sys_root() or '' - pkg_libdir = sysroot + pkg_libdir + sysroot = environment.properties[for_machine].get_sys_root() + if sysroot and not path_is_in_root(Path(pkg_libdir), Path(sysroot)): + pkg_libdir = destdir_join(sysroot, pkg_libdir) mlog.debug(f'Searching for {pkg_libdir!r} via pkgconfig lookup in {pkg_libdir_origin}') pkgconfig_paths = [pkg_libdir] if pkg_libdir else [] @@ -541,8 +547,11 @@ def __init__(self, name: str, environment: 'Environment', # compile args if self.build_config: - sysroot = environment.properties[self.for_machine].get_sys_root() or '' - inc_paths = mesonlib.OrderedSet([sysroot + self.build_config['c_api']['headers']]) + sysroot = environment.properties[self.for_machine].get_sys_root() + path = self.build_config['c_api']['headers'] + if sysroot and not path_is_in_root(Path(path), Path(sysroot)): + path = destdir_join(sysroot, path) + inc_paths = mesonlib.OrderedSet([path]) else: inc_paths = mesonlib.OrderedSet([ self.variables.get('INCLUDEPY'), diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 945ee8a835a2..67c4f564fc36 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -3101,7 +3101,7 @@ def test_python_build_config_extensions(self): python_build_config = { 'schema_version': '1.0', 'base_interpreter': sys.executable, - 'base_prefix': '/usr', + 'base_prefix': sys.base_prefix, 'platform': sysconfig.get_platform(), 'language': { 'version': sysconfig.get_python_version(), @@ -3168,37 +3168,54 @@ def test_python_build_config_extensions(self): if with_pkgconfig: libpc = sysconfig.get_config_var('LIBPC') if libpc is None: - continue + raise SkipTest('pkg-config subtest skipped because of no LIBPC') python_build_config['c_api']['pkgconfig_path'] = libpc # Old Ubuntu versions have incorrect LIBDIR, skip testing non-pkgconfig variant there. elif not os.path.exists(python_build_config['libpython']['dynamic']): - continue + raise SkipTest('non-pkgconfig subtest skipped because of wrong LIBDIR') with tempfile.NamedTemporaryFile(mode='w', delete=False, encoding='utf-8') as python_build_config_file: json.dump(python_build_config, fp=python_build_config_file) - with tempfile.NamedTemporaryFile(mode='w', delete=False, encoding='utf-8') as cross_file: - cross_file.write( - textwrap.dedent(f''' - [binaries] - pkg-config = 'pkg-config' - - [built-in options] - python.build_config = '{python_build_config_file.name}' - '''.strip()) - ) - cross_file.flush() - - for extra_args in ( - ['--python.build-config', python_build_config_file.name], - ['--cross-file', cross_file.name], - ): - with self.subTest(extra_args=extra_args): - self.init(testdir, extra_args=extra_args) - self.build() - with open(intro_installed_file) as f: - intro_installed = json.load(f) - self.assertEqual(sorted(expected_files), sorted(intro_installed)) - self.wipe() + for build_config_via_cross in (False, True): + for sys_root in (None, sys.base_prefix): + with self.subTest(build_config_via_cross=build_config_via_cross, sys_root=sys_root): + # fd.o pkg-config does not handle sys_root correctly + if sys_root is not None and with_pkgconfig and shutil.which('pkgconf') is None: + raise SkipTest('sys_root subtest skipped because of fd.o pkg-config') + + with tempfile.NamedTemporaryFile(mode='w', delete=False, encoding='utf-8') as cross_file: + cross_file.write( + textwrap.dedent(f''' + [binaries] + pkg-config = 'pkg-config' + ''') + ) + if build_config_via_cross: + cross_file.write( + textwrap.dedent(f''' + [built-in options] + python.build_config = '{python_build_config_file.name}' + ''') + ) + if sys_root is not None: + cross_file.write( + textwrap.dedent(f''' + [properties] + sys_root = '{sys_root}' + ''') + ) + cross_file.flush() + + extra_args = ['--cross-file', cross_file.name] + if not build_config_via_cross: + extra_args += ['--python.build-config', python_build_config_file.name] + + self.init(testdir, extra_args=extra_args) + self.build() + with open(intro_installed_file) as f: + intro_installed = json.load(f) + self.assertEqual(sorted(expected_files), sorted(intro_installed)) + self.wipe() def __reconfigure(self): # Set an older version to force a reconfigure from scratch