From 83e382b03ad0ef7cb58255d8c78bc18381ae44a8 Mon Sep 17 00:00:00 2001 From: Jakub Kocka Date: Wed, 7 Jan 2026 12:54:22 +0100 Subject: [PATCH 1/4] ci: Update of pre-commit hooks, freeze of mypy because of Python supported --- .pre-commit-config.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fa8ac3d..d192636 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,6 +10,7 @@ ci: autofix_prs: true autoupdate_commit_msg: 'ci: Bump pre-commit hooks' autoupdate_schedule: quarterly + skip: ['mypy'] repos: - repo: https://github.com/pre-commit/pre-commit-hooks @@ -23,20 +24,20 @@ repos: - id: check-yaml - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.13.3 + rev: v0.14.10 hooks: - id: ruff-check args: [--fix, --exit-non-zero-on-fix] - id: ruff-format - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.18.2 + rev: v1.18.2 # frozen: minimum Python version compatibility (newer versions require Python 3.9+) hooks: - id: mypy additional_dependencies: ['types-all-latest'] - repo: https://github.com/espressif/conventional-precommit-linter - rev: v1.10.0 + rev: v1.11.0 hooks: - id: conventional-precommit-linter stages: [commit-msg] From 9fcbd49a817faf165c8093aa428f2f91e73c4b15 Mon Sep 17 00:00:00 2001 From: Jakub Kocka Date: Wed, 7 Jan 2026 12:47:58 +0100 Subject: [PATCH 2/4] feat: Added Docker image for older ARMv7 support to resolve glibc issues --- .github/workflows/build-wheels-platforms.yml | 39 +++++++- .../build-wheels-python-dependent.yml | 42 +++++++- .github/workflows/wheels-repair.yml | 29 +++++- README.md | 3 + docker/Dockerfile.manylinux_armv7l_legacy | 96 +++++++++++++++++++ 5 files changed, 198 insertions(+), 11 deletions(-) create mode 100644 docker/Dockerfile.manylinux_armv7l_legacy diff --git a/.github/workflows/build-wheels-platforms.yml b/.github/workflows/build-wheels-platforms.yml index ce97ba8..cdc053e 100644 --- a/.github/workflows/build-wheels-platforms.yml +++ b/.github/workflows/build-wheels-platforms.yml @@ -28,6 +28,7 @@ jobs: - macOS ARM - Linux ARM64 - Linux ARMv7 + - Linux ARMv7 Legacy include: - os: Windows runner: windows-latest @@ -47,11 +48,14 @@ jobs: - os: Linux ARMv7 runner: ubuntu-latest arch: linux-armv7 + - os: Linux ARMv7 Legacy + runner: ubuntu-latest + arch: linux-armv7-legacy python-version: ['${{ needs.get-supported-versions.outputs.oldest_supported_python }}'] steps: - name: Set up QEMU for ARMv7 - if: matrix.os == 'Linux ARMv7' + if: matrix.os == 'Linux ARMv7' || matrix.os == 'Linux ARMv7 Legacy' uses: docker/setup-qemu-action@v3 with: platforms: linux/arm/v7 @@ -79,20 +83,20 @@ jobs: - name: Setup Python # Skip setting python on ARM because of missing compatibility: https://github.com/actions/setup-python/issues/108 - if: matrix.os != 'Linux ARMv7' + if: matrix.os != 'Linux ARMv7' && matrix.os != 'Linux ARMv7 Legacy' uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install build dependencies - if: matrix.os != 'Linux ARMv7' + if: matrix.os != 'Linux ARMv7' && matrix.os != 'Linux ARMv7 Legacy' run: | python -m pip install --upgrade pip python -m pip install -r build_requirements.txt - name: Get Tools versions - if: matrix.os != 'Linux ARMv7' + if: matrix.os != 'Linux ARMv7' && matrix.os != 'Linux ARMv7 Legacy' run: | python --version pip show pip setuptools @@ -136,8 +140,33 @@ jobs: python build_wheels.py " + - name: Build wheels for IDF - ARMv7 Legacy (in Docker) + # Build on Bullseye (glibc 2.31) for compatibility with older systems + # Note: Buster has Python 3.7 which is too old for our dependencies + if: matrix.os == 'Linux ARMv7 Legacy' + run: | + docker run --rm --platform linux/arm/v7 \ + -v $(pwd):/work \ + -w /work \ + -e MIN_IDF_MAJOR_VERSION=${{ needs.get-supported-versions.outputs.min_idf_major_version }} \ + -e MIN_IDF_MINOR_VERSION=${{ needs.get-supported-versions.outputs.min_idf_minor_version }} \ + -e GH_TOKEN="${GH_TOKEN}" \ + -e PIP_NO_CACHE_DIR=1 \ + python:${{ matrix.python-version }}-bullseye \ + bash -c " + set -e + python --version + # Install pip packages without cache to reduce memory usage + python -m pip install --no-cache-dir --upgrade pip + python -m pip install --no-cache-dir -r build_requirements.txt + bash os_dependencies/linux_arm.sh + # Source Rust environment after installation + . \$HOME/.cargo/env + python build_wheels.py + " + - name: Build wheels for IDF - Linux/macOS - if: matrix.os != 'Windows' && matrix.os != 'Linux ARMv7' + if: matrix.os != 'Windows' && matrix.os != 'Linux ARMv7' && matrix.os != 'Linux ARMv7 Legacy' run: | # Set ARCHFLAGS for macOS to prevent universal2 wheels if [ "${{ matrix.os }}" = "macOS ARM" ]; then diff --git a/.github/workflows/build-wheels-python-dependent.yml b/.github/workflows/build-wheels-python-dependent.yml index a207680..e8cfbc0 100644 --- a/.github/workflows/build-wheels-python-dependent.yml +++ b/.github/workflows/build-wheels-python-dependent.yml @@ -28,6 +28,7 @@ jobs: - macOS ARM - Linux ARM64 - Linux ARMv7 + - Linux ARMv7 Legacy include: - os: Windows runner: windows-latest @@ -47,6 +48,9 @@ jobs: - os: Linux ARMv7 runner: ubuntu-latest arch: linux-armv7 + - os: Linux ARMv7 Legacy + runner: ubuntu-latest + arch: linux-armv7-legacy python-version: ${{ fromJson(inputs.supported_python_versions) }} exclude: # Exclude oldest supported Python since it's already built in the platform builds @@ -62,10 +66,15 @@ jobs: os: Linux ARM64 - python-version: ${{ inputs.oldest_supported_python }} os: Linux ARMv7 + - python-version: ${{ inputs.oldest_supported_python }} + os: Linux ARMv7 Legacy + # Python 3.14 doesn't have bullseye images for ARM + - python-version: '3.14' + os: Linux ARMv7 Legacy steps: - name: Set up QEMU for ARMv7 - if: matrix.os == 'Linux ARMv7' + if: matrix.os == 'Linux ARMv7' || matrix.os == 'Linux ARMv7 Legacy' uses: docker/setup-qemu-action@v3 with: platforms: linux/arm/v7 @@ -75,19 +84,19 @@ jobs: - name: Setup Python # Skip setting python on ARMv7 (runs in Docker) - if: matrix.os != 'Linux ARMv7' + if: matrix.os != 'Linux ARMv7' && matrix.os != 'Linux ARMv7 Legacy' uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Get Python version - if: matrix.os != 'Linux ARMv7' + if: matrix.os != 'Linux ARMv7' && matrix.os != 'Linux ARMv7 Legacy' run: | python --version python -m pip install --upgrade pip - name: Install dependencies - if: matrix.os != 'Linux ARMv7' + if: matrix.os != 'Linux ARMv7' && matrix.os != 'Linux ARMv7 Legacy' run: python -m pip install -r build_requirements.txt - name: Install additional OS dependencies - Ubuntu @@ -138,8 +147,31 @@ jobs: python build_wheels_from_file.py dependent_requirements_${{ matrix.arch }} " + - name: Build Python dependent wheels - ARMv7 Legacy (in Docker) + # Build on Bullseye (glibc 2.31) for compatibility with older systems + if: matrix.os == 'Linux ARMv7 Legacy' + run: | + docker run --rm --platform linux/arm/v7 \ + -v $(pwd):/work \ + -w /work \ + -e PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1 \ + -e GH_TOKEN="${GH_TOKEN}" \ + -e PIP_NO_CACHE_DIR=1 \ + python:${{ matrix.python-version }}-bullseye \ + bash -c " + set -e + python --version + # Install pip packages without cache to reduce memory usage + python -m pip install --no-cache-dir --upgrade pip + python -m pip install --no-cache-dir -r build_requirements.txt + bash os_dependencies/linux_arm.sh + # Source Rust environment after installation + . \$HOME/.cargo/env + python build_wheels_from_file.py dependent_requirements_${{ matrix.arch }} + " + - name: Build Python dependent wheels - Linux/macOS - if: matrix.os != 'Windows' && matrix.os != 'Linux ARMv7' + if: matrix.os != 'Windows' && matrix.os != 'Linux ARMv7' && matrix.os != 'Linux ARMv7 Legacy' run: | # Set ARCHFLAGS for macOS to prevent universal2 wheels if [ "${{ matrix.os }}" = "macOS ARM" ]; then diff --git a/.github/workflows/wheels-repair.yml b/.github/workflows/wheels-repair.yml index 0853022..9944fcc 100644 --- a/.github/workflows/wheels-repair.yml +++ b/.github/workflows/wheels-repair.yml @@ -23,6 +23,7 @@ jobs: - Linux x86_64 - Linux ARM64 - Linux ARMv7 + - Linux ARMv7 Legacy include: - platform: Windows runner: windows-latest @@ -58,6 +59,15 @@ jobs: qemu_platform: arm docker_platform: linux/arm/v7 arch: linux-armv7 + - platform: Linux ARMv7 Legacy + runner: ubuntu-latest + tool: auditwheel + manylinux_platform: idf-python-wheels-armv7l-legacy + docker_image: ghcr.io/espressif/github-esp-dockerfiles/idf-python-wheels-armv7l-legacy:v1 + setup_qemu: true + qemu_platform: arm + docker_platform: linux/arm/v7 + arch: linux-armv7-legacy steps: - name: Checkout repository @@ -66,7 +76,9 @@ jobs: - name: Download wheel artifacts uses: actions/download-artifact@v4 with: - pattern: wheels-download-directory-* + # Download only artifacts for this specific architecture to avoid mixing wheels + # (especially important for ARMv7 vs ARMv7 Legacy which produce same-named wheels) + pattern: wheels-download-directory-${{ matrix.arch }}-* path: ./downloaded_wheels merge-multiple: true @@ -145,6 +157,21 @@ jobs: python3 repair_wheels.py " + - name: Repair Linux ARMv7 Legacy wheels in manylinux container + if: matrix.platform == 'Linux ARMv7 Legacy' + run: | + docker run --rm \ + --platform ${{ matrix.docker_platform }} \ + -v $(pwd):/work \ + -w /work \ + ${{ matrix.docker_image }} \ + bash -c " + bash os_dependencies/linux_arm.sh + python3 -m pip install --upgrade pip + python3 -m pip install -r build_requirements.txt + python3 repair_wheels.py + " + - name: Re-upload artifacts with repaired wheels uses: actions/upload-artifact@v4 with: diff --git a/README.md b/README.md index d6eb67f..78686bd 100644 --- a/README.md +++ b/README.md @@ -141,4 +141,7 @@ The main file is `build-wheels-platforms.yml` which is scheduled to run periodic > Python dependent wheels are wheels which depend on the [CPython’s Application Binary Interface (ABI)](https://docs.python.org/3/c-api/stable.html). These are checked based on the [wheel filename format](https://peps.python.org/pep-0491/#file-format) where the `abi tag` is checked for `cp`. Such wheels need to be build also for all supported Python versions, not only for the minimum Python version supported by [ESP-IDF]. +## Docker files +Docker files are here just as a copy for better understanding in the scope of one repository. Actual Docker files are in its own repository where there are build and published from. https://github.com/espressif/github-esp-dockerfiles + [ESP-IDF]: https://github.com/espressif/esp-idf diff --git a/docker/Dockerfile.manylinux_armv7l_legacy b/docker/Dockerfile.manylinux_armv7l_legacy new file mode 100644 index 0000000..9da6f92 --- /dev/null +++ b/docker/Dockerfile.manylinux_armv7l_legacy @@ -0,0 +1,96 @@ +# Minimal manylinux-compatible Docker image for ARMv7l (32-bit ARM) +# Based on Debian Bullseye for older glibc compatibility (2.31) +# Use this for compatibility with older systems (e.g., older Raspberry Pi OS) + +FROM arm32v7/debian:bullseye + +LABEL maintainer="Espressif Systems" +LABEL description="Minimal manylinux-compatible environment for building Python wheels on ARMv7l (legacy glibc 2.31)" + +# Set environment variables +ENV DEBIAN_FRONTEND=noninteractive \ + LC_ALL=C.UTF-8 \ + LANG=C.UTF-8 + +# Install build dependencies and Python versions +RUN apt-get update && apt-get install -y \ + # Build essentials + build-essential \ + gcc \ + g++ \ + make \ + cmake \ + git \ + wget \ + curl \ + ca-certificates \ + patchelf \ + # Python build dependencies + libssl-dev \ + libffi-dev \ + libbz2-dev \ + libreadline-dev \ + libsqlite3-dev \ + libncurses5-dev \ + libncursesw5-dev \ + libgdbm-dev \ + liblzma-dev \ + tk-dev \ + zlib1g-dev \ + # Additional libraries commonly needed for wheels + libxml2-dev \ + libxslt1-dev \ + libyaml-dev \ + libglib2.0-dev \ + # PyGObject dependencies + libcairo2-dev \ + pkg-config \ + libgirepository1.0-dev \ + # dbus-python dependencies + libdbus-1-dev \ + libdbus-glib-1-dev \ + # Pillow dependencies (include both -dev and runtime libs for auditwheel) + libjpeg-dev \ + libpng-dev \ + libtiff-dev \ + libtiff5 \ + libfreetype6-dev \ + liblcms2-dev \ + libwebp-dev \ + libopenjp2-7-dev \ + libfribidi-dev \ + libharfbuzz-dev \ + libxcb1-dev \ + libxau-dev \ + brotli \ + libbrotli-dev \ + # Python from Debian repos (Bullseye provides 3.9) + python3 \ + python3-dev \ + python3-venv \ + python3-pip \ + && rm -rf /var/lib/apt/lists/* + +# Install pip and wheel tools +RUN python3 -m pip install --no-cache-dir --upgrade \ + pip \ + setuptools \ + wheel \ + auditwheel + +# Set up manylinux platform tag +# This makes auditwheel recognize the environment as manylinux-compatible +# Bullseye has glibc 2.31, so we use manylinux_2_31 +RUN echo "manylinux_2_31_armv7l" > /etc/system-release-cpe + +# Create a marker file for manylinux detection +RUN mkdir -p /opt/python && \ + echo "manylinux_2_31_armv7l" > /opt/python/PLATFORM + +# Set working directory +WORKDIR /workspace + +# Default Python +ENV PATH="/usr/bin:${PATH}" + +CMD ["/bin/bash"] From 694f52ffd7fc3cfda9d3a8925e1e5f0550e20b62 Mon Sep 17 00:00:00 2001 From: Jakub Kocka Date: Mon, 12 Jan 2026 10:26:33 +0100 Subject: [PATCH 3/4] fix: Resolved CRC issues because of pre-build packages, image dependencies --- .github/workflows/build-wheels-platforms.yml | 2 +- .../build-wheels-python-dependent.yml | 2 +- .github/workflows/wheels-repair.yml | 10 +++-- _helper_functions.py | 45 +++++++++++++++++++ build_wheels.py | 15 ++++--- build_wheels_from_file.py | 21 ++++++--- docker/Dockerfile.manylinux_armv7l | 17 ++++++- docker/Dockerfile.manylinux_armv7l_legacy | 16 ++++++- 8 files changed, 109 insertions(+), 19 deletions(-) diff --git a/.github/workflows/build-wheels-platforms.yml b/.github/workflows/build-wheels-platforms.yml index cdc053e..7bf2d53 100644 --- a/.github/workflows/build-wheels-platforms.yml +++ b/.github/workflows/build-wheels-platforms.yml @@ -50,7 +50,7 @@ jobs: arch: linux-armv7 - os: Linux ARMv7 Legacy runner: ubuntu-latest - arch: linux-armv7-legacy + arch: linux-armv7legacy python-version: ['${{ needs.get-supported-versions.outputs.oldest_supported_python }}'] steps: diff --git a/.github/workflows/build-wheels-python-dependent.yml b/.github/workflows/build-wheels-python-dependent.yml index e8cfbc0..2f3b46b 100644 --- a/.github/workflows/build-wheels-python-dependent.yml +++ b/.github/workflows/build-wheels-python-dependent.yml @@ -50,7 +50,7 @@ jobs: arch: linux-armv7 - os: Linux ARMv7 Legacy runner: ubuntu-latest - arch: linux-armv7-legacy + arch: linux-armv7legacy python-version: ${{ fromJson(inputs.supported_python_versions) }} exclude: # Exclude oldest supported Python since it's already built in the platform builds diff --git a/.github/workflows/wheels-repair.yml b/.github/workflows/wheels-repair.yml index 9944fcc..ea0376a 100644 --- a/.github/workflows/wheels-repair.yml +++ b/.github/workflows/wheels-repair.yml @@ -67,7 +67,7 @@ jobs: setup_qemu: true qemu_platform: arm docker_platform: linux/arm/v7 - arch: linux-armv7-legacy + arch: linux-armv7legacy steps: - name: Checkout repository @@ -143,6 +143,7 @@ jobs: " - name: Repair Linux ARMv7 wheels in manylinux container + # Using --break-system-packages and --ignore-installed to avoid conflicts with system packages if: matrix.platform == 'Linux ARMv7' run: | docker run --rm \ @@ -153,11 +154,12 @@ jobs: bash -c " bash os_dependencies/linux_arm.sh python3 -m pip install --break-system-packages --upgrade pip - python3 -m pip install --break-system-packages -r build_requirements.txt + python3 -m pip install --break-system-packages --ignore-installed -r build_requirements.txt python3 repair_wheels.py " - name: Repair Linux ARMv7 Legacy wheels in manylinux container + # Using --break-system-packages and --ignore-installed to avoid conflicts with system packages if: matrix.platform == 'Linux ARMv7 Legacy' run: | docker run --rm \ @@ -167,8 +169,8 @@ jobs: ${{ matrix.docker_image }} \ bash -c " bash os_dependencies/linux_arm.sh - python3 -m pip install --upgrade pip - python3 -m pip install -r build_requirements.txt + python3 -m pip install --break-system-packages --upgrade pip + python3 -m pip install --break-system-packages --ignore-installed -r build_requirements.txt python3 repair_wheels.py " diff --git a/_helper_functions.py b/_helper_functions.py index 3462941..32f305e 100644 --- a/_helper_functions.py +++ b/_helper_functions.py @@ -3,10 +3,55 @@ # # SPDX-License-Identifier: Apache-2.0 # +import platform +import re + from colorama import Fore from colorama import Style from packaging.requirements import Requirement +# Packages that should be built from source on Linux to ensure correct library linking +# These packages often have pre-built wheels on PyPI that link against different library versions +# NOTE: This only applies to Linux (especially ARM) - Windows and macOS pre-built wheels work fine +# NOTE: Do NOT add packages with Rust components (cryptography, pynacl, bcrypt) here +# as they have complex build requirements and may not support all Python versions +FORCE_SOURCE_BUILD_PACKAGES_LINUX = [ + "cffi", + "pillow", + "pyyaml", + "brotli", + "greenlet", + "bitarray", +] + + +def get_no_binary_args(requirement_name: str) -> list: + """Get --no-binary arguments if this package should be built from source. + + This only applies on Linux platforms where pre-built wheels may link against + different library versions. On Windows and macOS, pre-built wheels work correctly. + + Args: + requirement_name: Package name or requirement string (e.g., "cffi" or "cffi>=1.0") + + Returns: + List with --no-binary arguments if package should be built from source, empty list otherwise + """ + # Only force source builds on Linux (where we have library version issues) + if platform.system() != "Linux": + return [] + + # Extract package name from requirement string (e.g., "cffi>=1.0" -> "cffi") + match = re.match(r"^([a-zA-Z0-9_-]+)", str(requirement_name).strip()) + if not match: + return [] + pkg_name = match.group(1).lower().replace("-", "_") + + for pkg in FORCE_SOURCE_BUILD_PACKAGES_LINUX: + if pkg.lower().replace("-", "_") == pkg_name: + return ["--no-binary", match.group(1)] + return [] + def print_color(text: str, color: str = Fore.BLUE): """Print colored text specified by color argument based on colorama diff --git a/build_wheels.py b/build_wheels.py index 9877565..abfdb60 100644 --- a/build_wheels.py +++ b/build_wheels.py @@ -20,6 +20,7 @@ from packaging.requirements import InvalidRequirement from packaging.requirements import Requirement +from _helper_functions import get_no_binary_args from _helper_functions import merge_requirements from _helper_functions import print_color from yaml_list_adapter import YAMLListAdapter @@ -296,13 +297,16 @@ def build_wheels(requirements: set, local_links: bool = True) -> dict: stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) - print(out.stdout.decode("utf-8")) + print(out.stdout.decode("utf-8", errors="replace")) if out.stderr: - print_color(out.stderr.decode("utf-8"), Fore.RED) + print_color(out.stderr.decode("utf-8", errors="replace"), Fore.RED) non_classic_requirement.remove(non_classic_requirement[0]) continue # requirement wheel build + # Get no-binary args for packages that should be built from source + no_binary_args = get_no_binary_args(requirement.name) + out = subprocess.run( [ f"{sys.executable}", @@ -318,14 +322,15 @@ def build_wheels(requirements: set, local_links: bool = True) -> dict: f"{dir}", "--no-cache-dir", "--no-build-isolation", - ], + ] + + no_binary_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) - print(out.stdout.decode("utf-8")) + print(out.stdout.decode("utf-8", errors="replace")) if out.stderr: - print_color(out.stderr.decode("utf-8"), Fore.RED) + print_color(out.stderr.decode("utf-8", errors="replace"), Fore.RED) if out.returncode != 0: failed_wheels += 1 diff --git a/build_wheels_from_file.py b/build_wheels_from_file.py index 604b2c1..ce7b783 100644 --- a/build_wheels_from_file.py +++ b/build_wheels_from_file.py @@ -10,6 +10,7 @@ from colorama import Fore +from _helper_functions import get_no_binary_args from _helper_functions import print_color parser = argparse.ArgumentParser(description="Process build arguments.") @@ -49,6 +50,9 @@ raise SystemExit(f"Python version dependent requirements directory or file not found ({e})") for requirement in requirements: + # Get no-binary args for packages that should be built from source + no_binary_args = get_no_binary_args(requirement) + out = subprocess.run( [ f"{sys.executable}", @@ -60,14 +64,15 @@ "downloaded_wheels", "--wheel-dir", "downloaded_wheels", - ], + ] + + no_binary_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) - print(out.stdout.decode("utf-8")) + print(out.stdout.decode("utf-8", errors="replace")) if out.stderr: - print_color(out.stderr.decode("utf-8"), Fore.RED) + print_color(out.stderr.decode("utf-8", errors="replace"), Fore.RED) if out.returncode != 0: failed_wheels += 1 @@ -84,6 +89,9 @@ # Build wheels from passed requirements else: for requirement in in_requirements: + # Get no-binary args for packages that should be built from source + no_binary_args = get_no_binary_args(requirement) + out = subprocess.run( [ f"{sys.executable}", @@ -95,14 +103,15 @@ "downloaded_wheels", "--wheel-dir", "downloaded_wheels", - ], + ] + + no_binary_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) - print(out.stdout.decode("utf-8")) + print(out.stdout.decode("utf-8", errors="replace")) if out.stderr: - print_color(out.stderr.decode("utf-8"), Fore.RED) + print_color(out.stderr.decode("utf-8", errors="replace"), Fore.RED) if out.returncode != 0: failed_wheels += 1 diff --git a/docker/Dockerfile.manylinux_armv7l b/docker/Dockerfile.manylinux_armv7l index dcb8414..cb9aa27 100644 --- a/docker/Dockerfile.manylinux_armv7l +++ b/docker/Dockerfile.manylinux_armv7l @@ -12,6 +12,7 @@ ENV DEBIAN_FRONTEND=noninteractive \ LANG=C.UTF-8 # Install build dependencies and Python versions +# Note: We build patchelf from source to ensure proper ELF alignment on ARM RUN apt-get update && apt-get install -y \ # Build essentials build-essential \ @@ -23,7 +24,8 @@ RUN apt-get update && apt-get install -y \ wget \ curl \ ca-certificates \ - patchelf \ + autoconf \ + automake \ # Python build dependencies libssl-dev \ libffi-dev \ @@ -69,6 +71,19 @@ RUN apt-get update && apt-get install -y \ python3-pip \ && rm -rf /var/lib/apt/lists/* +# Build and install patchelf from source to ensure proper ELF alignment on ARM +# System patchelf versions can cause "ELF load command address/offset not properly aligned" errors +RUN cd /tmp && \ + git clone https://github.com/NixOS/patchelf.git && \ + cd patchelf && \ + git checkout 0.18.0 && \ + ./bootstrap.sh && \ + ./configure && \ + make && \ + make install && \ + cd / && rm -rf /tmp/patchelf && \ + patchelf --version + # Install pip and wheel tools # --break-system-packages is OK because it is docker image and not a system RUN python3 -m pip install --no-cache-dir --break-system-packages --upgrade \ diff --git a/docker/Dockerfile.manylinux_armv7l_legacy b/docker/Dockerfile.manylinux_armv7l_legacy index 9da6f92..3f59d14 100644 --- a/docker/Dockerfile.manylinux_armv7l_legacy +++ b/docker/Dockerfile.manylinux_armv7l_legacy @@ -13,6 +13,7 @@ ENV DEBIAN_FRONTEND=noninteractive \ LANG=C.UTF-8 # Install build dependencies and Python versions +# Note: We don't install patchelf from apt because Bullseye has 0.12 but auditwheel needs >= 0.14 RUN apt-get update && apt-get install -y \ # Build essentials build-essential \ @@ -24,7 +25,8 @@ RUN apt-get update && apt-get install -y \ wget \ curl \ ca-certificates \ - patchelf \ + autoconf \ + automake \ # Python build dependencies libssl-dev \ libffi-dev \ @@ -71,6 +73,18 @@ RUN apt-get update && apt-get install -y \ python3-pip \ && rm -rf /var/lib/apt/lists/* +# Build and install patchelf >= 0.14 from source (Bullseye has 0.12 which is too old for auditwheel) +RUN cd /tmp && \ + git clone https://github.com/NixOS/patchelf.git && \ + cd patchelf && \ + git checkout 0.18.0 && \ + ./bootstrap.sh && \ + ./configure && \ + make && \ + make install && \ + cd / && rm -rf /tmp/patchelf && \ + patchelf --version + # Install pip and wheel tools RUN python3 -m pip install --no-cache-dir --upgrade \ pip \ From bdf9bd825907f3f5f4da2cced758e27d2ee3a6bf Mon Sep 17 00:00:00 2001 From: Jakub Kocka Date: Wed, 14 Jan 2026 11:41:10 +0100 Subject: [PATCH 4/4] fix: ELF load command address/offset not properly aligned Related esptool issue: https://github.com/espressif/esptool/actions/runs/20955204267/job/60330279537 --- .github/workflows/build-wheels-platforms.yml | 2 ++ .github/workflows/build-wheels-python-dependent.yml | 2 ++ repair_wheels.py | 10 ++++++++-- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-wheels-platforms.yml b/.github/workflows/build-wheels-platforms.yml index 7bf2d53..313962a 100644 --- a/.github/workflows/build-wheels-platforms.yml +++ b/.github/workflows/build-wheels-platforms.yml @@ -127,6 +127,7 @@ jobs: -e MIN_IDF_MINOR_VERSION=${{ needs.get-supported-versions.outputs.min_idf_minor_version }} \ -e GH_TOKEN="${GH_TOKEN}" \ -e PIP_NO_CACHE_DIR=1 \ + -e LDFLAGS="-Wl,-z,max-page-size=0x1000" \ python:${{ matrix.python-version }}-bookworm \ bash -c " set -e @@ -152,6 +153,7 @@ jobs: -e MIN_IDF_MINOR_VERSION=${{ needs.get-supported-versions.outputs.min_idf_minor_version }} \ -e GH_TOKEN="${GH_TOKEN}" \ -e PIP_NO_CACHE_DIR=1 \ + -e LDFLAGS="-Wl,-z,max-page-size=0x1000" \ python:${{ matrix.python-version }}-bullseye \ bash -c " set -e diff --git a/.github/workflows/build-wheels-python-dependent.yml b/.github/workflows/build-wheels-python-dependent.yml index 2f3b46b..4c019b8 100644 --- a/.github/workflows/build-wheels-python-dependent.yml +++ b/.github/workflows/build-wheels-python-dependent.yml @@ -134,6 +134,7 @@ jobs: -e PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1 \ -e GH_TOKEN="${GH_TOKEN}" \ -e PIP_NO_CACHE_DIR=1 \ + -e LDFLAGS="-Wl,-z,max-page-size=0x1000" \ python:${{ matrix.python-version }}-bookworm \ bash -c " set -e @@ -157,6 +158,7 @@ jobs: -e PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1 \ -e GH_TOKEN="${GH_TOKEN}" \ -e PIP_NO_CACHE_DIR=1 \ + -e LDFLAGS="-Wl,-z,max-page-size=0x1000" \ python:${{ matrix.python-version }}-bullseye \ bash -c " set -e diff --git a/repair_wheels.py b/repair_wheels.py index 8d1783f..9cbd7ed 100644 --- a/repair_wheels.py +++ b/repair_wheels.py @@ -119,9 +119,15 @@ def repair_wheel_macos(wheel_path: Path, temp_dir: Path) -> subprocess.Completed def repair_wheel_linux(wheel_path: Path, temp_dir: Path) -> subprocess.CompletedProcess[str]: - """Repair Linux wheel using auditwheel.""" + """Repair Linux wheel using auditwheel. + + Uses --strip option to strip debugging symbols which can help with + ELF alignment issues on ARM (fixes "ELF load command address/offset not properly aligned" errors). + """ result = subprocess.run( - ["auditwheel", "repair", str(wheel_path), "-w", str(temp_dir)], capture_output=True, text=True + ["auditwheel", "repair", str(wheel_path), "-w", str(temp_dir), "--strip"], + capture_output=True, + text=True, ) return result