Skip to content

Commit ff461c8

Browse files
committed
Add ability to repair wheels for other architectures
1 parent f3025f2 commit ff461c8

File tree

8 files changed

+208
-102
lines changed

8 files changed

+208
-102
lines changed

Diff for: src/auditwheel/elfutils.py

+36
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
from typing import Iterator
66

77
from elftools.common.exceptions import ELFError
8+
from elftools.elf.dynamic import DynamicSegment
89
from elftools.elf.elffile import ELFFile
910

1011
from .lddtree import parse_ld_paths
12+
from .libc import Libc
1113

1214

1315
def elf_read_dt_needed(fn: str) -> list[str]:
@@ -161,3 +163,37 @@ def filter_undefined_symbols(
161163
if intersection:
162164
result[lib] = sorted(intersection)
163165
return result
166+
167+
168+
def elf_get_platform_info(path: str) -> tuple[Libc | None, str | None]:
169+
with open(path, "rb") as f:
170+
try:
171+
elf = ELFFile(f)
172+
except ELFError:
173+
return (None, None)
174+
arch = {
175+
"x64": "x86_64",
176+
"x86": "i686",
177+
"AArch64": "aarch64",
178+
"64-bit PowerPC": "ppc64",
179+
"IBM S/390": "s390x",
180+
"ARM": "armv7l",
181+
"RISC-V": "riscv64",
182+
}[elf.get_machine_arch()]
183+
if arch == "ppc64" and elf.header.e_ident.EI_DATA == "ELFDATA2LSB":
184+
arch = "ppc64le"
185+
186+
libc = None
187+
for seg in elf.iter_segments():
188+
if not isinstance(seg, DynamicSegment):
189+
continue
190+
for tag in seg.iter_tags():
191+
if tag.entry.d_tag == "DT_NEEDED":
192+
if tag.needed == "libc.so.6":
193+
libc = Libc.GLIBC
194+
break
195+
if tag.needed.startswith("libc.musl-"):
196+
libc = Libc.MUSL
197+
break
198+
break
199+
return (libc, arch)

Diff for: src/auditwheel/libc.py

+2-6
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import logging
44
from enum import IntEnum
55

6-
from .error import InvalidLibc
76
from .musllinux import find_musl_libc
87

98
logger = logging.getLogger(__name__)
@@ -15,10 +14,7 @@ class Libc(IntEnum):
1514

1615

1716
def get_libc() -> Libc:
18-
try:
19-
find_musl_libc()
17+
if find_musl_libc() is not None:
2018
logger.debug("Detected musl libc")
2119
return Libc.MUSL
22-
except InvalidLibc:
23-
logger.debug("Falling back to GNU libc")
24-
return Libc.GLIBC
20+
return Libc.GLIBC

Diff for: src/auditwheel/main_repair.py

+22-6
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,19 @@ def configure_parser(sub_parsers):
4343
p.add_argument(
4444
"--plat",
4545
action=EnvironmentDefault,
46+
required=False,
4647
metavar="PLATFORM",
4748
env="AUDITWHEEL_PLAT",
4849
dest="PLAT",
4950
help="Desired target platform. See the available platforms under the "
50-
f'PLATFORMS section below. (default: "{highest_policy}")',
51+
f'PLATFORMS section below. (default on current arch: "{highest_policy}")',
5152
choices=policy_names,
52-
default=highest_policy,
53+
)
54+
p.add_argument(
55+
"--best-plat",
56+
action="store_true",
57+
dest="BEST_PLAT",
58+
help="Automatically determine the best target platform.",
5359
)
5460
p.add_argument(
5561
"-L",
@@ -115,26 +121,36 @@ def execute(args, p):
115121
for wheel_file in args.WHEEL_FILE:
116122
if not isfile(wheel_file):
117123
p.error("cannot access %s. No such file" % wheel_file)
124+
wheel_policy.set_platform_from_wheel(wheel_file)
118125

119126
logger.info("Repairing %s", basename(wheel_file))
120127

121-
if not exists(args.WHEEL_DIR):
122-
os.makedirs(args.WHEEL_DIR)
123-
124128
try:
125129
wheel_abi = analyze_wheel_abi(wheel_policy, wheel_file, exclude)
126130
except NonPlatformWheel:
127131
logger.info(NonPlatformWheel.LOG_MESSAGE)
128132
return 1
129133

134+
if args.BEST_PLAT:
135+
if args.PLAT:
136+
p.error("Cannot specify both --best-plat and --plat")
137+
args.PLAT = wheel_abi.overall_tag
138+
139+
if not exists(args.WHEEL_DIR):
140+
os.makedirs(args.WHEEL_DIR)
141+
142+
highest_policy = wheel_policy.get_policy_name(wheel_policy.priority_highest)
143+
if args.PLAT is None:
144+
args.PLAT = highest_policy
130145
policy = wheel_policy.get_policy_by_name(args.PLAT)
131146
reqd_tag = policy["priority"]
132147

133148
if reqd_tag > wheel_policy.get_priority_by_name(wheel_abi.sym_tag):
134149
msg = (
135150
'cannot repair "%s" to "%s" ABI because of the presence '
136151
"of too-recent versioned symbols. You'll need to compile "
137-
"the wheel on an older toolchain." % (wheel_file, args.PLAT)
152+
"the wheel on an older toolchain or pick a newer platform."
153+
% (wheel_file, args.PLAT)
138154
)
139155
p.error(msg)
140156

Diff for: src/auditwheel/main_show.py

+2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ def execute(args, p):
3434
if not isfile(args.WHEEL_FILE):
3535
p.error("cannot access %s. No such file" % args.WHEEL_FILE)
3636

37+
wheel_policy.set_platform_from_wheel(args.WHEEL_FILE)
38+
3739
try:
3840
winfo = analyze_wheel_abi(wheel_policy, args.WHEEL_FILE, frozenset())
3941
except NonPlatformWheel:

Diff for: src/auditwheel/musllinux.py

+18-23
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,10 @@
33
import logging
44
import pathlib
55
import re
6-
import subprocess
76
from typing import NamedTuple
87

9-
from auditwheel.error import InvalidLibc
10-
118
LOG = logging.getLogger(__name__)
9+
VERSION_RE = re.compile(b"[^.](?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)\0")
1210

1311

1412
class MuslVersion(NamedTuple):
@@ -17,31 +15,28 @@ class MuslVersion(NamedTuple):
1715
patch: int
1816

1917

20-
def find_musl_libc() -> pathlib.Path:
18+
def find_musl_libc(library_path: str | None = None) -> pathlib.Path | None:
2119
try:
22-
(dl_path,) = list(pathlib.Path("/lib").glob("libc.musl-*.so.1"))
20+
(dl_path,) = list(pathlib.Path(library_path or "/lib").glob("libc.musl-*.so.1"))
2321
except ValueError:
24-
LOG.debug("musl libc not detected")
25-
raise InvalidLibc
22+
return None
2623

2724
return dl_path
2825

2926

30-
def get_musl_version(ld_path: pathlib.Path) -> MuslVersion:
27+
def get_musl_version(ld_path: pathlib.Path) -> MuslVersion | None:
3128
try:
32-
ld = subprocess.run(
33-
[ld_path], check=False, errors="strict", stderr=subprocess.PIPE
34-
).stderr
29+
with open(ld_path, "rb") as fp:
30+
text = fp.read()
3531
except FileNotFoundError:
36-
LOG.error("Failed to determine musl version", exc_info=True)
37-
raise InvalidLibc
38-
39-
match = re.search(
40-
r"Version " r"(?P<major>\d+)." r"(?P<minor>\d+)." r"(?P<patch>\d+)", ld
41-
)
42-
if not match:
43-
raise InvalidLibc
44-
45-
return MuslVersion(
46-
int(match.group("major")), int(match.group("minor")), int(match.group("patch"))
47-
)
32+
return None
33+
34+
for match in VERSION_RE.finditer(text):
35+
return MuslVersion(
36+
int(match.group("major")),
37+
int(match.group("minor")),
38+
int(match.group("patch")),
39+
)
40+
41+
LOG.error("Failed to determine musl version", exc_info=True)
42+
return None

0 commit comments

Comments
 (0)