Repository navigation
[WIP] Build free-threaded (cp313t/cp314t) wheels and declare GIL-free support #112
Workflow file for this run
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: Build and publish | |
| on: | |
| push: | |
| branches: | |
| - main | |
| tags: | |
| - 'v*' | |
| pull_request: | |
| workflow_dispatch: | |
| inputs: | |
| publish: | |
| description: 'Publish to PyPI (testpypi, pypi, or none)' | |
| required: false | |
| default: 'none' | |
| type: choice | |
| options: | |
| - none | |
| - testpypi | |
| - pypi | |
| # Only cancels-in-progress on PRs (head_ref only defined in PR, fallback run_id always unique) | |
| concurrency: | |
| group: ${{ github.head_ref || github.run_id }} | |
| cancel-in-progress: true | |
| jobs: | |
| build_wheels: | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ ubuntu-22.04, macos-14, windows-2022 ] | |
| python-version: [ "3.11", "3.12", "3.13", "3.14" ] | |
| # "" builds the default interpreter, "t" the free-threaded one. | |
| abi: [ "", "t" ] | |
| exclude: | |
| # Free threading does not exist before 3.13: cp311t and cp312t are | |
| # not build identifiers at all, so cibuildwheel would select nothing | |
| # and the job would fail on an empty wheelhouse. | |
| - { python-version: "3.11", abi: "t" } | |
| - { python-version: "3.12", abi: "t" } | |
| # NumPy ships no cp313t wheels from 2.5 onwards (2.4.6 is the last | |
| # release with one), so a 3.13t build would compile NumPy from | |
| # source: that timed out under QEMU and failed outright on macOS. | |
| - { python-version: "3.13", abi: "t" } | |
| env: | |
| PYTHON_VERSION: ${{ matrix.python-version }} | |
| steps: | |
| - uses: actions/checkout@v5 | |
| with: | |
| submodules: recursive | |
| - uses: actions/setup-python@v6 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| allow-prereleases: true | |
| - name: Set Python subversion | |
| shell: bash | |
| run: | | |
| echo "PYTHON_SUBVERSION=$(echo $PYTHON_VERSION | cut -c 3-)" >> $GITHUB_ENV | |
| - name: Set up QEMU | |
| if: runner.os == 'Linux' | |
| uses: docker/setup-qemu-action@v3 | |
| with: | |
| platforms: all | |
| - name: Build wheels | |
| uses: pypa/cibuildwheel@v3.3.0 | |
| env: | |
| # No CIBW_ENABLE needed: cibuildwheel 3.x gates only cp313t behind | |
| # cpython-freethreading, and cp314t is selectable by default. | |
| CIBW_BUILD: "cp3${{ env.PYTHON_SUBVERSION }}${{ matrix.abi }}-*" | |
| CIBW_SKIP: "*-win32 *-manylinux_i686 *-musllinux*" | |
| CIBW_ARCHS_MACOS: "x86_64 universal2" | |
| CIBW_ARCHS_LINUX: "auto aarch64" | |
| CIBW_BEFORE_BUILD_LINUX: "yum install -y openblas-devel || apt-get install -y libopenblas-dev" | |
| CIBW_BEFORE_BUILD_WINDOWS: "pip install delvewheel && vcpkg install openblas:x64-windows" | |
| CIBW_ENVIRONMENT_WINDOWS: "CMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" | |
| # Bundle openblas.dll into the wheel (auditwheel/delocate handle this on Linux/macOS) | |
| CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: "delvewheel repair -w {dest_dir} {wheel} --add-path C:/vcpkg/installed/x64-windows/bin" | |
| # On free-threaded builds also assert the import did not re-enable the | |
| # GIL, i.e. the extension really declares Py_MOD_GIL_NOT_USED. | |
| CIBW_TEST_COMMAND: python -c "import sys, sysconfig, sparsediffpy; ft = bool(sysconfig.get_config_var('Py_GIL_DISABLED')); assert not ft or not sys._is_gil_enabled(), 'GIL re-enabled on import'; print('freethreaded_build=', ft)" | |
| - name: Check wheels | |
| shell: bash | |
| run: | | |
| python -m pip install --upgrade twine | |
| python -m twine check wheelhouse/* | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: wheels-${{ matrix.os }}-${{ matrix.python-version }}${{ matrix.abi }} | |
| path: ./wheelhouse/*.whl | |
| build_sdist: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v5 | |
| with: | |
| submodules: recursive | |
| - name: Build sdist | |
| run: pipx run build --sdist | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: sdist | |
| path: ./dist/*.tar.gz | |
| upload_testpypi: | |
| needs: [build_wheels, build_sdist] | |
| runs-on: ubuntu-latest | |
| environment: testpypi | |
| permissions: | |
| id-token: write | |
| if: >- | |
| (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')) || | |
| (github.event_name == 'workflow_dispatch' && (inputs.publish == 'testpypi' || inputs.publish == 'pypi')) | |
| steps: | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| pattern: "{wheels-*,sdist}" | |
| path: dist | |
| merge-multiple: true | |
| - name: Publish to TestPyPI | |
| uses: pypa/gh-action-pypi-publish@release/v1 | |
| with: | |
| repository-url: https://test.pypi.org/legacy/ | |
| upload_pypi: | |
| needs: [upload_testpypi] | |
| runs-on: ubuntu-latest | |
| environment: pypi | |
| permissions: | |
| id-token: write | |
| if: >- | |
| (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')) || | |
| (github.event_name == 'workflow_dispatch' && inputs.publish == 'pypi') | |
| steps: | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| pattern: "{wheels-*,sdist}" | |
| path: dist | |
| merge-multiple: true | |
| - name: Publish to PyPI | |
| uses: pypa/gh-action-pypi-publish@release/v1 |