Skip to content

Commit

Permalink
Add ability to repair wheels for other architectures
Browse files Browse the repository at this point in the history
  • Loading branch information
rhelmot committed Aug 26, 2024
1 parent f3025f2 commit d0ea4fb
Show file tree
Hide file tree
Showing 8 changed files with 208 additions and 102 deletions.
36 changes: 36 additions & 0 deletions src/auditwheel/elfutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
from typing import Iterator

from elftools.common.exceptions import ELFError
from elftools.elf.dynamic import DynamicSegment
from elftools.elf.elffile import ELFFile

from .lddtree import parse_ld_paths
from .libc import Libc


def elf_read_dt_needed(fn: str) -> list[str]:
Expand Down Expand Up @@ -161,3 +163,37 @@ def filter_undefined_symbols(
if intersection:
result[lib] = sorted(intersection)
return result


def elf_get_platform_info(path: str) -> tuple[Libc | None, str | None]:
with open(path, "rb") as f:
try:
elf = ELFFile(f)
except ELFError:
return (None, None)
arch = {
"x64": "x86_64",
"x86": "i686",
"AArch64": "aarch64",
"64-bit PowerPC": "ppc64",
"IBM S/390": "s390x",
"ARM": "armv7l",
"RISC-V": "riscv64",
}[elf.get_machine_arch()]
if arch == "ppc64" and elf.header.e_ident.EI_DATA == "ELFDATA2LSB":
arch = "ppc64le"

libc = None
for seg in elf.iter_segments():
if not isinstance(seg, DynamicSegment):
continue
for tag in seg.iter_tags():
if tag.entry.d_tag == "DT_NEEDED":
if tag.needed == "libc.so.6":
libc = Libc.GLIBC
break
if tag.needed.startswith("libc.musl-"):
libc = Libc.MUSL
break
break
return (libc, arch)
8 changes: 2 additions & 6 deletions src/auditwheel/libc.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import logging
from enum import IntEnum

from .error import InvalidLibc
from .musllinux import find_musl_libc

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


def get_libc() -> Libc:
try:
find_musl_libc()
if find_musl_libc() is not None:
logger.debug("Detected musl libc")
return Libc.MUSL
except InvalidLibc:
logger.debug("Falling back to GNU libc")
return Libc.GLIBC
return Libc.GLIBC
28 changes: 22 additions & 6 deletions src/auditwheel/main_repair.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,19 @@ def configure_parser(sub_parsers):
p.add_argument(
"--plat",
action=EnvironmentDefault,
required=False,
metavar="PLATFORM",
env="AUDITWHEEL_PLAT",
dest="PLAT",
help="Desired target platform. See the available platforms under the "
f'PLATFORMS section below. (default: "{highest_policy}")',
f'PLATFORMS section below. (default on current arch: "{highest_policy}")',
choices=policy_names,
default=highest_policy,
)
p.add_argument(
"--best-plat",
action="store_true",
dest="BEST_PLAT",
help="Automatically determine the best target platform.",
)
p.add_argument(
"-L",
Expand Down Expand Up @@ -115,26 +121,36 @@ def execute(args, p):
for wheel_file in args.WHEEL_FILE:
if not isfile(wheel_file):
p.error("cannot access %s. No such file" % wheel_file)
wheel_policy.set_platform_from_wheel(wheel_file)

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

if not exists(args.WHEEL_DIR):
os.makedirs(args.WHEEL_DIR)

try:
wheel_abi = analyze_wheel_abi(wheel_policy, wheel_file, exclude)
except NonPlatformWheel:
logger.info(NonPlatformWheel.LOG_MESSAGE)
return 1

if args.BEST_PLAT:
if args.PLAT:
p.error("Cannot specify both --best-plat and --plat")
args.PLAT = wheel_abi.overall_tag

if not exists(args.WHEEL_DIR):
os.makedirs(args.WHEEL_DIR)

highest_policy = wheel_policy.get_policy_name(wheel_policy.priority_highest)
if args.PLAT is None:
args.PLAT = highest_policy
policy = wheel_policy.get_policy_by_name(args.PLAT)
reqd_tag = policy["priority"]

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

Expand Down
2 changes: 2 additions & 0 deletions src/auditwheel/main_show.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ def execute(args, p):
if not isfile(args.WHEEL_FILE):
p.error("cannot access %s. No such file" % args.WHEEL_FILE)

wheel_policy.set_platform_from_wheel(args.WHEEL_FILE)

try:
winfo = analyze_wheel_abi(wheel_policy, args.WHEEL_FILE, frozenset())
except NonPlatformWheel:
Expand Down
41 changes: 18 additions & 23 deletions src/auditwheel/musllinux.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@
import logging
import pathlib
import re
import subprocess
from typing import NamedTuple

from auditwheel.error import InvalidLibc

LOG = logging.getLogger(__name__)
VERSION_RE = re.compile(b"[^.](?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)\0")


class MuslVersion(NamedTuple):
Expand All @@ -17,31 +15,28 @@ class MuslVersion(NamedTuple):
patch: int


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

return dl_path


def get_musl_version(ld_path: pathlib.Path) -> MuslVersion:
def get_musl_version(ld_path: pathlib.Path) -> MuslVersion | None:
try:
ld = subprocess.run(
[ld_path], check=False, errors="strict", stderr=subprocess.PIPE
).stderr
with open(ld_path, "rb") as fp:
text = fp.read()
except FileNotFoundError:
LOG.error("Failed to determine musl version", exc_info=True)
raise InvalidLibc

match = re.search(
r"Version " r"(?P<major>\d+)." r"(?P<minor>\d+)." r"(?P<patch>\d+)", ld
)
if not match:
raise InvalidLibc

return MuslVersion(
int(match.group("major")), int(match.group("minor")), int(match.group("patch"))
)
return None

for match in VERSION_RE.finditer(text):
return MuslVersion(
int(match.group("major")),
int(match.group("minor")),
int(match.group("patch")),
)

LOG.error("Failed to determine musl version", exc_info=True)
return None
Loading

0 comments on commit d0ea4fb

Please sign in to comment.