Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions .github/actions/build-wheel/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# 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.

# Composite action that builds a datafusion-python wheel with maturin.
# Centralises the abi3-vs-free-threaded argument logic so platform jobs
# stay short and changes to wheel-build flags happen in one place.

name: "Build wheel"
description: "Build datafusion-python wheel with maturin (abi3 or free-threaded)"

inputs:
target:
description: "Rust target triple (e.g. x86_64-unknown-linux-gnu). Required when manylinux is set; ignored for native builds."
required: false
default: ""
python-tag:
description: "abi3 (covers 3.10..3.14 GIL builds) or a free-threaded interpreter such as 3.13t / 3.14t"
required: true
build-mode:
description: "release or debug"
required: true
features:
description: "Comma-separated extra features (in addition to those implied by the python-tag)"
required: false
default: "substrait"
manylinux:
description: "manylinux tag for maturin-action (e.g. 2_28). Leave empty to use uv-run maturin natively."
required: false
default: ""
out-dir:
description: "Output directory for built wheels"
required: false
default: "dist"

outputs:
args:
description: "Computed maturin args (for debugging)"
value: ${{ steps.args.outputs.args }}

runs:
using: "composite"
steps:
- name: Compute maturin args
id: args
shell: bash
run: |
set -euo pipefail
FEATURES="${{ inputs.features }}"
TAG="${{ inputs.python-tag }}"
if [ "$TAG" = "abi3" ]; then
# Default features include the `abi3` cargo feature.
# One wheel covers Python 3.10..3.14 (GIL builds only).
BUILD_ARGS="--features ${FEATURES}"
else
# Free-threaded build: disable abi3, force mimalloc back in, pin interpreter.
BUILD_ARGS="--no-default-features --features mimalloc,${FEATURES} --interpreter python${TAG}"
fi
if [ "${{ inputs.build-mode }}" = "release" ]; then
BUILD_ARGS="--release --strip ${BUILD_ARGS}"
fi
BUILD_ARGS="${BUILD_ARGS} --out ${{ inputs.out-dir }}"
echo "args=${BUILD_ARGS}" >> "$GITHUB_OUTPUT"
echo "maturin args: ${BUILD_ARGS}"

- name: Build via maturin-action (manylinux container)
if: inputs.manylinux != ''
uses: PyO3/maturin-action@v1
with:
target: ${{ inputs.target }}
manylinux: ${{ inputs.manylinux }}
maturin-version: "1.8.1"
args: ${{ steps.args.outputs.args }}
rustup-components: rust-std

