Stage npm, ffmpeg and its bionic runtime libraries, and static ARM ripgrep. Bind caches to actual build inputs. Generate entrypoints from the project manifest and verify real CLI/TUI startup and media conversion offline. Keep versions unchanged. Windows service work remains out of scope.
68 lines
2.7 KiB
Python
68 lines
2.7 KiB
Python
"""Exercise the linker repair against a real native extension on Linux."""
|
|
from __future__ import annotations
|
|
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import sysconfig
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from scripts.termux import python_linkage
|
|
|
|
|
|
@pytest.mark.platforms("linux")
|
|
def test_python_symbols_gain_an_explicit_library_dependency(tmp_path):
|
|
import _ctypes
|
|
|
|
library = Path(sysconfig.get_config_var("LIBDIR")) / sysconfig.get_config_var("LDLIBRARY")
|
|
extension = tmp_path / Path(_ctypes.__file__).name
|
|
shutil.copy2(_ctypes.__file__, extension)
|
|
original = subprocess.check_output(["patchelf", "--print-needed", str(extension)], text=True).splitlines()
|
|
for name in original:
|
|
if name.startswith("libpython"):
|
|
subprocess.run(["patchelf", "--remove-needed", name, str(extension)], check=True)
|
|
assert python_linkage.link_extension(extension, library)
|
|
needed = subprocess.check_output(["patchelf", "--print-needed", str(extension)], text=True).splitlines()
|
|
soname = subprocess.check_output(["patchelf", "--print-soname", str(library)], text=True).strip()
|
|
assert soname in needed
|
|
assert not python_linkage.link_extension(extension, library), "a second pass must not edit a correct extension"
|
|
subprocess.run(
|
|
[sys.executable, "-c", "import _ctypes; print(_ctypes.__file__)"],
|
|
cwd=tmp_path, check=True,
|
|
)
|
|
|
|
|
|
def test_wheel_rewrite_regenerates_record_for_changed_member(tmp_path):
|
|
import base64
|
|
import csv
|
|
import hashlib
|
|
import io
|
|
import zipfile
|
|
|
|
wheel = tmp_path / "sample-1.0-cp311-cp311-linux_aarch64.whl"
|
|
record = "sample-1.0.dist-info/RECORD"
|
|
with zipfile.ZipFile(wheel, "w") as archive:
|
|
archive.writestr("sample/_native.so", b"unrepaired native bytes")
|
|
archive.writestr("sample/__init__.py", b"")
|
|
archive.writestr("sample-1.0.dist-info/WHEEL", "Wheel-Version: 1.0\nTag: cp311-cp311-linux_aarch64\n")
|
|
archive.writestr(record, "")
|
|
|
|
def repair(path, library):
|
|
assert library == tmp_path / "libpython.so"
|
|
path.write_bytes(b"repaired native bytes")
|
|
return True
|
|
|
|
python_linkage.repair_wheel(wheel, tmp_path / "libpython.so", repair=repair)
|
|
with zipfile.ZipFile(wheel) as archive:
|
|
assert archive.read("sample/_native.so") == b"repaired native bytes"
|
|
rows = {r[0]: r[1:] for r in csv.reader(io.StringIO(archive.read(record).decode()))}
|
|
for name in archive.namelist():
|
|
if name == record:
|
|
assert rows[name] == ["", ""]
|
|
continue
|
|
data = archive.read(name)
|
|
digest = base64.urlsafe_b64encode(hashlib.sha256(data).digest()).rstrip(b"=").decode()
|
|
assert rows[name] == ["sha256=" + digest, str(len(data))]
|