🔧 Build uv-ffi Wheels (Extended) #59
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: "🔧 Build uv-ffi Wheels (Extended)" | |
| on: | |
| # NO release trigger — publish.yml dispatches this | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: 'Tag to build (e.g. v0.10.7)' | |
| required: false | |
| default: '' | |
| jobs: | |
| build-linux-glibc-2_17: | |
| name: "glibc 2.17 • ${{ matrix.target }}" | |
| runs-on: ubuntu-latest | |
| continue-on-error: true | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| target: | |
| - x86_64 | |
| - i686 | |
| - aarch64 | |
| - armv7 | |
| - ppc64le | |
| - s390x-unknown-linux-gnu | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.event.inputs.tag || github.ref }} | |
| - name: Cache Rust dependencies | |
| uses: swatinem/rust-cache@v2 | |
| - name: Check PyPI for existing wheel | |
| id: pypi_check | |
| shell: bash | |
| run: | | |
| VERSION="${{ github.event.inputs.tag }}" | |
| VERSION="${VERSION#v}" | |
| if [ -z "$VERSION" ]; then | |
| VERSION=$(grep -m1 '^version' crates/uv-ffi/pyproject.toml | sed 's/version = "\(.*\)"/\1/') | |
| fi | |
| TARGET="${{ matrix.target }}" | |
| TARGET="${TARGET%%-*}" | |
| [[ "$TARGET" == "armv7" ]] && TARGET="armv7l" | |
| echo "Checking PyPI for any manylinux_2_17_${TARGET} wheels for $VERSION..." | |
| PYPI_FILES=$(curl -s "https://pypi.org/pypi/uv-ffi/$VERSION/json" | python3 -c "import sys,json; files=[f['filename'] for f in json.load(sys.stdin).get('urls',[])]; matches=[f for f in files if 'manylinux_2_17_${TARGET}' in f]; print('found' if matches else 'missing'); [print(' ',f) for f in matches]" 2>/dev/null) | |
| if [[ "$PYPI_FILES" == found* ]]; then | |
| echo "Wheels for manylinux_2_17_${TARGET} already on PyPI - skipping build" | |
| echo "skip=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "Wheels for manylinux_2_17_${TARGET} not on PyPI - proceeding" | |
| echo "skip=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Install cross-compilers | |
| run: sudo apt-get update -qq && sudo apt-get install -y gcc-s390x-linux-gnu gcc-aarch64-linux-gnu gcc-arm-linux-gnueabihf gcc-powerpc64le-linux-gnu | |
| - name: Set up QEMU (cross targets) | |
| if: matrix.target != 'x86_64' && matrix.target != 'i686' | |
| uses: docker/setup-qemu-action@v3 | |
| with: | |
| platforms: all | |
| - name: Build wheels (manylinux_2_17) | |
| if: steps.pypi_check.outputs.skip == 'false' | |
| uses: PyO3/maturin-action@v1 | |
| with: | |
| working-directory: crates/uv-ffi | |
| target: ${{ matrix.target }} | |
| manylinux: "2_17" | |
| rust-toolchain: "1.91.0" # ← pin to older toolchain | |
| before-script-linux: | | |
| if command -v yum &>/dev/null; then | |
| yum install -y gcc-s390x-linux-gnu binutils-s390x-linux-gnu 2>/dev/null || true | |
| fi | |
| if command -v apt-get &>/dev/null; then | |
| apt-get install -y gcc-s390x-linux-gnu 2>/dev/null || true | |
| fi | |
| args: --release --out ${{ github.workspace }}/wheels/ --interpreter python3.10 python3.11 python3.12 python3.13 python3.14 | |
| env: | |
| PYO3_USE_ABI3_FORWARD_COMPATIBILITY: "1" | |
| CFLAGS_aarch64_unknown_linux_gnu: "-march=armv8-a -D__ARM_ARCH=8" | |
| CFLAGS_armv7_unknown_linux_gnueabihf: "-march=armv7-a" | |
| CARGO_PROFILE_RELEASE_LTO: "thin" | |
| - name: Upload wheels | |
| if: steps.pypi_check.outputs.skip == 'false' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: "ext-wheels-linux-glibc217-${{ matrix.target }}" | |
| path: wheels/ | |
| if-no-files-found: warn | |
| build-cp37-linux: | |
| name: "CPython 3.7 • ${{ matrix.target }}" | |
| runs-on: ubuntu-latest | |
| continue-on-error: true | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| target: | |
| - x86_64 | |
| - i686 | |
| - aarch64 | |
| - armv7 | |
| - ppc64le | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.event.inputs.tag || github.ref }} | |
| - name: Set up QEMU | |
| if: matrix.target != 'x86_64' && matrix.target != 'i686' | |
| uses: docker/setup-qemu-action@v3 | |
| with: | |
| platforms: all | |
| - name: Build cp37 wheel (manylinux_2_17) | |
| uses: PyO3/maturin-action@v1 | |
| with: | |
| working-directory: crates/uv-ffi | |
| target: ${{ matrix.target }} | |
| manylinux: "2_17" | |
| rust-toolchain: "1.91.0" | |
| args: --release --out ${{ github.workspace }}/wheels/ --interpreter python3.7 --no-default-features | |
| env: | |
| CFLAGS_aarch64_unknown_linux_gnu: "-march=armv8-a -D__ARM_ARCH=8" | |
| CFLAGS_armv7_unknown_linux_gnueabihf: "-march=armv7-a" | |
| CARGO_PROFILE_RELEASE_LTO: "thin" | |
| - name: Upload wheels | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: "ext-wheels-cp37-${{ matrix.target }}" | |
| path: wheels/ | |
| if-no-files-found: warn | |
| build-linux-musl: | |
| name: "musl 1.2 • ${{ matrix.target }}" | |
| runs-on: ubuntu-latest | |
| continue-on-error: true | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| target: | |
| - x86_64 | |
| - i686 | |
| - aarch64 | |
| - armv7 | |
| - ppc64le | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.event.inputs.tag || github.ref }} | |
| - name: Check PyPI for existing wheel | |
| id: pypi_check | |
| shell: bash | |
| run: | | |
| VERSION="${{ github.event.inputs.tag }}" | |
| VERSION="${VERSION#v}" | |
| if [ -z "$VERSION" ]; then | |
| VERSION=$(grep -m1 '^version' crates/uv-ffi/pyproject.toml | sed 's/version = "\(.*\)"/\1/') | |
| fi | |
| TARGET="${{ matrix.target }}" | |
| TARGET="${TARGET%%-*}" | |
| [[ "$TARGET" == "armv7" ]] && TARGET="armv7l" | |
| echo "Checking PyPI for any musllinux_1_2_${TARGET} wheels for $VERSION..." | |
| PYPI_FILES=$(curl -s "https://pypi.org/pypi/uv-ffi/$VERSION/json" | python3 -c "import sys,json; files=[f['filename'] for f in json.load(sys.stdin).get('urls',[])]; matches=[f for f in files if 'musllinux_1_2_${TARGET}' in f]; print('found' if matches else 'missing'); [print(' ',f) for f in matches]" 2>/dev/null) | |
| if [[ "$PYPI_FILES" == found* ]]; then | |
| echo "Wheels for musllinux_1_2_${TARGET} already on PyPI - skipping build" | |
| echo "skip=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "Wheels for musllinux_1_2_${TARGET} not on PyPI - proceeding" | |
| echo "skip=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Set up QEMU (cross targets) | |
| if: matrix.target != 'x86_64' && matrix.target != 'i686' | |
| uses: docker/setup-qemu-action@v3 | |
| with: | |
| platforms: all | |
| - name: Build wheels (musllinux_1_2) | |
| if: steps.pypi_check.outputs.skip == 'false' | |
| uses: PyO3/maturin-action@v1 | |
| with: | |
| working-directory: crates/uv-ffi | |
| target: ${{ matrix.target }} | |
| manylinux: musllinux_1_2 | |
| args: --release --out ${{ github.workspace }}/wheels/ --interpreter python3.10 python3.11 python3.12 python3.13 python3.14 | |
| env: | |
| PYO3_USE_ABI3_FORWARD_COMPATIBILITY: "1" | |
| CFLAGS_aarch64_unknown_linux_gnu: "-march=armv8-a" | |
| CFLAGS_armv7_unknown_linux_gnueabihf: "-march=armv7-a" | |
| - name: Upload wheels | |
| if: steps.pypi_check.outputs.skip == 'false' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: "ext-wheels-linux-musl-${{ matrix.target }}" | |
| path: wheels/ | |
| if-no-files-found: warn | |
| build-linux-riscv64: | |
| name: "riscv64 glibc 2.31" | |
| runs-on: ubuntu-latest | |
| continue-on-error: true | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.event.inputs.tag || github.ref }} | |
| - name: Check PyPI for existing wheel | |
| id: pypi_check | |
| shell: bash | |
| run: | | |
| VERSION="${{ github.event.inputs.tag }}" | |
| VERSION="${VERSION#v}" | |
| if [ -z "$VERSION" ]; then | |
| VERSION=$(grep -m1 '^version' crates/uv-ffi/pyproject.toml | sed 's/version = "\(.*\)"/\1/') | |
| fi | |
| echo "Checking PyPI for any manylinux_2_31_riscv64 wheels for $VERSION..." | |
| PYPI_FILES=$(curl -s "https://pypi.org/pypi/uv-ffi/$VERSION/json" | python3 -c "import sys,json; files=[f['filename'] for f in json.load(sys.stdin).get('urls',[])]; matches=[f for f in files if 'manylinux_2_31_riscv64' in f]; print('found' if matches else 'missing')" 2>/dev/null) | |
| if [[ "$PYPI_FILES" == found* ]]; then | |
| echo "Wheels for riscv64 already on PyPI - skipping build" | |
| echo "skip=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "Wheels for riscv64 not on PyPI - proceeding" | |
| echo "skip=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@v3 | |
| with: | |
| platforms: riscv64 | |
| - name: Build wheels (manylinux_2_31) | |
| if: steps.pypi_check.outputs.skip == 'false' | |
| uses: PyO3/maturin-action@v1 | |
| with: | |
| working-directory: crates/uv-ffi | |
| target: riscv64gc-unknown-linux-gnu | |
| manylinux: "2_31" | |
| args: --release --out ${{ github.workspace }}/wheels/ --interpreter python3.10 python3.11 python3.12 python3.13 python3.14 | |
| env: | |
| PYO3_USE_ABI3_FORWARD_COMPATIBILITY: "1" | |
| - name: Upload wheels | |
| if: steps.pypi_check.outputs.skip == 'false' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: "ext-wheels-linux-riscv64" | |
| path: wheels/ | |
| if-no-files-found: warn | |
| build-macos-universal2: | |
| name: "macOS universal2 • py${{ matrix.python }}" | |
| runs-on: macos-latest | |
| continue-on-error: true | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.event.inputs.tag || github.ref }} | |
| - name: Check PyPI for existing wheel | |
| id: pypi_check | |
| shell: bash | |
| run: | | |
| VERSION="${{ github.event.inputs.tag }}" | |
| VERSION="${VERSION#v}" | |
| if [ -z "$VERSION" ]; then | |
| VERSION=$(grep -m1 '^version' crates/uv-ffi/pyproject.toml | sed 's/version = "\(.*\)"/\1/') | |
| fi | |
| PYVER="cp$( echo "${{ matrix.python }}" | tr -d '.' )" | |
| WHEEL="uv_ffi-${VERSION}-${PYVER}-${PYVER}-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl" | |
| echo "Checking PyPI for $WHEEL..." | |
| PYPI_FILES=$(curl -s "https://pypi.org/pypi/uv-ffi/$VERSION/json" | python3 -c "import sys,json; files=[f['filename'] for f in json.load(sys.stdin).get('urls',[])]; print('found' if '${WHEEL}' in files else 'missing')" 2>/dev/null) | |
| if [ "$PYPI_FILES" = "found" ]; then | |
| echo "Wheel $WHEEL already on PyPI - skipping build" | |
| echo "skip=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "Wheel $WHEEL not on PyPI - proceeding" | |
| echo "skip=false" >> $GITHUB_OUTPUT | |
| fi | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ matrix.python }} | |
| - name: Build universal2 wheel | |
| if: steps.pypi_check.outputs.skip == 'false' | |
| uses: PyO3/maturin-action@v1 | |
| with: | |
| working-directory: crates/uv-ffi | |
| target: universal2-apple-darwin | |
| args: --release --out ${{ github.workspace }}/wheels/ --interpreter python${{ matrix.python }} | |
| env: | |
| PYO3_USE_ABI3_FORWARD_COMPATIBILITY: "1" | |
| - name: Upload wheels | |
| if: steps.pypi_check.outputs.skip == 'false' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: "ext-wheels-macos-universal2-py${{ matrix.python }}" | |
| path: wheels/ | |
| if-no-files-found: warn | |
| build-windows-i686: | |
| name: "Windows i686 • py${{ matrix.python }}" | |
| runs-on: windows-latest | |
| continue-on-error: true | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.event.inputs.tag || github.ref }} | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ matrix.python }} | |
| architecture: x86 | |
| - name: Check PyPI for existing wheel | |
| id: pypi_check | |
| shell: powershell | |
| run: | | |
| $tag = "${{ github.event.inputs.tag }}" | |
| $version = if ($tag) { $tag.TrimStart("v") } else { (Select-String -Path "crates\uv-ffi\pyproject.toml" -Pattern '^version = "([^"]+)"' | Select-Object -First 1).Matches.Groups[1].Value } | |
| $pyver = "cp" + ("${{ matrix.python }}" -replace '\.', '') | |
| $wheel = "uv_ffi-${version}-${pyver}-${pyver}-win32.whl" | |
| Write-Host "Checking PyPI for $wheel..." | |
| try { $files = (Invoke-RestMethod "https://pypi.org/pypi/uv-ffi/$version/json").urls.filename } catch { $files = @() } | |
| if ($files -contains $wheel) { | |
| Write-Host "Wheel $wheel already on PyPI - skipping build" | |
| "skip=true" | Out-File -FilePath $env:GITHUB_OUTPUT -Append | |
| } else { | |
| Write-Host "Wheel $wheel not on PyPI - proceeding" | |
| "skip=false" | Out-File -FilePath $env:GITHUB_OUTPUT -Append | |
| } | |
| - name: Build wheel (i686) | |
| if: steps.pypi_check.outputs.skip == 'false' | |
| uses: PyO3/maturin-action@v1 | |
| with: | |
| working-directory: crates/uv-ffi | |
| target: i686-pc-windows-msvc | |
| args: --release --out ${{ github.workspace }}\wheels\ --interpreter python${{ matrix.python }} | |
| env: | |
| PYO3_USE_ABI3_FORWARD_COMPATIBILITY: "1" | |
| - name: Upload wheels | |
| if: steps.pypi_check.outputs.skip == 'false' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: "ext-wheels-windows-i686-py${{ matrix.python }}" | |
| path: wheels/ | |
| if-no-files-found: warn | |
| collect-wheels: | |
| name: "Collect all extended wheels" | |
| runs-on: ubuntu-latest | |
| if: always() | |
| needs: | |
| - build-linux-glibc-2_17 | |
| - build-linux-musl | |
| - build-linux-riscv64 | |
| - build-macos-universal2 | |
| - build-windows-i686 | |
| - build-cp37-linux | |
| steps: | |
| - name: Download all wheel artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: "ext-wheels-*" | |
| path: all-wheels/ | |
| merge-multiple: true | |
| - name: List collected wheels | |
| run: | | |
| echo "=== Extended wheel build results ===" | |
| find all-wheels/ -name "*.whl" | sort | tee wheel-list.txt | |
| echo "" | |
| echo "Total wheels: $(wc -l < wheel-list.txt)" | |
| - name: Log job outcomes | |
| run: | | |
| echo "Job results:" | |
| echo " glibc-2_17 : ${{ needs.build-linux-glibc-2_17.result }}" | |
| echo " musl : ${{ needs.build-linux-musl.result }}" | |
| echo " riscv64 : ${{ needs.build-linux-riscv64.result }}" | |
| echo " macos-uni2 : ${{ needs.build-macos-universal2.result }}" | |
| echo " windows-i686: ${{ needs.build-windows-i686.result }}" | |
| - name: Upload combined artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: "ext-wheels-ALL" | |
| path: all-wheels/ | |
| if-no-files-found: warn |