Skip to content

Commit f5f83dd

Browse files
authored
feat: add GNU_PROPERTY_X86_ISA_1_NEEDED detection (#535)
* chore: use a dataclass for ldd result * feat: add GNU_PROPERTY_X86_ISA_1_NEEDED detection ISA extensions usage is not defined by a PEP yet. This first implementation fails to repair the wheel if the usage of x86-64-v[2-4] is required. The check can be disabled with `--disable-isa-ext-check`. The detection being related to a declaration when building, it will not detect the requirement for binaries where the declaration is missing. All executables built on a manylinux_2_34 image will be detected as x86-64-v2.
1 parent bade7e0 commit f5f83dd

27 files changed

+879
-339
lines changed

scripts/create-arch-wheels.sh

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/bin/sh
2+
3+
# This script is used to create wheels for unsupported architectures
4+
# in order to extend coverage and check errors with those.
5+
6+
set -eux
7+
8+
SCRIPT_DIR="$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd -P)"
9+
INTEGRATION_TEST_DIR="${SCRIPT_DIR}/../tests/integration"
10+
mkdir -p "${INTEGRATION_TEST_DIR}/arch-wheels"
11+
12+
# "mips64le" built with buildpack-deps:bookworm and renamed cp313-cp313
13+
# "386" "amd64" "arm/v5" "arm/v7" "arm64/v8"
14+
for ARCH in "ppc64le" "riscv64" "s390x"; do
15+
docker run --platform linux/${ARCH} -i --rm -v "${INTEGRATION_TEST_DIR}:/tests" debian:trixie-20250203 << "EOF"
16+
# for, "arm/v5" QEMU will report armv7l, running on aarch64 will report aarch64, force armv5l/armv7l
17+
case "$(dpkg --print-architecture)" in
18+
armel) export _PYTHON_HOST_PLATFORM="linux-armv5l";;
19+
armhf) export _PYTHON_HOST_PLATFORM="linux-armv7l";;
20+
*) ;;
21+
esac
22+
DEBIAN_FRONTEND=noninteractive apt-get update
23+
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends gcc python3-pip python3-dev
24+
python3 -m pip wheel --no-deps -w /tests/arch-wheels /tests/testsimple
25+
EOF
26+
27+
done

src/auditwheel/architecture.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
from __future__ import annotations
2+
3+
import functools
4+
import platform
5+
import struct
6+
import sys
7+
from enum import Enum
8+
9+
10+
class Architecture(Enum):
11+
value: str
12+
13+
aarch64 = "aarch64"
14+
armv7l = "armv7l"
15+
i686 = "i686"
16+
loongarch64 = "loongarch64"
17+
ppc64 = "ppc64"
18+
ppc64le = "ppc64le"
19+
riscv64 = "riscv64"
20+
s390x = "s390x"
21+
x86_64 = "x86_64"
22+
x86_64_v2 = "x86_64_v2"
23+
x86_64_v3 = "x86_64_v3"
24+
x86_64_v4 = "x86_64_v4"
25+
26+
def __str__(self):
27+
return self.value
28+
29+
@property
30+
def baseline(self):
31+
if self.value.startswith("x86_64"):
32+
return Architecture.x86_64
33+
return self
34+
35+
@classmethod
36+
@functools.lru_cache(None)
37+
def _member_list(cls) -> list[Architecture]:
38+
return list(cls)
39+
40+
def is_subset(self, other: Architecture) -> bool:
41+
if self.baseline != other.baseline:
42+
return False
43+
member_list = Architecture._member_list()
44+
return member_list.index(self) <= member_list.index(other)
45+
46+
def is_superset(self, other: Architecture) -> bool:
47+
if self.baseline != other.baseline:
48+
return False
49+
return other.is_subset(self)
50+
51+
@staticmethod
52+
def get_native_architecture(*, bits: int | None = None) -> Architecture:
53+
machine = platform.machine()
54+
if sys.platform.startswith("win"):
55+
machine = {"AMD64": "x86_64", "ARM64": "aarch64", "x86": "i686"}.get(
56+
machine, machine
57+
)
58+
elif sys.platform.startswith("darwin"):
59+
machine = {"arm64": "aarch64"}.get(machine, machine)
60+
61+
if bits is None:
62+
# c.f. https://github.com/pypa/packaging/pull/711
63+
bits = 8 * struct.calcsize("P")
64+
65+
if machine in {"x86_64", "i686"}:
66+
machine = {64: "x86_64", 32: "i686"}[bits]
67+
elif machine in {"aarch64", "armv8l"}:
68+
# use armv7l policy for 64-bit arm kernel in 32-bit mode (armv8l)
69+
machine = {64: "aarch64", 32: "armv7l"}[bits]
70+
71+
return Architecture(machine)

src/auditwheel/json.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from __future__ import annotations
2+
3+
import dataclasses
4+
import json
5+
from enum import Enum
6+
from typing import Any
7+
8+
9+
def _encode_value(value: Any) -> Any:
10+
if dataclasses.is_dataclass(value) and not isinstance(value, type):
11+
return dataclasses.asdict(value)
12+
if isinstance(value, frozenset):
13+
return sorted(value)
14+
if isinstance(value, Enum):
15+
return repr(value)
16+
msg = f"object of type {value.__class__.__name__!r} can't be encoded to JSON"
17+
raise TypeError(msg)
18+
19+
20+
def dumps(obj: Any) -> str:
21+
return json.dumps(obj, indent=4, default=_encode_value)

0 commit comments

Comments
 (0)