Support Python 3.14 (and drop Python 3.9 and 3.10) (#142) #195
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: Python wheel package build and publish | |
| on: | |
| release: | |
| types: [published] | |
| # Enable Run Workflow button in GitHub UI | |
| workflow_dispatch: | |
| inputs: | |
| # Manual dispatch allows optional upload of wheels to PyPI | |
| upload_dest: | |
| type: choice | |
| description: Upload wheels to | |
| options: | |
| - No Upload | |
| - PyPI | |
| - Test PyPI | |
| push: | |
| branches: [ main ] | |
| pull_request: | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| # For PyPI Trusted Publisher | |
| id-token: write | |
| jobs: | |
| build_sdist: | |
| name: Build SDist | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Build SDist | |
| run: pipx run build --sdist | |
| - name: Check metadata | |
| run: pipx run twine check dist/* | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: sdist | |
| path: dist/*.tar.gz | |
| build_wheels: | |
| name: Wheels - ${{ matrix.cibw_archs }} ${{ matrix.arch_note}} - ${{ matrix.os }} | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: windows-latest | |
| cibw_archs: "auto64" | |
| # Linux x86_64 manylinux | |
| - os: ubuntu-latest | |
| cibw_archs: "x86_64" | |
| # manylinux_2_28 uses dnf (not yum). libffi-devel needed for Python 3.12+ cffi. | |
| cibw_before_build_linux: "dnf install -y libffi-devel || yum install -y libffi-devel || true" | |
| # skip musllinux | |
| cibw_skip: "*musl*" | |
| # Linux x86_64 musllinux | |
| # Separate runner for a Musl build of graphblas. The glibc build is not guaranteed to be compatible. | |
| - os: ubuntu-latest | |
| cibw_archs: "x86_64" | |
| arch_note: "musl" | |
| # skip manylinux (built elsewhere) | |
| cibw_skip: "*many*" | |
| # Linux aarch64 — native ARM runner (no QEMU emulation needed) | |
| - os: ubuntu-24.04-arm | |
| cibw_archs: "auto" | |
| # numpy wheels not available for aarch64 musllinux | |
| cibw_skip: "*musl*" | |
| # macOS x86_64 — macos-15-intel is the last Intel macOS runner. | |
| # libomp from Homebrew determines the minimum supported macOS version. | |
| - os: macos-15-intel | |
| cibw_archs: "x86_64" | |
| # macOS Apple Silicon | |
| # Keep runner version as old as possible: libomp determines minimum macOS version. | |
| - os: macos-14 | |
| cibw_archs: "arm64" | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Setup for testing | |
| if: github.event_name == 'push' || github.event_name == 'pull_request' | |
| # Ask suitesparse.sh to compile in the fastest way possible and provide a GB version to build | |
| run: | | |
| echo "SUITESPARSE_FASTEST_BUILD=1" >> $GITHUB_ENV | |
| shell: bash | |
| - name: Setup GraphBLAS version from GB_VERSION.txt | |
| # Use GraphBLAS version specified in GB_VERSION.txt unless specified in a git tag (next workflow step). | |
| # Git tag method required for uploads to PyPI. | |
| if: github.event_name != 'release' && github.event.inputs.upload_dest != 'PyPI' | |
| run: echo "GB_VERSION_REF=refs/tags/$(cat GB_VERSION.txt).0" >> $GITHUB_ENV | |
| shell: bash | |
| - name: Setup GraphBLAS version from git tag | |
| if: ${{ startsWith(github.ref, 'refs/tags/') }} | |
| # If this is a tagged ref, like a release, then use the tag for the graphblas version | |
| run: echo "GB_VERSION_REF=${{ github.ref }}" >> $GITHUB_ENV | |
| shell: bash | |
| - name: Install tools (macOS) | |
| if: contains(matrix.os, 'macos') | |
| # Install coreutils which includes `nproc` used by `make -j` in suitesparse.sh | |
| # | |
| # Explicitly install libomp to be clear about the dependency. | |
| # | |
| # libomp determines the minimum macOS version that we can build for | |
| run: | | |
| brew fetch --retry coreutils && brew install coreutils | |
| brew fetch --retry libomp && brew reinstall libomp | |
| echo MACOSX_DEPLOYMENT_TARGET=$(otool -l $(brew --prefix libomp)/lib/libomp.dylib | grep minos | awk '{print $2}') >> $GITHUB_ENV | |
| - uses: pypa/cibuildwheel@v3.4 | |
| with: | |
| output-dir: wheelhouse | |
| env: | |
| # very verbose | |
| CIBW_BUILD_VERBOSITY: 3 | |
| # cibuildwheel v3: PyPy no longer built by default; enable explicitly | |
| # Note: cpython-freethreading not enabled because cffi doesn't support it yet | |
| CIBW_ENABLE: "pypy" | |
| # Build SuiteSparse | |
| CIBW_BEFORE_ALL: bash suitesparse.sh ${{ env.GB_VERSION_REF }} | |
| CIBW_BEFORE_BUILD_LINUX: ${{ matrix.cibw_before_build_linux }} | |
| CIBW_ENVIRONMENT_PASS_LINUX: SUITESPARSE_FASTEST_BUILD | |
| # CMAKE_GNUtoMS=ON asks suitesparse.sh to build libraries in MSVC style on Windows. | |
| CIBW_ENVIRONMENT_WINDOWS: CMAKE_GNUtoMS=ON GRAPHBLAS_PREFIX="C:/GraphBLAS" | |
| # macOS libomp requires special configs. BREW_LIBOMP=1 asks suitesparse.sh to include them. | |
| # SUITESPARSE_MACOS_ARCH asks to build a particular architecture. Either x86 or arm64. | |
| CIBW_ENVIRONMENT_MACOS: BREW_LIBOMP="1" SUITESPARSE_MACOS_ARCH=${{ matrix.cibw_archs }} | |
| # Uncomment to only build CPython wheels | |
| # CIBW_BUILD: "cp*" | |
| # Architectures to build specified in matrix | |
| CIBW_ARCHS: ${{ matrix.cibw_archs }} | |
| CIBW_SKIP: "${{ matrix.cibw_skip }}" | |
| # Use delvewheel on Windows. | |
| # This copies graphblas.dll into the wheel. "repair" in cibuildwheel parlance includes copying any shared | |
| # libraries from the build host into the wheel to make the wheel self-contained. | |
| # Cibuildwheel includes tools for this for Linux and macOS, and they recommend delvewheel for Windows. | |
| # Note: Currently using a workaround: --no-mangle instead of stripping graphblas.dll | |
| # see https://github.com/adang1345/delvewheel/issues/33 | |
| CIBW_BEFORE_BUILD_WINDOWS: "pip install delvewheel" | |
| CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: "delvewheel repair --add-path \"C:\\GraphBLAS\\bin\" --no-mangle \"libgomp-1.dll;libgcc_s_seh-1.dll\" -w {dest_dir} {wheel}" | |
| # make cibuildwheel install test dependencies from pyproject.toml | |
| CIBW_TEST_EXTRAS: "test" | |
| # run tests | |
| CIBW_TEST_COMMAND: "pytest --pyargs suitesparse_graphblas -s -k test_print_jit_config && pytest -v --pyargs suitesparse_graphblas" | |
| CIBW_TEST_SKIP: ${{ matrix.cibw_test_skip }} | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: wheels-${{ matrix.os }}-${{ matrix.cibw_archs }}${{ matrix.arch_note}} | |
| path: wheelhouse/*.whl | |
| if-no-files-found: error | |
| upload_all: | |
| name: Upload to PyPI | |
| needs: [build_wheels, build_sdist] | |
| runs-on: ubuntu-latest | |
| # only upload releases to PyPI | |
| if: github.repository == 'GraphBLAS/python-suitesparse-graphblas' && ((github.event_name == 'release' && github.event.action == 'published') || (github.event_name == 'workflow_dispatch' && github.event.inputs.upload_dest != 'No Upload')) | |
| steps: | |
| - uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.x" | |
| - uses: actions/download-artifact@v5 | |
| with: | |
| path: dist | |
| merge-multiple: true | |
| # Upload to PyPI | |
| - uses: pypa/gh-action-pypi-publish@v1.13.0 | |
| name: Upload to PyPI | |
| if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.upload_dest == 'PyPI') | |
| with: | |
| # PyPI does not allow replacing a file. Without this flag the entire action fails if even a single duplicate exists. | |
| skip-existing: true | |
| verbose: true | |
| password: ${{ secrets.PYPI_TOKEN }} | |
| # Upload to Test PyPI | |
| - uses: pypa/gh-action-pypi-publish@v1.13.0 | |
| name: Upload to Test PyPI | |
| if: github.event_name == 'workflow_dispatch' && github.event.inputs.upload_dest == 'Test PyPI' | |
| with: | |
| # PyPI does not allow replacing a file. Without this flag the entire action fails if even a single duplicate exists. | |
| skip-existing: true | |
| verbose: true | |
| repository-url: https://test.pypi.org/legacy/ |