diff --git a/.github/workflows/smoke.yml b/.github/workflows/smoke.yml index 78d80a072..5d1efbaa1 100644 --- a/.github/workflows/smoke.yml +++ b/.github/workflows/smoke.yml @@ -157,3 +157,45 @@ jobs: . $CONDA/etc/profile.d/conda.sh conda activate pyav python -m pytest + + armv7: + name: "armv7 cross-build + qemu smoke" + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v6 + - name: Set up host Python 3.11 + uses: actions/setup-python@v6 + with: + python-version: "3.11" + - name: Set up host Python 3.14t + uses: actions/setup-python@v6 + with: + python-version: "3.14t" + - name: Install build tooling + run: | + for py in python3.11 python3.14t; do + $py -m pip install -U "cython>=3.1.0,<4" "setuptools>=77.0" wheel + done + # patchelf 0.18 (ubuntu-24.04's system version) corrupts ELF files + # with large p_align; pin <0.18 for auditwheel to use. + python3.11 -m pip install -U ziglang auditwheel 'patchelf<0.18' + - name: Cross-compile armv7l wheels with zig + env: + HOST_PY311: python3.11 + HOST_PY314T: python3.14t + TOOLS_PY: python3.11 + run: bash scripts/build-armv7l-cross.sh + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + with: + platforms: arm + - name: Import-test wheels under emulation + run: | + docker run --rm --platform linux/arm/v7 -v "$PWD/dist:/dist:ro" \ + quay.io/pypa/manylinux_2_31_armv7l bash -ceu ' + for py in /opt/python/cp311-cp311 /opt/python/cp314-cp314t; do + echo "::group::$("$py/bin/python" --version)" + "$py/bin/pip" install --no-index --find-links /dist av + "$py/bin/python" -m av --version + echo "::endgroup::" + done' diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b0be4a542..030cddb90 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -96,7 +96,9 @@ jobs: for py in python3.11 python3.14t; do $py -m pip install -U "cython>=3.1.0,<4" "setuptools>=77.0" wheel done - python3.11 -m pip install -U ziglang auditwheel + # patchelf 0.18 (ubuntu-24.04's system version) corrupts ELF files + # with large p_align; pin <0.18 for auditwheel to use. + python3.11 -m pip install -U ziglang auditwheel 'patchelf<0.18' - name: Cross-compile armv7l wheels with zig env: HOST_PY311: python3.11 diff --git a/scripts/build-armv7l-cross.sh b/scripts/build-armv7l-cross.sh index 69eb3bfea..f304d51fd 100755 --- a/scripts/build-armv7l-cross.sh +++ b/scripts/build-armv7l-cross.sh @@ -102,21 +102,31 @@ build_one() { rm -rf "$raw"; mkdir -p "$raw" # The _PYTHON_* vars make sysconfig report armv7l; zig does the compiling. + # max-page-size=4096 aligns our extensions to the armv7l (4K) page size; + # lld's default 64K alignment uses 4K file offsets, which the loader rejects. + # Setting CFLAGS replaces the target's default OPT flags, so -O2 -DNDEBUG + # must be passed explicitly (without NDEBUG the free-threaded build trips a + # Py_SET_REFCNT assertion that release wheels compile out). env \ CC="$BIN_DIR/zig-cc" CXX="$BIN_DIR/zig-cxx" AR="$BIN_DIR/zig-ar" \ - LDSHARED="$BIN_DIR/zig-cc -shared" \ + LDSHARED="$BIN_DIR/zig-cc -shared -Wl,-z,max-page-size=4096" \ _PYTHON_HOST_PLATFORM=linux-armv7l \ _PYTHON_SYSCONFIGDATA_NAME="$(basename "${scd[0]}" .py)" \ PYTHONPATH="$(dirname "${scd[0]}")" \ - CFLAGS="-I${inc[0]} -Wno-error=incompatible-pointer-types" \ + CFLAGS="-I${inc[0]} -O2 -DNDEBUG -Wno-error=incompatible-pointer-types" \ PKG_CONFIG_PATH=/tmp/vendor/lib/pkgconfig \ LD_LIBRARY_PATH=/tmp/vendor/lib \ "$host_py" -m pip wheel . --no-build-isolation --no-deps -w "$raw" # Bundle FFmpeg + its armhf system deps and stamp the platform tag. The shim # lets auditwheel accept the armv7l --plat from an x86_64 host; --ldpaths - # points it at the target's libs instead of the host search path. - "$TOOLS_PY" scripts/auditwheel-cross.py armv7l glibc repair \ + # points it at the target's libs instead of the host search path. Force the + # pip patchelf (<0.18, on PATH via the scripts dir) ahead of the system one: + # ubuntu-24.04 ships patchelf 0.18.0, which silently corrupts ELF files with + # large p_align, breaking the bundled FFmpeg libs at runtime. + local pe_dir + pe_dir="$("$TOOLS_PY" -c 'import sysconfig; print(sysconfig.get_path("scripts"))')" + PATH="$pe_dir:$PATH" "$TOOLS_PY" scripts/auditwheel-cross.py armv7l glibc repair \ --ldpaths "/tmp/vendor/lib:$PY_DIR/syslib" \ --plat "$PLAT" -w "$DIST_DIR" "$raw"/*.whl }