From 3a80d893eff6eef11cc144a4fd31851ffd1be73a Mon Sep 17 00:00:00 2001 From: Ralf Gommers Date: Tue, 15 Jul 2025 15:19:39 +0200 Subject: [PATCH] WIP: ENH: implement support for build-details.json (PEP 739) --- mesonpy/__init__.py | 25 ++++++++++++++++++++++--- mesonpy/_tags.py | 32 +++++++++++++++++++++----------- 2 files changed, 43 insertions(+), 14 deletions(-) diff --git a/mesonpy/__init__.py b/mesonpy/__init__.py index d4dd3bd0..c743e8bc 100644 --- a/mesonpy/__init__.py +++ b/mesonpy/__init__.py @@ -312,11 +312,15 @@ def __init__( manifest: Dict[str, List[Tuple[pathlib.Path, str]]], limited_api: bool, allow_windows_shared_libs: bool, + is_cross: bool, + build_details: Optional[Dict[str, str | Dict[str, Any]]] = None, ) -> None: self._metadata = metadata self._manifest = manifest self._limited_api = limited_api self._allow_windows_shared_libs = allow_windows_shared_libs + self._is_cross = is_cross + self._build_details = build_details @property def _has_internal_libs(self) -> bool: @@ -347,8 +351,8 @@ def tag(self) -> mesonpy._tags.Tag: # does not contain any extension module (does not # distribute any file in {platlib}) thus use generic # implementation and ABI tags. - return mesonpy._tags.Tag('py3', 'none', None) - return mesonpy._tags.Tag(None, self._stable_abi, None) + return mesonpy._tags.Tag('py3', 'none', None, self._build_details) + return mesonpy._tags.Tag(None, self._stable_abi, None, self._build_details) @property def name(self) -> str: @@ -757,6 +761,21 @@ def __init__( ''') self._meson_native_file.write_text(native_file_data, encoding='utf-8') + # Handle cross compilation + self._is_cross = any(s.startswith('--cross-file') for s in self._meson_args['setup']) + self._build_details = None + if self._is_cross: + # Use build-details.json (PEP 739) to determine + # platform/interpreter/abi tags, if given. + for setup_arg in self._meson_args['setup']: + if setup_arg.startswith('-Dpython.build_config='): + with open(setup_arg.split('-Dpython.build_config=')[1]) as f: + self._build_details = json.load(f) + break + else: + # TODO: warn that interpreter details may be wrong. Get platform from cross file. + pass + # reconfigure if we have a valid Meson build directory. Meson # uses the presence of the 'meson-private/coredata.dat' file # in the build directory as indication that the build @@ -1067,7 +1086,7 @@ def sdist(self, directory: Path) -> pathlib.Path: def wheel(self, directory: Path) -> pathlib.Path: """Generates a wheel in the specified directory.""" self.build() - builder = _WheelBuilder(self._metadata, self._manifest, self._limited_api, self._allow_windows_shared_libs) + builder = _WheelBuilder(self._metadata, self._manifest, self._limited_api, self._allow_windows_shared_libs, self._is_cross, self._build_details) return builder.build(directory) def editable(self, directory: Path) -> pathlib.Path: diff --git a/mesonpy/_tags.py b/mesonpy/_tags.py index 9c09fe5a..a3a994ef 100644 --- a/mesonpy/_tags.py +++ b/mesonpy/_tags.py @@ -29,10 +29,15 @@ _32_BIT_INTERPRETER = struct.calcsize('P') == 4 -def get_interpreter_tag() -> str: - name = sys.implementation.name +def get_interpreter_tag(build_details: Optional[dict] = None) -> str: + if build_details is None: + name = sys.implementation.name + version = sys.version_info + else: + name = build_details['implementation']['name'] + _v = build_details['implementation']['version'] + version = (_v['major'], _v['minor']) name = INTERPRETERS.get(name, name) - version = sys.version_info return f'{name}{version[0]}{version[1]}' @@ -53,7 +58,12 @@ def _get_cpython_abi() -> str: return f'cp{version[0]}{version[1]}{debug}{pymalloc}' -def get_abi_tag() -> str: +def get_abi_tag(build_details: Optional[dict] = None) -> str: + if build_details is not None: + ext_suffix = build_details['abi']['extension_suffix'] + else: + ext_suffix = sysconfig.get_config_var('EXT_SUFFIX') + # The best solution to obtain the Python ABI is to parse the # $SOABI or $EXT_SUFFIX sysconfig variables as defined in PEP-314. @@ -62,7 +72,7 @@ def get_abi_tag() -> str: # See https://foss.heptapod.net/pypy/pypy/-/issues/3816 and # https://github.com/pypa/packaging/pull/607. try: - empty, abi, ext = str(sysconfig.get_config_var('EXT_SUFFIX')).split('.') + empty, abi, ext = str(ext_suffix).split('.') except ValueError as exc: # CPython <= 3.8.7 on Windows does not implement PEP3149 and # uses '.pyd' as $EXT_SUFFIX, which does not allow to extract @@ -178,8 +188,8 @@ def _get_ios_platform_tag() -> str: return f'ios_{version[0]}_{version[1]}_{multiarch}' -def get_platform_tag() -> str: - platform = sysconfig.get_platform() +def get_platform_tag(build_details: Optional[dict] = None) -> str: + platform = build_details['platform'] if build_details is not None else sysconfig.get_platform() if platform.startswith('macosx'): return _get_macosx_platform_tag() if platform.startswith('ios'): @@ -194,10 +204,10 @@ def get_platform_tag() -> str: class Tag: - def __init__(self, interpreter: Optional[str] = None, abi: Optional[str] = None, platform: Optional[str] = None): - self.interpreter = interpreter or get_interpreter_tag() - self.abi = abi or get_abi_tag() - self.platform = platform or get_platform_tag() + def __init__(self, interpreter: Optional[str] = None, abi: Optional[str] = None, platform: Optional[str] = None, build_details: Optional[dict] = None): + self.interpreter = interpreter or get_interpreter_tag(build_details) + self.abi = abi or get_abi_tag(build_details) + self.platform = platform or get_platform_tag(build_details) def __str__(self) -> str: return f'{self.interpreter}-{self.abi}-{self.platform}'