Skip to content

Commit 9a15dc2

Browse files
committed
Use BuildDetails for the native build case as well
Signed-off-by: Michał Górny <mgorny@quansight.com>
1 parent 08a6811 commit 9a15dc2

3 files changed

Lines changed: 106 additions & 74 deletions

File tree

mesonpy/__init__.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ class _WheelBuilder():
340340
_manifest: Dict[str, List[_Entry]]
341341
_limited_api: bool
342342
_allow_windows_shared_libs: bool
343-
_build_details: mesonpy._tags.BuildDetails | None
343+
_build_details: mesonpy._tags.BuildDetails
344344

345345
@property
346346
def _has_internal_libs(self) -> bool:
@@ -835,7 +835,7 @@ def __init__(
835835
''')
836836
self._meson_native_file.write_text(native_file_data, encoding='utf-8')
837837

838-
# Starting with version 1.10, Meson can consume a `build-detail.json`
838+
# Starting with version 1.10, Meson can consume a `build-details.json`
839839
# file following the specification is PEP 739 to obtain required
840840
# information to build extension modules without having to run the
841841
# interpreter. The path to the `build-details.json` can be specified
@@ -852,6 +852,8 @@ def __init__(
852852
with open(value, 'r', encoding='utf8') as f:
853853
self._build_details = json.load(f)
854854
break
855+
if self._build_details is None:
856+
self._build_details = mesonpy._tags.get_build_details_for_system()
855857

856858
# reconfigure if we have a valid Meson build directory. Meson
857859
# uses the presence of the 'meson-private/coredata.dat' file
@@ -1344,7 +1346,8 @@ def build_editable(
13441346
if not config_settings:
13451347
config_settings = {}
13461348
if 'build-dir' not in config_settings and 'builddir' not in config_settings:
1347-
config_settings['build-dir'] = 'build/' + mesonpy._tags.get_abi_tag()
1349+
# TODO: account for build-details.json for the abi tag?
1350+
config_settings['build-dir'] = 'build/' + mesonpy._tags.get_abi_tag(mesonpy._tags.get_build_details_for_system())
13481351

13491352
out = pathlib.Path(wheel_directory)
13501353
with _project(config_settings) as project:

mesonpy/_tags.py

Lines changed: 95 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -45,31 +45,99 @@ class BuildDetails(TypedDict):
4545
_32_BIT_INTERPRETER = struct.calcsize('P') == 4
4646

4747

48-
def get_interpreter_tag(build_details: BuildDetails | None = None) -> str:
49-
if build_details is None:
50-
name = sys.implementation.name
51-
major, minor = sys.version_info[:2]
52-
else:
53-
name = build_details['implementation']['name']
54-
_v = build_details['implementation']['version']
55-
major = _v['major']
56-
minor = _v['minor']
48+
def _get_macosx_platform() -> str:
49+
ver, _, arch = platform.mac_ver()
50+
major, minor = map(int, ver.split('.')[:2])
51+
52+
# Python built with older macOS SDK on macOS 11, reports an
53+
# nonexistent macOS 10.16 version instead of the real version.
54+
#
55+
# The packaging module introduced a workaround
56+
# https://github.com/pypa/packaging/commit/67c4a2820c549070bbfc4bfbf5e2a250075048da
57+
#
58+
# This results in packaging versions up to 21.3 generating
59+
# platform tags like "macosx_10_16_x86_64" and later versions
60+
# generating "macosx_11_0_x86_64". Using the latter would be more
61+
# correct but prevents the resulting wheel from being installed on
62+
# systems using packaging 21.3 or earlier (pip 22.3 or earlier).
63+
#
64+
# Fortunately packaging versions carrying the workaround still
65+
# accepts "macosx_10_16_x86_64" as a compatible platform tag. We
66+
# can therefore ignore the issue and generate the slightly
67+
# incorrect tag.
68+
69+
if _32_BIT_INTERPRETER:
70+
# 32-bit Python running on a 64-bit kernel.
71+
if arch == 'ppc64':
72+
arch = 'ppc'
73+
if arch == 'x86_64':
74+
arch = 'i386'
75+
76+
return f'macosx-{major}.{minor}-{arch}'
77+
78+
79+
def _get_ios_platform() -> str:
80+
ver = platform.ios_ver().release
81+
major, minor = map(int, ver.split('.')[:2])
82+
83+
# Although _multiarch is an internal implementation detail, it's a core part
84+
# of how CPython is implemented on iOS; this attribute is also relied upon
85+
# by `packaging` as part of tag determination.
86+
multiarch = sys.implementation._multiarch.replace('-', '_')
87+
88+
return f'ios-{major}.{minor}-{multiarch}'
89+
90+
91+
def get_build_details_for_system() -> BuildDetails:
92+
platform = sysconfig.get_platform()
93+
if platform.startswith('macosx'):
94+
platform = _get_macosx_platform()
95+
elif platform.startswith('ios'):
96+
platform = _get_ios_platform()
97+
elif _32_BIT_INTERPRETER:
98+
# 32-bit Python running on a 64-bit kernel.
99+
if platform == 'linux-x86_64':
100+
platform = 'linux_i686'
101+
if platform == 'linux-aarch64':
102+
platform = 'linux_armv7l'
103+
104+
return {
105+
'abi': {
106+
# PyPy reports a $SOABI that does not agree with $EXT_SUFFIX.
107+
# Using $EXT_SUFFIX will not break when PyPy will fix this.
108+
# See https://foss.heptapod.net/pypy/pypy/-/issues/3816 and
109+
# https://github.com/pypa/packaging/pull/607.
110+
'extension_suffix': str(sysconfig.get_config_var('EXT_SUFFIX')),
111+
},
112+
'implementation': {
113+
'name': sys.implementation.name,
114+
'version': {
115+
'major': sys.version_info.major,
116+
'minor': sys.version_info.minor,
117+
},
118+
},
119+
'platform': platform,
120+
}
121+
122+
123+
def get_interpreter_tag(build_details: BuildDetails) -> str:
124+
name = build_details['implementation']['name']
125+
_v = build_details['implementation']['version']
126+
major = _v['major']
127+
minor = _v['minor']
57128
name = INTERPRETERS.get(name, name)
58129
return f'{name}{major}{minor}'
59130

60131

61-
def get_abi_tag(build_details: BuildDetails | None = None) -> str:
132+
def get_abi_tag(build_details: BuildDetails) -> str:
62133
# The best solution to obtain the Python ABI is to parse the
63134
# $SOABI or $EXT_SUFFIX sysconfig variables as defined in PEP-314.
64135

65136
# PyPy reports a $SOABI that does not agree with $EXT_SUFFIX.
66137
# Using $EXT_SUFFIX will not break when PyPy will fix this.
67138
# See https://foss.heptapod.net/pypy/pypy/-/issues/3816 and
68139
# https://github.com/pypa/packaging/pull/607.
69-
if build_details is None:
70-
ext_suffix = str(sysconfig.get_config_var('EXT_SUFFIX'))
71-
else:
72-
ext_suffix = build_details['abi']['extension_suffix']
140+
ext_suffix = build_details['abi']['extension_suffix']
73141
empty, abi, ext = ext_suffix.split('.')
74142

75143
# The packaging module initially based his understanding of the
@@ -91,8 +159,8 @@ def get_abi_tag(build_details: BuildDetails | None = None) -> str:
91159
return abi.replace('.', '_').replace('-', '_')
92160

93161

94-
def _get_macosx_platform_tag(build_details: BuildDetails | None = None) -> str:
95-
ver, _, arch = platform.mac_ver()
162+
def _get_macosx_platform_tag(platform: str) -> str:
163+
platform_os, version, arch = platform.split('-', 2)
96164

97165
# Override the architecture with the one provided in the
98166
# _PYTHON_HOST_PLATFORM environment variable. This environment
@@ -112,24 +180,7 @@ def _get_macosx_platform_tag(build_details: BuildDetails | None = None) -> str:
112180
parts = os.environ.get('MACOSX_DEPLOYMENT_TARGET', '').split('.')[:2]
113181
version = tuple(map(int, parts + ['0'] * (2 - len(parts))))
114182
except ValueError:
115-
version = tuple(map(int, ver.split('.')))[:2]
116-
117-
# Python built with older macOS SDK on macOS 11, reports an
118-
# nonexistent macOS 10.16 version instead of the real version.
119-
#
120-
# The packaging module introduced a workaround
121-
# https://github.com/pypa/packaging/commit/67c4a2820c549070bbfc4bfbf5e2a250075048da
122-
#
123-
# This results in packaging versions up to 21.3 generating
124-
# platform tags like "macosx_10_16_x86_64" and later versions
125-
# generating "macosx_11_0_x86_64". Using the latter would be more
126-
# correct but prevents the resulting wheel from being installed on
127-
# systems using packaging 21.3 or earlier (pip 22.3 or earlier).
128-
#
129-
# Fortunately packaging versions carrying the workaround still
130-
# accepts "macosx_10_16_x86_64" as a compatible platform tag. We
131-
# can therefore ignore the issue and generate the slightly
132-
# incorrect tag.
183+
version = tuple(map(int, version.split('.')[:2]))
133184

134185
# The minimum macOS ABI version on arm64 is 11.0. The macOS SDK
135186
# on arm64 silently bumps any compatibility version specified via
@@ -151,59 +202,36 @@ def _get_macosx_platform_tag(build_details: BuildDetails | None = None) -> str:
151202
# the patch level. Reset the patch level to zero.
152203
minor = 0
153204

154-
# When using build-details.json, the platform recorded should be correct
155-
# per the bitness of the interpreter.
156-
if build_details is None and _32_BIT_INTERPRETER:
157-
# 32-bit Python running on a 64-bit kernel.
158-
if arch == 'ppc64':
159-
arch = 'ppc'
160-
if arch == 'x86_64':
161-
arch = 'i386'
205+
return f'{platform_os}_{major}_{minor}_{arch}'
162206

163-
return f'macosx_{major}_{minor}_{arch}'
164207

208+
def _get_ios_platform_tag(platform: str) -> str:
209+
platform_os, version, multiarch = platform.split('-', 2)
165210

166-
def _get_ios_platform_tag() -> str:
167211
# Override the iOS version if one is provided via the
168212
# IPHONEOS_DEPLOYMENT_TARGET environment variable.
169213
try:
170214
parts = os.environ.get('IPHONEOS_DEPLOYMENT_TARGET', '').split('.')[:2]
171-
version = tuple(map(int, parts + ['0'] * (2 - len(parts))))
215+
version = '.'.join(map(int, parts + ['0'] * (2 - len(parts))))
172216
except ValueError:
173-
version = tuple(map(int, platform.ios_ver().release.split('.')))[:2] # type: ignore[attr-defined]
174-
175-
# Although _multiarch is an internal implementation detail, it's a core part
176-
# of how CPython is implemented on iOS; this attribute is also relied upon
177-
# by `packaging` as part of tag determination.
178-
multiarch = sys.implementation._multiarch.replace('-', '_')
217+
pass
179218

180-
return f'ios_{version[0]}_{version[1]}_{multiarch}'
219+
return f'{platform_os}_{version.replace('.', '_')}_{multiarch.replace('-', '_')}'
181220

182221

183222
def get_platform_tag(build_details: BuildDetails | None = None) -> str:
184-
if build_details is None:
185-
platform = sysconfig.get_platform()
186-
else:
187-
platform = build_details['platform']
188-
223+
platform = build_details['platform']
189224
if platform.startswith('macosx'):
190-
return _get_macosx_platform_tag(build_details)
225+
return _get_macosx_platform_tag(platform)
191226
if platform.startswith('ios'):
192-
return _get_ios_platform_tag()
193-
# When using build-details.json, the platform recorded should be correct
194-
# per the bitness of the interpreter.
195-
if build_details is None and _32_BIT_INTERPRETER:
196-
# 32-bit Python running on a 64-bit kernel.
197-
if platform == 'linux-x86_64':
198-
return 'linux_i686'
199-
if platform == 'linux-aarch64':
200-
return 'linux_armv7l'
227+
return _get_ios_platform_tag(platform)
201228
return platform.replace('-', '_').replace('.', '_').lower()
202229

203230

204231
class Tag:
205232
def __init__(self, interpreter: str | None = None, abi: str | None = None, platform: str | None = None,
206233
build_details: BuildDetails | None = None):
234+
assert (interpreter is not None and abi is not None and platform is not None) or build_details is not None
207235
self.interpreter = interpreter or get_interpreter_tag(build_details)
208236
self.abi = abi or get_abi_tag(build_details)
209237
self.platform = platform or get_platform_tag(build_details)

tests/test_tags.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,12 @@ def get_abi3_suffix():
5555

5656
SUFFIX = sysconfig.get_config_var('EXT_SUFFIX')
5757
ABI3SUFFIX = get_abi3_suffix()
58+
SYSTEM_BUILD_DETAILS = mesonpy._tags.get_build_details_for_system()
5859

5960

6061
def test_wheel_tag():
61-
assert str(mesonpy._tags.Tag()) == f'{INTERPRETER}-{ABI}-{PLATFORM}'
62-
assert str(mesonpy._tags.Tag(abi='abi3')) == f'{INTERPRETER}-abi3-{PLATFORM}'
62+
assert str(mesonpy._tags.Tag(build_details=SYSTEM_BUILD_DETAILS)) == f'{INTERPRETER}-{ABI}-{PLATFORM}'
63+
assert str(mesonpy._tags.Tag(abi='abi3', build_details=SYSTEM_BUILD_DETAILS)) == f'{INTERPRETER}-abi3-{PLATFORM}'
6364

6465

6566
@pytest.mark.skipif(sys.platform != 'darwin', reason='macOS specific test')
@@ -120,7 +121,7 @@ def wheel_builder_test_factory(content, pure=True, limited_api=False):
120121
manifest = defaultdict(list)
121122
for key, value in content.items():
122123
manifest[key] = [mesonpy._Entry(pathlib.Path(x), os.path.join('build', x)) for x in value]
123-
return mesonpy._WheelBuilder(None, manifest, limited_api, False, None)
124+
return mesonpy._WheelBuilder(None, manifest, limited_api, False, SYSTEM_BUILD_DETAILS)
124125

125126

126127
def test_tag_empty_wheel():
@@ -171,4 +172,4 @@ def test_build_details():
171172
build_details = json.load(f)
172173
except FileNotFoundError:
173174
return pytest.skip('build-details.json not found')
174-
assert str(mesonpy._tags.Tag()) == str(mesonpy._tags.Tag(build_details=build_details))
175+
assert str(mesonpy._tags.Tag(build_details=SYSTEM_BUILD_DETAILS)) == str(mesonpy._tags.Tag(build_details=build_details))

0 commit comments

Comments
 (0)