Skip to content

🔧 Build uv-ffi Wheels (Extended) #59

🔧 Build uv-ffi Wheels (Extended)

🔧 Build uv-ffi Wheels (Extended) #59

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