Skip to content

Wheel builder

Wheel builder #9596

Workflow file for this run

# Workflow to build and test wheels.
# To work on the wheel building infrastructure on a fork, comment out:
#
# if: github.repository == 'scipy/scipy'
#
# in the get_commit_message job include [wheel build] in your commit
# message to trigger the build. All files related to wheel building are located
# at tools/wheels/
name: Wheel builder
on:
schedule:
# ┌───────────── minute (0 - 59)
# │ ┌───────────── hour (0 - 23)
# │ │ ┌───────────── day of the month (1 - 31)
# │ │ │ ┌───────────── month (1 - 12 or JAN-DEC)
# │ │ │ │ ┌───────────── day of the week (0 - 6 or SUN-SAT)
# │ │ │ │ │
- cron: "9 9 * * *"
push:
branches:
- maintenance/**
pull_request:
branches:
- main
- maintenance/**
workflow_dispatch:
permissions:
contents: read # to fetch code (actions/checkout)
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
get_commit_message:
name: Get commit message
runs-on: ubuntu-latest
if: github.repository == 'scipy/scipy'
outputs:
message: ${{ steps.commit_message.outputs.message }}
steps:
- name: Checkout scipy
uses: actions/checkout@v4.1.1
# Gets the correct commit message for pull request
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Get commit message
id: commit_message
run: |
set -xe
COMMIT_MSG=$(git log --no-merges -1)
RUN="0"
if [[ "$COMMIT_MSG" == *"[wheel build]"* ]]; then
RUN="1"
fi
echo "message=$RUN" >> $GITHUB_OUTPUT
echo github.ref ${{ github.ref }}
build_wheels:
name: Wheel, ${{ matrix.python[0] }}-${{ matrix.buildplat[1] }}
${{ matrix.buildplat[2] }} ${{ matrix.buildplat[3] }}
${{ matrix.buildplat[4] }}
needs: get_commit_message
if: >-
contains(needs.get_commit_message.outputs.message, '1') ||
github.event_name == 'schedule' ||
github.event_name == 'workflow_dispatch'
runs-on: ${{ matrix.buildplat[0] }}
strategy:
# Ensure that a wheel builder finishes even if another fails
fail-fast: false
matrix:
# Github Actions doesn't support pairing matrix values together, let's improvise
# https://github.com/github/feedback/discussions/7835#discussioncomment-1769026
buildplat:
# should also be able to do multi-archs on a single entry, e.g.
# [windows-2019, win*, "AMD64 x86"]. However, those two require a different compiler setup
# so easier to separate out here.
- [ubuntu-22.04, manylinux, x86_64, "", ""]
- [ubuntu-22.04, musllinux, x86_64, "", ""]
- [macos-12, macosx, x86_64, openblas, "10.9"]
- [macos-13, macosx, x86_64, accelerate, "14.0"]
- [macos-14, macosx, arm64, openblas, "12.0"]
- [macos-14, macosx, arm64, accelerate, "14.0"]
- [windows-2019, win, AMD64, "", ""]
python: [["cp310", "3.10"], ["cp311", "3.11"], ["cp312", "3.12"]]
# python[0] is used to specify the python versions made by cibuildwheel
env:
IS_32_BIT: ${{ matrix.buildplat[2] == 'x86' }}
# upload to staging if it's a push to a maintenance branch and the last
# commit message contains '[wheel build]'
IS_PUSH: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/heads/maintenance') && contains(needs.get_commit_message.outputs.message, '1') }}
IS_SCHEDULE_DISPATCH: ${{ github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }}
steps:
- name: Checkout scipy
uses: actions/checkout@v4.1.1
with:
submodules: true
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: 3.11
- name: win_amd64 - install rtools
run: |
# mingw-w64
choco install rtools -y --no-progress --force --version=4.0.0.20220206
echo "c:\rtools40\ucrt64\bin;" >> $env:GITHUB_PATH
if: ${{ runner.os == 'Windows' && env.IS_32_BIT == 'false' }}
- name: Setup macOS
if: startsWith( matrix.buildplat[0], 'macos-' )
run: |
if [[ ${{ matrix.buildplat[3] }} == 'accelerate' ]]; then
echo CIBW_CONFIG_SETTINGS=\"setup-args=-Dblas=accelerate\" >> "$GITHUB_ENV"
# Always use preinstalled gfortran for Accelerate builds
ln -s $(which gfortran-13) gfortran
export PATH=$PWD:$PATH
echo "PATH=$PATH" >> "$GITHUB_ENV"
LIB_PATH=$(dirname $(gfortran --print-file-name libgfortran.dylib))
fi
# Add libraries installed by cibw_before_build_macos.sh to path
if [[ ${{ matrix.buildplat[2] }} == 'arm64' ]]; then
LIB_PATH=$LIB_PATH:/opt/arm64-builds/lib
else
LIB_PATH=$LIB_PATH:/usr/local/lib
fi
if [[ ${{ matrix.buildplat[4] }} == '10.9' ]]; then
# Newest version of Xcode that supports macOS 10.9
XCODE_VER='13.4.1'
else
XCODE_VER='15.2'
fi
CIBW="sudo xcode-select -s /Applications/Xcode_${XCODE_VER}.app"
echo "CIBW_BEFORE_ALL=$CIBW" >> $GITHUB_ENV
# setting SDKROOT necessary when using the gfortran compiler
# installed in cibw_before_build_macos.sh
sudo xcode-select -s /Applications/Xcode_${XCODE_VER}.app
CIBW="MACOSX_DEPLOYMENT_TARGET=${{ matrix.buildplat[4] }}\
LD_LIBRARY_PATH=$LIB_PATH:$LD_LIBRARY_PATH\
SDKROOT=$(xcrun --sdk macosx --show-sdk-path)\
PIP_PRE=1\
PIP_NO_BUILD_ISOLATION=false\
PKG_CONFIG_PATH=$LIB_PATH/pkgconfig\
PIP_EXTRA_INDEX_URL=https://pypi.anaconda.org/scientific-python-nightly-wheels/simple"
echo "CIBW_ENVIRONMENT_MACOS=$CIBW" >> "$GITHUB_ENV"
echo "REPAIR_PATH=$LIB_PATH" >> "$GITHUB_ENV"
GFORTRAN_LIB="\$(dirname \$(gfortran --print-file-name libgfortran.dylib))"
CIBW="DYLD_LIBRARY_PATH=$GFORTRAN_LIB:$LIB_PATH delocate-listdeps {wheel} &&\
DYLD_LIBRARY_PATH=$GFORTRAN_LIB:$LIB_PATH delocate-wheel --require-archs \
{delocate_archs} -w {dest_dir} {wheel}"
# Rename x86 Accelerate wheel to test on macOS 13 runner
if [[ ${{ matrix.buildplat[0] }} == 'macos-13' && ${{ matrix.buildplat[4] }} == '14.0' ]]; then
CIBW+=" && mv {dest_dir}/\$(basename {wheel}) \
{dest_dir}/\$(echo \$(basename {wheel}) | sed 's/14_0/13_0/')"
fi
echo "CIBW_REPAIR_WHEEL_COMMAND_MACOS=$CIBW" >> "$GITHUB_ENV"
- name: Build wheels
uses: pypa/cibuildwheel@v2.17.0
env:
CIBW_BUILD: ${{ matrix.python[0] }}-${{ matrix.buildplat[1] }}*
CIBW_ARCHS: ${{ matrix.buildplat[2] }}
CIBW_ENVIRONMENT_PASS_LINUX: RUNNER_OS
CIBW_PRERELEASE_PYTHONS: True
# TODO remove the CIBW_BEFORE_BUILD_* lines once there are
# numpy2.0 wheels available on PyPI. Also remove/comment out the
# PIP_NO_BUILD_ISOLATION and PIP_EXTRA_INDEX_URL from CIBW_ENVIRONMENT
# (also for _MACOS and _WINDOWS below)
CIBW_BEFORE_BUILD_LINUX: "pip install numpy>=2.0.0.dev0 meson-python cython pythran pybind11 ninja; bash {project}/tools/wheels/cibw_before_build_linux.sh {project}"
CIBW_BEFORE_BUILD_WINDOWS: "pip install numpy>=2.0.0.dev0 meson-python cython pythran pybind11 ninja && bash {project}/tools/wheels/cibw_before_build_win.sh {project}"
CIBW_BEFORE_BUILD_MACOS: "pip install numpy>=2.0.0.dev0 meson-python cython pythran pybind11 ninja; bash {project}/tools/wheels/cibw_before_build_macos.sh {project}"
# Allow pip to find install nightly wheels if necessary
# Setting PIP_NO_BUILD_ISOLATION=false makes pip use build-isolation.
CIBW_ENVIRONMENT: "PIP_NO_BUILD_ISOLATION=false PIP_PRE=1 PIP_EXTRA_INDEX_URL=https://pypi.anaconda.org/scientific-python-nightly-wheels/simple"
CIBW_ENVIRONMENT_WINDOWS: >
PKG_CONFIG_PATH=c:/opt/64/lib/pkgconfig
PIP_PRE=1
PIP_EXTRA_INDEX_URL=https://pypi.anaconda.org/scientific-python-nightly-wheels/simple
PIP_NO_BUILD_ISOLATION=false
- name: Rename after test (macOS x86 Accelerate only)
# Rename x86 Accelerate wheel back so it targets macOS >= 14
if: matrix.buildplat[0] == 'macos-13' && matrix.buildplat[4] == '14.0'
run: |
mv ./wheelhouse/*.whl $(find ./wheelhouse -type f -name '*.whl' | sed 's/13_0/14_0/')
- uses: actions/upload-artifact@v4
with:
path: ./wheelhouse/*.whl
name: ${{ matrix.python[0] }}-${{ matrix.buildplat[1] }}
${{ matrix.buildplat[2] }} ${{ matrix.buildplat[3] }}
${{ matrix.buildplat[4] }}
- uses: conda-incubator/setup-miniconda@v3
with:
# for installation of anaconda-client, required for upload to
# anaconda.org
# default (and activated) environment name is test
# Note that this step is *after* specific pythons have been used to
# build and test the wheel
auto-update-conda: true
python-version: "3.10"
miniconda-version: "latest"
- name: Upload wheels
if: success()
shell: bash -el {0}
# see https://github.com/marketplace/actions/setup-miniconda for why
# `-el {0}` is required.
env:
SCIPY_STAGING_UPLOAD_TOKEN: ${{ secrets.SCIPY_STAGING_UPLOAD_TOKEN }}
SCIPY_NIGHTLY_UPLOAD_TOKEN: ${{ secrets.SCIPY_NIGHTLY_UPLOAD_TOKEN }}
run: |
conda install -y anaconda-client
source tools/wheels/upload_wheels.sh
set_upload_vars
# For cron jobs (restricted to main branch) or "Run workflow" trigger
# an upload to:
#
# https://anaconda.org/scientific-python-nightly-wheels/scipy
#
# Pushes to a maintenance branch that contain '[wheel build]' will
# cause wheels to be built and uploaded to:
#
# https://anaconda.org/multibuild-wheels-staging/scipy
#
# The tokens were originally generated at anaconda.org
upload_wheels