- name: Build via native maturin
if: inputs.manylinux == ''
shell: bash
# Use `uvx` so maturin is available even when `uv sync` was skipped
# (free-threaded matrix entries don't pre-populate the project venv).
run: uvx maturin@1.8.1 build ${{ steps.args.outputs.args }}
128 changes: 71 additions & 57 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,12 @@ jobs:
# ============================================
build-manylinux-x86_64:
needs: [generate-license, lint-rust, lint-python]
name: ManyLinux x86_64
name: ManyLinux x86_64 (${{ matrix.python-tag }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-tag: ["abi3", "3.13t", "3.14t"]
steps:
- uses: actions/checkout@v6

Expand All @@ -153,7 +157,7 @@ jobs:
- name: Cache Cargo
uses: Swatinem/rust-cache@v2
with:
key: ${{ inputs.build_mode }}
key: ${{ inputs.build_mode }}-${{ matrix.python-tag }}

- uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b
with:
Expand All @@ -172,25 +176,18 @@ jobs:
free -h
swapon --show

- name: Build (release mode)
uses: PyO3/maturin-action@v1
if: inputs.build_mode == 'release'
with:
target: x86_64-unknown-linux-gnu
manylinux: "2_28"
args: --release --strip --features protoc,substrait --out dist
rustup-components: rust-std

- name: Build (debug mode)
uses: PyO3/maturin-action@v1
if: inputs.build_mode == 'debug'
- name: Build wheel
uses: ./.github/actions/build-wheel
with:
target: x86_64-unknown-linux-gnu
python-tag: ${{ matrix.python-tag }}
build-mode: ${{ inputs.build_mode }}
features: "protoc,substrait"
manylinux: "2_28"
args: --features protoc,substrait --out dist
rustup-components: rust-std

# FFI test wheel only needs to be built once per platform; gate to abi3.
- name: Build FFI test library
if: matrix.python-tag == 'abi3'
uses: PyO3/maturin-action@v1
with:
target: x86_64-unknown-linux-gnu
Expand All @@ -202,10 +199,11 @@ jobs:
- name: Archive wheels
uses: actions/upload-artifact@v7
with:
name: dist-manylinux-x86_64
name: dist-manylinux-x86_64-${{ matrix.python-tag }}
path: dist/*

- name: Archive FFI test wheel
if: matrix.python-tag == 'abi3'
uses: actions/upload-artifact@v7
with:
name: test-ffi-manylinux-x86_64
Expand All @@ -216,8 +214,12 @@ jobs:
# ============================================
build-manylinux-aarch64:
needs: [generate-license, lint-rust, lint-python]
name: ManyLinux arm64
name: ManyLinux arm64 (${{ matrix.python-tag }})
runs-on: ubuntu-24.04-arm
strategy:
fail-fast: false
matrix:
python-tag: ["abi3", "3.13t", "3.14t"]
steps:
- uses: actions/checkout@v6

Expand All @@ -234,7 +236,7 @@ jobs:
- name: Cache Cargo
uses: Swatinem/rust-cache@v2
with:
key: ${{ inputs.build_mode }}
key: ${{ inputs.build_mode }}-${{ matrix.python-tag }}

- uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b
with:
Expand All @@ -253,43 +255,34 @@ jobs:
free -h
swapon --show

- name: Build (release mode)
uses: PyO3/maturin-action@v1
if: inputs.build_mode == 'release'
with:
target: aarch64-unknown-linux-gnu
manylinux: "2_28"
args: --release --strip --features protoc,substrait --out dist
rustup-components: rust-std

- name: Build (debug mode)
uses: PyO3/maturin-action@v1
if: inputs.build_mode == 'debug'
- name: Build wheel
uses: ./.github/actions/build-wheel
with:
target: aarch64-unknown-linux-gnu
python-tag: ${{ matrix.python-tag }}
build-mode: ${{ inputs.build_mode }}
features: "protoc,substrait"
manylinux: "2_28"
args: --features protoc,substrait --out dist
rustup-components: rust-std

- name: Archive wheels
uses: actions/upload-artifact@v7
if: inputs.build_mode == 'release'
with:
name: dist-manylinux-aarch64
name: dist-manylinux-aarch64-${{ matrix.python-tag }}
path: dist/*

# ============================================
# Build - macOS arm64 / Windows
# ============================================
build-python-mac-win:
needs: [generate-license, lint-rust, lint-python]
name: macOS arm64 & Windows
name: macOS arm64 & Windows (${{ matrix.python-tag }} / ${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
python-version: ["3.10"]
os: [macos-latest, windows-latest]
python-tag: ["abi3", "3.13t", "3.14t"]
steps:
- uses: actions/checkout@v6

Expand All @@ -305,7 +298,14 @@ jobs:
- name: Cache Cargo
uses: Swatinem/rust-cache@v2
with:
key: ${{ inputs.build_mode }}
key: ${{ inputs.build_mode }}-${{ matrix.python-tag }}

- name: Setup Python (free-threaded)
if: matrix.python-tag != 'abi3'
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-tag }}
freethreaded: true

- uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b
with:
Expand All @@ -318,22 +318,22 @@ jobs:
repo-token: ${{ secrets.GITHUB_TOKEN }}

- name: Install dependencies
if: matrix.python-tag == 'abi3'
run: uv sync --dev --no-install-package datafusion

# Run clippy BEFORE maturin so we can avoid rebuilding. The features must match
# exactly the features used by maturin. Linux maturin builds need to happen in a
# container so only run this for our mac runner.
# Clippy is interpreter-agnostic; run once per OS (against the abi3 entry)
# so the matrix doesn't pay the cost three times.
- name: Run Clippy
if: matrix.os != 'windows-latest'
if: matrix.os != 'windows-latest' && matrix.python-tag == 'abi3'
run: cargo clippy --no-deps --all-targets --features substrait -- -D warnings

- name: Build Python package (release mode)
if: inputs.build_mode == 'release'
run: uv run --no-project maturin build --release --strip --features substrait

- name: Build Python package (debug mode)
if: inputs.build_mode != 'release'
run: uv run --no-project maturin build --features substrait
- name: Build wheel
uses: ./.github/actions/build-wheel
with:
python-tag: ${{ matrix.python-tag }}
build-mode: ${{ inputs.build_mode }}
features: "substrait"
out-dir: "target/wheels"

- name: List Windows wheels
if: matrix.os == 'windows-latest'
Expand All @@ -350,7 +350,7 @@ jobs:
uses: actions/upload-artifact@v7
if: inputs.build_mode == 'release'
with:
name: dist-${{ matrix.os }}
name: dist-${{ matrix.os }}-${{ matrix.python-tag }}
path: target/wheels/*

# ============================================
Expand All @@ -359,11 +359,12 @@ jobs:
build-macos-x86_64:
if: inputs.build_mode == 'release'
needs: [generate-license, lint-rust, lint-python]
name: macOS x86_64 (${{ matrix.python-tag }})
runs-on: macos-15-intel
strategy:
fail-fast: false
matrix:
python-version: ["3.10"]
python-tag: ["abi3", "3.13t", "3.14t"]
steps:
- uses: actions/checkout@v6

Expand All @@ -379,7 +380,14 @@ jobs:
- name: Cache Cargo
uses: Swatinem/rust-cache@v2
with:
key: ${{ inputs.build_mode }}
key: ${{ inputs.build_mode }}-${{ matrix.python-tag }}

- name: Setup Python (free-threaded)
if: matrix.python-tag != 'abi3'
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-tag }}
freethreaded: true

- uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b
with:
Expand All @@ -392,19 +400,24 @@ jobs:
repo-token: ${{ secrets.GITHUB_TOKEN }}

- name: Install dependencies
if: matrix.python-tag == 'abi3'
run: uv sync --dev --no-install-package datafusion

- name: Build (release mode)
run: |
uv run --no-project maturin build --release --strip --features substrait
- name: Build wheel
uses: ./.github/actions/build-wheel
with:
python-tag: ${{ matrix.python-tag }}
build-mode: ${{ inputs.build_mode }}
features: "substrait"
out-dir: "target/wheels"

- name: List Mac wheels
run: find target/wheels/

- name: Archive wheels
uses: actions/upload-artifact@v7
with:
name: dist-macos-aarch64
name: dist-macos-aarch64-${{ matrix.python-tag }}
path: target/wheels/*

# ============================================
Expand Down Expand Up @@ -509,11 +522,12 @@ jobs:
with:
enable-cache: true

# Download the Linux wheel built in the previous job
# Download the Linux wheel built in the previous job.
# Docs only need the abi3 wheel — interpreter doesn't matter for sphinx.
- name: Download pre-built Linux wheel
uses: actions/download-artifact@v8
with:
name: dist-manylinux-x86_64
name: dist-manylinux-x86_64-abi3
path: wheels/

# Install from the pre-built wheels
Expand Down
Loading
Loading