Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions crossenv/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,10 @@ def get_uname_info(self):
"EXT_SUFFIX"
]

self.sysconfig_extension_suffixes = (
[self.sysconfig_ext_suffix] + self._host_abi3_suffixes() + [".so"]
)

# Normalize case of the host system/sysname.
self.host_system = {
"ios": "iOS",
Expand All @@ -631,6 +635,47 @@ def get_uname_info(self):
"watchos": "Darwin",
}.get(self.host_sys_platform, self.host_sys_platform.title())

def _host_soabi_platform(self):
"""
SOABI_PLATFORM from the host's EXT_SUFFIX, or "" if absent.

EXT_SUFFIX is "." + SOABI + ".so", and SOABI is
"cpython-<ver>[-<PLATFORM_TRIPLET>]". CPython only emits the
platform-tagged abi3 suffixes when that triplet is defined.
"""
ext_suffix = self.sysconfig_ext_suffix
if not ext_suffix.startswith(".") or not ext_suffix.endswith(".so"):
return ""
return "-".join(ext_suffix[1:-3].split("-")[2:])

def _host_abi3_suffixes(self):
"""
Stable-ABI suffixes the HOST interpreter can load.

CPython builds these in Python/dynload_shlib.c. The plain ".abi3"
entries sit inside an "#ifndef Py_GIL_DISABLED" block, and both the
platform-tagged forms and the ".abi3t" forms only exist from 3.15.
"""
free_threaded = self.host_sysconfigdata.build_time_vars.get("Py_GIL_DISABLED")
try:
version_info = tuple(int(p) for p in self.host_version.split("."))
except ValueError:
version_info = ()

if version_info < (3, 15):
# Free-threaded builds had no stable ABI before 3.15.
return [] if free_threaded else [".abi3.so"]

stem = "abi3t" if free_threaded else "abi3"
# Plain form first, deliberately: setuptools get_abi3_suffix() returns
# the first entry containing ".abi3", and the portable name is the one
# we want emitted for a cross build.
suffixes = [".%s.so" % stem]
soabi_platform = self._host_soabi_platform()
if soabi_platform:
suffixes.append(".%s-%s.so" % (stem, soabi_platform))
return suffixes

def expand_platform_tags(self):
"""
Convert legacy manylinux tags to PEP600, because pip only looks for one
Expand Down
2 changes: 1 addition & 1 deletion crossenv/scripts/importlib-machinery-patch.py.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ def _PathFinder_find_spec(cls, fullname, path=None, target=None):

PathFinder.find_spec = _PathFinder_find_spec

EXTENSION_SUFFIXES = [{{repr(self.sysconfig_ext_suffix)}}, ".abi3.so", ".so"]
EXTENSION_SUFFIXES = {{repr(self.sysconfig_extension_suffixes)}}
49 changes: 49 additions & 0 deletions tests/test_environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,3 +294,52 @@ def test_machine_override(tmp_path, host_python, build_python):
)
out = out.strip()
assert out == "foobar foobar"


def test_abi3_extension_suffixes(crossenv):
out = crossenv.check_output(
[
"python",
"-c",
dedent("""\
import sys
import sysconfig
from importlib.machinery import EXTENSION_SUFFIXES
print(sys.version_info.major, sys.version_info.minor)
print(1 if sysconfig.get_config_var("Py_GIL_DISABLED") else 0)
print(" ".join(EXTENSION_SUFFIXES))
"""),
],
universal_newlines=True,
)
version_line, gil_line, suffix_line = out.strip().splitlines()
version_info = tuple(int(part) for part in version_line.split())
free_threaded = gil_line == "1"
suffixes = suffix_line.split()

ext_suffix = suffixes[0]
assert suffixes[-1] == ".so"

abi3 = [suffix for suffix in suffixes if ".abi3" in suffix]

if version_info < (3, 15):
# Free-threaded builds had no stable ABI before 3.15, and the
# platform-tagged forms did not exist yet.
assert abi3 == ([] if free_threaded else [".abi3.so"])
return

# setuptools get_abi3_suffix() returns the first entry containing ".abi3",
# so the untagged form has to come first.
stem = ".abi3t" if free_threaded else ".abi3"
assert abi3[0] == stem + ".so"

# A host with a PLATFORM_TRIPLET offers the tagged form too, so the loop
# below is not vacuous.
if len(ext_suffix[1:-3].split("-")) > 2:
assert len(abi3) == 2

# Any platform-tagged form must carry the host's own tag, not the build
# machine's.
for suffix in abi3[1:]:
tag = suffix.split("-", 1)[1]
assert ext_suffix.endswith("-" + tag)
Loading