Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
932529cab1 | ||
|
|
70fb1c4a1a | ||
|
|
405a475ac7 | ||
|
|
78da7bafcc | ||
|
|
8d86d3122b | ||
|
|
89a5cfe272 | ||
|
|
8d5d84e0fc | ||
|
|
4ada77dea4 | ||
|
|
829441f4a0 | ||
|
|
a8d190a111 | ||
|
|
bf817c6dc8 | ||
|
|
384c8d5c82 | ||
|
|
c37e5a2d13 | ||
|
|
130fdd2548 | ||
|
|
ed120540d9 | ||
|
|
6c6e0f6ba5 | ||
|
|
877d3bf649 | ||
|
|
21e9ef1800 | ||
|
|
c333b131d3 | ||
|
|
4c2ed1e632 | ||
|
|
9f2a3cb67c | ||
|
|
687406f664 | ||
|
|
2e6b11e10c | ||
|
|
b713086763 | ||
|
|
8f574d107b | ||
|
|
97da90432e | ||
|
|
67ee9dda92 |
@@ -31,7 +31,7 @@ jobs:
|
|||||||
run: pipx run nox -s pylint -- --output-format=github
|
run: pipx run nox -s pylint -- --output-format=github
|
||||||
|
|
||||||
test:
|
test:
|
||||||
name: Test cibuildwheel on ${{ matrix.os }} (${{ matrix.python_version }})
|
name: Test on ${{ matrix.os }} (${{ matrix.python_version }})
|
||||||
needs: lint
|
needs: lint
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
@@ -49,6 +49,8 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python_version }}
|
python-version: ${{ matrix.python_version }}
|
||||||
|
|
||||||
|
- uses: yezz123/setup-uv@v4
|
||||||
|
|
||||||
# Install podman on this CI instance for podman tests on linux
|
# Install podman on this CI instance for podman tests on linux
|
||||||
# Snippet from: https://github.com/redhat-actions/podman-login/blob/main/.github/workflows/example.yml
|
# Snippet from: https://github.com/redhat-actions/podman-login/blob/main/.github/workflows/example.yml
|
||||||
- name: Install latest podman
|
- name: Install latest podman
|
||||||
@@ -69,7 +71,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
python -m pip install ".[test]"
|
uv pip install --system ".[test]"
|
||||||
|
|
||||||
- name: Generate a sample project
|
- name: Generate a sample project
|
||||||
run: |
|
run: |
|
||||||
@@ -82,6 +84,9 @@ jobs:
|
|||||||
output-dir: wheelhouse
|
output-dir: wheelhouse
|
||||||
env:
|
env:
|
||||||
CIBW_ARCHS_MACOS: x86_64 universal2 arm64
|
CIBW_ARCHS_MACOS: x86_64 universal2 arm64
|
||||||
|
CIBW_BUILD_FRONTEND: 'build[uv]'
|
||||||
|
CIBW_FREE_THREADED_SUPPORT: 1
|
||||||
|
CIBW_PRERELEASE_PYTHONS: 1
|
||||||
|
|
||||||
- name: Run a sample build (GitHub Action, only)
|
- name: Run a sample build (GitHub Action, only)
|
||||||
uses: ./
|
uses: ./
|
||||||
@@ -125,19 +130,41 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
python ./bin/run_tests.py --run-podman
|
python ./bin/run_tests.py --run-podman
|
||||||
|
|
||||||
test-emulated:
|
emulated-archs:
|
||||||
name: Test emulated cibuildwheel using qemu
|
name: Get qemu emulated architectures
|
||||||
needs: lint
|
needs: lint
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 180
|
outputs:
|
||||||
|
archs: ${{ steps.archs.outputs.archs }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
|
run: python -m pip install ".[test]"
|
||||||
|
- name: Get qemu emulated architectures
|
||||||
|
id: archs
|
||||||
run: |
|
run: |
|
||||||
python -m pip install ".[test]"
|
OUTPUT=$(python -c "from json import dumps; from test.utils import EMULATED_ARCHS; print(dumps(EMULATED_ARCHS))")
|
||||||
|
echo "${OUTPUT}"
|
||||||
|
echo "archs=${OUTPUT}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
test-emulated:
|
||||||
|
name: Test Linux ${{ matrix.arch }} using qemu
|
||||||
|
needs: emulated-archs
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 180
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
arch: ${{ fromJSON(needs.emulated-archs.outputs.archs) }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: "3.x"
|
||||||
|
- name: Install dependencies
|
||||||
|
run: python -m pip install ".[test,uv]"
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
id: qemu
|
id: qemu
|
||||||
@@ -146,5 +173,38 @@ jobs:
|
|||||||
platforms: all
|
platforms: all
|
||||||
|
|
||||||
- name: Run the emulation tests
|
- name: Run the emulation tests
|
||||||
|
run: pytest --run-emulation ${{ matrix.arch }} test/test_emulation.py
|
||||||
|
|
||||||
|
test-pyodide:
|
||||||
|
name: Test cibuildwheel building pyodide wheels
|
||||||
|
needs: lint
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
timeout-minutes: 180
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-python@v5
|
||||||
|
name: Install Python 3.12
|
||||||
|
with:
|
||||||
|
python-version: '3.12'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
pytest --run-emulation test/test_emulation.py
|
python -m pip install ".[test]"
|
||||||
|
|
||||||
|
- name: Generate a sample project
|
||||||
|
run: |
|
||||||
|
python -m test.test_projects test.test_0_basic.basic_project sample_proj
|
||||||
|
|
||||||
|
- name: Run a sample build (GitHub Action)
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
package-dir: sample_proj
|
||||||
|
output-dir: wheelhouse
|
||||||
|
env:
|
||||||
|
CIBW_PLATFORM: pyodide
|
||||||
|
|
||||||
|
- name: Run tests with 'CIBW_PLATFORM' set to 'pyodide'
|
||||||
|
run: |
|
||||||
|
python ./bin/run_tests.py
|
||||||
|
env:
|
||||||
|
CIBW_PLATFORM: pyodide
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ repos:
|
|||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
|
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
rev: v0.4.4
|
rev: v0.4.8
|
||||||
hooks:
|
hooks:
|
||||||
- id: ruff
|
- id: ruff
|
||||||
args: ["--fix", "--show-fixes"]
|
args: ["--fix", "--show-fixes"]
|
||||||
@@ -40,6 +40,7 @@ repos:
|
|||||||
- types-jinja2
|
- types-jinja2
|
||||||
- types-pyyaml
|
- types-pyyaml
|
||||||
- types-requests
|
- types-requests
|
||||||
|
- uv
|
||||||
- validate-pyproject
|
- validate-pyproject
|
||||||
- id: mypy
|
- id: mypy
|
||||||
name: mypy 3.12
|
name: mypy 3.12
|
||||||
@@ -68,15 +69,15 @@ repos:
|
|||||||
files: ^docs/changelog.md$
|
files: ^docs/changelog.md$
|
||||||
|
|
||||||
- repo: https://github.com/codespell-project/codespell
|
- repo: https://github.com/codespell-project/codespell
|
||||||
rev: v2.2.6
|
rev: v2.3.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: codespell
|
- id: codespell
|
||||||
args: ["-L", "sur", "-w"]
|
args: ["-L", "sur,assertin", "-w"]
|
||||||
exclude: ^docs/working-examples\.md$ # Autogenerated
|
exclude: ^docs/working-examples\.md$ # Autogenerated
|
||||||
|
|
||||||
|
|
||||||
- repo: https://github.com/python-jsonschema/check-jsonschema
|
- repo: https://github.com/python-jsonschema/check-jsonschema
|
||||||
rev: 0.28.3
|
rev: 0.28.4
|
||||||
hooks:
|
hooks:
|
||||||
- id: check-dependabot
|
- id: check-dependabot
|
||||||
- id: check-github-actions
|
- id: check-github-actions
|
||||||
|
|||||||
@@ -22,24 +22,25 @@ Python wheels are great. Building them across **Mac, Linux, Windows**, on **mult
|
|||||||
What does it do?
|
What does it do?
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
| | macOS Intel | macOS Apple Silicon | Windows 64bit | Windows 32bit | Windows Arm64 | manylinux<br/>musllinux x86_64 | manylinux<br/>musllinux i686 | manylinux<br/>musllinux aarch64 | manylinux<br/>musllinux ppc64le | manylinux<br/>musllinux s390x |
|
| | macOS Intel | macOS Apple Silicon | Windows 64bit | Windows 32bit | Windows Arm64 | manylinux<br/>musllinux x86_64 | manylinux<br/>musllinux i686 | manylinux<br/>musllinux aarch64 | manylinux<br/>musllinux ppc64le | manylinux<br/>musllinux s390x | Pyodide |
|
||||||
|----------------|----|-----|-----|-----|-----|----|-----|----|-----|-----|
|
|----------------|----|-----|-----|-----|-----|----|-----|----|-----|-----|-----|
|
||||||
| CPython 3.6 | ✅ | N/A | ✅ | ✅ | N/A | ✅ | ✅ | ✅ | ✅ | ✅ |
|
| CPython 3.6 | ✅ | N/A | ✅ | ✅ | N/A | ✅ | ✅ | ✅ | ✅ | ✅ | N/A |
|
||||||
| CPython 3.7 | ✅ | N/A | ✅ | ✅ | N/A | ✅ | ✅ | ✅ | ✅ | ✅ |
|
| CPython 3.7 | ✅ | N/A | ✅ | ✅ | N/A | ✅ | ✅ | ✅ | ✅ | ✅ | N/A |
|
||||||
| CPython 3.8 | ✅ | ✅ | ✅ | ✅ | N/A | ✅ | ✅ | ✅ | ✅ | ✅ |
|
| CPython 3.8 | ✅ | ✅ | ✅ | ✅ | N/A | ✅ | ✅ | ✅ | ✅ | ✅ | N/A |
|
||||||
| CPython 3.9 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ |
|
| CPython 3.9 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | N/A |
|
||||||
| CPython 3.10 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ |
|
| CPython 3.10 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | N/A |
|
||||||
| CPython 3.11 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ |
|
| CPython 3.11 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | N/A |
|
||||||
| CPython 3.12 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ |
|
| CPython 3.12 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁴ |
|
||||||
| CPython 3.13³ | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ |
|
| CPython 3.13³ | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | N/A |
|
||||||
| PyPy 3.7 v7.3 | ✅ | N/A | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A |
|
| PyPy 3.7 v7.3 | ✅ | N/A | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A | N/A |
|
||||||
| PyPy 3.8 v7.3 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A |
|
| PyPy 3.8 v7.3 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A | N/A |
|
||||||
| PyPy 3.9 v7.3 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A |
|
| PyPy 3.9 v7.3 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A | N/A |
|
||||||
| PyPy 3.10 v7.3 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A |
|
| PyPy 3.10 v7.3 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A | N/A |
|
||||||
|
|
||||||
<sup>¹ PyPy is only supported for manylinux wheels.</sup><br>
|
<sup>¹ PyPy is only supported for manylinux wheels.</sup><br>
|
||||||
<sup>² Windows arm64 support is experimental.</sup><br>
|
<sup>² Windows arm64 support is experimental.</sup><br>
|
||||||
<sup>³ CPython 3.13 is available using the [CIBW_PRERELEASE_PYTHONS](https://cibuildwheel.pypa.io/en/stable/options/#prerelease-pythons) option.</sup><br>
|
<sup>³ CPython 3.13 is available using the [`CIBW_PRERELEASE_PYTHONS`](https://cibuildwheel.pypa.io/en/stable/options/#prerelease-pythons) option. Free-threaded mode requires opt-in.</sup><br>
|
||||||
|
<sup>⁴ Experimental, not yet supported on PyPI, but can be used directly in web deployment. Use `--platform pyodide` to build.</sup><br>
|
||||||
|
|
||||||
- Builds manylinux, musllinux, macOS 10.9+, and Windows wheels for CPython and PyPy
|
- Builds manylinux, musllinux, macOS 10.9+, and Windows wheels for CPython and PyPy
|
||||||
- Works on GitHub Actions, Azure Pipelines, Travis CI, AppVeyor, CircleCI, GitLab CI, and Cirrus CI
|
- Works on GitHub Actions, Azure Pipelines, Travis CI, AppVeyor, CircleCI, GitLab CI, and Cirrus CI
|
||||||
@@ -93,7 +94,7 @@ jobs:
|
|||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@v5
|
||||||
|
|
||||||
- name: Install cibuildwheel
|
- name: Install cibuildwheel
|
||||||
run: python -m pip install cibuildwheel==2.18.1
|
run: python -m pip install cibuildwheel==2.19.1
|
||||||
|
|
||||||
- name: Build wheels
|
- name: Build wheels
|
||||||
run: python -m cibuildwheel --output-dir wheelhouse
|
run: python -m cibuildwheel --output-dir wheelhouse
|
||||||
@@ -163,9 +164,9 @@ Here are some repos that use cibuildwheel.
|
|||||||
| [Tornado][] | ![github icon][] | ![linux icon][] ![apple icon][] ![windows icon][] | Tornado is a Python web framework and asynchronous networking library. Uses stable ABI for a small C extension. |
|
| [Tornado][] | ![github icon][] | ![linux icon][] ![apple icon][] ![windows icon][] | Tornado is a Python web framework and asynchronous networking library. Uses stable ABI for a small C extension. |
|
||||||
| [Matplotlib][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The venerable Matplotlib, a Python library with C++ portions |
|
| [Matplotlib][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The venerable Matplotlib, a Python library with C++ portions |
|
||||||
| [NCNN][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | ncnn is a high-performance neural network inference framework optimized for the mobile platform |
|
| [NCNN][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | ncnn is a high-performance neural network inference framework optimized for the mobile platform |
|
||||||
|
| [duckdb][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | DuckDB is an analytical in-process SQL database management system |
|
||||||
| [Prophet][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Tool for producing high quality forecasts for time series data that has multiple seasonality with linear or non-linear growth. |
|
| [Prophet][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Tool for producing high quality forecasts for time series data that has multiple seasonality with linear or non-linear growth. |
|
||||||
| [MyPy][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | The compiled version of MyPy using MyPyC. |
|
| [MyPy][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | The compiled version of MyPy using MyPyC. |
|
||||||
| [duckdb][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | DuckDB is an in-process SQL OLAP Database Management System |
|
|
||||||
| [Kivy][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Open source UI framework written in Python, running on Windows, Linux, macOS, Android and iOS |
|
| [Kivy][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Open source UI framework written in Python, running on Windows, Linux, macOS, Android and iOS |
|
||||||
|
|
||||||
[scikit-learn]: https://github.com/scikit-learn/scikit-learn
|
[scikit-learn]: https://github.com/scikit-learn/scikit-learn
|
||||||
@@ -174,9 +175,9 @@ Here are some repos that use cibuildwheel.
|
|||||||
[Tornado]: https://github.com/tornadoweb/tornado
|
[Tornado]: https://github.com/tornadoweb/tornado
|
||||||
[Matplotlib]: https://github.com/matplotlib/matplotlib
|
[Matplotlib]: https://github.com/matplotlib/matplotlib
|
||||||
[NCNN]: https://github.com/Tencent/ncnn
|
[NCNN]: https://github.com/Tencent/ncnn
|
||||||
|
[duckdb]: https://github.com/duckdb/duckdb
|
||||||
[Prophet]: https://github.com/facebook/prophet
|
[Prophet]: https://github.com/facebook/prophet
|
||||||
[MyPy]: https://github.com/mypyc/mypy_mypyc-wheels
|
[MyPy]: https://github.com/mypyc/mypy_mypyc-wheels
|
||||||
[duckdb]: https://github.com/duckdb/duckdb
|
|
||||||
[Kivy]: https://github.com/kivy/kivy
|
[Kivy]: https://github.com/kivy/kivy
|
||||||
|
|
||||||
[appveyor icon]: docs/data/readme_icons/appveyor.svg
|
[appveyor icon]: docs/data/readme_icons/appveyor.svg
|
||||||
@@ -210,6 +211,33 @@ Changelog
|
|||||||
|
|
||||||
<!-- this section was generated by bin/update_readme_changelog.py -- do not edit manually -->
|
<!-- this section was generated by bin/update_readme_changelog.py -- do not edit manually -->
|
||||||
|
|
||||||
|
### v2.19.1
|
||||||
|
|
||||||
|
- 🐛 Don't require setup-python on GHA for Pyodide (#1868)
|
||||||
|
- 🐛 Specify full python path for uv (fixes issue in 0.2.10 & 0.2.11) (#1881)
|
||||||
|
- 🛠 Update for pip 24.1b2 on CPython 3.13. (#1879)
|
||||||
|
- 🛠 Fix a warning in our schema generation script. (#1866)
|
||||||
|
- 🛠 Cleaner output on pytest 8-8.2. (#1865)
|
||||||
|
|
||||||
|
|
||||||
|
### v2.19.0
|
||||||
|
|
||||||
|
See the [release post](https://iscinumpy.dev/post/cibuildwheel-2-19-0/) for more info on new features!
|
||||||
|
|
||||||
|
- 🌟 Add Pyodide platform. Set with `--platform pyodide` or `CIBW_PLATFORM: pyodide` on Linux with a host Python 3.12 to build WebAssembly wheels. Not accepted on PyPI currently, but usable directly in a website using Pyodide, for live docs, etc. (#1456, #1859)
|
||||||
|
- 🌟 Add `build[uv]` backend, which will take a pre-existing uv install (or install `cibuildwheel[uv]`) and use `uv` for all environment setup and installs on Python 3.8+. This is significantly faster in most cases. (#1856)
|
||||||
|
- ✨ Add free-threaded macOS builds and update CPython to 3.13.0b2. (#1854)
|
||||||
|
- 🐛 Issue copying a wheel to a non-existent output dir fixed. (#1851, #1862)
|
||||||
|
- 🐛 Better determinism for the test environment seeding. (#1835)
|
||||||
|
- 🛠 `VIRTUAL_ENV` variable now set. (#1842)
|
||||||
|
- 🛠 Remove a pip<21.3 workaround. (#1842)
|
||||||
|
- 🛠 Error handling was refactored to use exceptions. (#1719)
|
||||||
|
- 🛠 Hardcoded paths in tests avoided. (#1834)
|
||||||
|
- 🛠 Single Python tests made more generic. (#1835)
|
||||||
|
- 🛠 Sped up our ci by splitting up emulation tests. (#1839)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### v2.18.1
|
### v2.18.1
|
||||||
|
|
||||||
- 🌟 Add free-threaded Linux and Windows builds for 3.13. New identifiers `cp313t-*`, new option `CIBW_FREE_THREADED_SUPPORT`/`tool.cibuildwheel.free-threaded-support` required to opt-in. [See the docs](https://cibuildwheel.pypa.io/en/stable/options/#free-threaded-support) for more information. (#1831)
|
- 🌟 Add free-threaded Linux and Windows builds for 3.13. New identifiers `cp313t-*`, new option `CIBW_FREE_THREADED_SUPPORT`/`tool.cibuildwheel.free-threaded-support` required to opt-in. [See the docs](https://cibuildwheel.pypa.io/en/stable/options/#free-threaded-support) for more information. (#1831)
|
||||||
@@ -256,20 +284,6 @@ _11 March 2024_
|
|||||||
- 📚 Moved the docs onto the official PyPA domain - they're now available at https://cibuildwheel.pypa.io . (#1775)
|
- 📚 Moved the docs onto the official PyPA domain - they're now available at https://cibuildwheel.pypa.io . (#1775)
|
||||||
- 📚 Docs and examples improvements (#1762, #1734)
|
- 📚 Docs and examples improvements (#1762, #1734)
|
||||||
|
|
||||||
|
|
||||||
### v2.16.5
|
|
||||||
|
|
||||||
_30 January 2024_
|
|
||||||
|
|
||||||
- 🐛 Fix an incompatibility with the GitHub Action and new GitHub Runner images for Windows that bundle Powershell 7.3+ (#1741)
|
|
||||||
- 🛠 Preliminary support for new `macos-14` arm64 runners (#1743)
|
|
||||||
|
|
||||||
### v2.16.4
|
|
||||||
|
|
||||||
_28 January 2024_
|
|
||||||
|
|
||||||
- 🛠 Update manylinux pins to upgrade from a problematic PyPy version. (#1737)
|
|
||||||
|
|
||||||
<!-- END bin/update_readme_changelog.py -->
|
<!-- END bin/update_readme_changelog.py -->
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
+2
-2
@@ -24,11 +24,11 @@ branding:
|
|||||||
runs:
|
runs:
|
||||||
using: composite
|
using: composite
|
||||||
steps:
|
steps:
|
||||||
# Set up a non-EOL, cibuildwheel & pipx supported Python version
|
# Set up the version of Python that supports pyodide
|
||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@v5
|
||||||
id: python
|
id: python
|
||||||
with:
|
with:
|
||||||
python-version: "3.8 - 3.12"
|
python-version: "3.12"
|
||||||
update-environment: false
|
update-environment: false
|
||||||
|
|
||||||
- id: cibw
|
- id: cibw
|
||||||
|
|||||||
@@ -48,19 +48,21 @@ properties:
|
|||||||
type: string_array
|
type: string_array
|
||||||
build-frontend:
|
build-frontend:
|
||||||
default: default
|
default: default
|
||||||
description: Set the tool to use to build, either "pip" (default for now) or "build"
|
description: Set the tool to use to build, either "pip" (default for now), "build", or "build[uv]"
|
||||||
oneOf:
|
oneOf:
|
||||||
- enum: [pip, build, default]
|
- enum: [pip, build, "build[uv]", default]
|
||||||
- type: string
|
- type: string
|
||||||
pattern: '^pip; ?args:'
|
pattern: '^pip; ?args:'
|
||||||
- type: string
|
- type: string
|
||||||
pattern: '^build; ?args:'
|
pattern: '^build; ?args:'
|
||||||
|
- type: string
|
||||||
|
pattern: '^build\\[uv\\]; ?args:'
|
||||||
- type: object
|
- type: object
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
required: [name]
|
required: [name]
|
||||||
properties:
|
properties:
|
||||||
name:
|
name:
|
||||||
enum: [pip, build]
|
enum: [pip, build, "build[uv]"]
|
||||||
args:
|
args:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
@@ -280,6 +282,7 @@ oses = {
|
|||||||
"linux": as_object(non_global_options),
|
"linux": as_object(non_global_options),
|
||||||
"windows": as_object(not_linux),
|
"windows": as_object(not_linux),
|
||||||
"macos": as_object(not_linux),
|
"macos": as_object(not_linux),
|
||||||
|
"pyodide": as_object(not_linux),
|
||||||
}
|
}
|
||||||
|
|
||||||
oses["linux"]["properties"]["repair-wheel-command"] = {
|
oses["linux"]["properties"]["repair-wheel-command"] = {
|
||||||
|
|||||||
Executable
+148
@@ -0,0 +1,148 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import difflib
|
||||||
|
import logging
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Final
|
||||||
|
|
||||||
|
import click
|
||||||
|
import packaging.specifiers
|
||||||
|
import requests
|
||||||
|
import rich
|
||||||
|
from packaging.version import InvalidVersion, Version
|
||||||
|
from rich.logging import RichHandler
|
||||||
|
from rich.syntax import Syntax
|
||||||
|
|
||||||
|
from cibuildwheel._compat import tomllib
|
||||||
|
|
||||||
|
log = logging.getLogger("cibw")
|
||||||
|
|
||||||
|
# Looking up the dir instead of using utils.resources_dir
|
||||||
|
# since we want to write to it.
|
||||||
|
DIR: Final[Path] = Path(__file__).parent.parent.resolve()
|
||||||
|
RESOURCES_DIR: Final[Path] = DIR / "cibuildwheel/resources"
|
||||||
|
|
||||||
|
NODEJS_DIST: Final[str] = "https://nodejs.org/dist/"
|
||||||
|
NODEJS_INDEX: Final[str] = f"{NODEJS_DIST}index.json"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True, order=True)
|
||||||
|
class VersionTuple:
|
||||||
|
version: Version
|
||||||
|
version_string: str
|
||||||
|
|
||||||
|
|
||||||
|
def parse_nodejs_index() -> list[VersionTuple]:
|
||||||
|
versions: list[VersionTuple] = []
|
||||||
|
response = requests.get(NODEJS_INDEX)
|
||||||
|
response.raise_for_status()
|
||||||
|
versions_info = response.json()
|
||||||
|
for version_info in versions_info:
|
||||||
|
version_string = version_info.get("version", "???")
|
||||||
|
if not version_info.get("lts", False):
|
||||||
|
log.debug("Ignoring non LTS release %r", version_string)
|
||||||
|
continue
|
||||||
|
if "linux-x64" not in version_info.get("files", []):
|
||||||
|
log.warning(
|
||||||
|
"Ignoring release %r which does not include a linux-x64 binary", version_string
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
version = Version(version_string)
|
||||||
|
if version.is_devrelease:
|
||||||
|
log.info("Ignoring development release %r", str(version))
|
||||||
|
continue
|
||||||
|
if version.is_prerelease:
|
||||||
|
log.info("Ignoring pre-release %r", str(version))
|
||||||
|
continue
|
||||||
|
versions.append(VersionTuple(version, version_string))
|
||||||
|
except InvalidVersion:
|
||||||
|
log.warning("Ignoring release %r", version_string)
|
||||||
|
versions.sort(reverse=True)
|
||||||
|
return versions
|
||||||
|
|
||||||
|
|
||||||
|
@click.command()
|
||||||
|
@click.option("--force", is_flag=True)
|
||||||
|
@click.option(
|
||||||
|
"--level", default="INFO", type=click.Choice(["WARNING", "INFO", "DEBUG"], case_sensitive=False)
|
||||||
|
)
|
||||||
|
def update_nodejs(force: bool, level: str) -> None:
|
||||||
|
logging.basicConfig(
|
||||||
|
level="INFO",
|
||||||
|
format="%(message)s",
|
||||||
|
datefmt="[%X]",
|
||||||
|
handlers=[RichHandler(rich_tracebacks=True, markup=True)],
|
||||||
|
)
|
||||||
|
log.setLevel(level)
|
||||||
|
|
||||||
|
toml_file_path = RESOURCES_DIR / "nodejs.toml"
|
||||||
|
|
||||||
|
original_toml = toml_file_path.read_text()
|
||||||
|
with toml_file_path.open("rb") as f:
|
||||||
|
nodejs_data = tomllib.load(f)
|
||||||
|
|
||||||
|
nodejs_data.pop("url")
|
||||||
|
|
||||||
|
major_versions = [VersionTuple(Version(key), key) for key in nodejs_data]
|
||||||
|
major_versions.sort(reverse=True)
|
||||||
|
|
||||||
|
versions = parse_nodejs_index()
|
||||||
|
|
||||||
|
# update existing versions, 1 per LTS
|
||||||
|
for major_version in major_versions:
|
||||||
|
current = Version(nodejs_data[major_version.version_string])
|
||||||
|
specifier = packaging.specifiers.SpecifierSet(
|
||||||
|
specifiers=f"=={major_version.version.major}.*"
|
||||||
|
)
|
||||||
|
for version in versions:
|
||||||
|
if specifier.contains(version.version) and version.version > current:
|
||||||
|
nodejs_data[major_version.version_string] = version.version_string
|
||||||
|
break
|
||||||
|
|
||||||
|
# check for a new major LTS to insert
|
||||||
|
if versions and versions[0].version.major > major_versions[0].version.major:
|
||||||
|
major_versions.insert(
|
||||||
|
0,
|
||||||
|
VersionTuple(Version(str(versions[0].version.major)), f"v{versions[0].version.major}"),
|
||||||
|
)
|
||||||
|
nodejs_data[major_versions[0].version_string] = versions[0].version_string
|
||||||
|
|
||||||
|
versions_toml = "\n".join(
|
||||||
|
f'{major_version.version_string} = "{nodejs_data[major_version.version_string]}"'
|
||||||
|
for major_version in major_versions
|
||||||
|
)
|
||||||
|
result_toml = f'url = "{NODEJS_DIST}"\n{versions_toml}\n'
|
||||||
|
|
||||||
|
rich.print() # spacer
|
||||||
|
|
||||||
|
if original_toml == result_toml:
|
||||||
|
rich.print("[green]Check complete, nodejs version unchanged.")
|
||||||
|
return
|
||||||
|
|
||||||
|
rich.print("nodejs version updated.")
|
||||||
|
rich.print("Changes:")
|
||||||
|
rich.print()
|
||||||
|
|
||||||
|
toml_relpath = toml_file_path.relative_to(DIR).as_posix()
|
||||||
|
diff_lines = difflib.unified_diff(
|
||||||
|
original_toml.splitlines(keepends=True),
|
||||||
|
result_toml.splitlines(keepends=True),
|
||||||
|
fromfile=toml_relpath,
|
||||||
|
tofile=toml_relpath,
|
||||||
|
)
|
||||||
|
rich.print(Syntax("".join(diff_lines), "diff", theme="ansi_light"))
|
||||||
|
rich.print()
|
||||||
|
|
||||||
|
if force:
|
||||||
|
toml_file_path.write_text(result_toml)
|
||||||
|
rich.print("[green]TOML file updated.")
|
||||||
|
else:
|
||||||
|
rich.print("[yellow]File left unchanged. Use --force flag to update.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
update_nodejs()
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
__version__ = "2.18.1"
|
__version__ = "2.19.1"
|
||||||
|
|||||||
+73
-34
@@ -1,11 +1,13 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import dataclasses
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
import tarfile
|
import tarfile
|
||||||
import textwrap
|
import textwrap
|
||||||
|
import traceback
|
||||||
import typing
|
import typing
|
||||||
from collections.abc import Iterable, Sequence, Set
|
from collections.abc import Iterable, Sequence, Set
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@@ -15,8 +17,10 @@ from typing import Protocol
|
|||||||
import cibuildwheel
|
import cibuildwheel
|
||||||
import cibuildwheel.linux
|
import cibuildwheel.linux
|
||||||
import cibuildwheel.macos
|
import cibuildwheel.macos
|
||||||
|
import cibuildwheel.pyodide
|
||||||
import cibuildwheel.util
|
import cibuildwheel.util
|
||||||
import cibuildwheel.windows
|
import cibuildwheel.windows
|
||||||
|
from cibuildwheel import errors
|
||||||
from cibuildwheel._compat.typing import assert_never
|
from cibuildwheel._compat.typing import assert_never
|
||||||
from cibuildwheel.architecture import Architecture, allowed_architectures_check
|
from cibuildwheel.architecture import Architecture, allowed_architectures_check
|
||||||
from cibuildwheel.logger import log
|
from cibuildwheel.logger import log
|
||||||
@@ -30,10 +34,38 @@ from cibuildwheel.util import (
|
|||||||
chdir,
|
chdir,
|
||||||
detect_ci_provider,
|
detect_ci_provider,
|
||||||
fix_ansi_codes_for_github_actions,
|
fix_ansi_codes_for_github_actions,
|
||||||
|
strtobool,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class GlobalOptions:
|
||||||
|
print_traceback_on_error: bool = True # decides what happens when errors are hit.
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
|
global_options = GlobalOptions()
|
||||||
|
try:
|
||||||
|
main_inner(global_options)
|
||||||
|
except errors.FatalError as e:
|
||||||
|
message = e.args[0]
|
||||||
|
if log.step_active:
|
||||||
|
log.step_end_with_error(message)
|
||||||
|
else:
|
||||||
|
print(f"cibuildwheel: {message}", file=sys.stderr)
|
||||||
|
|
||||||
|
if global_options.print_traceback_on_error:
|
||||||
|
traceback.print_exc(file=sys.stderr)
|
||||||
|
|
||||||
|
sys.exit(e.return_code)
|
||||||
|
|
||||||
|
|
||||||
|
def main_inner(global_options: GlobalOptions) -> None:
|
||||||
|
"""
|
||||||
|
`main_inner` is the same as `main`, but it raises FatalError exceptions
|
||||||
|
rather than exiting directly.
|
||||||
|
"""
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="Build wheels for all the platforms.",
|
description="Build wheels for all the platforms.",
|
||||||
epilog="""
|
epilog="""
|
||||||
@@ -45,7 +77,7 @@ def main() -> None:
|
|||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--platform",
|
"--platform",
|
||||||
choices=["auto", "linux", "macos", "windows"],
|
choices=["auto", "linux", "macos", "windows", "pyodide"],
|
||||||
default=None,
|
default=None,
|
||||||
help="""
|
help="""
|
||||||
Platform to build for. Use this option to override the
|
Platform to build for. Use this option to override the
|
||||||
@@ -131,8 +163,17 @@ def main() -> None:
|
|||||||
help="Enable pre-release Python versions if available.",
|
help="Enable pre-release Python versions if available.",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--debug-traceback",
|
||||||
|
action="store_true",
|
||||||
|
default=strtobool(os.environ.get("CIBW_DEBUG_TRACEBACK", "0")),
|
||||||
|
help="Print a full traceback for all errors",
|
||||||
|
)
|
||||||
|
|
||||||
args = CommandLineArguments(**vars(parser.parse_args()))
|
args = CommandLineArguments(**vars(parser.parse_args()))
|
||||||
|
|
||||||
|
global_options.print_traceback_on_error = args.debug_traceback
|
||||||
|
|
||||||
args.package_dir = args.package_dir.resolve()
|
args.package_dir = args.package_dir.resolve()
|
||||||
|
|
||||||
# This are always relative to the base directory, even in SDist builds
|
# This are always relative to the base directory, even in SDist builds
|
||||||
@@ -176,11 +217,10 @@ def _compute_platform_only(only: str) -> PlatformName:
|
|||||||
return "macos"
|
return "macos"
|
||||||
if "win_" in only or "win32" in only:
|
if "win_" in only or "win32" in only:
|
||||||
return "windows"
|
return "windows"
|
||||||
print(
|
if "pyodide_" in only:
|
||||||
f"Invalid --only='{only}', must be a build selector with a known platform",
|
return "pyodide"
|
||||||
file=sys.stderr,
|
msg = f"Invalid --only='{only}', must be a build selector with a known platform"
|
||||||
)
|
raise errors.ConfigurationError(msg)
|
||||||
sys.exit(2)
|
|
||||||
|
|
||||||
|
|
||||||
def _compute_platform_auto() -> PlatformName:
|
def _compute_platform_auto() -> PlatformName:
|
||||||
@@ -191,34 +231,27 @@ def _compute_platform_auto() -> PlatformName:
|
|||||||
elif sys.platform == "win32":
|
elif sys.platform == "win32":
|
||||||
return "windows"
|
return "windows"
|
||||||
else:
|
else:
|
||||||
print(
|
msg = (
|
||||||
'cibuildwheel: Unable to detect platform from "sys.platform". cibuildwheel doesn\'t '
|
'cibuildwheel: Unable to detect platform from "sys.platform". cibuildwheel doesn\'t '
|
||||||
"support building wheels for this platform. You might be able to build for a different "
|
"support building wheels for this platform. You might be able to build for a different "
|
||||||
"platform using the --platform argument. Check --help output for more information.",
|
"platform using the --platform argument. Check --help output for more information."
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(2)
|
raise errors.ConfigurationError(msg)
|
||||||
|
|
||||||
|
|
||||||
def _compute_platform(args: CommandLineArguments) -> PlatformName:
|
def _compute_platform(args: CommandLineArguments) -> PlatformName:
|
||||||
platform_option_value = args.platform or os.environ.get("CIBW_PLATFORM", "auto")
|
platform_option_value = args.platform or os.environ.get("CIBW_PLATFORM", "auto")
|
||||||
|
|
||||||
if args.only and args.platform is not None:
|
if args.only and args.platform is not None:
|
||||||
print(
|
msg = "--platform cannot be specified with --only, it is computed from --only"
|
||||||
"--platform cannot be specified with --only, it is computed from --only",
|
raise errors.ConfigurationError(msg)
|
||||||
file=sys.stderr,
|
|
||||||
)
|
|
||||||
sys.exit(2)
|
|
||||||
if args.only and args.archs is not None:
|
if args.only and args.archs is not None:
|
||||||
print(
|
msg = "--arch cannot be specified with --only, it is computed from --only"
|
||||||
"--arch cannot be specified with --only, it is computed from --only",
|
raise errors.ConfigurationError(msg)
|
||||||
file=sys.stderr,
|
|
||||||
)
|
|
||||||
sys.exit(2)
|
|
||||||
|
|
||||||
if platform_option_value not in PLATFORMS | {"auto"}:
|
if platform_option_value not in PLATFORMS | {"auto"}:
|
||||||
print(f"cibuildwheel: Unsupported platform: {platform_option_value}", file=sys.stderr)
|
msg = f"Unsupported platform: {platform_option_value}"
|
||||||
sys.exit(2)
|
raise errors.ConfigurationError(msg)
|
||||||
|
|
||||||
if args.only:
|
if args.only:
|
||||||
return _compute_platform_only(args.only)
|
return _compute_platform_only(args.only)
|
||||||
@@ -246,11 +279,18 @@ def get_platform_module(platform: PlatformName) -> PlatformModule:
|
|||||||
return cibuildwheel.windows
|
return cibuildwheel.windows
|
||||||
if platform == "macos":
|
if platform == "macos":
|
||||||
return cibuildwheel.macos
|
return cibuildwheel.macos
|
||||||
|
if platform == "pyodide":
|
||||||
|
return cibuildwheel.pyodide
|
||||||
assert_never(platform)
|
assert_never(platform)
|
||||||
|
|
||||||
|
|
||||||
def build_in_directory(args: CommandLineArguments) -> None:
|
def build_in_directory(args: CommandLineArguments) -> None:
|
||||||
platform: PlatformName = _compute_platform(args)
|
platform: PlatformName = _compute_platform(args)
|
||||||
|
if platform == "pyodide" and sys.platform == "win32":
|
||||||
|
msg = "cibuildwheel: Building for pyodide is not supported on Windows"
|
||||||
|
print(msg, file=sys.stderr)
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
options = compute_options(platform=platform, command_line_arguments=args, env=os.environ)
|
options = compute_options(platform=platform, command_line_arguments=args, env=os.environ)
|
||||||
|
|
||||||
package_dir = options.globals.package_dir
|
package_dir = options.globals.package_dir
|
||||||
@@ -258,9 +298,8 @@ def build_in_directory(args: CommandLineArguments) -> None:
|
|||||||
|
|
||||||
if not any(package_dir.joinpath(name).exists() for name in package_files):
|
if not any(package_dir.joinpath(name).exists() for name in package_files):
|
||||||
names = ", ".join(sorted(package_files, reverse=True))
|
names = ", ".join(sorted(package_files, reverse=True))
|
||||||
msg = f"cibuildwheel: Could not find any of {{{names}}} at root of package"
|
msg = f"Could not find any of {{{names}}} at root of package"
|
||||||
print(msg, file=sys.stderr)
|
raise errors.ConfigurationError(msg)
|
||||||
sys.exit(2)
|
|
||||||
|
|
||||||
platform_module = get_platform_module(platform)
|
platform_module = get_platform_module(platform)
|
||||||
identifiers = get_build_identifiers(
|
identifiers = get_build_identifiers(
|
||||||
@@ -291,16 +330,14 @@ def build_in_directory(args: CommandLineArguments) -> None:
|
|||||||
options.check_for_invalid_configuration(identifiers)
|
options.check_for_invalid_configuration(identifiers)
|
||||||
allowed_architectures_check(platform, options.globals.architectures)
|
allowed_architectures_check(platform, options.globals.architectures)
|
||||||
except ValueError as err:
|
except ValueError as err:
|
||||||
print("cibuildwheel:", *err.args, file=sys.stderr)
|
raise errors.DeprecationError(*err.args) from err
|
||||||
sys.exit(4)
|
|
||||||
|
|
||||||
if not identifiers:
|
if not identifiers:
|
||||||
print(
|
message = f"No build identifiers selected: {options.globals.build_selector}"
|
||||||
f"cibuildwheel: No build identifiers selected: {options.globals.build_selector}",
|
if args.allow_empty:
|
||||||
file=sys.stderr,
|
print(f"cibuildwheel: {message}", file=sys.stderr)
|
||||||
)
|
else:
|
||||||
if not args.allow_empty:
|
raise errors.NothingToDoError(message)
|
||||||
sys.exit(3)
|
|
||||||
|
|
||||||
output_dir = options.globals.output_dir
|
output_dir = options.globals.output_dir
|
||||||
|
|
||||||
@@ -355,7 +392,9 @@ def print_preamble(platform: str, options: Options, identifiers: Sequence[str])
|
|||||||
|
|
||||||
|
|
||||||
def get_build_identifiers(
|
def get_build_identifiers(
|
||||||
platform_module: PlatformModule, build_selector: BuildSelector, architectures: Set[Architecture]
|
platform_module: PlatformModule,
|
||||||
|
build_selector: BuildSelector,
|
||||||
|
architectures: Set[Architecture],
|
||||||
) -> list[str]:
|
) -> list[str]:
|
||||||
python_configurations = platform_module.get_python_configurations(build_selector, architectures)
|
python_configurations = platform_module.get_python_configurations(build_selector, architectures)
|
||||||
return [config.identifier for config in python_configurations]
|
return [config.identifier for config in python_configurations]
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ PRETTY_NAMES: Final[dict[PlatformName, str]] = {
|
|||||||
"linux": "Linux",
|
"linux": "Linux",
|
||||||
"macos": "macOS",
|
"macos": "macOS",
|
||||||
"windows": "Windows",
|
"windows": "Windows",
|
||||||
|
"pyodide": "Pyodide",
|
||||||
}
|
}
|
||||||
|
|
||||||
ARCH_SYNONYMS: Final[list[dict[PlatformName, str | None]]] = [
|
ARCH_SYNONYMS: Final[list[dict[PlatformName, str | None]]] = [
|
||||||
@@ -46,6 +47,9 @@ class Architecture(Enum):
|
|||||||
AMD64 = "AMD64"
|
AMD64 = "AMD64"
|
||||||
ARM64 = "ARM64"
|
ARM64 = "ARM64"
|
||||||
|
|
||||||
|
# WebAssembly
|
||||||
|
wasm32 = "wasm32"
|
||||||
|
|
||||||
# Allow this to be sorted
|
# Allow this to be sorted
|
||||||
def __lt__(self, other: Architecture) -> bool:
|
def __lt__(self, other: Architecture) -> bool:
|
||||||
return self.value < other.value
|
return self.value < other.value
|
||||||
@@ -72,8 +76,9 @@ class Architecture(Enum):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def auto_archs(platform: PlatformName) -> set[Architecture]:
|
def native_arch(platform: PlatformName) -> Architecture | None:
|
||||||
native_machine = platform_module.machine()
|
if platform == "pyodide":
|
||||||
|
return Architecture.wasm32
|
||||||
|
|
||||||
# Cross-platform support. Used for --print-build-identifiers or docker builds.
|
# Cross-platform support. Used for --print-build-identifiers or docker builds.
|
||||||
host_platform: PlatformName = (
|
host_platform: PlatformName = (
|
||||||
@@ -82,6 +87,7 @@ class Architecture(Enum):
|
|||||||
else ("macos" if sys.platform.startswith("darwin") else "linux")
|
else ("macos" if sys.platform.startswith("darwin") else "linux")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
native_machine = platform_module.machine()
|
||||||
native_architecture = Architecture(native_machine)
|
native_architecture = Architecture(native_machine)
|
||||||
|
|
||||||
# we might need to rename the native arch to the machine we're running
|
# we might need to rename the native arch to the machine we're running
|
||||||
@@ -93,11 +99,18 @@ class Architecture(Enum):
|
|||||||
|
|
||||||
if synonym is None:
|
if synonym is None:
|
||||||
# can't build anything on this platform
|
# can't build anything on this platform
|
||||||
return set()
|
return None
|
||||||
|
|
||||||
native_architecture = Architecture(synonym)
|
native_architecture = Architecture(synonym)
|
||||||
|
|
||||||
result = {native_architecture}
|
return native_architecture
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def auto_archs(platform: PlatformName) -> set[Architecture]:
|
||||||
|
native_arch = Architecture.native_arch(platform)
|
||||||
|
if native_arch is None:
|
||||||
|
return set() # can't build anything on this platform
|
||||||
|
result = {native_arch}
|
||||||
|
|
||||||
if platform == "linux" and Architecture.x86_64 in result:
|
if platform == "linux" and Architecture.x86_64 in result:
|
||||||
# x86_64 machines can run i686 containers
|
# x86_64 machines can run i686 containers
|
||||||
@@ -120,6 +133,7 @@ class Architecture(Enum):
|
|||||||
},
|
},
|
||||||
"macos": {Architecture.x86_64, Architecture.arm64, Architecture.universal2},
|
"macos": {Architecture.x86_64, Architecture.arm64, Architecture.universal2},
|
||||||
"windows": {Architecture.x86, Architecture.AMD64, Architecture.ARM64},
|
"windows": {Architecture.x86, Architecture.AMD64, Architecture.ARM64},
|
||||||
|
"pyodide": {Architecture.wasm32},
|
||||||
}
|
}
|
||||||
return all_archs_map[platform]
|
return all_archs_map[platform]
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
"""
|
||||||
|
Errors that can cause the build to fail. Each subclass of FatalError has
|
||||||
|
a different return code, by defining them all here, we can ensure that they're
|
||||||
|
semantically clear and unique.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class FatalError(BaseException):
|
||||||
|
"""
|
||||||
|
Raising an error of this type will cause the message to be printed to
|
||||||
|
stderr and the process to be terminated. Within cibuildwheel, raising this
|
||||||
|
exception produces a better error message, and optional traceback.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return_code: int = 1
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigurationError(FatalError):
|
||||||
|
return_code = 2
|
||||||
|
|
||||||
|
|
||||||
|
class NothingToDoError(FatalError):
|
||||||
|
return_code = 3
|
||||||
|
|
||||||
|
|
||||||
|
class DeprecationError(FatalError):
|
||||||
|
return_code = 4
|
||||||
+51
-42
@@ -8,6 +8,9 @@ from dataclasses import dataclass
|
|||||||
from pathlib import Path, PurePath, PurePosixPath
|
from pathlib import Path, PurePath, PurePosixPath
|
||||||
from typing import OrderedDict, Tuple
|
from typing import OrderedDict, Tuple
|
||||||
|
|
||||||
|
from packaging.version import Version
|
||||||
|
|
||||||
|
from . import errors
|
||||||
from ._compat.typing import assert_never
|
from ._compat.typing import assert_never
|
||||||
from .architecture import Architecture
|
from .architecture import Architecture
|
||||||
from .logger import log
|
from .logger import log
|
||||||
@@ -145,11 +148,7 @@ def check_all_python_exist(
|
|||||||
exist = False
|
exist = False
|
||||||
if not exist:
|
if not exist:
|
||||||
message = "\n".join(messages)
|
message = "\n".join(messages)
|
||||||
print(
|
raise errors.FatalError(message)
|
||||||
f"cibuildwheel:\n{message}",
|
|
||||||
file=sys.stderr,
|
|
||||||
)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
def build_in_container(
|
def build_in_container(
|
||||||
@@ -194,6 +193,8 @@ def build_in_container(
|
|||||||
log.build_start(config.identifier)
|
log.build_start(config.identifier)
|
||||||
build_options = options.build_options(config.identifier)
|
build_options = options.build_options(config.identifier)
|
||||||
build_frontend = build_options.build_frontend or BuildFrontendConfig("pip")
|
build_frontend = build_options.build_frontend or BuildFrontendConfig("pip")
|
||||||
|
use_uv = build_frontend.name == "build[uv]" and Version(config.version) >= Version("3.8")
|
||||||
|
pip = ["uv", "pip"] if use_uv else ["pip"]
|
||||||
|
|
||||||
dependency_constraint_flags: list[PathOrStr] = []
|
dependency_constraint_flags: list[PathOrStr] = []
|
||||||
|
|
||||||
@@ -221,19 +222,19 @@ def build_in_container(
|
|||||||
# check config python is still on PATH
|
# check config python is still on PATH
|
||||||
which_python = container.call(["which", "python"], env=env, capture_output=True).strip()
|
which_python = container.call(["which", "python"], env=env, capture_output=True).strip()
|
||||||
if PurePosixPath(which_python) != python_bin / "python":
|
if PurePosixPath(which_python) != python_bin / "python":
|
||||||
print(
|
msg = "python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it."
|
||||||
"cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it.",
|
raise errors.FatalError(msg)
|
||||||
file=sys.stderr,
|
|
||||||
)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
which_pip = container.call(["which", "pip"], env=env, capture_output=True).strip()
|
if use_uv:
|
||||||
if PurePosixPath(which_pip) != python_bin / "pip":
|
which_uv = container.call(["which", "uv"], env=env, capture_output=True).strip()
|
||||||
print(
|
if not which_uv:
|
||||||
"cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it.",
|
msg = "uv not found on PATH. You must use a supported manylinux or musllinux environment with uv."
|
||||||
file=sys.stderr,
|
raise errors.FatalError(msg)
|
||||||
)
|
else:
|
||||||
sys.exit(1)
|
which_pip = container.call(["which", "pip"], env=env, capture_output=True).strip()
|
||||||
|
if PurePosixPath(which_pip) != python_bin / "pip":
|
||||||
|
msg = "pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it."
|
||||||
|
raise errors.FatalError(msg)
|
||||||
|
|
||||||
compatible_wheel = find_compatible_wheel(built_wheels, config.identifier)
|
compatible_wheel = find_compatible_wheel(built_wheels, config.identifier)
|
||||||
if compatible_wheel:
|
if compatible_wheel:
|
||||||
@@ -277,10 +278,12 @@ def build_in_container(
|
|||||||
],
|
],
|
||||||
env=env,
|
env=env,
|
||||||
)
|
)
|
||||||
elif build_frontend.name == "build":
|
elif build_frontend.name == "build" or build_frontend.name == "build[uv]":
|
||||||
if not 0 <= build_options.build_verbosity < 2:
|
if not 0 <= build_options.build_verbosity < 2:
|
||||||
msg = f"build_verbosity {build_options.build_verbosity} is not supported for build frontend. Ignoring."
|
msg = f"build_verbosity {build_options.build_verbosity} is not supported for build frontend. Ignoring."
|
||||||
log.warning(msg)
|
log.warning(msg)
|
||||||
|
if use_uv:
|
||||||
|
extra_flags += ["--installer=uv"]
|
||||||
container.call(
|
container.call(
|
||||||
[
|
[
|
||||||
"python",
|
"python",
|
||||||
@@ -325,20 +328,32 @@ def build_in_container(
|
|||||||
|
|
||||||
# set up a virtual environment to install and test from, to make sure
|
# set up a virtual environment to install and test from, to make sure
|
||||||
# there are no dependencies that were pulled in at build time.
|
# there are no dependencies that were pulled in at build time.
|
||||||
container.call(["pip", "install", "virtualenv", *dependency_constraint_flags], env=env)
|
if not use_uv:
|
||||||
|
container.call(
|
||||||
|
["pip", "install", "virtualenv", *dependency_constraint_flags], env=env
|
||||||
|
)
|
||||||
|
|
||||||
testing_temp_dir = PurePosixPath(
|
testing_temp_dir = PurePosixPath(
|
||||||
container.call(["mktemp", "-d"], capture_output=True).strip()
|
container.call(["mktemp", "-d"], capture_output=True).strip()
|
||||||
)
|
)
|
||||||
venv_dir = testing_temp_dir / "venv"
|
venv_dir = testing_temp_dir / "venv"
|
||||||
|
|
||||||
container.call(["python", "-m", "virtualenv", "--no-download", venv_dir], env=env)
|
if use_uv:
|
||||||
|
container.call(["uv", "venv", venv_dir], env=env)
|
||||||
|
else:
|
||||||
|
# Use embedded dependencies from virtualenv to ensure determinism
|
||||||
|
venv_args = ["--no-periodic-update", "--pip=embed"]
|
||||||
|
# In Python<3.12, setuptools & wheel are installed as well
|
||||||
|
if Version(config.version) < Version("3.12"):
|
||||||
|
venv_args.extend(("--setuptools=embed", "--wheel=embed"))
|
||||||
|
container.call(["python", "-m", "virtualenv", *venv_args, venv_dir], env=env)
|
||||||
|
|
||||||
virtualenv_env = env.copy()
|
virtualenv_env = env.copy()
|
||||||
virtualenv_env["PATH"] = f"{venv_dir / 'bin'}:{virtualenv_env['PATH']}"
|
virtualenv_env["PATH"] = f"{venv_dir / 'bin'}:{virtualenv_env['PATH']}"
|
||||||
|
virtualenv_env["VIRTUAL_ENV"] = str(venv_dir)
|
||||||
|
|
||||||
# TODO remove me once virtualenv provides pip>=24.1b1
|
# TODO remove me once virtualenv provides pip>=24.1b1
|
||||||
if config.version == "3.13":
|
if config.version == "3.13" and not use_uv:
|
||||||
container.call(["pip", "install", "pip>=24.1b1"], env=virtualenv_env)
|
container.call(["pip", "install", "pip>=24.1b1"], env=virtualenv_env)
|
||||||
|
|
||||||
if build_options.before_test:
|
if build_options.before_test:
|
||||||
@@ -357,13 +372,13 @@ def build_in_container(
|
|||||||
# Let's just pick the first one.
|
# Let's just pick the first one.
|
||||||
wheel_to_test = repaired_wheels[0]
|
wheel_to_test = repaired_wheels[0]
|
||||||
container.call(
|
container.call(
|
||||||
["pip", "install", str(wheel_to_test) + build_options.test_extras],
|
[*pip, "install", str(wheel_to_test) + build_options.test_extras],
|
||||||
env=virtualenv_env,
|
env=virtualenv_env,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Install any requirements to run the tests
|
# Install any requirements to run the tests
|
||||||
if build_options.test_requires:
|
if build_options.test_requires:
|
||||||
container.call(["pip", "install", *build_options.test_requires], env=virtualenv_env)
|
container.call([*pip, "install", *build_options.test_requires], env=virtualenv_env)
|
||||||
|
|
||||||
# Run the tests from a different directory
|
# Run the tests from a different directory
|
||||||
test_command_prepared = prepare_command(
|
test_command_prepared = prepare_command(
|
||||||
@@ -419,21 +434,18 @@ def build(options: Options, tmp_path: Path) -> None: # noqa: ARG001
|
|||||||
check=True,
|
check=True,
|
||||||
stdout=subprocess.DEVNULL,
|
stdout=subprocess.DEVNULL,
|
||||||
)
|
)
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError as error:
|
||||||
print(
|
msg = unwrap(
|
||||||
unwrap(
|
f"""
|
||||||
f"""
|
cibuildwheel: {build_step.container_engine.name} not found. An
|
||||||
cibuildwheel: {build_step.container_engine.name} not found. An
|
OCI exe like Docker or Podman is required to run Linux builds.
|
||||||
OCI exe like Docker or Podman is required to run Linux builds.
|
If you're building on Travis CI, add `services: [docker]` to
|
||||||
If you're building on Travis CI, add `services: [docker]` to
|
your .travis.yml. If you're building on Circle CI in Linux,
|
||||||
your .travis.yml. If you're building on Circle CI in Linux,
|
add a `setup_remote_docker` step to your .circleci/config.yml.
|
||||||
add a `setup_remote_docker` step to your .circleci/config.yml.
|
If you're building on Cirrus CI, use `docker_builder` task.
|
||||||
If you're building on Cirrus CI, use `docker_builder` task.
|
"""
|
||||||
"""
|
|
||||||
),
|
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
sys.exit(2)
|
raise errors.ConfigurationError(msg) from error
|
||||||
|
|
||||||
try:
|
try:
|
||||||
ids_to_build = [x.identifier for x in build_step.platform_configs]
|
ids_to_build = [x.identifier for x in build_step.platform_configs]
|
||||||
@@ -456,11 +468,9 @@ def build(options: Options, tmp_path: Path) -> None: # noqa: ARG001
|
|||||||
)
|
)
|
||||||
|
|
||||||
except subprocess.CalledProcessError as error:
|
except subprocess.CalledProcessError as error:
|
||||||
log.step_end_with_error(
|
|
||||||
f"Command {error.cmd} failed with code {error.returncode}. {error.stdout}"
|
|
||||||
)
|
|
||||||
troubleshoot(options, error)
|
troubleshoot(options, error)
|
||||||
sys.exit(1)
|
msg = f"Command {error.cmd} failed with code {error.returncode}. {error.stdout or ''}"
|
||||||
|
raise errors.FatalError(msg) from error
|
||||||
|
|
||||||
|
|
||||||
def _matches_prepared_command(error_cmd: Sequence[str], command_template: str) -> bool:
|
def _matches_prepared_command(error_cmd: Sequence[str], command_template: str) -> bool:
|
||||||
@@ -479,7 +489,6 @@ def troubleshoot(options: Options, error: Exception) -> None:
|
|||||||
) # TODO allow matching of overrides too?
|
) # TODO allow matching of overrides too?
|
||||||
):
|
):
|
||||||
# the wheel build step or the repair step failed
|
# the wheel build step or the repair step failed
|
||||||
print("Checking for common errors...")
|
|
||||||
so_files = list(options.globals.package_dir.glob("**/*.so"))
|
so_files = list(options.globals.package_dir.glob("**/*.so"))
|
||||||
|
|
||||||
if so_files:
|
if so_files:
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ PLATFORM_IDENTIFIER_DESCRIPTIONS: Final[dict[str, str]] = {
|
|||||||
"macosx_x86_64": "macOS x86_64",
|
"macosx_x86_64": "macOS x86_64",
|
||||||
"macosx_universal2": "macOS Universal 2 - x86_64 and arm64",
|
"macosx_universal2": "macOS Universal 2 - x86_64 and arm64",
|
||||||
"macosx_arm64": "macOS arm64 - Apple Silicon",
|
"macosx_arm64": "macOS arm64 - Apple Silicon",
|
||||||
|
"pyodide_wasm32": "Pyodide",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -151,6 +152,10 @@ class Logger:
|
|||||||
c = self.colors
|
c = self.colors
|
||||||
print(f"{c.bright_red}Error{c.end}: {error}\n", file=sys.stderr)
|
print(f"{c.bright_red}Error{c.end}: {error}\n", file=sys.stderr)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def step_active(self) -> bool:
|
||||||
|
return self.step_start_time is not None
|
||||||
|
|
||||||
def _start_fold_group(self, name: str) -> None:
|
def _start_fold_group(self, name: str) -> None:
|
||||||
self._end_fold_group()
|
self._end_fold_group()
|
||||||
self.active_fold_group_name = name
|
self.active_fold_group_name = name
|
||||||
|
|||||||
+172
-74
@@ -1,6 +1,5 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import contextlib
|
|
||||||
import functools
|
import functools
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
@@ -15,7 +14,9 @@ from pathlib import Path
|
|||||||
from typing import Literal, Tuple
|
from typing import Literal, Tuple
|
||||||
|
|
||||||
from filelock import FileLock
|
from filelock import FileLock
|
||||||
|
from packaging.version import Version
|
||||||
|
|
||||||
|
from . import errors
|
||||||
from ._compat.typing import assert_never
|
from ._compat.typing import assert_never
|
||||||
from .architecture import Architecture
|
from .architecture import Architecture
|
||||||
from .environment import ParsedEnvironment
|
from .environment import ParsedEnvironment
|
||||||
@@ -30,12 +31,16 @@ from .util import (
|
|||||||
BuildSelector,
|
BuildSelector,
|
||||||
NonPlatformWheelError,
|
NonPlatformWheelError,
|
||||||
call,
|
call,
|
||||||
|
combine_constraints,
|
||||||
detect_ci_provider,
|
detect_ci_provider,
|
||||||
download,
|
download,
|
||||||
find_compatible_wheel,
|
find_compatible_wheel,
|
||||||
|
find_uv,
|
||||||
|
free_thread_enable_313,
|
||||||
get_build_verbosity_extra_flags,
|
get_build_verbosity_extra_flags,
|
||||||
get_pip_version,
|
get_pip_version,
|
||||||
install_certifi_script,
|
install_certifi_script,
|
||||||
|
move_file,
|
||||||
prepare_command,
|
prepare_command,
|
||||||
read_python_configs,
|
read_python_configs,
|
||||||
shell,
|
shell,
|
||||||
@@ -46,6 +51,7 @@ from .util import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@functools.lru_cache(maxsize=None)
|
||||||
def get_macos_version() -> tuple[int, int]:
|
def get_macos_version() -> tuple[int, int]:
|
||||||
"""
|
"""
|
||||||
Returns the macOS major/minor version, as a tuple, e.g. (10, 15) or (11, 0)
|
Returns the macOS major/minor version, as a tuple, e.g. (10, 15) or (11, 0)
|
||||||
@@ -57,9 +63,29 @@ def get_macos_version() -> tuple[int, int]:
|
|||||||
"""
|
"""
|
||||||
version_str, _, _ = platform.mac_ver()
|
version_str, _, _ = platform.mac_ver()
|
||||||
version = tuple(map(int, version_str.split(".")[:2]))
|
version = tuple(map(int, version_str.split(".")[:2]))
|
||||||
|
if (10, 15) < version < (11, 0):
|
||||||
|
# When built against an older macOS SDK, Python will report macOS 10.16
|
||||||
|
# instead of the real version.
|
||||||
|
version_str = call(
|
||||||
|
sys.executable,
|
||||||
|
"-sS",
|
||||||
|
"-c",
|
||||||
|
"import platform; print(platform.mac_ver()[0])",
|
||||||
|
env={"SYSTEM_VERSION_COMPAT": "0"},
|
||||||
|
capture_stdout=True,
|
||||||
|
)
|
||||||
|
version = tuple(map(int, version_str.split(".")[:2]))
|
||||||
return typing.cast(Tuple[int, int], version)
|
return typing.cast(Tuple[int, int], version)
|
||||||
|
|
||||||
|
|
||||||
|
@functools.lru_cache(maxsize=None)
|
||||||
|
def get_test_macosx_deployment_target() -> str:
|
||||||
|
version = get_macos_version()
|
||||||
|
if version >= (11, 0):
|
||||||
|
return f"{version[0]}.0"
|
||||||
|
return f"{version[0]}.{version[1]}"
|
||||||
|
|
||||||
|
|
||||||
def get_macos_sdks() -> list[str]:
|
def get_macos_sdks() -> list[str]:
|
||||||
output = call("xcodebuild", "-showsdks", capture_stdout=True)
|
output = call("xcodebuild", "-showsdks", capture_stdout=True)
|
||||||
return [m.group(1) for m in re.finditer(r"-sdk (macosx\S+)", output)]
|
return [m.group(1) for m in re.finditer(r"-sdk (macosx\S+)", output)]
|
||||||
@@ -114,35 +140,44 @@ def get_python_configurations(
|
|||||||
return python_configurations
|
return python_configurations
|
||||||
|
|
||||||
|
|
||||||
def install_cpython(tmp: Path, version: str, url: str) -> Path:
|
def install_cpython(tmp: Path, version: str, url: str, free_threading: bool) -> Path:
|
||||||
installation_path = Path(f"/Library/Frameworks/Python.framework/Versions/{version}")
|
ft = "T" if free_threading else ""
|
||||||
|
installation_path = Path(f"/Library/Frameworks/Python{ft}.framework/Versions/{version}")
|
||||||
with FileLock(CIBW_CACHE_PATH / f"cpython{version}.lock"):
|
with FileLock(CIBW_CACHE_PATH / f"cpython{version}.lock"):
|
||||||
installed_system_packages = call("pkgutil", "--pkgs", capture_stdout=True).splitlines()
|
installed_system_packages = call("pkgutil", "--pkgs", capture_stdout=True).splitlines()
|
||||||
# if this version of python isn't installed, get it from python.org and install
|
# if this version of python isn't installed, get it from python.org and install
|
||||||
python_package_identifier = f"org.python.Python.PythonFramework-{version}"
|
python_package_identifier = f"org.python.Python.Python{ft}Framework-{version}"
|
||||||
if python_package_identifier not in installed_system_packages:
|
if python_package_identifier not in installed_system_packages:
|
||||||
if detect_ci_provider() is None:
|
if detect_ci_provider() is None:
|
||||||
# if running locally, we don't want to install CPython with sudo
|
# if running locally, we don't want to install CPython with sudo
|
||||||
# let the user know & provide a link to the installer
|
# let the user know & provide a link to the installer
|
||||||
print(
|
msg = (
|
||||||
f"Error: CPython {version} is not installed.\n"
|
f"Error: CPython {version} is not installed.\n"
|
||||||
"cibuildwheel will not perform system-wide installs when running outside of CI.\n"
|
"cibuildwheel will not perform system-wide installs when running outside of CI.\n"
|
||||||
f"To build locally, install CPython {version} on this machine, or, disable this version of Python using CIBW_SKIP=cp{version.replace('.', '')}-macosx_*\n"
|
f"To build locally, install CPython {version} on this machine, or, disable this version of Python using CIBW_SKIP=cp{version.replace('.', '')}-macosx_*\n"
|
||||||
f"\nDownload link: {url}",
|
f"\nDownload link: {url}"
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
raise SystemExit(1)
|
raise errors.FatalError(msg)
|
||||||
pkg_path = tmp / "Python.pkg"
|
pkg_path = tmp / "Python.pkg"
|
||||||
# download the pkg
|
# download the pkg
|
||||||
download(url, pkg_path)
|
download(url, pkg_path)
|
||||||
# install
|
# install
|
||||||
call("sudo", "installer", "-pkg", pkg_path, "-target", "/")
|
args = []
|
||||||
|
if version.startswith("3.13"):
|
||||||
|
# Python 3.13 is the first version to have a free-threading option
|
||||||
|
args += ["-applyChoiceChangesXML", str(free_thread_enable_313.resolve())]
|
||||||
|
call("sudo", "installer", "-pkg", pkg_path, *args, "-target", "/")
|
||||||
pkg_path.unlink()
|
pkg_path.unlink()
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1"
|
env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1"
|
||||||
call(installation_path / "bin" / "python3", install_certifi_script, env=env)
|
|
||||||
|
|
||||||
return installation_path / "bin" / "python3"
|
if free_threading:
|
||||||
|
call(installation_path / f"bin/python{version}t", "-m", "ensurepip", env=env)
|
||||||
|
call(installation_path / f"bin/python{version}t", install_certifi_script, env=env)
|
||||||
|
else:
|
||||||
|
call(installation_path / "bin/python3", install_certifi_script, env=env)
|
||||||
|
|
||||||
|
return installation_path / "bin" / (f"python{version}t" if free_threading else "python3")
|
||||||
|
|
||||||
|
|
||||||
def install_pypy(tmp: Path, url: str) -> Path:
|
def install_pypy(tmp: Path, url: str) -> Path:
|
||||||
@@ -166,23 +201,41 @@ def setup_python(
|
|||||||
dependency_constraint_flags: Sequence[PathOrStr],
|
dependency_constraint_flags: Sequence[PathOrStr],
|
||||||
environment: ParsedEnvironment,
|
environment: ParsedEnvironment,
|
||||||
build_frontend: BuildFrontendName,
|
build_frontend: BuildFrontendName,
|
||||||
) -> dict[str, str]:
|
) -> tuple[Path, dict[str, str]]:
|
||||||
|
if build_frontend == "build[uv]" and Version(python_configuration.version) < Version("3.8"):
|
||||||
|
build_frontend = "build"
|
||||||
|
|
||||||
|
uv_path = find_uv()
|
||||||
|
use_uv = build_frontend == "build[uv]" and Version(python_configuration.version) >= Version(
|
||||||
|
"3.8"
|
||||||
|
)
|
||||||
|
|
||||||
tmp.mkdir()
|
tmp.mkdir()
|
||||||
implementation_id = python_configuration.identifier.split("-")[0]
|
implementation_id = python_configuration.identifier.split("-")[0]
|
||||||
log.step(f"Installing Python {implementation_id}...")
|
log.step(f"Installing Python {implementation_id}...")
|
||||||
if implementation_id.startswith("cp"):
|
if implementation_id.startswith("cp"):
|
||||||
base_python = install_cpython(tmp, python_configuration.version, python_configuration.url)
|
free_threading = "t-macos" in python_configuration.identifier
|
||||||
|
base_python = install_cpython(
|
||||||
|
tmp, python_configuration.version, python_configuration.url, free_threading
|
||||||
|
)
|
||||||
|
|
||||||
elif implementation_id.startswith("pp"):
|
elif implementation_id.startswith("pp"):
|
||||||
base_python = install_pypy(tmp, python_configuration.url)
|
base_python = install_pypy(tmp, python_configuration.url)
|
||||||
else:
|
else:
|
||||||
msg = "Unknown Python implementation"
|
msg = "Unknown Python implementation"
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
assert base_python.exists()
|
assert (
|
||||||
|
base_python.exists()
|
||||||
|
), f"{base_python.name} not found, has {list(base_python.parent.iterdir())}"
|
||||||
|
|
||||||
log.step("Setting up build environment...")
|
log.step("Setting up build environment...")
|
||||||
venv_path = tmp / "venv"
|
venv_path = tmp / "venv"
|
||||||
env = virtualenv(
|
env = virtualenv(
|
||||||
python_configuration.version, base_python, venv_path, dependency_constraint_flags
|
python_configuration.version,
|
||||||
|
base_python,
|
||||||
|
venv_path,
|
||||||
|
dependency_constraint_flags,
|
||||||
|
use_uv=use_uv,
|
||||||
)
|
)
|
||||||
venv_bin_path = venv_path / "bin"
|
venv_bin_path = venv_path / "bin"
|
||||||
assert venv_bin_path.exists()
|
assert venv_bin_path.exists()
|
||||||
@@ -199,43 +252,43 @@ def setup_python(
|
|||||||
|
|
||||||
# upgrade pip to the version matching our constraints
|
# upgrade pip to the version matching our constraints
|
||||||
# if necessary, reinstall it to ensure that it's available on PATH as 'pip'
|
# if necessary, reinstall it to ensure that it's available on PATH as 'pip'
|
||||||
call(
|
if build_frontend == "build[uv]":
|
||||||
"python",
|
assert uv_path is not None
|
||||||
"-m",
|
pip = [str(uv_path), "pip"]
|
||||||
"pip",
|
else:
|
||||||
"install",
|
pip = ["python", "-m", "pip"]
|
||||||
"--upgrade",
|
|
||||||
"pip",
|
if not use_uv:
|
||||||
*dependency_constraint_flags,
|
call(
|
||||||
env=env,
|
*pip,
|
||||||
cwd=venv_path,
|
"install",
|
||||||
)
|
"--upgrade",
|
||||||
|
"pip",
|
||||||
|
*dependency_constraint_flags,
|
||||||
|
env=env,
|
||||||
|
cwd=venv_path,
|
||||||
|
)
|
||||||
|
|
||||||
# Apply our environment after pip is ready
|
# Apply our environment after pip is ready
|
||||||
env = environment.as_dictionary(prev_environment=env)
|
env = environment.as_dictionary(prev_environment=env)
|
||||||
|
|
||||||
# check what pip version we're on
|
# check what pip version we're on
|
||||||
assert (venv_bin_path / "pip").exists()
|
if not use_uv:
|
||||||
call("which", "pip", env=env)
|
assert (venv_bin_path / "pip").exists()
|
||||||
call("pip", "--version", env=env)
|
call("which", "pip", env=env)
|
||||||
which_pip = call("which", "pip", env=env, capture_stdout=True).strip()
|
call("pip", "--version", env=env)
|
||||||
if which_pip != str(venv_bin_path / "pip"):
|
which_pip = call("which", "pip", env=env, capture_stdout=True).strip()
|
||||||
print(
|
if which_pip != str(venv_bin_path / "pip"):
|
||||||
"cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it.",
|
msg = "cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it."
|
||||||
file=sys.stderr,
|
raise errors.FatalError(msg)
|
||||||
)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
# check what Python version we're on
|
# check what Python version we're on
|
||||||
call("which", "python", env=env)
|
call("which", "python", env=env)
|
||||||
call("python", "--version", env=env)
|
call("python", "--version", env=env)
|
||||||
which_python = call("which", "python", env=env, capture_stdout=True).strip()
|
which_python = call("which", "python", env=env, capture_stdout=True).strip()
|
||||||
if which_python != str(venv_bin_path / "python"):
|
if which_python != str(venv_bin_path / "python"):
|
||||||
print(
|
msg = "cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it."
|
||||||
"cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it.",
|
raise errors.FatalError(msg)
|
||||||
file=sys.stderr,
|
|
||||||
)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
config_is_arm64 = python_configuration.identifier.endswith("arm64")
|
config_is_arm64 = python_configuration.identifier.endswith("arm64")
|
||||||
config_is_universal2 = python_configuration.identifier.endswith("universal2")
|
config_is_universal2 = python_configuration.identifier.endswith("universal2")
|
||||||
@@ -243,8 +296,25 @@ def setup_python(
|
|||||||
# Set MACOSX_DEPLOYMENT_TARGET, if the user didn't set it.
|
# Set MACOSX_DEPLOYMENT_TARGET, if the user didn't set it.
|
||||||
# For arm64, the minimal deployment target is 11.0.
|
# For arm64, the minimal deployment target is 11.0.
|
||||||
# On x86_64 (or universal2), use 10.9 as a default.
|
# On x86_64 (or universal2), use 10.9 as a default.
|
||||||
# PyPy defaults to 10.7, causing inconsistencies if it's left unset.
|
# CPython 3.13 needs 10.13.
|
||||||
env.setdefault("MACOSX_DEPLOYMENT_TARGET", "11.0" if config_is_arm64 else "10.9")
|
if config_is_arm64:
|
||||||
|
default_target = "11.0"
|
||||||
|
elif Version(python_configuration.version) >= Version("3.13"):
|
||||||
|
default_target = "10.13"
|
||||||
|
elif python_configuration.identifier.startswith("pp") and Version(
|
||||||
|
python_configuration.version
|
||||||
|
) >= Version("3.9"):
|
||||||
|
default_target = "10.15"
|
||||||
|
else:
|
||||||
|
default_target = "10.9"
|
||||||
|
env.setdefault("MACOSX_DEPLOYMENT_TARGET", default_target)
|
||||||
|
|
||||||
|
# This is a floor, it can't be set lower than the default_target.
|
||||||
|
if Version(env["MACOSX_DEPLOYMENT_TARGET"]) < Version(default_target):
|
||||||
|
log.warning(
|
||||||
|
f"Bumping MACOSX_DEPLOYMENT_TARGET ({env['MACOSX_DEPLOYMENT_TARGET']}) to the minimum required ({default_target})."
|
||||||
|
)
|
||||||
|
env["MACOSX_DEPLOYMENT_TARGET"] = default_target
|
||||||
|
|
||||||
if python_configuration.version not in {"3.6", "3.7"}:
|
if python_configuration.version not in {"3.6", "3.7"}:
|
||||||
if config_is_arm64:
|
if config_is_arm64:
|
||||||
@@ -303,10 +373,22 @@ def setup_python(
|
|||||||
*dependency_constraint_flags,
|
*dependency_constraint_flags,
|
||||||
env=env,
|
env=env,
|
||||||
)
|
)
|
||||||
|
elif build_frontend == "build[uv]":
|
||||||
|
assert uv_path is not None
|
||||||
|
call(
|
||||||
|
uv_path,
|
||||||
|
"pip",
|
||||||
|
"install",
|
||||||
|
"--upgrade",
|
||||||
|
"delocate",
|
||||||
|
"build[virtualenv, uv]",
|
||||||
|
*dependency_constraint_flags,
|
||||||
|
env=env,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
assert_never(build_frontend)
|
assert_never(build_frontend)
|
||||||
|
|
||||||
return env
|
return base_python, env
|
||||||
|
|
||||||
|
|
||||||
def build(options: Options, tmp_path: Path) -> None:
|
def build(options: Options, tmp_path: Path) -> None:
|
||||||
@@ -335,6 +417,14 @@ def build(options: Options, tmp_path: Path) -> None:
|
|||||||
for config in python_configurations:
|
for config in python_configurations:
|
||||||
build_options = options.build_options(config.identifier)
|
build_options = options.build_options(config.identifier)
|
||||||
build_frontend = build_options.build_frontend or BuildFrontendConfig("pip")
|
build_frontend = build_options.build_frontend or BuildFrontendConfig("pip")
|
||||||
|
use_uv = build_frontend.name == "build[uv]" and Version(config.version) >= Version(
|
||||||
|
"3.8"
|
||||||
|
)
|
||||||
|
uv_path = find_uv()
|
||||||
|
if use_uv and uv_path is None:
|
||||||
|
msg = "uv not found"
|
||||||
|
raise AssertionError(msg)
|
||||||
|
pip = ["pip"] if not use_uv else [str(uv_path), "pip"]
|
||||||
log.build_start(config.identifier)
|
log.build_start(config.identifier)
|
||||||
|
|
||||||
identifier_tmp_dir = tmp_path / config.identifier
|
identifier_tmp_dir = tmp_path / config.identifier
|
||||||
@@ -352,13 +442,15 @@ def build(options: Options, tmp_path: Path) -> None:
|
|||||||
build_options.dependency_constraints.get_for_python_version(config.version),
|
build_options.dependency_constraints.get_for_python_version(config.version),
|
||||||
]
|
]
|
||||||
|
|
||||||
env = setup_python(
|
base_python, env = setup_python(
|
||||||
identifier_tmp_dir / "build",
|
identifier_tmp_dir / "build",
|
||||||
config,
|
config,
|
||||||
dependency_constraint_flags,
|
dependency_constraint_flags,
|
||||||
build_options.environment,
|
build_options.environment,
|
||||||
build_frontend.name,
|
build_frontend.name,
|
||||||
)
|
)
|
||||||
|
if not use_uv:
|
||||||
|
pip_version = get_pip_version(env)
|
||||||
|
|
||||||
compatible_wheel = find_compatible_wheel(built_wheels, config.identifier)
|
compatible_wheel = find_compatible_wheel(built_wheels, config.identifier)
|
||||||
if compatible_wheel:
|
if compatible_wheel:
|
||||||
@@ -384,15 +476,14 @@ def build(options: Options, tmp_path: Path) -> None:
|
|||||||
extra_flags += build_frontend.args
|
extra_flags += build_frontend.args
|
||||||
|
|
||||||
build_env = env.copy()
|
build_env = env.copy()
|
||||||
build_env["VIRTUALENV_PIP"] = get_pip_version(env)
|
if not use_uv:
|
||||||
|
build_env["VIRTUALENV_PIP"] = pip_version
|
||||||
if build_options.dependency_constraints:
|
if build_options.dependency_constraints:
|
||||||
constraint_path = build_options.dependency_constraints.get_for_python_version(
|
constraint_path = build_options.dependency_constraints.get_for_python_version(
|
||||||
config.version
|
config.version
|
||||||
)
|
)
|
||||||
user_constraints = build_env.get("PIP_CONSTRAINT")
|
combine_constraints(
|
||||||
our_constraints = constraint_path.as_uri()
|
build_env, constraint_path, identifier_tmp_dir if use_uv else None
|
||||||
build_env["PIP_CONSTRAINT"] = " ".join(
|
|
||||||
c for c in [user_constraints, our_constraints] if c
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if build_frontend.name == "pip":
|
if build_frontend.name == "pip":
|
||||||
@@ -410,10 +501,12 @@ def build(options: Options, tmp_path: Path) -> None:
|
|||||||
*extra_flags,
|
*extra_flags,
|
||||||
env=build_env,
|
env=build_env,
|
||||||
)
|
)
|
||||||
elif build_frontend.name == "build":
|
elif build_frontend.name == "build" or build_frontend.name == "build[uv]":
|
||||||
if not 0 <= build_options.build_verbosity < 2:
|
if not 0 <= build_options.build_verbosity < 2:
|
||||||
msg = f"build_verbosity {build_options.build_verbosity} is not supported for build frontend. Ignoring."
|
msg = f"build_verbosity {build_options.build_verbosity} is not supported for build frontend. Ignoring."
|
||||||
log.warning(msg)
|
log.warning(msg)
|
||||||
|
if use_uv:
|
||||||
|
extra_flags.append("--installer=uv")
|
||||||
call(
|
call(
|
||||||
"python",
|
"python",
|
||||||
"-m",
|
"-m",
|
||||||
@@ -543,15 +636,18 @@ def build(options: Options, tmp_path: Path) -> None:
|
|||||||
|
|
||||||
# set up a virtual environment to install and test from, to make sure
|
# set up a virtual environment to install and test from, to make sure
|
||||||
# there are no dependencies that were pulled in at build time.
|
# there are no dependencies that were pulled in at build time.
|
||||||
call("pip", "install", "virtualenv", *dependency_constraint_flags, env=env)
|
if not use_uv:
|
||||||
|
call("pip", "install", "virtualenv", *dependency_constraint_flags, env=env)
|
||||||
|
|
||||||
venv_dir = identifier_tmp_dir / f"venv-test-{testing_arch}"
|
venv_dir = identifier_tmp_dir / f"venv-test-{testing_arch}"
|
||||||
|
|
||||||
arch_prefix = []
|
arch_prefix = []
|
||||||
|
uv_arch_args = []
|
||||||
if testing_arch != machine_arch:
|
if testing_arch != machine_arch:
|
||||||
if machine_arch == "arm64" and testing_arch == "x86_64":
|
if machine_arch == "arm64" and testing_arch == "x86_64":
|
||||||
# rosetta2 will provide the emulation with just the arch prefix.
|
# rosetta2 will provide the emulation with just the arch prefix.
|
||||||
arch_prefix = ["arch", "-x86_64"]
|
arch_prefix = ["arch", "-x86_64"]
|
||||||
|
uv_arch_args = ["--python-platform", "x86_64-apple-darwin"]
|
||||||
else:
|
else:
|
||||||
msg = f"don't know how to emulate {testing_arch} on {machine_arch}"
|
msg = f"don't know how to emulate {testing_arch} on {machine_arch}"
|
||||||
raise RuntimeError(msg)
|
raise RuntimeError(msg)
|
||||||
@@ -560,25 +656,31 @@ def build(options: Options, tmp_path: Path) -> None:
|
|||||||
call_with_arch = functools.partial(call, *arch_prefix)
|
call_with_arch = functools.partial(call, *arch_prefix)
|
||||||
shell_with_arch = functools.partial(call, *arch_prefix, "/bin/sh", "-c")
|
shell_with_arch = functools.partial(call, *arch_prefix, "/bin/sh", "-c")
|
||||||
|
|
||||||
# Use --no-download to ensure determinism by using seed libraries
|
if use_uv:
|
||||||
# built into virtualenv
|
pip_install = functools.partial(call, *pip, "install", *uv_arch_args)
|
||||||
call_with_arch("python", "-m", "virtualenv", "--no-download", venv_dir, env=env)
|
call("uv", "venv", venv_dir, f"--python={base_python}", env=env)
|
||||||
|
else:
|
||||||
|
pip_install = functools.partial(call_with_arch, *pip, "install")
|
||||||
|
# Use pip version from the initial env to ensure determinism
|
||||||
|
venv_args = ["--no-periodic-update", f"--pip={pip_version}"]
|
||||||
|
# In Python<3.12, setuptools & wheel are installed as well, use virtualenv embedded ones
|
||||||
|
if Version(config.version) < Version("3.12"):
|
||||||
|
venv_args.extend(("--setuptools=embed", "--wheel=embed"))
|
||||||
|
call_with_arch("python", "-m", "virtualenv", *venv_args, venv_dir, env=env)
|
||||||
|
|
||||||
virtualenv_env = env.copy()
|
virtualenv_env = env.copy()
|
||||||
|
virtualenv_env["MACOSX_DEPLOYMENT_TARGET"] = get_test_macosx_deployment_target()
|
||||||
virtualenv_env["PATH"] = os.pathsep.join(
|
virtualenv_env["PATH"] = os.pathsep.join(
|
||||||
[
|
[
|
||||||
str(venv_dir / "bin"),
|
str(venv_dir / "bin"),
|
||||||
virtualenv_env["PATH"],
|
virtualenv_env["PATH"],
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
virtualenv_env["VIRTUAL_ENV"] = str(venv_dir)
|
||||||
|
|
||||||
# check that we are using the Python from the virtual environment
|
# check that we are using the Python from the virtual environment
|
||||||
call_with_arch("which", "python", env=virtualenv_env)
|
call_with_arch("which", "python", env=virtualenv_env)
|
||||||
|
|
||||||
# TODO remove me once virtualenv provides pip>=24.1b1
|
|
||||||
if config.version == "3.13":
|
|
||||||
call("python", "-m", "pip", "install", "pip>=24.1b1", env=virtualenv_env)
|
|
||||||
|
|
||||||
if build_options.before_test:
|
if build_options.before_test:
|
||||||
before_test_prepared = prepare_command(
|
before_test_prepared = prepare_command(
|
||||||
build_options.before_test,
|
build_options.before_test,
|
||||||
@@ -605,18 +707,14 @@ def build(options: Options, tmp_path: Path) -> None:
|
|||||||
else:
|
else:
|
||||||
virtualenv_env_install_wheel = virtualenv_env
|
virtualenv_env_install_wheel = virtualenv_env
|
||||||
|
|
||||||
call_with_arch(
|
pip_install(
|
||||||
"pip",
|
|
||||||
"install",
|
|
||||||
f"{repaired_wheel}{build_options.test_extras}",
|
f"{repaired_wheel}{build_options.test_extras}",
|
||||||
env=virtualenv_env_install_wheel,
|
env=virtualenv_env_install_wheel,
|
||||||
)
|
)
|
||||||
|
|
||||||
# test the wheel
|
# test the wheel
|
||||||
if build_options.test_requires:
|
if build_options.test_requires:
|
||||||
call_with_arch(
|
pip_install(
|
||||||
"pip",
|
|
||||||
"install",
|
|
||||||
*build_options.test_requires,
|
*build_options.test_requires,
|
||||||
env=virtualenv_env_install_wheel,
|
env=virtualenv_env_install_wheel,
|
||||||
)
|
)
|
||||||
@@ -639,18 +737,18 @@ def build(options: Options, tmp_path: Path) -> None:
|
|||||||
|
|
||||||
# we're all done here; move it to output (overwrite existing)
|
# we're all done here; move it to output (overwrite existing)
|
||||||
if compatible_wheel is None:
|
if compatible_wheel is None:
|
||||||
with contextlib.suppress(FileNotFoundError):
|
output_wheel = build_options.output_dir.joinpath(repaired_wheel.name)
|
||||||
(build_options.output_dir / repaired_wheel.name).unlink()
|
moved_wheel = move_file(repaired_wheel, output_wheel)
|
||||||
|
if moved_wheel != output_wheel.resolve():
|
||||||
shutil.move(str(repaired_wheel), build_options.output_dir)
|
log.warning(
|
||||||
built_wheels.append(build_options.output_dir / repaired_wheel.name)
|
"{repaired_wheel} was moved to {moved_wheel} instead of {output_wheel}"
|
||||||
|
)
|
||||||
|
built_wheels.append(output_wheel)
|
||||||
|
|
||||||
# clean up
|
# clean up
|
||||||
shutil.rmtree(identifier_tmp_dir)
|
shutil.rmtree(identifier_tmp_dir)
|
||||||
|
|
||||||
log.build_end()
|
log.build_end()
|
||||||
except subprocess.CalledProcessError as error:
|
except subprocess.CalledProcessError as error:
|
||||||
log.step_end_with_error(
|
msg = f"Command {error.cmd} failed with code {error.returncode}. {error.stdout or ''}"
|
||||||
f"Command {error.cmd} failed with code {error.returncode}. {error.stdout}"
|
raise errors.FatalError(msg) from error
|
||||||
)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|||||||
+12
-16
@@ -8,15 +8,14 @@ import difflib
|
|||||||
import enum
|
import enum
|
||||||
import functools
|
import functools
|
||||||
import shlex
|
import shlex
|
||||||
import sys
|
|
||||||
import textwrap
|
import textwrap
|
||||||
import traceback
|
|
||||||
from collections.abc import Callable, Generator, Iterable, Iterator, Set
|
from collections.abc import Callable, Generator, Iterable, Iterator, Set
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Literal, Mapping, Sequence, TypedDict, Union # noqa: TID251
|
from typing import Any, Literal, Mapping, Sequence, TypedDict, Union # noqa: TID251
|
||||||
|
|
||||||
from packaging.specifiers import SpecifierSet
|
from packaging.specifiers import SpecifierSet
|
||||||
|
|
||||||
|
from . import errors
|
||||||
from ._compat import tomllib
|
from ._compat import tomllib
|
||||||
from ._compat.typing import NotRequired, assert_never
|
from ._compat.typing import NotRequired, assert_never
|
||||||
from .architecture import Architecture
|
from .architecture import Architecture
|
||||||
@@ -52,6 +51,7 @@ class CommandLineArguments:
|
|||||||
print_build_identifiers: bool
|
print_build_identifiers: bool
|
||||||
allow_empty: bool
|
allow_empty: bool
|
||||||
prerelease_pythons: bool
|
prerelease_pythons: bool
|
||||||
|
debug_traceback: bool
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def defaults() -> CommandLineArguments:
|
def defaults() -> CommandLineArguments:
|
||||||
@@ -65,6 +65,7 @@ class CommandLineArguments:
|
|||||||
package_dir=Path("."),
|
package_dir=Path("."),
|
||||||
prerelease_pythons=False,
|
prerelease_pythons=False,
|
||||||
print_build_identifiers=False,
|
print_build_identifiers=False,
|
||||||
|
debug_traceback=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -597,18 +598,14 @@ class Options:
|
|||||||
try:
|
try:
|
||||||
build_frontend = BuildFrontendConfig.from_config_string(build_frontend_str)
|
build_frontend = BuildFrontendConfig.from_config_string(build_frontend_str)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
print(f"cibuildwheel: {e}", file=sys.stderr)
|
msg = f"Failed to parse build frontend. {e}"
|
||||||
sys.exit(2)
|
raise errors.ConfigurationError(msg) from e
|
||||||
|
|
||||||
try:
|
try:
|
||||||
environment = parse_environment(environment_config)
|
environment = parse_environment(environment_config)
|
||||||
except (EnvironmentParseError, ValueError):
|
except (EnvironmentParseError, ValueError) as e:
|
||||||
print(
|
msg = f"Malformed environment option {environment_config!r}"
|
||||||
f"cibuildwheel: Malformed environment option {environment_config!r}",
|
raise errors.ConfigurationError(msg) from e
|
||||||
file=sys.stderr,
|
|
||||||
)
|
|
||||||
traceback.print_exc(None, sys.stderr)
|
|
||||||
sys.exit(2)
|
|
||||||
|
|
||||||
# Pass through environment variables
|
# Pass through environment variables
|
||||||
if self.platform == "linux":
|
if self.platform == "linux":
|
||||||
@@ -680,9 +677,8 @@ class Options:
|
|||||||
try:
|
try:
|
||||||
container_engine = OCIContainerEngineConfig.from_config_string(container_engine_str)
|
container_engine = OCIContainerEngineConfig.from_config_string(container_engine_str)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
msg = f"cibuildwheel: Failed to parse container config. {e}"
|
msg = f"Failed to parse container config. {e}"
|
||||||
print(msg, file=sys.stderr)
|
raise errors.ConfigurationError(msg) from e
|
||||||
sys.exit(2)
|
|
||||||
|
|
||||||
return BuildOptions(
|
return BuildOptions(
|
||||||
globals=self.globals,
|
globals=self.globals,
|
||||||
@@ -862,6 +858,6 @@ def _get_pinned_container_images() -> Mapping[str, Mapping[str, str]]:
|
|||||||
def deprecated_selectors(name: str, selector: str, *, error: bool = False) -> None:
|
def deprecated_selectors(name: str, selector: str, *, error: bool = False) -> None:
|
||||||
if "p2" in selector or "p35" in selector:
|
if "p2" in selector or "p35" in selector:
|
||||||
msg = f"cibuildwheel 2.x no longer supports Python < 3.6. Please use the 1.x series or update {name}"
|
msg = f"cibuildwheel 2.x no longer supports Python < 3.6. Please use the 1.x series or update {name}"
|
||||||
print(msg, file=sys.stderr)
|
|
||||||
if error:
|
if error:
|
||||||
sys.exit(4)
|
raise errors.DeprecationError(msg)
|
||||||
|
log.warning(msg)
|
||||||
|
|||||||
@@ -0,0 +1,404 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
from collections.abc import Sequence, Set
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from filelock import FileLock
|
||||||
|
|
||||||
|
from . import errors
|
||||||
|
from .architecture import Architecture
|
||||||
|
from .environment import ParsedEnvironment
|
||||||
|
from .logger import log
|
||||||
|
from .options import Options
|
||||||
|
from .typing import PathOrStr
|
||||||
|
from .util import (
|
||||||
|
CIBW_CACHE_PATH,
|
||||||
|
AlreadyBuiltWheelError,
|
||||||
|
BuildFrontendConfig,
|
||||||
|
BuildSelector,
|
||||||
|
NonPlatformWheelError,
|
||||||
|
call,
|
||||||
|
download,
|
||||||
|
ensure_node,
|
||||||
|
extract_zip,
|
||||||
|
find_compatible_wheel,
|
||||||
|
get_pip_version,
|
||||||
|
move_file,
|
||||||
|
prepare_command,
|
||||||
|
read_python_configs,
|
||||||
|
shell,
|
||||||
|
split_config_settings,
|
||||||
|
test_fail_cwd_file,
|
||||||
|
virtualenv,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class PythonConfiguration:
|
||||||
|
version: str
|
||||||
|
identifier: str
|
||||||
|
pyodide_version: str
|
||||||
|
emscripten_version: str
|
||||||
|
node_version: str
|
||||||
|
|
||||||
|
|
||||||
|
def install_emscripten(tmp: Path, version: str) -> Path:
|
||||||
|
# We don't need to match the emsdk version to the version we install, but
|
||||||
|
# we do for stability
|
||||||
|
url = f"https://github.com/emscripten-core/emsdk/archive/refs/tags/{version}.zip"
|
||||||
|
installation_path = CIBW_CACHE_PATH / f"emsdk-{version}"
|
||||||
|
emsdk_path = installation_path / f"emsdk-{version}/emsdk"
|
||||||
|
emcc_path = installation_path / f"emsdk-{version}/upstream/emscripten/emcc"
|
||||||
|
with FileLock(f"{installation_path}.lock"):
|
||||||
|
if installation_path.exists():
|
||||||
|
return emcc_path
|
||||||
|
emsdk_zip = tmp / "emsdk.zip"
|
||||||
|
download(url, emsdk_zip)
|
||||||
|
installation_path.mkdir()
|
||||||
|
extract_zip(emsdk_zip, installation_path)
|
||||||
|
call(emsdk_path, "install", version)
|
||||||
|
call(emsdk_path, "activate", version)
|
||||||
|
|
||||||
|
return emcc_path
|
||||||
|
|
||||||
|
|
||||||
|
def install_xbuildenv(env: dict[str, str], pyodide_version: str) -> str:
|
||||||
|
pyodide_root = (
|
||||||
|
CIBW_CACHE_PATH
|
||||||
|
/ f".pyodide-xbuildenv-{pyodide_version}/{pyodide_version}/xbuildenv/pyodide-root"
|
||||||
|
)
|
||||||
|
with FileLock(CIBW_CACHE_PATH / "xbuildenv.lock"):
|
||||||
|
if pyodide_root.exists():
|
||||||
|
return str(pyodide_root)
|
||||||
|
|
||||||
|
# We don't want to mutate env but we need to delete any existing
|
||||||
|
# PYODIDE_ROOT so copy it first.
|
||||||
|
env = dict(env)
|
||||||
|
env.pop("PYODIDE_ROOT", None)
|
||||||
|
call(
|
||||||
|
"pyodide",
|
||||||
|
"xbuildenv",
|
||||||
|
"install",
|
||||||
|
pyodide_version,
|
||||||
|
env=env,
|
||||||
|
cwd=CIBW_CACHE_PATH,
|
||||||
|
)
|
||||||
|
return str(pyodide_root)
|
||||||
|
|
||||||
|
|
||||||
|
def get_base_python(identifier: str) -> Path:
|
||||||
|
implementation_id = identifier.split("-")[0]
|
||||||
|
majorminor = implementation_id[len("cp") :]
|
||||||
|
version_info = (int(majorminor[0]), int(majorminor[1:]))
|
||||||
|
if version_info == sys.version_info[:2]:
|
||||||
|
return Path(sys.executable)
|
||||||
|
|
||||||
|
major_minor = ".".join(str(v) for v in version_info)
|
||||||
|
python_name = f"python{major_minor}"
|
||||||
|
which_python = shutil.which(python_name)
|
||||||
|
if which_python is None:
|
||||||
|
msg = f"CPython {major_minor} is not installed."
|
||||||
|
raise errors.FatalError(msg)
|
||||||
|
return Path(which_python)
|
||||||
|
|
||||||
|
|
||||||
|
def setup_python(
|
||||||
|
tmp: Path,
|
||||||
|
python_configuration: PythonConfiguration,
|
||||||
|
dependency_constraint_flags: Sequence[PathOrStr],
|
||||||
|
environment: ParsedEnvironment,
|
||||||
|
) -> dict[str, str]:
|
||||||
|
base_python = get_base_python(python_configuration.identifier)
|
||||||
|
|
||||||
|
log.step("Setting up build environment...")
|
||||||
|
venv_path = tmp / "venv"
|
||||||
|
env = virtualenv(python_configuration.version, base_python, venv_path, [], use_uv=False)
|
||||||
|
venv_bin_path = venv_path / "bin"
|
||||||
|
assert venv_bin_path.exists()
|
||||||
|
env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1"
|
||||||
|
|
||||||
|
# upgrade pip to the version matching our constraints
|
||||||
|
# if necessary, reinstall it to ensure that it's available on PATH as 'pip'
|
||||||
|
call(
|
||||||
|
"python",
|
||||||
|
"-m",
|
||||||
|
"pip",
|
||||||
|
"install",
|
||||||
|
"--upgrade",
|
||||||
|
"pip",
|
||||||
|
*dependency_constraint_flags,
|
||||||
|
env=env,
|
||||||
|
cwd=venv_path,
|
||||||
|
)
|
||||||
|
|
||||||
|
env = environment.as_dictionary(prev_environment=env)
|
||||||
|
|
||||||
|
# check what pip version we're on
|
||||||
|
assert (venv_bin_path / "pip").exists()
|
||||||
|
call("which", "pip", env=env)
|
||||||
|
call("pip", "--version", env=env)
|
||||||
|
which_pip = call("which", "pip", env=env, capture_stdout=True).strip()
|
||||||
|
if which_pip != str(venv_bin_path / "pip"):
|
||||||
|
msg = "pip available on PATH doesn't match our venv instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it."
|
||||||
|
raise errors.FatalError(msg)
|
||||||
|
|
||||||
|
# check what Python version we're on
|
||||||
|
call("which", "python", env=env)
|
||||||
|
call("python", "--version", env=env)
|
||||||
|
which_python = call("which", "python", env=env, capture_stdout=True).strip()
|
||||||
|
if which_python != str(venv_bin_path / "python"):
|
||||||
|
msg = "python available on PATH doesn't match our venv instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it."
|
||||||
|
raise errors.FatalError(msg)
|
||||||
|
|
||||||
|
log.step("Installing build tools...")
|
||||||
|
call(
|
||||||
|
"pip",
|
||||||
|
"install",
|
||||||
|
"--upgrade",
|
||||||
|
"auditwheel-emscripten",
|
||||||
|
"build[virtualenv]",
|
||||||
|
"pyodide-build",
|
||||||
|
*dependency_constraint_flags,
|
||||||
|
env=env,
|
||||||
|
)
|
||||||
|
|
||||||
|
log.step("Installing emscripten...")
|
||||||
|
emcc_path = install_emscripten(tmp, python_configuration.emscripten_version)
|
||||||
|
|
||||||
|
env["PATH"] = os.pathsep.join([str(emcc_path.parent), env["PATH"]])
|
||||||
|
|
||||||
|
log.step("Installing Pyodide xbuildenv...")
|
||||||
|
env["PYODIDE_ROOT"] = install_xbuildenv(env, python_configuration.pyodide_version)
|
||||||
|
|
||||||
|
return env
|
||||||
|
|
||||||
|
|
||||||
|
def get_python_configurations(
|
||||||
|
build_selector: BuildSelector,
|
||||||
|
architectures: Set[Architecture], # noqa: ARG001
|
||||||
|
) -> list[PythonConfiguration]:
|
||||||
|
full_python_configs = read_python_configs("pyodide")
|
||||||
|
|
||||||
|
python_configurations = [PythonConfiguration(**item) for item in full_python_configs]
|
||||||
|
python_configurations = [c for c in python_configurations if build_selector(c.identifier)]
|
||||||
|
return python_configurations
|
||||||
|
|
||||||
|
|
||||||
|
def build(options: Options, tmp_path: Path) -> None:
|
||||||
|
python_configurations = get_python_configurations(
|
||||||
|
options.globals.build_selector, options.globals.architectures
|
||||||
|
)
|
||||||
|
|
||||||
|
if not python_configurations:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
before_all_options_identifier = python_configurations[0].identifier
|
||||||
|
before_all_options = options.build_options(before_all_options_identifier)
|
||||||
|
|
||||||
|
if before_all_options.before_all:
|
||||||
|
log.step("Running before_all...")
|
||||||
|
env = before_all_options.environment.as_dictionary(prev_environment=os.environ)
|
||||||
|
before_all_prepared = prepare_command(
|
||||||
|
before_all_options.before_all, project=".", package=before_all_options.package_dir
|
||||||
|
)
|
||||||
|
shell(before_all_prepared, env=env)
|
||||||
|
|
||||||
|
built_wheels: list[Path] = []
|
||||||
|
|
||||||
|
for config in python_configurations:
|
||||||
|
build_options = options.build_options(config.identifier)
|
||||||
|
build_frontend = build_options.build_frontend or BuildFrontendConfig("build")
|
||||||
|
|
||||||
|
if build_frontend.name == "pip":
|
||||||
|
msg = "The pyodide platform doesn't support pip frontend"
|
||||||
|
raise errors.FatalError(msg)
|
||||||
|
|
||||||
|
log.build_start(config.identifier)
|
||||||
|
|
||||||
|
identifier_tmp_dir = tmp_path / config.identifier
|
||||||
|
built_wheel_dir = identifier_tmp_dir / "built_wheel"
|
||||||
|
repaired_wheel_dir = identifier_tmp_dir / "repaired_wheel"
|
||||||
|
identifier_tmp_dir.mkdir()
|
||||||
|
built_wheel_dir.mkdir()
|
||||||
|
repaired_wheel_dir.mkdir()
|
||||||
|
|
||||||
|
dependency_constraint_flags: Sequence[PathOrStr] = []
|
||||||
|
if build_options.dependency_constraints:
|
||||||
|
constraints_path = build_options.dependency_constraints.get_for_python_version(
|
||||||
|
config.version, variant="pyodide"
|
||||||
|
)
|
||||||
|
dependency_constraint_flags = ["-c", constraints_path]
|
||||||
|
|
||||||
|
env = setup_python(
|
||||||
|
identifier_tmp_dir / "build",
|
||||||
|
config,
|
||||||
|
dependency_constraint_flags,
|
||||||
|
build_options.environment,
|
||||||
|
)
|
||||||
|
pip_version = get_pip_version(env)
|
||||||
|
# The Pyodide command line runner mounts all directories in the host
|
||||||
|
# filesystem into the Pyodide file system, except for the custom
|
||||||
|
# file systems /dev, /lib, /proc, and /tmp. Mounting the mount
|
||||||
|
# points for alternate file systems causes some mysterious failure
|
||||||
|
# of the process (it just quits without any clear error).
|
||||||
|
#
|
||||||
|
# Because of this, by default Pyodide can't see anything under /tmp.
|
||||||
|
# This environment variable tells it also to mount our temp
|
||||||
|
# directory.
|
||||||
|
oldmounts = ""
|
||||||
|
extra_mounts = [str(identifier_tmp_dir)]
|
||||||
|
if str(Path(".").resolve()).startswith("/tmp"):
|
||||||
|
extra_mounts.append(str(Path(".").resolve()))
|
||||||
|
|
||||||
|
if "_PYODIDE_EXTRA_MOUNTS" in env:
|
||||||
|
oldmounts = env["_PYODIDE_EXTRA_MOUNTS"] + ":"
|
||||||
|
env["_PYODIDE_EXTRA_MOUNTS"] = oldmounts + ":".join(extra_mounts)
|
||||||
|
|
||||||
|
compatible_wheel = find_compatible_wheel(built_wheels, config.identifier)
|
||||||
|
if compatible_wheel:
|
||||||
|
log.step_end()
|
||||||
|
print(
|
||||||
|
f"\nFound previously built wheel {compatible_wheel.name}, that's compatible with {config.identifier}. Skipping build step..."
|
||||||
|
)
|
||||||
|
built_wheel = compatible_wheel
|
||||||
|
else:
|
||||||
|
if build_options.before_build:
|
||||||
|
log.step("Running before_build...")
|
||||||
|
before_build_prepared = prepare_command(
|
||||||
|
build_options.before_build, project=".", package=build_options.package_dir
|
||||||
|
)
|
||||||
|
shell(before_build_prepared, env=env)
|
||||||
|
|
||||||
|
log.step("Building wheel...")
|
||||||
|
|
||||||
|
extra_flags = split_config_settings(build_options.config_settings, "build")
|
||||||
|
extra_flags += build_frontend.args
|
||||||
|
|
||||||
|
if not 0 <= build_options.build_verbosity < 2:
|
||||||
|
msg = f"build_verbosity {build_options.build_verbosity} is not supported for build frontend. Ignoring."
|
||||||
|
log.warning(msg)
|
||||||
|
|
||||||
|
build_env = env.copy()
|
||||||
|
if build_options.dependency_constraints:
|
||||||
|
our_constraints = str(constraints_path)
|
||||||
|
user_constraints = build_env.get("PIP_CONSTRAINT")
|
||||||
|
build_env["PIP_CONSTRAINT"] = " ".join(
|
||||||
|
c for c in [our_constraints, user_constraints] if c
|
||||||
|
)
|
||||||
|
build_env["VIRTUALENV_PIP"] = pip_version
|
||||||
|
call(
|
||||||
|
"pyodide",
|
||||||
|
"build",
|
||||||
|
build_options.package_dir,
|
||||||
|
f"--outdir={built_wheel_dir}",
|
||||||
|
*extra_flags,
|
||||||
|
env=build_env,
|
||||||
|
)
|
||||||
|
built_wheel = next(built_wheel_dir.glob("*.whl"))
|
||||||
|
|
||||||
|
if built_wheel.name.endswith("none-any.whl"):
|
||||||
|
raise NonPlatformWheelError()
|
||||||
|
|
||||||
|
if build_options.repair_command:
|
||||||
|
log.step("Repairing wheel...")
|
||||||
|
|
||||||
|
repair_command_prepared = prepare_command(
|
||||||
|
build_options.repair_command,
|
||||||
|
wheel=built_wheel,
|
||||||
|
dest_dir=repaired_wheel_dir,
|
||||||
|
)
|
||||||
|
shell(repair_command_prepared, env=env)
|
||||||
|
else:
|
||||||
|
shutil.move(str(built_wheel), repaired_wheel_dir)
|
||||||
|
|
||||||
|
repaired_wheel = next(repaired_wheel_dir.glob("*.whl"))
|
||||||
|
|
||||||
|
if repaired_wheel.name in {wheel.name for wheel in built_wheels}:
|
||||||
|
raise AlreadyBuiltWheelError(repaired_wheel.name)
|
||||||
|
|
||||||
|
if build_options.test_command and build_options.test_selector(config.identifier):
|
||||||
|
log.step("Testing wheel...")
|
||||||
|
|
||||||
|
venv_dir = identifier_tmp_dir / "venv-test"
|
||||||
|
# set up a virtual environment to install and test from, to make sure
|
||||||
|
# there are no dependencies that were pulled in at build time.
|
||||||
|
|
||||||
|
virtualenv_env = env.copy()
|
||||||
|
virtualenv_env["PATH"] = os.pathsep.join(
|
||||||
|
[
|
||||||
|
str(ensure_node(config.node_version)),
|
||||||
|
virtualenv_env["PATH"],
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
# pyodide venv uses virtualenv under the hood
|
||||||
|
# use the pip embedded with virtualenv & disable network updates
|
||||||
|
virtualenv_create_env = virtualenv_env.copy()
|
||||||
|
virtualenv_create_env["VIRTUALENV_PIP"] = pip_version
|
||||||
|
virtualenv_create_env["VIRTUALENV_NO_PERIODIC_UPDATE"] = "1"
|
||||||
|
|
||||||
|
call("pyodide", "venv", venv_dir, env=virtualenv_create_env)
|
||||||
|
|
||||||
|
virtualenv_env["PATH"] = os.pathsep.join(
|
||||||
|
[
|
||||||
|
str(venv_dir / "bin"),
|
||||||
|
virtualenv_env["PATH"],
|
||||||
|
]
|
||||||
|
)
|
||||||
|
virtualenv_env["VIRTUAL_ENV"] = str(venv_dir)
|
||||||
|
|
||||||
|
# check that we are using the Python from the virtual environment
|
||||||
|
call("which", "python", env=virtualenv_env)
|
||||||
|
|
||||||
|
if build_options.before_test:
|
||||||
|
before_test_prepared = prepare_command(
|
||||||
|
build_options.before_test,
|
||||||
|
project=".",
|
||||||
|
package=build_options.package_dir,
|
||||||
|
)
|
||||||
|
shell(before_test_prepared, env=virtualenv_env)
|
||||||
|
|
||||||
|
# install the wheel
|
||||||
|
call(
|
||||||
|
"pip",
|
||||||
|
"install",
|
||||||
|
f"{repaired_wheel}{build_options.test_extras}",
|
||||||
|
env=virtualenv_env,
|
||||||
|
)
|
||||||
|
|
||||||
|
# test the wheel
|
||||||
|
if build_options.test_requires:
|
||||||
|
call("pip", "install", *build_options.test_requires, env=virtualenv_env)
|
||||||
|
|
||||||
|
# run the tests from a temp dir, with an absolute path in the command
|
||||||
|
# (this ensures that Python runs the tests against the installed wheel
|
||||||
|
# and not the repo code)
|
||||||
|
test_command_prepared = prepare_command(
|
||||||
|
build_options.test_command,
|
||||||
|
project=Path(".").resolve(),
|
||||||
|
package=build_options.package_dir.resolve(),
|
||||||
|
)
|
||||||
|
|
||||||
|
test_cwd = identifier_tmp_dir / "test_cwd"
|
||||||
|
test_cwd.mkdir(exist_ok=True)
|
||||||
|
(test_cwd / "test_fail.py").write_text(test_fail_cwd_file.read_text())
|
||||||
|
|
||||||
|
shell(test_command_prepared, cwd=test_cwd, env=virtualenv_env)
|
||||||
|
|
||||||
|
# we're all done here; move it to output (overwrite existing)
|
||||||
|
if compatible_wheel is None:
|
||||||
|
output_wheel = build_options.output_dir.joinpath(repaired_wheel.name)
|
||||||
|
moved_wheel = move_file(repaired_wheel, output_wheel)
|
||||||
|
if moved_wheel != output_wheel.resolve():
|
||||||
|
log.warning(
|
||||||
|
"{repaired_wheel} was moved to {moved_wheel} instead of {output_wheel}"
|
||||||
|
)
|
||||||
|
built_wheels.append(output_wheel)
|
||||||
|
|
||||||
|
finally:
|
||||||
|
pass
|
||||||
@@ -120,12 +120,15 @@ python_configurations = [
|
|||||||
{ identifier = "cp311-macosx_x86_64", version = "3.11", url = "https://www.python.org/ftp/python/3.11.9/python-3.11.9-macos11.pkg" },
|
{ identifier = "cp311-macosx_x86_64", version = "3.11", url = "https://www.python.org/ftp/python/3.11.9/python-3.11.9-macos11.pkg" },
|
||||||
{ identifier = "cp311-macosx_arm64", version = "3.11", url = "https://www.python.org/ftp/python/3.11.9/python-3.11.9-macos11.pkg" },
|
{ identifier = "cp311-macosx_arm64", version = "3.11", url = "https://www.python.org/ftp/python/3.11.9/python-3.11.9-macos11.pkg" },
|
||||||
{ identifier = "cp311-macosx_universal2", version = "3.11", url = "https://www.python.org/ftp/python/3.11.9/python-3.11.9-macos11.pkg" },
|
{ identifier = "cp311-macosx_universal2", version = "3.11", url = "https://www.python.org/ftp/python/3.11.9/python-3.11.9-macos11.pkg" },
|
||||||
{ identifier = "cp312-macosx_x86_64", version = "3.12", url = "https://www.python.org/ftp/python/3.12.3/python-3.12.3-macos11.pkg" },
|
{ identifier = "cp312-macosx_x86_64", version = "3.12", url = "https://www.python.org/ftp/python/3.12.4/python-3.12.4-macos11.pkg" },
|
||||||
{ identifier = "cp312-macosx_arm64", version = "3.12", url = "https://www.python.org/ftp/python/3.12.3/python-3.12.3-macos11.pkg" },
|
{ identifier = "cp312-macosx_arm64", version = "3.12", url = "https://www.python.org/ftp/python/3.12.4/python-3.12.4-macos11.pkg" },
|
||||||
{ identifier = "cp312-macosx_universal2", version = "3.12", url = "https://www.python.org/ftp/python/3.12.3/python-3.12.3-macos11.pkg" },
|
{ identifier = "cp312-macosx_universal2", version = "3.12", url = "https://www.python.org/ftp/python/3.12.4/python-3.12.4-macos11.pkg" },
|
||||||
{ identifier = "cp313-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.0/python-3.13.0b1-macos11.pkg" },
|
{ identifier = "cp313-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.0/python-3.13.0b2-macos11.pkg" },
|
||||||
{ identifier = "cp313-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.0/python-3.13.0b1-macos11.pkg" },
|
{ identifier = "cp313-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.0/python-3.13.0b2-macos11.pkg" },
|
||||||
{ identifier = "cp313-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.0/python-3.13.0b1-macos11.pkg" },
|
{ identifier = "cp313-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.0/python-3.13.0b2-macos11.pkg" },
|
||||||
|
{ identifier = "cp313t-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.0/python-3.13.0b2-macos11.pkg" },
|
||||||
|
{ identifier = "cp313t-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.0/python-3.13.0b2-macos11.pkg" },
|
||||||
|
{ identifier = "cp313t-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.0/python-3.13.0b2-macos11.pkg" },
|
||||||
{ identifier = "pp37-macosx_x86_64", version = "3.7", url = "https://downloads.python.org/pypy/pypy3.7-v7.3.9-osx64.tar.bz2" },
|
{ identifier = "pp37-macosx_x86_64", version = "3.7", url = "https://downloads.python.org/pypy/pypy3.7-v7.3.9-osx64.tar.bz2" },
|
||||||
{ identifier = "pp38-macosx_x86_64", version = "3.8", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-macos_x86_64.tar.bz2" },
|
{ identifier = "pp38-macosx_x86_64", version = "3.8", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-macos_x86_64.tar.bz2" },
|
||||||
{ identifier = "pp38-macosx_arm64", version = "3.8", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-macos_arm64.tar.bz2" },
|
{ identifier = "pp38-macosx_arm64", version = "3.8", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-macos_arm64.tar.bz2" },
|
||||||
@@ -149,20 +152,25 @@ python_configurations = [
|
|||||||
{ identifier = "cp310-win_amd64", version = "3.10.11", arch = "64" },
|
{ identifier = "cp310-win_amd64", version = "3.10.11", arch = "64" },
|
||||||
{ identifier = "cp311-win32", version = "3.11.9", arch = "32" },
|
{ identifier = "cp311-win32", version = "3.11.9", arch = "32" },
|
||||||
{ identifier = "cp311-win_amd64", version = "3.11.9", arch = "64" },
|
{ identifier = "cp311-win_amd64", version = "3.11.9", arch = "64" },
|
||||||
{ identifier = "cp312-win32", version = "3.12.3", arch = "32" },
|
{ identifier = "cp312-win32", version = "3.12.4", arch = "32" },
|
||||||
{ identifier = "cp312-win_amd64", version = "3.12.3", arch = "64" },
|
{ identifier = "cp312-win_amd64", version = "3.12.4", arch = "64" },
|
||||||
{ identifier = "cp313-win32", version = "3.13.0-b1", arch = "32" },
|
{ identifier = "cp313-win32", version = "3.13.0-b2", arch = "32" },
|
||||||
{ identifier = "cp313t-win32", version = "3.13.0-b1", arch = "32" },
|
{ identifier = "cp313t-win32", version = "3.13.0-b2", arch = "32" },
|
||||||
{ identifier = "cp313-win_amd64", version = "3.13.0-b1", arch = "64" },
|
{ identifier = "cp313-win_amd64", version = "3.13.0-b2", arch = "64" },
|
||||||
{ identifier = "cp313t-win_amd64", version = "3.13.0-b1", arch = "64" },
|
{ identifier = "cp313t-win_amd64", version = "3.13.0-b2", arch = "64" },
|
||||||
{ identifier = "cp39-win_arm64", version = "3.9.10", arch = "ARM64" },
|
{ identifier = "cp39-win_arm64", version = "3.9.10", arch = "ARM64" },
|
||||||
{ identifier = "cp310-win_arm64", version = "3.10.11", arch = "ARM64" },
|
{ identifier = "cp310-win_arm64", version = "3.10.11", arch = "ARM64" },
|
||||||
{ identifier = "cp311-win_arm64", version = "3.11.9", arch = "ARM64" },
|
{ identifier = "cp311-win_arm64", version = "3.11.9", arch = "ARM64" },
|
||||||
{ identifier = "cp312-win_arm64", version = "3.12.3", arch = "ARM64" },
|
{ identifier = "cp312-win_arm64", version = "3.12.4", arch = "ARM64" },
|
||||||
{ identifier = "cp313-win_arm64", version = "3.13.0-b1", arch = "ARM64" },
|
{ identifier = "cp313-win_arm64", version = "3.13.0-b2", arch = "ARM64" },
|
||||||
{ identifier = "cp313t-win_arm64", version = "3.13.0-b1", arch = "ARM64" },
|
{ identifier = "cp313t-win_arm64", version = "3.13.0-b2", arch = "ARM64" },
|
||||||
{ identifier = "pp37-win_amd64", version = "3.7", arch = "64", url = "https://downloads.python.org/pypy/pypy3.7-v7.3.9-win64.zip" },
|
{ identifier = "pp37-win_amd64", version = "3.7", arch = "64", url = "https://downloads.python.org/pypy/pypy3.7-v7.3.9-win64.zip" },
|
||||||
{ identifier = "pp38-win_amd64", version = "3.8", arch = "64", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-win64.zip" },
|
{ identifier = "pp38-win_amd64", version = "3.8", arch = "64", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-win64.zip" },
|
||||||
{ identifier = "pp39-win_amd64", version = "3.9", arch = "64", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-win64.zip" },
|
{ identifier = "pp39-win_amd64", version = "3.9", arch = "64", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-win64.zip" },
|
||||||
{ identifier = "pp310-win_amd64", version = "3.10", arch = "64", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.16-win64.zip" },
|
{ identifier = "pp310-win_amd64", version = "3.10", arch = "64", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.16-win64.zip" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[pyodide]
|
||||||
|
python_configurations = [
|
||||||
|
{ identifier = "cp312-pyodide_wasm32", version = "3.12.1", pyodide_version = "0.26.1", emscripten_version = "3.1.58", node_version = "v20" },
|
||||||
|
]
|
||||||
|
|||||||
@@ -96,12 +96,13 @@
|
|||||||
},
|
},
|
||||||
"build-frontend": {
|
"build-frontend": {
|
||||||
"default": "default",
|
"default": "default",
|
||||||
"description": "Set the tool to use to build, either \"pip\" (default for now) or \"build\"",
|
"description": "Set the tool to use to build, either \"pip\" (default for now), \"build\", or \"build[uv]\"",
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{
|
{
|
||||||
"enum": [
|
"enum": [
|
||||||
"pip",
|
"pip",
|
||||||
"build",
|
"build",
|
||||||
|
"build[uv]",
|
||||||
"default"
|
"default"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -113,6 +114,10 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"pattern": "^build; ?args:"
|
"pattern": "^build; ?args:"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^build\\[uv\\]; ?args:"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
@@ -123,7 +128,8 @@
|
|||||||
"name": {
|
"name": {
|
||||||
"enum": [
|
"enum": [
|
||||||
"pip",
|
"pip",
|
||||||
"build"
|
"build",
|
||||||
|
"build[uv]"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"args": {
|
"args": {
|
||||||
@@ -765,6 +771,51 @@
|
|||||||
"$ref": "#/properties/test-requires"
|
"$ref": "#/properties/test-requires"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"pyodide": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"archs": {
|
||||||
|
"$ref": "#/properties/archs"
|
||||||
|
},
|
||||||
|
"before-all": {
|
||||||
|
"$ref": "#/properties/before-all"
|
||||||
|
},
|
||||||
|
"before-build": {
|
||||||
|
"$ref": "#/properties/before-build"
|
||||||
|
},
|
||||||
|
"before-test": {
|
||||||
|
"$ref": "#/properties/before-test"
|
||||||
|
},
|
||||||
|
"build-frontend": {
|
||||||
|
"$ref": "#/properties/build-frontend"
|
||||||
|
},
|
||||||
|
"build-verbosity": {
|
||||||
|
"$ref": "#/properties/build-verbosity"
|
||||||
|
},
|
||||||
|
"config-settings": {
|
||||||
|
"$ref": "#/properties/config-settings"
|
||||||
|
},
|
||||||
|
"dependency-versions": {
|
||||||
|
"$ref": "#/properties/dependency-versions"
|
||||||
|
},
|
||||||
|
"environment": {
|
||||||
|
"$ref": "#/properties/environment"
|
||||||
|
},
|
||||||
|
"repair-wheel-command": {
|
||||||
|
"$ref": "#/properties/repair-wheel-command"
|
||||||
|
},
|
||||||
|
"test-command": {
|
||||||
|
"$ref": "#/properties/test-command"
|
||||||
|
},
|
||||||
|
"test-extras": {
|
||||||
|
"$ref": "#/properties/test-extras"
|
||||||
|
},
|
||||||
|
"test-requires": {
|
||||||
|
"$ref": "#/properties/test-requires"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,123 @@
|
|||||||
|
# This file was autogenerated by uv via the following command:
|
||||||
|
# nox -s update_constraints
|
||||||
|
annotated-types==0.7.0
|
||||||
|
# via pydantic
|
||||||
|
anyio==4.4.0
|
||||||
|
# via httpx
|
||||||
|
auditwheel-emscripten==0.0.14
|
||||||
|
# via pyodide-build
|
||||||
|
build==1.2.1
|
||||||
|
# via
|
||||||
|
# -r .nox/update_constraints/tmp/constraints-pyodide.in
|
||||||
|
# pyodide-build
|
||||||
|
certifi==2024.6.2
|
||||||
|
# via
|
||||||
|
# httpcore
|
||||||
|
# httpx
|
||||||
|
# requests
|
||||||
|
charset-normalizer==3.3.2
|
||||||
|
# via requests
|
||||||
|
click==8.1.7
|
||||||
|
# via typer
|
||||||
|
cloudpickle==3.0.0
|
||||||
|
# via loky
|
||||||
|
cmake==3.29.5
|
||||||
|
# via pyodide-build
|
||||||
|
distlib==0.3.8
|
||||||
|
# via virtualenv
|
||||||
|
filelock==3.14.0
|
||||||
|
# via virtualenv
|
||||||
|
h11==0.14.0
|
||||||
|
# via httpcore
|
||||||
|
httpcore==1.0.5
|
||||||
|
# via httpx
|
||||||
|
httpx==0.27.0
|
||||||
|
# via unearth
|
||||||
|
idna==3.7
|
||||||
|
# via
|
||||||
|
# anyio
|
||||||
|
# httpx
|
||||||
|
# requests
|
||||||
|
leb128==1.0.7
|
||||||
|
# via auditwheel-emscripten
|
||||||
|
loky==3.4.1
|
||||||
|
# via pyodide-build
|
||||||
|
markdown-it-py==3.0.0
|
||||||
|
# via rich
|
||||||
|
mdurl==0.1.2
|
||||||
|
# via markdown-it-py
|
||||||
|
packaging==24.1
|
||||||
|
# via
|
||||||
|
# auditwheel-emscripten
|
||||||
|
# build
|
||||||
|
# pyodide-build
|
||||||
|
# unearth
|
||||||
|
pip==24.0
|
||||||
|
# via -r .nox/update_constraints/tmp/constraints-pyodide.in
|
||||||
|
platformdirs==4.2.2
|
||||||
|
# via virtualenv
|
||||||
|
pydantic==2.7.4
|
||||||
|
# via
|
||||||
|
# pyodide-build
|
||||||
|
# pyodide-lock
|
||||||
|
pydantic-core==2.18.4
|
||||||
|
# via pydantic
|
||||||
|
pygments==2.18.0
|
||||||
|
# via rich
|
||||||
|
pyodide-build==0.26.1
|
||||||
|
# via -r .nox/update_constraints/tmp/constraints-pyodide.in
|
||||||
|
pyodide-cli==0.2.3
|
||||||
|
# via
|
||||||
|
# auditwheel-emscripten
|
||||||
|
# pyodide-build
|
||||||
|
pyodide-lock==0.1.0a6
|
||||||
|
# via pyodide-build
|
||||||
|
pyproject-hooks==1.1.0
|
||||||
|
# via build
|
||||||
|
pyyaml==6.0.1
|
||||||
|
# via pyodide-build
|
||||||
|
requests==2.32.3
|
||||||
|
# via pyodide-build
|
||||||
|
resolvelib==1.0.1
|
||||||
|
# via pyodide-build
|
||||||
|
rich==13.7.1
|
||||||
|
# via
|
||||||
|
# pyodide-build
|
||||||
|
# pyodide-cli
|
||||||
|
# typer
|
||||||
|
ruamel-yaml==0.18.6
|
||||||
|
# via pyodide-build
|
||||||
|
ruamel-yaml-clib==0.2.8
|
||||||
|
# via ruamel-yaml
|
||||||
|
shellingham==1.5.4
|
||||||
|
# via typer
|
||||||
|
sniffio==1.3.1
|
||||||
|
# via
|
||||||
|
# anyio
|
||||||
|
# httpx
|
||||||
|
typer==0.12.3
|
||||||
|
# via
|
||||||
|
# auditwheel-emscripten
|
||||||
|
# pyodide-build
|
||||||
|
# pyodide-cli
|
||||||
|
types-requests==2.32.0.20240602
|
||||||
|
# via pyodide-build
|
||||||
|
typing-extensions==4.12.2
|
||||||
|
# via
|
||||||
|
# pydantic
|
||||||
|
# pydantic-core
|
||||||
|
# typer
|
||||||
|
unearth==0.15.4
|
||||||
|
# via pyodide-build
|
||||||
|
urllib3==2.2.1
|
||||||
|
# via
|
||||||
|
# requests
|
||||||
|
# types-requests
|
||||||
|
virtualenv==20.26.2
|
||||||
|
# via
|
||||||
|
# build
|
||||||
|
# pyodide-build
|
||||||
|
wheel==0.43.0
|
||||||
|
# via
|
||||||
|
# auditwheel-emscripten
|
||||||
|
# pyodide-build
|
||||||
@@ -14,7 +14,7 @@ importlib-metadata==7.1.0
|
|||||||
# via build
|
# via build
|
||||||
macholib==1.16.3
|
macholib==1.16.3
|
||||||
# via delocate
|
# via delocate
|
||||||
packaging==24.0
|
packaging==24.1
|
||||||
# via
|
# via
|
||||||
# build
|
# build
|
||||||
# delocate
|
# delocate
|
||||||
@@ -26,9 +26,9 @@ pyproject-hooks==1.1.0
|
|||||||
# via build
|
# via build
|
||||||
tomli==2.0.1
|
tomli==2.0.1
|
||||||
# via build
|
# via build
|
||||||
typing-extensions==4.11.0
|
typing-extensions==4.12.2
|
||||||
# via delocate
|
# via delocate
|
||||||
virtualenv==20.26.2
|
virtualenv==20.26.2
|
||||||
# via -r cibuildwheel/resources/constraints.in
|
# via -r cibuildwheel/resources/constraints.in
|
||||||
zipp==3.18.2
|
zipp==3.19.2
|
||||||
# via importlib-metadata
|
# via importlib-metadata
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ filelock==3.14.0
|
|||||||
# via virtualenv
|
# via virtualenv
|
||||||
macholib==1.16.3
|
macholib==1.16.3
|
||||||
# via delocate
|
# via delocate
|
||||||
packaging==24.0
|
packaging==24.1
|
||||||
# via
|
# via
|
||||||
# build
|
# build
|
||||||
# delocate
|
# delocate
|
||||||
@@ -22,7 +22,7 @@ platformdirs==4.2.2
|
|||||||
# via virtualenv
|
# via virtualenv
|
||||||
pyproject-hooks==1.1.0
|
pyproject-hooks==1.1.0
|
||||||
# via build
|
# via build
|
||||||
typing-extensions==4.11.0
|
typing-extensions==4.12.2
|
||||||
# via delocate
|
# via delocate
|
||||||
virtualenv==20.26.2
|
virtualenv==20.26.2
|
||||||
# via -r cibuildwheel/resources/constraints.in
|
# via -r cibuildwheel/resources/constraints.in
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ filelock==3.14.0
|
|||||||
# via virtualenv
|
# via virtualenv
|
||||||
macholib==1.16.3
|
macholib==1.16.3
|
||||||
# via delocate
|
# via delocate
|
||||||
packaging==24.0
|
packaging==24.1
|
||||||
# via
|
# via
|
||||||
# build
|
# build
|
||||||
# delocate
|
# delocate
|
||||||
@@ -22,7 +22,7 @@ platformdirs==4.2.2
|
|||||||
# via virtualenv
|
# via virtualenv
|
||||||
pyproject-hooks==1.1.0
|
pyproject-hooks==1.1.0
|
||||||
# via build
|
# via build
|
||||||
typing-extensions==4.11.0
|
typing-extensions==4.12.2
|
||||||
# via delocate
|
# via delocate
|
||||||
virtualenv==20.26.2
|
virtualenv==20.26.2
|
||||||
# via -r cibuildwheel/resources/constraints.in
|
# via -r cibuildwheel/resources/constraints.in
|
||||||
|
|||||||
@@ -12,17 +12,17 @@ filelock==3.14.0
|
|||||||
# via virtualenv
|
# via virtualenv
|
||||||
macholib==1.16.3
|
macholib==1.16.3
|
||||||
# via delocate
|
# via delocate
|
||||||
packaging==24.0
|
packaging==24.1
|
||||||
# via
|
# via
|
||||||
# build
|
# build
|
||||||
# delocate
|
# delocate
|
||||||
pip==24.1b1
|
pip==24.1b2
|
||||||
# via -r cibuildwheel/resources/constraints.in
|
# via -r cibuildwheel/resources/constraints.in
|
||||||
platformdirs==4.2.2
|
platformdirs==4.2.2
|
||||||
# via virtualenv
|
# via virtualenv
|
||||||
pyproject-hooks==1.1.0
|
pyproject-hooks==1.1.0
|
||||||
# via build
|
# via build
|
||||||
typing-extensions==4.11.0
|
typing-extensions==4.12.2
|
||||||
# via delocate
|
# via delocate
|
||||||
virtualenv==20.26.2
|
virtualenv==20.26.2
|
||||||
# via -r cibuildwheel/resources/constraints.in
|
# via -r cibuildwheel/resources/constraints.in
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ importlib-metadata==7.1.0
|
|||||||
# via build
|
# via build
|
||||||
macholib==1.16.3
|
macholib==1.16.3
|
||||||
# via delocate
|
# via delocate
|
||||||
packaging==24.0
|
packaging==24.1
|
||||||
# via
|
# via
|
||||||
# build
|
# build
|
||||||
# delocate
|
# delocate
|
||||||
@@ -26,9 +26,9 @@ pyproject-hooks==1.1.0
|
|||||||
# via build
|
# via build
|
||||||
tomli==2.0.1
|
tomli==2.0.1
|
||||||
# via build
|
# via build
|
||||||
typing-extensions==4.11.0
|
typing-extensions==4.12.2
|
||||||
# via delocate
|
# via delocate
|
||||||
virtualenv==20.26.2
|
virtualenv==20.26.2
|
||||||
# via -r cibuildwheel/resources/constraints.in
|
# via -r cibuildwheel/resources/constraints.in
|
||||||
zipp==3.18.2
|
zipp==3.19.2
|
||||||
# via importlib-metadata
|
# via importlib-metadata
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ importlib-metadata==7.1.0
|
|||||||
# via build
|
# via build
|
||||||
macholib==1.16.3
|
macholib==1.16.3
|
||||||
# via delocate
|
# via delocate
|
||||||
packaging==24.0
|
packaging==24.1
|
||||||
# via
|
# via
|
||||||
# build
|
# build
|
||||||
# delocate
|
# delocate
|
||||||
@@ -26,9 +26,9 @@ pyproject-hooks==1.1.0
|
|||||||
# via build
|
# via build
|
||||||
tomli==2.0.1
|
tomli==2.0.1
|
||||||
# via build
|
# via build
|
||||||
typing-extensions==4.11.0
|
typing-extensions==4.12.2
|
||||||
# via delocate
|
# via delocate
|
||||||
virtualenv==20.26.2
|
virtualenv==20.26.2
|
||||||
# via -r cibuildwheel/resources/constraints.in
|
# via -r cibuildwheel/resources/constraints.in
|
||||||
zipp==3.18.2
|
zipp==3.19.2
|
||||||
# via importlib-metadata
|
# via importlib-metadata
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ filelock==3.14.0
|
|||||||
# via virtualenv
|
# via virtualenv
|
||||||
macholib==1.16.3
|
macholib==1.16.3
|
||||||
# via delocate
|
# via delocate
|
||||||
packaging==24.0
|
packaging==24.1
|
||||||
# via
|
# via
|
||||||
# build
|
# build
|
||||||
# delocate
|
# delocate
|
||||||
@@ -22,7 +22,7 @@ platformdirs==4.2.2
|
|||||||
# via virtualenv
|
# via virtualenv
|
||||||
pyproject-hooks==1.1.0
|
pyproject-hooks==1.1.0
|
||||||
# via build
|
# via build
|
||||||
typing-extensions==4.11.0
|
typing-extensions==4.12.2
|
||||||
# via delocate
|
# via delocate
|
||||||
virtualenv==20.26.2
|
virtualenv==20.26.2
|
||||||
# via -r cibuildwheel/resources/constraints.in
|
# via -r cibuildwheel/resources/constraints.in
|
||||||
|
|||||||
@@ -46,3 +46,5 @@ repair-wheel-command = "auditwheel repair -w {dest_dir} {wheel}"
|
|||||||
repair-wheel-command = "delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}"
|
repair-wheel-command = "delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}"
|
||||||
|
|
||||||
[tool.cibuildwheel.windows]
|
[tool.cibuildwheel.windows]
|
||||||
|
|
||||||
|
[tool.cibuildwheel.pyodide]
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>attributeSetting</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>choiceAttribute</key>
|
||||||
|
<string>selected</string>
|
||||||
|
<key>choiceIdentifier</key>
|
||||||
|
<string>org.python.Python.PythonTFramework-3.13</string>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
url = "https://nodejs.org/dist/"
|
||||||
|
v20 = "v20.14.0"
|
||||||
@@ -1,54 +1,54 @@
|
|||||||
[x86_64]
|
[x86_64]
|
||||||
manylinux1 = quay.io/pypa/manylinux1_x86_64:2024-04-29-76807b8
|
manylinux1 = quay.io/pypa/manylinux1_x86_64:2024-04-29-76807b8
|
||||||
manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-08-05-4535177
|
manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-08-05-4535177
|
||||||
manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2024-05-13-0983f6f
|
manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2024.06.12-1
|
||||||
manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-12-26-0d38463
|
manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-12-26-0d38463
|
||||||
manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2024-05-13-0983f6f
|
manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2024.06.12-1
|
||||||
musllinux_1_1 = quay.io/pypa/musllinux_1_1_x86_64:2024-05-13-0983f6f
|
musllinux_1_1 = quay.io/pypa/musllinux_1_1_x86_64:2024.06.12-1
|
||||||
musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2024-05-13-0983f6f
|
musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2024.06.12-1
|
||||||
|
|
||||||
[i686]
|
[i686]
|
||||||
manylinux1 = quay.io/pypa/manylinux1_i686:2024-04-29-76807b8
|
manylinux1 = quay.io/pypa/manylinux1_i686:2024-04-29-76807b8
|
||||||
manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-08-05-4535177
|
manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-08-05-4535177
|
||||||
manylinux2014 = quay.io/pypa/manylinux2014_i686:2024-05-13-0983f6f
|
manylinux2014 = quay.io/pypa/manylinux2014_i686:2024.06.12-1
|
||||||
manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-12-26-0d38463
|
manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-12-26-0d38463
|
||||||
musllinux_1_1 = quay.io/pypa/musllinux_1_1_i686:2024-05-13-0983f6f
|
musllinux_1_1 = quay.io/pypa/musllinux_1_1_i686:2024.06.12-1
|
||||||
musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2024-05-13-0983f6f
|
musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2024.06.12-1
|
||||||
|
|
||||||
[pypy_x86_64]
|
[pypy_x86_64]
|
||||||
manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-08-05-4535177
|
manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-08-05-4535177
|
||||||
manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2024-05-13-0983f6f
|
manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2024.06.12-1
|
||||||
manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-12-26-0d38463
|
manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-12-26-0d38463
|
||||||
manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2024-05-13-0983f6f
|
manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2024.06.12-1
|
||||||
|
|
||||||
[pypy_i686]
|
[pypy_i686]
|
||||||
manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-08-05-4535177
|
manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-08-05-4535177
|
||||||
manylinux2014 = quay.io/pypa/manylinux2014_i686:2024-05-13-0983f6f
|
manylinux2014 = quay.io/pypa/manylinux2014_i686:2024.06.12-1
|
||||||
manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-12-26-0d38463
|
manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-12-26-0d38463
|
||||||
|
|
||||||
[aarch64]
|
[aarch64]
|
||||||
manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2024-05-13-0983f6f
|
manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2024.06.12-1
|
||||||
manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2022-12-26-0d38463
|
manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2022-12-26-0d38463
|
||||||
manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2024-05-13-0983f6f
|
manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2024.06.12-1
|
||||||
musllinux_1_1 = quay.io/pypa/musllinux_1_1_aarch64:2024-05-13-0983f6f
|
musllinux_1_1 = quay.io/pypa/musllinux_1_1_aarch64:2024.06.12-1
|
||||||
musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2024-05-13-0983f6f
|
musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2024.06.12-1
|
||||||
|
|
||||||
[ppc64le]
|
[ppc64le]
|
||||||
manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2024-05-13-0983f6f
|
manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2024.06.12-1
|
||||||
manylinux_2_24 = quay.io/pypa/manylinux_2_24_ppc64le:2022-12-26-0d38463
|
manylinux_2_24 = quay.io/pypa/manylinux_2_24_ppc64le:2022-12-26-0d38463
|
||||||
manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2024-05-13-0983f6f
|
manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2024.06.12-1
|
||||||
musllinux_1_1 = quay.io/pypa/musllinux_1_1_ppc64le:2024-05-13-0983f6f
|
musllinux_1_1 = quay.io/pypa/musllinux_1_1_ppc64le:2024.06.12-1
|
||||||
musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2024-05-13-0983f6f
|
musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2024.06.12-1
|
||||||
|
|
||||||
[s390x]
|
[s390x]
|
||||||
manylinux2014 = quay.io/pypa/manylinux2014_s390x:2024-05-13-0983f6f
|
manylinux2014 = quay.io/pypa/manylinux2014_s390x:2024.06.12-1
|
||||||
manylinux_2_24 = quay.io/pypa/manylinux_2_24_s390x:2022-12-26-0d38463
|
manylinux_2_24 = quay.io/pypa/manylinux_2_24_s390x:2022-12-26-0d38463
|
||||||
manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2024-05-13-0983f6f
|
manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2024.06.12-1
|
||||||
musllinux_1_1 = quay.io/pypa/musllinux_1_1_s390x:2024-05-13-0983f6f
|
musllinux_1_1 = quay.io/pypa/musllinux_1_1_s390x:2024.06.12-1
|
||||||
musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2024-05-13-0983f6f
|
musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2024.06.12-1
|
||||||
|
|
||||||
[pypy_aarch64]
|
[pypy_aarch64]
|
||||||
manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2024-05-13-0983f6f
|
manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2024.06.12-1
|
||||||
manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2022-12-26-0d38463
|
manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2022-12-26-0d38463
|
||||||
manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2024-05-13-0983f6f
|
manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2024.06.12-1
|
||||||
|
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ else:
|
|||||||
PathOrStr = Union[str, "os.PathLike[str]"]
|
PathOrStr = Union[str, "os.PathLike[str]"]
|
||||||
|
|
||||||
|
|
||||||
PlatformName = Literal["linux", "macos", "windows"]
|
PlatformName = Literal["linux", "macos", "windows", "pyodide"]
|
||||||
PLATFORMS: Final[set[PlatformName]] = {"linux", "macos", "windows"}
|
PLATFORMS: Final[set[PlatformName]] = {"linux", "macos", "windows", "pyodide"}
|
||||||
|
|
||||||
|
|
||||||
class GenericPythonConfiguration(Protocol):
|
class GenericPythonConfiguration(Protocol):
|
||||||
|
|||||||
+201
-52
@@ -6,21 +6,25 @@ import itertools
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import shlex
|
import shlex
|
||||||
|
import shutil
|
||||||
import ssl
|
import ssl
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
import tarfile
|
||||||
import textwrap
|
import textwrap
|
||||||
import time
|
import time
|
||||||
import typing
|
import typing
|
||||||
import urllib.request
|
import urllib.request
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from collections.abc import Generator, Iterable, Mapping, Sequence
|
from collections.abc import Generator, Iterable, Mapping, MutableMapping, Sequence
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from functools import cached_property, lru_cache
|
from functools import cached_property, lru_cache
|
||||||
from pathlib import Path, PurePath
|
from pathlib import Path, PurePath
|
||||||
|
from tempfile import TemporaryDirectory
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from typing import Any, ClassVar, Final, Literal, TextIO, TypeVar
|
from typing import Any, ClassVar, Final, Literal, TextIO, TypeVar
|
||||||
|
from zipfile import ZipFile
|
||||||
|
|
||||||
import bracex
|
import bracex
|
||||||
import certifi
|
import certifi
|
||||||
@@ -32,29 +36,34 @@ from packaging.version import Version
|
|||||||
from platformdirs import user_cache_path
|
from platformdirs import user_cache_path
|
||||||
|
|
||||||
from ._compat import tomllib
|
from ._compat import tomllib
|
||||||
|
from .architecture import Architecture
|
||||||
from .typing import PathOrStr, PlatformName
|
from .typing import PathOrStr, PlatformName
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"resources_dir",
|
|
||||||
"MANYLINUX_ARCHS",
|
"MANYLINUX_ARCHS",
|
||||||
"call",
|
|
||||||
"shell",
|
|
||||||
"find_compatible_wheel",
|
|
||||||
"format_safe",
|
|
||||||
"prepare_command",
|
|
||||||
"get_build_verbosity_extra_flags",
|
|
||||||
"read_python_configs",
|
|
||||||
"selector_matches",
|
|
||||||
"strtobool",
|
|
||||||
"cached_property",
|
"cached_property",
|
||||||
|
"call",
|
||||||
"chdir",
|
"chdir",
|
||||||
|
"combine_constraints",
|
||||||
|
"find_compatible_wheel",
|
||||||
|
"find_uv",
|
||||||
|
"format_safe",
|
||||||
|
"get_build_verbosity_extra_flags",
|
||||||
|
"prepare_command",
|
||||||
|
"read_python_configs",
|
||||||
|
"resources_dir",
|
||||||
|
"selector_matches",
|
||||||
|
"shell",
|
||||||
"split_config_settings",
|
"split_config_settings",
|
||||||
|
"strtobool",
|
||||||
]
|
]
|
||||||
|
|
||||||
resources_dir: Final[Path] = Path(__file__).parent / "resources"
|
resources_dir: Final[Path] = Path(__file__).parent / "resources"
|
||||||
|
|
||||||
install_certifi_script: Final[Path] = resources_dir / "install_certifi.py"
|
install_certifi_script: Final[Path] = resources_dir / "install_certifi.py"
|
||||||
|
|
||||||
|
free_thread_enable_313: Final[Path] = resources_dir / "free-threaded-enable-313.xml"
|
||||||
|
|
||||||
test_fail_cwd_file: Final[Path] = resources_dir / "testing_temp_dir_file.py"
|
test_fail_cwd_file: Final[Path] = resources_dir / "testing_temp_dir_file.py"
|
||||||
|
|
||||||
|
|
||||||
@@ -195,7 +204,9 @@ def get_build_verbosity_extra_flags(level: int) -> list[str]:
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
def split_config_settings(config_settings: str, frontend: Literal["pip", "build"]) -> list[str]:
|
def split_config_settings(
|
||||||
|
config_settings: str, frontend: Literal["pip", "build", "build[uv]"]
|
||||||
|
) -> list[str]:
|
||||||
config_settings_list = shlex.split(config_settings)
|
config_settings_list = shlex.split(config_settings)
|
||||||
s = "s" if frontend == "pip" else ""
|
s = "s" if frontend == "pip" else ""
|
||||||
return [f"--config-setting{s}={setting}" for setting in config_settings_list]
|
return [f"--config-setting{s}={setting}" for setting in config_settings_list]
|
||||||
@@ -334,6 +345,55 @@ def download(url: str, dest: Path) -> None:
|
|||||||
sleep(3)
|
sleep(3)
|
||||||
|
|
||||||
|
|
||||||
|
def extract_zip(zip_src: Path, dest: Path) -> None:
|
||||||
|
with ZipFile(zip_src) as zip_:
|
||||||
|
for zinfo in zip_.filelist:
|
||||||
|
zip_.extract(zinfo, dest)
|
||||||
|
|
||||||
|
# Set permissions to the same values as they were set in the archive
|
||||||
|
# We have to do this manually due to
|
||||||
|
# https://github.com/python/cpython/issues/59999
|
||||||
|
# But some files in the zipfile seem to have external_attr with 0
|
||||||
|
# permissions. In that case just use the default value???
|
||||||
|
permissions = (zinfo.external_attr >> 16) & 0o777
|
||||||
|
if permissions != 0:
|
||||||
|
dest.joinpath(zinfo.filename).chmod(permissions)
|
||||||
|
|
||||||
|
|
||||||
|
def extract_tar(tar_src: Path, dest: Path) -> None:
|
||||||
|
with tarfile.open(tar_src) as tar_:
|
||||||
|
tar_.extraction_filter = getattr(tarfile, "tar_filter", (lambda member, _: member))
|
||||||
|
tar_.extractall(dest)
|
||||||
|
|
||||||
|
|
||||||
|
def move_file(src_file: Path, dst_file: Path) -> Path:
|
||||||
|
"""Moves a file safely while avoiding potential semantic confusion:
|
||||||
|
|
||||||
|
1. `dst_file` must point to the target filename, not a directory
|
||||||
|
2. `dst_file` will be overwritten if it already exists
|
||||||
|
3. any missing parent directories will be created
|
||||||
|
|
||||||
|
Returns the fully resolved Path of the resulting file.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
NotADirectoryError: If any part of the intermediate path to `dst_file` is an existing file
|
||||||
|
IsADirectoryError: If `dst_file` points directly to an existing directory
|
||||||
|
"""
|
||||||
|
src_file = src_file.resolve(strict=True)
|
||||||
|
dst_file = dst_file.resolve()
|
||||||
|
|
||||||
|
if dst_file.is_dir():
|
||||||
|
msg = "dst_file must be a valid target filename, not an existing directory."
|
||||||
|
raise IsADirectoryError(msg)
|
||||||
|
dst_file.unlink(missing_ok=True)
|
||||||
|
dst_file.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# using shutil.move() as Path.rename() is not guaranteed to work across filesystem boundaries
|
||||||
|
# explicit str() needed for Python 3.8
|
||||||
|
resulting_file = shutil.move(str(src_file), str(dst_file))
|
||||||
|
return Path(resulting_file).resolve(strict=True)
|
||||||
|
|
||||||
|
|
||||||
class DependencyConstraints:
|
class DependencyConstraints:
|
||||||
def __init__(self, base_file_path: Path):
|
def __init__(self, base_file_path: Path):
|
||||||
assert base_file_path.exists()
|
assert base_file_path.exists()
|
||||||
@@ -343,12 +403,14 @@ class DependencyConstraints:
|
|||||||
def with_defaults() -> DependencyConstraints:
|
def with_defaults() -> DependencyConstraints:
|
||||||
return DependencyConstraints(base_file_path=resources_dir / "constraints.txt")
|
return DependencyConstraints(base_file_path=resources_dir / "constraints.txt")
|
||||||
|
|
||||||
def get_for_python_version(self, version: str) -> Path:
|
def get_for_python_version(
|
||||||
|
self, version: str, *, variant: Literal["python", "pyodide"] = "python"
|
||||||
|
) -> Path:
|
||||||
version_parts = version.split(".")
|
version_parts = version.split(".")
|
||||||
|
|
||||||
# try to find a version-specific dependency file e.g. if
|
# try to find a version-specific dependency file e.g. if
|
||||||
# ./constraints.txt is the base, look for ./constraints-python36.txt
|
# ./constraints.txt is the base, look for ./constraints-python36.txt
|
||||||
specific_stem = self.base_file_path.stem + f"-python{version_parts[0]}{version_parts[1]}"
|
specific_stem = self.base_file_path.stem + f"-{variant}{version_parts[0]}{version_parts[1]}"
|
||||||
specific_name = specific_stem + self.base_file_path.suffix
|
specific_name = specific_stem + self.base_file_path.suffix
|
||||||
specific_file_path = self.base_file_path.with_name(specific_name)
|
specific_file_path = self.base_file_path.with_name(specific_name)
|
||||||
|
|
||||||
@@ -373,7 +435,7 @@ class DependencyConstraints:
|
|||||||
return self.base_file_path.name
|
return self.base_file_path.name
|
||||||
|
|
||||||
|
|
||||||
BuildFrontendName = Literal["pip", "build"]
|
BuildFrontendName = Literal["pip", "build", "build[uv]"]
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
@@ -385,8 +447,8 @@ class BuildFrontendConfig:
|
|||||||
def from_config_string(config_string: str) -> BuildFrontendConfig:
|
def from_config_string(config_string: str) -> BuildFrontendConfig:
|
||||||
config_dict = parse_key_value_string(config_string, ["name"], ["args"])
|
config_dict = parse_key_value_string(config_string, ["name"], ["args"])
|
||||||
name = " ".join(config_dict["name"])
|
name = " ".join(config_dict["name"])
|
||||||
if name not in {"pip", "build"}:
|
if name not in {"pip", "build", "build[uv]"}:
|
||||||
msg = f"Unrecognised build frontend {name}, only 'pip' and 'build' are supported"
|
msg = f"Unrecognised build frontend {name!r}, only 'pip', 'build', and 'build[uv]' are supported"
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
|
|
||||||
name = typing.cast(BuildFrontendName, name)
|
name = typing.cast(BuildFrontendName, name)
|
||||||
@@ -534,6 +596,38 @@ def get_pip_version(env: Mapping[str, str]) -> str:
|
|||||||
return pip_version
|
return pip_version
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache(maxsize=None)
|
||||||
|
def ensure_node(major_version: str) -> Path:
|
||||||
|
input_file = resources_dir / "nodejs.toml"
|
||||||
|
with input_file.open("rb") as f:
|
||||||
|
loaded_file = tomllib.load(f)
|
||||||
|
version = str(loaded_file[major_version])
|
||||||
|
base_url = str(loaded_file["url"])
|
||||||
|
ext = "zip" if IS_WIN else "tar.xz"
|
||||||
|
platform = "win" if IS_WIN else ("darwin" if sys.platform.startswith("darwin") else "linux")
|
||||||
|
linux_arch = Architecture.native_arch("linux")
|
||||||
|
assert linux_arch is not None
|
||||||
|
arch = {"x86_64": "x64", "i686": "x86", "aarch64": "arm64"}.get(
|
||||||
|
linux_arch.value, linux_arch.value
|
||||||
|
)
|
||||||
|
name = f"node-{version}-{platform}-{arch}"
|
||||||
|
path = CIBW_CACHE_PATH / name
|
||||||
|
with FileLock(str(path) + ".lock"):
|
||||||
|
if not path.exists():
|
||||||
|
url = f"{base_url}{version}/{name}.{ext}"
|
||||||
|
with TemporaryDirectory() as tmp_path:
|
||||||
|
archive = Path(tmp_path) / f"{name}.{ext}"
|
||||||
|
download(url, archive)
|
||||||
|
if ext == "zip":
|
||||||
|
extract_zip(archive, path.parent)
|
||||||
|
else:
|
||||||
|
extract_tar(archive, path.parent)
|
||||||
|
assert path.exists()
|
||||||
|
if not IS_WIN:
|
||||||
|
return path / "bin"
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
@lru_cache(maxsize=None)
|
@lru_cache(maxsize=None)
|
||||||
def _ensure_virtualenv(version: str) -> Path:
|
def _ensure_virtualenv(version: str) -> Path:
|
||||||
version_parts = version.split(".")
|
version_parts = version.split(".")
|
||||||
@@ -599,48 +693,64 @@ def _parse_constraints_for_virtualenv(
|
|||||||
|
|
||||||
|
|
||||||
def virtualenv(
|
def virtualenv(
|
||||||
version: str, python: Path, venv_path: Path, dependency_constraint_flags: Sequence[PathOrStr]
|
version: str,
|
||||||
|
python: Path,
|
||||||
|
venv_path: Path,
|
||||||
|
dependency_constraint_flags: Sequence[PathOrStr],
|
||||||
|
*,
|
||||||
|
use_uv: bool,
|
||||||
) -> dict[str, str]:
|
) -> dict[str, str]:
|
||||||
|
"""
|
||||||
|
Create a virtual environment. If `use_uv` is True,
|
||||||
|
dependency_constraint_flags are ignored since nothing is installed in the
|
||||||
|
venv. Otherwise, pip is installed, and setuptools + wheel if Python < 3.12.
|
||||||
|
"""
|
||||||
assert python.exists()
|
assert python.exists()
|
||||||
virtualenv_app = _ensure_virtualenv(version)
|
|
||||||
allowed_seed_packages = ["pip", "setuptools", "wheel"]
|
|
||||||
constraints = _parse_constraints_for_virtualenv(
|
|
||||||
allowed_seed_packages, dependency_constraint_flags
|
|
||||||
)
|
|
||||||
additional_flags: list[str] = []
|
|
||||||
for package in allowed_seed_packages:
|
|
||||||
if package in constraints:
|
|
||||||
additional_flags.append(f"--{package}={constraints[package]}")
|
|
||||||
else:
|
|
||||||
additional_flags.append(f"--no-{package}")
|
|
||||||
|
|
||||||
# Using symlinks to pre-installed seed packages is really the fastest way to get a virtual
|
if use_uv:
|
||||||
# environment. The initial cost is a bit higher but reusing is much faster.
|
call("uv", "venv", venv_path, "--python", python)
|
||||||
# Windows does not always allow symlinks so just disabling for now.
|
else:
|
||||||
# Requires pip>=19.3 so disabling for "embed" because this means we don't know what's the
|
virtualenv_app = _ensure_virtualenv(version)
|
||||||
# version of pip that will end-up installed.
|
allowed_seed_packages = ["pip", "setuptools", "wheel"]
|
||||||
# c.f. https://virtualenv.pypa.io/en/latest/cli_interface.html#section-seeder
|
constraints = _parse_constraints_for_virtualenv(
|
||||||
if (
|
allowed_seed_packages, dependency_constraint_flags
|
||||||
not IS_WIN
|
)
|
||||||
and constraints["pip"] != "embed"
|
additional_flags: list[str] = []
|
||||||
and Version(constraints["pip"]) >= Version("19.3")
|
for package in allowed_seed_packages:
|
||||||
):
|
if package in constraints:
|
||||||
additional_flags.append("--symlink-app-data")
|
additional_flags.append(f"--{package}={constraints[package]}")
|
||||||
|
else:
|
||||||
|
additional_flags.append(f"--no-{package}")
|
||||||
|
|
||||||
|
# Using symlinks to pre-installed seed packages is really the fastest way to get a virtual
|
||||||
|
# environment. The initial cost is a bit higher but reusing is much faster.
|
||||||
|
# Windows does not always allow symlinks so just disabling for now.
|
||||||
|
# Requires pip>=19.3 so disabling for "embed" because this means we don't know what's the
|
||||||
|
# version of pip that will end-up installed.
|
||||||
|
# c.f. https://virtualenv.pypa.io/en/latest/cli_interface.html#section-seeder
|
||||||
|
if (
|
||||||
|
not IS_WIN
|
||||||
|
and constraints["pip"] != "embed"
|
||||||
|
and Version(constraints["pip"]) >= Version("19.3")
|
||||||
|
):
|
||||||
|
additional_flags.append("--symlink-app-data")
|
||||||
|
|
||||||
|
call(
|
||||||
|
sys.executable,
|
||||||
|
"-sS", # just the stdlib, https://github.com/pypa/virtualenv/issues/2133#issuecomment-1003710125
|
||||||
|
virtualenv_app,
|
||||||
|
"--activators=",
|
||||||
|
"--no-periodic-update",
|
||||||
|
*additional_flags,
|
||||||
|
"--python",
|
||||||
|
python,
|
||||||
|
venv_path,
|
||||||
|
)
|
||||||
|
|
||||||
call(
|
|
||||||
sys.executable,
|
|
||||||
"-sS", # just the stdlib, https://github.com/pypa/virtualenv/issues/2133#issuecomment-1003710125
|
|
||||||
virtualenv_app,
|
|
||||||
"--activators=",
|
|
||||||
"--no-periodic-update",
|
|
||||||
*additional_flags,
|
|
||||||
"--python",
|
|
||||||
python,
|
|
||||||
venv_path,
|
|
||||||
)
|
|
||||||
paths = [str(venv_path), str(venv_path / "Scripts")] if IS_WIN else [str(venv_path / "bin")]
|
paths = [str(venv_path), str(venv_path / "Scripts")] if IS_WIN else [str(venv_path / "bin")]
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
env["PATH"] = os.pathsep.join([*paths, env["PATH"]])
|
env["PATH"] = os.pathsep.join([*paths, env["PATH"]])
|
||||||
|
env["VIRTUAL_ENV"] = str(venv_path)
|
||||||
return env
|
return env
|
||||||
|
|
||||||
|
|
||||||
@@ -782,3 +892,42 @@ def parse_key_value_string(
|
|||||||
result[field_name] += values
|
result[field_name] += values
|
||||||
|
|
||||||
return dict(result)
|
return dict(result)
|
||||||
|
|
||||||
|
|
||||||
|
def find_uv() -> Path | None:
|
||||||
|
# Prefer uv in our environment
|
||||||
|
with contextlib.suppress(ImportError, FileNotFoundError):
|
||||||
|
# pylint: disable-next=import-outside-toplevel
|
||||||
|
from uv import find_uv_bin
|
||||||
|
|
||||||
|
return Path(find_uv_bin())
|
||||||
|
|
||||||
|
uv_on_path = shutil.which("uv")
|
||||||
|
return Path(uv_on_path) if uv_on_path else None
|
||||||
|
|
||||||
|
|
||||||
|
def combine_constraints(
|
||||||
|
env: MutableMapping[str, str], /, constraints_path: Path, tmp_dir: Path | None
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
This will workaround a bug in pip<=21.1.1 or uv<=0.2.0 if a tmp_dir is given.
|
||||||
|
If set to None, this will use the modern URI method.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if tmp_dir:
|
||||||
|
if " " in str(constraints_path):
|
||||||
|
assert " " not in str(tmp_dir)
|
||||||
|
tmp_file = tmp_dir / "constraints.txt"
|
||||||
|
tmp_file.write_bytes(constraints_path.read_bytes())
|
||||||
|
constraints_path = tmp_file
|
||||||
|
our_constraints = str(constraints_path)
|
||||||
|
else:
|
||||||
|
our_constraints = (
|
||||||
|
constraints_path.as_uri() if " " in str(constraints_path) else str(constraints_path)
|
||||||
|
)
|
||||||
|
|
||||||
|
user_constraints = env.get("PIP_CONSTRAINT")
|
||||||
|
|
||||||
|
env["UV_CONSTRAINT"] = env["PIP_CONSTRAINT"] = " ".join(
|
||||||
|
c for c in [our_constraints, user_constraints] if c
|
||||||
|
)
|
||||||
|
|||||||
+79
-84
@@ -4,18 +4,16 @@ import os
|
|||||||
import platform as platform_module
|
import platform as platform_module
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
|
||||||
import textwrap
|
import textwrap
|
||||||
from collections.abc import MutableMapping, Sequence, Set
|
from collections.abc import MutableMapping, Sequence, Set
|
||||||
from contextlib import suppress
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from zipfile import ZipFile
|
|
||||||
|
|
||||||
from filelock import FileLock
|
from filelock import FileLock
|
||||||
from packaging.version import Version
|
from packaging.version import Version
|
||||||
|
|
||||||
|
from . import errors
|
||||||
from ._compat.typing import assert_never
|
from ._compat.typing import assert_never
|
||||||
from .architecture import Architecture
|
from .architecture import Architecture
|
||||||
from .environment import ParsedEnvironment
|
from .environment import ParsedEnvironment
|
||||||
@@ -30,10 +28,14 @@ from .util import (
|
|||||||
BuildSelector,
|
BuildSelector,
|
||||||
NonPlatformWheelError,
|
NonPlatformWheelError,
|
||||||
call,
|
call,
|
||||||
|
combine_constraints,
|
||||||
download,
|
download,
|
||||||
|
extract_zip,
|
||||||
find_compatible_wheel,
|
find_compatible_wheel,
|
||||||
|
find_uv,
|
||||||
get_build_verbosity_extra_flags,
|
get_build_verbosity_extra_flags,
|
||||||
get_pip_version,
|
get_pip_version,
|
||||||
|
move_file,
|
||||||
prepare_command,
|
prepare_command,
|
||||||
read_python_configs,
|
read_python_configs,
|
||||||
shell,
|
shell,
|
||||||
@@ -96,11 +98,6 @@ def get_python_configurations(
|
|||||||
return python_configurations
|
return python_configurations
|
||||||
|
|
||||||
|
|
||||||
def extract_zip(zip_src: Path, dest: Path) -> None:
|
|
||||||
with ZipFile(zip_src) as zip_:
|
|
||||||
zip_.extractall(dest)
|
|
||||||
|
|
||||||
|
|
||||||
@lru_cache(maxsize=None)
|
@lru_cache(maxsize=None)
|
||||||
def _ensure_nuget() -> Path:
|
def _ensure_nuget() -> Path:
|
||||||
nuget = CIBW_CACHE_PATH / "nuget.exe"
|
nuget = CIBW_CACHE_PATH / "nuget.exe"
|
||||||
@@ -226,7 +223,7 @@ def setup_python(
|
|||||||
dependency_constraint_flags: Sequence[PathOrStr],
|
dependency_constraint_flags: Sequence[PathOrStr],
|
||||||
environment: ParsedEnvironment,
|
environment: ParsedEnvironment,
|
||||||
build_frontend: BuildFrontendName,
|
build_frontend: BuildFrontendName,
|
||||||
) -> dict[str, str]:
|
) -> tuple[Path, dict[str, str]]:
|
||||||
tmp.mkdir()
|
tmp.mkdir()
|
||||||
implementation_id = python_configuration.identifier.split("-")[0]
|
implementation_id = python_configuration.identifier.split("-")[0]
|
||||||
python_libs_base = None
|
python_libs_base = None
|
||||||
@@ -249,10 +246,19 @@ def setup_python(
|
|||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
assert base_python.exists()
|
assert base_python.exists()
|
||||||
|
|
||||||
|
use_uv = build_frontend == "build[uv]" and Version(python_configuration.version) >= Version(
|
||||||
|
"3.8"
|
||||||
|
)
|
||||||
|
uv_path = find_uv()
|
||||||
|
|
||||||
log.step("Setting up build environment...")
|
log.step("Setting up build environment...")
|
||||||
venv_path = tmp / "venv"
|
venv_path = tmp / "venv"
|
||||||
env = virtualenv(
|
env = virtualenv(
|
||||||
python_configuration.version, base_python, venv_path, dependency_constraint_flags
|
python_configuration.version,
|
||||||
|
base_python,
|
||||||
|
venv_path,
|
||||||
|
dependency_constraint_flags,
|
||||||
|
use_uv=use_uv,
|
||||||
)
|
)
|
||||||
|
|
||||||
# set up environment variables for run_with_env
|
# set up environment variables for run_with_env
|
||||||
@@ -260,19 +266,14 @@ def setup_python(
|
|||||||
env["PYTHON_ARCH"] = python_configuration.arch
|
env["PYTHON_ARCH"] = python_configuration.arch
|
||||||
env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1"
|
env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1"
|
||||||
|
|
||||||
# pip older than 21.3 builds executables such as pip.exe for x64 platform.
|
# upgrade pip to the version matching our constraints
|
||||||
# The first re-install of pip updates pip module but builds pip.exe using
|
# if necessary, reinstall it to ensure that it's available on PATH as 'pip.exe'
|
||||||
# the old pip which still generates x64 executable. But the second
|
if not use_uv:
|
||||||
# re-install uses updated pip and correctly builds pip.exe for the target.
|
|
||||||
# This can be removed once ARM64 Pythons (currently 3.9 and 3.10) bundle
|
|
||||||
# pip versions newer than 21.3.
|
|
||||||
if python_configuration.arch == "ARM64" and Version(get_pip_version(env)) < Version("21.3"):
|
|
||||||
call(
|
call(
|
||||||
"python",
|
"python",
|
||||||
"-m",
|
"-m",
|
||||||
"pip",
|
"pip",
|
||||||
"install",
|
"install",
|
||||||
"--force-reinstall",
|
|
||||||
"--upgrade",
|
"--upgrade",
|
||||||
"pip",
|
"pip",
|
||||||
*dependency_constraint_flags,
|
*dependency_constraint_flags,
|
||||||
@@ -280,20 +281,6 @@ def setup_python(
|
|||||||
cwd=venv_path,
|
cwd=venv_path,
|
||||||
)
|
)
|
||||||
|
|
||||||
# upgrade pip to the version matching our constraints
|
|
||||||
# if necessary, reinstall it to ensure that it's available on PATH as 'pip.exe'
|
|
||||||
call(
|
|
||||||
"python",
|
|
||||||
"-m",
|
|
||||||
"pip",
|
|
||||||
"install",
|
|
||||||
"--upgrade",
|
|
||||||
"pip",
|
|
||||||
*dependency_constraint_flags,
|
|
||||||
env=env,
|
|
||||||
cwd=venv_path,
|
|
||||||
)
|
|
||||||
|
|
||||||
# update env with results from CIBW_ENVIRONMENT
|
# update env with results from CIBW_ENVIRONMENT
|
||||||
env = environment.as_dictionary(prev_environment=env)
|
env = environment.as_dictionary(prev_environment=env)
|
||||||
|
|
||||||
@@ -303,23 +290,18 @@ def setup_python(
|
|||||||
call("python", "-c", "\"import struct; print(struct.calcsize('P') * 8)\"", env=env)
|
call("python", "-c", "\"import struct; print(struct.calcsize('P') * 8)\"", env=env)
|
||||||
where_python = call("where", "python", env=env, capture_stdout=True).splitlines()[0].strip()
|
where_python = call("where", "python", env=env, capture_stdout=True).splitlines()[0].strip()
|
||||||
if where_python != str(venv_path / "Scripts" / "python.exe"):
|
if where_python != str(venv_path / "Scripts" / "python.exe"):
|
||||||
print(
|
msg = "python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it."
|
||||||
"cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it.",
|
raise errors.FatalError(msg)
|
||||||
file=sys.stderr,
|
|
||||||
)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
# check what pip version we're on
|
# check what pip version we're on
|
||||||
assert (venv_path / "Scripts" / "pip.exe").exists()
|
if not use_uv:
|
||||||
where_pip = call("where", "pip", env=env, capture_stdout=True).splitlines()[0].strip()
|
assert (venv_path / "Scripts" / "pip.exe").exists()
|
||||||
if where_pip.strip() != str(venv_path / "Scripts" / "pip.exe"):
|
where_pip = call("where", "pip", env=env, capture_stdout=True).splitlines()[0].strip()
|
||||||
print(
|
if where_pip.strip() != str(venv_path / "Scripts" / "pip.exe"):
|
||||||
"cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it.",
|
msg = "pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it."
|
||||||
file=sys.stderr,
|
raise errors.FatalError(msg)
|
||||||
)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
call("pip", "--version", env=env)
|
call("pip", "--version", env=env)
|
||||||
|
|
||||||
log.step("Installing build tools...")
|
log.step("Installing build tools...")
|
||||||
if build_frontend == "build":
|
if build_frontend == "build":
|
||||||
@@ -331,13 +313,24 @@ def setup_python(
|
|||||||
*dependency_constraint_flags,
|
*dependency_constraint_flags,
|
||||||
env=env,
|
env=env,
|
||||||
)
|
)
|
||||||
|
elif build_frontend == "build[uv]":
|
||||||
|
assert uv_path is not None
|
||||||
|
call(
|
||||||
|
uv_path,
|
||||||
|
"pip",
|
||||||
|
"install",
|
||||||
|
"--upgrade",
|
||||||
|
"build[virtualenv]",
|
||||||
|
*dependency_constraint_flags,
|
||||||
|
env=env,
|
||||||
|
)
|
||||||
|
|
||||||
if python_libs_base:
|
if python_libs_base:
|
||||||
# Set up the environment for various backends to enable cross-compilation
|
# Set up the environment for various backends to enable cross-compilation
|
||||||
setup_setuptools_cross_compile(tmp, python_configuration, python_libs_base, env)
|
setup_setuptools_cross_compile(tmp, python_configuration, python_libs_base, env)
|
||||||
setup_rust_cross_compile(tmp, python_configuration, python_libs_base, env)
|
setup_rust_cross_compile(tmp, python_configuration, python_libs_base, env)
|
||||||
|
|
||||||
return env
|
return base_python, env
|
||||||
|
|
||||||
|
|
||||||
def build(options: Options, tmp_path: Path) -> None:
|
def build(options: Options, tmp_path: Path) -> None:
|
||||||
@@ -365,6 +358,9 @@ def build(options: Options, tmp_path: Path) -> None:
|
|||||||
for config in python_configurations:
|
for config in python_configurations:
|
||||||
build_options = options.build_options(config.identifier)
|
build_options = options.build_options(config.identifier)
|
||||||
build_frontend = build_options.build_frontend or BuildFrontendConfig("pip")
|
build_frontend = build_options.build_frontend or BuildFrontendConfig("pip")
|
||||||
|
use_uv = build_frontend.name == "build[uv]" and Version(config.version) >= Version(
|
||||||
|
"3.8"
|
||||||
|
)
|
||||||
log.build_start(config.identifier)
|
log.build_start(config.identifier)
|
||||||
|
|
||||||
identifier_tmp_dir = tmp_path / config.identifier
|
identifier_tmp_dir = tmp_path / config.identifier
|
||||||
@@ -380,13 +376,15 @@ def build(options: Options, tmp_path: Path) -> None:
|
|||||||
]
|
]
|
||||||
|
|
||||||
# install Python
|
# install Python
|
||||||
env = setup_python(
|
base_python, env = setup_python(
|
||||||
identifier_tmp_dir / "build",
|
identifier_tmp_dir / "build",
|
||||||
config,
|
config,
|
||||||
dependency_constraint_flags,
|
dependency_constraint_flags,
|
||||||
build_options.environment,
|
build_options.environment,
|
||||||
build_frontend.name,
|
build_frontend.name,
|
||||||
)
|
)
|
||||||
|
if not use_uv:
|
||||||
|
pip_version = get_pip_version(env)
|
||||||
|
|
||||||
compatible_wheel = find_compatible_wheel(built_wheels, config.identifier)
|
compatible_wheel = find_compatible_wheel(built_wheels, config.identifier)
|
||||||
if compatible_wheel:
|
if compatible_wheel:
|
||||||
@@ -415,26 +413,14 @@ def build(options: Options, tmp_path: Path) -> None:
|
|||||||
extra_flags += build_frontend.args
|
extra_flags += build_frontend.args
|
||||||
|
|
||||||
build_env = env.copy()
|
build_env = env.copy()
|
||||||
build_env["VIRTUALENV_PIP"] = get_pip_version(env)
|
if not use_uv:
|
||||||
|
build_env["VIRTUALENV_PIP"] = pip_version
|
||||||
|
|
||||||
if build_options.dependency_constraints:
|
if build_options.dependency_constraints:
|
||||||
constraints_path = build_options.dependency_constraints.get_for_python_version(
|
constraints_path = build_options.dependency_constraints.get_for_python_version(
|
||||||
config.version
|
config.version
|
||||||
)
|
)
|
||||||
# Bug in pip <= 21.1.3 - we can't have a space in the
|
combine_constraints(build_env, constraints_path, identifier_tmp_dir)
|
||||||
# constraints file, and pip doesn't support drive letters
|
|
||||||
# in uhi. After probably pip 21.2, we can use uri. For
|
|
||||||
# now, use a temporary file.
|
|
||||||
if " " in str(constraints_path):
|
|
||||||
assert " " not in str(identifier_tmp_dir)
|
|
||||||
tmp_file = identifier_tmp_dir / "constraints.txt"
|
|
||||||
tmp_file.write_bytes(constraints_path.read_bytes())
|
|
||||||
constraints_path = tmp_file
|
|
||||||
|
|
||||||
our_constraints = str(constraints_path)
|
|
||||||
user_constraints = build_env.get("PIP_CONSTRAINT")
|
|
||||||
build_env["PIP_CONSTRAINT"] = " ".join(
|
|
||||||
c for c in [our_constraints, user_constraints] if c
|
|
||||||
)
|
|
||||||
|
|
||||||
if build_frontend.name == "pip":
|
if build_frontend.name == "pip":
|
||||||
extra_flags += get_build_verbosity_extra_flags(build_options.build_verbosity)
|
extra_flags += get_build_verbosity_extra_flags(build_options.build_verbosity)
|
||||||
@@ -451,10 +437,12 @@ def build(options: Options, tmp_path: Path) -> None:
|
|||||||
*extra_flags,
|
*extra_flags,
|
||||||
env=build_env,
|
env=build_env,
|
||||||
)
|
)
|
||||||
elif build_frontend.name == "build":
|
elif build_frontend.name == "build" or build_frontend.name == "build[uv]":
|
||||||
if not 0 <= build_options.build_verbosity < 2:
|
if not 0 <= build_options.build_verbosity < 2:
|
||||||
msg = f"build_verbosity {build_options.build_verbosity} is not supported for build frontend. Ignoring."
|
msg = f"build_verbosity {build_options.build_verbosity} is not supported for build frontend. Ignoring."
|
||||||
log.warning(msg)
|
log.warning(msg)
|
||||||
|
if use_uv:
|
||||||
|
extra_flags.append("--installer=uv")
|
||||||
call(
|
call(
|
||||||
"python",
|
"python",
|
||||||
"-m",
|
"-m",
|
||||||
@@ -508,12 +496,20 @@ def build(options: Options, tmp_path: Path) -> None:
|
|||||||
log.step("Testing wheel...")
|
log.step("Testing wheel...")
|
||||||
# set up a virtual environment to install and test from, to make sure
|
# set up a virtual environment to install and test from, to make sure
|
||||||
# there are no dependencies that were pulled in at build time.
|
# there are no dependencies that were pulled in at build time.
|
||||||
call("pip", "install", "virtualenv", *dependency_constraint_flags, env=env)
|
if not use_uv:
|
||||||
|
call("pip", "install", "virtualenv", *dependency_constraint_flags, env=env)
|
||||||
|
|
||||||
venv_dir = identifier_tmp_dir / "venv-test"
|
venv_dir = identifier_tmp_dir / "venv-test"
|
||||||
|
|
||||||
# Use --no-download to ensure determinism by using seed libraries
|
if use_uv:
|
||||||
# built into virtualenv
|
call("uv", "venv", venv_dir, f"--python={base_python}", env=env)
|
||||||
call("python", "-m", "virtualenv", "--no-download", venv_dir, env=env)
|
else:
|
||||||
|
# Use pip version from the initial env to ensure determinism
|
||||||
|
venv_args = ["--no-periodic-update", f"--pip={pip_version}"]
|
||||||
|
# In Python<3.12, setuptools & wheel are installed as well, use virtualenv embedded ones
|
||||||
|
if Version(config.version) < Version("3.12"):
|
||||||
|
venv_args.extend(("--setuptools=embed", "--wheel=embed"))
|
||||||
|
call("python", "-m", "virtualenv", *venv_args, venv_dir, env=env)
|
||||||
|
|
||||||
virtualenv_env = env.copy()
|
virtualenv_env = env.copy()
|
||||||
virtualenv_env["PATH"] = os.pathsep.join(
|
virtualenv_env["PATH"] = os.pathsep.join(
|
||||||
@@ -522,14 +518,11 @@ def build(options: Options, tmp_path: Path) -> None:
|
|||||||
virtualenv_env["PATH"],
|
virtualenv_env["PATH"],
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
virtualenv_env["VIRTUAL_ENV"] = str(venv_dir)
|
||||||
|
|
||||||
# check that we are using the Python from the virtual environment
|
# check that we are using the Python from the virtual environment
|
||||||
call("where", "python", env=virtualenv_env)
|
call("where", "python", env=virtualenv_env)
|
||||||
|
|
||||||
# TODO remove me once virtualenv provides pip>=24.1b1
|
|
||||||
if config.version.startswith("3.13."):
|
|
||||||
call("python", "-m", "pip", "install", "--pre", "-U", "pip", env=virtualenv_env)
|
|
||||||
|
|
||||||
if build_options.before_test:
|
if build_options.before_test:
|
||||||
before_test_prepared = prepare_command(
|
before_test_prepared = prepare_command(
|
||||||
build_options.before_test,
|
build_options.before_test,
|
||||||
@@ -538,9 +531,11 @@ def build(options: Options, tmp_path: Path) -> None:
|
|||||||
)
|
)
|
||||||
shell(before_test_prepared, env=virtualenv_env)
|
shell(before_test_prepared, env=virtualenv_env)
|
||||||
|
|
||||||
|
pip = ["uv", "pip"] if use_uv else ["pip"]
|
||||||
|
|
||||||
# install the wheel
|
# install the wheel
|
||||||
call(
|
call(
|
||||||
"pip",
|
*pip,
|
||||||
"install",
|
"install",
|
||||||
str(repaired_wheel) + build_options.test_extras,
|
str(repaired_wheel) + build_options.test_extras,
|
||||||
env=virtualenv_env,
|
env=virtualenv_env,
|
||||||
@@ -548,7 +543,7 @@ def build(options: Options, tmp_path: Path) -> None:
|
|||||||
|
|
||||||
# test the wheel
|
# test the wheel
|
||||||
if build_options.test_requires:
|
if build_options.test_requires:
|
||||||
call("pip", "install", *build_options.test_requires, env=virtualenv_env)
|
call(*pip, "install", *build_options.test_requires, env=virtualenv_env)
|
||||||
|
|
||||||
# run the tests from a temp dir, with an absolute path in the command
|
# run the tests from a temp dir, with an absolute path in the command
|
||||||
# (this ensures that Python runs the tests against the installed wheel
|
# (this ensures that Python runs the tests against the installed wheel
|
||||||
@@ -567,11 +562,13 @@ def build(options: Options, tmp_path: Path) -> None:
|
|||||||
|
|
||||||
# we're all done here; move it to output (remove if already exists)
|
# we're all done here; move it to output (remove if already exists)
|
||||||
if compatible_wheel is None:
|
if compatible_wheel is None:
|
||||||
with suppress(FileNotFoundError):
|
output_wheel = build_options.output_dir.joinpath(repaired_wheel.name)
|
||||||
(build_options.output_dir / repaired_wheel.name).unlink()
|
moved_wheel = move_file(repaired_wheel, output_wheel)
|
||||||
|
if moved_wheel != output_wheel.resolve():
|
||||||
shutil.move(str(repaired_wheel), build_options.output_dir)
|
log.warning(
|
||||||
built_wheels.append(build_options.output_dir / repaired_wheel.name)
|
"{repaired_wheel} was moved to {moved_wheel} instead of {output_wheel}"
|
||||||
|
)
|
||||||
|
built_wheels.append(output_wheel)
|
||||||
|
|
||||||
# clean up
|
# clean up
|
||||||
# (we ignore errors because occasionally Windows fails to unlink a file and we
|
# (we ignore errors because occasionally Windows fails to unlink a file and we
|
||||||
@@ -580,7 +577,5 @@ def build(options: Options, tmp_path: Path) -> None:
|
|||||||
|
|
||||||
log.build_end()
|
log.build_end()
|
||||||
except subprocess.CalledProcessError as error:
|
except subprocess.CalledProcessError as error:
|
||||||
log.step_end_with_error(
|
msg = f"Command {error.cmd} failed with code {error.returncode}. {error.stdout or ''}"
|
||||||
f"Command {error.cmd} failed with code {error.returncode}. {error.stdout}"
|
raise errors.FatalError(msg) from error
|
||||||
)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|||||||
@@ -4,6 +4,33 @@ title: Changelog
|
|||||||
|
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
### v2.19.1
|
||||||
|
|
||||||
|
- 🐛 Don't require setup-python on GHA for Pyodide (#1868)
|
||||||
|
- 🐛 Specify full python path for uv (fixes issue in 0.2.10 & 0.2.11) (#1881)
|
||||||
|
- 🛠 Update for pip 24.1b2 on CPython 3.13. (#1879)
|
||||||
|
- 🛠 Fix a warning in our schema generation script. (#1866)
|
||||||
|
- 🛠 Cleaner output on pytest 8-8.2. (#1865)
|
||||||
|
|
||||||
|
|
||||||
|
### v2.19.0
|
||||||
|
|
||||||
|
See the [release post](https://iscinumpy.dev/post/cibuildwheel-2-19-0/) for more info on new features!
|
||||||
|
|
||||||
|
- 🌟 Add Pyodide platform. Set with `--platform pyodide` or `CIBW_PLATFORM: pyodide` on Linux with a host Python 3.12 to build WebAssembly wheels. Not accepted on PyPI currently, but usable directly in a website using Pyodide, for live docs, etc. (#1456, #1859)
|
||||||
|
- 🌟 Add `build[uv]` backend, which will take a pre-existing uv install (or install `cibuildwheel[uv]`) and use `uv` for all environment setup and installs on Python 3.8+. This is significantly faster in most cases. (#1856)
|
||||||
|
- ✨ Add free-threaded macOS builds and update CPython to 3.13.0b2. (#1854)
|
||||||
|
- 🐛 Issue copying a wheel to a non-existent output dir fixed. (#1851, #1862)
|
||||||
|
- 🐛 Better determinism for the test environment seeding. (#1835)
|
||||||
|
- 🛠 `VIRTUAL_ENV` variable now set. (#1842)
|
||||||
|
- 🛠 Remove a pip<21.3 workaround. (#1842)
|
||||||
|
- 🛠 Error handling was refactored to use exceptions. (#1719)
|
||||||
|
- 🛠 Hardcoded paths in tests avoided. (#1834)
|
||||||
|
- 🛠 Single Python tests made more generic. (#1835)
|
||||||
|
- 🛠 Sped up our ci by splitting up emulation tests. (#1839)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### v2.18.1
|
### v2.18.1
|
||||||
|
|
||||||
- 🌟 Add free-threaded Linux and Windows builds for 3.13. New identifiers `cp313t-*`, new option `CIBW_FREE_THREADED_SUPPORT`/`tool.cibuildwheel.free-threaded-support` required to opt-in. [See the docs](https://cibuildwheel.pypa.io/en/stable/options/#free-threaded-support) for more information. (#1831)
|
- 🌟 Add free-threaded Linux and Windows builds for 3.13. New identifiers `cp313t-*`, new option `CIBW_FREE_THREADED_SUPPORT`/`tool.cibuildwheel.free-threaded-support` required to opt-in. [See the docs](https://cibuildwheel.pypa.io/en/stable/options/#free-threaded-support) for more information. (#1831)
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ Then, increment the project version number using:
|
|||||||
bin/bump_version.py
|
bin/bump_version.py
|
||||||
```
|
```
|
||||||
|
|
||||||
You'll be prompted to enter the new version number. Update the changelog when prompted. The script will create a 'bump version' commit and version tag.
|
(or `nox -s bump_version`) You'll be prompted to enter the new version number. Update the changelog when prompted. The script will create a 'bump version' commit and version tag.
|
||||||
|
|
||||||
Finally, cut the release and push to GitHub.
|
Finally, cut the release and push to GitHub.
|
||||||
|
|
||||||
@@ -186,3 +186,5 @@ git push && git push --tags
|
|||||||
```
|
```
|
||||||
|
|
||||||
Then head to https://github.com/pypa/cibuildwheel/releases and create a GitHub release from the new tag, pasting in the changelog entry. Once the release is created inside GitHub, a CI job will create the assets and upload them to PyPI.
|
Then head to https://github.com/pypa/cibuildwheel/releases and create a GitHub release from the new tag, pasting in the changelog entry. Once the release is created inside GitHub, a CI job will create the assets and upload them to PyPI.
|
||||||
|
|
||||||
|
If there were any schema updates, run `pipx run ./bin/generate_schema.py --schemastore > partial-cibuildwheel.json` and contribute the changes to SchemaStore.
|
||||||
|
|||||||
+3
-3
@@ -142,7 +142,7 @@ There are two suggested methods for keeping cibuildwheel up to date that instead
|
|||||||
If you use GitHub Actions for builds, you can use cibuildwheel as an action:
|
If you use GitHub Actions for builds, you can use cibuildwheel as an action:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
uses: pypa/cibuildwheel@v2.18.1
|
uses: pypa/cibuildwheel@v2.19.1
|
||||||
```
|
```
|
||||||
|
|
||||||
This is a composite step that just runs cibuildwheel using pipx. You can set command-line options as `with:` parameters, and use `env:` as normal.
|
This is a composite step that just runs cibuildwheel using pipx. You can set command-line options as `with:` parameters, and use `env:` as normal.
|
||||||
@@ -164,7 +164,7 @@ The second option, and the only one that supports other CI systems, is using a `
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# requirements-cibw.txt
|
# requirements-cibw.txt
|
||||||
cibuildwheel==2.18.1
|
cibuildwheel==2.19.1
|
||||||
```
|
```
|
||||||
|
|
||||||
Then your install step would have `python -m pip install -r requirements-cibw.txt` in it. Your `.github/dependabot.yml` file could look like this:
|
Then your install step would have `python -m pip install -r requirements-cibw.txt` in it. Your `.github/dependabot.yml` file could look like this:
|
||||||
@@ -328,7 +328,7 @@ Solutions to this vary, but the simplest is to use pipx:
|
|||||||
# most runners have pipx preinstalled, but in case you don't
|
# most runners have pipx preinstalled, but in case you don't
|
||||||
python3 -m pip install pipx
|
python3 -m pip install pipx
|
||||||
|
|
||||||
pipx run cibuildwheel==2.18.1 --output-dir wheelhouse
|
pipx run cibuildwheel==2.19.1 --output-dir wheelhouse
|
||||||
pipx run twine upload wheelhouse/*.whl
|
pipx run twine upload wheelhouse/*.whl
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
+84
-20
@@ -91,9 +91,9 @@ You can configure cibuildwheel with a config file, such as `pyproject.toml`.
|
|||||||
Options have the same names as the environment variable overrides, but are
|
Options have the same names as the environment variable overrides, but are
|
||||||
placed in `[tool.cibuildwheel]` and are lower case, with dashes, following
|
placed in `[tool.cibuildwheel]` and are lower case, with dashes, following
|
||||||
common [TOML][] practice. Anything placed in subsections `linux`, `windows`,
|
common [TOML][] practice. Anything placed in subsections `linux`, `windows`,
|
||||||
or `macos` will only affect those platforms. Lists can be used instead of
|
`macos`, or `pyodide` will only affect those platforms. Lists can be used
|
||||||
strings for items that are naturally a list. Multiline strings also work just
|
instead of strings for items that are naturally a list. Multiline strings also
|
||||||
like in the environment variables. Environment variables will take
|
work just like in the environment variables. Environment variables will take
|
||||||
precedence if defined.
|
precedence if defined.
|
||||||
|
|
||||||
The example above using environment variables could have been written like this:
|
The example above using environment variables could have been written like this:
|
||||||
@@ -247,7 +247,7 @@ environment variables will completely override any TOML configuration.
|
|||||||
|
|
||||||
> Override the auto-detected target platform
|
> Override the auto-detected target platform
|
||||||
|
|
||||||
Options: `auto` `linux` `macos` `windows`
|
Options: `auto` `linux` `macos` `windows` `pyodide`
|
||||||
|
|
||||||
Default: `auto`
|
Default: `auto`
|
||||||
|
|
||||||
@@ -255,6 +255,7 @@ Default: `auto`
|
|||||||
|
|
||||||
- For `linux`, you need [Docker or Podman](#container-engine) running, on Linux, macOS, or Windows.
|
- For `linux`, you need [Docker or Podman](#container-engine) running, on Linux, macOS, or Windows.
|
||||||
- For `macos` and `windows`, you need to be running on the respective system, with a working compiler toolchain installed - Xcode Command Line tools for macOS, and MSVC for Windows.
|
- For `macos` and `windows`, you need to be running on the respective system, with a working compiler toolchain installed - Xcode Command Line tools for macOS, and MSVC for Windows.
|
||||||
|
- For `pyodide` you need to be on an x86-64 linux runner and `python3.12` must be available in `PATH`.
|
||||||
|
|
||||||
This option can also be set using the [command-line option](#command-line) `--platform`. This option is not available in the `pyproject.toml` config.
|
This option can also be set using the [command-line option](#command-line) `--platform`. This option is not available in the `pyproject.toml` config.
|
||||||
|
|
||||||
@@ -309,6 +310,9 @@ For CPython, the minimally supported macOS version is 10.9; for PyPy 3.7, macOS
|
|||||||
|
|
||||||
Windows arm64 platform support is experimental.
|
Windows arm64 platform support is experimental.
|
||||||
|
|
||||||
|
For an experimental WebAssembly build with `--platform pyodide`,
|
||||||
|
`cp312-pyodide_wasm32` is the only platform identifier.
|
||||||
|
|
||||||
See the [cibuildwheel 1 documentation](https://cibuildwheel.pypa.io/en/1.x/) for past end-of-life versions of Python, and PyPy2.7.
|
See the [cibuildwheel 1 documentation](https://cibuildwheel.pypa.io/en/1.x/) for past end-of-life versions of Python, and PyPy2.7.
|
||||||
|
|
||||||
#### Examples
|
#### Examples
|
||||||
@@ -474,6 +478,7 @@ Options:
|
|||||||
- Linux: `x86_64` `i686` `aarch64` `ppc64le` `s390x`
|
- Linux: `x86_64` `i686` `aarch64` `ppc64le` `s390x`
|
||||||
- macOS: `x86_64` `arm64` `universal2`
|
- macOS: `x86_64` `arm64` `universal2`
|
||||||
- Windows: `AMD64` `x86` `ARM64`
|
- Windows: `AMD64` `x86` `ARM64`
|
||||||
|
- Pyodide: `wasm32`
|
||||||
- `auto`: The default archs for your machine - see the table below.
|
- `auto`: The default archs for your machine - see the table below.
|
||||||
- `auto64`: Just the 64-bit auto archs
|
- `auto64`: Just the 64-bit auto archs
|
||||||
- `auto32`: Just the 32-bit auto archs
|
- `auto32`: Just the 32-bit auto archs
|
||||||
@@ -619,7 +624,7 @@ This option can also be set using the [command-line option](#command-line) `--pr
|
|||||||
## Build customization
|
## Build customization
|
||||||
|
|
||||||
### `CIBW_BUILD_FRONTEND` {: #build-frontend}
|
### `CIBW_BUILD_FRONTEND` {: #build-frontend}
|
||||||
> Set the tool to use to build, either "pip" (default for now) or "build"
|
> Set the tool to use to build, either "pip" (default for now), "build", or "build[uv]"
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
|
|
||||||
@@ -631,17 +636,30 @@ Default: `pip`
|
|||||||
Choose which build frontend to use. Can either be "pip", which will run
|
Choose which build frontend to use. Can either be "pip", which will run
|
||||||
`python -m pip wheel`, or "build", which will run `python -m build --wheel`.
|
`python -m pip wheel`, or "build", which will run `python -m build --wheel`.
|
||||||
|
|
||||||
|
You can also use "build[uv]", which will use an external [uv][] everywhere
|
||||||
|
possible, both through `--installer=uv` passed to build, as well as when making
|
||||||
|
all build and test environments. This will generally speed up cibuildwheel.
|
||||||
|
Make sure you have an external uv on Windows and macOS, either by
|
||||||
|
pre-installing it, or installing cibuildwheel with the uv extra,
|
||||||
|
`cibuildwheel[uv]`. `uv` will not be used for Python 3.6 or Python 3.7. You
|
||||||
|
cannot use uv currently on Windows for ARM or for musllinux on s390x as
|
||||||
|
binaries are not provided by uv. Legacy dependencies like setuptools on Python
|
||||||
|
< 3.12 and pip are not installed if using uv.
|
||||||
|
|
||||||
|
Pyodide ignores this setting, as only "build" is supported.
|
||||||
|
|
||||||
You can specify extra arguments to pass to `pip wheel` or `build` using the
|
You can specify extra arguments to pass to `pip wheel` or `build` using the
|
||||||
optional `args` option.
|
optional `args` option.
|
||||||
|
|
||||||
!!! tip
|
!!! tip
|
||||||
Until v2.0.0, [pip] was the only way to build wheels, and is still the
|
Until v2.0.0, [pip][] was the only way to build wheels, and is still the
|
||||||
default. However, we expect that at some point in the future, cibuildwheel
|
default. However, we expect that at some point in the future, cibuildwheel
|
||||||
will change the default to [build], in line with the PyPA's recommendation.
|
will change the default to [build][], in line with the PyPA's recommendation.
|
||||||
If you want to try `build` before this, you can use this option.
|
If you want to try `build` before this, you can use this option.
|
||||||
|
|
||||||
[pip]: https://pip.pypa.io/en/stable/cli/pip_wheel/
|
[pip]: https://pip.pypa.io/en/stable/cli/pip_wheel/
|
||||||
[build]: https://github.com/pypa/build/
|
[build]: https://github.com/pypa/build/
|
||||||
|
[uv]: https://github.com/astral-sh/uv
|
||||||
|
|
||||||
#### Examples
|
#### Examples
|
||||||
|
|
||||||
@@ -656,6 +674,12 @@ optional `args` option.
|
|||||||
|
|
||||||
# supply an extra argument to 'pip wheel'
|
# supply an extra argument to 'pip wheel'
|
||||||
CIBW_BUILD_FRONTEND: "pip; args: --no-build-isolation"
|
CIBW_BUILD_FRONTEND: "pip; args: --no-build-isolation"
|
||||||
|
|
||||||
|
# Use uv and build
|
||||||
|
CIBW_BUILD_FRONTEND: "build[uv]"
|
||||||
|
|
||||||
|
# Use uv and build with an argument
|
||||||
|
CIBW_BUILD_FRONTEND: "build[uv]; args: --no-isolation"
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! tab examples "pyproject.toml"
|
!!! tab examples "pyproject.toml"
|
||||||
@@ -670,6 +694,12 @@ optional `args` option.
|
|||||||
|
|
||||||
# supply an extra argument to 'pip wheel'
|
# supply an extra argument to 'pip wheel'
|
||||||
build-frontend = { name = "pip", args = ["--no-build-isolation"] }
|
build-frontend = { name = "pip", args = ["--no-build-isolation"] }
|
||||||
|
|
||||||
|
# Use uv and build
|
||||||
|
build-frontend = "build[uv]"
|
||||||
|
|
||||||
|
# Use uv and build with an argument
|
||||||
|
build-frontend = { name = "build[uv]", args = ["--no-isolation"] }
|
||||||
```
|
```
|
||||||
|
|
||||||
### `CIBW_CONFIG_SETTINGS` {: #config-settings}
|
### `CIBW_CONFIG_SETTINGS` {: #config-settings}
|
||||||
@@ -684,7 +714,7 @@ a table of items, including arrays.
|
|||||||
single values.
|
single values.
|
||||||
|
|
||||||
Platform-specific environment variables also available:<br/>
|
Platform-specific environment variables also available:<br/>
|
||||||
`CIBW_CONFIG_SETTINGS_MACOS` | `CIBW_CONFIG_SETTINGS_WINDOWS` | `CIBW_CONFIG_SETTINGS_LINUX`
|
`CIBW_CONFIG_SETTINGS_MACOS` | `CIBW_CONFIG_SETTINGS_WINDOWS` | `CIBW_CONFIG_SETTINGS_LINUX` | `CIBW_CONFIG_SETTINGS_PYODIDE`
|
||||||
|
|
||||||
|
|
||||||
#### Examples
|
#### Examples
|
||||||
@@ -715,7 +745,7 @@ You can use `$PATH` syntax to insert other variables, or the `$(pwd)` syntax to
|
|||||||
To specify more than one environment variable, separate the assignments by spaces.
|
To specify more than one environment variable, separate the assignments by spaces.
|
||||||
|
|
||||||
Platform-specific environment variables are also available:<br/>
|
Platform-specific environment variables are also available:<br/>
|
||||||
`CIBW_ENVIRONMENT_MACOS` | `CIBW_ENVIRONMENT_WINDOWS` | `CIBW_ENVIRONMENT_LINUX`
|
`CIBW_ENVIRONMENT_MACOS` | `CIBW_ENVIRONMENT_WINDOWS` | `CIBW_ENVIRONMENT_LINUX` | `CIBW_ENVIRONMENT_PYODIDE`
|
||||||
|
|
||||||
#### Examples
|
#### Examples
|
||||||
|
|
||||||
@@ -788,7 +818,7 @@ Platform-specific environment variables are also available:<br/>
|
|||||||
In configuration mode, you can use a [TOML][] table instead of a raw string as shown above.
|
In configuration mode, you can use a [TOML][] table instead of a raw string as shown above.
|
||||||
|
|
||||||
!!! note
|
!!! note
|
||||||
cibuildwheel always defines the environment variable `CIBUILDWHEEL=1`. This can be useful for [building wheels with optional extensions](faq.md#building-packages-with-optional-c-extensions).
|
cibuildwheel always defines the environment variable `CIBUILDWHEEL=1`. This can be useful for [building wheels with optional extensions](faq.md#optional-extensions).
|
||||||
|
|
||||||
!!! note
|
!!! note
|
||||||
To do its work, cibuildwheel sets the variables `VIRTUALENV_PIP`, `DIST_EXTRA_CONFIG`, `SETUPTOOLS_EXT_SUFFIX`, `PIP_DISABLE_PIP_VERSION_CHECK`, `PIP_ROOT_USER_ACTION`, and it extends the variables `PATH` and `PIP_CONSTRAINT`. Your assignments to these options might be replaced or extended.
|
To do its work, cibuildwheel sets the variables `VIRTUALENV_PIP`, `DIST_EXTRA_CONFIG`, `SETUPTOOLS_EXT_SUFFIX`, `PIP_DISABLE_PIP_VERSION_CHECK`, `PIP_ROOT_USER_ACTION`, and it extends the variables `PATH` and `PIP_CONSTRAINT`. Your assignments to these options might be replaced or extended.
|
||||||
@@ -847,7 +877,7 @@ On linux, overriding it triggers a new container launch. It cannot be overridden
|
|||||||
on macOS and Windows.
|
on macOS and Windows.
|
||||||
|
|
||||||
Platform-specific environment variables also available:<br/>
|
Platform-specific environment variables also available:<br/>
|
||||||
`CIBW_BEFORE_ALL_MACOS` | `CIBW_BEFORE_ALL_WINDOWS` | `CIBW_BEFORE_ALL_LINUX`
|
`CIBW_BEFORE_ALL_MACOS` | `CIBW_BEFORE_ALL_WINDOWS` | `CIBW_BEFORE_ALL_LINUX` | `CIBW_BEFORE_ALL_PYODIDE`
|
||||||
|
|
||||||
!!! note
|
!!! note
|
||||||
|
|
||||||
@@ -912,7 +942,7 @@ The active Python binary can be accessed using `python`, and pip with `pip`; cib
|
|||||||
The command is run in a shell, so you can write things like `cmd1 && cmd2`.
|
The command is run in a shell, so you can write things like `cmd1 && cmd2`.
|
||||||
|
|
||||||
Platform-specific environment variables are also available:<br/>
|
Platform-specific environment variables are also available:<br/>
|
||||||
`CIBW_BEFORE_BUILD_MACOS` | `CIBW_BEFORE_BUILD_WINDOWS` | `CIBW_BEFORE_BUILD_LINUX`
|
`CIBW_BEFORE_BUILD_MACOS` | `CIBW_BEFORE_BUILD_WINDOWS` | `CIBW_BEFORE_BUILD_LINUX` | `CIBW_BEFORE_BUILD_PYODIDE`
|
||||||
|
|
||||||
#### Examples
|
#### Examples
|
||||||
|
|
||||||
@@ -993,6 +1023,7 @@ Default:
|
|||||||
- on Linux: `'auditwheel repair -w {dest_dir} {wheel}'`
|
- on Linux: `'auditwheel repair -w {dest_dir} {wheel}'`
|
||||||
- on macOS: `'delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}'`
|
- on macOS: `'delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}'`
|
||||||
- on Windows: `''`
|
- on Windows: `''`
|
||||||
|
- on Pyodide: `''`
|
||||||
|
|
||||||
A shell command to repair a built wheel by copying external library dependencies into the wheel tree and relinking them.
|
A shell command to repair a built wheel by copying external library dependencies into the wheel tree and relinking them.
|
||||||
The command is run on each built wheel (except for pure Python ones) before testing it.
|
The command is run on each built wheel (except for pure Python ones) before testing it.
|
||||||
@@ -1006,7 +1037,7 @@ The following placeholders must be used inside the command and will be replaced
|
|||||||
The command is run in a shell, so you can run multiple commands like `cmd1 && cmd2`.
|
The command is run in a shell, so you can run multiple commands like `cmd1 && cmd2`.
|
||||||
|
|
||||||
Platform-specific environment variables are also available:<br/>
|
Platform-specific environment variables are also available:<br/>
|
||||||
`CIBW_REPAIR_WHEEL_COMMAND_MACOS` | `CIBW_REPAIR_WHEEL_COMMAND_WINDOWS` | `CIBW_REPAIR_WHEEL_COMMAND_LINUX`
|
`CIBW_REPAIR_WHEEL_COMMAND_MACOS` | `CIBW_REPAIR_WHEEL_COMMAND_WINDOWS` | `CIBW_REPAIR_WHEEL_COMMAND_LINUX` | `CIBW_REPAIR_WHEEL_COMMAND_PYODIDE`
|
||||||
|
|
||||||
!!! tip
|
!!! tip
|
||||||
cibuildwheel doesn't yet ship a default repair command for Windows.
|
cibuildwheel doesn't yet ship a default repair command for Windows.
|
||||||
@@ -1017,6 +1048,12 @@ Platform-specific environment variables are also available:<br/>
|
|||||||
|
|
||||||
[Delvewheel]: https://github.com/adang1345/delvewheel
|
[Delvewheel]: https://github.com/adang1345/delvewheel
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
When using `--platform pyodide`, `pyodide build` is used to do the build,
|
||||||
|
which already uses `auditwheel-emscripten` to repair the wheel, so the default
|
||||||
|
repair command is empty. If there is a way to do this in two steps in the future,
|
||||||
|
this could change.
|
||||||
|
|
||||||
#### Examples
|
#### Examples
|
||||||
|
|
||||||
!!! tab examples "Environment variables"
|
!!! tab examples "Environment variables"
|
||||||
@@ -1281,7 +1318,7 @@ here and it will be used instead.
|
|||||||
`./constraints.txt` if that's not found.
|
`./constraints.txt` if that's not found.
|
||||||
|
|
||||||
Platform-specific environment variables are also available:<br/>
|
Platform-specific environment variables are also available:<br/>
|
||||||
`CIBW_DEPENDENCY_VERSIONS_MACOS` | `CIBW_DEPENDENCY_VERSIONS_WINDOWS`
|
`CIBW_DEPENDENCY_VERSIONS_MACOS` | `CIBW_DEPENDENCY_VERSIONS_WINDOWS` | `CIBW_DEPENDENCY_VERSIONS_PYODIDE`
|
||||||
|
|
||||||
!!! note
|
!!! note
|
||||||
This option does not affect the tools used on the Linux build - those versions
|
This option does not affect the tools used on the Linux build - those versions
|
||||||
@@ -1339,7 +1376,7 @@ not be installed after building.
|
|||||||
The command is run in a shell, so you can write things like `cmd1 && cmd2`.
|
The command is run in a shell, so you can write things like `cmd1 && cmd2`.
|
||||||
|
|
||||||
Platform-specific environment variables are also available:<br/>
|
Platform-specific environment variables are also available:<br/>
|
||||||
`CIBW_TEST_COMMAND_MACOS` | `CIBW_TEST_COMMAND_WINDOWS` | `CIBW_TEST_COMMAND_LINUX`
|
`CIBW_TEST_COMMAND_MACOS` | `CIBW_TEST_COMMAND_WINDOWS` | `CIBW_TEST_COMMAND_LINUX` | `CIBW_TEST_COMMAND_PYODIDE`
|
||||||
|
|
||||||
#### Examples
|
#### Examples
|
||||||
|
|
||||||
@@ -1402,7 +1439,7 @@ The active Python binary can be accessed using `python`, and pip with `pip`; cib
|
|||||||
The command is run in a shell, so you can write things like `cmd1 && cmd2`.
|
The command is run in a shell, so you can write things like `cmd1 && cmd2`.
|
||||||
|
|
||||||
Platform-specific environment variables are also available:<br/>
|
Platform-specific environment variables are also available:<br/>
|
||||||
`CIBW_BEFORE_TEST_MACOS` | `CIBW_BEFORE_TEST_WINDOWS` | `CIBW_BEFORE_TEST_LINUX`
|
`CIBW_BEFORE_TEST_MACOS` | `CIBW_BEFORE_TEST_WINDOWS` | `CIBW_BEFORE_TEST_LINUX` | `CIBW_BEFORE_TEST_PYODIDE`
|
||||||
|
|
||||||
#### Examples
|
#### Examples
|
||||||
|
|
||||||
@@ -1462,7 +1499,7 @@ Platform-specific environment variables are also available:<br/>
|
|||||||
Space-separated list of dependencies required for running the tests.
|
Space-separated list of dependencies required for running the tests.
|
||||||
|
|
||||||
Platform-specific environment variables are also available:<br/>
|
Platform-specific environment variables are also available:<br/>
|
||||||
`CIBW_TEST_REQUIRES_MACOS` | `CIBW_TEST_REQUIRES_WINDOWS` | `CIBW_TEST_REQUIRES_LINUX`
|
`CIBW_TEST_REQUIRES_MACOS` | `CIBW_TEST_REQUIRES_WINDOWS` | `CIBW_TEST_REQUIRES_LINUX` | `CIBW_TEST_REQUIRES_PYODIDE`
|
||||||
|
|
||||||
#### Examples
|
#### Examples
|
||||||
|
|
||||||
@@ -1502,7 +1539,7 @@ tests. This can be used to avoid having to redefine test dependencies in
|
|||||||
`setup.cfg` or `setup.py`.
|
`setup.cfg` or `setup.py`.
|
||||||
|
|
||||||
Platform-specific environment variables are also available:<br/>
|
Platform-specific environment variables are also available:<br/>
|
||||||
`CIBW_TEST_EXTRAS_MACOS` | `CIBW_TEST_EXTRAS_WINDOWS` | `CIBW_TEST_EXTRAS_LINUX`
|
`CIBW_TEST_EXTRAS_MACOS` | `CIBW_TEST_EXTRAS_WINDOWS` | `CIBW_TEST_EXTRAS_LINUX` | `CIBW_TEST_EXTRAS_PYODIDE`
|
||||||
|
|
||||||
#### Examples
|
#### Examples
|
||||||
|
|
||||||
@@ -1575,13 +1612,27 @@ Default: Off (0).
|
|||||||
export CIBW_DEBUG_KEEP_CONTAINER=TRUE
|
export CIBW_DEBUG_KEEP_CONTAINER=TRUE
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `CIBW_DEBUG_TRACEBACK`
|
||||||
|
> Print full traceback when errors occur.
|
||||||
|
|
||||||
|
Print a full traceback for the cibuildwheel process when errors occur. This
|
||||||
|
option is provided for debugging cibuildwheel.
|
||||||
|
|
||||||
|
This option can also be set using the [command-line option](#command-line) `--debug-traceback`.
|
||||||
|
|
||||||
|
#### Examples
|
||||||
|
|
||||||
|
```shell
|
||||||
|
export CIBW_DEBUG_TRACEBACK=TRUE
|
||||||
|
```
|
||||||
|
|
||||||
### `CIBW_BUILD_VERBOSITY` {: #build-verbosity}
|
### `CIBW_BUILD_VERBOSITY` {: #build-verbosity}
|
||||||
> Increase/decrease the output of pip wheel
|
> Increase/decrease the output of pip wheel
|
||||||
|
|
||||||
A number from 1 to 3 to increase the level of verbosity (corresponding to invoking pip with `-v`, `-vv`, and `-vvv`), between -1 and -3 (`-q`, `-qq`, and `-qqq`), or just 0 (default verbosity). These flags are useful while debugging a build when the output of the actual build invoked by `pip wheel` is required. Has no effect on the `build` backend, which produces verbose output by default.
|
A number from 1 to 3 to increase the level of verbosity (corresponding to invoking pip with `-v`, `-vv`, and `-vvv`), between -1 and -3 (`-q`, `-qq`, and `-qqq`), or just 0 (default verbosity). These flags are useful while debugging a build when the output of the actual build invoked by `pip wheel` is required. Has no effect on the `build` backend, which produces verbose output by default.
|
||||||
|
|
||||||
Platform-specific environment variables are also available:<br/>
|
Platform-specific environment variables are also available:<br/>
|
||||||
`CIBW_BUILD_VERBOSITY_MACOS` | `CIBW_BUILD_VERBOSITY_WINDOWS` | `CIBW_BUILD_VERBOSITY_LINUX`
|
`CIBW_BUILD_VERBOSITY_MACOS` | `CIBW_BUILD_VERBOSITY_WINDOWS` | `CIBW_BUILD_VERBOSITY_LINUX` | `CIBW_BUILD_VERBOSITY_PYODIDE`
|
||||||
|
|
||||||
#### Examples
|
#### Examples
|
||||||
|
|
||||||
@@ -1601,12 +1652,25 @@ Platform-specific environment variables are also available:<br/>
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Command line options {: #command-line}
|
## Command line {: #command-line}
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
```text
|
```text
|
||||||
« subprocess_run("cibuildwheel", "--help") »
|
« subprocess_run("cibuildwheel", "--help") »
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Return codes
|
||||||
|
|
||||||
|
cibuildwheel exits 0 on success, or >0 if an error occurs.
|
||||||
|
|
||||||
|
Specific error codes are defined:
|
||||||
|
|
||||||
|
- 2 means a configuration error
|
||||||
|
- 3 means no builds are selected (and --allow-empty wasn't passed)
|
||||||
|
- 4 means you specified an option that has been deprecated.
|
||||||
|
|
||||||
|
|
||||||
## Placeholders
|
## Placeholders
|
||||||
|
|
||||||
Some options support placeholders, like `{project}`, `{package}` or `{wheel}`, that are substituted by cibuildwheel before they are used. If, for some reason, you need to write the literal name of a placeholder, e.g. literally `{project}` in a command that would ordinarily substitute `{project}`, prefix it with a hash character - `#{project}`. This is only necessary in commands where the specific string between the curly brackets would be substituted - otherwise, strings not modified.
|
Some options support placeholders, like `{project}`, `{package}` or `{wheel}`, that are substituted by cibuildwheel before they are used. If, for some reason, you need to write the literal name of a placeholder, e.g. literally `{project}` in a command that would ordinarily substitute `{project}`, prefix it with a hash character - `#{project}`. This is only necessary in commands where the specific string between the curly brackets would be substituted - otherwise, strings not modified.
|
||||||
|
|||||||
+9
-2
@@ -107,6 +107,13 @@ You can override the cache folder using the ``CIBW_CACHE_PATH`` environment vari
|
|||||||
Download link: https://www.python.org/ftp/python/3.6.8/python-3.6.8-macosx10.9.pkg
|
Download link: https://www.python.org/ftp/python/3.6.8/python-3.6.8-macosx10.9.pkg
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Pyodide (WebAssembly) builds (experimental)
|
||||||
|
|
||||||
|
Pre-requisite: you need to have a matching host version of Python (unlike all
|
||||||
|
other cibuildwheel platforms). Linux host highly recommended; macOS hosts may
|
||||||
|
work (e.g. invoking `pytest` directly in [`CIBW_TEST_COMMAND`](options.md#test-command) is [currently failing](https://github.com/pyodide/pyodide/issues/4802)) and Windows hosts will not work.
|
||||||
|
|
||||||
|
You must target pyodide with `--platform pyodide` (or use `--only` on the identifier).
|
||||||
|
|
||||||
## Configure a CI service
|
## Configure a CI service
|
||||||
|
|
||||||
@@ -154,7 +161,7 @@ To build Linux, Mac, and Windows wheels using GitHub Actions, create a `.github/
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Build wheels
|
- name: Build wheels
|
||||||
run: pipx run cibuildwheel==2.18.1
|
run: pipx run cibuildwheel==2.19.1
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
@@ -191,7 +198,7 @@ To build Linux, Mac, and Windows wheels using GitHub Actions, create a `.github/
|
|||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@v5
|
||||||
|
|
||||||
- name: Install cibuildwheel
|
- name: Install cibuildwheel
|
||||||
run: python -m pip install cibuildwheel==2.18.1
|
run: python -m pip install cibuildwheel==2.19.1
|
||||||
|
|
||||||
- name: Build wheels
|
- name: Build wheels
|
||||||
run: python -m cibuildwheel --output-dir wheelhouse
|
run: python -m cibuildwheel --output-dir wheelhouse
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ title: Working examples
|
|||||||
| [Tornado][] | ![github icon][] | ![linux icon][] ![apple icon][] ![windows icon][] | Tornado is a Python web framework and asynchronous networking library. Uses stable ABI for a small C extension. |
|
| [Tornado][] | ![github icon][] | ![linux icon][] ![apple icon][] ![windows icon][] | Tornado is a Python web framework and asynchronous networking library. Uses stable ABI for a small C extension. |
|
||||||
| [Matplotlib][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The venerable Matplotlib, a Python library with C++ portions |
|
| [Matplotlib][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The venerable Matplotlib, a Python library with C++ portions |
|
||||||
| [NCNN][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | ncnn is a high-performance neural network inference framework optimized for the mobile platform |
|
| [NCNN][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | ncnn is a high-performance neural network inference framework optimized for the mobile platform |
|
||||||
|
| [duckdb][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | DuckDB is an analytical in-process SQL database management system |
|
||||||
| [Prophet][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Tool for producing high quality forecasts for time series data that has multiple seasonality with linear or non-linear growth. |
|
| [Prophet][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Tool for producing high quality forecasts for time series data that has multiple seasonality with linear or non-linear growth. |
|
||||||
| [MyPy][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | The compiled version of MyPy using MyPyC. |
|
| [MyPy][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | The compiled version of MyPy using MyPyC. |
|
||||||
| [duckdb][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | DuckDB is an in-process SQL OLAP Database Management System |
|
|
||||||
| [Kivy][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Open source UI framework written in Python, running on Windows, Linux, macOS, Android and iOS |
|
| [Kivy][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Open source UI framework written in Python, running on Windows, Linux, macOS, Android and iOS |
|
||||||
| [MemRay][] | ![github icon][] | ![linux icon][] | Memray is a memory profiler for Python |
|
| [MemRay][] | ![github icon][] | ![linux icon][] | Memray is a memory profiler for Python |
|
||||||
| [Triton][] | ![github icon][] | ![linux icon][] | Self hosted runners |
|
| [Triton][] | ![github icon][] | ![linux icon][] | Self hosted runners |
|
||||||
@@ -108,8 +108,8 @@ title: Working examples
|
|||||||
| [numpythia][] | ![github icon][] | ![apple icon][] ![linux icon][] | The interface between PYTHIA and NumPy |
|
| [numpythia][] | ![github icon][] | ![apple icon][] ![linux icon][] | The interface between PYTHIA and NumPy |
|
||||||
| [pyjet][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The interface between FastJet and NumPy |
|
| [pyjet][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The interface between FastJet and NumPy |
|
||||||
| [ril][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A python binding to Rust Imaging library using maturin and Pyo3, utilizes Github Action cache to improve speed. Builds abi3 wheels. |
|
| [ril][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A python binding to Rust Imaging library using maturin and Pyo3, utilizes Github Action cache to improve speed. Builds abi3 wheels. |
|
||||||
| [GSD][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Cython and NumPy project with 64-bit wheels. |
|
|
||||||
| [SiPM][] | ![github icon][] | ![apple icon][] ![linux icon][] | High performance library for SiPM detectors simulation using C++17, OpenMP and AVX2 intrinsics. |
|
| [SiPM][] | ![github icon][] | ![apple icon][] ![linux icon][] | High performance library for SiPM detectors simulation using C++17, OpenMP and AVX2 intrinsics. |
|
||||||
|
| [GSD][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Cython and NumPy project with 64-bit wheels. |
|
||||||
| [CorrectionLib][] | ![github icon][] | ![apple icon][] ![linux icon][] | Structured JSON powered correction library for HEP, designed for the CMS experiment at CERN. |
|
| [CorrectionLib][] | ![github icon][] | ![apple icon][] ![linux icon][] | Structured JSON powered correction library for HEP, designed for the CMS experiment at CERN. |
|
||||||
| [aalink][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Async Python interface for Ableton Link. |
|
| [aalink][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Async Python interface for Ableton Link. |
|
||||||
| [xmlstarlet][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Python 3.6+ CFFI bindings with true MSVC build. |
|
| [xmlstarlet][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Python 3.6+ CFFI bindings with true MSVC build. |
|
||||||
@@ -123,9 +123,9 @@ title: Working examples
|
|||||||
[Tornado]: https://github.com/tornadoweb/tornado
|
[Tornado]: https://github.com/tornadoweb/tornado
|
||||||
[Matplotlib]: https://github.com/matplotlib/matplotlib
|
[Matplotlib]: https://github.com/matplotlib/matplotlib
|
||||||
[NCNN]: https://github.com/Tencent/ncnn
|
[NCNN]: https://github.com/Tencent/ncnn
|
||||||
|
[duckdb]: https://github.com/duckdb/duckdb
|
||||||
[Prophet]: https://github.com/facebook/prophet
|
[Prophet]: https://github.com/facebook/prophet
|
||||||
[MyPy]: https://github.com/mypyc/mypy_mypyc-wheels
|
[MyPy]: https://github.com/mypyc/mypy_mypyc-wheels
|
||||||
[duckdb]: https://github.com/duckdb/duckdb
|
|
||||||
[Kivy]: https://github.com/kivy/kivy
|
[Kivy]: https://github.com/kivy/kivy
|
||||||
[MemRay]: https://github.com/bloomberg/memray
|
[MemRay]: https://github.com/bloomberg/memray
|
||||||
[Triton]: https://github.com/openai/triton
|
[Triton]: https://github.com/openai/triton
|
||||||
@@ -217,8 +217,8 @@ title: Working examples
|
|||||||
[numpythia]: https://github.com/scikit-hep/numpythia
|
[numpythia]: https://github.com/scikit-hep/numpythia
|
||||||
[pyjet]: https://github.com/scikit-hep/pyjet
|
[pyjet]: https://github.com/scikit-hep/pyjet
|
||||||
[ril]: https://github.com/Cryptex-github/ril-py
|
[ril]: https://github.com/Cryptex-github/ril-py
|
||||||
[GSD]: https://github.com/glotzerlab/gsd
|
|
||||||
[SiPM]: https://github.com/EdoPro98/SimSiPM
|
[SiPM]: https://github.com/EdoPro98/SimSiPM
|
||||||
|
[GSD]: https://github.com/glotzerlab/gsd
|
||||||
[CorrectionLib]: https://github.com/cms-nanoAOD/correctionlib
|
[CorrectionLib]: https://github.com/cms-nanoAOD/correctionlib
|
||||||
[aalink]: https://github.com/artfwo/aalink
|
[aalink]: https://github.com/artfwo/aalink
|
||||||
[xmlstarlet]: https://github.com/dimitern/xmlstarlet
|
[xmlstarlet]: https://github.com/dimitern/xmlstarlet
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ stack: python 3.7
|
|||||||
init:
|
init:
|
||||||
- cmd: set PATH=C:\Python37;C:\Python37\Scripts;%PATH%
|
- cmd: set PATH=C:\Python37;C:\Python37\Scripts;%PATH%
|
||||||
|
|
||||||
install: python -m pip install cibuildwheel==2.18.1
|
install: python -m pip install cibuildwheel==2.19.1
|
||||||
|
|
||||||
build_script: python -m cibuildwheel --output-dir wheelhouse
|
build_script: python -m cibuildwheel --output-dir wheelhouse
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ jobs:
|
|||||||
- bash: |
|
- bash: |
|
||||||
set -o errexit
|
set -o errexit
|
||||||
python3 -m pip install --upgrade pip
|
python3 -m pip install --upgrade pip
|
||||||
pip3 install cibuildwheel==2.18.1
|
pip3 install cibuildwheel==2.19.1
|
||||||
displayName: Install dependencies
|
displayName: Install dependencies
|
||||||
- bash: cibuildwheel --output-dir wheelhouse .
|
- bash: cibuildwheel --output-dir wheelhouse .
|
||||||
displayName: Build wheels
|
displayName: Build wheels
|
||||||
@@ -20,7 +20,7 @@ jobs:
|
|||||||
- bash: |
|
- bash: |
|
||||||
set -o errexit
|
set -o errexit
|
||||||
python3 -m pip install --upgrade pip
|
python3 -m pip install --upgrade pip
|
||||||
python3 -m pip install cibuildwheel==2.18.1
|
python3 -m pip install cibuildwheel==2.19.1
|
||||||
displayName: Install dependencies
|
displayName: Install dependencies
|
||||||
- bash: cibuildwheel --output-dir wheelhouse .
|
- bash: cibuildwheel --output-dir wheelhouse .
|
||||||
displayName: Build wheels
|
displayName: Build wheels
|
||||||
@@ -34,7 +34,7 @@ jobs:
|
|||||||
- bash: |
|
- bash: |
|
||||||
set -o errexit
|
set -o errexit
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install cibuildwheel==2.18.1
|
pip install cibuildwheel==2.19.1
|
||||||
displayName: Install dependencies
|
displayName: Install dependencies
|
||||||
- bash: cibuildwheel --output-dir wheelhouse .
|
- bash: cibuildwheel --output-dir wheelhouse .
|
||||||
displayName: Build wheels
|
displayName: Build wheels
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ jobs:
|
|||||||
- run:
|
- run:
|
||||||
name: Build the Linux wheels.
|
name: Build the Linux wheels.
|
||||||
command: |
|
command: |
|
||||||
pip3 install --user cibuildwheel==2.18.1
|
pip3 install --user cibuildwheel==2.19.1
|
||||||
cibuildwheel --output-dir wheelhouse
|
cibuildwheel --output-dir wheelhouse
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: wheelhouse/
|
path: wheelhouse/
|
||||||
@@ -28,7 +28,7 @@ jobs:
|
|||||||
- run:
|
- run:
|
||||||
name: Build the Linux aarch64 wheels.
|
name: Build the Linux aarch64 wheels.
|
||||||
command: |
|
command: |
|
||||||
python3 -m pip install --user cibuildwheel==2.18.1
|
python3 -m pip install --user cibuildwheel==2.19.1
|
||||||
python3 -m cibuildwheel --output-dir wheelhouse
|
python3 -m cibuildwheel --output-dir wheelhouse
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: wheelhouse/
|
path: wheelhouse/
|
||||||
@@ -42,7 +42,7 @@ jobs:
|
|||||||
- run:
|
- run:
|
||||||
name: Build the OS X wheels.
|
name: Build the OS X wheels.
|
||||||
command: |
|
command: |
|
||||||
pip3 install cibuildwheel==2.18.1
|
pip3 install cibuildwheel==2.19.1
|
||||||
cibuildwheel --output-dir wheelhouse
|
cibuildwheel --output-dir wheelhouse
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: wheelhouse/
|
path: wheelhouse/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
build_and_store_wheels: &BUILD_AND_STORE_WHEELS
|
build_and_store_wheels: &BUILD_AND_STORE_WHEELS
|
||||||
install_cibuildwheel_script:
|
install_cibuildwheel_script:
|
||||||
- python -m pip install cibuildwheel==2.18.1
|
- python -m pip install cibuildwheel==2.19.1
|
||||||
run_cibuildwheel_script:
|
run_cibuildwheel_script:
|
||||||
- cibuildwheel
|
- cibuildwheel
|
||||||
wheels_artifacts:
|
wheels_artifacts:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
build_and_store_wheels: &BUILD_AND_STORE_WHEELS
|
build_and_store_wheels: &BUILD_AND_STORE_WHEELS
|
||||||
install_cibuildwheel_script:
|
install_cibuildwheel_script:
|
||||||
- python -m pip install cibuildwheel==2.18.1
|
- python -m pip install cibuildwheel==2.19.1
|
||||||
run_cibuildwheel_script:
|
run_cibuildwheel_script:
|
||||||
- cibuildwheel
|
- cibuildwheel
|
||||||
wheels_artifacts:
|
wheels_artifacts:
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Build wheels
|
- name: Build wheels
|
||||||
uses: pypa/cibuildwheel@v2.18.1
|
uses: pypa/cibuildwheel@v2.19.1
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Build wheels
|
- name: Build wheels
|
||||||
uses: pypa/cibuildwheel@v2.18.1
|
uses: pypa/cibuildwheel@v2.19.1
|
||||||
# env:
|
# env:
|
||||||
# CIBW_SOME_OPTION: value
|
# CIBW_SOME_OPTION: value
|
||||||
# ...
|
# ...
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ jobs:
|
|||||||
platforms: all
|
platforms: all
|
||||||
|
|
||||||
- name: Build wheels
|
- name: Build wheels
|
||||||
uses: pypa/cibuildwheel@v2.18.1
|
uses: pypa/cibuildwheel@v2.19.1
|
||||||
env:
|
env:
|
||||||
# configure cibuildwheel to build native archs ('auto'), and some
|
# configure cibuildwheel to build native archs ('auto'), and some
|
||||||
# emulated ones
|
# emulated ones
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ linux:
|
|||||||
DOCKER_TLS_CERTDIR: ""
|
DOCKER_TLS_CERTDIR: ""
|
||||||
script:
|
script:
|
||||||
- curl -sSL https://get.docker.com/ | sh
|
- curl -sSL https://get.docker.com/ | sh
|
||||||
- python -m pip install cibuildwheel==2.18.1
|
- python -m pip install cibuildwheel==2.19.1
|
||||||
- cibuildwheel --output-dir wheelhouse
|
- cibuildwheel --output-dir wheelhouse
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
@@ -23,7 +23,7 @@ windows:
|
|||||||
before_script:
|
before_script:
|
||||||
- choco install python -y --version 3.8.6
|
- choco install python -y --version 3.8.6
|
||||||
- choco install git.install -y
|
- choco install git.install -y
|
||||||
- py -m pip install cibuildwheel==2.18.1
|
- py -m pip install cibuildwheel==2.19.1
|
||||||
script:
|
script:
|
||||||
- py -m cibuildwheel --output-dir wheelhouse --platform windows
|
- py -m cibuildwheel --output-dir wheelhouse --platform windows
|
||||||
artifacts:
|
artifacts:
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ linux:
|
|||||||
- curl -sSL https://get.docker.com/ | sh
|
- curl -sSL https://get.docker.com/ | sh
|
||||||
# Warning: This is extremely slow, be careful with how many wheels you build
|
# Warning: This is extremely slow, be careful with how many wheels you build
|
||||||
- docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
|
- docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
|
||||||
- python -m pip install cibuildwheel==2.18.1
|
- python -m pip install cibuildwheel==2.19.1
|
||||||
# Assuming your CI runner's default architecture is x86_64...
|
# Assuming your CI runner's default architecture is x86_64...
|
||||||
- cibuildwheel --output-dir wheelhouse --platform linux --archs aarch64
|
- cibuildwheel --output-dir wheelhouse --platform linux --archs aarch64
|
||||||
artifacts:
|
artifacts:
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ jobs:
|
|||||||
- ln -s /c/Python38/python.exe /c/Python38/python3.exe
|
- ln -s /c/Python38/python.exe /c/Python38/python3.exe
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- python3 -m pip install cibuildwheel==2.18.1
|
- python3 -m pip install cibuildwheel==2.19.1
|
||||||
|
|
||||||
script:
|
script:
|
||||||
# build the wheels, put them into './dist'
|
# build the wheels, put them into './dist'
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ jobs:
|
|||||||
- ln -s /c/Python38/python.exe /c/Python38/python3.exe
|
- ln -s /c/Python38/python.exe /c/Python38/python3.exe
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- python3 -m pip install cibuildwheel==2.18.1
|
- python3 -m pip install cibuildwheel==2.19.1
|
||||||
|
|
||||||
script:
|
script:
|
||||||
# build the wheels, put them into './wheelhouse'
|
# build the wheels, put them into './wheelhouse'
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ jobs:
|
|||||||
- stage: deploy
|
- stage: deploy
|
||||||
name: Build and deploy Linux wheels
|
name: Build and deploy Linux wheels
|
||||||
services: docker
|
services: docker
|
||||||
install: python3 -m pip install cibuildwheel==2.18.1 twine
|
install: python3 -m pip install cibuildwheel==2.19.1 twine
|
||||||
script: python3 -m cibuildwheel --output-dir wheelhouse
|
script: python3 -m cibuildwheel --output-dir wheelhouse
|
||||||
after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
|
after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
|
||||||
# Deploy on windows
|
# Deploy on windows
|
||||||
@@ -62,7 +62,7 @@ jobs:
|
|||||||
name: Build and deploy Windows wheels
|
name: Build and deploy Windows wheels
|
||||||
os: windows
|
os: windows
|
||||||
language: shell
|
language: shell
|
||||||
install: python3 -m pip install cibuildwheel==2.18.1 twine
|
install: python3 -m pip install cibuildwheel==2.19.1 twine
|
||||||
script: python3 -m cibuildwheel --output-dir wheelhouse
|
script: python3 -m cibuildwheel --output-dir wheelhouse
|
||||||
after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
|
after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
|
||||||
|
|
||||||
|
|||||||
+28
-5
@@ -70,6 +70,8 @@ def update_constraints(session: nox.Session) -> None:
|
|||||||
Update the dependencies inplace.
|
Update the dependencies inplace.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
resources = Path("cibuildwheel/resources")
|
||||||
|
|
||||||
if session.venv_backend != "uv":
|
if session.venv_backend != "uv":
|
||||||
session.install("uv>=0.1.23")
|
session.install("uv>=0.1.23")
|
||||||
|
|
||||||
@@ -79,22 +81,42 @@ def update_constraints(session: nox.Session) -> None:
|
|||||||
# CUSTOM_COMPILE_COMMAND is a pip-compile option that tells users how to
|
# CUSTOM_COMPILE_COMMAND is a pip-compile option that tells users how to
|
||||||
# regenerate the constraints files
|
# regenerate the constraints files
|
||||||
env["UV_CUSTOM_COMPILE_COMMAND"] = f"nox -s {session.name}"
|
env["UV_CUSTOM_COMPILE_COMMAND"] = f"nox -s {session.name}"
|
||||||
|
output_file = resources / f"constraints-python{python_version.replace('.', '')}.txt"
|
||||||
session.run(
|
session.run(
|
||||||
"uv",
|
"uv",
|
||||||
"pip",
|
"pip",
|
||||||
"compile",
|
"compile",
|
||||||
f"--python-version={python_version}",
|
f"--python-version={python_version}",
|
||||||
"--upgrade",
|
"--upgrade",
|
||||||
"cibuildwheel/resources/constraints.in",
|
resources / "constraints.in",
|
||||||
f"--output-file=cibuildwheel/resources/constraints-python{python_version.replace('.', '')}.txt",
|
f"--output-file={output_file}",
|
||||||
env=env,
|
env=env,
|
||||||
)
|
)
|
||||||
RESOURCES = DIR / "cibuildwheel" / "resources"
|
|
||||||
shutil.copyfile(
|
shutil.copyfile(
|
||||||
RESOURCES / "constraints-python312.txt",
|
resources / "constraints-python312.txt",
|
||||||
RESOURCES / "constraints.txt",
|
resources / "constraints.txt",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
build_platforms = nox.project.load_toml(resources / "build-platforms.toml")
|
||||||
|
pyodides = build_platforms["pyodide"]["python_configurations"]
|
||||||
|
for pyodide in pyodides:
|
||||||
|
python_version = ".".join(pyodide["version"].split(".")[:2])
|
||||||
|
pyodide_version = pyodide["pyodide_version"]
|
||||||
|
output_file = resources / f"constraints-pyodide{python_version.replace('.', '')}.txt"
|
||||||
|
tmp_file = Path(session.create_tmp()) / "constraints-pyodide.in"
|
||||||
|
tmp_file.write_text(f"pip\nbuild[virtualenv]\npyodide-build=={pyodide_version}")
|
||||||
|
session.run(
|
||||||
|
"uv",
|
||||||
|
"pip",
|
||||||
|
"compile",
|
||||||
|
f"--python-version={python_version}",
|
||||||
|
"--upgrade",
|
||||||
|
tmp_file,
|
||||||
|
f"--output-file={output_file}",
|
||||||
|
env=env,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@nox.session
|
@nox.session
|
||||||
def update_pins(session: nox.Session) -> None:
|
def update_pins(session: nox.Session) -> None:
|
||||||
@@ -105,6 +127,7 @@ def update_pins(session: nox.Session) -> None:
|
|||||||
session.run("python", "bin/update_pythons.py", "--force")
|
session.run("python", "bin/update_pythons.py", "--force")
|
||||||
session.run("python", "bin/update_docker.py")
|
session.run("python", "bin/update_docker.py")
|
||||||
session.run("python", "bin/update_virtualenv.py", "--force")
|
session.run("python", "bin/update_virtualenv.py", "--force")
|
||||||
|
session.run("python", "bin/update_nodejs.py", "--force")
|
||||||
|
|
||||||
|
|
||||||
@nox.session(reuse_venv=True)
|
@nox.session(reuse_venv=True)
|
||||||
|
|||||||
+9
-2
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "cibuildwheel"
|
name = "cibuildwheel"
|
||||||
version = "2.18.1"
|
version = "2.19.1"
|
||||||
description = "Build Python wheels on CI with minimal configuration."
|
description = "Build Python wheels on CI with minimal configuration."
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
license = "BSD-2-Clause"
|
license = "BSD-2-Clause"
|
||||||
@@ -78,6 +78,7 @@ test = [
|
|||||||
"tomli_w",
|
"tomli_w",
|
||||||
"validate-pyproject",
|
"validate-pyproject",
|
||||||
]
|
]
|
||||||
|
uv = ["uv"]
|
||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
cibuildwheel = "cibuildwheel.__main__:main"
|
cibuildwheel = "cibuildwheel.__main__:main"
|
||||||
@@ -92,7 +93,12 @@ Homepage = "https://github.com/pypa/cibuildwheel"
|
|||||||
|
|
||||||
[tool.pytest.ini_options]
|
[tool.pytest.ini_options]
|
||||||
minversion = "6.0"
|
minversion = "6.0"
|
||||||
addopts = ["-ra", "--showlocals", "--strict-markers", "--strict-config"]
|
addopts = [
|
||||||
|
"-rfEsX", # TODO: replace with -ra when pytest > 8.2.2 is released
|
||||||
|
"--showlocals",
|
||||||
|
"--strict-markers",
|
||||||
|
"--strict-config",
|
||||||
|
]
|
||||||
junit_family = "xunit2"
|
junit_family = "xunit2"
|
||||||
xfail_strict = true
|
xfail_strict = true
|
||||||
filterwarnings = ["error"]
|
filterwarnings = ["error"]
|
||||||
@@ -176,6 +182,7 @@ messages_control.disable = [
|
|||||||
"wrong-import-position",
|
"wrong-import-position",
|
||||||
"unused-argument", # Handled by Ruff
|
"unused-argument", # Handled by Ruff
|
||||||
"broad-exception-raised", # Could be improved eventually
|
"broad-exception-raised", # Could be improved eventually
|
||||||
|
"consider-using-in", # MyPy can't narrow "in"
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.ruff]
|
[tool.ruff]
|
||||||
|
|||||||
+19
-4
@@ -6,14 +6,18 @@ from typing import Generator
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from cibuildwheel.util import detect_ci_provider
|
from cibuildwheel.util import detect_ci_provider, find_uv
|
||||||
|
|
||||||
from .utils import platform
|
from .utils import EMULATED_ARCHS, platform
|
||||||
|
|
||||||
|
|
||||||
def pytest_addoption(parser) -> None:
|
def pytest_addoption(parser) -> None:
|
||||||
parser.addoption(
|
parser.addoption(
|
||||||
"--run-emulation", action="store_true", default=False, help="run emulation tests"
|
"--run-emulation",
|
||||||
|
action="store",
|
||||||
|
default=None,
|
||||||
|
help="run emulation tests",
|
||||||
|
choices=("all", *EMULATED_ARCHS),
|
||||||
)
|
)
|
||||||
parser.addoption("--run-podman", action="store_true", default=False, help="run podman tests")
|
parser.addoption("--run-podman", action="store_true", default=False, help="run podman tests")
|
||||||
parser.addoption(
|
parser.addoption(
|
||||||
@@ -27,10 +31,21 @@ def pytest_addoption(parser) -> None:
|
|||||||
@pytest.fixture(
|
@pytest.fixture(
|
||||||
params=[{"CIBW_BUILD_FRONTEND": "pip"}, {"CIBW_BUILD_FRONTEND": "build"}], ids=["pip", "build"]
|
params=[{"CIBW_BUILD_FRONTEND": "pip"}, {"CIBW_BUILD_FRONTEND": "build"}], ids=["pip", "build"]
|
||||||
)
|
)
|
||||||
def build_frontend_env(request) -> dict[str, str]:
|
def build_frontend_env_nouv(request) -> dict[str, str]:
|
||||||
|
if platform == "pyodide":
|
||||||
|
pytest.skip("Can't use pip as build frontend for pyodide platform")
|
||||||
|
|
||||||
return request.param # type: ignore[no-any-return]
|
return request.param # type: ignore[no-any-return]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture()
|
||||||
|
def build_frontend_env(build_frontend_env_nouv: dict[str, str]) -> dict[str, str]:
|
||||||
|
if build_frontend_env_nouv["CIBW_BUILD_FRONTEND"] == "build" and find_uv() is not None:
|
||||||
|
return {"CIBW_BUILD_FRONTEND": "build[uv]"}
|
||||||
|
|
||||||
|
return build_frontend_env_nouv
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def docker_cleanup() -> Generator[None, None, None]:
|
def docker_cleanup() -> Generator[None, None, None]:
|
||||||
def get_images() -> set[str]:
|
def get_images() -> set[str]:
|
||||||
|
|||||||
+26
-13
@@ -20,7 +20,7 @@ limited_api_project = test_projects.new_c_project(
|
|||||||
CAN_USE_ABI3 = IS_CPYTHON and not Py_GIL_DISABLED
|
CAN_USE_ABI3 = IS_CPYTHON and not Py_GIL_DISABLED
|
||||||
cmdclass = {}
|
cmdclass = {}
|
||||||
extension_kwargs = {}
|
extension_kwargs = {}
|
||||||
if CAN_USE_ABI3 and sys.version_info[:2] >= (3, 8):
|
if CAN_USE_ABI3 and sys.version_info[:2] >= (3, 10):
|
||||||
from wheel.bdist_wheel import bdist_wheel as _bdist_wheel
|
from wheel.bdist_wheel import bdist_wheel as _bdist_wheel
|
||||||
|
|
||||||
class bdist_wheel_abi3(_bdist_wheel):
|
class bdist_wheel_abi3(_bdist_wheel):
|
||||||
@@ -33,7 +33,7 @@ limited_api_project = test_projects.new_c_project(
|
|||||||
return python, "abi3", plat
|
return python, "abi3", plat
|
||||||
|
|
||||||
cmdclass["bdist_wheel"] = bdist_wheel_abi3
|
cmdclass["bdist_wheel"] = bdist_wheel_abi3
|
||||||
extension_kwargs["define_macros"] = [("Py_LIMITED_API", "0x03080000")]
|
extension_kwargs["define_macros"] = [("Py_LIMITED_API", "0x030A0000")]
|
||||||
extension_kwargs["py_limited_api"] = True
|
extension_kwargs["py_limited_api"] = True
|
||||||
"""
|
"""
|
||||||
),
|
),
|
||||||
@@ -48,23 +48,32 @@ def test_abi3(tmp_path):
|
|||||||
project_dir = tmp_path / "project"
|
project_dir = tmp_path / "project"
|
||||||
limited_api_project.generate(project_dir)
|
limited_api_project.generate(project_dir)
|
||||||
|
|
||||||
|
single_python_tag = "cp{}{}".format(*utils.SINGLE_PYTHON_VERSION)
|
||||||
|
|
||||||
# build the wheels
|
# build the wheels
|
||||||
actual_wheels = utils.cibuildwheel_run(
|
actual_wheels = utils.cibuildwheel_run(
|
||||||
project_dir,
|
project_dir,
|
||||||
add_env={
|
add_env={
|
||||||
# free_threaded and PyPy do not have a Py_LIMITED_API equivalent, just build one of those
|
# free_threaded and PyPy do not have a Py_LIMITED_API equivalent, just build one of those
|
||||||
"CIBW_BUILD": "cp3?-* cp31?-* cp313t-* pp310-*"
|
# also limit the number of builds for test performance reasons
|
||||||
|
"CIBW_BUILD": f"cp39-* cp310-* pp310-* {single_python_tag}-* cp313t-*"
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
# check that the expected wheels are produced
|
# check that the expected wheels are produced
|
||||||
expected_wheels = [
|
expected_wheels = utils.expected_wheels("spam", "0.1.0")
|
||||||
w.replace("cp38-cp38", "cp38-abi3")
|
if utils.platform == "pyodide":
|
||||||
for w in utils.expected_wheels("spam", "0.1.0")
|
# there's only 1 possible configuration for pyodide, the single_python_tag one
|
||||||
if ("-pp310" in w or "-pp" not in w)
|
expected_wheels = [
|
||||||
and "-cp39" not in w
|
w.replace(f"{single_python_tag}-{single_python_tag}", f"{single_python_tag}-abi3")
|
||||||
and ("-cp313t" in w or "-cp31" not in w)
|
for w in expected_wheels
|
||||||
]
|
]
|
||||||
|
else:
|
||||||
|
expected_wheels = [
|
||||||
|
w.replace("cp310-cp310", "cp310-abi3")
|
||||||
|
for w in expected_wheels
|
||||||
|
if "-cp39" in w or "-cp310" in w or "-pp310" in w or "-cp313t" in w
|
||||||
|
]
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
|
|
||||||
@@ -183,9 +192,9 @@ def test_abi_none(tmp_path, capfd):
|
|||||||
project_dir,
|
project_dir,
|
||||||
add_env={
|
add_env={
|
||||||
"CIBW_TEST_REQUIRES": "pytest",
|
"CIBW_TEST_REQUIRES": "pytest",
|
||||||
"CIBW_TEST_COMMAND": "pytest {project}/test",
|
"CIBW_TEST_COMMAND": f"{utils.invoke_pytest()} {{project}}/test",
|
||||||
# limit the number of builds for test performance reasons
|
# limit the number of builds for test performance reasons
|
||||||
"CIBW_BUILD": "cp38-* cp310-* cp313t-* pp310-*",
|
"CIBW_BUILD": "cp38-* cp{}{}-* cp313t-* pp310-*".format(*utils.SINGLE_PYTHON_VERSION),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -196,4 +205,8 @@ def test_abi_none(tmp_path, capfd):
|
|||||||
# check that each wheel was built once, and reused
|
# check that each wheel was built once, and reused
|
||||||
captured = capfd.readouterr()
|
captured = capfd.readouterr()
|
||||||
assert "Building wheel..." in captured.out
|
assert "Building wheel..." in captured.out
|
||||||
assert "Found previously built wheel" in captured.out
|
if utils.platform == "pyodide":
|
||||||
|
# there's only 1 possible configuration for pyodide, we won't see the message expected on following builds
|
||||||
|
assert "Found previously built wheel" not in captured.out
|
||||||
|
else:
|
||||||
|
assert "Found previously built wheel" in captured.out
|
||||||
|
|||||||
+21
-8
@@ -10,13 +10,22 @@ from . import test_projects, utils
|
|||||||
project_with_before_build_asserts = test_projects.new_c_project(
|
project_with_before_build_asserts = test_projects.new_c_project(
|
||||||
setup_py_add=textwrap.dedent(
|
setup_py_add=textwrap.dedent(
|
||||||
r"""
|
r"""
|
||||||
# assert that the Python version as written to text_info.txt in the CIBW_BEFORE_ALL step
|
import os
|
||||||
# is the same one as is currently running.
|
|
||||||
with open("text_info.txt") as f:
|
with open("text_info.txt") as f:
|
||||||
stored_text = f.read()
|
stored_text = f.read()
|
||||||
|
|
||||||
print("## stored text: " + stored_text)
|
print("## stored text: " + stored_text)
|
||||||
assert stored_text == "sample text 123"
|
assert stored_text == "sample text 123"
|
||||||
|
|
||||||
|
# assert that the Python version as written to python_prefix.txt in the CIBW_BEFORE_ALL step
|
||||||
|
# is not the same one as is currently running.
|
||||||
|
with open('python_prefix.txt') as f:
|
||||||
|
stored_prefix = f.read()
|
||||||
|
print('stored_prefix', stored_prefix)
|
||||||
|
print('sys.prefix', sys.prefix)
|
||||||
|
# Works around path-comparison bugs caused by short-paths on Windows e.g.
|
||||||
|
# vssadm~1 instead of vssadministrator
|
||||||
|
assert not os.path.samefile(stored_prefix, sys.prefix)
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -30,21 +39,24 @@ def test(tmp_path):
|
|||||||
print("dummy text", file=ff)
|
print("dummy text", file=ff)
|
||||||
|
|
||||||
# build the wheels
|
# build the wheels
|
||||||
before_all_command = '''python -c "import os;open('{project}/text_info.txt', 'w').write('sample text '+os.environ.get('TEST_VAL', ''))"'''
|
before_all_command = (
|
||||||
|
"""python -c "import os, sys;open('{project}/text_info.txt', 'w').write('sample text '+os.environ.get('TEST_VAL', ''))" && """
|
||||||
|
'''python -c "import sys; open('{project}/python_prefix.txt', 'w').write(sys.prefix)"'''
|
||||||
|
)
|
||||||
actual_wheels = utils.cibuildwheel_run(
|
actual_wheels = utils.cibuildwheel_run(
|
||||||
project_dir,
|
project_dir,
|
||||||
add_env={
|
add_env={
|
||||||
# write python version information to a temporary file, this is
|
# write python version information to a temporary file, this is
|
||||||
# checked in setup.py
|
# checked in setup.py
|
||||||
"CIBW_BEFORE_ALL": before_all_command,
|
"CIBW_BEFORE_ALL": before_all_command,
|
||||||
"CIBW_BEFORE_ALL_LINUX": f'{before_all_command} && python -c "import sys; assert sys.version_info >= (3, 6)"',
|
"CIBW_BEFORE_ALL_LINUX": f'{before_all_command} && python -c "import sys; assert sys.version_info >= (3, 8)"',
|
||||||
"CIBW_ENVIRONMENT": "TEST_VAL='123'",
|
"CIBW_ENVIRONMENT": "TEST_VAL='123'",
|
||||||
},
|
},
|
||||||
|
single_python=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
# also check that we got the right wheels
|
# also check that we got the right wheels
|
||||||
(project_dir / "text_info.txt").unlink()
|
expected_wheels = utils.expected_wheels("spam", "0.1.0", single_python=True)
|
||||||
expected_wheels = utils.expected_wheels("spam", "0.1.0")
|
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
|
|
||||||
@@ -72,7 +84,8 @@ def test_cwd(tmp_path):
|
|||||||
"CIBW_BEFORE_ALL": f'''python -c "import os; assert os.getcwd() == {str(project_dir)!r}"''',
|
"CIBW_BEFORE_ALL": f'''python -c "import os; assert os.getcwd() == {str(project_dir)!r}"''',
|
||||||
"CIBW_BEFORE_ALL_LINUX": '''python -c "import os; assert os.getcwd() == '/project'"''',
|
"CIBW_BEFORE_ALL_LINUX": '''python -c "import os; assert os.getcwd() == '/project'"''',
|
||||||
},
|
},
|
||||||
|
single_python=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
expected_wheels = utils.expected_wheels("spam", "0.1.0")
|
expected_wheels = utils.expected_wheels("spam", "0.1.0", single_python=True)
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|||||||
+19
-20
@@ -7,33 +7,32 @@ import pytest
|
|||||||
|
|
||||||
from . import test_projects, utils
|
from . import test_projects, utils
|
||||||
|
|
||||||
|
# pyodide does not support building without isolation, need to check the base_prefix
|
||||||
|
SYS_PREFIX = f"sys.{'base_' if utils.platform == 'pyodide' else ''}prefix"
|
||||||
|
|
||||||
|
|
||||||
project_with_before_build_asserts = test_projects.new_c_project(
|
project_with_before_build_asserts = test_projects.new_c_project(
|
||||||
setup_py_add=textwrap.dedent(
|
setup_py_add=textwrap.dedent(
|
||||||
r"""
|
rf"""
|
||||||
import os
|
import os
|
||||||
|
|
||||||
# assert that the Python version as written to pythonversion_bb.txt in the CIBW_BEFORE_BUILD step
|
# assert that the Python version as written to pythonversion_bb.txt in the CIBW_BEFORE_BUILD step
|
||||||
# is the same one as is currently running.
|
# is the same one as is currently running.
|
||||||
version_file = 'c:\\pythonversion_bb.txt' if sys.platform == 'win32' else '/tmp/pythonversion_bb.txt'
|
with open('pythonversion_bb.txt') as f:
|
||||||
with open(version_file) as f:
|
|
||||||
stored_version = f.read()
|
stored_version = f.read()
|
||||||
print('stored_version', stored_version)
|
print('stored_version', stored_version)
|
||||||
print('sys.version', sys.version)
|
print('sys.version', sys.version)
|
||||||
assert stored_version == sys.version
|
assert stored_version == sys.version
|
||||||
|
|
||||||
# check that the executable also was written
|
# check that the prefix also was written
|
||||||
executable_file = 'c:\\pythonexecutable_bb.txt' if sys.platform == 'win32' else '/tmp/pythonexecutable_bb.txt'
|
with open('pythonprefix_bb.txt') as f:
|
||||||
with open(executable_file) as f:
|
stored_prefix = f.read()
|
||||||
stored_executable = f.read()
|
print('stored_prefix', stored_prefix)
|
||||||
print('stored_executable', stored_executable)
|
print('{SYS_PREFIX}', {SYS_PREFIX})
|
||||||
print('sys.executable', sys.executable)
|
# Works around path-comparison bugs caused by short-paths on Windows e.g.
|
||||||
|
# vssadm~1 instead of vssadministrator
|
||||||
|
|
||||||
# windows/mac are case insensitive
|
assert os.path.samefile(stored_prefix, {SYS_PREFIX})
|
||||||
stored_path = os.path.realpath(stored_executable).lower()
|
|
||||||
current_path = os.path.realpath(sys.executable).lower()
|
|
||||||
|
|
||||||
# TODO: This is not valid in an virtual environment
|
|
||||||
assert stored_path == current_path, '{0} != {1}'.format(stored_path, current_path)
|
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -44,8 +43,8 @@ def test(tmp_path):
|
|||||||
project_with_before_build_asserts.generate(project_dir)
|
project_with_before_build_asserts.generate(project_dir)
|
||||||
|
|
||||||
before_build = (
|
before_build = (
|
||||||
"""python -c "import sys; open('{output_dir}pythonversion_bb.txt', 'w').write(sys.version)" && """
|
"""python -c "import sys; open('{project}/pythonversion_bb.txt', 'w').write(sys.version)" && """
|
||||||
'''python -c "import sys; open('{output_dir}pythonexecutable_bb.txt', 'w').write(sys.executable)"'''
|
f'''python -c "import sys; open('{{project}}/pythonprefix_bb.txt', 'w').write({SYS_PREFIX})"'''
|
||||||
)
|
)
|
||||||
|
|
||||||
# build the wheels
|
# build the wheels
|
||||||
@@ -54,8 +53,7 @@ def test(tmp_path):
|
|||||||
add_env={
|
add_env={
|
||||||
# write python version information to a temporary file, this is
|
# write python version information to a temporary file, this is
|
||||||
# checked in setup.py
|
# checked in setup.py
|
||||||
"CIBW_BEFORE_BUILD": before_build.format(output_dir="/tmp/"),
|
"CIBW_BEFORE_BUILD": before_build,
|
||||||
"CIBW_BEFORE_BUILD_WINDOWS": before_build.format(output_dir=r"c:\\"),
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -88,7 +86,8 @@ def test_cwd(tmp_path):
|
|||||||
"CIBW_BEFORE_BUILD": f'''python -c "import os; assert os.getcwd() == {str(project_dir)!r}"''',
|
"CIBW_BEFORE_BUILD": f'''python -c "import os; assert os.getcwd() == {str(project_dir)!r}"''',
|
||||||
"CIBW_BEFORE_BUILD_LINUX": '''python -c "import os; assert os.getcwd() == '/project'"''',
|
"CIBW_BEFORE_BUILD_LINUX": '''python -c "import os; assert os.getcwd() == '/project'"''',
|
||||||
},
|
},
|
||||||
|
single_python=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
expected_wheels = utils.expected_wheels("spam", "0.1.0")
|
expected_wheels = utils.expected_wheels("spam", "0.1.0", single_python=True)
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|||||||
+22
-10
@@ -6,32 +6,31 @@ before_test_project = test_projects.new_c_project()
|
|||||||
before_test_project.files["test/spam_test.py"] = r"""
|
before_test_project.files["test/spam_test.py"] = r"""
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
from pathlib import Path
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
|
|
||||||
|
PROJECT_DIR = Path(__file__).joinpath("..", "..").resolve()
|
||||||
|
|
||||||
|
|
||||||
class TestBeforeTest(TestCase):
|
class TestBeforeTest(TestCase):
|
||||||
def test_version(self):
|
def test_version(self):
|
||||||
# assert that the Python version as written to pythonversion_bt.txt in the CIBW_BEFORE_TEST step
|
# assert that the Python version as written to pythonversion_bt.txt in the CIBW_BEFORE_TEST step
|
||||||
# is the same one as is currently running.
|
# is the same one as is currently running.
|
||||||
# because of use symlinks in MacOS run this test is also need
|
# because of use symlinks in MacOS run this test is also need
|
||||||
version_file = 'c:\\pythonversion_bt.txt' if sys.platform == 'win32' else '/tmp/pythonversion_bt.txt'
|
stored_version = PROJECT_DIR.joinpath('pythonversion_bt.txt').read_text()
|
||||||
with open(version_file) as f:
|
|
||||||
stored_version = f.read()
|
|
||||||
print('stored_version', stored_version)
|
print('stored_version', stored_version)
|
||||||
print('sys.version', sys.version)
|
print('sys.version', sys.version)
|
||||||
assert stored_version == sys.version
|
assert stored_version == sys.version
|
||||||
|
|
||||||
def test_prefix(self):
|
def test_prefix(self):
|
||||||
# check that the prefix also was written
|
# check that the prefix also was written
|
||||||
prefix_file = 'c:\\pythonprefix_bt.txt' if sys.platform == 'win32' else '/tmp/pythonprefix_bt.txt'
|
stored_prefix = PROJECT_DIR.joinpath('pythonprefix_bt.txt').read_text()
|
||||||
with open(prefix_file) as f:
|
|
||||||
stored_prefix = f.read()
|
|
||||||
print('stored_prefix', stored_prefix)
|
print('stored_prefix', stored_prefix)
|
||||||
print('sys.prefix', sys.prefix)
|
print('sys.prefix', sys.prefix)
|
||||||
# Works around path-comparison bugs caused by short-paths on Windows e.g.
|
# Works around path-comparison bugs caused by short-paths on Windows e.g.
|
||||||
# vssadm~1 instead of vssadministrator
|
# vssadm~1 instead of vssadministrator
|
||||||
|
|
||||||
assert os.stat(stored_prefix) == os.stat(sys.prefix)
|
assert os.path.samefile(stored_prefix, sys.prefix)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
@@ -41,18 +40,31 @@ def test(tmp_path):
|
|||||||
test_project_dir = project_dir / "dependency"
|
test_project_dir = project_dir / "dependency"
|
||||||
test_projects.new_c_project().generate(test_project_dir)
|
test_projects.new_c_project().generate(test_project_dir)
|
||||||
|
|
||||||
|
before_test_steps = [
|
||||||
|
'''python -c "import os, sys; open('{project}/pythonversion_bt.txt', 'w').write(sys.version)"''',
|
||||||
|
'''python -c "import os, sys; open('{project}/pythonprefix_bt.txt', 'w').write(sys.prefix)"''',
|
||||||
|
]
|
||||||
|
|
||||||
|
if utils.platform == "pyodide":
|
||||||
|
before_test_steps.extend(
|
||||||
|
["pyodide build {project}/dependency", "pip install --find-links dist/ spam"]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
before_test_steps.append("python -m pip install {project}/dependency")
|
||||||
|
|
||||||
|
before_test = " && ".join(before_test_steps)
|
||||||
|
|
||||||
# build the wheels
|
# build the wheels
|
||||||
actual_wheels = utils.cibuildwheel_run(
|
actual_wheels = utils.cibuildwheel_run(
|
||||||
project_dir,
|
project_dir,
|
||||||
add_env={
|
add_env={
|
||||||
# write python version information to a temporary file, this is
|
# write python version information to a temporary file, this is
|
||||||
# checked in setup.py
|
# checked in setup.py
|
||||||
"CIBW_BEFORE_TEST": """python -c "import sys; open('/tmp/pythonversion_bt.txt', 'w').write(sys.version)" && python -c "import sys; open('/tmp/pythonprefix_bt.txt', 'w').write(sys.prefix)" && python -m pip install {project}/dependency""",
|
"CIBW_BEFORE_TEST": before_test,
|
||||||
"CIBW_BEFORE_TEST_WINDOWS": """python -c "import sys; open('c:\\pythonversion_bt.txt', 'w').write(sys.version)" && python -c "import sys; open('c:\\pythonprefix_bt.txt', 'w').write(sys.prefix)" && python -m pip install {project}/dependency""",
|
|
||||||
"CIBW_TEST_REQUIRES": "pytest",
|
"CIBW_TEST_REQUIRES": "pytest",
|
||||||
# the 'false ||' bit is to ensure this command runs in a shell on
|
# the 'false ||' bit is to ensure this command runs in a shell on
|
||||||
# mac/linux.
|
# mac/linux.
|
||||||
"CIBW_TEST_COMMAND": "false || pytest {project}/test",
|
"CIBW_TEST_COMMAND": f"false || {utils.invoke_pytest()} {{project}}/test",
|
||||||
"CIBW_TEST_COMMAND_WINDOWS": "pytest {project}/test",
|
"CIBW_TEST_COMMAND_WINDOWS": "pytest {project}/test",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,21 +6,24 @@ from . import utils
|
|||||||
from .test_projects.c import new_c_project
|
from .test_projects.c import new_c_project
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("frontend_name", ["pip", "build"])
|
@pytest.mark.parametrize(
|
||||||
|
"frontend_name",
|
||||||
|
[
|
||||||
|
pytest.param("pip", marks=utils.skip_if_pyodide("No pip for pyodide")),
|
||||||
|
"build",
|
||||||
|
],
|
||||||
|
)
|
||||||
def test_build_frontend_args(tmp_path, capfd, frontend_name):
|
def test_build_frontend_args(tmp_path, capfd, frontend_name):
|
||||||
project = new_c_project()
|
project = new_c_project()
|
||||||
project_dir = tmp_path / "project"
|
project_dir = tmp_path / "project"
|
||||||
project.generate(project_dir)
|
project.generate(project_dir)
|
||||||
|
|
||||||
# the build will fail because the frontend is called with '-h' - it prints the help message
|
# the build will fail because the frontend is called with '-h' - it prints the help message
|
||||||
|
add_env = {"CIBW_BUILD_FRONTEND": f"{frontend_name}; args: -h"}
|
||||||
|
if utils.platform == "pyodide":
|
||||||
|
add_env["TERM"] = "dumb" # disable color / style
|
||||||
with pytest.raises(subprocess.CalledProcessError):
|
with pytest.raises(subprocess.CalledProcessError):
|
||||||
utils.cibuildwheel_run(
|
utils.cibuildwheel_run(project_dir, add_env=add_env, single_python=True)
|
||||||
project_dir,
|
|
||||||
add_env={
|
|
||||||
"CIBW_BUILD": "cp311-*",
|
|
||||||
"CIBW_BUILD_FRONTEND": f"{frontend_name}; args: -h",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
captured = capfd.readouterr()
|
captured = capfd.readouterr()
|
||||||
print(captured.out)
|
print(captured.out)
|
||||||
@@ -29,6 +32,8 @@ def test_build_frontend_args(tmp_path, capfd, frontend_name):
|
|||||||
if frontend_name == "pip":
|
if frontend_name == "pip":
|
||||||
assert "Usage:" in captured.out
|
assert "Usage:" in captured.out
|
||||||
assert "Wheel Options:" in captured.out
|
assert "Wheel Options:" in captured.out
|
||||||
|
elif utils.platform == "pyodide":
|
||||||
|
assert "Usage: pyodide build" in captured.out
|
||||||
else:
|
else:
|
||||||
assert "usage:" in captured.out
|
assert "usage:" in captured.out
|
||||||
assert "A simple, correct Python build frontend." in captured.out
|
assert "A simple, correct Python build frontend." in captured.out
|
||||||
|
|||||||
+11
-9
@@ -6,12 +6,12 @@ from . import test_projects, utils
|
|||||||
|
|
||||||
project_with_skip_asserts = test_projects.new_c_project(
|
project_with_skip_asserts = test_projects.new_c_project(
|
||||||
setup_py_add=textwrap.dedent(
|
setup_py_add=textwrap.dedent(
|
||||||
r"""
|
rf"""
|
||||||
# explode if run on PyPyor Python 3.7 (these should be skipped)
|
|
||||||
if sys.implementation.name != "cpython":
|
if sys.implementation.name != "cpython":
|
||||||
raise Exception("Only CPython shall be built")
|
raise Exception("Only CPython shall be built")
|
||||||
if sys.version_info[0:2] == (3, 7):
|
expected_version = {"({}, {})".format(*utils.SINGLE_PYTHON_VERSION)}
|
||||||
raise Exception("CPython 3.7 should be skipped")
|
if sys.version_info[0:2] != expected_version:
|
||||||
|
raise Exception("CPython {{}}.{{}} should be skipped".format(*sys.version_info[0:2]))
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -21,17 +21,19 @@ def test(tmp_path):
|
|||||||
project_dir = tmp_path / "project"
|
project_dir = tmp_path / "project"
|
||||||
project_with_skip_asserts.generate(project_dir)
|
project_with_skip_asserts.generate(project_dir)
|
||||||
|
|
||||||
|
skip = " ".join(
|
||||||
|
f"cp3{minor}-*" for minor in range(6, 30) if (3, minor) != utils.SINGLE_PYTHON_VERSION
|
||||||
|
)
|
||||||
|
|
||||||
# build the wheels
|
# build the wheels
|
||||||
actual_wheels = utils.cibuildwheel_run(
|
actual_wheels = utils.cibuildwheel_run(
|
||||||
project_dir,
|
project_dir,
|
||||||
add_env={
|
add_env={
|
||||||
"CIBW_BUILD": "cp3*-*",
|
"CIBW_BUILD": "cp3*-*",
|
||||||
"CIBW_SKIP": "cp37-*",
|
"CIBW_SKIP": f"*t-* {skip}",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
# check that we got the right wheels. There should be no PyPy or 3.7.
|
# check that we got the right wheels. There should be a single version of CPython.
|
||||||
expected_wheels = [
|
expected_wheels = utils.expected_wheels("spam", "0.1.0", single_python=True)
|
||||||
w for w in utils.expected_wheels("spam", "0.1.0") if ("-cp3" in w) and ("-cp37" not in w)
|
|
||||||
]
|
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|||||||
@@ -21,17 +21,16 @@ def test_podman(tmp_path, capfd, request):
|
|||||||
actual_wheels = utils.cibuildwheel_run(
|
actual_wheels = utils.cibuildwheel_run(
|
||||||
project_dir,
|
project_dir,
|
||||||
add_env={
|
add_env={
|
||||||
"CIBW_BUILD": "cp310-*{manylinux,musllinux}_x86_64",
|
"CIBW_ARCHS": "x86_64",
|
||||||
"CIBW_BEFORE_ALL": "echo 'test log statement from before-all'",
|
"CIBW_BEFORE_ALL": "echo 'test log statement from before-all'",
|
||||||
"CIBW_CONTAINER_ENGINE": "podman",
|
"CIBW_CONTAINER_ENGINE": "podman",
|
||||||
},
|
},
|
||||||
|
single_python=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
# check that the expected wheels are produced
|
# check that the expected wheels are produced
|
||||||
expected_wheels = [
|
expected_wheels = [
|
||||||
w
|
w for w in utils.expected_wheels("spam", "0.1.0", single_python=True) if "x86_64" in w
|
||||||
for w in utils.expected_wheels("spam", "0.1.0")
|
|
||||||
if ("-cp310-" in w) and ("x86_64" in w) and ("manylinux" in w or "musllinux" in w)
|
|
||||||
]
|
]
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
@@ -51,15 +50,16 @@ def test_create_args(tmp_path, capfd):
|
|||||||
actual_wheels = utils.cibuildwheel_run(
|
actual_wheels = utils.cibuildwheel_run(
|
||||||
project_dir,
|
project_dir,
|
||||||
add_env={
|
add_env={
|
||||||
"CIBW_BUILD": "cp310-manylinux_*",
|
"CIBW_SKIP": "*-musllinux_*",
|
||||||
"CIBW_BEFORE_ALL": "echo TEST_CREATE_ARGS is set to $TEST_CREATE_ARGS",
|
"CIBW_BEFORE_ALL": "echo TEST_CREATE_ARGS is set to $TEST_CREATE_ARGS",
|
||||||
"CIBW_CONTAINER_ENGINE": "docker; create_args: --env=TEST_CREATE_ARGS=itworks",
|
"CIBW_CONTAINER_ENGINE": "docker; create_args: --env=TEST_CREATE_ARGS=itworks",
|
||||||
},
|
},
|
||||||
|
single_python=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
expected_wheels = [
|
expected_wheels = utils.expected_wheels(
|
||||||
w for w in utils.expected_wheels("spam", "0.1.0") if ("cp310-manylinux" in w)
|
"spam", "0.1.0", musllinux_versions=[], single_python=True
|
||||||
]
|
)
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
captured = capfd.readouterr()
|
captured = capfd.readouterr()
|
||||||
|
|||||||
+27
-41
@@ -56,76 +56,62 @@ PyMODINIT_FUNC PyInit_spam(void)
|
|||||||
cpp_test_project.files["setup.py"] = jinja2.Template(setup_py_template)
|
cpp_test_project.files["setup.py"] = jinja2.Template(setup_py_template)
|
||||||
cpp_test_project.files["spam.cpp"] = jinja2.Template(spam_cpp_template)
|
cpp_test_project.files["spam.cpp"] = jinja2.Template(spam_cpp_template)
|
||||||
|
|
||||||
cpp11_project = cpp_test_project.copy()
|
|
||||||
cpp11_project.template_context["extra_compile_args"] = (
|
|
||||||
["/std:c++11"] if utils.platform == "windows" else ["-std=c++11"]
|
|
||||||
)
|
|
||||||
cpp11_project.template_context["spam_cpp_top_level_add"] = "#include <array>"
|
|
||||||
|
|
||||||
|
|
||||||
def test_cpp11(tmp_path):
|
def test_cpp11(tmp_path):
|
||||||
# This test checks that the C++11 standard is supported
|
# This test checks that the C++11 standard is supported
|
||||||
project_dir = tmp_path / "project"
|
project_dir = tmp_path / "project"
|
||||||
|
cpp11_project = cpp_test_project.copy()
|
||||||
|
cpp11_project.template_context["extra_compile_args"] = (
|
||||||
|
["/std:c++11"] if utils.platform == "windows" else ["-std=c++11"]
|
||||||
|
)
|
||||||
|
cpp11_project.template_context["spam_cpp_top_level_add"] = "#include <array>"
|
||||||
cpp11_project.generate(project_dir)
|
cpp11_project.generate(project_dir)
|
||||||
|
|
||||||
actual_wheels = utils.cibuildwheel_run(project_dir)
|
actual_wheels = utils.cibuildwheel_run(project_dir, single_python=True)
|
||||||
expected_wheels = list(utils.expected_wheels("spam", "0.1.0"))
|
expected_wheels = utils.expected_wheels("spam", "0.1.0", single_python=True)
|
||||||
|
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
|
|
||||||
cpp14_project = cpp_test_project.copy()
|
|
||||||
cpp14_project.template_context["extra_compile_args"] = (
|
|
||||||
["/std:c++14"] if utils.platform == "windows" else ["-std=c++14"]
|
|
||||||
)
|
|
||||||
cpp14_project.template_context["spam_cpp_top_level_add"] = "int a = 100'000;"
|
|
||||||
|
|
||||||
|
|
||||||
def test_cpp14(tmp_path):
|
def test_cpp14(tmp_path):
|
||||||
# This test checks that the C++14 standard is supported
|
# This test checks that the C++14 standard is supported
|
||||||
project_dir = tmp_path / "project"
|
project_dir = tmp_path / "project"
|
||||||
|
cpp14_project = cpp_test_project.copy()
|
||||||
|
cpp14_project.template_context["extra_compile_args"] = (
|
||||||
|
["/std:c++14"] if utils.platform == "windows" else ["-std=c++14"]
|
||||||
|
)
|
||||||
|
cpp14_project.template_context["spam_cpp_top_level_add"] = "int a = 100'000;"
|
||||||
cpp14_project.generate(project_dir)
|
cpp14_project.generate(project_dir)
|
||||||
|
|
||||||
actual_wheels = utils.cibuildwheel_run(project_dir)
|
actual_wheels = utils.cibuildwheel_run(project_dir, single_python=True)
|
||||||
expected_wheels = list(utils.expected_wheels("spam", "0.1.0"))
|
expected_wheels = utils.expected_wheels("spam", "0.1.0", single_python=True)
|
||||||
|
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
|
|
||||||
cpp17_project = cpp_test_project.copy()
|
|
||||||
|
|
||||||
cpp17_project.template_context["extra_compile_args"] = [
|
|
||||||
"/std:c++17" if utils.platform == "windows" else "-std=c++17"
|
|
||||||
]
|
|
||||||
cpp17_project.template_context["spam_cpp_top_level_add"] = r"""
|
|
||||||
#include <utility>
|
|
||||||
auto a = std::pair(5.0, false);
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def test_cpp17(tmp_path):
|
def test_cpp17(tmp_path):
|
||||||
# This test checks that the C++17 standard is supported
|
# This test checks that the C++17 standard is supported
|
||||||
project_dir = tmp_path / "project"
|
project_dir = tmp_path / "project"
|
||||||
|
cpp17_project = cpp_test_project.copy()
|
||||||
|
cpp17_project.template_context["extra_compile_args"] = [
|
||||||
|
"/std:c++17" if utils.platform == "windows" else "-std=c++17"
|
||||||
|
]
|
||||||
|
cpp17_project.template_context["spam_cpp_top_level_add"] = r"""
|
||||||
|
#include <utility>
|
||||||
|
auto a = std::pair(5.0, false);
|
||||||
|
"""
|
||||||
cpp17_project.generate(project_dir)
|
cpp17_project.generate(project_dir)
|
||||||
|
|
||||||
if os.environ.get("APPVEYOR_BUILD_WORKER_IMAGE", "") == "Visual Studio 2015":
|
if os.environ.get("APPVEYOR_BUILD_WORKER_IMAGE", "") == "Visual Studio 2015":
|
||||||
pytest.skip("Visual Studio 2015 does not support C++17")
|
pytest.skip("Visual Studio 2015 does not support C++17")
|
||||||
|
|
||||||
# Pypy's distutils sets the default compiler to 'msvc9compiler', which
|
add_env = {}
|
||||||
# is too old to support cpp17.
|
|
||||||
add_env = {"CIBW_SKIP": "pp*"}
|
|
||||||
|
|
||||||
if utils.platform == "macos":
|
if utils.platform == "macos":
|
||||||
add_env["MACOSX_DEPLOYMENT_TARGET"] = "10.13"
|
add_env["MACOSX_DEPLOYMENT_TARGET"] = "10.13"
|
||||||
|
|
||||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
|
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env, single_python=True)
|
||||||
expected_wheels = [
|
expected_wheels = utils.expected_wheels(
|
||||||
w
|
"spam", "0.1.0", macosx_deployment_target="10.13", single_python=True
|
||||||
for w in utils.expected_wheels("spam", "0.1.0", macosx_deployment_target="10.13")
|
)
|
||||||
if "-pp" not in w
|
|
||||||
]
|
|
||||||
|
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
from test import test_projects
|
from contextlib import nullcontext as does_not_raise
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from test import test_projects
|
||||||
|
|
||||||
from . import utils
|
from . import utils
|
||||||
|
|
||||||
basic_project = test_projects.new_c_project()
|
basic_project = test_projects.new_c_project()
|
||||||
@@ -30,8 +32,14 @@ def test(tmp_path, capfd):
|
|||||||
project_dir = tmp_path / "project"
|
project_dir = tmp_path / "project"
|
||||||
basic_project.generate(project_dir)
|
basic_project.generate(project_dir)
|
||||||
|
|
||||||
with pytest.raises(subprocess.CalledProcessError):
|
num_builds = len(utils.cibuildwheel_get_build_identifiers(project_dir))
|
||||||
utils.cibuildwheel_run(
|
if num_builds > 1:
|
||||||
|
expectation = pytest.raises(subprocess.CalledProcessError)
|
||||||
|
else:
|
||||||
|
expectation = does_not_raise()
|
||||||
|
|
||||||
|
with expectation:
|
||||||
|
result = utils.cibuildwheel_run(
|
||||||
project_dir,
|
project_dir,
|
||||||
add_env={
|
add_env={
|
||||||
"CIBW_REPAIR_WHEEL_COMMAND": "python repair.py {wheel} {dest_dir}",
|
"CIBW_REPAIR_WHEEL_COMMAND": "python repair.py {wheel} {dest_dir}",
|
||||||
@@ -39,4 +47,14 @@ def test(tmp_path, capfd):
|
|||||||
)
|
)
|
||||||
|
|
||||||
captured = capfd.readouterr()
|
captured = capfd.readouterr()
|
||||||
assert "Build failed because a wheel named" in captured.err
|
if num_builds > 1:
|
||||||
|
assert "Build failed because a wheel named" in captured.err
|
||||||
|
else:
|
||||||
|
# We only produced one wheel (currently Pyodide)
|
||||||
|
# check that it has the right name
|
||||||
|
#
|
||||||
|
# As far as I can tell, this is the only full test coverage for
|
||||||
|
# CIBW_REPAIR_WHEEL_COMMAND so this is useful even in the case when no
|
||||||
|
# error is raised
|
||||||
|
assert "spam-0.1.0-py2-none-pyodide" in captured.out
|
||||||
|
assert result[0].startswith("spam-0.1.0-py2-none-")
|
||||||
@@ -53,7 +53,7 @@ def get_versions_from_constraint_file(constraint_file):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("python_version", ["3.6", "3.8", "3.10"])
|
@pytest.mark.parametrize("python_version", ["3.6", "3.8", "3.10"])
|
||||||
def test_pinned_versions(tmp_path, python_version, build_frontend_env):
|
def test_pinned_versions(tmp_path, python_version, build_frontend_env_nouv):
|
||||||
if utils.platform == "linux":
|
if utils.platform == "linux":
|
||||||
pytest.skip("linux doesn't pin individual tool versions, it pins manylinux images instead")
|
pytest.skip("linux doesn't pin individual tool versions, it pins manylinux images instead")
|
||||||
if python_version == "3.6" and utils.platform == "macos" and platform.machine() == "arm64":
|
if python_version == "3.6" and utils.platform == "macos" and platform.machine() == "arm64":
|
||||||
@@ -79,7 +79,7 @@ def test_pinned_versions(tmp_path, python_version, build_frontend_env):
|
|||||||
add_env={
|
add_env={
|
||||||
"CIBW_BUILD": build_pattern,
|
"CIBW_BUILD": build_pattern,
|
||||||
"CIBW_ENVIRONMENT": cibw_environment_option,
|
"CIBW_ENVIRONMENT": cibw_environment_option,
|
||||||
**build_frontend_env,
|
**build_frontend_env_nouv,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@ def test_pinned_versions(tmp_path, python_version, build_frontend_env):
|
|||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
|
|
||||||
def test_dependency_constraints_file(tmp_path, build_frontend_env):
|
def test_dependency_constraints_file(tmp_path, build_frontend_env_nouv):
|
||||||
if utils.platform == "linux":
|
if utils.platform == "linux":
|
||||||
pytest.skip("linux doesn't pin individual tool versions, it pins manylinux images instead")
|
pytest.skip("linux doesn't pin individual tool versions, it pins manylinux images instead")
|
||||||
|
|
||||||
@@ -130,7 +130,7 @@ def test_dependency_constraints_file(tmp_path, build_frontend_env):
|
|||||||
"CIBW_ENVIRONMENT": cibw_environment_option,
|
"CIBW_ENVIRONMENT": cibw_environment_option,
|
||||||
"CIBW_DEPENDENCY_VERSIONS": str(constraints_file),
|
"CIBW_DEPENDENCY_VERSIONS": str(constraints_file),
|
||||||
"CIBW_SKIP": "cp36-*",
|
"CIBW_SKIP": "cp36-*",
|
||||||
**build_frontend_env,
|
**build_frontend_env_nouv,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
import textwrap
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from cibuildwheel.util import CIBW_CACHE_PATH
|
||||||
|
|
||||||
|
from . import test_projects, utils
|
||||||
|
|
||||||
|
basic_project = test_projects.new_c_project()
|
||||||
|
basic_project.files["check_node.py"] = r"""
|
||||||
|
import sys
|
||||||
|
import shutil
|
||||||
|
from pathlib import Path
|
||||||
|
from pyodide.code import run_js
|
||||||
|
|
||||||
|
|
||||||
|
def check_node():
|
||||||
|
# cibuildwheel adds a pinned node version to the PATH
|
||||||
|
# check it's in the PATH then, check it's the one that runs pyoodide
|
||||||
|
cibw_cache_path = Path(sys.argv[1]).resolve(strict=True)
|
||||||
|
# find the node executable in PATH
|
||||||
|
node = shutil.which("node")
|
||||||
|
assert node is not None, "node is None"
|
||||||
|
node_path = Path(node).resolve(strict=True)
|
||||||
|
# it shall be in cibuildwheel cache
|
||||||
|
assert cibw_cache_path in node_path.parents, f"{cibw_cache_path} not a parent of {node_path}"
|
||||||
|
# find the path to the node executable that runs pyodide
|
||||||
|
node_js = run_js("globalThis.process.execPath")
|
||||||
|
assert node_js is not None, "node_js is None"
|
||||||
|
node_js_path = Path(node_js).resolve(strict=True)
|
||||||
|
# it shall be the one pinned by cibuildwheel
|
||||||
|
assert node_js_path == node_path, f"{node_js_path} != {node_path}"
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
check_node()
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("use_pyproject_toml", [True, False])
|
||||||
|
def test_pyodide_build(tmp_path, use_pyproject_toml):
|
||||||
|
if sys.platform == "win32":
|
||||||
|
pytest.skip("emsdk doesn't work correctly on Windows")
|
||||||
|
|
||||||
|
if not shutil.which("python3.12"):
|
||||||
|
pytest.skip("Python 3.12 not installed")
|
||||||
|
|
||||||
|
if use_pyproject_toml:
|
||||||
|
basic_project.files["pyproject.toml"] = textwrap.dedent(
|
||||||
|
"""
|
||||||
|
[build-system]
|
||||||
|
requires = ["setuptools"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
project_dir = tmp_path / "project"
|
||||||
|
basic_project.generate(project_dir)
|
||||||
|
|
||||||
|
# check for node in 1 case only to reduce CI load
|
||||||
|
add_env = {}
|
||||||
|
if use_pyproject_toml:
|
||||||
|
add_env["CIBW_TEST_COMMAND"] = f"python {{project}}/check_node.py {CIBW_CACHE_PATH}"
|
||||||
|
|
||||||
|
# build the wheels
|
||||||
|
actual_wheels = utils.cibuildwheel_run(
|
||||||
|
project_dir,
|
||||||
|
add_args=["--platform", "pyodide"],
|
||||||
|
add_env=add_env,
|
||||||
|
)
|
||||||
|
|
||||||
|
# check that the expected wheels are produced
|
||||||
|
expected_wheels = [
|
||||||
|
"spam-0.1.0-cp312-cp312-pyodide_2024_0_wasm32.whl",
|
||||||
|
]
|
||||||
|
|
||||||
|
print("actual_wheels", actual_wheels)
|
||||||
|
print("expected_wheels", expected_wheels)
|
||||||
|
|
||||||
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
+10
-6
@@ -1,5 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import itertools
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@@ -18,9 +19,13 @@ def test_spam():
|
|||||||
|
|
||||||
|
|
||||||
def test(tmp_path, request):
|
def test(tmp_path, request):
|
||||||
if not request.config.getoption("--run-emulation"):
|
archs = request.config.getoption("--run-emulation")
|
||||||
|
if archs is None:
|
||||||
pytest.skip("needs --run-emulation option to run")
|
pytest.skip("needs --run-emulation option to run")
|
||||||
|
|
||||||
|
if archs == "all":
|
||||||
|
archs = " ".join(utils.EMULATED_ARCHS)
|
||||||
|
|
||||||
project_dir = tmp_path / "project"
|
project_dir = tmp_path / "project"
|
||||||
project_with_a_test.generate(project_dir)
|
project_with_a_test.generate(project_dir)
|
||||||
|
|
||||||
@@ -30,15 +35,14 @@ def test(tmp_path, request):
|
|||||||
add_env={
|
add_env={
|
||||||
"CIBW_TEST_REQUIRES": "pytest",
|
"CIBW_TEST_REQUIRES": "pytest",
|
||||||
"CIBW_TEST_COMMAND": "pytest {project}/test",
|
"CIBW_TEST_COMMAND": "pytest {project}/test",
|
||||||
"CIBW_ARCHS": "aarch64 ppc64le s390x",
|
"CIBW_ARCHS": archs,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
# also check that we got the right wheels
|
# also check that we got the right wheels
|
||||||
expected_wheels = (
|
expected_wheels = itertools.chain.from_iterable(
|
||||||
utils.expected_wheels("spam", "0.1.0", machine_arch="aarch64")
|
utils.expected_wheels("spam", "0.1.0", machine_arch=arch, single_arch=True)
|
||||||
+ utils.expected_wheels("spam", "0.1.0", machine_arch="ppc64le")
|
for arch in archs.split(" ")
|
||||||
+ utils.expected_wheels("spam", "0.1.0", machine_arch="s390x")
|
|
||||||
)
|
)
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
|
|||||||
@@ -49,10 +49,11 @@ def test(tmp_path):
|
|||||||
"CIBW_ENVIRONMENT": """CIBW_TEST_VAR="a b c" CIBW_TEST_VAR_2=1 CIBW_TEST_VAR_3="$(echo 'test string 3')" PATH=$PATH:/opt/cibw_test_path""",
|
"CIBW_ENVIRONMENT": """CIBW_TEST_VAR="a b c" CIBW_TEST_VAR_2=1 CIBW_TEST_VAR_3="$(echo 'test string 3')" PATH=$PATH:/opt/cibw_test_path""",
|
||||||
"CIBW_ENVIRONMENT_WINDOWS": f'''CIBW_TEST_VAR="a b c" CIBW_TEST_VAR_2=1 CIBW_TEST_VAR_3="$({python_echo} 'test string 3')" PATH="$PATH;/opt/cibw_test_path"''',
|
"CIBW_ENVIRONMENT_WINDOWS": f'''CIBW_TEST_VAR="a b c" CIBW_TEST_VAR_2=1 CIBW_TEST_VAR_3="$({python_echo} 'test string 3')" PATH="$PATH;/opt/cibw_test_path"''',
|
||||||
},
|
},
|
||||||
|
single_python=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
# also check that we got the right wheels built
|
# also check that we got the right wheels built
|
||||||
expected_wheels = utils.expected_wheels("spam", "0.1.0")
|
expected_wheels = utils.expected_wheels("spam", "0.1.0", single_python=True)
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
|
|
||||||
@@ -92,10 +93,18 @@ def test_overridden_path(tmp_path, capfd):
|
|||||||
|
|
||||||
assert len(os.listdir(output_dir)) == 0
|
assert len(os.listdir(output_dir)) == 0
|
||||||
captured = capfd.readouterr()
|
captured = capfd.readouterr()
|
||||||
assert "python available on PATH doesn't match our installed instance" in captured.err
|
assert "python available on PATH doesn't match our installed instance" in captured.err.replace(
|
||||||
|
"venv", "installed"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("build_frontend", ["pip", "build"])
|
@pytest.mark.parametrize(
|
||||||
|
"build_frontend",
|
||||||
|
[
|
||||||
|
pytest.param("pip", marks=utils.skip_if_pyodide("No pip for pyodide")),
|
||||||
|
"build",
|
||||||
|
],
|
||||||
|
)
|
||||||
def test_overridden_pip_constraint(tmp_path, build_frontend):
|
def test_overridden_pip_constraint(tmp_path, build_frontend):
|
||||||
"""
|
"""
|
||||||
Verify that users can use PIP_CONSTRAINT to specify a specific version of
|
Verify that users can use PIP_CONSTRAINT to specify a specific version of
|
||||||
@@ -108,7 +117,7 @@ def test_overridden_pip_constraint(tmp_path, build_frontend):
|
|||||||
setup_py_add=textwrap.dedent(
|
setup_py_add=textwrap.dedent(
|
||||||
"""
|
"""
|
||||||
import pytz
|
import pytz
|
||||||
assert pytz.__version__ == "2022.4"
|
assert pytz.__version__ == "2022.4", f"{pytz.__version__!r} != '2022.4'"
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -133,12 +142,12 @@ def test_overridden_pip_constraint(tmp_path, build_frontend):
|
|||||||
actual_wheels = utils.cibuildwheel_run(
|
actual_wheels = utils.cibuildwheel_run(
|
||||||
project_dir,
|
project_dir,
|
||||||
add_env={
|
add_env={
|
||||||
"CIBW_BUILD": "cp312-*",
|
|
||||||
"CIBW_BUILD_FRONTEND": build_frontend,
|
"CIBW_BUILD_FRONTEND": build_frontend,
|
||||||
"PIP_CONSTRAINT": str(constraints_file),
|
"PIP_CONSTRAINT": str(constraints_file),
|
||||||
"CIBW_ENVIRONMENT_LINUX": "PIP_CONSTRAINT=./constraints.txt",
|
"CIBW_ENVIRONMENT_LINUX": "PIP_CONSTRAINT=./constraints.txt",
|
||||||
},
|
},
|
||||||
|
single_python=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
expected_wheels = [w for w in utils.expected_wheels("spam", "0.1.0") if "cp312" in w]
|
expected_wheels = utils.expected_wheels("spam", "0.1.0", single_python=True)
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|||||||
+11
-21
@@ -6,6 +6,7 @@ import sys
|
|||||||
import textwrap
|
import textwrap
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from tempfile import TemporaryDirectory
|
from tempfile import TemporaryDirectory
|
||||||
|
|
||||||
from test.test_projects.base import TestProject
|
from test.test_projects.base import TestProject
|
||||||
|
|
||||||
from . import test_projects, utils
|
from . import test_projects, utils
|
||||||
@@ -33,6 +34,8 @@ def cibuildwheel_from_sdist_run(sdist_path, add_env=None, config_file=None):
|
|||||||
if add_env:
|
if add_env:
|
||||||
env.update(add_env)
|
env.update(add_env)
|
||||||
|
|
||||||
|
env["CIBW_BUILD"] = "cp{}{}-*".format(*utils.SINGLE_PYTHON_VERSION)
|
||||||
|
|
||||||
with TemporaryDirectory() as tmp_output_dir:
|
with TemporaryDirectory() as tmp_output_dir:
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
[
|
[
|
||||||
@@ -73,15 +76,11 @@ def test_simple(tmp_path):
|
|||||||
|
|
||||||
# build the wheels from sdist
|
# build the wheels from sdist
|
||||||
actual_wheels = cibuildwheel_from_sdist_run(
|
actual_wheels = cibuildwheel_from_sdist_run(
|
||||||
sdist_path,
|
sdist_path, add_env={"CIBW_BEFORE_BUILD": setup_py_assertion_cmd}
|
||||||
add_env={
|
|
||||||
"CIBW_BEFORE_BUILD": setup_py_assertion_cmd,
|
|
||||||
"CIBW_BUILD": "cp39-*",
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# check that the expected wheels are produced
|
# check that the expected wheels are produced
|
||||||
expected_wheels = [w for w in utils.expected_wheels("spam", "0.1.0") if "cp39" in w]
|
expected_wheels = utils.expected_wheels("spam", "0.1.0", single_python=True)
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
|
|
||||||
@@ -105,14 +104,10 @@ def test_external_config_file_argument(tmp_path, capfd):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# build the wheels from sdist
|
# build the wheels from sdist
|
||||||
actual_wheels = cibuildwheel_from_sdist_run(
|
actual_wheels = cibuildwheel_from_sdist_run(sdist_path, config_file=str(config_file))
|
||||||
sdist_path,
|
|
||||||
add_env={"CIBW_BUILD": "cp39-*"},
|
|
||||||
config_file=str(config_file),
|
|
||||||
)
|
|
||||||
|
|
||||||
# check that the expected wheels are produced
|
# check that the expected wheels are produced
|
||||||
expected_wheels = [w for w in utils.expected_wheels("spam", "0.1.0") if "cp39" in w]
|
expected_wheels = utils.expected_wheels("spam", "0.1.0", single_python=True)
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
# check that before-all was run
|
# check that before-all was run
|
||||||
@@ -136,13 +131,10 @@ def test_config_in_pyproject_toml(tmp_path, capfd):
|
|||||||
sdist_path = make_sdist(project, sdist_dir)
|
sdist_path = make_sdist(project, sdist_dir)
|
||||||
|
|
||||||
# build the wheels from sdist
|
# build the wheels from sdist
|
||||||
actual_wheels = cibuildwheel_from_sdist_run(
|
actual_wheels = cibuildwheel_from_sdist_run(sdist_path)
|
||||||
sdist_path,
|
|
||||||
add_env={"CIBW_BUILD": "cp39-*"},
|
|
||||||
)
|
|
||||||
|
|
||||||
# check that the expected wheels are produced
|
# check that the expected wheels are produced
|
||||||
expected_wheels = [w for w in utils.expected_wheels("spam", "0.1.0") if "cp39" in w]
|
expected_wheels = utils.expected_wheels("spam", "0.1.0", single_python=True)
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
# check that before-build was run
|
# check that before-build was run
|
||||||
@@ -174,13 +166,11 @@ def test_internal_config_file_argument(tmp_path, capfd):
|
|||||||
|
|
||||||
# build the wheels from sdist, referencing the config file inside
|
# build the wheels from sdist, referencing the config file inside
|
||||||
actual_wheels = cibuildwheel_from_sdist_run(
|
actual_wheels = cibuildwheel_from_sdist_run(
|
||||||
sdist_path,
|
sdist_path, config_file="{package}/wheel_build_config.toml"
|
||||||
add_env={"CIBW_BUILD": "cp39-*"},
|
|
||||||
config_file="{package}/wheel_build_config.toml",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# check that the expected wheels are produced
|
# check that the expected wheels are produced
|
||||||
expected_wheels = [w for w in utils.expected_wheels("spam", "0.1.0") if "cp39" in w]
|
expected_wheels = utils.expected_wheels("spam", "0.1.0", single_python=True)
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
# check that before-all was run
|
# check that before-all was run
|
||||||
|
|||||||
+22
-21
@@ -14,7 +14,7 @@ ALL_MACOS_WHEELS = {
|
|||||||
*utils.expected_wheels("spam", "0.1.0", machine_arch="arm64", include_universal2=True),
|
*utils.expected_wheels("spam", "0.1.0", machine_arch="arm64", include_universal2=True),
|
||||||
}
|
}
|
||||||
|
|
||||||
DEPLOYMENT_TARGET_TOO_LOW_WARNING = "[WARNING] MACOSX_DEPLOYMENT_TARGET is set to a lower value"
|
DEPLOYMENT_TARGET_TOO_LOW_WARNING = "Bumping MACOSX_DEPLOYMENT_TARGET"
|
||||||
|
|
||||||
|
|
||||||
def get_xcode_version() -> tuple[int, int]:
|
def get_xcode_version() -> tuple[int, int]:
|
||||||
@@ -42,13 +42,11 @@ def test_cross_compiled_build(tmp_path):
|
|||||||
|
|
||||||
actual_wheels = utils.cibuildwheel_run(
|
actual_wheels = utils.cibuildwheel_run(
|
||||||
project_dir,
|
project_dir,
|
||||||
add_env={
|
add_env={"CIBW_ARCHS": "x86_64, universal2, arm64"},
|
||||||
"CIBW_BUILD": "cp39-*",
|
single_python=True,
|
||||||
"CIBW_ARCHS": "x86_64, universal2, arm64",
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
python_tag = "cp{}{}".format(*utils.SINGLE_PYTHON_VERSION)
|
||||||
expected_wheels = [w for w in ALL_MACOS_WHEELS if "cp39" in w]
|
expected_wheels = [w for w in ALL_MACOS_WHEELS if python_tag in w]
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
|
|
||||||
@@ -65,7 +63,7 @@ def test_cross_compiled_test(tmp_path, capfd, build_universal2):
|
|||||||
actual_wheels = utils.cibuildwheel_run(
|
actual_wheels = utils.cibuildwheel_run(
|
||||||
project_dir,
|
project_dir,
|
||||||
add_env={
|
add_env={
|
||||||
"CIBW_BUILD": "cp39-*" if build_universal2 else "*p39-*",
|
"CIBW_BUILD": "cp310-*" if build_universal2 else "*p310-*",
|
||||||
"CIBW_TEST_COMMAND": '''python -c "import platform; print('running tests on ' + platform.machine())"''',
|
"CIBW_TEST_COMMAND": '''python -c "import platform; print('running tests on ' + platform.machine())"''',
|
||||||
"CIBW_ARCHS": "universal2" if build_universal2 else "x86_64 arm64",
|
"CIBW_ARCHS": "universal2" if build_universal2 else "x86_64 arm64",
|
||||||
"CIBW_BUILD_VERBOSITY": "3",
|
"CIBW_BUILD_VERBOSITY": "3",
|
||||||
@@ -103,11 +101,11 @@ def test_cross_compiled_test(tmp_path, capfd, build_universal2):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if build_universal2:
|
if build_universal2:
|
||||||
expected_wheels = [w for w in ALL_MACOS_WHEELS if "cp39" in w and "universal2" in w]
|
expected_wheels = [w for w in ALL_MACOS_WHEELS if "cp310" in w and "universal2" in w]
|
||||||
else:
|
else:
|
||||||
expected_wheels = [w for w in ALL_MACOS_WHEELS if "p39-" in w and "universal2" not in w]
|
expected_wheels = [w for w in ALL_MACOS_WHEELS if "p310-" in w and "universal2" not in w]
|
||||||
if platform_machine == "x86_64":
|
if platform_machine == "x86_64":
|
||||||
expected_wheels = [w for w in expected_wheels if not ("pp39" in w and "arm64" in w)]
|
expected_wheels = [w for w in expected_wheels if not ("pp310" in w and "arm64" in w)]
|
||||||
|
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
@@ -123,11 +121,11 @@ def test_deployment_target_warning_is_firing(tmp_path, capfd):
|
|||||||
utils.cibuildwheel_run(
|
utils.cibuildwheel_run(
|
||||||
project_dir,
|
project_dir,
|
||||||
add_env={
|
add_env={
|
||||||
"CIBW_BUILD": "cp39-*",
|
|
||||||
"CIBW_ARCHS": "x86_64",
|
"CIBW_ARCHS": "x86_64",
|
||||||
"MACOSX_DEPLOYMENT_TARGET": "10.8",
|
"MACOSX_DEPLOYMENT_TARGET": "10.8",
|
||||||
"CIBW_BUILD_VERBOSITY": "3",
|
"CIBW_BUILD_VERBOSITY": "3",
|
||||||
},
|
},
|
||||||
|
single_python=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
captured = capfd.readouterr()
|
captured = capfd.readouterr()
|
||||||
@@ -149,11 +147,11 @@ def test_universal2_testing_on_x86_64(tmp_path, capfd, skip_arm64_test):
|
|||||||
actual_wheels = utils.cibuildwheel_run(
|
actual_wheels = utils.cibuildwheel_run(
|
||||||
project_dir,
|
project_dir,
|
||||||
add_env={
|
add_env={
|
||||||
"CIBW_BUILD": "cp39-*",
|
|
||||||
"CIBW_TEST_COMMAND": '''python -c "import platform; print('running tests on ' + platform.machine())"''',
|
"CIBW_TEST_COMMAND": '''python -c "import platform; print('running tests on ' + platform.machine())"''',
|
||||||
"CIBW_ARCHS": "universal2",
|
"CIBW_ARCHS": "universal2",
|
||||||
"CIBW_TEST_SKIP": "*_universal2:arm64" if skip_arm64_test else "",
|
"CIBW_TEST_SKIP": "*_universal2:arm64" if skip_arm64_test else "",
|
||||||
},
|
},
|
||||||
|
single_python=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
captured = capfd.readouterr()
|
captured = capfd.readouterr()
|
||||||
@@ -168,12 +166,13 @@ def test_universal2_testing_on_x86_64(tmp_path, capfd, skip_arm64_test):
|
|||||||
else:
|
else:
|
||||||
assert warning_message in captured.err
|
assert warning_message in captured.err
|
||||||
|
|
||||||
expected_wheels = [w for w in ALL_MACOS_WHEELS if "cp39" in w and "universal2" in w]
|
python_tag = "cp{}{}".format(*utils.SINGLE_PYTHON_VERSION)
|
||||||
|
expected_wheels = [w for w in ALL_MACOS_WHEELS if python_tag in w and "universal2" in w]
|
||||||
|
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
|
|
||||||
def test_universal2_testing_on_arm64(tmp_path, capfd):
|
def test_universal2_testing_on_arm64(build_frontend_env, tmp_path, capfd):
|
||||||
# cibuildwheel should test the universal2 wheel on both x86_64 and arm64, when run on arm64
|
# cibuildwheel should test the universal2 wheel on both x86_64 and arm64, when run on arm64
|
||||||
if utils.platform != "macos":
|
if utils.platform != "macos":
|
||||||
pytest.skip("this test is only relevant to macos")
|
pytest.skip("this test is only relevant to macos")
|
||||||
@@ -186,19 +185,21 @@ def test_universal2_testing_on_arm64(tmp_path, capfd):
|
|||||||
actual_wheels = utils.cibuildwheel_run(
|
actual_wheels = utils.cibuildwheel_run(
|
||||||
project_dir,
|
project_dir,
|
||||||
add_env={
|
add_env={
|
||||||
"CIBW_BUILD": "cp39-*",
|
|
||||||
"CIBW_ARCHS": "universal2",
|
"CIBW_ARCHS": "universal2",
|
||||||
# check that a native dependency is correctly installed, once per each testing arch
|
# check that a native dependency is correctly installed, once per each testing arch
|
||||||
"CIBW_TEST_REQUIRES": "numpy",
|
"CIBW_TEST_REQUIRES": "--only-binary :all: pillow>=10.3", # pillow>=10.3 provides wheels for macOS 10.10, not 10.9
|
||||||
"CIBW_TEST_COMMAND": '''python -c "import numpy, platform; print(f'running tests on {platform.machine()} with numpy {numpy.__version__}')"''',
|
"CIBW_TEST_COMMAND": '''python -c "import PIL, platform; print(f'running tests on {platform.machine()} with pillow {PIL.__version__}')"''',
|
||||||
|
**build_frontend_env,
|
||||||
},
|
},
|
||||||
|
single_python=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
captured = capfd.readouterr()
|
captured = capfd.readouterr()
|
||||||
assert "running tests on arm64" in captured.out
|
assert "running tests on arm64 with pillow" in captured.out
|
||||||
assert "running tests on x86_64" in captured.out
|
assert "running tests on x86_64 with pillow" in captured.out
|
||||||
|
|
||||||
expected_wheels = [w for w in ALL_MACOS_WHEELS if "cp39" in w and "universal2" in w]
|
python_tag = "cp{}{}".format(*utils.SINGLE_PYTHON_VERSION)
|
||||||
|
expected_wheels = [w for w in ALL_MACOS_WHEELS if python_tag in w and "universal2" in w]
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ def test(musllinux_image, tmp_path):
|
|||||||
|
|
||||||
# build the wheels
|
# build the wheels
|
||||||
add_env = {
|
add_env = {
|
||||||
"CIBW_BUILD": "*-musllinux*",
|
"CIBW_SKIP": "*-manylinux*",
|
||||||
"CIBW_MUSLLINUX_X86_64_IMAGE": musllinux_image,
|
"CIBW_MUSLLINUX_X86_64_IMAGE": musllinux_image,
|
||||||
"CIBW_MUSLLINUX_I686_IMAGE": musllinux_image,
|
"CIBW_MUSLLINUX_I686_IMAGE": musllinux_image,
|
||||||
"CIBW_MUSLLINUX_AARCH64_IMAGE": musllinux_image,
|
"CIBW_MUSLLINUX_AARCH64_IMAGE": musllinux_image,
|
||||||
@@ -46,11 +46,12 @@ def test(musllinux_image, tmp_path):
|
|||||||
"CIBW_MUSLLINUX_S390X_IMAGE": musllinux_image,
|
"CIBW_MUSLLINUX_S390X_IMAGE": musllinux_image,
|
||||||
}
|
}
|
||||||
|
|
||||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
|
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env, single_python=True)
|
||||||
expected_wheels = utils.expected_wheels(
|
expected_wheels = utils.expected_wheels(
|
||||||
"spam",
|
"spam",
|
||||||
"0.1.0",
|
"0.1.0",
|
||||||
manylinux_versions=[],
|
manylinux_versions=[],
|
||||||
musllinux_versions=[musllinux_image],
|
musllinux_versions=[musllinux_image],
|
||||||
|
single_python=True,
|
||||||
)
|
)
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|||||||
@@ -42,15 +42,20 @@ PyMODINIT_FUNC PyInit_spam(void)
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
SETUP_PY_TEMPLATE = r"""
|
SETUP_PY_TEMPLATE = r"""
|
||||||
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from setuptools import setup, Extension
|
from setuptools import setup, Extension
|
||||||
|
|
||||||
{{ setup_py_add }}
|
{{ setup_py_add }}
|
||||||
|
|
||||||
libraries = []
|
libraries = []
|
||||||
if sys.platform.startswith('linux'):
|
# Emscripten fails if you pass -lc...
|
||||||
|
# See: https://github.com/emscripten-core/emscripten/issues/16680
|
||||||
|
if sys.platform.startswith('linux') and "emscripten" not in os.environ.get("_PYTHON_HOST_PLATFORM", ""):
|
||||||
libraries.extend(['m', 'c'])
|
libraries.extend(['m', 'c'])
|
||||||
|
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
ext_modules=[Extension(
|
ext_modules=[Extension(
|
||||||
'spam',
|
'spam',
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
from test import test_projects
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from test import test_projects
|
||||||
|
|
||||||
from . import utils
|
from . import utils
|
||||||
|
|
||||||
pure_python_project = test_projects.TestProject()
|
pure_python_project = test_projects.TestProject()
|
||||||
|
|||||||
@@ -45,13 +45,12 @@ def test(capfd, tmp_path):
|
|||||||
add_env={
|
add_env={
|
||||||
"CIBW_BEFORE_BUILD": "python {project}/bin/before_build.py",
|
"CIBW_BEFORE_BUILD": "python {project}/bin/before_build.py",
|
||||||
"CIBW_TEST_COMMAND": "python {package}/test/run_tests.py",
|
"CIBW_TEST_COMMAND": "python {package}/test/run_tests.py",
|
||||||
# this shouldn't depend on the version of python, so build only CPython 3.10
|
|
||||||
"CIBW_BUILD": "cp310-*",
|
|
||||||
},
|
},
|
||||||
|
single_python=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
# check that the expected wheels are produced
|
# check that the expected wheels are produced
|
||||||
expected_wheels = [w for w in utils.expected_wheels("spam", "0.1.0") if "cp310" in w]
|
expected_wheels = utils.expected_wheels("spam", "0.1.0", single_python=True)
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
captured = capfd.readouterr()
|
captured = capfd.readouterr()
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ class TestSpam(TestCase):
|
|||||||
|
|
||||||
self.assertTrue(path_contains(sys.prefix, sys.executable))
|
self.assertTrue(path_contains(sys.prefix, sys.executable))
|
||||||
self.assertTrue(path_contains(sys.prefix, spam.__file__))
|
self.assertTrue(path_contains(sys.prefix, spam.__file__))
|
||||||
|
self.assertIn("VIRTUAL_ENV", os.environ)
|
||||||
|
|
||||||
def test_uname(self):
|
def test_uname(self):
|
||||||
if platform.system() == "Windows":
|
if platform.system() == "Windows":
|
||||||
@@ -66,7 +67,7 @@ class TestSpam(TestCase):
|
|||||||
# See #336 for more info.
|
# See #336 for more info.
|
||||||
bits = struct.calcsize("P") * 8
|
bits = struct.calcsize("P") * 8
|
||||||
if bits == 32:
|
if bits == 32:
|
||||||
self.assertEqual(platform.machine(), "i686")
|
self.assertIn(platform.machine(), ["i686", "wasm32"])
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
@@ -81,7 +82,7 @@ def test(tmp_path):
|
|||||||
"CIBW_TEST_REQUIRES": "pytest",
|
"CIBW_TEST_REQUIRES": "pytest",
|
||||||
# the 'false ||' bit is to ensure this command runs in a shell on
|
# the 'false ||' bit is to ensure this command runs in a shell on
|
||||||
# mac/linux.
|
# mac/linux.
|
||||||
"CIBW_TEST_COMMAND": "false || pytest {project}/test",
|
"CIBW_TEST_COMMAND": f"false || {utils.invoke_pytest()} {{project}}/test",
|
||||||
"CIBW_TEST_COMMAND_WINDOWS": "COLOR 00 || pytest {project}/test",
|
"CIBW_TEST_COMMAND_WINDOWS": "COLOR 00 || pytest {project}/test",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -102,13 +103,14 @@ def test_extras_require(tmp_path):
|
|||||||
"CIBW_TEST_EXTRAS": "test",
|
"CIBW_TEST_EXTRAS": "test",
|
||||||
# the 'false ||' bit is to ensure this command runs in a shell on
|
# the 'false ||' bit is to ensure this command runs in a shell on
|
||||||
# mac/linux.
|
# mac/linux.
|
||||||
"CIBW_TEST_COMMAND": "false || pytest {project}/test",
|
"CIBW_TEST_COMMAND": f"false || {utils.invoke_pytest()} {{project}}/test",
|
||||||
"CIBW_TEST_COMMAND_WINDOWS": "COLOR 00 || pytest {project}/test",
|
"CIBW_TEST_COMMAND_WINDOWS": "COLOR 00 || pytest {project}/test",
|
||||||
},
|
},
|
||||||
|
single_python=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
# also check that we got the right wheels
|
# also check that we got the right wheels
|
||||||
expected_wheels = utils.expected_wheels("spam", "0.1.0")
|
expected_wheels = utils.expected_wheels("spam", "0.1.0", single_python=True)
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -18,17 +18,14 @@ def test_wheel_tag_is_correct_when_using_macosx_deployment_target(tmp_path):
|
|||||||
deployment_target = "10.11"
|
deployment_target = "10.11"
|
||||||
actual_wheels = utils.cibuildwheel_run(
|
actual_wheels = utils.cibuildwheel_run(
|
||||||
project_dir,
|
project_dir,
|
||||||
add_env={
|
add_env={"MACOSX_DEPLOYMENT_TARGET": deployment_target},
|
||||||
"CIBW_BUILD": "cp39-*",
|
single_python=True,
|
||||||
"MACOSX_DEPLOYMENT_TARGET": deployment_target,
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# check that the expected wheels are produced
|
# check that the expected wheels are produced
|
||||||
expected_wheels = utils.expected_wheels(
|
expected_wheels = utils.expected_wheels(
|
||||||
"spam", "0.1.0", macosx_deployment_target=deployment_target
|
"spam", "0.1.0", macosx_deployment_target=deployment_target, single_python=True
|
||||||
)
|
)
|
||||||
expected_wheels = [w for w in expected_wheels if "cp39" in w]
|
|
||||||
|
|
||||||
print("actual_wheels", actual_wheels)
|
print("actual_wheels", actual_wheels)
|
||||||
print("expected_wheels", expected_wheels)
|
print("expected_wheels", expected_wheels)
|
||||||
|
|||||||
@@ -60,16 +60,13 @@ def test_wheel_tag_is_correct_when_using_windows_cross_compile(tmp_path, use_pyp
|
|||||||
# build the wheels
|
# build the wheels
|
||||||
actual_wheels = utils.cibuildwheel_run(
|
actual_wheels = utils.cibuildwheel_run(
|
||||||
project_dir,
|
project_dir,
|
||||||
add_env={
|
|
||||||
"CIBW_BUILD": "cp310-*",
|
|
||||||
},
|
|
||||||
add_args=["--archs", "ARM64"],
|
add_args=["--archs", "ARM64"],
|
||||||
|
single_python=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
# check that the expected wheels are produced
|
# check that the expected wheels are produced
|
||||||
expected_wheels = [
|
tag = "cp{}{}".format(*utils.SINGLE_PYTHON_VERSION)
|
||||||
"spam-0.1.0-cp310-cp310-win_arm64.whl",
|
expected_wheels = [f"spam-0.1.0-{tag}-{tag}-win_arm64.whl"]
|
||||||
]
|
|
||||||
|
|
||||||
print("actual_wheels", actual_wheels)
|
print("actual_wheels", actual_wheels)
|
||||||
print("expected_wheels", expected_wheels)
|
print("expected_wheels", expected_wheels)
|
||||||
|
|||||||
+68
-19
@@ -11,9 +11,18 @@ import platform as pm
|
|||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from tempfile import TemporaryDirectory
|
from tempfile import TemporaryDirectory
|
||||||
|
from typing import Final
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from cibuildwheel.architecture import Architecture
|
||||||
from cibuildwheel.util import CIBW_CACHE_PATH
|
from cibuildwheel.util import CIBW_CACHE_PATH
|
||||||
|
|
||||||
|
EMULATED_ARCHS: Final[list[str]] = sorted(
|
||||||
|
arch.value for arch in (Architecture.all_archs("linux") - Architecture.auto_archs("linux"))
|
||||||
|
)
|
||||||
|
SINGLE_PYTHON_VERSION: Final[tuple[int, int]] = (3, 12)
|
||||||
|
|
||||||
platform: str
|
platform: str
|
||||||
|
|
||||||
if "CIBW_PLATFORM" in os.environ:
|
if "CIBW_PLATFORM" in os.environ:
|
||||||
@@ -67,7 +76,13 @@ def _update_pip_cache_dir(env: dict[str, str]) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def cibuildwheel_run(
|
def cibuildwheel_run(
|
||||||
project_path, package_dir=".", env=None, add_env=None, output_dir=None, add_args=None
|
project_path,
|
||||||
|
package_dir=".",
|
||||||
|
env=None,
|
||||||
|
add_env=None,
|
||||||
|
output_dir=None,
|
||||||
|
add_args=None,
|
||||||
|
single_python=False,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Runs cibuildwheel as a subprocess, building the project at project_path.
|
Runs cibuildwheel as a subprocess, building the project at project_path.
|
||||||
@@ -99,6 +114,9 @@ def cibuildwheel_run(
|
|||||||
|
|
||||||
env.setdefault("CIBW_FREE_THREADED_SUPPORT", "1")
|
env.setdefault("CIBW_FREE_THREADED_SUPPORT", "1")
|
||||||
|
|
||||||
|
if single_python:
|
||||||
|
env["CIBW_BUILD"] = "cp{}{}-*".format(*SINGLE_PYTHON_VERSION)
|
||||||
|
|
||||||
with TemporaryDirectory() as tmp_output_dir:
|
with TemporaryDirectory() as tmp_output_dir:
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
[
|
[
|
||||||
@@ -119,17 +137,11 @@ def cibuildwheel_run(
|
|||||||
return wheels
|
return wheels
|
||||||
|
|
||||||
|
|
||||||
def _get_arm64_macosx_deployment_target(macosx_deployment_target: str) -> str:
|
def _floor_macosx(*args: str) -> str:
|
||||||
"""
|
"""
|
||||||
The first version of macOS that supports arm is 11.0. So the wheel tag
|
Make sure a deployment target is not less than some value.
|
||||||
cannot contain an earlier deployment target, even if
|
|
||||||
MACOSX_DEPLOYMENT_TARGET sets it.
|
|
||||||
"""
|
"""
|
||||||
version_tuple = tuple(map(int, macosx_deployment_target.split(".")))
|
return max(args, key=lambda x: tuple(map(int, x.split("."))))
|
||||||
if version_tuple <= (11, 0):
|
|
||||||
return "11.0"
|
|
||||||
else:
|
|
||||||
return macosx_deployment_target
|
|
||||||
|
|
||||||
|
|
||||||
def expected_wheels(
|
def expected_wheels(
|
||||||
@@ -141,6 +153,8 @@ def expected_wheels(
|
|||||||
machine_arch=None,
|
machine_arch=None,
|
||||||
python_abi_tags=None,
|
python_abi_tags=None,
|
||||||
include_universal2=False,
|
include_universal2=False,
|
||||||
|
single_python=False,
|
||||||
|
single_arch=False,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Returns a list of expected wheels from a run of cibuildwheel.
|
Returns a list of expected wheels from a run of cibuildwheel.
|
||||||
@@ -170,6 +184,8 @@ def expected_wheels(
|
|||||||
if musllinux_versions is None:
|
if musllinux_versions is None:
|
||||||
musllinux_versions = ["musllinux_1_2"]
|
musllinux_versions = ["musllinux_1_2"]
|
||||||
|
|
||||||
|
if platform == "pyodide" and python_abi_tags is None:
|
||||||
|
python_abi_tags = ["cp312-cp312"]
|
||||||
if python_abi_tags is None:
|
if python_abi_tags is None:
|
||||||
python_abi_tags = [
|
python_abi_tags = [
|
||||||
"cp36-cp36m",
|
"cp36-cp36m",
|
||||||
@@ -180,9 +196,8 @@ def expected_wheels(
|
|||||||
"cp311-cp311",
|
"cp311-cp311",
|
||||||
"cp312-cp312",
|
"cp312-cp312",
|
||||||
"cp313-cp313",
|
"cp313-cp313",
|
||||||
|
"cp313-cp313t",
|
||||||
]
|
]
|
||||||
if platform != "macos":
|
|
||||||
python_abi_tags.append("cp313-cp313t")
|
|
||||||
|
|
||||||
if machine_arch in ["x86_64", "AMD64", "x86", "aarch64"]:
|
if machine_arch in ["x86_64", "AMD64", "x86", "aarch64"]:
|
||||||
python_abi_tags += [
|
python_abi_tags += [
|
||||||
@@ -201,20 +216,37 @@ def expected_wheels(
|
|||||||
"cp311-cp311",
|
"cp311-cp311",
|
||||||
"cp312-cp312",
|
"cp312-cp312",
|
||||||
"cp313-cp313",
|
"cp313-cp313",
|
||||||
|
"cp313-cp313t",
|
||||||
"pp38-pypy38_pp73",
|
"pp38-pypy38_pp73",
|
||||||
"pp39-pypy39_pp73",
|
"pp39-pypy39_pp73",
|
||||||
"pp310-pypy310_pp73",
|
"pp310-pypy310_pp73",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if single_python:
|
||||||
|
python_tag = "cp{}{}-".format(*SINGLE_PYTHON_VERSION)
|
||||||
|
python_abi_tags = [
|
||||||
|
next(
|
||||||
|
tag
|
||||||
|
for tag in python_abi_tags
|
||||||
|
if tag.startswith(python_tag) and not tag.endswith("t")
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
wheels = []
|
wheels = []
|
||||||
|
|
||||||
|
if platform == "pyodide":
|
||||||
|
assert len(python_abi_tags) == 1
|
||||||
|
python_abi_tag = python_abi_tags[0]
|
||||||
|
platform_tag = "pyodide_2024_0_wasm32"
|
||||||
|
return [f"{package_name}-{package_version}-{python_abi_tag}-{platform_tag}.whl"]
|
||||||
|
|
||||||
for python_abi_tag in python_abi_tags:
|
for python_abi_tag in python_abi_tags:
|
||||||
platform_tags = []
|
platform_tags = []
|
||||||
|
|
||||||
if platform == "linux":
|
if platform == "linux":
|
||||||
architectures = [arch_name_for_linux(machine_arch)]
|
architectures = [arch_name_for_linux(machine_arch)]
|
||||||
|
|
||||||
if machine_arch == "x86_64":
|
if machine_arch == "x86_64" and not single_arch:
|
||||||
architectures.append("i686")
|
architectures.append("i686")
|
||||||
|
|
||||||
if len(manylinux_versions) > 0:
|
if len(manylinux_versions) > 0:
|
||||||
@@ -244,18 +276,24 @@ def expected_wheels(
|
|||||||
|
|
||||||
elif platform == "macos":
|
elif platform == "macos":
|
||||||
if machine_arch == "arm64":
|
if machine_arch == "arm64":
|
||||||
arm64_macosx_deployment_target = _get_arm64_macosx_deployment_target(
|
arm64_macosx = _floor_macosx(macosx_deployment_target, "11.0")
|
||||||
macosx_deployment_target
|
platform_tags = [f'macosx_{arm64_macosx.replace(".", "_")}_arm64']
|
||||||
)
|
|
||||||
platform_tags = [f'macosx_{arm64_macosx_deployment_target.replace(".", "_")}_arm64']
|
|
||||||
else:
|
else:
|
||||||
platform_tags = [f'macosx_{macosx_deployment_target.replace(".", "_")}_x86_64']
|
if python_abi_tag.startswith("pp") and not python_abi_tag.startswith(
|
||||||
|
("pp37", "pp38")
|
||||||
|
):
|
||||||
|
pypy_macosx = _floor_macosx(macosx_deployment_target, "10.15")
|
||||||
|
platform_tags = [f'macosx_{pypy_macosx.replace(".", "_")}_x86_64']
|
||||||
|
elif python_abi_tag.startswith("cp313"):
|
||||||
|
pypy_macosx = _floor_macosx(macosx_deployment_target, "10.13")
|
||||||
|
platform_tags = [f'macosx_{pypy_macosx.replace(".", "_")}_x86_64']
|
||||||
|
else:
|
||||||
|
platform_tags = [f'macosx_{macosx_deployment_target.replace(".", "_")}_x86_64']
|
||||||
|
|
||||||
if include_universal2:
|
if include_universal2:
|
||||||
platform_tags.append(
|
platform_tags.append(
|
||||||
f'macosx_{macosx_deployment_target.replace(".", "_")}_universal2',
|
f'macosx_{macosx_deployment_target.replace(".", "_")}_universal2',
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
msg = f"Unsupported platform {platform!r}"
|
msg = f"Unsupported platform {platform!r}"
|
||||||
raise Exception(msg)
|
raise Exception(msg)
|
||||||
@@ -278,6 +316,17 @@ def get_macos_version():
|
|||||||
return tuple(map(int, version_str.split(".")[:2]))
|
return tuple(map(int, version_str.split(".")[:2]))
|
||||||
|
|
||||||
|
|
||||||
|
def skip_if_pyodide(reason: str):
|
||||||
|
return pytest.mark.skipif(platform == "pyodide", reason=reason)
|
||||||
|
|
||||||
|
|
||||||
|
def invoke_pytest() -> str:
|
||||||
|
# see https://github.com/pyodide/pyodide/issues/4802
|
||||||
|
if platform == "pyodide" and sys.platform.startswith("darwin"):
|
||||||
|
return "python -m pytest"
|
||||||
|
return "pytest"
|
||||||
|
|
||||||
|
|
||||||
def arch_name_for_linux(arch: str):
|
def arch_name_for_linux(arch: str):
|
||||||
"""
|
"""
|
||||||
Archs have different names on different platforms, but it's useful to be
|
Archs have different names on different platforms, but it's useful to be
|
||||||
|
|||||||
@@ -344,6 +344,28 @@ def test_before_all(before_all, platform_specific, platform, intercepted_build_a
|
|||||||
assert build_options.before_all == (before_all or "")
|
assert build_options.before_all == (before_all or "")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("method", ["unset", "command_line", "env_var"])
|
||||||
|
def test_debug_traceback(monkeypatch, method, capfd):
|
||||||
|
if method == "command_line":
|
||||||
|
monkeypatch.setattr(sys, "argv", [*sys.argv, "--debug-traceback"])
|
||||||
|
elif method == "env_var":
|
||||||
|
monkeypatch.setenv("CIBW_DEBUG_TRACEBACK", "TRUE")
|
||||||
|
|
||||||
|
# set an option that produces a configuration error
|
||||||
|
monkeypatch.setenv("CIBW_BUILD_FRONTEND", "invalid_value")
|
||||||
|
|
||||||
|
with pytest.raises(SystemExit) as exit:
|
||||||
|
main()
|
||||||
|
assert exit.value.code == 2
|
||||||
|
|
||||||
|
_, err = capfd.readouterr()
|
||||||
|
|
||||||
|
if method == "unset":
|
||||||
|
assert "Traceback (most recent call last)" not in err
|
||||||
|
else:
|
||||||
|
assert "Traceback (most recent call last)" in err
|
||||||
|
|
||||||
|
|
||||||
def test_defaults(platform, intercepted_build_args):
|
def test_defaults(platform, intercepted_build_args):
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|||||||
@@ -264,3 +264,16 @@ def test_only_overrides_env_vars(monkeypatch, intercepted_build_args, envvar_nam
|
|||||||
assert options.globals.build_selector.skip_config == ""
|
assert options.globals.build_selector.skip_config == ""
|
||||||
assert options.platform == "linux"
|
assert options.platform == "linux"
|
||||||
assert options.globals.architectures == Architecture.all_archs("linux")
|
assert options.globals.architectures == Architecture.all_archs("linux")
|
||||||
|
|
||||||
|
|
||||||
|
def test_pyodide_on_windows(monkeypatch, capsys):
|
||||||
|
monkeypatch.setattr(sys, "platform", "win32")
|
||||||
|
monkeypatch.setattr(sys, "argv", [*sys.argv, "--only", "cp312-pyodide_wasm32"])
|
||||||
|
|
||||||
|
with pytest.raises(SystemExit) as exit:
|
||||||
|
main()
|
||||||
|
|
||||||
|
_, err = capsys.readouterr()
|
||||||
|
|
||||||
|
assert exit.value.code == 2
|
||||||
|
assert "cibuildwheel: Building for pyodide is not supported on Windows" in err
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from pathlib import Path
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from cibuildwheel import errors
|
||||||
from cibuildwheel.__main__ import get_build_identifiers, get_platform_module
|
from cibuildwheel.__main__ import get_build_identifiers, get_platform_module
|
||||||
from cibuildwheel.bashlex_eval import local_environment_executor
|
from cibuildwheel.bashlex_eval import local_environment_executor
|
||||||
from cibuildwheel.environment import parse_environment
|
from cibuildwheel.environment import parse_environment
|
||||||
@@ -127,7 +128,8 @@ def test_passthrough_evil(tmp_path, monkeypatch, env_var_value):
|
|||||||
|
|
||||||
|
|
||||||
xfail_env_parse = pytest.mark.xfail(
|
xfail_env_parse = pytest.mark.xfail(
|
||||||
raises=SystemExit, reason="until we can figure out the right way to quote these values"
|
raises=errors.ConfigurationError,
|
||||||
|
reason="until we can figure out the right way to quote these values",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user