fix(termux): replace the retired libffi runtime archive

Termux removed libffi 3.5.2 from its rolling package pool. The pinned URL
returns 404 and stops runtime-library staging. Pin the available 3.8.0
archive with its downloaded SHA-256, which matches the package index.

Name the package, version and URL when a download fails. Flush staging
progress so piped build logs preserve the failure order. Update the
bionic Python tests to follow the current supplier and binary layout.

Verified all 83 library/license archives and 265 staged shared libraries.
The complete staging step and its verified cache hit both returned 0.
The pinned Python ctypes extension finds its required libffi symbols.
Focused tests: 19 passed, 2 skipped. Bionic execution remains a CI gate.
This commit is contained in:
ethernet
2026-09-09 00:01:51 -04:00
parent 8b7eae99ef
commit 3f43d634d2
4 changed files with 29 additions and 19 deletions

View File

@@ -142,9 +142,9 @@
"version": "2.8.4"
},
"libffi": {
"sha256": "8c8c1d6ffb049d8496a21c1202d9b4dc9145140886fdbb45716684565f4ed3f5",
"url": "https://packages.termux.dev/apt/termux-main/pool/main/libf/libffi/libffi_3.5.2_aarch64.deb",
"version": "3.5.2"
"sha256": "4f255badf74cd31f6a2801c17fa1444199c84c834b517b7c843e2fe9ebe91d77",
"url": "https://packages.termux.dev/apt/termux-main/pool/main/libf/libffi/libffi_3.8.0_aarch64.deb",
"version": "3.8.0"
},
"libgraphite": {
"sha256": "15ac85737a35150cecb9e46ecb32567ae2a4fc28071cdf58fb95e1b77d7889d9",

View File

@@ -117,8 +117,11 @@ def _ensure_extracted(work: Path, name: str, row: dict) -> Path:
except OSError:
archive_ok = False
if not archive_ok:
Download([Source(row["url"], archive, row["sha256"])],
partials_dir=scratch).run()
try:
Download([Source(row["url"], archive, row["sha256"])],
partials_dir=scratch).run()
except Exception as exc:
raise StageError(f"{name} {row['version']}: download failed from {row['url']}: {exc}") from exc
if extract.exists():
shutil.rmtree(extract)
extract.mkdir(parents=True, exist_ok=True)
@@ -176,7 +179,7 @@ def stage(payload: Path, table: dict, licenses: dict | None = None) -> Path:
target = payload / "runtime-libs/share/doc"
shutil.copytree(notices, target, dirs_exist_ok=True, symlinks=True)
merged += n
print(f" {name} {row['version']}: {n} new .so* -> runtime-libs/lib")
print(f" {name} {row['version']}: {n} new .so* -> runtime-libs/lib", flush=True)
if not any(out.glob("*.so*")):
raise StageError("no shared objects staged")

View File

@@ -64,9 +64,8 @@ def _assert_pinned_bionic_row(lock, pkg, url_suffix_re):
def test_python_bionic_row_matches_supplier(lock):
"""The python bionic row is an explicit pin of the TUR .deb; the row and
Python.fetch_url(bionic arm) must agree on the same pinned artifact."""
_assert_pinned_bionic_row(lock, "python", r"python3\.11_(?P<ver>[0-9.]+)_aarch64\.deb$")
"""The Python package definition and lock must agree on the termux-main artifact."""
_assert_pinned_bionic_row(lock, "python", r"/p/python/python_(?P<ver>[0-9.]+(?:-[0-9]+)?)_aarch64\.deb$")
def test_node_bionic_row_matches_supplier(lock):
"""The node bionic row is an explicit pin of the termux-main nodejs .deb;
the row and Nodejs.fetch_url(bionic arm) must agree."""
@@ -79,18 +78,13 @@ def test_termux_docker_row_pins_digest(lock):
assert row["url"] == f"docker://termux/termux-docker@{version}"
def test_bionic_fetch_urls_resolve():
def test_python_bionic_pin_is_independent_of_desktop_build_version(lock):
from pm.registry import get_package
py = get_package("python")
version = "3.11.15+20260807"
assert py.fetch_url(version, "linux-arm64-bionic").endswith(
"python3.11_3.11.15_aarch64.deb"
)
nd = get_package("node")
assert nd.fetch_url("26.4.0", "linux-arm64-bionic").endswith(
"nodejs_26.4.0-1_aarch64.deb"
)
package = lock["packages"]["python"]
assert py.fetch_url(package["version"], "linux-arm64-bionic") == package["artifacts"]["linux-arm64-bionic"]["url"]
assert py.deb_package == "python"
def test_uv_bionic_row_matches_supplier(lock):
@@ -164,7 +158,7 @@ def test_python_bionic_verify_is_file_evidence(tmp_path: Path):
from pm.registry import get_package
py = get_package("python")
bin_rel = Path("data/data/com.termux/files/usr/bin/python3.11")
bin_rel = Path(py.prefix_rel) / py.main_rel("linux-arm64-bionic")
entry = tmp_path / "entry"
(entry / bin_rel).parent.mkdir(parents=True)
(entry / bin_rel).write_bytes(b"bionic-elf-bytes")

View File

@@ -217,6 +217,19 @@ def test_rebuild_removes_superseded_license_files(tmp_path, lib_source):
assert (out / "liba.so").is_file()
def test_failed_download_identifies_the_pin_without_publishing(tmp_path, lib_source):
_, table = lib_source
(tmp_path / "debs/libb.deb").unlink()
payload = tmp_path / "payload"
with pytest.raises(srl.StageError) as caught:
srl.stage(payload, table)
message = str(caught.value)
assert "libb" in message and table["libb"]["version"] in message
assert table["libb"]["url"] in message
assert "404" in message
assert not (payload / "runtime-libs/manifest.json").exists()
def test_stale_scratch_cannot_poison_a_rebuilt_cache(tmp_path, lib_source):
_, table = lib_source
out = _stage(tmp_path, table)