blob: 04a3ba9ce7b43b7ec4fc2cfbf94addbf72c34dda [file]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# .github/workflows/_build_python_wheels.yml
name: _build_python_wheels
on:
workflow_call:
inputs:
version:
type: string
required: false
default: ""
upload_artifacts:
type: boolean
required: false
default: true
use_latest_ci:
type: boolean
required: false
default: true
description: "Use latest CI configuration and scripts from master branch"
commit:
type: string
required: false
default: ""
description: "Specific commit to checkout for building wheels"
outputs:
artifact_name:
description: "Name of the uploaded artifact containing wheels"
value: ${{ jobs.collect.outputs.artifact_name }}
permissions:
contents: read
jobs:
linux:
runs-on: ${{ matrix.runner }}
strategy:
matrix:
include:
- target: x86_64
runner: ubuntu-latest
manylinux: 2_34
- target: x86_64
runner: ubuntu-latest
manylinux: musllinux_1_2
- target: aarch64
runner: ubuntu-24.04-arm
manylinux: auto
- target: aarch64
runner: ubuntu-24.04-arm
manylinux: musllinux_1_2
steps:
- name: Download latest copy script from master
if: inputs.use_latest_ci
run: |
curl -fsSL --retry 5 --retry-all-errors "https://raw.githubusercontent.com/${{ github.repository }}/master/scripts/copy-latest-from-master.sh" \
-o /tmp/copy-latest-from-master.sh
chmod +x /tmp/copy-latest-from-master.sh
- uses: actions/checkout@v7.0.1
with:
ref: ${{ inputs.commit }}
- name: Save and apply latest CI from master
if: inputs.use_latest_ci
run: |
/tmp/copy-latest-from-master.sh save .github scripts
/tmp/copy-latest-from-master.sh apply
- name: Setup Python
uses: actions/setup-python@v7.0.0
with:
python-version: "3.10"
- name: Setup Rust with cache
uses: ./.github/actions/utils/setup-rust-with-cache
- name: Generate third-party license manifest
run: |
TARGET="$(uname -m)-unknown-linux-musl"
tarball="$(mktemp)"
# Staged to a file because curl --retry cannot rewind a pipe.
curl -sSfL --retry 5 --retry-all-errors -o "$tarball" \
"https://github.com/EmbarkStudios/cargo-about/releases/download/0.9.0/cargo-about-0.9.0-${TARGET}.tar.gz"
tar -xzf "$tarball" -C "$HOME/.cargo/bin" --strip-components=1 "cargo-about-0.9.0-${TARGET}/cargo-about"
rm -f "$tarball"
./scripts/ci/third-party-licenses.sh --generate --manifest foreign/python/Cargo.toml --output foreign/python/LICENSE-binary
- name: Build wheels
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.target }}
working-directory: foreign/python
before-script-linux: |
# manylinux_2_28 uses yum, not dnf
if command -v dnf &> /dev/null; then
dnf install -y perl-IPC-Cmd
elif command -v yum &> /dev/null; then
yum install -y perl-IPC-Cmd
fi
manylinux: ${{ matrix.manylinux }}
args: --release --out dist --interpreter python3.10 python3.11 python3.12 python3.13
sccache: "true"
- name: Upload wheels
if: inputs.upload_artifacts
uses: actions/upload-artifact@v7
with:
name: wheels-linux-${{ matrix.target }}-${{ matrix.manylinux }}
path: foreign/python/dist
retention-days: 7
macos:
runs-on: ${{ matrix.target == 'x86_64' && 'macos-15-intel' || 'macos-15' }}
strategy:
matrix:
target: [x86_64, aarch64]
steps:
- name: Download latest copy script from master
if: inputs.use_latest_ci
run: |
curl -fsSL --retry 5 --retry-all-errors "https://raw.githubusercontent.com/${{ github.repository }}/master/scripts/copy-latest-from-master.sh" \
-o /tmp/copy-latest-from-master.sh
chmod +x /tmp/copy-latest-from-master.sh
- uses: actions/checkout@v7.0.1
with:
ref: ${{ inputs.commit }}
- name: Save and apply latest CI from master
if: inputs.use_latest_ci
run: |
/tmp/copy-latest-from-master.sh save .github scripts
/tmp/copy-latest-from-master.sh apply
- name: Setup Python
uses: actions/setup-python@v7.0.0
with:
python-version: "3.10"
- name: Setup Rust with cache
uses: ./.github/actions/utils/setup-rust-with-cache
- name: Generate third-party license manifest
run: |
# Embark Studios releases an aarch64-apple-darwin tarball but
# NOT x86_64-apple-darwin (404 as of 0.9.0). Intel macOS
# falls back to compiling cargo-about from source.
ARCH="$(uname -m | sed 's/arm64/aarch64/')"
if [ "$ARCH" = "aarch64" ]; then
TARGET="aarch64-apple-darwin"
tarball="$(mktemp)"
curl -sSfL --retry 5 --retry-all-errors -o "$tarball" \
"https://github.com/EmbarkStudios/cargo-about/releases/download/0.9.0/cargo-about-0.9.0-${TARGET}.tar.gz"
tar -xzf "$tarball" -C "$HOME/.cargo/bin" --strip-components=1 "cargo-about-0.9.0-${TARGET}/cargo-about"
rm -f "$tarball"
else
cargo install cargo-about --locked --version 0.9.0 --features cli
fi
./scripts/ci/third-party-licenses.sh --generate --manifest foreign/python/Cargo.toml --output foreign/python/LICENSE-binary
- name: Build wheels
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.target }}
working-directory: foreign/python
args: --release --out dist --interpreter python3.10 python3.11 python3.12 python3.13
sccache: "true"
- name: Upload wheels
if: inputs.upload_artifacts
uses: actions/upload-artifact@v7
with:
name: wheels-macos-${{ matrix.target }}
path: foreign/python/dist
retention-days: 7
windows:
runs-on: windows-latest
strategy:
fail-fast: false
# Windows exposes no `python3.X` on PATH and maturin's py-launcher
# fallback does not see setup-python tool-cache installs, so one
# job cannot target several interpreters. One job per version
# instead, each falling through to maturin's default of the
# interpreter on PATH. 3.10 is Windows-only excluded, see
# WHEEL_MATRIX_SKIP in scripts/ci/sync-python-interpreter-version.sh.
matrix:
python-version: ["3.11", "3.12", "3.13"]
steps:
- name: Download latest copy script from master
if: inputs.use_latest_ci
shell: bash
run: |
curl -fsSL --retry 5 --retry-all-errors "https://raw.githubusercontent.com/${{ github.repository }}/master/scripts/copy-latest-from-master.sh" \
-o /tmp/copy-latest-from-master.sh
chmod +x /tmp/copy-latest-from-master.sh
- uses: actions/checkout@v7.0.1
with:
ref: ${{ inputs.commit }}
- name: Save and apply latest CI from master
if: inputs.use_latest_ci
shell: bash
run: |
/tmp/copy-latest-from-master.sh save .github scripts
/tmp/copy-latest-from-master.sh apply
- name: Setup Python
uses: actions/setup-python@v7.0.0
with:
python-version: ${{ matrix.python-version }}
architecture: x64
- name: Setup Rust with cache
uses: ./.github/actions/utils/setup-rust-with-cache
with:
shared-key: python-wheels-windows
# aws-lc-sys, reached through quinn's default rustls provider,
# assembles its x86_64 crypto with NASM. The runner image ships
# CMake but not NASM, and Chocolatey leaves it off PATH.
- name: Install NASM
shell: pwsh
run: |
$nasmDir = "C:\Program Files\NASM"
choco install nasm --no-progress -y
Add-Content -Path $env:GITHUB_PATH -Value $nasmDir
# GITHUB_PATH only reaches later steps, so prove the install
# landed where expected here rather than in a linker error.
$env:PATH = "$nasmDir;$env:PATH"
nasm -v
- name: Generate third-party license manifest
shell: bash
run: |
TARGET="x86_64-pc-windows-msvc"
# HOME is Windows-style here, which MSYS tar mishandles as -C.
CARGO_BIN="$(cygpath -u "$HOME")/.cargo/bin"
tarball="$(cygpath -u "$(mktemp)")"
curl -sSfL --retry 5 --retry-all-errors -o "$tarball" \
"https://github.com/EmbarkStudios/cargo-about/releases/download/0.9.0/cargo-about-0.9.0-${TARGET}.tar.gz"
tar -xzf "$tarball" -C "$CARGO_BIN" --strip-components=1 "cargo-about-0.9.0-${TARGET}/cargo-about.exe"
rm -f "$tarball"
./scripts/ci/third-party-licenses.sh --generate --manifest foreign/python/Cargo.toml --output foreign/python/LICENSE-binary
- name: Build wheels
uses: PyO3/maturin-action@v1
with:
target: x86_64
working-directory: foreign/python
args: --release --out dist
sccache: "true"
- name: Upload wheels
if: inputs.upload_artifacts
uses: actions/upload-artifact@v7
with:
name: wheels-windows-x64-py${{ matrix.python-version }}
path: foreign/python/dist
retention-days: 7
sdist:
runs-on: ubuntu-latest
steps:
- name: Download latest copy script from master
if: inputs.use_latest_ci
run: |
curl -fsSL --retry 5 --retry-all-errors "https://raw.githubusercontent.com/${{ github.repository }}/master/scripts/copy-latest-from-master.sh" \
-o /tmp/copy-latest-from-master.sh
chmod +x /tmp/copy-latest-from-master.sh
- uses: actions/checkout@v7.0.1
with:
ref: ${{ inputs.commit }}
- name: Save and apply latest CI from master
if: inputs.use_latest_ci
run: |
/tmp/copy-latest-from-master.sh save .github scripts
/tmp/copy-latest-from-master.sh apply
- name: Build sdist
uses: PyO3/maturin-action@v1
with:
command: sdist
working-directory: foreign/python
args: --out dist
- name: Upload sdist
if: inputs.upload_artifacts
uses: actions/upload-artifact@v7
with:
name: wheels-sdist
path: foreign/python/dist
retention-days: 7
collect:
name: Collect all wheels
needs: [linux, macos, windows, sdist]
if: ${{ !cancelled() }}
runs-on: ubuntu-latest
outputs:
artifact_name: ${{ steps.output.outputs.artifact_name }}
steps:
- name: Download all wheels
uses: actions/download-artifact@v8
with:
pattern: wheels-*
merge-multiple: true
path: dist
- name: List wheels
run: |
echo "## 📦 Built Python Wheels" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "| Platform | Architecture | File |" >> "$GITHUB_STEP_SUMMARY"
echo "|----------|-------------|------|" >> "$GITHUB_STEP_SUMMARY"
for wheel in dist/*.whl; do
filename=$(basename "$wheel")
if [[ "$filename" == *"linux"* ]]; then platform="Linux"
elif [[ "$filename" == *"macosx"* ]]; then platform="macOS"
elif [[ "$filename" == *"win"* ]]; then platform="Windows"
else platform="Universal"; fi
if [[ "$filename" == *"x86_64"* ]] || [[ "$filename" == *"amd64"* ]]; then arch="x86_64"
elif [[ "$filename" == *"aarch64"* ]] || [[ "$filename" == *"arm64"* ]]; then arch="arm64"
elif [[ "$filename" == *"i686"* ]]; then arch="x86"
else arch="any"; fi
echo "| $platform | $arch | \`$filename\` |" >> "$GITHUB_STEP_SUMMARY"
done
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "**Total wheels built:** $(ls -1 dist/*.whl | wc -l)" >> "$GITHUB_STEP_SUMMARY"
- name: Upload combined artifact
uses: actions/upload-artifact@v7
with:
name: python-wheels-all
path: dist
retention-days: 30
- id: output
run: echo "artifact_name=python-wheels-all" >> "$GITHUB_OUTPUT"
# Runs last so the artifact and the summary survive for inspection
# when a leg is missing. A failed build job uploads nothing, so a
# short count means one did not contribute. These counts are not
# derived from the matrices above - update them together.
- name: Verify wheel coverage
run: |
EXPECTED_LINUX=16 # 4 build variants x 4 interpreters
EXPECTED_MACOS=8 # 2 targets x 4 interpreters
EXPECTED_WINDOWS=3 # 1 interpreter per job, 3.10 excluded
EXPECTED_SDIST=1
missing=0
check() {
local label="$1" pattern="$2" expected="$3" actual
actual=$(find dist -maxdepth 1 -name "$pattern" | wc -l)
if [ "$actual" -ne "$expected" ]; then
echo "::error::${label}: expected ${expected}, found ${actual}"
missing=1
else
echo "${label}: ${actual}"
fi
}
check Linux '*linux*.whl' "$EXPECTED_LINUX"
check macOS '*macosx*.whl' "$EXPECTED_MACOS"
check Windows '*win*.whl' "$EXPECTED_WINDOWS"
check sdist '*.tar.gz' "$EXPECTED_SDIST"
exit "$missing"