Compare commits

...
74 Commits
Author SHA1 Message Date
Henry Schreiner 9e4e50bd76 Bump version: v3.1.2 2025-07-29 09:12:51 -06:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 8ef9414f60 [pre-commit.ci] pre-commit autoupdate (#2532)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.12.4 → v0.12.5](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.4...v0.12.5)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-07-28 16:06:41 -06:00
Joe Rickerby 1953c04972 Adding @mhsmith as platform maintainer for Android (#2516) 2025-07-28 13:00:54 -06:00
Russell Keith-Magee 46a6d27995 Bump iOS support package versions. (#2530) 2025-07-28 12:26:52 -06:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 0dc0b48e4d [Bot] Update dependencies (#2529)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-07-28 08:20:49 -06:00
Matthieu Darbois b887417e0e fix: enable all shall not enable cpython-experimental-riscv64 (#2528) 2025-07-27 23:18:35 -06:00
Matthieu Darbois 36c5079ce5 tests: reduce testing time on iOS (#2523)
A number of tests only require to run with a single version of python.
This will reduce the time needed to run those tests.
2025-07-27 23:15:55 -06:00
Matthieu Darbois 68c03665d3 feat: add deprecation warning for cpython-experimental-riscv64 enable (#2526) 2025-07-27 15:58:03 -04:00
Matthieu Darbois 1c38fac9c8 fix: invalid warning on test-skip = ["*-macosx_universal2:arm64"] (#2522) 2025-07-27 09:13:38 -04:00
Henry SchreinerandMatthieu Darbois 140bc1a96a tests: mark and add skips (#2524)
* tests: mark and add skips

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix comment

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Matthieu Darbois <mayeut@users.noreply.github.com>
2025-07-27 12:12:42 +02:00
Henry Schreiner 55fcb25edc fix: error on CIBW_FREE_THREADED_SUPPORT set (#2520)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-07-26 19:09:26 +02:00
Clément Robertandmayeut c89609aaf2 BUG: fix incorrect number of wheels in logs (#2517)
* BUG: fix incorrect number of wheels in logs

* add a test

---------

Co-authored-by: mayeut <mayeut@users.noreply.github.com>
2025-07-26 13:13:01 +02:00
tabedzkiHenry SchreinerJoe Rickerbypre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
13b086e3c6 docs: clarify language around Python requirements for local cibuildwheel usage (#2513)
* First set of changes to setup.md

* Update docs/setup.md

Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>

* Update docs/setup.md

Co-authored-by: Joe Rickerby <joerick@mac.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-07-25 00:46:16 -04:00
Malcolm Smith 302af02853 fix(android): execute wheel via sys.executable (#2515)
* Android: execute `wheel` via `sys.executable`

* Suppress unhelpful cyclic-import warning
2025-07-24 16:38:22 -04:00
Matthieu Darbois 9361af3204 feat: riscv64 builds are now enabled by default (#2509)
PyPI now accepts `riscv64` uploads for manylinux/musllinux.
Those builds do not require an opt-in anymore.
2025-07-24 14:52:07 -04:00
Malcolm Smith 47c5a35421 Add Cirrus macOS as a runner that supports building for Android but not testing (#2514)
* Add Cirrus macOS as a runner that supports building for Android but not testing

* Try to trigger Cirrus by changing .cirrus.yml
2025-07-24 14:51:39 -04:00
Joe Rickerby e6de07ed39 Bump version: v3.1.1 2025-07-24 19:14:08 +01:00
timrid 2ca692b1e5 docs: iOS typo fix in docs (#2510)
Update platforms.md

Fixed typo
2025-07-24 12:29:16 -04:00
Clément Robert 1ac7fa7f00 fix: report defects in logs and HTML summaries (#2512)
* BUG: fix incorrect reporting of number of wheels produced

* BUG: fix incorrect naming of test only sessions in html summaries
2025-07-24 12:28:30 -04:00
Henry Schreiner ffd835cef1 Bump version: v3.1.0 2025-07-23 23:23:06 -04:00
Henry Schreiner 3e2a9aa6e8 fix: regenerate schema
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-07-23 22:47:02 -04:00
cibuildwheel-bot[bot] 10c727eed9 feat: Python 3.14rc1 build by default (#2507) 2025-07-23 20:59:02 -04:00
pre-commit-ci[bot]pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>Henry Schreiner
f628c9dd23 [pre-commit.ci] pre-commit autoupdate (#2505)
* [pre-commit.ci] pre-commit autoupdate

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.12.3 → v0.12.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.3...v0.12.4)
- [github.com/pre-commit/mirrors-mypy: v1.16.1 → v1.17.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.16.1...v1.17.0)

* chore: fix typing issue from match statement

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-07-23 17:18:48 -04:00
0f487ee2cb feat: add support for building Android wheels (#2349)
* Add Android to resource files

* Add Android to miscellaneous places

* Add Android documentation

* Docs cleanups

* Add Android platform module; implement top-level structure and target Python installation

* Implement setup_env and build_wheel

* lru-dict build working

* Alter prefix in sysconfigdata file; fix various issues with FLAGS variables

* Implement Android testing

* Add type annotations to _cross_venv

* Revert Python 3.8 to pip 25.0.1

* Make test-sources required on Android

* Add Android integration tests

* Test cleanups

* Add test of all available Python versions

* Update test-sources and test-command behavior to match iOS

* Documentation cleanups

* Replace Builder class with a set of global functions

* Rename "env" to "build_env"

* Remove Chaquopy repository from default pip command line

* Move native_platform to platforms module

* Fix parse_config_settings

Co-authored-by: Joe Rickerby <joerick@mac.com>

* Add unit tests for parse_config_settings and arch_synonym

* Make `shell_prepared` arguments keyword-only, and add tests for the commands that use it

* Replace `importlib.util.spec_from_file_location` with `runpy.run_path`

* Use python-build-standalone

* Update Android Python

* Enable KVM in Linux CI

* Move KVM code to test_android.py

* Use Java 17 on Azure

* Install emulator if necessary before running -accel-check

* Free up additional disk space on Linux runners

* Add sudo

* Skip emulator tests on CI platforms that don't support it

* Download Android Python from Maven Central

* Free up more disk space on Linux runners

* fix: minor fixups

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Set sysconfig._BASE_PREFIX to support sysconfig.get_path("include")

* Get ANDROID_API_LEVEL from the build environment, not cibuildwheel's own environment

* Correct relative path of test-sources

* Pass a CMake toolchain file to the build

* Add "repair" step which adds libc++ to the wheel when necessary

* Add missing needs_emulator decorator

* Provide useful error message if ANDROID_HOME is not set

* Remove use of HOST environment variable

* Update to Python 3.15.5

* Fix PyLint warnings, clarify comment

* Group common arguments into a dataclass

* Handle environment variables containing newlines

* Discourage the use of `pytest` test commands without `python -m`

* Use single quotes in user-visible messages

* Improve testing documentation

* Pass wheel filename to `log.build_end`

* In GitHub Actions example, skip Android tests on macOS

* Correct relative paths in `patchelf --set-rpath`

* Clarify `test-sources` docs

* Update to Python 3.13.5+20250722.214220

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>
Co-authored-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-07-23 17:18:33 -04:00
Matthieu Darbois e2e24882d8 feat: add default riscv64 images (#2506) 2025-07-23 13:28:36 +02:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> a8bff94dbb [Bot] Update dependencies (#2504)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-07-21 14:16:55 +02:00
Henry Schreiner fbf37256a5 feat: include free-threaded 3.14 by default (#2503)
This is no longer experimental in 3.13 beta 3, so we don't need an enable for it anymore. Someone can skip expliclity with `'cp31?t-*'` if they really don't want free-threaded wheels.

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-07-19 19:38:05 -04:00
Matthieu Darbois 9613645abc feat: add support for manylinux_2_34 i686 image (#2500) 2025-07-18 11:30:42 -07:00
Henry Schreiner 7247e3887c fix: show options for --only (#2499)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-07-17 00:00:49 +02:00
Henry SchreinerandJoe Rickerby d0e4aded3e feat: add summary to Action (#2469)
* chore: add summary to Action

* refactor: new summary table

* fix: fixup tests and formatting

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix: pyodide missing some logging

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix: nicer printout, nicer in-place summary

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix: use summary for everything

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix: support only one output wheel from repair

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Add new Github summary format

* Remove a couple of humanize uses

* fix: filter ANSI codes in summary

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix: add sha256

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix: nicer wheel/wheels depending on how many are present

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>
2025-07-15 20:45:53 -07:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> a2c263d4ab [pre-commit.ci] pre-commit autoupdate (#2497)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.12.2 → v0.12.3](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.2...v0.12.3)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-07-14 21:34:17 -07:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 576ebccc78 [Bot] Update dependencies (#2496)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-07-14 01:31:16 -07:00
Agriya KhetarpalandHenry Schreiner cd9b48cb74 feat: Pyodide 0.28 release (#2487)
* Bump testing version to 0.28.0

* Bump Pyodide constraints

* Add Pyodide 0.28.0 identifier

* Override to a different, alpha version for testing

* Update docs for Pyodide versions everywhere

* Add comment on using older Pyodide alpha version

* Bump to `"cp313*"` for compatibility

* Drop identifier for Pyodide 0.28.0a3

* Add back Pyodide 0.28.0a3 identifier

* Add helpful comment about pyodide-prerelease option

* Fix test and add a comment about it

* Keep one Pyodide version per Python version

* Fix docs for `pyodide-prerelease`; Python ➡️ Pyodide

* Update utils.py

* Drop CI test step for overridden Pyodide version

Suggested-by: Joe Rickerby <joerick@mac.com>

---------

Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
2025-07-12 18:38:50 -07:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 5e245905b3 [Bot] Update dependencies (#2488)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-07-09 22:14:56 -07:00
Hanlu 157d81eddb chore: remove redundant word in comment (#2492)
Signed-off-by: liangmulu <liangmulu@outlook.com>
2025-07-09 13:57:38 +01:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 75b1860702 [pre-commit.ci] pre-commit autoupdate (#2491)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.12.1 → v0.12.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.1...v0.12.2)
- [github.com/python-jsonschema/check-jsonschema: 0.33.1 → 0.33.2](https://github.com/python-jsonschema/check-jsonschema/compare/0.33.1...0.33.2)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-07-08 09:00:18 -04:00
MichaIng 807cd81699 fix: typo in riscv64 CPython 3.9 identifier (#2490)
Signed-off-by: MichaIng <micha@dietpi.com>
2025-07-08 08:59:53 -04:00
Henry SchreinerandJoe Rickerby 834ea40510 chore: pattern matching (#2434)
* chore: use match for stringify_setting

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* chore: more match statements

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>
2025-07-08 08:59:20 -04:00
Matthieu DarboisandHenry Schreiner 46ef6044e2 feat: add support for manylinux_2_28 i686 image (#2480)
Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
2025-07-08 09:32:51 +01:00
Henry Schreiner dafc3545eb fix: support only one output wheel from repair (#2478)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-07-08 09:32:22 +01:00
Joe Rickerby a6ec930f15 Welcome to our new platform maintainers! (#2481)
* Add new platform maintainers to README!

* Add a codeowners file
2025-07-08 09:14:34 +01:00
Joe Rickerby 95d2f3a92f Bump version: v3.0.1 2025-07-05 19:03:42 +01:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 40de3fe510 [pre-commit.ci] pre-commit autoupdate (#2483)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.12.0 → v0.12.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.0...v0.12.1)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-07-01 16:58:23 -04:00
920081014e feat: added Python 3.14 preview for iOS (#2475)
* Added Python 3.14 preview for iOS

* Test all Python versions with iOS

* add cp314-ios to the build selector table

---------

Co-authored-by: Andrew Murray <radarhere@users.noreply.github.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>
2025-06-30 10:32:25 -04:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 94fe0a212f [Bot] Update dependencies (#2482)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-06-30 10:31:30 -04:00
Henry Schreiner 405ddd5315 fix: pyodide missing some logging (#2477)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-06-28 09:18:18 +02:00
pre-commit-ci[bot]pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>Henry Schreiner
34b4f1e86e [pre-commit.ci] pre-commit autoupdate (#2474)
* [pre-commit.ci] pre-commit autoupdate

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.13 → v0.12.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.13...v0.12.0)
- [github.com/pre-commit/mirrors-mypy: v1.16.0 → v1.16.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.16.0...v1.16.1)
- [github.com/python-jsonschema/check-jsonschema: 0.33.0 → 0.33.1](https://github.com/python-jsonschema/check-jsonschema/compare/0.33.0...0.33.1)

* chore: address new lints

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-06-24 09:48:01 -04:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> e69b5532ab [Bot] Update dependencies (#2473)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-06-23 13:08:10 -04:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 3e86452449 [Bot] Update dependencies (#2471)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-06-20 10:11:04 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> e73749579b chore(deps): bump actions/attest-build-provenance from 2.3.0 to 2.4.0 in the actions group (#2472)
chore(deps): bump actions/attest-build-provenance in the actions group

Bumps the actions group with 1 update: [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance).


Updates `actions/attest-build-provenance` from 2.3.0 to 2.4.0
- [Release notes](https://github.com/actions/attest-build-provenance/releases)
- [Changelog](https://github.com/actions/attest-build-provenance/blob/main/RELEASE.md)
- [Commits](https://github.com/actions/attest-build-provenance/compare/db473fddc028af60658334401dc6fa3ffd8669fd...e8998f949152b193b063cb0ec769d69d929409be)

---
updated-dependencies:
- dependency-name: actions/attest-build-provenance
  dependency-version: 2.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-18 16:08:18 -04:00
Henry Schreiner 588dee0e0c docs: include Windows ARM in examples (#2468)
docs: include Windows ARM
2025-06-14 09:58:07 -04:00
Henry Schreiner 9c53c4ee46 chore: minor tweaks to generate schema script (#2467)
chore: minor tweeks to generate schema script

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-06-14 14:58:57 +02:00
Joe Rickerby 5f22145df4 Bump version: v3.0.0 2025-06-11 14:02:58 +01:00
Joe Rickerby a73177515a Docs: mobile layout fix (#2466)
* Remove console warning

* Fix table overflow issue on mobile
2025-06-11 08:57:43 +01:00
Henry Schreiner ff86a64577 docs: add tips for numpy (#2465)
* docs: add tips for numpy

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* docs: update example for numpy 2.3.0

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Update docs/faq.md

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-06-11 08:57:24 +01:00
Henry Schreiner 6f5e480fec chore: use pip's groups in CI (#2463)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-06-11 08:56:12 +01:00
Henry Schreiner 3c5ff09888 Bump version: v3.0.0rc3 2025-06-11 00:57:01 -04:00
cibuildwheel-bot[bot] 1b9a56e014 [Bot] Update dependencies (#2455) 2025-06-10 21:06:51 -04:00
Henry Schreiner aa9fe2a24e ci: use uv python for docs (binary b1) (#2462)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-06-10 18:28:34 -04:00
Henry SchreinerandJoe Rickerby e188d9e260 feat: remove 32-bit linux from auto arch, fix auto32 on linux aarch64 (#2458)
* feat: remove 32-bit linux from auto arch, fix auto32 on linux aarch64

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Apply suggestions from code review

Co-authored-by: Joe Rickerby <joerick@mac.com>

* Update cibuildwheel/architecture.py

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>
2025-06-10 17:57:41 -04:00
Henry Schreiner 3fa7bd1e72 ci: fix cirrus and reduce rebuilds (#2460)
* ci: fix Cirrus CI on Windows

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* ci: skip unrelated changes more often

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-06-10 17:57:25 -04:00
Joe Rickerby c6368be701 Move to the OS-latest image tags on Azure Pipelines (#2461) 2025-06-10 16:48:53 -04:00
Henry Schreiner dcecda6962 chore: handle update of cog to 3.5 (#2459)
chore: handle update of cog

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-06-10 01:54:45 -04:00
Henry Schreiner adc58e93d1 Revert "ci: update example too"
This reverts commit 487154abbb.
2025-06-09 23:39:56 -04:00
Henry Schreiner 487154abbb ci: update example too 2025-06-09 23:15:34 -04:00
Henry Schreiner 0fe82c5882 chore: order python-standalone releases for stability (#2456)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-06-09 18:32:32 -04:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 0b6c7d0aff [pre-commit.ci] pre-commit autoupdate (#2457)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.12 → v0.11.13](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.12...v0.11.13)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-06-09 19:37:40 +01:00
Henry Schreiner 38dab4b641 docs: warning about pyodide-version (#2450) 2025-06-06 09:57:58 -04:00
Henry Schreiner 2edf578499 Bump version: v3.0.0rc2 2025-06-06 00:05:23 -04:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 70c6aba901 [Bot] Update dependencies (#2449)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-06-06 00:00:58 -04:00
Joe Rickerby 31f536bdf3 Bump version: v3.0.0rc1 2025-06-05 11:39:19 +00:00
cibuildwheel-bot[bot] 7601bfcb45 [Bot] Update dependencies (#2443) 2025-06-04 18:21:09 -04:00
Joe Rickerby b5c33b7a09 Docs fixes (#2446) 2025-06-04 18:20:54 -04:00
Henry Schreiner 4e8d0757df chore: touch up release script (#2442)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-06-03 17:34:22 -04:00
105 changed files with 3378 additions and 1318 deletions
+2 -2
View File
@@ -10,7 +10,7 @@ fi
$PYTHON --version $PYTHON --version
$PYTHON -m venv venv $PYTHON -m venv venv
venv/bin/python -m pip install -U pip dependency-groups venv/bin/python -m pip install -U pip
venv/bin/python -m dependency_groups test | xargs venv/bin/python -m pip install -e. venv/bin/python -m pip install -e. --group test
venv/bin/python -m pip freeze venv/bin/python -m pip freeze
venv/bin/python --version venv/bin/python --version
+9 -6
View File
@@ -1,9 +1,9 @@
only_if: changesInclude('.cirrus.yml') || ($CIRRUS_BRANCH == "main" && !changesIncludeOnly('docs/*', '.pre-commit-config.yaml')) || $CIRRUS_BRANCH =~ 'cirrus.*' only_if: changesInclude('.cirrus.yml') || ($CIRRUS_BRANCH == "main" && !changesIncludeOnly('.github/*', 'bin/*', 'docs/*', '.circleci/*', '.travis.yml', '.pre-commit-config.yaml', '.readthedocs.yml', 'azure-pipelines.yml', 'README.md', 'mkdocs.yml', 'noxfile.py')) || $CIRRUS_BRANCH =~ 'cirrus.*'
run_tests: &RUN_TESTS run_tests: &RUN_TESTS
install_cibuildwheel_script: install_cibuildwheel_script:
- python -m pip install dependency-groups - python -m pip install -U pip
- python -m dependency_groups test | xargs python -m pip install -e. - python -m pip install -e. --group test
run_cibuildwheel_tests_script: run_cibuildwheel_tests_script:
- python ./bin/run_tests.py - python ./bin/run_tests.py
@@ -46,8 +46,8 @@ linux_aarch64_task:
<<: *RUN_TESTS <<: *RUN_TESTS
windows_x86_task: windows_x86_task:
# The task takes ~55 minutes while the timeout happens # The task takes ~55 minutes while the timeout happens after 60 minutes by default,
# after 60 minutes by default, let's allow some wiggle room. # let's allow some wiggle room.
timeout_in: 120m timeout_in: 120m
windows_container: windows_container:
image: cirrusci/windowsservercore:visualstudio2022 image: cirrusci/windowsservercore:visualstudio2022
@@ -55,9 +55,12 @@ windows_x86_task:
memory: 8G memory: 8G
install_pre_requirements_script: install_pre_requirements_script:
- certutil -generateSSTFromWU roots.sst
- certutil -addstore -f root roots.sst
- del roots.sst
- choco install -y --no-progress python3 --version 3.12.4 - choco install -y --no-progress python3 --version 3.12.4
- refreshenv - refreshenv
- echo PATH=%PATH% >> "%CIRRUS_ENV%" - powershell -Command "$cleanPath = $env:PATH -replace ';+$', ''; Add-Content -Path $env:CIRRUS_ENV -Value ('PATH=' + $cleanPath)"
<<: *RUN_TESTS <<: *RUN_TESTS
macos_arm64_task: macos_arm64_task:
+3
View File
@@ -0,0 +1,3 @@
cibuildwheel/platforms/ios.py @freakboy3742
cibuildwheel/platforms/pyodide.py @hoodmane @ryanking13 @agriyakhetarpal
cibuildwheel/platforms/android.py @mhsmith
+1 -1
View File
@@ -33,7 +33,7 @@ jobs:
path: dist path: dist
- name: Generate artifact attestation for sdist and wheel - name: Generate artifact attestation for sdist and wheel
uses: actions/attest-build-provenance@db473fddc028af60658334401dc6fa3ffd8669fd # v2.3.0 uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2.4.0
with: with:
subject-path: "dist/cibuildwheel-*" subject-path: "dist/cibuildwheel-*"
+30 -20
View File
@@ -12,8 +12,19 @@ on:
- reopened - reopened
- labeled - labeled
paths-ignore: paths-ignore:
- 'docs/**' - .ci*
- bin/*
- docs/**
- examples/azure-pipelines-*
- examples/ci*
- examples/travis-ci-*
- .pre-commit-config.yaml - .pre-commit-config.yaml
- .readthedocs.yml
- .travis.yml
- README.md
- azure-pipelines.yml
- mkdocs.yml
- noxfile.py
workflow_dispatch: workflow_dispatch:
# allow manual runs on branches without a PR # allow manual runs on branches without a PR
@@ -23,7 +34,7 @@ concurrency:
jobs: jobs:
lint: lint:
name: Linters (mypy, flake8, etc.) name: Linters (mypy, ruff, etc.)
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -40,6 +51,7 @@ jobs:
needs: lint needs: lint
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
fail-fast: false
matrix: matrix:
os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, windows-11-arm, macos-13, macos-15] os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, windows-11-arm, macos-13, macos-15]
python_version: ['3.13'] python_version: ['3.13']
@@ -59,13 +71,23 @@ jobs:
- uses: astral-sh/setup-uv@v6 - uses: astral-sh/setup-uv@v6
# free some space to prevent reaching GHA disk space limits - name: Free up disk space
- name: Clean docker images
if: runner.os == 'Linux' if: runner.os == 'Linux'
run: | run: |
docker system prune -a -f docker system prune -a -f
sudo rm -rf $ANDROID_HOME/ndk/{26,28}.* /opt/hostedtoolcache/CodeQL \
/usr/local/lib/node_modules /usr/local/share/chromium \
/usr/local/share/powershell
df -h df -h
# https://github.blog/changelog/2024-04-02-github-actions-hardware-accelerated-android-virtualization-now-available/
- name: Enable KVM for Android emulator
if: runner.os == 'Linux' && runner.arch == 'X64'
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
# for oci_container unit tests # for oci_container unit tests
- name: Set up QEMU - name: Set up QEMU
if: runner.os == 'Linux' if: runner.os == 'Linux'
@@ -76,6 +98,7 @@ jobs:
uv sync --no-dev --group test uv sync --no-dev --group test
- uses: joerick/pr-labels-action@v1.0.9 - uses: joerick/pr-labels-action@v1.0.9
- name: Set CIBW_ENABLE - name: Set CIBW_ENABLE
shell: bash shell: bash
run: | run: |
@@ -113,16 +136,16 @@ jobs:
with: with:
package-dir: sample_proj package-dir: sample_proj
output-dir: wheelhouse_only output-dir: wheelhouse_only
only: cp312-${{ runner.os == 'Linux' && (runner.arch == 'ARM64' && 'manylinux_aarch64' || 'manylinux_x86_64') || (runner.os == 'Windows' && 'win_amd64' || 'macosx_x86_64') }} only: cp313-${{ runner.os == 'Linux' && (runner.arch == 'ARM64' && 'manylinux_aarch64' || 'manylinux_x86_64') || (runner.os == 'Windows' && 'win_amd64' || 'macosx_x86_64') }}
- name: Create custom configuration file - name: Create custom configuration file
shell: bash shell: bash
run: | run: |
cat > sample_proj/cibw.toml <<EOF cat > sample_proj/cibw.toml <<EOF
[tool.cibuildwheel] [tool.cibuildwheel]
# Only build on CPython 3.12 on native arch # Only build on CPython 3.13 on native arch
archs = ["native"] archs = ["native"]
build = "cp312-*" build = "cp313-*"
# Skip musllinux # Skip musllinux
skip = "*-musllinux*" skip = "*-musllinux*"
EOF EOF
@@ -222,19 +245,6 @@ jobs:
env: env:
CIBW_PLATFORM: pyodide CIBW_PLATFORM: pyodide
- name: Run a sample build (GitHub Action) for an overridden Pyodide version
uses: ./
with:
package-dir: sample_proj
output-dir: wheelhouse
# In case this breaks at any point in time, switch to using the latest version
# available or any other version that is not the same as the default one set
# in cibuildwheel/resources/build-platforms.toml.
env:
CIBW_PLATFORM: pyodide
CIBW_BUILD: "cp312*"
CIBW_PYODIDE_VERSION: "0.27.6"
- name: Run tests with 'CIBW_PLATFORM' set to 'pyodide' - name: Run tests with 'CIBW_PLATFORM' set to 'pyodide'
run: | run: |
uv run --no-sync ./bin/run_tests.py uv run --no-sync ./bin/run_tests.py
+6 -6
View File
@@ -20,8 +20,8 @@ linux:
script: script:
- curl -sSL https://get.docker.com/ | sh - curl -sSL https://get.docker.com/ | sh
- docker run --rm --privileged docker.io/tonistiigi/binfmt:latest --install all - docker run --rm --privileged docker.io/tonistiigi/binfmt:latest --install all
- python -m pip install dependency-groups - python -m pip install -U pip
- python -m dependency_groups test | xargs python -m pip install -e. pytest-custom-exit-code - python -m pip install -e. pytest-custom-exit-code --group test
- python ./bin/run_tests.py - python ./bin/run_tests.py
windows: windows:
@@ -35,8 +35,8 @@ windows:
variables: variables:
CIBW_ENABLE: "all" CIBW_ENABLE: "all"
script: script:
- py -m pip install dependency-groups - py -m pip install -U pip
- py -m pip install -e. pytest-custom-exit-code $(py -m dependency_groups test) - py -m pip install -e. pytest-custom-exit-code --group test
- py bin\run_tests.py - py bin\run_tests.py
tags: tags:
- saas-windows-medium-amd64 - saas-windows-medium-amd64
@@ -50,8 +50,8 @@ macos:
variables: variables:
CIBW_ENABLE: "all" CIBW_ENABLE: "all"
script: script:
- python3 -m pip install dependency-groups - python3 -m pip install -U pip
- python3 -m dependency_groups test | xargs python3 -m pip install -e. pytest-custom-exit-code - python3 -m pip install -e. pytest-custom-exit-code --group test
- python3 ./bin/run_tests.py - python3 ./bin/run_tests.py
tags: tags:
- saas-macos-medium-m1 - saas-macos-medium-m1
+8 -6
View File
@@ -14,28 +14,31 @@ 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.11.12 rev: v0.12.5
hooks: hooks:
- id: ruff - id: ruff
args: ["--fix", "--show-fixes"] args: ["--fix", "--show-fixes"]
- id: ruff-format - id: ruff-format
- repo: https://github.com/pre-commit/mirrors-mypy - repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.16.0 rev: v1.17.0
hooks: hooks:
- id: mypy - id: mypy
name: mypy 3.11 on cibuildwheel/ name: mypy 3.11 on cibuildwheel/
args: ["--python-version=3.11"] args: ["--python-version=3.11"]
exclude: ^cibuildwheel/resources/_cross_venv.py$ # Requires Python 3.13 or later
additional_dependencies: &mypy-dependencies additional_dependencies: &mypy-dependencies
- bracex - bracex
- build
- dependency-groups>=1.2 - dependency-groups>=1.2
- humanize
- nox>=2025.2.9 - nox>=2025.2.9
- orjson - orjson
- packaging - packaging
- pyelftools
- pygithub - pygithub
- pytest - pytest
- rich - rich
- tomli
- tomli_w - tomli_w
- types-certifi - types-certifi
- types-click - types-click
@@ -47,7 +50,6 @@ repos:
- validate-pyproject - validate-pyproject
- id: mypy - id: mypy
name: mypy 3.13 name: mypy 3.13
exclude: ^cibuildwheel/resources/.*py$
args: ["--python-version=3.13"] args: ["--python-version=3.13"]
additional_dependencies: *mypy-dependencies additional_dependencies: *mypy-dependencies
@@ -70,7 +72,7 @@ repos:
pass_filenames: false pass_filenames: false
entry: cog -c -P -r -I ./bin README.md entry: cog -c -P -r -I ./bin README.md
files: '^(README\.md|docs/changelog\.md|docs/options\.md|bin/readme.*)$' files: '^(README\.md|docs/changelog\.md|docs/options\.md|bin/readme.*)$'
additional_dependencies: [cogapp] additional_dependencies: [cogapp>=3.5]
- repo: https://github.com/codespell-project/codespell - repo: https://github.com/codespell-project/codespell
rev: v2.4.1 rev: v2.4.1
@@ -81,7 +83,7 @@ repos:
- repo: https://github.com/python-jsonschema/check-jsonschema - repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.33.0 rev: 0.33.2
hooks: hooks:
- id: check-dependabot - id: check-dependabot
- id: check-github-actions - id: check-github-actions
+1 -3
View File
@@ -5,9 +5,7 @@ version: 2
build: build:
os: ubuntu-24.04 os: ubuntu-24.04
commands: commands:
- asdf install python 3.14-dev
- asdf global python 3.14-dev
- asdf plugin add uv - asdf plugin add uv
- asdf install uv latest - asdf install uv latest
- asdf global uv latest - asdf global uv latest
- NO_COLOR=1 uv run --no-dev --group docs mkdocs build --strict --site-dir $READTHEDOCS_OUTPUT/html - NO_COLOR=1 uv run --python 3.14 --managed-python --no-dev --group docs mkdocs build --strict --site-dir $READTHEDOCS_OUTPUT/html
+2 -2
View File
@@ -64,8 +64,8 @@ jobs:
install: install:
- if [ "${TRAVIS_OS_NAME}" == "linux" ]; then docker run --rm --privileged docker.io/tonistiigi/binfmt:latest --install all; fi - if [ "${TRAVIS_OS_NAME}" == "linux" ]; then docker run --rm --privileged docker.io/tonistiigi/binfmt:latest --install all; fi
- $PYTHON -m pip install -U pip dependency-groups - $PYTHON -m pip install -U pip
- $PYTHON -m dependency_groups test | xargs $PYTHON -m pip install -e. - $PYTHON -m pip install -e. --group test
script: | script: |
# travis_wait disable the output while waiting # travis_wait disable the output while waiting
+114 -66
View File
@@ -23,24 +23,24 @@ What does it do?
While cibuildwheel itself requires a recent Python version to run (we support the last three releases), it can target the following versions to build wheels: While cibuildwheel itself requires a recent Python version to run (we support the last three releases), it can target the following versions to build wheels:
| | 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 | manylinux<br/>musllinux armv7l | iOS | Pyodide | | | 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 | manylinux<br/>musllinux armv7l | Android | iOS | Pyodide |
|--------------------|----|-----|-----|-----|-----|----|-----|----|-----|-----|---|-----|-----| |--------------------|----|-----|----|-----|-----|----|-----|----|-----|-----|---|-----|-----|-----|
| CPython 3.8 | ✅ | ✅ | ✅ | ✅ | N/A | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A | N/A | | CPython 3.8 | ✅ | ✅ | ✅ | ✅ | N/A | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A | N/A | N/A |
| CPython 3.9 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A | N/A | | CPython 3.9 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A | N/A | N/A |
| CPython 3.10 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A | N/A | | CPython 3.10 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A | N/A | N/A |
| CPython 3.11 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A | N/A | | CPython 3.11 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A | N/A | N/A |
| CPython 3.12 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A | ✅⁴ | | CPython 3.12 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A | N/A | ✅⁴ |
| CPython 3.13³ | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | ✅ | N/A | | CPython 3.13³ | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | ✅ | ✅ | N/A |
| CPython 3.14³ | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | | N/A | | CPython 3.14 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A | N/A | N/A |
| PyPy 3.8 v7.3 | ✅ | ✅ | ✅ | N/A | 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 | N/A | N/A | N/A | N/A |
| PyPy 3.9 v7.3 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A | N/A | N/A | N/A | | PyPy 3.9 v7.3 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A | N/A | N/A | N/A | N/A |
| PyPy 3.10 v7.3 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A | N/A | N/A | N/A | | PyPy 3.10 v7.3 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A | N/A | N/A | N/A | N/A |
| PyPy 3.11 v7.3 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A | N/A | N/A | N/A | | PyPy 3.11 v7.3 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A | N/A | N/A | N/A | N/A |
| GraalPy 3.11 v24.2 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | N/A | ✅¹ | N/A | N/A | N/A | N/A | N/A | | GraalPy 3.11 v24.2 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | N/A | ✅¹ | N/A | N/A | N/A | N/A | N/A | N/A |
<sup>¹ PyPy & GraalPy are only supported for manylinux wheels.</sup><br> <sup>¹ PyPy & GraalPy are only supported for manylinux wheels.</sup><br>
<sup>² Windows arm64 support is experimental.</sup><br> <sup>² Windows arm64 support is experimental.</sup><br>
<sup>³ Free-threaded mode requires opt-in using [`enable`](https://cibuildwheel.pypa.io/en/stable/options/#enable).</sup><br> <sup>³ Free-threaded mode requires opt-in on 3.13 using [`enable`](https://cibuildwheel.pypa.io/en/stable/options/#enable).</sup><br>
<sup>⁴ Experimental, not yet supported on PyPI, but can be used directly in web deployment. Use `--platform pyodide` to build.</sup><br> <sup>⁴ Experimental, not yet supported on PyPI, but can be used directly in web deployment. Use `--platform pyodide` to build.</sup><br>
<sup>⁵ manylinux armv7l support is experimental. As there are no RHEL based image for this architecture, it's using an Ubuntu based image instead.</sup><br> <sup>⁵ manylinux armv7l support is experimental. As there are no RHEL based image for this architecture, it's using an Ubuntu based image instead.</sup><br>
@@ -56,19 +56,19 @@ Usage
`cibuildwheel` runs inside a CI service. Supported platforms depend on which service you're using: `cibuildwheel` runs inside a CI service. Supported platforms depend on which service you're using:
| | Linux | macOS | Windows | Linux ARM | macOS ARM | Windows ARM | iOS | | | Linux | macOS | Windows | Linux ARM | macOS ARM | Windows ARM | Android | iOS |
|-----------------|-------|-------|---------|-----------|-----------|-------------|-----| |-----------------|-------|-------|---------|-----------|-----------|-------------|---------|-----|
| GitHub Actions | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅³ | | GitHub Actions | ✅ | ✅ | ✅ | ✅ | ✅ | ✅² | ✅⁴ | ✅³ |
| Azure Pipelines | ✅ | ✅ | ✅ | | ✅ | ✅² | ✅³ | | Azure Pipelines | ✅ | ✅ | ✅ | | ✅ | ✅² | ✅⁴ | ✅³ |
| Travis CI | ✅ | | ✅ | ✅ | | | | | Travis CI | ✅ | | ✅ | ✅ | | | ✅⁴ | |
| CircleCI | ✅ | ✅ | | ✅ | ✅ | | ✅³ | | CircleCI | ✅ | ✅ | | ✅ | ✅ | | ✅⁴ | ✅³ |
| Gitlab CI | ✅ | ✅ | ✅ | ✅¹ | ✅ | | ✅³ | | Gitlab CI | ✅ | ✅ | ✅ | ✅¹ | ✅ | | ✅⁴ | ✅³ |
| Cirrus CI | ✅ | ✅ | ✅ | ✅ | ✅ | | | | Cirrus CI | ✅ | ✅ | ✅ | ✅ | ✅ | | ✅⁴ | |
<sup>¹ [Requires emulation](https://cibuildwheel.pypa.io/en/stable/faq/#emulation), distributed separately. Other services may also support Linux ARM through emulation or third-party build hosts, but these are not tested in our CI.</sup><br> <sup>¹ [Requires emulation](https://cibuildwheel.pypa.io/en/stable/faq/#emulation), distributed separately. Other services may also support Linux ARM through emulation or third-party build hosts, but these are not tested in our CI.</sup><br>
<sup>² [Uses cross-compilation](https://cibuildwheel.pypa.io/en/stable/faq/#windows-arm64). It is not possible to test `arm64` on this CI platform.</sup><br> <sup>² [Uses cross-compilation](https://cibuildwheel.pypa.io/en/stable/faq/#windows-arm64). It is not possible to test `arm64` on this CI platform.</sup><br>
<sup>³ Requires a macOS runner; runs tests on the simulator for the runner's architecture.</sup> <sup>³ Requires a macOS runner; runs tests on the simulator for the runner's architecture.</sup><br>
<sup>⁴ Building for Android requires the runner to be Linux x86_64, macOS ARM64 or macOS x86_64. Testing has [additional requirements](https://cibuildwheel.pypa.io/en/stable/platforms/#android).</sup><br>
<!--intro-end--> <!--intro-end-->
Example setup Example setup
@@ -88,7 +88,7 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, macos-13, macos-latest] os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, windows-11-arm, macos-13, macos-latest]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -97,7 +97,7 @@ jobs:
- uses: actions/setup-python@v5 - uses: actions/setup-python@v5
- name: Install cibuildwheel - name: Install cibuildwheel
run: python -m pip install cibuildwheel==3.0.0b5 run: python -m pip install cibuildwheel==3.1.2
- name: Build wheels - name: Build wheels
run: python -m cibuildwheel --output-dir wheelhouse run: python -m cibuildwheel --output-dir wheelhouse
@@ -151,7 +151,7 @@ The following diagram summarises the steps that cibuildwheel takes on each platf
| | [`pyodide-version`](https://cibuildwheel.pypa.io/en/stable/options/#pyodide-version) | Specify the Pyodide version to use for `pyodide` platform builds | | | [`pyodide-version`](https://cibuildwheel.pypa.io/en/stable/options/#pyodide-version) | Specify the Pyodide version to use for `pyodide` platform builds |
| **Testing** | [`test-command`](https://cibuildwheel.pypa.io/en/stable/options/#test-command) | The command to test each built wheel | | **Testing** | [`test-command`](https://cibuildwheel.pypa.io/en/stable/options/#test-command) | The command to test each built wheel |
| | [`before-test`](https://cibuildwheel.pypa.io/en/stable/options/#before-test) | Execute a shell command before testing each wheel | | | [`before-test`](https://cibuildwheel.pypa.io/en/stable/options/#before-test) | Execute a shell command before testing each wheel |
| | [`test-sources`](https://cibuildwheel.pypa.io/en/stable/options/#test-sources) | Files and folders from the source tree that are copied into an isolated tree before running the tests | | | [`test-sources`](https://cibuildwheel.pypa.io/en/stable/options/#test-sources) | Paths that are copied into the working directory of the tests |
| | [`test-requires`](https://cibuildwheel.pypa.io/en/stable/options/#test-requires) | Install Python dependencies before running the tests | | | [`test-requires`](https://cibuildwheel.pypa.io/en/stable/options/#test-requires) | Install Python dependencies before running the tests |
| | [`test-extras`](https://cibuildwheel.pypa.io/en/stable/options/#test-extras) | Install your wheel for testing using `extras_require` | | | [`test-extras`](https://cibuildwheel.pypa.io/en/stable/options/#test-extras) | Install your wheel for testing using `extras_require` |
| | [`test-groups`](https://cibuildwheel.pypa.io/en/stable/options/#test-groups) | Specify test dependencies from your project's `dependency-groups` | | | [`test-groups`](https://cibuildwheel.pypa.io/en/stable/options/#test-groups) | Specify test dependencies from your project's `dependency-groups` |
@@ -162,7 +162,7 @@ The following diagram summarises the steps that cibuildwheel takes on each platf
| | [`build-verbosity`](https://cibuildwheel.pypa.io/en/stable/options/#build-verbosity) | Increase/decrease the output of the build | | | [`build-verbosity`](https://cibuildwheel.pypa.io/en/stable/options/#build-verbosity) | Increase/decrease the output of the build |
<!--[[[end]]] (checksum: 4d6a8418630e9ed43251973d93798a1b) --> <!--[[[end]]] (sum: FxE3nIgFiY) -->
These options can be specified in a pyproject.toml file, or as environment variables, see [configuration docs](https://cibuildwheel.pypa.io/en/latest/configuration/). These options can be specified in a pyproject.toml file, or as environment variables, see [configuration docs](https://cibuildwheel.pypa.io/en/latest/configuration/).
@@ -227,55 +227,92 @@ Changelog
<!-- [[[cog from readme_changelog import mini_changelog; print(mini_changelog()) ]]] --> <!-- [[[cog from readme_changelog import mini_changelog; print(mini_changelog()) ]]] -->
### v3.1.2
_29 July 2025_
- ⚠️ Add an error if `CIBW_FREE_THREADING_SUPPORT` is set; you are likely missing 3.13t wheels, please use the `enable`/`CIBW_ENABLE` (#2520)
- 🛠 `riscv64` now enabled if you target that architecture, it's now supported on PyPI (#2509)
- 🛠 Add warning when using `cpython-experimental-riscv64` (no longer needed) (#2526, #2528)
- 🛠 iOS versions bumped, fixing issues with 3.14 (now RC 1) (#2530)
- 🐛 Fix bug in Android running wheel from our GitHub Action (#2517)
- 🐛 Fix warning when using `test-skip` of `"*-macosx_universal2:arm64"` (#2522)
- 🐛 Fix incorrect number of wheels reported in logs, again (#2517)
- 📚 We welcome our Android platform maintainer (#2516)
### v3.1.1
_24 July 2025_
- 🐛 Fix a bug showing an incorrect wheel count at the end of execution, and misrepresenting test-only runs in the GitHub Action summary (#2512)
- 📚 Docs fix (#2510)
### v3.1.0
_23 July 2025_
- 🌟 CPython 3.14 wheels are now built by default - without the `"cpython-prerelease"` `enable` set. It's time to build and upload these wheels to PyPI! This release includes CPython 3.14.0rc1, which is guaranteed to be ABI compatible with the final release. (#2507) Free-threading is no longer experimental in 3.14, so you have to skip it explicitly with `'cp31?t-*'` if you don't support it yet. (#2503)
- 🌟 Adds the ability to [build wheels for Android](https://cibuildwheel.pypa.io/en/stable/platforms/#android)! Set the [`platform` option](https://cibuildwheel.pypa.io/en/stable/options/#platform) to `android` on Linux or macOS to try it out! (#2349)
- 🌟 Adds Pyodide 0.28, which builds 3.13 wheels (#2487)
- ✨ Support for 32-bit `manylinux_2_28` (now a consistent default) and `manylinux_2_34` added (#2500)
- 🛠 Improved summary, will also use markdown summary output on GHA (#2469)
- 🛠 The riscv64 images now have a working default (as they are now part of pypy/manylinux), but are still experimental (and behind an `enable`) since you can't push them to PyPI yet (#2506)
- 🛠 Fixed a typo in the 3.9 MUSL riscv64 identifier (`cp39-musllinux_ricv64` -> `cp39-musllinux_riscv64`) (#2490)
- 🛠 Mistyping `--only` now shows the correct possibilities, and even suggests near matches on Python 3.14+ (#2499)
- 🛠 Only support one output from the repair step on linux like other platforms; auditwheel fixed this over four years ago! (#2478)
- 🛠 We now use pattern matching extensively (#2434)
- 📚 We now have platform maintainers for our special platforms and interpreters! (#2481)
### v3.0.1
_5 July 2025_
- 🛠 Updates CPython 3.14 prerelease to 3.14.0b3 (#2471)
- ✨ Adds a CPython 3.14 prerelease iOS build (only when prerelease builds are [enabled](https://cibuildwheel.pypa.io/en/stable/options/#enable)) (#2475)
### v3.0.0 ### v3.0.0
Not yet released, but available for testing. _11 June 2025_
Note - when using a beta version, be sure to check the [latest docs](https://cibuildwheel.pypa.io/en/latest/), rather than the stable version, which is still on v2.X. See @henryiii's [release post](https://iscinumpy.dev/post/cibuildwheel-3-0-0/) for more info on new features!
<!-- - 🌟 Adds the ability to [build wheels for iOS](https://cibuildwheel.pypa.io/en/stable/platforms/#ios)! Set the [`platform` option](https://cibuildwheel.pypa.io/en/stable/options/#platform) to `ios` on a Mac with the iOS toolchain to try it out! (#2286, #2363, #2432)
note to self, when doing final release, change to docs URLs in this section to the stable version! - 🌟 Adds support for the GraalPy interpreter! Enable for your project using the [`enable` option](https://cibuildwheel.pypa.io/en/stable/options/#enable). (#1538, #2411, #2414)
--> - ✨ Adds CPython 3.14 support, under the [`enable` option](https://cibuildwheel.pypa.io/en/stable/options/#enable) `cpython-prerelease`. This version of cibuildwheel uses 3.14.0b2. (#2390)
If you've used previous versions of the beta: _While CPython is in beta, the ABI can change, so your wheels might not be compatible with the final release. For this reason, we don't recommend distributing wheels until RC1, at which point 3.14 will be available in cibuildwheel without the flag._ (#2390)
- ⚠️ Previous betas of v3.0 changed the working directory for tests. This has been rolled back to the v2.x behaviour, so you might need to change configs if you adapted to the beta 1 or 2 behaviour. See [issue #2406](https://github.com/pypa/cibuildwheel/issues/2406) for more information.
- ⚠️ GraalPy shipped with the identifier `gp242-*` in previous betas, this has been changed to `gp311_242-*` to be consistent with other interpreters, and to fix a bug with GraalPy and project requires-python detection. If you were using GraalPy, you might need to update your config to use the new identifier.
- ⚠️ `test-sources` now uses `project` directory instead of the `package` directory (matching the docs).
#### v3.0.0b5 - ✨ Adds the [test-sources option](https://cibuildwheel.pypa.io/en/stable/options/#test-sources), which copies files and folders into the temporary working directory we run tests from. (#2062, #2284, #2420, #2437)
_3 June 2025_ This is particularly important for iOS builds, which do not support placeholders in the `test-command`, but can also be useful for other platforms.
- ✨ Support multiple commands on iOS, joined by `&&`, like the other platforms. (#2432) - ✨ Adds [`dependency-versions`](https://cibuildwheel.pypa.io/en/stable/options/#dependency-versions) inline syntax (#2122)
- ✨ Add `pyodide-prerelease` enable option, with an early build of 0.28 (Python 3.13). (#2431) - ✨ Improves support for Pyodide builds and adds the experimental [`pyodide-version`](https://cibuildwheel.pypa.io/en/stable/options/#pyodide-version) option, which allows you to specify the version of Pyodide to use for builds. (#2002)
- 🛠 test-sources now uses the `project` directory instead of the `package` directory (matching the docs). (#2437) - ✨ Add `pyodide-prerelease` [enable](https://cibuildwheel.pypa.io/en/stable/options/#enable) option, with an early build of 0.28 (Python 3.13). (#2431)
- 🛠 Fixed a bug with GraalPy if vsdevcmd prints an error. Cirrus CI works again. (#2414) - ✨ Adds the [`test-environment`](https://cibuildwheel.pypa.io/en/stable/options/#test-environment) option, which allows you to set environment variables for the test command. (#2388)
- ✨ Adds the [`xbuild-tools`](https://cibuildwheel.pypa.io/en/stable/options/#xbuild-tools) option, which allows you to specify tools safe for cross-compilation. Currently only used on iOS; will be useful for Android in the future. (#2317)
- 🛠 The default [manylinux image](https://cibuildwheel.pypa.io/en/stable/options/#linux-image) has changed from `manylinux2014` to `manylinux_2_28`. (#2330)
- 🛠 EOL images `manylinux1`, `manylinux2010`, `manylinux_2_24` and `musllinux_1_1` can no longer be specified by their shortname. The full OCI name can still be used for these images, if you wish. (#2316)
- 🛠 Invokes `build` rather than `pip wheel` to build wheels by default. You can control this via the [`build-frontend`](https://cibuildwheel.pypa.io/en/stable/options/#build-frontend) option. You might notice that you can see your build log output now! (#2321)
- 🛠 Build verbosity settings have been reworked to have consistent meanings between build backends when non-zero. (#2339)
- 🛠 Removed the `CIBW_PRERELEASE_PYTHONS` and `CIBW_FREE_THREADED_SUPPORT` options - these have been folded into the [`enable`](https://cibuildwheel.pypa.io/en/stable/options/#enable) option instead. (#2095)
- 🛠 Build environments no longer have setuptools and wheel preinstalled. (#2329)
- 🛠 Use the standard Schema line for the integrated JSONSchema. (#2433) - 🛠 Use the standard Schema line for the integrated JSONSchema. (#2433)
- ⚠️ Dropped support for building Python 3.6 and 3.7 wheels. If you need to build wheels for these versions, use cibuildwheel v2.23.3 or earlier. (#2282)
- ⚠️ The minimum Python version required to run cibuildwheel is now Python 3.11. You can still build wheels for Python 3.8 and newer. (#1912)
- ⚠️ 32-bit Linux wheels no longer built by default - the [arch](https://cibuildwheel.pypa.io/en/stable/options/#archs) was removed from `"auto"`. It now requires explicit `"auto32"`. Note that modern manylinux images (like the new default, `manylinux_2_28`) do not have 32-bit versions. (#2458)
- ⚠️ PyPy wheels no longer built by default, due to a change to our options system. To continue building PyPy wheels, you'll now need to set the [`enable` option](https://cibuildwheel.pypa.io/en/stable/options/#enable) to `pypy` or `pypy-eol`. (#2095)
- ⚠️ Dropped official support for Appveyor. If it was working for you before, it will probably continue to do so, but we can't be sure, because our CI doesn't run there anymore. (#2386)
- 📚 A reorganisation of the docs, and numerous updates. (#2280)
- 📚 Use Python 3.14 color output in docs CLI output. (#2407) - 📚 Use Python 3.14 color output in docs CLI output. (#2407)
- 📚 Docs now primarily use the pyproject.toml name of options, rather than the environment variable name. (#2389)
#### v3.0.0b4
_29 May 2025_
- 🛠 Dependency updates, including Python 3.14.0b2. (#2371)
- 🛠 Remove the addition of `PYTHONSAFEPATH` to `test-environment`. (#2429)
- 📚 README table now matches docs and auto-updates. (#2427, #2428) - 📚 README table now matches docs and auto-updates. (#2427, #2428)
#### v3.0.0b3 <!-- [[[end]]] (sum: sVC5DNuhaF) -->
_28 May 2025_
- 🛠 Reverts the test working dir (when test-sources isn't set) to a temporary dir, rather than the project. (#2420)
- 📚 Docs now primarily use the pyproject.toml name of options, rather than the environment variable name. (#2389)
#### v3.0.0b2
_25 May 2025_
- ✨ Adds the [`CIBW_TEST_ENVIRONMENT`](https://cibuildwheel.pypa.io/en/latest/options/#test-environment) option, which allows you to set environment variables for the test command. cibuildwheel now sets `PYTHONSAFEPATH=1` in test environments by default, to avoid picking up package imports from the local directory - we want to test the installed wheel, not the source tree! You can change that, or any other environment variable in the test environment using this option. (#2388)
- ✨ Improves support for Pyodide builds and adds the [`CIBW_PYODIDE_VERSION`](https://cibuildwheel.pypa.io/en/latest/options/#pyodide-version) option, which allows you to specify the version of Pyodide to use for builds. (#2002)
<!-- [[[end]]] (checksum: c95d170eee4d70055d927723c033426a) -->
--- ---
@@ -295,12 +332,23 @@ Everyone interacting with the cibuildwheel project via codebase, issue tracker,
Maintainers Maintainers
----------- -----------
Core:
- Joe Rickerby [@joerick](https://github.com/joerick) - Joe Rickerby [@joerick](https://github.com/joerick)
- Yannick Jadoul [@YannickJadoul](https://github.com/YannickJadoul) - Yannick Jadoul [@YannickJadoul](https://github.com/YannickJadoul)
- Matthieu Darbois [@mayeut](https://github.com/mayeut) - Matthieu Darbois [@mayeut](https://github.com/mayeut)
- Henry Schreiner [@henryiii](https://github.com/henryiii) - Henry Schreiner [@henryiii](https://github.com/henryiii)
- Grzegorz Bokota [@Czaki](https://github.com/Czaki) - Grzegorz Bokota [@Czaki](https://github.com/Czaki)
Platform maintainers:
- Russell Keith-Magee [@freakboy3742](https://github.com/freakboy3742) (iOS)
- Agriya Khetarpal [@agriyakhetarpal](https://github.com/agriyakhetarpal) (Pyodide)
- Hood Chatham [@hoodmane](https://github.com/hoodmane) (Pyodide)
- Gyeongjae Choi [@ryanking13](https://github.com/ryanking13) (Pyodide)
- Tim Felgentreff [@timfel](https://github.com/timfel) (GraalPy)
- Malcolm Smith [@mhsmith](https://github.com/mhsmith) (Android)
Credits Credits
------- -------
+26 -10
View File
@@ -1,21 +1,37 @@
pr: pr:
paths: paths:
exclude: exclude:
- docs/* - .github/**
- bin/*
- docs/**
- examples/github-*
- examples/ci*
- examples/travis-ci-*
- .ci*
- .pre-commit-config.yaml - .pre-commit-config.yaml
- .readthedocs.yml
- .travis.yml
- README.md
- mkdocs.yml
- noxfile.py
jobs: jobs:
- job: linux_311 - job: linux_311
timeoutInMinutes: 180 timeoutInMinutes: 180
pool: {vmImage: 'Ubuntu-22.04'} pool: {vmImage: 'ubuntu-latest'}
steps: steps:
- task: UsePythonVersion@0 - task: UsePythonVersion@0
inputs: inputs:
versionSpec: '3.11' versionSpec: '3.11'
- task: JavaToolInstaller@0
inputs:
versionSpec: '17'
jdkArchitectureOption: 'x64'
jdkSourceOption: 'PreInstalled'
- bash: | - bash: |
docker run --rm --privileged docker.io/tonistiigi/binfmt:latest --install all docker run --rm --privileged docker.io/tonistiigi/binfmt:latest --install all
python -m pip install dependency-groups python -m pip install -U pip
python -m dependency_groups test | xargs python -m pip install -e. python -m pip install -e. --group test
if [ "$(Build.SourceBranch)" = "refs/heads/main" ]; then if [ "$(Build.SourceBranch)" = "refs/heads/main" ]; then
echo "INFO: Exporting CIBW_ENABLE=all for main branch test run." echo "INFO: Exporting CIBW_ENABLE=all for main branch test run."
export CIBW_ENABLE=all export CIBW_ENABLE=all
@@ -25,15 +41,15 @@ jobs:
python ./bin/run_tests.py python ./bin/run_tests.py
- job: macos_311 - job: macos_311
pool: {vmImage: 'macOS-13'} pool: {vmImage: 'macOS-latest'}
timeoutInMinutes: 120 timeoutInMinutes: 120
steps: steps:
- task: UsePythonVersion@0 - task: UsePythonVersion@0
inputs: inputs:
versionSpec: '3.11' versionSpec: '3.11'
- bash: | - bash: |
python -m pip install dependency-groups python -m pip install -U pip
python -m dependency_groups test | xargs python -m pip install -e. python -m pip install -e. --group test
if [ "$(Build.SourceBranch)" = "refs/heads/main" ]; then if [ "$(Build.SourceBranch)" = "refs/heads/main" ]; then
echo "INFO: Exporting CIBW_ENABLE=all for main branch test run." echo "INFO: Exporting CIBW_ENABLE=all for main branch test run."
export CIBW_ENABLE=all export CIBW_ENABLE=all
@@ -43,15 +59,15 @@ jobs:
python ./bin/run_tests.py python ./bin/run_tests.py
- job: windows_311 - job: windows_311
pool: {vmImage: 'windows-2019'} pool: {vmImage: 'windows-latest'}
timeoutInMinutes: 180 timeoutInMinutes: 180
steps: steps:
- task: UsePythonVersion@0 - task: UsePythonVersion@0
inputs: inputs:
versionSpec: '3.11' versionSpec: '3.11'
- bash: | - bash: |
python -m pip install dependency-groups python -m pip install -U pip
python -m dependency_groups test | xargs python -m pip install -e. python -m pip install -e. --group test
if [ "$(Build.SourceBranch)" = "refs/heads/main" ]; then if [ "$(Build.SourceBranch)" = "refs/heads/main" ]; then
echo "INFO: Exporting CIBW_ENABLE=all for main branch test run." echo "INFO: Exporting CIBW_ENABLE=all for main branch test run."
export CIBW_ENABLE=all export CIBW_ENABLE=all
+3 -3
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# /// script # /// script
# dependencies = ["click", "packaging", "tomli; python_version<'3.11'"] # dependencies = ["click", "packaging"]
# /// # ///
@@ -129,8 +129,8 @@ def bump_version() -> None:
contents = contents.replace(find, replace) contents = contents.replace(find, replace)
path.write_text(contents, encoding="utf8") path.write_text(contents, encoding="utf8")
print("Files updated. If you want to update the changelog as part of this") print("Files updated. If you want to update docs/changelog.md as part of")
print("commit, do that now.") print("this commit, do that now.")
print() print()
while input('Type "done" to continue: ').strip().lower() != "done": while input('Type "done" to continue: ').strip().lower() != "done":
+4 -3
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env -S uv run -q
# /// script # /// script
# dependencies = ["pyyaml"] # dependencies = ["pyyaml"]
@@ -63,7 +63,7 @@ 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), "build", or "build[uv]" description: Set the tool to use to build, either "build" (default), "build[uv]", or "pip"
oneOf: oneOf:
- enum: [pip, build, "build[uv]", default] - enum: [pip, build, "build[uv]", default]
- type: string - type: string
@@ -348,6 +348,7 @@ oses = {
"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), "pyodide": as_object(not_linux),
"android": as_object(not_linux),
"ios": as_object(not_linux), "ios": as_object(not_linux),
} }
@@ -368,7 +369,7 @@ schema["properties"] |= oses
if args.schemastore: if args.schemastore:
schema["$id"] = "https://json.schemastore.org/partial-cibuildwheel.json" schema["$id"] = "https://json.schemastore.org/partial-cibuildwheel.json"
schema["description"] = ( schema["description"] = (
"cibuildwheel's toml file, generated with ./bin/generate_schema.py --schemastore from cibuildwheel." "cibuildwheel's settings. Generated with ./bin/generate_schema.py --schemastore from cibuildwheel."
) )
print(json.dumps(schema, indent=2)) print(json.dumps(schema, indent=2))
+1 -1
View File
@@ -28,7 +28,7 @@ def git_repo_has_changes() -> bool:
def generate_basic_project(path: Path) -> None: def generate_basic_project(path: Path) -> None:
sys.path.insert(0, "") sys.path.insert(0, "")
from test.test_projects.c import new_c_project from test.test_projects.c import new_c_project # noqa: PLC0415
project = new_c_project() project = new_c_project()
project.generate(path) project.generate(path)
+27 -6
View File
@@ -43,16 +43,40 @@ images = [
), ),
# manylinux_2_28 images # manylinux_2_28 images
PyPAImage( PyPAImage(
"manylinux_2_28", ["x86_64", "aarch64", "ppc64le", "s390x", "pypy_x86_64", "pypy_aarch64"] "manylinux_2_28",
[
"x86_64",
"i686",
"aarch64",
"ppc64le",
"s390x",
"pypy_x86_64",
"pypy_i686",
"pypy_aarch64",
],
), ),
# manylinux_2_31 images # manylinux_2_31 images
PyPAImage("manylinux_2_31", ["armv7l"]), PyPAImage("manylinux_2_31", ["armv7l"]),
# manylinux_2_34 images # manylinux_2_34 images
PyPAImage( PyPAImage(
"manylinux_2_34", ["x86_64", "aarch64", "ppc64le", "s390x", "pypy_x86_64", "pypy_aarch64"] "manylinux_2_34",
[
"x86_64",
"i686",
"aarch64",
"ppc64le",
"s390x",
"pypy_x86_64",
"pypy_i686",
"pypy_aarch64",
],
), ),
# manylinux_2_39 images
PyPAImage("manylinux_2_39", ["riscv64"]),
# musllinux_1_2 images # musllinux_1_2 images
PyPAImage("musllinux_1_2", ["x86_64", "i686", "aarch64", "ppc64le", "s390x", "armv7l"]), PyPAImage(
"musllinux_1_2", ["x86_64", "i686", "aarch64", "ppc64le", "s390x", "armv7l", "riscv64"]
),
] ]
config = configparser.ConfigParser() config = configparser.ConfigParser()
@@ -116,8 +140,5 @@ for image in images:
suffix = f"_{platform.removeprefix('pypy_')}" suffix = f"_{platform.removeprefix('pypy_')}"
config[platform][image.manylinux_version] = f"{image.image_name}{suffix}:{tag_name}" config[platform][image.manylinux_version] = f"{image.image_name}{suffix}:{tag_name}"
if not config.has_section("riscv64"):
config["riscv64"] = {}
with open(RESOURCES / "pinned_docker_images.cfg", "w") as f: with open(RESOURCES / "pinned_docker_images.cfg", "w") as f:
config.write(f) config.write(f)
+10 -10
View File
@@ -25,25 +25,25 @@ def main() -> None:
f"repos/astral-sh/python-build-standalone/releases/tags/{latest_tag}" f"repos/astral-sh/python-build-standalone/releases/tags/{latest_tag}"
)["assets"] )["assets"]
assets: list[PythonBuildStandaloneAsset] = [] assets = [
PythonBuildStandaloneAsset(name=ga["name"], url=ga["browser_download_url"])
for ga in github_assets
if ga["name"].endswith("install_only.tar.gz")
]
for github_asset in github_assets: # Try to keep output order stable
name = github_asset["name"] assets = sorted(assets, key=lambda x: x["name"])
if not name.endswith("install_only.tar.gz"):
continue
url = github_asset["browser_download_url"]
assets.append({"name": name, "url": url})
# Write the assets to the JSON file. One day, we might need to support # Write the assets to the JSON file. One day, we might need to support
# multiple releases, but for now, we only support the latest one # multiple releases, but for now, we only support the latest one
json_file_contents: PythonBuildStandaloneReleaseData = { json_file_contents = PythonBuildStandaloneReleaseData(
"releases": [ releases=[
{ {
"tag": latest_tag, "tag": latest_tag,
"assets": assets, "assets": assets,
} }
] ]
} )
with PYTHON_BUILD_STANDALONE_RELEASES.open("w", encoding="utf-8") as f: with PYTHON_BUILD_STANDALONE_RELEASES.open("w", encoding="utf-8") as f:
json.dump(json_file_contents, f, indent=2) json.dump(json_file_contents, f, indent=2)
+48 -12
View File
@@ -10,6 +10,7 @@ import tomllib
from collections.abc import Mapping, MutableMapping from collections.abc import Mapping, MutableMapping
from pathlib import Path from pathlib import Path
from typing import Any, Final, Literal, TypedDict from typing import Any, Final, Literal, TypedDict
from xml.etree import ElementTree as ET
import click import click
import requests import requests
@@ -20,6 +21,7 @@ from rich.logging import RichHandler
from rich.syntax import Syntax from rich.syntax import Syntax
from cibuildwheel.extra import dump_python_configurations, get_pyodide_xbuildenv_info from cibuildwheel.extra import dump_python_configurations, get_pyodide_xbuildenv_info
from cibuildwheel.platforms.android import android_triplet
log = logging.getLogger("cibw") log = logging.getLogger("cibw")
@@ -57,6 +59,12 @@ class ConfigApple(TypedDict):
url: str url: str
class ConfigAndroid(TypedDict):
identifier: str
version: str
url: str
class ConfigPyodide(TypedDict): class ConfigPyodide(TypedDict):
identifier: str identifier: str
version: str version: str
@@ -64,7 +72,7 @@ class ConfigPyodide(TypedDict):
node_version: str node_version: str
AnyConfig = ConfigWinCP | ConfigWinPP | ConfigWinGP | ConfigApple | ConfigPyodide AnyConfig = ConfigWinCP | ConfigWinPP | ConfigWinGP | ConfigApple | ConfigAndroid | ConfigPyodide
# The following set of "Versions" classes allow the initial call to the APIs to # The following set of "Versions" classes allow the initial call to the APIs to
@@ -320,6 +328,39 @@ class CPythonVersions:
return None return None
class AndroidVersions:
# This should be replaced with official python.org downloads once they're available.
MAVEN_URL = "https://repo.maven.apache.org/maven2/com/chaquo/python/python"
def __init__(self) -> None:
response = requests.get(f"{self.MAVEN_URL}/maven-metadata.xml")
response.raise_for_status()
root = ET.fromstring(response.text)
self.versions: list[Version] = []
for version_elem in root.findall("./versioning/versions/version"):
version_str = version_elem.text
assert isinstance(version_str, str), version_str
self.versions.append(Version(version_str))
def update_version_android(
self, identifier: str, version: Version, spec: Specifier
) -> ConfigAndroid | None:
sorted_versions = sorted(spec.filter(self.versions), reverse=True)
# Return a config using the highest version for the given specifier.
if sorted_versions:
max_version = sorted_versions[0]
triplet = android_triplet(identifier)
return ConfigAndroid(
identifier=identifier,
version=str(version),
url=f"{self.MAVEN_URL}/{max_version}/python-{max_version}-{triplet}.tar.gz",
)
else:
return None
class CPythonIOSVersions: class CPythonIOSVersions:
def __init__(self) -> None: def __init__(self) -> None:
response = requests.get( response = requests.get(
@@ -413,6 +454,7 @@ class AllVersions:
self.macos_pypy = PyPyVersions("64") self.macos_pypy = PyPyVersions("64")
self.macos_pypy_arm64 = PyPyVersions("ARM64") self.macos_pypy_arm64 = PyPyVersions("ARM64")
self.android = AndroidVersions()
self.ios_cpython = CPythonIOSVersions() self.ios_cpython = CPythonIOSVersions()
self.graalpy = GraalPyVersions() self.graalpy = GraalPyVersions()
@@ -455,6 +497,8 @@ class AllVersions:
config_update = self.windows_t_arm64.update_version_windows(spec) config_update = self.windows_t_arm64.update_version_windows(spec)
elif "win_arm64" in identifier and identifier.startswith("cp"): elif "win_arm64" in identifier and identifier.startswith("cp"):
config_update = self.windows_arm64.update_version_windows(spec) config_update = self.windows_arm64.update_version_windows(spec)
elif "android" in identifier:
config_update = self.android.update_version_android(identifier, version, spec)
elif "ios" in identifier: elif "ios" in identifier:
config_update = self.ios_cpython.update_version_ios(identifier, version) config_update = self.ios_cpython.update_version_ios(identifier, version)
elif "pyodide" in identifier: elif "pyodide" in identifier:
@@ -490,17 +534,9 @@ def update_pythons(force: bool, level: str) -> None:
with toml_file_path.open("rb") as f: with toml_file_path.open("rb") as f:
configs = tomllib.load(f) configs = tomllib.load(f)
for config in configs["windows"]["python_configurations"]: for platform in ["windows", "macos", "android", "ios", "pyodide"]:
all_versions.update_config(config) for config in configs[platform]["python_configurations"]:
all_versions.update_config(config)
for config in configs["macos"]["python_configurations"]:
all_versions.update_config(config)
for config in configs["ios"]["python_configurations"]:
all_versions.update_config(config)
for config in configs["pyodide"]["python_configurations"]:
all_versions.update_config(config)
result_toml = dump_python_configurations(configs) result_toml = dump_python_configurations(configs)
+1 -1
View File
@@ -1 +1 @@
__version__ = "3.0.0b5" __version__ = "3.1.2"
+67 -83
View File
@@ -7,7 +7,6 @@ import shutil
import sys import sys
import tarfile import tarfile
import textwrap import textwrap
import time
import traceback import traceback
import typing import typing
from collections.abc import Generator, Iterable, Sequence from collections.abc import Generator, Iterable, Sequence
@@ -22,11 +21,12 @@ from cibuildwheel.architecture import Architecture, allowed_architectures_check
from cibuildwheel.ci import CIProvider, detect_ci_provider, fix_ansi_codes_for_github_actions from cibuildwheel.ci import CIProvider, detect_ci_provider, fix_ansi_codes_for_github_actions
from cibuildwheel.logger import log from cibuildwheel.logger import log
from cibuildwheel.options import CommandLineArguments, Options, compute_options from cibuildwheel.options import CommandLineArguments, Options, compute_options
from cibuildwheel.platforms import ALL_PLATFORM_MODULES, get_build_identifiers from cibuildwheel.platforms import ALL_PLATFORM_MODULES, get_build_identifiers, native_platform
from cibuildwheel.selector import BuildSelector, EnableGroup, selector_matches from cibuildwheel.selector import BuildSelector, EnableGroup, selector_matches
from cibuildwheel.typing import PLATFORMS, PlatformName from cibuildwheel.typing import PLATFORMS, PlatformName
from cibuildwheel.util.file import CIBW_CACHE_PATH from cibuildwheel.util.file import CIBW_CACHE_PATH
from cibuildwheel.util.helpers import strtobool from cibuildwheel.util.helpers import strtobool
from cibuildwheel.util.resources import read_all_configs
@dataclasses.dataclass @dataclasses.dataclass
@@ -94,14 +94,14 @@ def main_inner(global_options: GlobalOptions) -> None:
parser.add_argument( parser.add_argument(
"--platform", "--platform",
choices=["auto", "linux", "macos", "windows", "pyodide", "ios"], choices=["auto", "linux", "macos", "windows", "pyodide", "android", "ios"],
default=None, default=None,
help=""" help="""
Platform to build for. Use this option to override the auto-detected Platform to build for. Use this option to override the auto-detected
platform. Specifying "macos" or "windows" only works on that platform. Specifying "macos" or "windows" only works on that
operating system. "linux" works on any desktop OS, as long as operating system. "linux" works on any desktop OS, as long as
Docker/Podman is installed. "pyodide" only works on linux and macOS. Docker/Podman is installed. "pyodide" and "android" only work on
"ios" only work on macOS. Default: auto. Linux and macOS. "ios" only works on macOS. Default: auto.
""", """,
) )
@@ -133,6 +133,8 @@ def main_inner(global_options: GlobalOptions) -> None:
parser.add_argument( parser.add_argument(
"--only", "--only",
default=None, default=None,
choices=[v["identifier"] for vv in read_all_configs().values() for v in vv],
metavar="IDENTIFIER",
help=""" help="""
Force a single wheel build when given an identifier. Overrides Force a single wheel build when given an identifier. Overrides
CIBW_BUILD/CIBW_SKIP. --platform and --arch cannot be specified CIBW_BUILD/CIBW_SKIP. --platform and --arch cannot be specified
@@ -242,28 +244,14 @@ def _compute_platform_only(only: str) -> PlatformName:
return "windows" return "windows"
if "pyodide_" in only: if "pyodide_" in only:
return "pyodide" return "pyodide"
if "android_" in only:
return "android"
if "ios_" in only: if "ios_" in only:
return "ios" return "ios"
msg = f"Invalid --only='{only}', must be a build selector with a known platform" msg = f"Invalid --only='{only}', must be a build selector with a known platform"
raise errors.ConfigurationError(msg) raise errors.ConfigurationError(msg)
def _compute_platform_auto() -> PlatformName:
if sys.platform.startswith("linux"):
return "linux"
elif sys.platform == "darwin":
return "macos"
elif sys.platform == "win32":
return "windows"
else:
msg = (
'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 "
"platform using the --platform argument. Check --help output for more information."
)
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", "") or "auto" platform_option_value = args.platform or os.environ.get("CIBW_PLATFORM", "") or "auto"
@@ -283,43 +271,7 @@ def _compute_platform(args: CommandLineArguments) -> PlatformName:
elif platform_option_value != "auto": elif platform_option_value != "auto":
return typing.cast(PlatformName, platform_option_value) return typing.cast(PlatformName, platform_option_value)
return _compute_platform_auto() return native_platform()
@contextlib.contextmanager
def print_new_wheels(msg: str, output_dir: Path) -> Generator[None, None, None]:
"""
Prints the new items in a directory upon exiting. The message to display
can include {n} for number of wheels, {s} for total number of seconds,
and/or {m} for total number of minutes. Does not print anything if this
exits via exception.
"""
start_time = time.time()
existing_contents = set(output_dir.iterdir())
yield
final_contents = set(output_dir.iterdir())
new_contents = [
FileReport(wheel.name, f"{(wheel.stat().st_size + 1023) // 1024:,d}")
for wheel in final_contents - existing_contents
]
if not new_contents:
return
max_name_len = max(len(f.name) for f in new_contents)
max_size_len = max(len(f.size) for f in new_contents)
n = len(new_contents)
s = time.time() - start_time
m = s / 60
print(
msg.format(n=n, s=s, m=m),
*sorted(
f" {f.name:<{max_name_len}s} {f.size:>{max_size_len}s} kB" for f in new_contents
),
sep="\n",
)
def build_in_directory(args: CommandLineArguments) -> None: def build_in_directory(args: CommandLineArguments) -> None:
@@ -382,7 +334,7 @@ def build_in_directory(args: CommandLineArguments) -> None:
tmp_path = Path(mkdtemp(prefix="cibw-run-")).resolve(strict=True) tmp_path = Path(mkdtemp(prefix="cibw-run-")).resolve(strict=True)
try: try:
with print_new_wheels("\n{n} wheels produced in {m:.0f} minutes:", output_dir): with log.print_summary(options=options):
platform_module.build(options, tmp_path) platform_module.build(options, tmp_path)
finally: finally:
# avoid https://github.com/python/cpython/issues/86962 by performing # avoid https://github.com/python/cpython/issues/86962 by performing
@@ -417,41 +369,61 @@ def print_preamble(platform: str, options: Options, identifiers: Sequence[str])
print(f"Cache folder: {CIBW_CACHE_PATH}") print(f"Cache folder: {CIBW_CACHE_PATH}")
print() print()
warnings = detect_warnings(options=options, identifiers=identifiers) warnings = detect_warnings(options=options)
for warning in warnings: for warning in warnings:
log.warning(warning) log.warning(warning)
error_list = list(detect_errors(options=options, identifiers=identifiers))
if error_list:
for error in error_list:
log.error(error)
msg = "\n".join(error_list)
raise errors.ConfigurationError(msg)
print("Here we go!\n") print("Here we go!\n")
def detect_warnings(*, options: Options, identifiers: Iterable[str]) -> list[str]: def detect_errors(*, options: Options, identifiers: Iterable[str]) -> Generator[str, None, None]:
warnings = [] # Check for deprecated CIBW_FREE_THREADED_SUPPORT environment variable
if "CIBW_FREE_THREADED_SUPPORT" in os.environ:
python_version_deprecation = ((3, 11), 3) yield (
if sys.version_info[:2] < python_version_deprecation[0]: "CIBW_FREE_THREADED_SUPPORT environment variable is no longer supported. "
python_version = ".".join(map(str, python_version_deprecation[0])) 'Use tool.cibuildwheel.enable = ["cpython-freethreading"] in pyproject.toml '
msg = ( "or set CIBW_ENABLE=cpython-freethreading instead."
f"cibuildwheel {python_version_deprecation[1]} will require Python {python_version}+, "
"please upgrade the Python version used to run cibuildwheel. "
"This does not affect the versions you can target when building wheels. See: https://cibuildwheel.pypa.io/en/stable/#what-does-it-do"
) )
warnings.append(msg)
# warn about deprecated {python} and {pip} # Deprecated {python} and {pip}
for option_name in ["test_command", "before_build"]: for option_name in ["test_command", "before_build"]:
option_values = [getattr(options.build_options(i), option_name) for i in identifiers] option_values = [getattr(options.build_options(i), option_name) for i in identifiers]
if any(o and ("{python}" in o or "{pip}" in o) for o in option_values): if any(o and ("{python}" in o or "{pip}" in o) for o in option_values):
# Reminder: in an f-string, double braces means literal single brace # Reminder: in an f-string, double braces means literal single brace
msg = ( yield (
f"{option_name}: '{{python}}' and '{{pip}}' are no longer supported " f"{option_name}: '{{python}}' and '{{pip}}' are no longer supported "
"and have been removed in cibuildwheel 3. Simply use 'python' or 'pip' instead." "and have been removed in cibuildwheel 3. Simply use 'python' or 'pip' instead."
) )
raise errors.ConfigurationError(msg)
def detect_warnings(*, options: Options) -> Generator[str, None, None]:
python_version_deprecation = ((3, 11), 3)
if sys.version_info[:2] < python_version_deprecation[0]:
python_version = ".".join(map(str, python_version_deprecation[0]))
yield (
f"cibuildwheel {python_version_deprecation[1]} will require Python {python_version}+, "
"please upgrade the Python version used to run cibuildwheel. "
"This does not affect the versions you can target when building wheels. See: https://cibuildwheel.pypa.io/en/stable/#what-does-it-do"
)
build_selector = options.globals.build_selector build_selector = options.globals.build_selector
test_selector = options.globals.test_selector test_selector = options.globals.test_selector
if EnableGroup.CPythonExperimentalRiscV64 in build_selector.enable:
yield (
"'cpython-experimental-riscv64' enable is deprecated and will be removed in a future version. "
"It should be removed from tool.cibuildwheel.enable in pyproject.toml "
"or CIBW_ENABLE environment variable."
)
all_valid_identifiers = [ all_valid_identifiers = [
config.identifier config.identifier
for module in ALL_PLATFORM_MODULES.values() for module in ALL_PLATFORM_MODULES.values()
@@ -465,27 +437,25 @@ def detect_warnings(*, options: Options, identifiers: Iterable[str]) -> list[str
identifier for identifier in all_valid_identifiers if enabled_selector(identifier) identifier for identifier in all_valid_identifiers if enabled_selector(identifier)
] ]
warnings += check_for_invalid_selectors( yield from check_for_invalid_selectors(
selector_name="build", selector_name="build",
selector_value=build_selector.build_config, selector_value=build_selector.build_config,
all_valid_identifiers=all_valid_identifiers, all_valid_identifiers=all_valid_identifiers,
all_enabled_identifiers=all_enabled_identifiers, all_enabled_identifiers=all_enabled_identifiers,
) )
warnings += check_for_invalid_selectors( yield from check_for_invalid_selectors(
selector_name="skip", selector_name="skip",
selector_value=build_selector.skip_config, selector_value=build_selector.skip_config,
all_valid_identifiers=all_valid_identifiers, all_valid_identifiers=all_valid_identifiers,
all_enabled_identifiers=all_enabled_identifiers, all_enabled_identifiers=all_enabled_identifiers,
) )
warnings += check_for_invalid_selectors( yield from check_for_invalid_selectors(
selector_name="test_skip", selector_name="test_skip",
selector_value=test_selector.skip_config, selector_value=test_selector.skip_config,
all_valid_identifiers=all_valid_identifiers, all_valid_identifiers=all_valid_identifiers,
all_enabled_identifiers=all_enabled_identifiers, all_enabled_identifiers=all_enabled_identifiers,
) )
return warnings
def check_for_invalid_selectors( def check_for_invalid_selectors(
*, *,
@@ -497,17 +467,31 @@ def check_for_invalid_selectors(
warnings = [] warnings = []
for selector in selector_value.split(): for selector in selector_value.split():
if not any(selector_matches(selector, i) for i in all_enabled_identifiers): selector_ = selector
if selector_name == "test_skip":
# macosx_universal2 uses an additional identifier for tests which ends with ":{arch}"
values = selector.split(":")
universal2_identifiers = filter(
lambda x: x.endswith("-macosx_universal2"), all_valid_identifiers
)
if len(values) == 2 and any(
selector_matches(selector_, f"{i}:{arch}")
for i in universal2_identifiers
for arch in ["arm64", "x86_64"]
):
# just ignore the arch part in the rest of the check
selector_ = values[0]
if not any(selector_matches(selector_, i) for i in all_enabled_identifiers):
msg = f"Invalid {selector_name} selector: {selector!r}. " msg = f"Invalid {selector_name} selector: {selector!r}. "
error_type: type = errors.ConfigurationError error_type: type = errors.ConfigurationError
if any(selector_matches(selector, i) for i in all_valid_identifiers): if any(selector_matches(selector_, i) for i in all_valid_identifiers):
msg += "This selector matches a group that wasn't enabled. Enable it using the `enable` option or remove this selector. " msg += "This selector matches a group that wasn't enabled. Enable it using the `enable` option or remove this selector. "
if "p2" in selector or "p35" in selector: if "p2" in selector_ or "p35" in selector_:
msg += f"cibuildwheel 3.x no longer supports Python < 3.8. Please use the 1.x series or update `{selector_name}`. " msg += f"cibuildwheel 3.x no longer supports Python < 3.8. Please use the 1.x series or update `{selector_name}`. "
error_type = errors.DeprecationError error_type = errors.DeprecationError
if "p36" in selector or "p37" in selector: if "p36" in selector_ or "p37" in selector_:
msg += f"cibuildwheel 3.x no longer supports Python < 3.8. Please use the 2.x series or update `{selector_name}`. " msg += f"cibuildwheel 3.x no longer supports Python < 3.8. Please use the 2.x series or update `{selector_name}`. "
error_type = errors.DeprecationError error_type = errors.DeprecationError
+73 -49
View File
@@ -17,16 +17,25 @@ PRETTY_NAMES: Final[dict[PlatformName, str]] = {
"macos": "macOS", "macos": "macOS",
"windows": "Windows", "windows": "Windows",
"pyodide": "Pyodide", "pyodide": "Pyodide",
"android": "Android",
"ios": "iOS", "ios": "iOS",
} }
ARCH_SYNONYMS: Final[list[dict[PlatformName, str | None]]] = [ ARCH_SYNONYMS: Final[list[dict[PlatformName, str | None]]] = [
{"linux": "x86_64", "macos": "x86_64", "windows": "AMD64"}, {"linux": "x86_64", "macos": "x86_64", "windows": "AMD64", "android": "x86_64"},
{"linux": "i686", "macos": None, "windows": "x86"}, {"linux": "i686", "macos": None, "windows": "x86"},
{"linux": "aarch64", "macos": "arm64", "windows": "ARM64"}, {"linux": "aarch64", "macos": "arm64", "windows": "ARM64", "android": "arm64_v8a"},
] ]
def arch_synonym(arch: str, from_platform: PlatformName, to_platform: PlatformName) -> str | None:
for arch_synonym_ in ARCH_SYNONYMS:
if arch == arch_synonym_.get(from_platform):
return arch_synonym_.get(to_platform, arch)
return arch
def _check_aarch32_el0() -> bool: def _check_aarch32_el0() -> bool:
"""Check if running armv7l natively on aarch64 is supported""" """Check if running armv7l natively on aarch64 is supported"""
if not sys.platform.startswith("linux"): if not sys.platform.startswith("linux"):
@@ -42,7 +51,7 @@ def _check_aarch32_el0() -> bool:
@typing.final @typing.final
class Architecture(StrEnum): class Architecture(StrEnum):
# mac/linux archs # mac/linux/android archs
x86_64 = auto() x86_64 = auto()
# linux archs # linux archs
@@ -65,6 +74,9 @@ class Architecture(StrEnum):
# WebAssembly # WebAssembly
wasm32 = auto() wasm32 = auto()
# android archs
arm64_v8a = auto()
# iOS "multiarch" architectures that include both # iOS "multiarch" architectures that include both
# the CPU architecture and the ABI. # the CPU architecture and the ABI.
arm64_iphoneos = auto() arm64_iphoneos = auto()
@@ -75,24 +87,24 @@ class Architecture(StrEnum):
def parse_config(config: str, platform: PlatformName) -> "set[Architecture]": def parse_config(config: str, platform: PlatformName) -> "set[Architecture]":
result = set() result = set()
for arch_str in re.split(r"[\s,]+", config): for arch_str in re.split(r"[\s,]+", config):
if arch_str == "auto": match arch_str:
result |= Architecture.auto_archs(platform=platform) case "auto":
elif arch_str == "native": result |= Architecture.auto_archs(platform=platform)
native_arch = Architecture.native_arch(platform=platform) case "native":
if native_arch: if native_arch := Architecture.native_arch(platform=platform):
result.add(native_arch) result.add(native_arch)
elif arch_str == "all": case "all":
result |= Architecture.all_archs(platform=platform) result |= Architecture.all_archs(platform=platform)
elif arch_str == "auto64": case "auto64":
result |= Architecture.bitness_archs(platform=platform, bitness="64") result |= Architecture.bitness_archs(platform=platform, bitness="64")
elif arch_str == "auto32": case "auto32":
result |= Architecture.bitness_archs(platform=platform, bitness="32") result |= Architecture.bitness_archs(platform=platform, bitness="32")
else: case _:
try: try:
result.add(Architecture(arch_str)) result.add(Architecture(arch_str))
except ValueError as e: except ValueError as e:
msg = f"Invalid architecture '{arch_str}'" msg = f"Invalid architecture '{arch_str}'"
raise errors.ConfigurationError(msg) from e raise errors.ConfigurationError(msg) from e
return result return result
@staticmethod @staticmethod
@@ -123,15 +135,12 @@ class Architecture(StrEnum):
# 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
# on, as the same arch can have different names on different platforms # on, as the same arch can have different names on different platforms
if host_platform != platform: if host_platform != platform:
for arch_synonym in ARCH_SYNONYMS: synonym = arch_synonym(native_machine, host_platform, platform)
if native_machine == arch_synonym.get(host_platform): if synonym is None:
synonym = arch_synonym[platform] # can't build anything on this platform
return None
if synonym is None: native_architecture = Architecture(synonym)
# can't build anything on this platform
return None
native_architecture = Architecture(synonym)
return native_architecture return native_architecture
@@ -142,19 +151,12 @@ class Architecture(StrEnum):
return set() # can't build anything on this platform return set() # can't build anything on this platform
result = {native_arch} result = {native_arch}
if platform == "linux": match platform:
if Architecture.x86_64 in result: case "windows" if Architecture.AMD64 in result:
# x86_64 machines can run i686 containers result.add(Architecture.x86)
result.add(Architecture.i686) case "ios" if native_arch == Architecture.arm64_iphonesimulator:
elif Architecture.aarch64 in result and _check_aarch32_el0(): # Also build the device wheel if we're on ARM64.
result.add(Architecture.armv7l) result.add(Architecture.arm64_iphoneos)
elif platform == "windows" and Architecture.AMD64 in result:
result.add(Architecture.x86)
elif platform == "ios" and native_arch == Architecture.arm64_iphonesimulator:
# Also build the device wheel if we're on ARM64.
result.add(Architecture.arm64_iphoneos)
return result return result
@@ -173,6 +175,7 @@ class Architecture(StrEnum):
"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}, "pyodide": {Architecture.wasm32},
"android": {Architecture.x86_64, Architecture.arm64_v8a},
"ios": { "ios": {
Architecture.x86_64_iphonesimulator, Architecture.x86_64_iphonesimulator,
Architecture.arm64_iphonesimulator, Architecture.arm64_iphonesimulator,
@@ -183,14 +186,35 @@ class Architecture(StrEnum):
@staticmethod @staticmethod
def bitness_archs(platform: PlatformName, bitness: Literal["64", "32"]) -> "set[Architecture]": def bitness_archs(platform: PlatformName, bitness: Literal["64", "32"]) -> "set[Architecture]":
archs_32 = {Architecture.i686, Architecture.x86, Architecture.armv7l} # This map maps 64-bit architectures to their 32-bit equivalents.
auto_archs = Architecture.auto_archs(platform) archs_map = {
Architecture.x86_64: Architecture.i686,
Architecture.AMD64: Architecture.x86,
Architecture.aarch64: Architecture.armv7l,
}
native_arch = Architecture.native_arch(platform)
if bitness == "64": if native_arch is None:
return auto_archs - archs_32 return set() # can't build anything on this platform
if bitness == "32":
return auto_archs & archs_32 if native_arch == Architecture.wasm32:
typing.assert_never(bitness) return {native_arch} if bitness == "32" else set()
match bitness:
case "64":
return {native_arch} if native_arch not in archs_map.values() else set()
case "32":
if native_arch in archs_map.values():
return {native_arch}
elif native_arch in archs_map and platform in {"linux", "windows"}:
if native_arch == Architecture.aarch64 and not _check_aarch32_el0():
# If we're on aarch64, skip if we cannot build armv7l wheels.
return set()
return {archs_map[native_arch]}
else:
return set()
case _:
typing.assert_never(bitness)
def allowed_architectures_check( def allowed_architectures_check(
+12 -2
View File
@@ -4,6 +4,8 @@ from enum import Enum
from .util.helpers import strtobool from .util.helpers import strtobool
ANSI_CODE_REGEX = re.compile(r"(\033\[[0-9;]*m)")
class CIProvider(Enum): class CIProvider(Enum):
# official support # official support
@@ -46,7 +48,7 @@ def fix_ansi_codes_for_github_actions(text: str) -> str:
Github Actions forgets the current ANSI style on every new line. This Github Actions forgets the current ANSI style on every new line. This
function repeats the current ANSI style on every new line. function repeats the current ANSI style on every new line.
""" """
ansi_code_regex = re.compile(r"(\033\[[0-9;]*m)")
ansi_codes: list[str] = [] ansi_codes: list[str] = []
output = "" output = ""
@@ -55,7 +57,7 @@ def fix_ansi_codes_for_github_actions(text: str) -> str:
output += "".join(ansi_codes) + line output += "".join(ansi_codes) + line
# split the line at each ANSI code # split the line at each ANSI code
parts = ansi_code_regex.split(line) parts = ANSI_CODE_REGEX.split(line)
# if there are any ANSI codes, save them # if there are any ANSI codes, save them
if len(parts) > 1: if len(parts) > 1:
# iterate over the ANSI codes in this line # iterate over the ANSI codes in this line
@@ -67,3 +69,11 @@ def fix_ansi_codes_for_github_actions(text: str) -> str:
ansi_codes.append(code) ansi_codes.append(code)
return output return output
def filter_ansi_codes(text: str, /) -> str:
"""
Remove ANSI codes from text.
"""
return ANSI_CODE_REGEX.sub("", text)
+4 -5
View File
@@ -73,12 +73,16 @@ class EnvironmentAssignmentRaw:
return self.value return self.value
@dataclasses.dataclass
class EnvironmentAssignmentBash: class EnvironmentAssignmentBash:
""" """
An environment variable, in bash syntax. The value can use bash constructs An environment variable, in bash syntax. The value can use bash constructs
like "$OTHER_VAR" and "$(command arg1 arg2)". like "$OTHER_VAR" and "$(command arg1 arg2)".
""" """
name: str
value: str
def __init__(self, assignment: str): def __init__(self, assignment: str):
name, equals, value = assignment.partition("=") name, equals, value = assignment.partition("=")
if not equals: if not equals:
@@ -96,11 +100,6 @@ class EnvironmentAssignmentBash:
def __repr__(self) -> str: def __repr__(self) -> str:
return f"{self.name}={self.value}" return f"{self.name}={self.value}"
def __eq__(self, other: object) -> bool:
if isinstance(other, EnvironmentAssignmentBash):
return self.name == other.name and self.value == other.value
return False
@dataclasses.dataclass(kw_only=True) @dataclasses.dataclass(kw_only=True)
class ParsedEnvironment: class ParsedEnvironment:
+18
View File
@@ -85,3 +85,21 @@ class RepairStepProducedNoWheelError(FatalError):
) )
super().__init__(message) super().__init__(message)
self.return_code = 8 self.return_code = 8
class RepairStepProducedMultipleWheelsError(FatalError):
def __init__(self, wheels: list[str]) -> None:
message = textwrap.dedent(
f"""
Build failed because the repair step completed successfully but
produced multiple wheels: {wheels}
Your `repair-wheel-command` is expected to place one repaired
wheel in the {{dest_dir}} directory. See the documentation for
example configurations:
https://cibuildwheel.pypa.io/en/stable/options/#repair-wheel-command
"""
)
super().__init__(message)
self.return_code = 8
+16
View File
@@ -58,6 +58,22 @@ def _split_config_settings(config_settings: str) -> list[str]:
return [f"-C{setting}" for setting in config_settings_list] return [f"-C{setting}" for setting in config_settings_list]
# Based on build.__main__.main.
def parse_config_settings(config_settings_str: str) -> dict[str, str | list[str]]:
config_settings: dict[str, str | list[str]] = {}
for arg in shlex.split(config_settings_str):
setting, _, value = arg.partition("=")
existing_value = config_settings.get(setting)
if existing_value is None:
config_settings[setting] = value
elif isinstance(existing_value, str):
config_settings[setting] = [existing_value, value]
else:
existing_value.append(value)
return config_settings
def get_build_frontend_extra_flags( def get_build_frontend_extra_flags(
build_frontend: BuildFrontendConfig, verbosity_level: int, config_settings: str build_frontend: BuildFrontendConfig, verbosity_level: int, config_settings: str
) -> list[str]: ) -> list[str]:
+157 -21
View File
@@ -1,11 +1,24 @@
import codecs import codecs
import contextlib
import dataclasses
import functools
import hashlib
import io
import os import os
import re import re
import sys import sys
import textwrap
import time import time
from typing import IO, AnyStr, Final from collections.abc import Generator
from pathlib import Path
from typing import IO, TYPE_CHECKING, AnyStr, Final, Literal
from .ci import CIProvider, detect_ci_provider import humanize
from .ci import CIProvider, detect_ci_provider, filter_ansi_codes
if TYPE_CHECKING:
from .options import Options
FoldPattern = tuple[str, str] FoldPattern = tuple[str, str]
DEFAULT_FOLD_PATTERN: Final[FoldPattern] = ("{name}", "") DEFAULT_FOLD_PATTERN: Final[FoldPattern] = ("{name}", "")
@@ -37,6 +50,8 @@ PLATFORM_IDENTIFIER_DESCRIPTIONS: Final[dict[str, str]] = {
"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", "pyodide_wasm32": "Pyodide",
"android_arm64_v8a": "Android arm64_v8a",
"android_x86_64": "Android x86_64",
"ios_arm64_iphoneos": "iOS Device (ARM64)", "ios_arm64_iphoneos": "iOS Device (ARM64)",
"ios_arm64_iphonesimulator": "iOS Simulator (ARM64)", "ios_arm64_iphonesimulator": "iOS Simulator (ARM64)",
"ios_x86_64_iphonesimulator": "iOS Simulator (x86_64)", "ios_x86_64_iphonesimulator": "iOS Simulator (x86_64)",
@@ -69,14 +84,42 @@ class Symbols:
self.error = "" if unicode else "failed" self.error = "" if unicode else "failed"
@dataclasses.dataclass(kw_only=True, frozen=True)
class BuildInfo:
identifier: str
filename: Path | None
duration: float
@functools.cached_property
def size(self) -> str | None:
if self.filename is None:
return None
return humanize.naturalsize(self.filename.stat().st_size)
@functools.cached_property
def sha256(self) -> str | None:
if self.filename is None:
return None
with self.filename.open("rb") as f:
digest = hashlib.file_digest(f, "sha256")
return digest.hexdigest()
def __str__(self) -> str:
duration = humanize.naturaldelta(self.duration)
if self.filename:
return f"{self.identifier}: {self.filename.name} {self.size} in {duration}, SHA256={self.sha256}"
return f"{self.identifier}: {duration} (test only)"
class Logger: class Logger:
fold_mode: str fold_mode: Literal["azure", "github", "travis", "disabled"]
colors_enabled: bool colors_enabled: bool
unicode_enabled: bool unicode_enabled: bool
active_build_identifier: str | None = None active_build_identifier: str | None = None
build_start_time: float | None = None build_start_time: float | None = None
step_start_time: float | None = None step_start_time: float | None = None
active_fold_group_name: str | None = None active_fold_group_name: str | None = None
summary: list[BuildInfo]
def __init__(self) -> None: def __init__(self) -> None:
if sys.platform == "win32" and hasattr(sys.stdout, "reconfigure"): if sys.platform == "win32" and hasattr(sys.stdout, "reconfigure"):
@@ -88,25 +131,28 @@ class Logger:
ci_provider = detect_ci_provider() ci_provider = detect_ci_provider()
if ci_provider == CIProvider.azure_pipelines: match ci_provider:
self.fold_mode = "azure" case CIProvider.azure_pipelines:
self.colors_enabled = True self.fold_mode = "azure"
self.colors_enabled = True
elif ci_provider == CIProvider.github_actions: case CIProvider.github_actions:
self.fold_mode = "github" self.fold_mode = "github"
self.colors_enabled = True self.colors_enabled = True
elif ci_provider == CIProvider.travis_ci: case CIProvider.travis_ci:
self.fold_mode = "travis" self.fold_mode = "travis"
self.colors_enabled = True self.colors_enabled = True
elif ci_provider == CIProvider.appveyor: case CIProvider.appveyor:
self.fold_mode = "disabled" self.fold_mode = "disabled"
self.colors_enabled = True self.colors_enabled = True
else: case _:
self.fold_mode = "disabled" self.fold_mode = "disabled"
self.colors_enabled = file_supports_color(sys.stdout) self.colors_enabled = file_supports_color(sys.stdout)
self.summary = []
def build_start(self, identifier: str) -> None: def build_start(self, identifier: str) -> None:
self.step_end() self.step_end()
@@ -120,7 +166,7 @@ class Logger:
self.build_start_time = time.time() self.build_start_time = time.time()
self.active_build_identifier = identifier self.active_build_identifier = identifier
def build_end(self) -> None: def build_end(self, filename: Path | None) -> None:
assert self.build_start_time is not None assert self.build_start_time is not None
assert self.active_build_identifier is not None assert self.active_build_identifier is not None
self.step_end() self.step_end()
@@ -128,11 +174,14 @@ class Logger:
c = self.colors c = self.colors
s = self.symbols s = self.symbols
duration = time.time() - self.build_start_time duration = time.time() - self.build_start_time
duration_str = humanize.naturaldelta(duration, minimum_unit="milliseconds")
print() print()
print( print(f"{c.green}{s.done} {c.end}{self.active_build_identifier} finished in {duration_str}")
f"{c.green}{s.done} {c.end}{self.active_build_identifier} finished in {duration:.2f}s" self.summary.append(
BuildInfo(identifier=self.active_build_identifier, filename=filename, duration=duration)
) )
self.build_start_time = None self.build_start_time = None
self.active_build_identifier = None self.active_build_identifier = None
@@ -147,6 +196,7 @@ class Logger:
c = self.colors c = self.colors
s = self.symbols s = self.symbols
duration = time.time() - self.step_start_time duration = time.time() - self.step_start_time
if success: if success:
print(f"{c.green}{s.done} {c.end}{duration:.2f}s".rjust(78)) print(f"{c.green}{s.done} {c.end}{duration:.2f}s".rjust(78))
else: else:
@@ -183,6 +233,26 @@ class Logger:
c = self.colors c = self.colors
print(f"cibuildwheel: {c.bright_red}error{c.end}: {error}\n", file=sys.stderr) print(f"cibuildwheel: {c.bright_red}error{c.end}: {error}\n", file=sys.stderr)
@contextlib.contextmanager
def print_summary(self, *, options: "Options") -> Generator[None, None, None]:
start = time.time()
yield
duration = time.time() - start
if summary_path := os.environ.get("GITHUB_STEP_SUMMARY"):
github_summary = self._github_step_summary(duration=duration, options=options)
Path(summary_path).write_text(filter_ansi_codes(github_summary), encoding="utf-8")
n_wheels = len([info for info in self.summary if info.filename])
s = "s" if n_wheels > 1 else ""
duration_str = humanize.naturaldelta(duration)
print()
self._start_fold_group(f"{n_wheels} wheel{s} produced in {duration_str}")
for build_info in self.summary:
print(" ", build_info)
self._end_fold_group()
self.summary = []
@property @property
def step_active(self) -> bool: def step_active(self) -> bool:
return self.step_start_time is not None return self.step_start_time is not None
@@ -222,6 +292,72 @@ class Logger:
# lowercase, shorten # lowercase, shorten
return identifier.lower()[:20] return identifier.lower()[:20]
def _github_step_summary(self, duration: float, options: "Options") -> str:
"""
Returns the GitHub step summary, in markdown format.
"""
out = io.StringIO()
options_summary = options.summary(
identifiers=[bi.identifier for bi in self.summary], skip_unset=True
)
out.write(
textwrap.dedent("""\
### 🎡 cibuildwheel
<details>
<summary>
Build options
</summary>
```yaml
{options_summary}
```
</details>
""").format(options_summary=options_summary)
)
n_wheels = len([b for b in self.summary if b.filename])
wheel_rows = "\n".join(
"<tr>"
f"<td nowrap>{'<samp>' + b.filename.name + '</samp>' if b.filename else '*Test only*'}</td>"
f"<td nowrap>{b.size or 'N/A'}</td>"
f"<td nowrap><samp>{b.identifier}</samp></td>"
f"<td nowrap>{humanize.naturaldelta(b.duration)}</td>"
f"<td nowrap><samp>{b.sha256 or 'N/A'}</samp></td>"
"</tr>"
for b in self.summary
)
out.write(
textwrap.dedent("""\
<table>
<thead>
<tr>
<th align="left">Wheel</th>
<th align="left">Size</th>
<th align="left">Build identifier</th>
<th align="left">Time</th>
<th align="left">SHA256</th>
</tr>
</thead>
<tbody>
{wheel_rows}
</tbody>
</table>
<div align="right"><sup>{n} wheel{s} created in {duration_str}</sup></div>
""").format(
wheel_rows=wheel_rows,
n=n_wheels,
duration_str=humanize.naturaldelta(duration),
s="s" if n_wheels > 1 else "",
)
)
out.write("\n")
out.write("---")
out.write("\n")
return out.getvalue()
@property @property
def colors(self) -> Colors: def colors(self) -> Colors:
return Colors(enabled=self.colors_enabled) return Colors(enabled=self.colors_enabled)
+36 -33
View File
@@ -95,43 +95,46 @@ def _check_engine_version(engine: OCIContainerEngineConfig) -> None:
try: try:
version_string = call(engine.name, "version", "-f", "{{json .}}", capture_stdout=True) version_string = call(engine.name, "version", "-f", "{{json .}}", capture_stdout=True)
version_info = json.loads(version_string.strip()) version_info = json.loads(version_string.strip())
if engine.name == "docker": match engine.name:
client_api_version = FlexibleVersion(version_info["Client"]["ApiVersion"]) case "docker":
server_api_version = FlexibleVersion(version_info["Server"]["ApiVersion"]) client_api_version = FlexibleVersion(version_info["Client"]["ApiVersion"])
# --platform support was introduced in 1.32 as experimental, 1.41 removed the experimental flag server_api_version = FlexibleVersion(version_info["Server"]["ApiVersion"])
version = min(client_api_version, server_api_version) # --platform support was introduced in 1.32 as experimental, 1.41 removed the experimental flag
minimum_version = FlexibleVersion("1.41") version = min(client_api_version, server_api_version)
minimum_version_str = "20.10.0" # docker version minimum_version = FlexibleVersion("1.41")
error_msg = textwrap.dedent( minimum_version_str = "20.10.0" # docker version
f""" error_msg = textwrap.dedent(
Build failed because {engine.name} is too old. f"""
Build failed because {engine.name} is too old.
cibuildwheel requires {engine.name}>={minimum_version_str} running API version {minimum_version}. cibuildwheel requires {engine.name}>={minimum_version_str} running API version {minimum_version}.
The API version found by cibuildwheel is {version}. The API version found by cibuildwheel is {version}.
""" """
) )
elif engine.name == "podman": case "podman":
# podman uses the same version string for "Version" & "ApiVersion" # podman uses the same version string for "Version" & "ApiVersion"
client_version = FlexibleVersion(version_info["Client"]["Version"]) client_version = FlexibleVersion(version_info["Client"]["Version"])
if "Server" in version_info: if "Server" in version_info:
server_version = FlexibleVersion(version_info["Server"]["Version"]) server_version = FlexibleVersion(version_info["Server"]["Version"])
else: else:
server_version = client_version server_version = client_version
# --platform support was introduced in v3 # --platform support was introduced in v3
version = min(client_version, server_version) version = min(client_version, server_version)
minimum_version = FlexibleVersion("3") minimum_version = FlexibleVersion("3")
error_msg = textwrap.dedent( error_msg = textwrap.dedent(
f""" f"""
Build failed because {engine.name} is too old. Build failed because {engine.name} is too old.
cibuildwheel requires {engine.name}>={minimum_version}.
The version found by cibuildwheel is {version}.
"""
)
case _:
assert_never(engine.name)
cibuildwheel requires {engine.name}>={minimum_version}.
The version found by cibuildwheel is {version}.
"""
)
else:
assert_never(engine.name)
if version < minimum_version: if version < minimum_version:
raise OCIEngineTooOldError(error_msg) from None raise OCIEngineTooOldError(error_msg) from None
except (subprocess.CalledProcessError, KeyError, ValueError) as e: except (subprocess.CalledProcessError, KeyError, ValueError) as e:
msg = f"Build failed because {engine.name} is too old or is not working properly." msg = f"Build failed because {engine.name} is too old or is not working properly."
raise OCIEngineTooOldError(msg) from e raise OCIEngineTooOldError(msg) from e
+57 -37
View File
@@ -109,7 +109,7 @@ class BuildOptions:
test_groups: list[str] test_groups: list[str]
test_environment: ParsedEnvironment test_environment: ParsedEnvironment
build_verbosity: int build_verbosity: int
build_frontend: BuildFrontendConfig | None build_frontend: BuildFrontendConfig
config_settings: str config_settings: str
container_engine: OCIContainerEngineConfig container_engine: OCIContainerEngineConfig
pyodide_version: str | None pyodide_version: str | None
@@ -343,40 +343,46 @@ def _apply_inherit_rule(
msg = f"Don't know how to merge {before!r} and {after!r} with {rule}" msg = f"Don't know how to merge {before!r} and {after!r} with {rule}"
raise OptionsReaderError(msg) raise OptionsReaderError(msg)
if rule == InheritRule.APPEND: match rule:
return option_format.merge_values(before, after) case InheritRule.APPEND:
if rule == InheritRule.PREPEND: return option_format.merge_values(before, after)
return option_format.merge_values(after, before) case InheritRule.PREPEND:
return option_format.merge_values(after, before)
assert_never(rule) case _:
assert_never(rule)
def _stringify_setting( def _stringify_setting(
setting: SettingValue, setting: SettingValue,
option_format: OptionFormat | None, option_format: OptionFormat | None,
) -> str: ) -> str:
if isinstance(setting, Mapping): match setting:
try: case {}:
if option_format is None: assert isinstance(setting, Mapping) # MyPy 1.15 doesn't narrow this for us
raise OptionFormat.NotSupported try:
return option_format.format_table(setting) if option_format is None:
except OptionFormat.NotSupported: raise OptionFormat.NotSupported
msg = f"Error converting {setting!r} to a string: this setting doesn't accept a table" return option_format.format_table(setting)
raise OptionsReaderError(msg) from None except OptionFormat.NotSupported:
msg = (
if not isinstance(setting, str) and isinstance(setting, Sequence): f"Error converting {setting!r} to a string: this setting doesn't accept a table"
try: )
if option_format is None: raise OptionsReaderError(msg) from None
raise OptionFormat.NotSupported case bool() | int():
return option_format.format_list(setting) return str(setting)
except OptionFormat.NotSupported: case [*_]:
msg = f"Error converting {setting!r} to a string: this setting doesn't accept a list" try:
raise OptionsReaderError(msg) from None if option_format is None:
raise OptionFormat.NotSupported
if isinstance(setting, bool | int): return option_format.format_list(setting)
return str(setting) except OptionFormat.NotSupported:
msg = (
return setting f"Error converting {setting!r} to a string: this setting doesn't accept a list"
)
raise OptionsReaderError(msg) from None
case _:
assert isinstance(setting, str) # MyPy 1.15 doesn't narrow this for us
return setting
class OptionsReader: class OptionsReader:
@@ -659,7 +665,7 @@ class Options:
build_config = args.only build_config = args.only
skip_config = "" skip_config = ""
architectures = Architecture.all_archs(self.platform) architectures = Architecture.all_archs(self.platform)
enable = set(EnableGroup) enable |= EnableGroup.all_groups()
build_selector = BuildSelector( build_selector = BuildSelector(
build_config=build_config, build_config=build_config,
@@ -766,9 +772,8 @@ class Options:
env_plat=False, env_plat=False,
option_format=ShlexTableFormat(sep="; ", pair_sep=":", allow_merge=False), option_format=ShlexTableFormat(sep="; ", pair_sep=":", allow_merge=False),
) )
build_frontend: BuildFrontendConfig | None
if not build_frontend_str or build_frontend_str == "default": if not build_frontend_str or build_frontend_str == "default":
build_frontend = None build_frontend = BuildFrontendConfig("build")
else: else:
try: try:
build_frontend = BuildFrontendConfig.from_config_string(build_frontend_str) build_frontend = BuildFrontendConfig.from_config_string(build_frontend_str)
@@ -901,14 +906,18 @@ class Options:
defaults=True, defaults=True,
) )
def summary(self, identifiers: Iterable[str]) -> str: def summary(self, identifiers: Iterable[str], skip_unset: bool = False) -> str:
lines = [] lines = []
global_option_names = sorted(f.name for f in dataclasses.fields(self.globals)) global_option_names = sorted(f.name for f in dataclasses.fields(self.globals))
for option_name in global_option_names: for option_name in global_option_names:
option_value = getattr(self.globals, option_name) option_value = getattr(self.globals, option_name)
default_value = getattr(self.defaults.globals, option_name) default_value = getattr(self.defaults.globals, option_name)
lines.append(self.option_summary(option_name, option_value, default_value)) line = self.option_summary(
option_name, option_value, default_value, skip_unset=skip_unset
)
if line is not None:
lines.append(line)
build_options = self.build_options(identifier=None) build_options = self.build_options(identifier=None)
build_options_defaults = self.defaults.build_options(identifier=None) build_options_defaults = self.defaults.build_options(identifier=None)
@@ -928,9 +937,15 @@ class Options:
i: getattr(build_options_for_identifier[i], option_name) for i in identifiers i: getattr(build_options_for_identifier[i], option_name) for i in identifiers
} }
lines.append( line = self.option_summary(
self.option_summary(option_name, option_value, default_value, overrides=overrides) option_name,
option_value,
default_value,
overrides=overrides,
skip_unset=skip_unset,
) )
if line is not None:
lines.append(line)
return "\n".join(lines) return "\n".join(lines)
@@ -940,7 +955,8 @@ class Options:
option_value: Any, option_value: Any,
default_value: Any, default_value: Any,
overrides: Mapping[str, Any] | None = None, overrides: Mapping[str, Any] | None = None,
) -> str: skip_unset: bool = False,
) -> str | None:
""" """
Return a summary of the option value, including any overrides, with Return a summary of the option value, including any overrides, with
ANSI 'dim' color if it's the default. ANSI 'dim' color if it's the default.
@@ -954,6 +970,10 @@ class Options:
overrides_value_strs = {k: v for k, v in overrides_value_strs.items() if v != value_str} overrides_value_strs = {k: v for k, v in overrides_value_strs.items() if v != value_str}
has_been_set = (value_str != default_value_str) or overrides_value_strs has_been_set = (value_str != default_value_str) or overrides_value_strs
if skip_unset and not has_been_set:
return None
c = log.colors c = log.colors
result = c.gray if not has_been_set else "" result = c.gray if not has_been_set else ""
+20 -1
View File
@@ -1,12 +1,14 @@
from __future__ import annotations from __future__ import annotations
import sys
from collections.abc import Sequence from collections.abc import Sequence
from pathlib import Path from pathlib import Path
from typing import Final, Protocol from typing import Final, Protocol
from cibuildwheel import errors
from cibuildwheel.architecture import Architecture from cibuildwheel.architecture import Architecture
from cibuildwheel.options import Options from cibuildwheel.options import Options
from cibuildwheel.platforms import ios, linux, macos, pyodide, windows from cibuildwheel.platforms import android, ios, linux, macos, pyodide, windows
from cibuildwheel.selector import BuildSelector from cibuildwheel.selector import BuildSelector
from cibuildwheel.typing import GenericPythonConfiguration, PlatformName from cibuildwheel.typing import GenericPythonConfiguration, PlatformName
@@ -28,10 +30,27 @@ ALL_PLATFORM_MODULES: Final[dict[PlatformName, PlatformModule]] = {
"windows": windows, "windows": windows,
"macos": macos, "macos": macos,
"pyodide": pyodide, "pyodide": pyodide,
"android": android,
"ios": ios, "ios": ios,
} }
def native_platform() -> PlatformName:
if sys.platform.startswith("linux"):
return "linux"
elif sys.platform == "darwin":
return "macos"
elif sys.platform == "win32":
return "windows"
else:
msg = (
'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 "
"platform using the --platform argument. Check --help output for more information."
)
raise errors.ConfigurationError(msg)
def get_build_identifiers( def get_build_identifiers(
platform_module: PlatformModule, platform_module: PlatformModule,
build_selector: BuildSelector, build_selector: BuildSelector,
+645
View File
@@ -0,0 +1,645 @@
import csv
import hashlib
import os
import platform
import re
import shlex
import shutil
import subprocess
import sys
from collections.abc import Iterable, Iterator
from dataclasses import dataclass
from os.path import relpath
from pathlib import Path
from pprint import pprint
from runpy import run_path
from textwrap import dedent
from typing import Any
from build import ProjectBuilder
from build.env import IsolatedEnv
from elftools.common.exceptions import ELFError
from elftools.elf.elffile import ELFFile
from filelock import FileLock
from .. import errors, platforms # pylint: disable=cyclic-import
from ..architecture import Architecture, arch_synonym
from ..frontend import get_build_frontend_extra_flags, parse_config_settings
from ..logger import log
from ..options import BuildOptions, Options
from ..selector import BuildSelector
from ..util import resources
from ..util.cmd import call, shell
from ..util.file import CIBW_CACHE_PATH, copy_test_sources, download, move_file
from ..util.helpers import prepare_command
from ..util.packaging import find_compatible_wheel
from ..util.python_build_standalone import create_python_build_standalone_environment
from ..venv import constraint_flags, virtualenv
def android_triplet(identifier: str) -> str:
return {
"arm64_v8a": "aarch64-linux-android",
"x86_64": "x86_64-linux-android",
}[parse_identifier(identifier)[1]]
def parse_identifier(identifier: str) -> tuple[str, str]:
match = re.fullmatch(r"cp(\d)(\d+)-android_(.+)", identifier)
if not match:
msg = f"invalid Android identifier: '{identifier}'"
raise ValueError(msg)
major, minor, arch = match.groups()
return (f"{major}.{minor}", arch)
@dataclass(frozen=True)
class PythonConfiguration:
version: str
identifier: str
url: str
@property
def arch(self) -> str:
return parse_identifier(self.identifier)[1]
def all_python_configurations() -> list[PythonConfiguration]:
return [PythonConfiguration(**item) for item in resources.read_python_configs("android")]
def get_python_configurations(
build_selector: BuildSelector, architectures: set[Architecture]
) -> list[PythonConfiguration]:
return [
c
for c in all_python_configurations()
if c.arch in architectures and build_selector(c.identifier)
]
def shell_prepared(command: str, *, build_options: BuildOptions, env: dict[str, str]) -> None:
shell(
prepare_command(command, project=".", package=build_options.package_dir),
env=env,
)
def before_all(options: Options, python_configurations: list[PythonConfiguration]) -> None:
before_all_options = options.build_options(python_configurations[0].identifier)
if before_all_options.before_all:
log.step("Running before_all...")
shell_prepared(
before_all_options.before_all,
build_options=before_all_options,
env=before_all_options.environment.as_dictionary(os.environ),
)
@dataclass(frozen=True)
class BuildState:
config: PythonConfiguration
options: BuildOptions
build_path: Path
python_dir: Path
build_env: dict[str, str]
android_env: dict[str, str]
def build(options: Options, tmp_path: Path) -> None:
if "ANDROID_HOME" not in os.environ:
msg = (
"ANDROID_HOME environment variable is not set. For instructions, see "
"https://cibuildwheel.pypa.io/en/stable/platforms/#android"
)
raise errors.FatalError(msg)
configs = get_python_configurations(
options.globals.build_selector, options.globals.architectures
)
if not configs:
return
try:
before_all(options, configs)
built_wheels: list[Path] = []
for config in configs:
log.build_start(config.identifier)
build_options = options.build_options(config.identifier)
build_path = tmp_path / config.identifier
build_path.mkdir()
python_dir = setup_target_python(config, build_path)
build_env, android_env = setup_env(config, build_options, build_path, python_dir)
state = BuildState(
config, build_options, build_path, python_dir, build_env, android_env
)
compatible_wheel = find_compatible_wheel(built_wheels, config.identifier)
if compatible_wheel:
print(
f"\nFound previously built wheel {compatible_wheel.name} that is "
f"compatible with {config.identifier}. Skipping build step..."
)
repaired_wheel = compatible_wheel
else:
before_build(state)
built_wheel = build_wheel(state)
repaired_wheel = repair_wheel(state, built_wheel)
test_wheel(state, repaired_wheel)
output_wheel: Path | None = None
if compatible_wheel is None:
output_wheel = move_file(
repaired_wheel, build_options.output_dir / repaired_wheel.name
)
built_wheels.append(output_wheel)
shutil.rmtree(build_path)
log.build_end(output_wheel)
except subprocess.CalledProcessError as error:
msg = f"Command {error.cmd} failed with code {error.returncode}. {error.stdout or ''}"
raise errors.FatalError(msg) from error
def setup_target_python(config: PythonConfiguration, build_path: Path) -> Path:
log.step("Installing target Python...")
python_tgz = CIBW_CACHE_PATH / config.url.rpartition("/")[-1]
with FileLock(f"{python_tgz}.lock"):
if not python_tgz.exists():
download(config.url, python_tgz)
python_dir = build_path / "python"
python_dir.mkdir()
shutil.unpack_archive(python_tgz, python_dir)
return python_dir
def setup_env(
config: PythonConfiguration, build_options: BuildOptions, build_path: Path, python_dir: Path
) -> tuple[dict[str, str], dict[str, str]]:
"""
Returns two environment dicts, both pointing at the same virtual environment:
* build_env, which uses the environment normally.
* android_env, which uses the environment while simulating running on Android.
"""
log.step("Setting up build environment...")
# Create virtual environment
python_exe = create_python_build_standalone_environment(
config.version, build_path, CIBW_CACHE_PATH
)
venv_dir = build_path / "venv"
dependency_constraint = build_options.dependency_constraints.get_for_python_version(
version=config.version, tmp_dir=build_path
)
build_env = virtualenv(
config.version, python_exe, venv_dir, dependency_constraint, use_uv=False
)
create_cmake_toolchain(config, build_path, python_dir, build_env)
# Apply custom environment variables, and check environment is still valid
build_env = build_options.environment.as_dictionary(build_env)
build_env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1"
for command in ["python", "pip"]:
which = call("which", command, env=build_env, capture_stdout=True).strip()
if which != f"{venv_dir}/bin/{command}":
msg = (
f"{command} available on PATH doesn't match our installed instance. If you "
f"have modified PATH, ensure that you don't overwrite cibuildwheel's entry "
f"or insert {command} above it."
)
raise errors.FatalError(msg)
call(command, "--version", env=build_env)
# Construct an altered environment which simulates running on Android.
android_env = setup_android_env(config, python_dir, venv_dir, build_env)
# Install build tools
build_frontend = build_options.build_frontend
if build_frontend.name != "build":
msg = "Android requires the build frontend to be 'build'"
raise errors.FatalError(msg)
call("pip", "install", "build", *constraint_flags(dependency_constraint), env=build_env)
# Build-time requirements must be queried within android_env, because
# `get_requires_for_build` can run arbitrary code in setup.py scripts, which may be
# affected by the target platform. However, the requirements must be installed
# within build_env, because they're going to run on the build machine.
#
# The `build` CLI doesn't support this combination, so we use its API to query the
# requirements, and then install them ourselves with pip. We'll later run `build` in
# the same environment, passing the `--no-isolation` option.
class AndroidEnv(IsolatedEnv):
@property
def python_executable(self) -> str:
return f"{venv_dir}/bin/python"
def make_extra_environ(self) -> dict[str, str]:
return android_env
pb = ProjectBuilder.from_isolated_env(AndroidEnv(), build_options.package_dir)
if pb.build_system_requires:
call("pip", "install", *pb.build_system_requires, env=build_env)
requires_for_build = pb.get_requires_for_build(
"wheel", parse_config_settings(build_options.config_settings)
)
if requires_for_build:
call("pip", "install", *requires_for_build, env=build_env)
return build_env, android_env
def create_cmake_toolchain(
config: PythonConfiguration, build_path: Path, python_dir: Path, build_env: dict[str, str]
) -> None:
toolchain_path = build_path / "toolchain.cmake"
build_env["CMAKE_TOOLCHAIN_FILE"] = str(toolchain_path)
with open(toolchain_path, "w", encoding="UTF-8") as toolchain_file:
print(
dedent(
f"""\
# To support as many build systems as possible, we use environment
# variables as the single source of truth for compiler flags and paths,
# so they don't need to be specified here.
set(CMAKE_SYSTEM_NAME Android)
set(CMAKE_SYSTEM_PROCESSOR {android_triplet(config.identifier).split("-")[0]})
# Inhibit all of CMake's own NDK handling code.
set(CMAKE_SYSTEM_VERSION 1)
# Tell CMake where to look for headers and libraries.
list(INSERT CMAKE_FIND_ROOT_PATH 0 {python_dir}/prefix)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
"""
),
file=toolchain_file,
)
def localize_sysconfigdata(
python_dir: Path, build_env: dict[str, str], sysconfigdata_path: Path
) -> dict[str, Any]:
sysconfigdata: dict[str, Any] = run_path(str(sysconfigdata_path))["build_time_vars"]
with sysconfigdata_path.open("w", encoding="UTF-8") as f:
f.write("# Generated by cibuildwheel\n")
f.write("build_time_vars = ")
sysconfigdata = localized_vars(build_env, sysconfigdata, python_dir / "prefix")
pprint(sysconfigdata, stream=f, compact=True)
return sysconfigdata
def localized_vars(
build_env: dict[str, str], orig_vars: dict[str, Any], prefix: Path
) -> dict[str, Any]:
orig_prefix = orig_vars["prefix"]
localized_vars_ = {}
for key, value in orig_vars.items():
# The host's sysconfigdata will include references to build-time paths.
# Update these to refer to the current prefix.
final = value
if isinstance(final, str):
final = final.replace(orig_prefix, str(prefix))
if key == "ANDROID_API_LEVEL":
if api_level := build_env.get(key):
final = int(api_level)
# Build systems vary in whether FLAGS variables are read from sysconfig, and if so,
# whether they're replaced by environment variables or combined with them. Even
# setuptools has changed its behavior here
# (https://github.com/pypa/setuptools/issues/4836).
#
# Ensure consistency by clearing the sysconfig variables and letting the environment
# variables take effect alone. This will also work for any non-Python build systems
# which the build script may call.
elif key in ["CFLAGS", "CXXFLAGS", "LDFLAGS"]:
final = ""
# These variables contain an embedded copy of LDFLAGS.
elif key in ["LDSHARED", "LDCXXSHARED"]:
final = final.removesuffix(" " + orig_vars["LDFLAGS"])
localized_vars_[key] = final
return localized_vars_
def setup_android_env(
config: PythonConfiguration, python_dir: Path, venv_dir: Path, build_env: dict[str, str]
) -> dict[str, str]:
site_packages = next(venv_dir.glob("lib/python*/site-packages"))
for suffix in ["pth", "py"]:
shutil.copy(resources.PATH / f"_cross_venv.{suffix}", site_packages)
sysconfigdata_path = Path(
shutil.copy(
next(python_dir.glob("prefix/lib/python*/_sysconfigdata_*.py")),
site_packages,
)
)
sysconfigdata = localize_sysconfigdata(python_dir, build_env, sysconfigdata_path)
# Activate the code in _cross_venv.py.
android_env = build_env.copy()
android_env["CIBW_HOST_TRIPLET"] = android_triplet(config.identifier)
# Get the environment variables needed to build for Android (CC, CFLAGS, etc). These are
# generated by https://github.com/python/cpython/blob/main/Android/android-env.sh.
env_output = call(python_dir / "android.py", "env", env=build_env, capture_stdout=True)
# shlex.split should produce a sequence alternating between:
# * the word "export"
# * a key=value string, without quotes
for i, token in enumerate(shlex.split(env_output)):
if i % 2 == 0:
assert token == "export", token
else:
key, sep, value = token.partition("=")
assert sep == "=", token
android_env[key] = value
# localized_vars cleared the CFLAGS and CXXFLAGS in the sysconfigdata, but most
# packages take their optimization flags from these variables. Pass these flags via
# environment variables instead.
#
# We don't enable debug information, because it significantly increases binary size,
# and most Android app developers don't have the NDK installed, so they would have no
# way to strip it.
opt = " ".join(word for word in sysconfigdata["OPT"].split() if not word.startswith("-g"))
for key in ["CFLAGS", "CXXFLAGS"]:
android_env[key] += " " + opt
# Format the environment so it can be pasted into a shell when debugging.
for key, value in sorted(android_env.items()):
if os.environ.get(key) != value:
print(f"export {key}={shlex.quote(value)}")
return android_env
def before_build(state: BuildState) -> None:
if state.options.before_build:
log.step("Running before_build...")
shell_prepared(
state.options.before_build,
build_options=state.options,
env=state.build_env,
)
def build_wheel(state: BuildState) -> Path:
log.step("Building wheel...")
built_wheel_dir = state.build_path / "built_wheel"
call(
"python",
"-m",
"build",
state.options.package_dir,
"--wheel",
"--no-isolation",
"--skip-dependency-check",
f"--outdir={built_wheel_dir}",
*get_build_frontend_extra_flags(
state.options.build_frontend,
state.options.build_verbosity,
state.options.config_settings,
),
env=state.android_env,
)
built_wheels = list(built_wheel_dir.glob("*.whl"))
if len(built_wheels) != 1:
msg = f"{built_wheel_dir} contains {len(built_wheels)} wheels; expected 1"
raise errors.FatalError(msg)
built_wheel = built_wheels[0]
if built_wheel.name.endswith("none-any.whl"):
raise errors.NonPlatformWheelError()
return built_wheel
def repair_wheel(state: BuildState, built_wheel: Path) -> Path:
log.step("Repairing wheel...")
repaired_wheel_dir = state.build_path / "repaired_wheel"
repaired_wheel_dir.mkdir()
if state.options.repair_command:
shell(
prepare_command(
state.options.repair_command, wheel=built_wheel, dest_dir=repaired_wheel_dir
),
env=state.build_env,
)
else:
repair_default(state.android_env, built_wheel, repaired_wheel_dir)
repaired_wheels = list(repaired_wheel_dir.glob("*.whl"))
if len(repaired_wheels) == 0:
raise errors.RepairStepProducedNoWheelError()
if len(repaired_wheels) != 1:
msg = f"{repaired_wheel_dir} contains {len(repaired_wheels)} wheels; expected 1"
raise errors.FatalError(msg)
repaired_wheel = repaired_wheels[0]
if repaired_wheel.name.endswith("none-any.whl"):
raise errors.NonPlatformWheelError()
return repaired_wheel
def repair_default(
android_env: dict[str, str], built_wheel: Path, repaired_wheel_dir: Path
) -> None:
"""
Adds libc++ to the wheel if anything links against it. In the future this should be
moved to auditwheel and generalized to support more libraries.
"""
if (match := re.search(r"^(.+?)-", built_wheel.name)) is None:
msg = f"Failed to parse wheel filename: {built_wheel.name}"
raise errors.FatalError(msg)
wheel_name = match[1]
unpacked_dir = repaired_wheel_dir / "unpacked"
unpacked_dir.mkdir()
shutil.unpack_archive(built_wheel, unpacked_dir, format="zip")
# Some build systems are inconsistent about name normalization, so don't assume the
# dist-info name is identical to the wheel name.
record_paths = list(unpacked_dir.glob("*.dist-info/RECORD"))
if len(record_paths) != 1:
msg = f"{built_wheel.name} contains {len(record_paths)} dist-info/RECORD files; expected 1"
raise errors.FatalError(msg)
old_soname = "libc++_shared.so"
paths_to_patch = []
for path, elffile in elf_file_filter(
unpacked_dir / filename
for filename, *_ in csv.reader(record_paths[0].read_text().splitlines())
):
if (dynamic := elffile.get_section_by_name(".dynamic")) and any( # type: ignore[no-untyped-call]
tag.entry.d_tag == "DT_NEEDED" and tag.needed == old_soname
for tag in dynamic.iter_tags()
):
paths_to_patch.append(path)
if not paths_to_patch:
shutil.copyfile(built_wheel, repaired_wheel_dir / built_wheel.name)
else:
# Android doesn't support DT_RPATH, but supports DT_RUNPATH since API level 24
# (https://github.com/aosp-mirror/platform_bionic/blob/master/android-changes-for-ndk-developers.md).
if int(sysconfig_print('get_config_vars()["ANDROID_API_LEVEL"]', android_env)) < 24:
msg = f"Adding {old_soname} requires ANDROID_API_LEVEL to be at least 24"
raise errors.FatalError(msg)
toolchain = Path(android_env["CC"]).parent.parent
src_path = toolchain / f"sysroot/usr/lib/{android_env['CIBW_HOST_TRIPLET']}/{old_soname}"
# Use the same library location as auditwheel would.
libs_dir = unpacked_dir / (wheel_name + ".libs")
libs_dir.mkdir()
new_soname = soname_with_hash(src_path)
dst_path = libs_dir / new_soname
shutil.copyfile(src_path, dst_path)
call("patchelf", "--set-soname", new_soname, dst_path)
for path in paths_to_patch:
call("patchelf", "--replace-needed", old_soname, new_soname, path)
call(
"patchelf",
"--set-rpath",
f"${{ORIGIN}}/{relpath(libs_dir, path.parent)}",
path,
)
call(sys.executable, "-m", "wheel", "pack", unpacked_dir, "-d", repaired_wheel_dir)
def elf_file_filter(paths: Iterable[Path]) -> Iterator[tuple[Path, ELFFile]]:
"""Filter through an iterator of filenames and load up only ELF files"""
for path in paths:
if not path.name.endswith(".py"):
try:
with open(path, "rb") as f:
candidate = ELFFile(f) # type: ignore[no-untyped-call]
yield path, candidate
except ELFError:
pass # Not an ELF file
def soname_with_hash(src_path: Path) -> str:
"""Return the same library filename as auditwheel would"""
shorthash = hashlib.sha256(src_path.read_bytes()).hexdigest()[:8]
src_name = src_path.name
base, ext = src_name.split(".", 1)
if not base.endswith(f"-{shorthash}"):
return f"{base}-{shorthash}.{ext}"
else:
return src_name
def test_wheel(state: BuildState, wheel: Path) -> None:
test_command = state.options.test_command
if not (test_command and state.options.test_selector(state.config.identifier)):
return
log.step("Testing wheel...")
native_arch = arch_synonym(platform.machine(), platforms.native_platform(), "android")
if state.config.arch != native_arch:
log.warning(
f"Skipping tests for {state.config.arch}, as the build machine only "
f"supports {native_arch}"
)
return
if state.options.before_test:
shell_prepared(
state.options.before_test,
build_options=state.options,
env=state.build_env,
)
# Install the wheel and test-requires.
site_packages_dir = state.build_path / "site-packages"
site_packages_dir.mkdir()
call(
"pip",
"install",
"--only-binary=:all:",
"--platform",
sysconfig_print("get_platform()", state.android_env).replace("-", "_"),
"--target",
site_packages_dir,
f"{wheel}{state.options.test_extras}",
*state.options.test_requires,
env=state.build_env,
)
# Copy test-sources.
cwd_dir = state.build_path / "cwd"
cwd_dir.mkdir()
if state.options.test_sources:
copy_test_sources(state.options.test_sources, Path.cwd(), cwd_dir)
else:
(cwd_dir / "test_fail.py").write_text(
resources.TEST_FAIL_CWD_FILE.read_text(),
)
# Android doesn't support placeholders in the test command.
if any(("{" + placeholder + "}") in test_command for placeholder in ["project", "package"]):
msg = (
f"Test command {test_command!r} with a "
"'{project}' or '{package}' placeholder is not supported on Android, "
"because the source directory is not visible on the emulator."
)
raise errors.FatalError(msg)
# Parse test-command.
test_args = shlex.split(test_command)
if test_args[:2] in [["python", "-c"], ["python", "-m"]]:
test_args[:3] = [test_args[1], test_args[2], "--"]
elif test_args[0] in ["pytest"]:
# We transform some commands into the `python -m` form, but this is deprecated.
msg = (
f"Test command {test_command!r} is not supported on Android. "
"cibuildwheel will try to execute it as if it started with 'python -m'. "
"If this works, all you need to do is add that to your test command."
)
log.warning(msg)
test_args[:1] = ["-m", test_args[0], "--"]
else:
msg = (
f"Test command {test_command!r} is not supported on Android. "
f"Supported commands are 'python -m' and 'python -c'."
)
raise errors.FatalError(msg)
# Run the test app.
call(
state.python_dir / "android.py",
"test",
"--managed",
"maxVersion",
"--site-packages",
site_packages_dir,
"--cwd",
cwd_dir,
*test_args,
env=state.build_env,
)
def sysconfig_print(method_call: str, env: dict[str, str]) -> str:
return call(
"python",
"-c",
f'import sysconfig; print(sysconfig.{method_call}, end="")',
env=env,
capture_stdout=True,
)
+47 -45
View File
@@ -17,7 +17,6 @@ from .. import errors
from ..architecture import Architecture from ..architecture import Architecture
from ..environment import ParsedEnvironment from ..environment import ParsedEnvironment
from ..frontend import ( from ..frontend import (
BuildFrontendConfig,
BuildFrontendName, BuildFrontendName,
get_build_frontend_extra_flags, get_build_frontend_extra_flags,
) )
@@ -387,20 +386,21 @@ def setup_python(
env.setdefault("IPHONEOS_DEPLOYMENT_TARGET", "13.0") env.setdefault("IPHONEOS_DEPLOYMENT_TARGET", "13.0")
log.step("Installing build tools...") log.step("Installing build tools...")
if build_frontend == "pip": match build_frontend:
# No additional build tools required case "pip":
pass # No additional build tools required
elif build_frontend == "build": pass
call( case "build":
"pip", call(
"install", "pip",
"--upgrade", "install",
"build[virtualenv]", "--upgrade",
*constraint_flags(dependency_constraint), "build[virtualenv]",
env=env, *constraint_flags(dependency_constraint),
) env=env,
else: )
assert_never(build_frontend) case _:
assert_never(build_frontend)
return target_install_path, env return target_install_path, env
@@ -437,7 +437,7 @@ 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("build") build_frontend = build_options.build_frontend
# uv doesn't support iOS # uv doesn't support iOS
if build_frontend.name == "build[uv]": if build_frontend.name == "build[uv]":
msg = "uv doesn't support iOS" msg = "uv doesn't support iOS"
@@ -494,34 +494,35 @@ def build(options: Options, tmp_path: Path) -> None:
if constraints_path: if constraints_path:
combine_constraints(build_env, constraints_path, None) combine_constraints(build_env, constraints_path, None)
if build_frontend.name == "pip": match build_frontend.name:
# Path.resolve() is needed. Without it pip wheel may try to case "pip":
# fetch package from pypi.org. See # Path.resolve() is needed. Without it pip wheel may try to
# https://github.com/pypa/cibuildwheel/pull/369 # fetch package from pypi.org. See
call( # https://github.com/pypa/cibuildwheel/pull/369
"python", call(
"-m", "python",
"pip", "-m",
"wheel", "pip",
build_options.package_dir.resolve(), "wheel",
f"--wheel-dir={built_wheel_dir}", build_options.package_dir.resolve(),
"--no-deps", f"--wheel-dir={built_wheel_dir}",
*extra_flags, "--no-deps",
env=build_env, *extra_flags,
) env=build_env,
elif build_frontend.name == "build": )
call( case "build":
"python", call(
"-m", "python",
"build", "-m",
build_options.package_dir, "build",
"--wheel", build_options.package_dir,
f"--outdir={built_wheel_dir}", "--wheel",
*extra_flags, f"--outdir={built_wheel_dir}",
env=build_env, *extra_flags,
) env=build_env,
else: )
assert_never(build_frontend) case _:
assert_never(build_frontend)
test_wheel = built_wheel = next(built_wheel_dir.glob("*.whl")) test_wheel = built_wheel = next(built_wheel_dir.glob("*.whl"))
@@ -671,6 +672,7 @@ def build(options: Options, tmp_path: Path) -> None:
log.step_end() log.step_end()
# We're all done here; move it to output (overwrite existing) # We're all done here; move it to output (overwrite existing)
output_wheel: Path | None = None
if compatible_wheel is None: if compatible_wheel is None:
output_wheel = build_options.output_dir.joinpath(built_wheel.name) output_wheel = build_options.output_dir.joinpath(built_wheel.name)
moved_wheel = move_file(built_wheel, output_wheel) moved_wheel = move_file(built_wheel, output_wheel)
@@ -683,7 +685,7 @@ def build(options: Options, tmp_path: Path) -> None:
# Clean up # Clean up
shutil.rmtree(identifier_tmp_dir) shutil.rmtree(identifier_tmp_dir)
log.build_end() log.build_end(output_wheel)
except subprocess.CalledProcessError as error: except subprocess.CalledProcessError as error:
msg = f"Command {error.cmd} failed with code {error.returncode}. {error.stdout or ''}" msg = f"Command {error.cmd} failed with code {error.returncode}. {error.stdout or ''}"
raise errors.FatalError(msg) from error raise errors.FatalError(msg) from error
+52 -55
View File
@@ -10,7 +10,7 @@ from typing import assert_never
from .. import errors from .. import errors
from ..architecture import Architecture from ..architecture import Architecture
from ..frontend import BuildFrontendConfig, get_build_frontend_extra_flags from ..frontend import get_build_frontend_extra_flags
from ..logger import log from ..logger import log
from ..oci_container import OCIContainer, OCIContainerEngineConfig, OCIPlatform from ..oci_container import OCIContainer, OCIContainerEngineConfig, OCIPlatform
from ..options import BuildOptions, Options from ..options import BuildOptions, Options
@@ -204,7 +204,7 @@ def build_in_container(
log.build_start(config.identifier) log.build_start(config.identifier)
local_identifier_tmp_dir = local_tmp_dir / config.identifier local_identifier_tmp_dir = local_tmp_dir / config.identifier
build_options = options.build_options(config.identifier) build_options = options.build_options(config.identifier)
build_frontend = build_options.build_frontend or BuildFrontendConfig("build") build_frontend = build_options.build_frontend
use_uv = build_frontend.name == "build[uv]" use_uv = build_frontend.name == "build[uv]"
pip = ["uv", "pip"] if use_uv else ["pip"] pip = ["uv", "pip"] if use_uv else ["pip"]
@@ -253,7 +253,7 @@ def build_in_container(
print( print(
f"\nFound previously built wheel {compatible_wheel.name}, that's compatible with {config.identifier}. Skipping build step..." f"\nFound previously built wheel {compatible_wheel.name}, that's compatible with {config.identifier}. Skipping build step..."
) )
repaired_wheels = [compatible_wheel] repaired_wheel = compatible_wheel
else: else:
if build_options.before_build: if build_options.before_build:
log.step("Running before_build...") log.step("Running before_build...")
@@ -275,37 +275,38 @@ def build_in_container(
build_frontend, build_options.build_verbosity, build_options.config_settings build_frontend, build_options.build_verbosity, build_options.config_settings
) )
if build_frontend.name == "pip": match build_frontend.name:
container.call( case "pip":
[ container.call(
"python", [
"-m", "python",
"pip", "-m",
"wheel", "pip",
container_package_dir, "wheel",
f"--wheel-dir={built_wheel_dir}", container_package_dir,
"--no-deps", f"--wheel-dir={built_wheel_dir}",
*extra_flags, "--no-deps",
], *extra_flags,
env=env, ],
) env=env,
elif build_frontend.name == "build" or build_frontend.name == "build[uv]": )
if use_uv and "--no-isolation" not in extra_flags and "-n" not in extra_flags: case "build" | "build[uv]":
extra_flags += ["--installer=uv"] if use_uv and "--no-isolation" not in extra_flags and "-n" not in extra_flags:
container.call( extra_flags += ["--installer=uv"]
[ container.call(
"python", [
"-m", "python",
"build", "-m",
container_package_dir, "build",
"--wheel", container_package_dir,
f"--outdir={built_wheel_dir}", "--wheel",
*extra_flags, f"--outdir={built_wheel_dir}",
], *extra_flags,
env=env, ],
) env=env,
else: )
assert_never(build_frontend) case _:
assert_never(build_frontend)
built_wheel = container.glob(built_wheel_dir, "*.whl")[0] built_wheel = container.glob(built_wheel_dir, "*.whl")[0]
@@ -325,14 +326,16 @@ def build_in_container(
else: else:
container.call(["mv", built_wheel, repaired_wheel_dir]) container.call(["mv", built_wheel, repaired_wheel_dir])
repaired_wheels = container.glob(repaired_wheel_dir, "*.whl") match container.glob(repaired_wheel_dir, "*.whl"):
case []:
raise errors.RepairStepProducedNoWheelError()
case [repaired_wheel]:
pass
case too_many:
raise errors.RepairStepProducedMultipleWheelsError([p.name for p in too_many])
if not repaired_wheels: if repaired_wheel.name in {wheel.name for wheel in built_wheels}:
raise errors.RepairStepProducedNoWheelError() raise errors.AlreadyBuiltWheelError(repaired_wheel.name)
for repaired_wheel in repaired_wheels:
if repaired_wheel.name in {wheel.name for wheel in built_wheels}:
raise errors.AlreadyBuiltWheelError(repaired_wheel.name)
if build_options.test_command and build_options.test_selector(config.identifier): if build_options.test_command and build_options.test_selector(config.identifier):
log.step("Testing wheel...") log.step("Testing wheel...")
@@ -374,14 +377,8 @@ def build_in_container(
container.call(["sh", "-c", before_test_prepared], env=virtualenv_env) container.call(["sh", "-c", before_test_prepared], env=virtualenv_env)
# Install the wheel we just built # Install the wheel we just built
# Note: If auditwheel produced two wheels, it's because the earlier produced wheel
# conforms to multiple manylinux standards. These multiple versions of the wheel are
# functionally the same, differing only in name, wheel metadata, and possibly include
# different external shared libraries. so it doesn't matter which one we run the tests on.
# Let's just pick the first one.
wheel_to_test = repaired_wheels[0]
container.call( container.call(
[*pip, "install", str(wheel_to_test) + build_options.test_extras], [*pip, "install", str(repaired_wheel) + build_options.test_extras],
env=virtualenv_env, env=virtualenv_env,
) )
@@ -394,7 +391,7 @@ def build_in_container(
build_options.test_command, build_options.test_command,
project=container_project_path, project=container_project_path,
package=container_package_dir, package=container_package_dir,
wheel=wheel_to_test, wheel=repaired_wheel,
) )
test_cwd = testing_temp_dir / "test_cwd" test_cwd = testing_temp_dir / "test_cwd"
@@ -417,15 +414,15 @@ def build_in_container(
# clean up test environment # clean up test environment
container.call(["rm", "-rf", testing_temp_dir]) container.call(["rm", "-rf", testing_temp_dir])
# move repaired wheels to output # move repaired wheel to output
output_wheel: Path | None = None
if compatible_wheel is None: if compatible_wheel is None:
container.call(["mkdir", "-p", container_output_dir]) container.call(["mkdir", "-p", container_output_dir])
container.call(["mv", *repaired_wheels, container_output_dir]) container.call(["mv", repaired_wheel, container_output_dir])
built_wheels.extend( built_wheels.append(container_output_dir / repaired_wheel.name)
container_output_dir / repaired_wheel.name for repaired_wheel in repaired_wheels output_wheel = options.globals.output_dir / repaired_wheel.name
)
log.build_end() log.build_end(output_wheel)
log.step("Copying wheels back to host...") log.step("Copying wheels back to host...")
# copy the output back into the host # copy the output back into the host
+72 -65
View File
@@ -19,7 +19,7 @@ from .. import errors
from ..architecture import Architecture from ..architecture import Architecture
from ..ci import detect_ci_provider from ..ci import detect_ci_provider
from ..environment import ParsedEnvironment from ..environment import ParsedEnvironment
from ..frontend import BuildFrontendConfig, BuildFrontendName, get_build_frontend_extra_flags from ..frontend import BuildFrontendName, get_build_frontend_extra_flags
from ..logger import log from ..logger import log
from ..options import Options from ..options import Options
from ..selector import BuildSelector from ..selector import BuildSelector
@@ -345,39 +345,40 @@ def setup_python(
env.setdefault("SDKROOT", arm64_compatible_sdks[0]) env.setdefault("SDKROOT", arm64_compatible_sdks[0])
log.step("Installing build tools...") log.step("Installing build tools...")
if build_frontend == "pip": match build_frontend:
call( case "pip":
"pip", call(
"install", "pip",
"--upgrade", "install",
"delocate", "--upgrade",
*constraint_flags(dependency_constraint), "delocate",
env=env, *constraint_flags(dependency_constraint),
) env=env,
elif build_frontend == "build": )
call( case "build":
"pip", call(
"install", "pip",
"--upgrade", "install",
"delocate", "--upgrade",
"build[virtualenv]", "delocate",
*constraint_flags(dependency_constraint), "build[virtualenv]",
env=env, *constraint_flags(dependency_constraint),
) env=env,
elif build_frontend == "build[uv]": )
assert uv_path is not None case "build[uv]":
call( assert uv_path is not None
uv_path, call(
"pip", uv_path,
"install", "pip",
"--upgrade", "install",
"delocate", "--upgrade",
"build[virtualenv, uv]", "delocate",
*constraint_flags(dependency_constraint), "build[virtualenv, uv]",
env=env, *constraint_flags(dependency_constraint),
) env=env,
else: )
assert_never(build_frontend) case _:
assert_never(build_frontend)
return base_python, env return base_python, env
@@ -407,7 +408,7 @@ 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("build") build_frontend = build_options.build_frontend
use_uv = build_frontend.name == "build[uv]" use_uv = build_frontend.name == "build[uv]"
uv_path = find_uv() uv_path = find_uv()
if use_uv and uv_path is None: if use_uv and uv_path is None:
@@ -467,35 +468,40 @@ def build(options: Options, tmp_path: Path) -> None:
build_env, constraints_path, identifier_tmp_dir if use_uv else None build_env, constraints_path, identifier_tmp_dir if use_uv else None
) )
if build_frontend.name == "pip": match build_frontend.name:
# Path.resolve() is needed. Without it pip wheel may try to fetch package from pypi.org case "pip":
# see https://github.com/pypa/cibuildwheel/pull/369 # Path.resolve() is needed. Without it pip wheel may try to fetch package from pypi.org
call( # see https://github.com/pypa/cibuildwheel/pull/369
"python", call(
"-m", "python",
"pip", "-m",
"wheel", "pip",
build_options.package_dir.resolve(), "wheel",
f"--wheel-dir={built_wheel_dir}", build_options.package_dir.resolve(),
"--no-deps", f"--wheel-dir={built_wheel_dir}",
*extra_flags, "--no-deps",
env=build_env, *extra_flags,
) env=build_env,
elif build_frontend.name == "build" or build_frontend.name == "build[uv]": )
if use_uv and "--no-isolation" not in extra_flags and "-n" not in extra_flags: case "build" | "build[uv]":
extra_flags.append("--installer=uv") if (
call( use_uv
"python", and "--no-isolation" not in extra_flags
"-m", and "-n" not in extra_flags
"build", ):
build_options.package_dir, extra_flags.append("--installer=uv")
"--wheel", call(
f"--outdir={built_wheel_dir}", "python",
*extra_flags, "-m",
env=build_env, "build",
) build_options.package_dir,
else: "--wheel",
assert_never(build_frontend) f"--outdir={built_wheel_dir}",
*extra_flags,
env=build_env,
)
case _:
assert_never(build_frontend)
built_wheel = next(built_wheel_dir.glob("*.whl")) built_wheel = next(built_wheel_dir.glob("*.whl"))
@@ -726,6 +732,7 @@ def build(options: Options, tmp_path: Path) -> None:
shell_with_arch(test_command_prepared, cwd=test_cwd, env=virtualenv_env) shell_with_arch(test_command_prepared, cwd=test_cwd, env=virtualenv_env)
# we're all done here; move it to output (overwrite existing) # we're all done here; move it to output (overwrite existing)
output_wheel = None
if compatible_wheel is None: if compatible_wheel is None:
output_wheel = build_options.output_dir.joinpath(repaired_wheel.name) output_wheel = build_options.output_dir.joinpath(repaired_wheel.name)
moved_wheel = move_file(repaired_wheel, output_wheel) moved_wheel = move_file(repaired_wheel, output_wheel)
@@ -738,7 +745,7 @@ def build(options: Options, tmp_path: Path) -> None:
# clean up # clean up
shutil.rmtree(identifier_tmp_dir) shutil.rmtree(identifier_tmp_dir)
log.build_end() log.build_end(output_wheel)
except subprocess.CalledProcessError as error: except subprocess.CalledProcessError as error:
msg = f"Command {error.cmd} failed with code {error.returncode}. {error.stdout or ''}" msg = f"Command {error.cmd} failed with code {error.returncode}. {error.stdout or ''}"
raise errors.FatalError(msg) from error raise errors.FatalError(msg) from error
+9 -4
View File
@@ -3,6 +3,7 @@ import functools
import json import json
import os import os
import shutil import shutil
import subprocess
import sys import sys
import tomllib import tomllib
import typing import typing
@@ -16,7 +17,7 @@ from filelock import FileLock
from .. import errors from .. import errors
from ..architecture import Architecture from ..architecture import Architecture
from ..environment import ParsedEnvironment from ..environment import ParsedEnvironment
from ..frontend import BuildFrontendConfig, get_build_frontend_extra_flags from ..frontend import get_build_frontend_extra_flags
from ..logger import log from ..logger import log
from ..options import Options from ..options import Options
from ..selector import BuildSelector from ..selector import BuildSelector
@@ -352,7 +353,7 @@ 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("build") build_frontend = build_options.build_frontend
if build_frontend.name == "pip": if build_frontend.name == "pip":
msg = "The pyodide platform doesn't support pip frontend" msg = "The pyodide platform doesn't support pip frontend"
@@ -445,6 +446,7 @@ def build(options: Options, tmp_path: Path) -> None:
dest_dir=repaired_wheel_dir, dest_dir=repaired_wheel_dir,
) )
shell(repair_command_prepared, env=env) shell(repair_command_prepared, env=env)
log.step_end()
else: else:
shutil.move(str(built_wheel), repaired_wheel_dir) shutil.move(str(built_wheel), repaired_wheel_dir)
@@ -538,6 +540,7 @@ def build(options: Options, tmp_path: Path) -> None:
shell(test_command_prepared, cwd=test_cwd, env=virtualenv_env) shell(test_command_prepared, cwd=test_cwd, env=virtualenv_env)
# we're all done here; move it to output (overwrite existing) # we're all done here; move it to output (overwrite existing)
output_wheel: Path | None = None
if compatible_wheel is None: if compatible_wheel is None:
output_wheel = build_options.output_dir.joinpath(repaired_wheel.name) output_wheel = build_options.output_dir.joinpath(repaired_wheel.name)
moved_wheel = move_file(repaired_wheel, output_wheel) moved_wheel = move_file(repaired_wheel, output_wheel)
@@ -546,6 +549,8 @@ def build(options: Options, tmp_path: Path) -> None:
f"{repaired_wheel} was moved to {moved_wheel} instead of {output_wheel}" f"{repaired_wheel} was moved to {moved_wheel} instead of {output_wheel}"
) )
built_wheels.append(output_wheel) built_wheels.append(output_wheel)
log.build_end(output_wheel)
finally: except subprocess.CalledProcessError as error:
pass msg = f"Command {error.cmd} failed with code {error.returncode}. {error.stdout or ''}"
raise errors.FatalError(msg) from error
+59 -52
View File
@@ -15,7 +15,7 @@ from filelock import FileLock
from .. import errors from .. import errors
from ..architecture import Architecture from ..architecture import Architecture
from ..environment import ParsedEnvironment from ..environment import ParsedEnvironment
from ..frontend import BuildFrontendConfig, BuildFrontendName, get_build_frontend_extra_flags from ..frontend import BuildFrontendName, get_build_frontend_extra_flags
from ..logger import log from ..logger import log
from ..options import Options from ..options import Options
from ..selector import BuildSelector from ..selector import BuildSelector
@@ -305,26 +305,27 @@ def setup_python(
call("pip", "--version", env=env) call("pip", "--version", env=env)
log.step("Installing build tools...") log.step("Installing build tools...")
if build_frontend == "build": match build_frontend:
call( case "build":
"pip", call(
"install", "pip",
"--upgrade", "install",
"build[virtualenv]", "--upgrade",
*constraint_flags(dependency_constraint), "build[virtualenv]",
env=env, *constraint_flags(dependency_constraint),
) env=env,
elif build_frontend == "build[uv]": )
assert uv_path is not None case "build[uv]":
call( assert uv_path is not None
uv_path, call(
"pip", uv_path,
"install", "pip",
"--upgrade", "install",
"build[virtualenv]", "--upgrade",
*constraint_flags(dependency_constraint), "build[virtualenv]",
env=env, *constraint_flags(dependency_constraint),
) 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
@@ -394,7 +395,7 @@ 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("build") build_frontend = build_options.build_frontend
use_uv = build_frontend.name == "build[uv]" and can_use_uv(config) use_uv = build_frontend.name == "build[uv]" and can_use_uv(config)
log.build_start(config.identifier) log.build_start(config.identifier)
@@ -467,36 +468,41 @@ def build(options: Options, tmp_path: Path) -> None:
if constraints_path: if constraints_path:
combine_constraints(build_env, constraints_path, identifier_tmp_dir) combine_constraints(build_env, constraints_path, identifier_tmp_dir)
if build_frontend.name == "pip": match build_frontend.name:
# Path.resolve() is needed. Without it pip wheel may try to fetch package from pypi.org case "pip":
# see https://github.com/pypa/cibuildwheel/pull/369 # Path.resolve() is needed. Without it pip wheel may try to fetch package from pypi.org
call( # see https://github.com/pypa/cibuildwheel/pull/369
"python", call(
"-m", "python",
"pip", "-m",
"wheel", "pip",
options.globals.package_dir.resolve(), "wheel",
f"--wheel-dir={built_wheel_dir}", options.globals.package_dir.resolve(),
"--no-deps", f"--wheel-dir={built_wheel_dir}",
*extra_flags, "--no-deps",
env=build_env, *extra_flags,
) env=build_env,
elif build_frontend.name == "build" or build_frontend.name == "build[uv]": )
if use_uv and "--no-isolation" not in extra_flags and "-n" not in extra_flags: case "build" | "build[uv]":
extra_flags.append("--installer=uv") if (
use_uv
and "--no-isolation" not in extra_flags
and "-n" not in extra_flags
):
extra_flags.append("--installer=uv")
call( call(
"python", "python",
"-m", "-m",
"build", "build",
build_options.package_dir, build_options.package_dir,
"--wheel", "--wheel",
f"--outdir={built_wheel_dir}", f"--outdir={built_wheel_dir}",
*extra_flags, *extra_flags,
env=build_env, env=build_env,
) )
else: case _:
assert_never(build_frontend) assert_never(build_frontend)
built_wheel = next(built_wheel_dir.glob("*.whl")) built_wheel = next(built_wheel_dir.glob("*.whl"))
@@ -607,6 +613,7 @@ def build(options: Options, tmp_path: Path) -> None:
shell(test_command_prepared, cwd=test_cwd, env=virtualenv_env) shell(test_command_prepared, cwd=test_cwd, env=virtualenv_env)
# 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)
output_wheel = None
if compatible_wheel is None: if compatible_wheel is None:
output_wheel = build_options.output_dir.joinpath(repaired_wheel.name) output_wheel = build_options.output_dir.joinpath(repaired_wheel.name)
moved_wheel = move_file(repaired_wheel, output_wheel) moved_wheel = move_file(repaired_wheel, output_wheel)
@@ -621,7 +628,7 @@ def build(options: Options, tmp_path: Path) -> None:
# don't want to abort a build because of that) # don't want to abort a build because of that)
shutil.rmtree(identifier_tmp_dir, ignore_errors=True) shutil.rmtree(identifier_tmp_dir, ignore_errors=True)
log.build_end() log.build_end(output_wheel)
except subprocess.CalledProcessError as error: except subprocess.CalledProcessError as error:
msg = f"Command {error.cmd} failed with code {error.returncode}. {error.stdout or ''}" msg = f"Command {error.cmd} failed with code {error.returncode}. {error.stdout or ''}"
raise errors.FatalError(msg) from error raise errors.FatalError(msg) from error
+14 -32
View File
@@ -14,32 +14,16 @@ def get_parent(node: ast.AST | None, depth: int = 1) -> ast.AST | None:
def is_main(parent: ast.AST | None) -> bool: def is_main(parent: ast.AST | None) -> bool:
if parent is None: match parent:
return False case ast.If(test=ast.Compare(left=left, ops=[ast.Eq()], comparators=[comp])):
values = {left, comp}
# This would be much nicer with 3.10's pattern matching! mains = {x for x in values if isinstance(x, ast.Constant) and x.value == "__main__"}
if not isinstance(parent, ast.If): if len(mains) != 1:
return False return False
if not isinstance(parent.test, ast.Compare): consts = {x for x in values if isinstance(x, ast.Name) and x.id == "__name__"}
return False return len(consts) == 1
case _:
try: return False
(op,) = parent.test.ops
(comp,) = parent.test.comparators
except ValueError:
return False
if not isinstance(op, ast.Eq):
return False
values = {comp, parent.test.left}
mains = {x for x in values if isinstance(x, ast.Constant) and x.value == "__main__"}
if len(mains) != 1:
return False
consts = {x for x in values if isinstance(x, ast.Name) and x.id == "__name__"}
return len(consts) == 1
class Analyzer(ast.NodeVisitor): class Analyzer(ast.NodeVisitor):
@@ -65,12 +49,10 @@ class Analyzer(ast.NodeVisitor):
parent is not None and get_parent(parent) is None and is_main(get_parent(node, 3)) parent is not None and get_parent(parent) is None and is_main(get_parent(node, 3))
) )
if ( match node:
node.arg == "python_requires" case ast.keyword(arg="python_requires", value=ast.Constant(value=str(version))):
and isinstance(node.value, ast.Constant) if unnested or name_main_unnested:
and (unnested or name_main_unnested) self.requires_python = version
):
self.requires_python = node.value.value
def setup_py_python_requires(content: str) -> str | None: def setup_py_python_requires(content: str) -> str | None:
+1
View File
@@ -0,0 +1 @@
import _cross_venv; _cross_venv.initialize()
+83
View File
@@ -0,0 +1,83 @@
# This module is copied into the site-packages directory of an Android build environment, and
# activated via a .pth file when we want the environment to simulate Android.
import os
import platform
import re
import sys
import sysconfig
from pathlib import Path
from typing import Any
def initialize() -> None:
if not (host_triplet := os.environ.get("CIBW_HOST_TRIPLET")):
return
# os ######################################################################
def cross_os_uname() -> os.uname_result:
return os.uname_result(
(
"Linux",
"localhost",
# The Linux kernel version and release are unlikely to be significant, but return
# realistic values anyway (from an API level 24 emulator).
"3.18.91+",
"#1 SMP PREEMPT Tue Jan 9 20:35:43 UTC 2018",
host_triplet.split("-")[0],
)
)
os.name = "posix"
os.uname = cross_os_uname
# platform ################################################################
#
# We can't determine the user-visible Android version number from the API level, so return a
# string which will work fine for display, but will fail to parse as a version number.
def cross_android_ver(*args: Any, **kwargs: Any) -> platform.AndroidVer:
return platform.AndroidVer(
release=f"API level {cross_getandroidapilevel()}",
api_level=cross_getandroidapilevel(),
manufacturer="Google",
model="sdk_gphone64",
device="emu64",
is_emulator=True,
)
# platform.uname, platform.system etc. are all implemented in terms of platform.android_ver.
platform.android_ver = cross_android_ver
# sys #####################################################################
def cross_getandroidapilevel() -> int:
api_level = sysconfig.get_config_var("ANDROID_API_LEVEL")
assert isinstance(api_level, int)
return api_level
# Some packages may recognize sys.cross_compiling from the crossenv tool.
sys.cross_compiling = True # type: ignore[attr-defined]
sys.getandroidapilevel = cross_getandroidapilevel # type: ignore[attr-defined]
sys.implementation._multiarch = host_triplet # type: ignore[attr-defined]
sys.platform = "android"
# Determine the abiflags from the sysconfigdata filename.
sysconfigdata_path = next(Path(__file__).parent.glob("_sysconfigdata_*.py"))
abiflags_match = re.match(r"_sysconfigdata_(.*?)_", sysconfigdata_path.name)
assert abiflags_match is not None
sys.abiflags = abiflags_match[1]
# sysconfig ###############################################################
#
# We don't change the actual sys.base_prefix and base_exec_prefix, because that
# could have unpredictable effects. Instead, we change the internal variables
# used to generate sysconfig.get_path("include").
exec_prefix = sysconfig.get_config_var("exec_prefix")
sysconfig._BASE_PREFIX = sysconfig._BASE_EXEC_PREFIX = exec_prefix # type: ignore[attr-defined]
# Reload the sysconfigdata file, generating its name from sys.abiflags,
# sys.platform, and sys.implementation._multiarch.
sysconfig._init_config_vars() # type: ignore[attr-defined]
# sysconfig.get_platform, which determines the wheel tag, is implemented in terms of
# sys.platform, sysconfig.get_config_var("ANDROID_API_LEVEL") (see localized_vars in
# android.py), and os.uname.
+45 -36
View File
@@ -132,7 +132,7 @@ python_configurations = [
{ identifier = "cp314-musllinux_armv7l", version = "3.14", path_str = "/opt/python/cp314-cp314" }, { identifier = "cp314-musllinux_armv7l", version = "3.14", path_str = "/opt/python/cp314-cp314" },
{ identifier = "cp314t-musllinux_armv7l", version = "3.14", path_str = "/opt/python/cp314-cp314t" }, { identifier = "cp314t-musllinux_armv7l", version = "3.14", path_str = "/opt/python/cp314-cp314t" },
{ identifier = "cp38-musllinux_riscv64", version = "3.8", path_str = "/opt/python/cp38-cp38" }, { identifier = "cp38-musllinux_riscv64", version = "3.8", path_str = "/opt/python/cp38-cp38" },
{ identifier = "cp39-musllinux_ricv64", version = "3.9", path_str = "/opt/python/cp39-cp39" }, { identifier = "cp39-musllinux_riscv64", version = "3.9", path_str = "/opt/python/cp39-cp39" },
{ identifier = "cp310-musllinux_riscv64", version = "3.10", path_str = "/opt/python/cp310-cp310" }, { identifier = "cp310-musllinux_riscv64", version = "3.10", path_str = "/opt/python/cp310-cp310" },
{ identifier = "cp311-musllinux_riscv64", version = "3.11", path_str = "/opt/python/cp311-cp311" }, { identifier = "cp311-musllinux_riscv64", version = "3.11", path_str = "/opt/python/cp311-cp311" },
{ identifier = "cp312-musllinux_riscv64", version = "3.12", path_str = "/opt/python/cp312-cp312" }, { identifier = "cp312-musllinux_riscv64", version = "3.12", path_str = "/opt/python/cp312-cp312" },
@@ -159,28 +159,28 @@ python_configurations = [
{ identifier = "cp312-macosx_x86_64", version = "3.12", url = "https://www.python.org/ftp/python/3.12.10/python-3.12.10-macos11.pkg" }, { identifier = "cp312-macosx_x86_64", version = "3.12", url = "https://www.python.org/ftp/python/3.12.10/python-3.12.10-macos11.pkg" },
{ identifier = "cp312-macosx_arm64", version = "3.12", url = "https://www.python.org/ftp/python/3.12.10/python-3.12.10-macos11.pkg" }, { identifier = "cp312-macosx_arm64", version = "3.12", url = "https://www.python.org/ftp/python/3.12.10/python-3.12.10-macos11.pkg" },
{ identifier = "cp312-macosx_universal2", version = "3.12", url = "https://www.python.org/ftp/python/3.12.10/python-3.12.10-macos11.pkg" }, { identifier = "cp312-macosx_universal2", version = "3.12", url = "https://www.python.org/ftp/python/3.12.10/python-3.12.10-macos11.pkg" },
{ identifier = "cp313-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-macos11.pkg" }, { identifier = "cp313-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.5/python-3.13.5-macos11.pkg" },
{ identifier = "cp313-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-macos11.pkg" }, { identifier = "cp313-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.5/python-3.13.5-macos11.pkg" },
{ identifier = "cp313-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-macos11.pkg" }, { identifier = "cp313-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.5/python-3.13.5-macos11.pkg" },
{ identifier = "cp313t-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-macos11.pkg" }, { identifier = "cp313t-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.5/python-3.13.5-macos11.pkg" },
{ identifier = "cp313t-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-macos11.pkg" }, { identifier = "cp313t-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.5/python-3.13.5-macos11.pkg" },
{ identifier = "cp313t-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-macos11.pkg" }, { identifier = "cp313t-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.5/python-3.13.5-macos11.pkg" },
{ identifier = "cp314-macosx_x86_64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b2-macos11.pkg" }, { identifier = "cp314-macosx_x86_64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0rc1-macos11.pkg" },
{ identifier = "cp314-macosx_arm64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b2-macos11.pkg" }, { identifier = "cp314-macosx_arm64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0rc1-macos11.pkg" },
{ identifier = "cp314-macosx_universal2", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b2-macos11.pkg" }, { identifier = "cp314-macosx_universal2", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0rc1-macos11.pkg" },
{ identifier = "cp314t-macosx_x86_64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b2-macos11.pkg" }, { identifier = "cp314t-macosx_x86_64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0rc1-macos11.pkg" },
{ identifier = "cp314t-macosx_arm64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b2-macos11.pkg" }, { identifier = "cp314t-macosx_arm64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0rc1-macos11.pkg" },
{ identifier = "cp314t-macosx_universal2", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b2-macos11.pkg" }, { identifier = "cp314t-macosx_universal2", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0rc1-macos11.pkg" },
{ 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" },
{ identifier = "pp39-macosx_x86_64", version = "3.9", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-macos_x86_64.tar.bz2" }, { identifier = "pp39-macosx_x86_64", version = "3.9", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-macos_x86_64.tar.bz2" },
{ identifier = "pp39-macosx_arm64", version = "3.9", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-macos_arm64.tar.bz2" }, { identifier = "pp39-macosx_arm64", version = "3.9", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-macos_arm64.tar.bz2" },
{ identifier = "pp310-macosx_x86_64", version = "3.10", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.19-macos_x86_64.tar.bz2" }, { identifier = "pp310-macosx_x86_64", version = "3.10", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.19-macos_x86_64.tar.bz2" },
{ identifier = "pp310-macosx_arm64", version = "3.10", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.19-macos_arm64.tar.bz2" }, { identifier = "pp310-macosx_arm64", version = "3.10", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.19-macos_arm64.tar.bz2" },
{ identifier = "pp311-macosx_x86_64", version = "3.11", url = "https://downloads.python.org/pypy/pypy3.11-v7.3.19-macos_x86_64.tar.bz2" }, { identifier = "pp311-macosx_x86_64", version = "3.11", url = "https://downloads.python.org/pypy/pypy3.11-v7.3.20-macos_x86_64.tar.bz2" },
{ identifier = "pp311-macosx_arm64", version = "3.11", url = "https://downloads.python.org/pypy/pypy3.11-v7.3.19-macos_arm64.tar.bz2" }, { identifier = "pp311-macosx_arm64", version = "3.11", url = "https://downloads.python.org/pypy/pypy3.11-v7.3.20-macos_arm64.tar.bz2" },
{ identifier = "gp311_242-macosx_x86_64", version = "3.11", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.1/graalpy-24.2.1-macos-amd64.tar.gz" }, { identifier = "gp311_242-macosx_x86_64", version = "3.11", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.2/graalpy-24.2.2-macos-amd64.tar.gz" },
{ identifier = "gp311_242-macosx_arm64", version = "3.11", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.1/graalpy-24.2.1-macos-aarch64.tar.gz" }, { identifier = "gp311_242-macosx_arm64", version = "3.11", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.2/graalpy-24.2.2-macos-aarch64.tar.gz" },
] ]
[windows] [windows]
@@ -195,38 +195,47 @@ python_configurations = [
{ 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.10", arch = "32" }, { identifier = "cp312-win32", version = "3.12.10", arch = "32" },
{ identifier = "cp312-win_amd64", version = "3.12.10", arch = "64" }, { identifier = "cp312-win_amd64", version = "3.12.10", arch = "64" },
{ identifier = "cp313-win32", version = "3.13.3", arch = "32" }, { identifier = "cp313-win32", version = "3.13.5", arch = "32" },
{ identifier = "cp313t-win32", version = "3.13.3", arch = "32" }, { identifier = "cp313t-win32", version = "3.13.5", arch = "32" },
{ identifier = "cp313-win_amd64", version = "3.13.3", arch = "64" }, { identifier = "cp313-win_amd64", version = "3.13.5", arch = "64" },
{ identifier = "cp313t-win_amd64", version = "3.13.3", arch = "64" }, { identifier = "cp313t-win_amd64", version = "3.13.5", arch = "64" },
{ identifier = "cp314-win32", version = "3.14.0-b2", arch = "32" }, { identifier = "cp314-win32", version = "3.14.0-rc1", arch = "32" },
{ identifier = "cp314t-win32", version = "3.14.0-b2", arch = "32" }, { identifier = "cp314t-win32", version = "3.14.0-rc1", arch = "32" },
{ identifier = "cp314-win_amd64", version = "3.14.0-b2", arch = "64" }, { identifier = "cp314-win_amd64", version = "3.14.0-rc1", arch = "64" },
{ identifier = "cp314t-win_amd64", version = "3.14.0-b2", arch = "64" }, { identifier = "cp314t-win_amd64", version = "3.14.0-rc1", 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.10", arch = "ARM64" }, { identifier = "cp312-win_arm64", version = "3.12.10", arch = "ARM64" },
{ identifier = "cp313-win_arm64", version = "3.13.3", arch = "ARM64" }, { identifier = "cp313-win_arm64", version = "3.13.5", arch = "ARM64" },
{ identifier = "cp313t-win_arm64", version = "3.13.3", arch = "ARM64" }, { identifier = "cp313t-win_arm64", version = "3.13.5", arch = "ARM64" },
{ identifier = "cp314-win_arm64", version = "3.14.0-b2", arch = "ARM64" }, { identifier = "cp314-win_arm64", version = "3.14.0-rc1", arch = "ARM64" },
{ identifier = "cp314t-win_arm64", version = "3.14.0-b2", arch = "ARM64" }, { identifier = "cp314t-win_arm64", version = "3.14.0-rc1", arch = "ARM64" },
{ 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.19-win64.zip" }, { identifier = "pp310-win_amd64", version = "3.10", arch = "64", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.19-win64.zip" },
{ identifier = "pp311-win_amd64", version = "3.11", arch = "64", url = "https://downloads.python.org/pypy/pypy3.11-v7.3.19-win64.zip" }, { identifier = "pp311-win_amd64", version = "3.11", arch = "64", url = "https://downloads.python.org/pypy/pypy3.11-v7.3.20-win64.zip" },
{ identifier = "gp311_242-win_amd64", version = "3.11", arch = "64", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.1/graalpy-24.2.1-windows-amd64.zip" }, { identifier = "gp311_242-win_amd64", version = "3.11", arch = "64", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.2/graalpy-24.2.2-windows-amd64.zip" },
] ]
[pyodide] [pyodide]
python_configurations = [ python_configurations = [
{ identifier = "cp312-pyodide_wasm32", version = "3.12", default_pyodide_version = "0.27.6", node_version = "v22" }, { identifier = "cp312-pyodide_wasm32", version = "3.12", default_pyodide_version = "0.27.7", node_version = "v22" },
{ identifier = "cp313-pyodide_wasm32", version = "3.13", default_pyodide_version = "0.28.0a3", node_version = "v22" }, { identifier = "cp313-pyodide_wasm32", version = "3.13", default_pyodide_version = "0.28.0", node_version = "v22" },
]
[android]
python_configurations = [
{ identifier = "cp313-android_arm64_v8a", version = "3.13", url = "https://repo.maven.apache.org/maven2/com/chaquo/python/python/3.13.5+20250722.214220/python-3.13.5+20250722.214220-aarch64-linux-android.tar.gz" },
{ identifier = "cp313-android_x86_64", version = "3.13", url = "https://repo.maven.apache.org/maven2/com/chaquo/python/python/3.13.5+20250722.214220/python-3.13.5+20250722.214220-x86_64-linux-android.tar.gz" },
] ]
[ios] [ios]
python_configurations = [ python_configurations = [
{ identifier = "cp313-ios_arm64_iphoneos", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b6/Python-3.13-iOS-support.b6.tar.gz" }, { identifier = "cp313-ios_arm64_iphoneos", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b10/Python-3.13-iOS-support.b10.tar.gz" },
{ identifier = "cp313-ios_x86_64_iphonesimulator", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b6/Python-3.13-iOS-support.b6.tar.gz" }, { identifier = "cp313-ios_x86_64_iphonesimulator", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b10/Python-3.13-iOS-support.b10.tar.gz" },
{ identifier = "cp313-ios_arm64_iphonesimulator", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b6/Python-3.13-iOS-support.b6.tar.gz" }, { identifier = "cp313-ios_arm64_iphonesimulator", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b10/Python-3.13-iOS-support.b10.tar.gz" },
{ identifier = "cp314-ios_arm64_iphoneos", version = "3.14", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.14-b6/Python-3.14-iOS-support.b6.tar.gz" },
{ identifier = "cp314-ios_x86_64_iphonesimulator", version = "3.14", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.14-b6/Python-3.14-iOS-support.b6.tar.gz" },
{ identifier = "cp314-ios_arm64_iphonesimulator", version = "3.14", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.14-b6/Python-3.14-iOS-support.b6.tar.gz" },
] ]
@@ -108,7 +108,7 @@
}, },
"build-frontend": { "build-frontend": {
"default": "default", "default": "default",
"description": "Set the tool to use to build, either \"pip\" (default for now), \"build\", or \"build[uv]\"", "description": "Set the tool to use to build, either \"build\" (default), \"build[uv]\", or \"pip\"",
"oneOf": [ "oneOf": [
{ {
"enum": [ "enum": [
@@ -1072,6 +1072,66 @@
} }
} }
}, },
"android": {
"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"
},
"xbuild-tools": {
"$ref": "#/properties/xbuild-tools"
},
"pyodide-version": {
"$ref": "#/properties/pyodide-version"
},
"repair-wheel-command": {
"$ref": "#/properties/repair-wheel-command"
},
"test-command": {
"$ref": "#/properties/test-command"
},
"test-extras": {
"$ref": "#/properties/test-extras"
},
"test-sources": {
"$ref": "#/properties/test-sources"
},
"test-groups": {
"$ref": "#/properties/test-groups"
},
"test-requires": {
"$ref": "#/properties/test-requires"
},
"test-environment": {
"$ref": "#/properties/test-environment"
}
}
},
"ios": { "ios": {
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
@@ -10,7 +10,7 @@ build==1.2.2.post1
# via # via
# -r .nox/update_constraints/tmp/constraints-pyodide.in # -r .nox/update_constraints/tmp/constraints-pyodide.in
# pyodide-build # pyodide-build
certifi==2025.4.26 certifi==2025.7.14
# via # via
# httpcore # httpcore
# httpx # httpx
@@ -21,7 +21,7 @@ click==8.1.8
# via # via
# -r .nox/update_constraints/tmp/constraints-pyodide.in # -r .nox/update_constraints/tmp/constraints-pyodide.in
# typer # typer
distlib==0.3.9 distlib==0.4.0
# via virtualenv # via virtualenv
filelock==3.18.0 filelock==3.18.0
# via virtualenv # via virtualenv
@@ -54,15 +54,15 @@ platformdirs==4.3.8
# via # via
# pyodide-build # pyodide-build
# virtualenv # virtualenv
pydantic==2.11.5 pydantic==2.11.7
# via # via
# pyodide-build # pyodide-build
# pyodide-lock # pyodide-lock
pydantic-core==2.33.2 pydantic-core==2.33.2
# via pydantic # via pydantic
pygments==2.19.1 pygments==2.19.2
# via rich # via rich
pyodide-build==0.30.4 pyodide-build==0.30.5
# via -r .nox/update_constraints/tmp/constraints-pyodide.in # via -r .nox/update_constraints/tmp/constraints-pyodide.in
pyodide-cli==0.3.0 pyodide-cli==0.3.0
# via # via
@@ -72,16 +72,16 @@ pyodide-lock==0.1.0a7
# via pyodide-build # via pyodide-build
pyproject-hooks==1.2.0 pyproject-hooks==1.2.0
# via build # via build
requests==2.32.3 requests==2.32.4
# via pyodide-build # via pyodide-build
resolvelib==1.1.0 resolvelib==1.2.0
# via pyodide-build # via pyodide-build
rich==14.0.0 rich==14.1.0
# via # via
# pyodide-build # pyodide-build
# pyodide-cli # pyodide-cli
# typer # typer
ruamel-yaml==0.18.12 ruamel-yaml==0.18.14
# via pyodide-build # via pyodide-build
ruamel-yaml-clib==0.2.12 ruamel-yaml-clib==0.2.12
# via ruamel-yaml # via ruamel-yaml
@@ -94,7 +94,7 @@ typer==0.16.0
# auditwheel-emscripten # auditwheel-emscripten
# pyodide-build # pyodide-build
# pyodide-cli # pyodide-cli
typing-extensions==4.13.2 typing-extensions==4.14.1
# via # via
# anyio # anyio
# pydantic # pydantic
@@ -105,9 +105,9 @@ typing-inspection==0.4.1
# via pydantic # via pydantic
unearth==0.17.5 unearth==0.17.5
# via pyodide-build # via pyodide-build
urllib3==2.4.0 urllib3==2.5.0
# via requests # via requests
virtualenv==20.31.2 virtualenv==20.32.0
# via # via
# build # build
# pyodide-build # pyodide-build
@@ -0,0 +1,116 @@
# This file was autogenerated by uv via the following command:
# nox -s update_constraints
annotated-types==0.7.0
# via pydantic
anyio==4.9.0
# via httpx
auditwheel-emscripten==0.1.0
# via pyodide-build
build==1.2.2.post1
# via
# -r .nox/update_constraints/tmp/constraints-pyodide.in
# pyodide-build
certifi==2025.7.14
# via
# httpcore
# httpx
# requests
charset-normalizer==3.4.2
# via requests
click==8.1.8
# via
# -r .nox/update_constraints/tmp/constraints-pyodide.in
# typer
distlib==0.4.0
# via virtualenv
filelock==3.18.0
# via virtualenv
h11==0.16.0
# via httpcore
httpcore==1.0.9
# via httpx
httpx==0.28.1
# via unearth
idna==3.10
# via
# anyio
# httpx
# requests
leb128==1.0.8
# via auditwheel-emscripten
markdown-it-py==3.0.0
# via rich
mdurl==0.1.2
# via markdown-it-py
packaging==25.0
# via
# auditwheel-emscripten
# build
# pyodide-build
# unearth
pip==25.1.1
# via -r .nox/update_constraints/tmp/constraints-pyodide.in
platformdirs==4.3.8
# via
# pyodide-build
# virtualenv
pydantic==2.11.7
# via
# pyodide-build
# pyodide-lock
pydantic-core==2.33.2
# via pydantic
pygments==2.19.2
# via rich
pyodide-build==0.30.5
# via -r .nox/update_constraints/tmp/constraints-pyodide.in
pyodide-cli==0.3.0
# via
# auditwheel-emscripten
# pyodide-build
pyodide-lock==0.1.0a7
# via pyodide-build
pyproject-hooks==1.2.0
# via build
requests==2.32.4
# via pyodide-build
resolvelib==1.2.0
# via pyodide-build
rich==14.1.0
# via
# pyodide-build
# pyodide-cli
# typer
ruamel-yaml==0.18.14
# via pyodide-build
ruamel-yaml-clib==0.2.12
# via ruamel-yaml
shellingham==1.5.4
# via typer
sniffio==1.3.1
# via anyio
typer==0.16.0
# via
# auditwheel-emscripten
# pyodide-build
# pyodide-cli
typing-extensions==4.14.1
# via
# pydantic
# pydantic-core
# typer
# typing-inspection
typing-inspection==0.4.1
# via pydantic
unearth==0.17.5
# via pyodide-build
urllib3==2.5.0
# via requests
virtualenv==20.32.0
# via
# build
# pyodide-build
wheel==0.45.1
# via
# auditwheel-emscripten
# pyodide-build
@@ -6,7 +6,7 @@ build==1.2.2.post1
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
delocate==0.13.0 delocate==0.13.0
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
distlib==0.3.9 distlib==0.4.0
# via virtualenv # via virtualenv
filelock==3.18.0 filelock==3.18.0
# via virtualenv # via virtualenv
@@ -26,9 +26,9 @@ pyproject-hooks==1.2.0
# via build # via build
tomli==2.2.1 tomli==2.2.1
# via build # via build
typing-extensions==4.13.2 typing-extensions==4.14.1
# via delocate # via delocate
virtualenv==20.31.2 virtualenv==20.32.0
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
zipp==3.22.0 zipp==3.23.0
# via importlib-metadata # via importlib-metadata
@@ -6,7 +6,7 @@ build==1.2.2.post1
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
delocate==0.13.0 delocate==0.13.0
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
distlib==0.3.9 distlib==0.4.0
# via virtualenv # via virtualenv
filelock==3.18.0 filelock==3.18.0
# via virtualenv # via virtualenv
@@ -22,7 +22,7 @@ platformdirs==4.3.8
# via virtualenv # via virtualenv
pyproject-hooks==1.2.0 pyproject-hooks==1.2.0
# via build # via build
typing-extensions==4.13.2 typing-extensions==4.14.1
# via delocate # via delocate
virtualenv==20.31.2 virtualenv==20.32.0
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
@@ -6,7 +6,7 @@ build==1.2.2.post1
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
delocate==0.13.0 delocate==0.13.0
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
distlib==0.3.9 distlib==0.4.0
# via virtualenv # via virtualenv
filelock==3.18.0 filelock==3.18.0
# via virtualenv # via virtualenv
@@ -22,7 +22,7 @@ platformdirs==4.3.8
# via virtualenv # via virtualenv
pyproject-hooks==1.2.0 pyproject-hooks==1.2.0
# via build # via build
typing-extensions==4.13.2 typing-extensions==4.14.1
# via delocate # via delocate
virtualenv==20.31.2 virtualenv==20.32.0
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
@@ -6,7 +6,7 @@ build==1.2.2.post1
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
delocate==0.13.0 delocate==0.13.0
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
distlib==0.3.9 distlib==0.4.0
# via virtualenv # via virtualenv
filelock==3.18.0 filelock==3.18.0
# via virtualenv # via virtualenv
@@ -22,7 +22,7 @@ platformdirs==4.3.8
# via virtualenv # via virtualenv
pyproject-hooks==1.2.0 pyproject-hooks==1.2.0
# via build # via build
typing-extensions==4.13.2 typing-extensions==4.14.1
# via delocate # via delocate
virtualenv==20.31.2 virtualenv==20.32.0
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
@@ -6,7 +6,7 @@ build==1.2.2.post1
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
delocate==0.13.0 delocate==0.13.0
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
distlib==0.3.9 distlib==0.4.0
# via virtualenv # via virtualenv
filelock==3.18.0 filelock==3.18.0
# via virtualenv # via virtualenv
@@ -22,7 +22,7 @@ platformdirs==4.3.8
# via virtualenv # via virtualenv
pyproject-hooks==1.2.0 pyproject-hooks==1.2.0
# via build # via build
typing-extensions==4.13.2 typing-extensions==4.14.1
# via delocate # via delocate
virtualenv==20.31.2 virtualenv==20.32.0
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
@@ -6,7 +6,7 @@ build==1.2.2.post1
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
delocate==0.12.0 delocate==0.12.0
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
distlib==0.3.9 distlib==0.4.0
# via virtualenv # via virtualenv
filelock==3.16.1 filelock==3.16.1
# via virtualenv # via virtualenv
@@ -28,7 +28,7 @@ tomli==2.2.1
# via build # via build
typing-extensions==4.13.2 typing-extensions==4.13.2
# via delocate # via delocate
virtualenv==20.31.2 virtualenv==20.32.0
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
zipp==3.20.2 zipp==3.20.2
# via importlib-metadata # via importlib-metadata
@@ -6,7 +6,7 @@ build==1.2.2.post1
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
delocate==0.13.0 delocate==0.13.0
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
distlib==0.3.9 distlib==0.4.0
# via virtualenv # via virtualenv
filelock==3.18.0 filelock==3.18.0
# via virtualenv # via virtualenv
@@ -26,9 +26,9 @@ pyproject-hooks==1.2.0
# via build # via build
tomli==2.2.1 tomli==2.2.1
# via build # via build
typing-extensions==4.13.2 typing-extensions==4.14.1
# via delocate # via delocate
virtualenv==20.31.2 virtualenv==20.32.0
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
zipp==3.22.0 zipp==3.23.0
# via importlib-metadata # via importlib-metadata
+3 -3
View File
@@ -6,7 +6,7 @@ build==1.2.2.post1
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
delocate==0.13.0 delocate==0.13.0
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
distlib==0.3.9 distlib==0.4.0
# via virtualenv # via virtualenv
filelock==3.18.0 filelock==3.18.0
# via virtualenv # via virtualenv
@@ -22,7 +22,7 @@ platformdirs==4.3.8
# via virtualenv # via virtualenv
pyproject-hooks==1.2.0 pyproject-hooks==1.2.0
# via build # via build
typing-extensions==4.13.2 typing-extensions==4.14.1
# via delocate # via delocate
virtualenv==20.31.2 virtualenv==20.32.0
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
+6 -4
View File
@@ -31,14 +31,14 @@ container-engine = "docker"
pyodide-version = "" pyodide-version = ""
manylinux-x86_64-image = "manylinux_2_28" manylinux-x86_64-image = "manylinux_2_28"
manylinux-i686-image = "manylinux2014" manylinux-i686-image = "manylinux_2_28"
manylinux-aarch64-image = "manylinux_2_28" manylinux-aarch64-image = "manylinux_2_28"
manylinux-ppc64le-image = "manylinux_2_28" manylinux-ppc64le-image = "manylinux_2_28"
manylinux-s390x-image = "manylinux_2_28" manylinux-s390x-image = "manylinux_2_28"
manylinux-armv7l-image = "manylinux_2_31" manylinux-armv7l-image = "manylinux_2_31"
manylinux-riscv64-image = "ghcr.io/pypa/cibuildwheel/no_default_image:please_use_override" manylinux-riscv64-image = "manylinux_2_39"
manylinux-pypy_x86_64-image = "manylinux_2_28" manylinux-pypy_x86_64-image = "manylinux_2_28"
manylinux-pypy_i686-image = "manylinux2014" manylinux-pypy_i686-image = "manylinux_2_28"
manylinux-pypy_aarch64-image = "manylinux_2_28" manylinux-pypy_aarch64-image = "manylinux_2_28"
musllinux-x86_64-image = "musllinux_1_2" musllinux-x86_64-image = "musllinux_1_2"
@@ -47,7 +47,7 @@ musllinux-aarch64-image = "musllinux_1_2"
musllinux-ppc64le-image = "musllinux_1_2" musllinux-ppc64le-image = "musllinux_1_2"
musllinux-s390x-image = "musllinux_1_2" musllinux-s390x-image = "musllinux_1_2"
musllinux-armv7l-image = "musllinux_1_2" musllinux-armv7l-image = "musllinux_1_2"
musllinux-riscv64-image = "ghcr.io/pypa/cibuildwheel/no_default_image:please_use_override" musllinux-riscv64-image = "musllinux_1_2"
[tool.cibuildwheel.linux] [tool.cibuildwheel.linux]
@@ -58,6 +58,8 @@ repair-wheel-command = "delocate-wheel --require-archs {delocate_archs} -w {dest
[tool.cibuildwheel.windows] [tool.cibuildwheel.windows]
[tool.cibuildwheel.android]
[tool.cibuildwheel.ios] [tool.cibuildwheel.ios]
[tool.cibuildwheel.pyodide] [tool.cibuildwheel.pyodide]
+1 -1
View File
@@ -33,7 +33,7 @@ def main() -> None:
[sys.executable, "-E", "-s", "-m", "pip", "install", "--upgrade", "certifi"] [sys.executable, "-E", "-s", "-m", "pip", "install", "--upgrade", "certifi"]
) )
import certifi import certifi # noqa: PLC0415
# change working directory to the default SSL directory # change working directory to the default SSL directory
os.chdir(openssl_dir) os.chdir(openssl_dir)
+2 -2
View File
@@ -1,3 +1,3 @@
url = "https://nodejs.org/dist/" url = "https://nodejs.org/dist/"
v22 = "v22.16.0" v22 = "v22.17.1"
v20 = "v20.19.2" v20 = "v20.19.4"
+33 -27
View File
@@ -1,47 +1,53 @@
[x86_64] [x86_64]
manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.05.31-1 manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.07.27-1
manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.05.28-1 manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.07.27-1
manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.05.31-1 manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.07.27-1
musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.05.31-1 musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.07.27-1
[i686] [i686]
manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.05.31-1 manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.07.27-1
musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.05.31-1 manylinux_2_28 = quay.io/pypa/manylinux_2_28_i686:2025.07.27-1
manylinux_2_34 = quay.io/pypa/manylinux_2_34_i686:2025.07.27-1
musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.07.27-1
[aarch64] [aarch64]
manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.05.31-1 manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.07.27-1
manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.05.28-1 manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.07.27-1
manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.05.31-1 manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.07.27-1
musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.05.31-1 musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.07.27-1
[ppc64le] [ppc64le]
manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.05.31-1 manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.07.27-1
manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.05.28-1 manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.07.27-1
manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.05.31-1 manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.07.27-1
musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.05.31-1 musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.07.27-1
[s390x] [s390x]
manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.05.31-1 manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.07.27-1
manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.05.28-1 manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.07.27-1
manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.05.31-1 manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.07.27-1
musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.05.31-1 musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.07.27-1
[pypy_x86_64] [pypy_x86_64]
manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.05.31-1 manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.07.27-1
manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.05.28-1 manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.07.27-1
manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.05.31-1 manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.07.27-1
[pypy_i686] [pypy_i686]
manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.05.31-1 manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.07.27-1
manylinux_2_28 = quay.io/pypa/manylinux_2_28_i686:2025.07.27-1
manylinux_2_34 = quay.io/pypa/manylinux_2_34_i686:2025.07.27-1
[pypy_aarch64] [pypy_aarch64]
manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.05.31-1 manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.07.27-1
manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.05.28-1 manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.07.27-1
manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.05.31-1 manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.07.27-1
[armv7l] [armv7l]
manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.05.31-1 manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.07.27-1
musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.05.31-1 musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.07.27-1
[riscv64] [riscv64]
manylinux_2_39 = quay.io/pypa/manylinux_2_39_riscv64:2025.07.27-1
musllinux_1_2 = quay.io/pypa/musllinux_1_2_riscv64:2025.07.27-1
@@ -1,439 +1,455 @@
{ {
"releases": [ "releases": [
{ {
"tag": "20250529", "tag": "20250723",
"assets": [ "assets": [
{ {
"name": "cpython-3.10.17+20250529-aarch64-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.10.18+20250723-aarch64-apple-darwin-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-aarch64-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.10.18%2B20250723-aarch64-apple-darwin-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.10.17+20250529-aarch64-apple-darwin-install_only.tar.gz", "name": "cpython-3.10.18+20250723-aarch64-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-aarch64-apple-darwin-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.10.18%2B20250723-aarch64-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.10.17+20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz", "name": "cpython-3.10.18+20250723-armv7-unknown-linux-gnueabi-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.10.18%2B20250723-armv7-unknown-linux-gnueabi-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.10.17+20250529-armv7-unknown-linux-gnueabihf-install_only.tar.gz", "name": "cpython-3.10.18+20250723-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-armv7-unknown-linux-gnueabihf-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.10.18%2B20250723-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.10.17+20250529-i686-pc-windows-msvc-install_only.tar.gz", "name": "cpython-3.10.18+20250723-i686-pc-windows-msvc-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-i686-pc-windows-msvc-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.10.18%2B20250723-i686-pc-windows-msvc-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.10.17+20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.10.18+20250723-ppc64le-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.10.18%2B20250723-ppc64le-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.10.17+20250529-riscv64-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.10.18+20250723-riscv64-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-riscv64-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.10.18%2B20250723-riscv64-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.10.17+20250529-s390x-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.10.18+20250723-s390x-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-s390x-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.10.18%2B20250723-s390x-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.10.17+20250529-x86_64-apple-darwin-install_only.tar.gz", "name": "cpython-3.10.18+20250723-x86_64-apple-darwin-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-x86_64-apple-darwin-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.10.18%2B20250723-x86_64-apple-darwin-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.10.17+20250529-x86_64-pc-windows-msvc-install_only.tar.gz", "name": "cpython-3.10.18+20250723-x86_64-pc-windows-msvc-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-x86_64-pc-windows-msvc-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.10.18%2B20250723-x86_64-pc-windows-msvc-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.10.17+20250529-x86_64-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.10.18+20250723-x86_64-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-x86_64-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.10.18%2B20250723-x86_64-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.10.17+20250529-x86_64-unknown-linux-musl-install_only.tar.gz", "name": "cpython-3.10.18+20250723-x86_64-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-x86_64-unknown-linux-musl-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.10.18%2B20250723-x86_64-unknown-linux-musl-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.10.17+20250529-x86_64_v2-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.10.18+20250723-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-x86_64_v2-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.10.18%2B20250723-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.10.17+20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz", "name": "cpython-3.10.18+20250723-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.10.18%2B20250723-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.10.17+20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.10.18+20250723-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.10.18%2B20250723-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.10.17+20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz", "name": "cpython-3.10.18+20250723-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.10.18%2B20250723-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.10.17+20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.10.18+20250723-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.10.18%2B20250723-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.10.17+20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz", "name": "cpython-3.10.18+20250723-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.10.18%2B20250723-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.11.12+20250529-aarch64-apple-darwin-install_only.tar.gz", "name": "cpython-3.11.13+20250723-aarch64-apple-darwin-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-aarch64-apple-darwin-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-aarch64-apple-darwin-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.11.12+20250529-aarch64-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.11.13+20250723-aarch64-pc-windows-msvc-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-aarch64-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-aarch64-pc-windows-msvc-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.11.12+20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz", "name": "cpython-3.11.13+20250723-aarch64-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-aarch64-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.11.12+20250529-armv7-unknown-linux-gnueabihf-install_only.tar.gz", "name": "cpython-3.11.13+20250723-armv7-unknown-linux-gnueabi-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-armv7-unknown-linux-gnueabihf-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-armv7-unknown-linux-gnueabi-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.11.12+20250529-i686-pc-windows-msvc-install_only.tar.gz", "name": "cpython-3.11.13+20250723-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-i686-pc-windows-msvc-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.11.12+20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.11.13+20250723-i686-pc-windows-msvc-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-i686-pc-windows-msvc-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.11.12+20250529-riscv64-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.11.13+20250723-ppc64le-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-riscv64-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-ppc64le-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.11.12+20250529-s390x-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.11.13+20250723-riscv64-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-s390x-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-riscv64-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.11.12+20250529-x86_64-apple-darwin-install_only.tar.gz", "name": "cpython-3.11.13+20250723-s390x-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-x86_64-apple-darwin-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-s390x-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.11.12+20250529-x86_64-pc-windows-msvc-install_only.tar.gz", "name": "cpython-3.11.13+20250723-x86_64-apple-darwin-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-x86_64-pc-windows-msvc-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-x86_64-apple-darwin-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.11.12+20250529-x86_64-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.11.13+20250723-x86_64-pc-windows-msvc-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-x86_64-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-x86_64-pc-windows-msvc-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.11.12+20250529-x86_64-unknown-linux-musl-install_only.tar.gz", "name": "cpython-3.11.13+20250723-x86_64-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-x86_64-unknown-linux-musl-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-x86_64-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.11.12+20250529-x86_64_v2-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.11.13+20250723-x86_64-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-x86_64_v2-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-x86_64-unknown-linux-musl-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.11.12+20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz", "name": "cpython-3.11.13+20250723-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.11.12+20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.11.13+20250723-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.11.12+20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz", "name": "cpython-3.11.13+20250723-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.11.12+20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.11.13+20250723-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.11.12+20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz", "name": "cpython-3.11.13+20250723-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.12.10+20250529-aarch64-apple-darwin-install_only.tar.gz", "name": "cpython-3.11.13+20250723-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-aarch64-apple-darwin-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.12.10+20250529-aarch64-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.12.11+20250723-aarch64-apple-darwin-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-aarch64-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-aarch64-apple-darwin-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.12.10+20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz", "name": "cpython-3.12.11+20250723-aarch64-pc-windows-msvc-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-aarch64-pc-windows-msvc-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.12.10+20250529-armv7-unknown-linux-gnueabihf-install_only.tar.gz", "name": "cpython-3.12.11+20250723-aarch64-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-armv7-unknown-linux-gnueabihf-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-aarch64-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.12.10+20250529-i686-pc-windows-msvc-install_only.tar.gz", "name": "cpython-3.12.11+20250723-armv7-unknown-linux-gnueabi-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-i686-pc-windows-msvc-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-armv7-unknown-linux-gnueabi-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.12.10+20250529-riscv64-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.12.11+20250723-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-riscv64-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.12.10+20250529-x86_64-pc-windows-msvc-install_only.tar.gz", "name": "cpython-3.12.11+20250723-i686-pc-windows-msvc-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-x86_64-pc-windows-msvc-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-i686-pc-windows-msvc-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.12.10+20250529-x86_64-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.12.11+20250723-ppc64le-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-x86_64-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-ppc64le-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.12.10+20250529-x86_64-unknown-linux-musl-install_only.tar.gz", "name": "cpython-3.12.11+20250723-riscv64-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-x86_64-unknown-linux-musl-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-riscv64-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.12.10+20250529-x86_64_v2-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.12.11+20250723-s390x-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-x86_64_v2-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-s390x-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.12.10+20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz", "name": "cpython-3.12.11+20250723-x86_64-apple-darwin-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-x86_64-apple-darwin-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.12.10+20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.12.11+20250723-x86_64-pc-windows-msvc-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-x86_64-pc-windows-msvc-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.12.10+20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz", "name": "cpython-3.12.11+20250723-x86_64-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-x86_64-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.12.10+20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.12.11+20250723-x86_64-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-x86_64-unknown-linux-musl-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.12.10+20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz", "name": "cpython-3.12.11+20250723-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.13.3+20250529-aarch64-apple-darwin-install_only.tar.gz", "name": "cpython-3.12.11+20250723-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-aarch64-apple-darwin-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.13.3+20250529-aarch64-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.12.11+20250723-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-aarch64-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.13.3+20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz", "name": "cpython-3.12.11+20250723-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.13.3+20250529-armv7-unknown-linux-gnueabihf-install_only.tar.gz", "name": "cpython-3.12.11+20250723-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-armv7-unknown-linux-gnueabihf-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.13.3+20250529-i686-pc-windows-msvc-install_only.tar.gz", "name": "cpython-3.12.11+20250723-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-i686-pc-windows-msvc-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.13.3+20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.13.5+20250723-aarch64-apple-darwin-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-aarch64-apple-darwin-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.13.3+20250529-riscv64-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.13.5+20250723-aarch64-pc-windows-msvc-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-riscv64-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-aarch64-pc-windows-msvc-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.13.3+20250529-s390x-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.13.5+20250723-aarch64-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-s390x-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-aarch64-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.13.3+20250529-x86_64-apple-darwin-install_only.tar.gz", "name": "cpython-3.13.5+20250723-armv7-unknown-linux-gnueabi-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-x86_64-apple-darwin-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-armv7-unknown-linux-gnueabi-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.13.3+20250529-x86_64-pc-windows-msvc-install_only.tar.gz", "name": "cpython-3.13.5+20250723-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-x86_64-pc-windows-msvc-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.13.3+20250529-x86_64-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.13.5+20250723-i686-pc-windows-msvc-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-x86_64-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-i686-pc-windows-msvc-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.13.3+20250529-x86_64-unknown-linux-musl-install_only.tar.gz", "name": "cpython-3.13.5+20250723-ppc64le-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-x86_64-unknown-linux-musl-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-ppc64le-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.13.3+20250529-x86_64_v2-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.13.5+20250723-riscv64-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-x86_64_v2-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-riscv64-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.13.3+20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz", "name": "cpython-3.13.5+20250723-s390x-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-s390x-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.13.3+20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.13.5+20250723-x86_64-apple-darwin-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-x86_64-apple-darwin-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.13.3+20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz", "name": "cpython-3.13.5+20250723-x86_64-pc-windows-msvc-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-x86_64-pc-windows-msvc-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.13.3+20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.13.5+20250723-x86_64-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-x86_64-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.13.3+20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz", "name": "cpython-3.13.5+20250723-x86_64-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-x86_64-unknown-linux-musl-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.14.0a7+20250529-aarch64-apple-darwin-install_only.tar.gz", "name": "cpython-3.13.5+20250723-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-aarch64-apple-darwin-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.14.0a7+20250529-aarch64-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.13.5+20250723-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-aarch64-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.14.0a7+20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz", "name": "cpython-3.13.5+20250723-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.14.0a7+20250529-armv7-unknown-linux-gnueabihf-install_only.tar.gz", "name": "cpython-3.13.5+20250723-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-armv7-unknown-linux-gnueabihf-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.14.0a7+20250529-i686-pc-windows-msvc-install_only.tar.gz", "name": "cpython-3.13.5+20250723-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-i686-pc-windows-msvc-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.14.0a7+20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.13.5+20250723-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.14.0a7+20250529-riscv64-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.14.0rc1+20250723-aarch64-apple-darwin-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-riscv64-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-aarch64-apple-darwin-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.14.0a7+20250529-s390x-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.14.0rc1+20250723-aarch64-pc-windows-msvc-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-s390x-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-aarch64-pc-windows-msvc-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.14.0a7+20250529-x86_64-apple-darwin-install_only.tar.gz", "name": "cpython-3.14.0rc1+20250723-aarch64-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-x86_64-apple-darwin-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-aarch64-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.14.0a7+20250529-x86_64-pc-windows-msvc-install_only.tar.gz", "name": "cpython-3.14.0rc1+20250723-armv7-unknown-linux-gnueabi-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-x86_64-pc-windows-msvc-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-armv7-unknown-linux-gnueabi-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.14.0a7+20250529-x86_64-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.14.0rc1+20250723-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-x86_64-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.14.0a7+20250529-x86_64-unknown-linux-musl-install_only.tar.gz", "name": "cpython-3.14.0rc1+20250723-i686-pc-windows-msvc-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-x86_64-unknown-linux-musl-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-i686-pc-windows-msvc-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.14.0a7+20250529-x86_64_v2-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.14.0rc1+20250723-ppc64le-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-x86_64_v2-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-ppc64le-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.14.0a7+20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz", "name": "cpython-3.14.0rc1+20250723-riscv64-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-riscv64-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.12.10+20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.14.0rc1+20250723-s390x-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-s390x-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.14.0a7+20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.14.0rc1+20250723-x86_64-apple-darwin-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-x86_64-apple-darwin-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.12.10+20250529-s390x-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.14.0rc1+20250723-x86_64-pc-windows-msvc-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-s390x-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-x86_64-pc-windows-msvc-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.14.0a7+20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz", "name": "cpython-3.14.0rc1+20250723-x86_64-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-x86_64-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.14.0a7+20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.14.0rc1+20250723-x86_64-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-x86_64-unknown-linux-musl-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.12.10+20250529-x86_64-apple-darwin-install_only.tar.gz", "name": "cpython-3.14.0rc1+20250723-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-x86_64-apple-darwin-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.14.0a7+20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz", "name": "cpython-3.14.0rc1+20250723-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.9.22+20250529-aarch64-apple-darwin-install_only.tar.gz", "name": "cpython-3.14.0rc1+20250723-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-aarch64-apple-darwin-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.9.22+20250529-aarch64-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.14.0rc1+20250723-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-aarch64-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.9.22+20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz", "name": "cpython-3.14.0rc1+20250723-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.9.22+20250529-armv7-unknown-linux-gnueabihf-install_only.tar.gz", "name": "cpython-3.14.0rc1+20250723-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-armv7-unknown-linux-gnueabihf-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.9.22+20250529-i686-pc-windows-msvc-install_only.tar.gz", "name": "cpython-3.9.23+20250723-aarch64-apple-darwin-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-i686-pc-windows-msvc-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.9.23%2B20250723-aarch64-apple-darwin-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.9.22+20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.9.23+20250723-aarch64-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.9.23%2B20250723-aarch64-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.9.22+20250529-riscv64-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.9.23+20250723-armv7-unknown-linux-gnueabi-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-riscv64-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.9.23%2B20250723-armv7-unknown-linux-gnueabi-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.9.22+20250529-s390x-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.9.23+20250723-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-s390x-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.9.23%2B20250723-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.9.22+20250529-x86_64-apple-darwin-install_only.tar.gz", "name": "cpython-3.9.23+20250723-i686-pc-windows-msvc-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-x86_64-apple-darwin-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.9.23%2B20250723-i686-pc-windows-msvc-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.9.22+20250529-x86_64-pc-windows-msvc-install_only.tar.gz", "name": "cpython-3.9.23+20250723-ppc64le-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-x86_64-pc-windows-msvc-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.9.23%2B20250723-ppc64le-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.9.22+20250529-x86_64-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.9.23+20250723-riscv64-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-x86_64-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.9.23%2B20250723-riscv64-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.9.22+20250529-x86_64-unknown-linux-musl-install_only.tar.gz", "name": "cpython-3.9.23+20250723-s390x-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-x86_64-unknown-linux-musl-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.9.23%2B20250723-s390x-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.9.22+20250529-x86_64_v2-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.9.23+20250723-x86_64-apple-darwin-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-x86_64_v2-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.9.23%2B20250723-x86_64-apple-darwin-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.9.22+20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz", "name": "cpython-3.9.23+20250723-x86_64-pc-windows-msvc-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.9.23%2B20250723-x86_64-pc-windows-msvc-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.9.22+20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.9.23+20250723-x86_64-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.9.23%2B20250723-x86_64-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.9.22+20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz", "name": "cpython-3.9.23+20250723-x86_64-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.9.23%2B20250723-x86_64-unknown-linux-musl-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.9.22+20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz", "name": "cpython-3.9.23+20250723-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.9.23%2B20250723-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
}, },
{ {
"name": "cpython-3.9.22+20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz", "name": "cpython-3.9.23+20250723-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz" "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.9.23%2B20250723-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
},
{
"name": "cpython-3.9.23+20250723-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.9.23%2B20250723-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.9.23+20250723-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.9.23%2B20250723-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
},
{
"name": "cpython-3.9.23+20250723-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.9.23%2B20250723-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.9.23+20250723-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.9.23%2B20250723-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
} }
] ]
} }
@@ -8,11 +8,11 @@ from typing import NoReturn
class TestStringMethods(unittest.TestCase): class TestStringMethods(unittest.TestCase):
def test_fail(self) -> NoReturn: def test_fail(self) -> NoReturn:
if sys.platform == "ios": if sys.platform in ["android", "ios"]:
msg = ( msg = (
"You tried to run tests from the testbed app's working " "You tried to run tests from the testbed app's working "
"directory, without specifying `test-sources`. " "directory, without specifying `test-sources`. "
"On iOS, you must copy your test files to the testbed app by " "On this platform, you must copy your test files to the testbed app by "
"setting the `test-sources` option in your cibuildwheel " "setting the `test-sources` option in your cibuildwheel "
"configuration." "configuration."
) )
+1 -1
View File
@@ -1 +1 @@
default = { version = "20.31.2", url = "https://github.com/pypa/get-virtualenv/blob/20.31.2/public/virtualenv.pyz?raw=true" } default = { version = "20.32.0", url = "https://github.com/pypa/get-virtualenv/blob/20.32.0/public/virtualenv.pyz?raw=true" }
+14 -11
View File
@@ -39,7 +39,7 @@ class EnableGroup(StrEnum):
@classmethod @classmethod
def all_groups(cls) -> frozenset["EnableGroup"]: def all_groups(cls) -> frozenset["EnableGroup"]:
return frozenset(cls) return frozenset(set(cls) - {cls.CPythonExperimentalRiscV64})
@classmethod @classmethod
def parse_option_value(cls, value: str) -> frozenset["EnableGroup"]: def parse_option_value(cls, value: str) -> frozenset["EnableGroup"]:
@@ -85,24 +85,27 @@ class BuildSelector:
return False return False
# filter out groups that are not enabled # filter out groups that are not enabled
if EnableGroup.CPythonFreeThreading not in self.enable and fnmatch(build_id, "cp3??t-*"): if EnableGroup.CPythonFreeThreading not in self.enable and fnmatch(build_id, "cp313t-*"):
return False return False
if EnableGroup.CPythonPrerelease not in self.enable and fnmatch(build_id, "cp314*"): if EnableGroup.CPythonPrerelease not in self.enable and fnmatch(build_id, "cp315*"):
return False return False
if EnableGroup.PyPy not in self.enable and fnmatch(build_id, "pp31*"): if EnableGroup.PyPy not in self.enable and fnmatch(build_id, "pp31*"):
return False return False
if EnableGroup.PyPyEoL not in self.enable and fnmatch(build_id, "pp3?-*"): if EnableGroup.PyPyEoL not in self.enable and fnmatch(build_id, "pp3?-*"):
return False return False
if EnableGroup.CPythonExperimentalRiscV64 not in self.enable and fnmatch(
build_id, "*_riscv64"
):
return False
if EnableGroup.GraalPy not in self.enable and fnmatch(build_id, "gp*"): if EnableGroup.GraalPy not in self.enable and fnmatch(build_id, "gp*"):
return False return False
if EnableGroup.PyodidePrerelease not in self.enable and fnmatch( # TODO: Re-enable this when we have Pyodide prereleases again (e.g., 0.29.0a1+)
build_id, "cp313-pyodide_*" # Python 3.13 support became stable in Pyodide 0.28.0, so it no longer needs a prerelease
): # flag.
return False # Also update Pyodide tests in unit_test/build_selector_test.py accordingly.
# When re-enabling, update the pattern to match the experimental Python version in case
# it is bumped to Python 3.14 (likely cp314-pyodide_* but could remain as 3.13 as well).
# This depends on the CPython version being used in the Pyodide runtime at the time.
# if EnableGroup.PyodidePrerelease not in self.enable and fnmatch(
# build_id, "cp313-pyodide_*"
# ):
# return False
should_build = selector_matches(self.build_config, build_id) should_build = selector_matches(self.build_config, build_id)
should_skip = selector_matches(self.skip_config, build_id) should_skip = selector_matches(self.skip_config, build_id)
+1 -1
View File
@@ -12,7 +12,7 @@ __all__ = (
PathOrStr = str | os.PathLike[str] PathOrStr = str | os.PathLike[str]
PlatformName = Literal["linux", "macos", "windows", "pyodide", "ios"] PlatformName = Literal["linux", "macos", "windows", "pyodide", "android", "ios"]
PLATFORMS: Final[frozenset[PlatformName]] = frozenset(typing.get_args(PlatformName)) PLATFORMS: Final[frozenset[PlatformName]] = frozenset(typing.get_args(PlatformName))
+8 -23
View File
@@ -1,3 +1,4 @@
import dataclasses
import itertools import itertools
import os import os
import re import re
@@ -5,7 +6,6 @@ import shlex
import textwrap import textwrap
from collections import defaultdict from collections import defaultdict
from collections.abc import Sequence from collections.abc import Sequence
from functools import total_ordering
from ..typing import PathOrStr from ..typing import PathOrStr
@@ -140,19 +140,17 @@ def parse_key_value_string(
return dict(result) return dict(result)
@total_ordering @dataclasses.dataclass(order=True)
class FlexibleVersion: class FlexibleVersion:
version_str: str version_parts: tuple[int, ...] = dataclasses.field(init=False, repr=False)
version_parts: tuple[int, ...] suffix: str = dataclasses.field(init=False, repr=False)
suffix: str version_str: str = dataclasses.field(compare=False)
def __init__(self, version_str: str) -> None:
self.version_str = version_str
def __post_init__(self) -> None:
# Split into numeric parts and the optional suffix # Split into numeric parts and the optional suffix
match = re.match(r"^[v]?(\d+(\.\d+)*)(.*)$", version_str) match = re.match(r"^[v]?(\d+(\.\d+)*)(.*)$", self.version_str)
if not match: if not match:
msg = f"Invalid version string: {version_str}" msg = f"Invalid version string: {self.version_str}"
raise ValueError(msg) raise ValueError(msg)
version_part, _, suffix = match.groups() version_part, _, suffix = match.groups()
@@ -172,18 +170,5 @@ class FlexibleVersion:
parts = parts[:-1] parts = parts[:-1]
return parts return parts
def __eq__(self, other: object) -> bool:
if not isinstance(other, FlexibleVersion):
raise NotImplementedError()
return (self.version_parts, self.suffix) == (other.version_parts, other.suffix)
def __lt__(self, other: object) -> bool:
if not isinstance(other, FlexibleVersion):
raise NotImplementedError()
return (self.version_parts, self.suffix) < (other.version_parts, other.suffix)
def __repr__(self) -> str:
return f"FlexibleVersion('{self.version_str}')"
def __str__(self) -> str: def __str__(self) -> str:
return self.version_str return self.version_str
+3 -3
View File
@@ -158,9 +158,9 @@ def find_compatible_wheel(wheels: Sequence[T], identifier: str) -> T | None:
# If a minor version number is given, it has to be lower than the current one. # If a minor version number is given, it has to be lower than the current one.
continue continue
if platform.startswith(("manylinux", "musllinux", "macosx", "ios")): if platform.startswith(("manylinux", "musllinux", "macosx", "android", "ios")):
# Linux, macOS, and iOS require the beginning and ending match # On these platforms the wheel tag includes a platform version number, which we
# (macos/manylinux/iOS version number doesn't need to match) # should ignore.
os_, arch = platform.split("_", 1) os_, arch = platform.split("_", 1)
if not tag.platform.startswith(os_): if not tag.platform.startswith(os_):
continue continue
+6 -3
View File
@@ -22,8 +22,11 @@ TEST_FAIL_CWD_FILE: Final[Path] = PATH / "testing_temp_dir_file.py"
# this value is cached because it's used a lot in unit tests # this value is cached because it's used a lot in unit tests
@functools.cache @functools.cache
def read_python_configs(config: PlatformName) -> list[dict[str, str]]: def read_all_configs() -> dict[str, list[dict[str, str]]]:
with BUILD_PLATFORMS.open("rb") as f: with BUILD_PLATFORMS.open("rb") as f:
loaded_file = tomllib.load(f) loaded_file = tomllib.load(f)
results: list[dict[str, str]] = list(loaded_file[config]["python_configurations"]) return {k: list[dict[str, str]](v["python_configurations"]) for k, v in loaded_file.items()}
return results
def read_python_configs(config: PlatformName) -> list[dict[str, str]]:
return read_all_configs()[config]
+1 -2
View File
@@ -157,8 +157,7 @@ def virtualenv(
def find_uv() -> Path | None: def find_uv() -> Path | None:
# Prefer uv in our environment # Prefer uv in our environment
with contextlib.suppress(ImportError, FileNotFoundError): with contextlib.suppress(ImportError, FileNotFoundError):
# pylint: disable-next=import-outside-toplevel from uv import find_uv_bin # noqa: PLC0415
from uv import find_uv_bin
return Path(find_uv_bin()) return Path(find_uv_bin())
+74 -64
View File
@@ -4,80 +4,90 @@ title: Changelog
# Changelog # Changelog
### v3.1.2
_29 July 2025_
- ⚠️ Add an error if `CIBW_FREE_THREADING_SUPPORT` is set; you are likely missing 3.13t wheels, please use the `enable`/`CIBW_ENABLE` (#2520)
- 🛠 `riscv64` now enabled if you target that architecture, it's now supported on PyPI (#2509)
- 🛠 Add warning when using `cpython-experimental-riscv64` (no longer needed) (#2526, #2528)
- 🛠 iOS versions bumped, fixing issues with 3.14 (now RC 1) (#2530)
- 🐛 Fix bug in Android running wheel from our GitHub Action (#2517)
- 🐛 Fix warning when using `test-skip` of `"*-macosx_universal2:arm64"` (#2522)
- 🐛 Fix incorrect number of wheels reported in logs, again (#2517)
- 📚 We welcome our Android platform maintainer (#2516)
### v3.1.1
_24 July 2025_
- 🐛 Fix a bug showing an incorrect wheel count at the end of execution, and misrepresenting test-only runs in the GitHub Action summary (#2512)
- 📚 Docs fix (#2510)
### v3.1.0
_23 July 2025_
- 🌟 CPython 3.14 wheels are now built by default - without the `"cpython-prerelease"` `enable` set. It's time to build and upload these wheels to PyPI! This release includes CPython 3.14.0rc1, which is guaranteed to be ABI compatible with the final release. (#2507) Free-threading is no longer experimental in 3.14, so you have to skip it explicitly with `'cp31?t-*'` if you don't support it yet. (#2503)
- 🌟 Adds the ability to [build wheels for Android](https://cibuildwheel.pypa.io/en/stable/platforms/#android)! Set the [`platform` option](https://cibuildwheel.pypa.io/en/stable/options/#platform) to `android` on Linux or macOS to try it out! (#2349)
- 🌟 Adds Pyodide 0.28, which builds 3.13 wheels (#2487)
- ✨ Support for 32-bit `manylinux_2_28` (now a consistent default) and `manylinux_2_34` added (#2500)
- 🛠 Improved summary, will also use markdown summary output on GHA (#2469)
- 🛠 The riscv64 images now have a working default (as they are now part of pypy/manylinux), but are still experimental (and behind an `enable`) since you can't push them to PyPI yet (#2506)
- 🛠 Fixed a typo in the 3.9 MUSL riscv64 identifier (`cp39-musllinux_ricv64` -> `cp39-musllinux_riscv64`) (#2490)
- 🛠 Mistyping `--only` now shows the correct possibilities, and even suggests near matches on Python 3.14+ (#2499)
- 🛠 Only support one output from the repair step on linux like other platforms; auditwheel fixed this over four years ago! (#2478)
- 🛠 We now use pattern matching extensively (#2434)
- 📚 We now have platform maintainers for our special platforms and interpreters! (#2481)
### v3.0.1
_5 July 2025_
- 🛠 Updates CPython 3.14 prerelease to 3.14.0b3 (#2471)
- ✨ Adds a CPython 3.14 prerelease iOS build (only when prerelease builds are [enabled](https://cibuildwheel.pypa.io/en/stable/options/#enable)) (#2475)
### v3.0.0 ### v3.0.0
Not yet released, but available for testing. _11 June 2025_
Note - when using a beta version, be sure to check the [latest docs](https://cibuildwheel.pypa.io/en/latest/), rather than the stable version, which is still on v2.X. See @henryiii's [release post](https://iscinumpy.dev/post/cibuildwheel-3-0-0/) for more info on new features!
<!-- - 🌟 Adds the ability to [build wheels for iOS](https://cibuildwheel.pypa.io/en/stable/platforms/#ios)! Set the [`platform` option](https://cibuildwheel.pypa.io/en/stable/options/#platform) to `ios` on a Mac with the iOS toolchain to try it out! (#2286, #2363, #2432)
note to self, when doing final release, change to docs URLs in this section to the stable version! - 🌟 Adds support for the GraalPy interpreter! Enable for your project using the [`enable` option](https://cibuildwheel.pypa.io/en/stable/options/#enable). (#1538, #2411, #2414)
--> - ✨ Adds CPython 3.14 support, under the [`enable` option](https://cibuildwheel.pypa.io/en/stable/options/#enable) `cpython-prerelease`. This version of cibuildwheel uses 3.14.0b2. (#2390)
If you've used previous versions of the beta:
- ⚠️ Previous betas of v3.0 changed the working directory for tests. This has been rolled back to the v2.x behaviour, so you might need to change configs if you adapted to the beta 1 or 2 behaviour. See [issue #2406](https://github.com/pypa/cibuildwheel/issues/2406) for more information.
- ⚠️ GraalPy shipped with the identifier `gp242-*` in previous betas, this has been changed to `gp311_242-*` to be consistent with other interpreters, and to fix a bug with GraalPy and project requires-python detection. If you were using GraalPy, you might need to update your config to use the new identifier.
- ⚠️ `test-sources` now uses `project` directory instead of the `package` directory (matching the docs).
#### v3.0.0b5
_3 June 2025_
- ✨ Support multiple commands on iOS, joined by `&&`, like the other platforms. (#2432)
- ✨ Add `pyodide-prerelease` enable option, with an early build of 0.28 (Python 3.13). (#2431)
- 🛠 test-sources now uses the `project` directory instead of the `package` directory (matching the docs). (#2437)
- 🛠 Fixed a bug with GraalPy if vsdevcmd prints an error. Cirrus CI works again. (#2414)
- 🛠 Use the standard Schema line for the integrated JSONSchema. (#2433)
- 📚 Use Python 3.14 color output in docs CLI output. (#2407)
#### v3.0.0b4
_29 May 2025_
- 🛠 Dependency updates, including Python 3.14.0b2. (#2371)
- 🛠 Remove the addition of `PYTHONSAFEPATH` to `test-environment`. (#2429)
- 📚 README table now matches docs and auto-updates. (#2427, #2428)
#### v3.0.0b3
_28 May 2025_
- 🛠 Reverts the test working dir (when test-sources isn't set) to a temporary dir, rather than the project. (#2420)
- 📚 Docs now primarily use the pyproject.toml name of options, rather than the environment variable name. (#2389)
#### v3.0.0b2
_25 May 2025_
- ✨ Adds the [`CIBW_TEST_ENVIRONMENT`](https://cibuildwheel.pypa.io/en/latest/options/#test-environment) option, which allows you to set environment variables for the test command. cibuildwheel now sets `PYTHONSAFEPATH=1` in test environments by default, to avoid picking up package imports from the local directory - we want to test the installed wheel, not the source tree! You can change that, or any other environment variable in the test environment using this option. (#2388)
- ✨ Improves support for Pyodide builds and adds the [`CIBW_PYODIDE_VERSION`](https://cibuildwheel.pypa.io/en/latest/options/#pyodide-version) option, which allows you to specify the version of Pyodide to use for builds. (#2002)
#### v3.0.0b1
_19 May 2025_
- 🌟 Adds the ability to [build wheels for iOS](https://cibuildwheel.pypa.io/en/latest/platforms/#ios)! Set the [`platform` option](https://cibuildwheel.pypa.io/en/latest/options/#platform) to `ios` on a Mac with the iOS toolchain to try it out!
- 🌟 Adds support for the GraalPy interpreter! Enable for your project using the [`enable` option](https://cibuildwheel.pypa.io/en/latest/options/#enable). (#1538)
- ✨ Adds CPython 3.14 support, under the [`enable` option](https://cibuildwheel.pypa.io/en/latest/options/#enable) `cpython-prerelease`. This version of cibuildwheel uses 3.14.0b1.
_While CPython is in beta, the ABI can change, so your wheels might not be compatible with the final release. For this reason, we don't recommend distributing wheels until RC1, at which point 3.14 will be available in cibuildwheel without the flag._ (#2390) _While CPython is in beta, the ABI can change, so your wheels might not be compatible with the final release. For this reason, we don't recommend distributing wheels until RC1, at which point 3.14 will be available in cibuildwheel without the flag._ (#2390)
- ✨ Adds the [test-sources option](https://cibuildwheel.pypa.io/en/latest/options/#test-sources), and changes the working directory for tests.
- If this option is set, cibuildwheel will copy the files and folders specified in `test-sources` into a temporary directory, and run the tests from there. This is required for iOS builds, but also useful for other platforms, as it allows you to test the installed wheel without any chance of accidentally importing from the source tree. - ✨ Adds the [test-sources option](https://cibuildwheel.pypa.io/en/stable/options/#test-sources), which copies files and folders into the temporary working directory we run tests from. (#2062, #2284, #2420, #2437)
- ~If this option is not set, cibuildwheel will run the tests in the source tree. This is a change from the previous behavior, where cibuildwheel would run the tests from a temporary directory. We're still investigating what's best here, so if you encounter any issues with this, please let us know in [issue #2406](https://github.com/pypa/cibuildwheel/issues/2406).~
- If this option is not set, behaviour matches v2.x - cibuildwheel will run the tests from a temporary directory, and you can use the `{project}` placeholder in the `test-command` to refer to the project directory.
- ✨ Added´ `dependency-versions` inline syntax (#2123) This is particularly important for iOS builds, which do not support placeholders in the `test-command`, but can also be useful for other platforms.
- 🛠 The default [manylinux image](https://cibuildwheel.pypa.io/en/latest/options/#linux-image) has changed from `manylinux2014` to `manylinux_2_28` (#2330)
- 🛠 Invokes `build` rather than `pip wheel` to build wheels by default. You can control this via the [`build-frontend`](https://cibuildwheel.pypa.io/en/latest/options/#build-frontend) option. You might notice that you can see your build log output now! (#2321) - ✨ Adds [`dependency-versions`](https://cibuildwheel.pypa.io/en/stable/options/#dependency-versions) inline syntax (#2122)
- 🛠 Removed the `CIBW_PRERELEASE_PYTHONS` and `CIBW_FREE_THREADED_SUPPORT` options - these have been folded into the [`enable`](https://cibuildwheel.pypa.io/en/latest/options/#enable) option instead. (#2095) - ✨ Improves support for Pyodide builds and adds the experimental [`pyodide-version`](https://cibuildwheel.pypa.io/en/stable/options/#pyodide-version) option, which allows you to specify the version of Pyodide to use for builds. (#2002)
- 🛠 EOL images `manylinux1`, `manylinux2010`, `manylinux_2_24` and `musllinux_1_1` can no longer be specified by their shortname. The full OCI name can still be used for these images, if you wish (#2316) - ✨ Add `pyodide-prerelease` [enable](https://cibuildwheel.pypa.io/en/stable/options/#enable) option, with an early build of 0.28 (Python 3.13). (#2431)
- ✨ Adds the [`test-environment`](https://cibuildwheel.pypa.io/en/stable/options/#test-environment) option, which allows you to set environment variables for the test command. (#2388)
- ✨ Adds the [`xbuild-tools`](https://cibuildwheel.pypa.io/en/stable/options/#xbuild-tools) option, which allows you to specify tools safe for cross-compilation. Currently only used on iOS; will be useful for Android in the future. (#2317)
- 🛠 The default [manylinux image](https://cibuildwheel.pypa.io/en/stable/options/#linux-image) has changed from `manylinux2014` to `manylinux_2_28`. (#2330)
- 🛠 EOL images `manylinux1`, `manylinux2010`, `manylinux_2_24` and `musllinux_1_1` can no longer be specified by their shortname. The full OCI name can still be used for these images, if you wish. (#2316)
- 🛠 Invokes `build` rather than `pip wheel` to build wheels by default. You can control this via the [`build-frontend`](https://cibuildwheel.pypa.io/en/stable/options/#build-frontend) option. You might notice that you can see your build log output now! (#2321)
- 🛠 Build verbosity settings have been reworked to have consistent meanings between build backends when non-zero. (#2339)
- 🛠 Removed the `CIBW_PRERELEASE_PYTHONS` and `CIBW_FREE_THREADED_SUPPORT` options - these have been folded into the [`enable`](https://cibuildwheel.pypa.io/en/stable/options/#enable) option instead. (#2095)
- 🛠 Build environments no longer have setuptools and wheel preinstalled. (#2329) - 🛠 Build environments no longer have setuptools and wheel preinstalled. (#2329)
- 🛠 Use the standard Schema line for the integrated JSONSchema. (#2433)
- ⚠️ Dropped support for building Python 3.6 and 3.7 wheels. If you need to build wheels for these versions, use cibuildwheel v2.23.3 or earlier. (#2282) - ⚠️ Dropped support for building Python 3.6 and 3.7 wheels. If you need to build wheels for these versions, use cibuildwheel v2.23.3 or earlier. (#2282)
- ⚠️ The minimum Python version required to run cibuildwheel is now Python 3.11. You can still build wheels for Python 3.8 and newer. (#1912) - ⚠️ The minimum Python version required to run cibuildwheel is now Python 3.11. You can still build wheels for Python 3.8 and newer. (#1912)
- ⚠️ PyPy wheels no longer built by default, due to a change to our options system. To continue building PyPy wheels, you'll now need to set the [`enable` option](https://cibuildwheel.pypa.io/en/latest/options/#enable) to `pypy` or `pypy-eol`. (#2095) - ⚠️ 32-bit Linux wheels no longer built by default - the [arch](https://cibuildwheel.pypa.io/en/stable/options/#archs) was removed from `"auto"`. It now requires explicit `"auto32"`. Note that modern manylinux images (like the new default, `manylinux_2_28`) do not have 32-bit versions. (#2458)
- ⚠️ PyPy wheels no longer built by default, due to a change to our options system. To continue building PyPy wheels, you'll now need to set the [`enable` option](https://cibuildwheel.pypa.io/en/stable/options/#enable) to `pypy` or `pypy-eol`. (#2095)
- ⚠️ Dropped official support for Appveyor. If it was working for you before, it will probably continue to do so, but we can't be sure, because our CI doesn't run there anymore. (#2386) - ⚠️ Dropped official support for Appveyor. If it was working for you before, it will probably continue to do so, but we can't be sure, because our CI doesn't run there anymore. (#2386)
- 📚 A reorganisation of the docs, and numerous updates (#2280) - 📚 A reorganisation of the docs, and numerous updates. (#2280)
- 📚 Use Python 3.14 color output in docs CLI output. (#2407)
- 📚 Docs now primarily use the pyproject.toml name of options, rather than the environment variable name. (#2389)
- 📚 README table now matches docs and auto-updates. (#2427, #2428)
### v2.23.3 ### v2.23.3
@@ -553,7 +563,7 @@ _14 December 2021_
_26 November 2021_ _26 November 2021_
- 📈 cibuildwheel now defaults to manylinux2014 image for linux builds, rather than manylinux2010. If you want to stick with manylinux2010, it's simple to set this using [the image options](https://cibuildwheel.pypa.io/en/stable/options/#linux-image). (#926) - 📈 cibuildwheel now defaults to manylinux2014 image for linux builds, rather than manylinux2010. If you want to stick with manylinux2010, it's simple to set this using [the image options](https://cibuildwheel.pypa.io/en/stable/options/#linux-image). (#926)
- ✨ You can now pass environment variables from the host machine into the Docker container during a Linux build. Check out [the docs for `CIBW_ENVIRONMENT_PASS_LINUX `](https://cibuildwheel.pypa.io/en/latest/options/#environment-pass) for the details. (#914) - ✨ You can now pass environment variables from the host machine into the Docker container during a Linux build. Check out [the docs for `CIBW_ENVIRONMENT_PASS_LINUX `](https://cibuildwheel.pypa.io/en/stable/options/#environment-pass) for the details. (#914)
- ✨ Added support for building PyPy 3.8 wheels. (#881) - ✨ Added support for building PyPy 3.8 wheels. (#881)
- ✨ Added support for building Windows arm64 CPython wheels on a Windows arm64 runner. We can't test this in CI yet, so for now, this is experimental. (#920) - ✨ Added support for building Windows arm64 CPython wheels on a Windows arm64 runner. We can't test this in CI yet, so for now, this is experimental. (#920)
- 📚 Improved the deployment documentation (#911) - 📚 Improved the deployment documentation (#911)
@@ -584,7 +594,7 @@ _22 October 2021_
- 🌟 TOML option overrides! This provides much greater flexibility in configuration via pyproject.toml. (#854) - 🌟 TOML option overrides! This provides much greater flexibility in configuration via pyproject.toml. (#854)
You can now set build options for any subset of your builds using a match pattern. So, for example, you can customise CPython 3.8 builds with an override on `cp38-*` or musllinux builds by selecting `*musllinux*`. Check out [the docs](https://cibuildwheel.pypa.io/en/latest/options/#overrides) for more info on the specifics. You can now set build options for any subset of your builds using a match pattern. So, for example, you can customise CPython 3.8 builds with an override on `cp38-*` or musllinux builds by selecting `*musllinux*`. Check out [the docs](https://cibuildwheel.pypa.io/en/stable/options/#overrides) for more info on the specifics.
- 🛠 Added support for building PyPy wheels on macOS 11 CI runners. (#875) - 🛠 Added support for building PyPy wheels on macOS 11 CI runners. (#875)
+1 -1
View File
@@ -53,7 +53,7 @@ Commit this file, and push to GitHub - either to your default branch, or to a PR
For more info on this file, check out the [docs](https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions). For more info on this file, check out the [docs](https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions).
[`examples/github-deploy.yml`](https://github.com/pypa/cibuildwheel/blob/main/examples/github-deploy.yml) extends this minimal example to include iOS and Pyodide builds, and a demonstration of how to automatically upload the built wheels to PyPI. [`examples/github-deploy.yml`](https://github.com/pypa/cibuildwheel/blob/main/examples/github-deploy.yml) extends this minimal example to include Android, iOS and Pyodide builds, and a demonstration of how to automatically upload the built wheels to PyPI.
### Azure Pipelines [linux/mac/windows] {: #azure-pipelines} ### Azure Pipelines [linux/mac/windows] {: #azure-pipelines}
+1 -1
View File
@@ -59,7 +59,7 @@ h1, h2, h3, h4, h5, h6 {
left: -3.236em; left: -3.236em;
max-width: calc(100cqw); max-width: calc(100cqw);
width: fit-content; width: fit-content;
min-width: calc(100% + 3.236em + 3.236em); min-width: min(calc(100% + 3.236em + 3.236em), 100cqw);
} }
.rst-content .section .wy-table-responsive .docutils { .rst-content .section .wy-table-responsive .docutils {
padding-left: 3.236em; padding-left: 3.236em;
+53 -3
View File
@@ -49,6 +49,56 @@ myextension = Extension(
) )
``` ```
### Building with NumPy
If using NumPy, there are a couple of things that can help.
First, if you require the `numpy` package at build-time (some binding tools, like `pybind11` and `nanobind`, do not), then the backward compatibility for your `build-backend.build-requires` is a little complicated for Python <3.9:
* NumPy <1.25: You must build with the oldest version of NumPy you want to support at runtime.
* NumPy 1.25 and 1.26: Anything you build will be compatible with 1.19+ by default, and you can set the minimum target to, for example, 1.22 with `#define NPY_TARGET_VERSION NPY_1_22_API_VERSION`.
* NumPy 2.x: You must build with NumPy 2 to support NumPy 2; otherwise the same as 1.25+.
So the rule is:
* Python <3.8: Use the oldest supported NumPy (via helper `oldest-supported-numpy` if you want)
* Python 3.9+: Use latest supported NumPy (2+).
Second, there might be platforms you want to ship for that NumPy (or some other scientific Python libraries) are not shipping yet for. This is often true for beta candidates of new Python releases, for example. To work with this, you can use the Scientific Python Nightly wheels. Here's an example, depending on what frontend you use:
!!! tab "pip based"
For frontends like `build` (the default) and `pip`:
```toml
[tool.cibuildwheel]
environment.PIP_ONLY_BINARY = "numpy"
environment.PIP_PREFER_BINARY = "1"
[[tool.cibuildwheel.overrides]]
select = ["cp314*"]
inherit.environment = "append"
environment.PIP_EXTRA_INDEX_URL = "https://pypi.anaconda.org/scientific-python-nightly-wheels/simple/"
environment.PIP_PRERELEASE = "allow"
```
!!! tab "uv based"
For frontends like `build[uv]`:
```toml
[tool.cibuildwheel]
environment.UV_ONLY_BINARY = "numpy"
environment.UV_PREFER_BINARY = "1"
[[tool.cibuildwheel.overrides]]
select = ["cp314*"]
inherit.environment = "append"
environment.UV_INDEX = "https://pypi.anaconda.org/scientific-python-nightly-wheels/simple/"
environment.UV_INDEX_STRATEGY = "unsafe-best-match"
environment.UV_PRERELEASE = "allow"
```
(Note the `*_ONLY_BINARY` variable also supports `":all:"`, and you don't need both that and `*_PREFER_BINARY`, you can use either one, depending on if you want a missing wheel to be a failure or an attempt to build in CI.)
### Automatic updates using Dependabot {: #automatic-updates} ### Automatic updates using Dependabot {: #automatic-updates}
Selecting a moving target (like the latest release) is generally a bad idea in CI. If something breaks, you can't tell whether it was your code or an upstream update that caused the breakage, and in a worst-case scenario, it could occur during a release. Selecting a moving target (like the latest release) is generally a bad idea in CI. If something breaks, you can't tell whether it was your code or an upstream update that caused the breakage, and in a worst-case scenario, it could occur during a release.
@@ -60,7 +110,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@v3.0.0b5 uses: pypa/cibuildwheel@v3.1.2
``` ```
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.
@@ -82,7 +132,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==3.0.0b5 cibuildwheel==3.1.2
``` ```
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:
@@ -247,7 +297,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==3.0.0b5 --output-dir wheelhouse pipx run cibuildwheel==3.1.2 --output-dir wheelhouse
pipx run twine upload wheelhouse/*.whl pipx run twine upload wheelhouse/*.whl
``` ```
+109 -80
View File
@@ -8,7 +8,7 @@
> Override the auto-detected target platform > Override the auto-detected target platform
Options: `auto` `linux` `macos` `windows` `ios` `pyodide` Options: `auto` `linux` `macos` `windows` `android` `ios` `pyodide`
Default: `auto` Default: `auto`
@@ -16,6 +16,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 `android` you need to be running on Linux or macOS, with an Android SDK installed. See [here](platforms.md#android) for more details.
- For `ios` you need to be running on macOS, with Xcode and the iOS simulator installed. - For `ios` you need to be running on macOS, with Xcode and the iOS simulator installed.
- For `pyodide`, you need a Linux or macOS machine. - For `pyodide`, you need a Linux or macOS machine.
@@ -51,26 +52,25 @@ When both options are specified, both conditions are applied and only builds wit
When setting the options, you can use shell-style globbing syntax, as per [fnmatch](https://docs.python.org/3/library/fnmatch.html) with the addition of curly bracket syntax `{option1,option2}`, provided by [bracex](https://pypi.org/project/bracex/). All the build identifiers supported by cibuildwheel are shown below: When setting the options, you can use shell-style globbing syntax, as per [fnmatch](https://docs.python.org/3/library/fnmatch.html) with the addition of curly bracket syntax `{option1,option2}`, provided by [bracex](https://pypi.org/project/bracex/). All the build identifiers supported by cibuildwheel are shown below:
<div class="build-id-table-marker"></div> <div class="build-id-table-marker"></div>
| | macOS | Windows | Linux Intel | Linux Other | iOS | pyodide (WASM) | | | macOS | Windows | Linux Intel | Linux Other | Android | iOS | pyodide (WASM) |
|---------------|------------------------------------------------------------------------|-----------------------------------------------------|-----------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------|----------------------| |---------------|------------------------------------------------------------------------|-----------------------------------------------------|-----------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------|---------------------------------------------------------------------------------------------------|----------------------|
| Python 3.8 | cp38-macosx_x86_64<br/>cp38-macosx_universal2<br/>cp38-macosx_arm64 | cp38-win_amd64<br/>cp38-win32 | cp38-manylinux_x86_64<br/>cp38-manylinux_i686<br/>cp38-musllinux_x86_64<br/>cp38-musllinux_i686 | cp38-manylinux_aarch64<br/>cp38-manylinux_ppc64le<br/>cp38-manylinux_s390x<br/>cp38-manylinux_armv7l<br/>cp38-manylinux_riscv64<br/>cp38-musllinux_aarch64<br/>cp38-musllinux_ppc64le<br/>cp38-musllinux_s390x<br/>cp38-musllinux_armv7l<br/>cp38-musllinux_riscv64 | | | | Python 3.8 | cp38-macosx_x86_64<br/>cp38-macosx_universal2<br/>cp38-macosx_arm64 | cp38-win_amd64<br/>cp38-win32 | cp38-manylinux_x86_64<br/>cp38-manylinux_i686<br/>cp38-musllinux_x86_64<br/>cp38-musllinux_i686 | cp38-manylinux_aarch64<br/>cp38-manylinux_ppc64le<br/>cp38-manylinux_s390x<br/>cp38-manylinux_armv7l<br/>cp38-manylinux_riscv64<br/>cp38-musllinux_aarch64<br/>cp38-musllinux_ppc64le<br/>cp38-musllinux_s390x<br/>cp38-musllinux_armv7l<br/>cp38-musllinux_riscv64 | | | |
| Python 3.9 | cp39-macosx_x86_64<br/>cp39-macosx_universal2<br/>cp39-macosx_arm64 | cp39-win_amd64<br/>cp39-win32<br/>cp39-win_arm64 | cp39-manylinux_x86_64<br/>cp39-manylinux_i686<br/>cp39-musllinux_x86_64<br/>cp39-musllinux_i686 | cp39-manylinux_aarch64<br/>cp39-manylinux_ppc64le<br/>cp39-manylinux_s390x<br/>cp39-manylinux_armv7l<br/>cp39-manylinux_riscv64<br/>cp39-musllinux_aarch64<br/>cp39-musllinux_ppc64le<br/>cp39-musllinux_s390x<br/>cp39-musllinux_armv7l<br/>cp39-musllinux_riscv64 | | | | Python 3.9 | cp39-macosx_x86_64<br/>cp39-macosx_universal2<br/>cp39-macosx_arm64 | cp39-win_amd64<br/>cp39-win32<br/>cp39-win_arm64 | cp39-manylinux_x86_64<br/>cp39-manylinux_i686<br/>cp39-musllinux_x86_64<br/>cp39-musllinux_i686 | cp39-manylinux_aarch64<br/>cp39-manylinux_ppc64le<br/>cp39-manylinux_s390x<br/>cp39-manylinux_armv7l<br/>cp39-manylinux_riscv64<br/>cp39-musllinux_aarch64<br/>cp39-musllinux_ppc64le<br/>cp39-musllinux_s390x<br/>cp39-musllinux_armv7l<br/>cp39-musllinux_riscv64 | | | |
| Python 3.10 | cp310-macosx_x86_64<br/>cp310-macosx_universal2<br/>cp310-macosx_arm64 | cp310-win_amd64<br/>cp310-win32<br/>cp310-win_arm64 | cp310-manylinux_x86_64<br/>cp310-manylinux_i686<br/>cp310-musllinux_x86_64<br/>cp310-musllinux_i686 | cp310-manylinux_aarch64<br/>cp310-manylinux_ppc64le<br/>cp310-manylinux_s390x<br/>cp310-manylinux_armv7l<br/>cp310-manylinux_riscv64<br/>cp310-musllinux_aarch64<br/>cp310-musllinux_ppc64le<br/>cp310-musllinux_s390x<br/>cp310-musllinux_armv7l<br/>cp310-musllinux_riscv64 | | | | Python 3.10 | cp310-macosx_x86_64<br/>cp310-macosx_universal2<br/>cp310-macosx_arm64 | cp310-win_amd64<br/>cp310-win32<br/>cp310-win_arm64 | cp310-manylinux_x86_64<br/>cp310-manylinux_i686<br/>cp310-musllinux_x86_64<br/>cp310-musllinux_i686 | cp310-manylinux_aarch64<br/>cp310-manylinux_ppc64le<br/>cp310-manylinux_s390x<br/>cp310-manylinux_armv7l<br/>cp310-manylinux_riscv64<br/>cp310-musllinux_aarch64<br/>cp310-musllinux_ppc64le<br/>cp310-musllinux_s390x<br/>cp310-musllinux_armv7l<br/>cp310-musllinux_riscv64 | | | |
| Python 3.11 | cp311-macosx_x86_64<br/>cp311-macosx_universal2<br/>cp311-macosx_arm64 | cp311-win_amd64<br/>cp311-win32<br/>cp311-win_arm64 | cp311-manylinux_x86_64<br/>cp311-manylinux_i686<br/>cp311-musllinux_x86_64<br/>cp311-musllinux_i686 | cp311-manylinux_aarch64<br/>cp311-manylinux_ppc64le<br/>cp311-manylinux_s390x<br/>cp311-manylinux_armv7l<br/>cp311-manylinux_riscv64<br/>cp311-musllinux_aarch64<br/>cp311-musllinux_ppc64le<br/>cp311-musllinux_s390x<br/>cp311-musllinux_armv7l<br/>cp311-musllinux_riscv64 | | | | Python 3.11 | cp311-macosx_x86_64<br/>cp311-macosx_universal2<br/>cp311-macosx_arm64 | cp311-win_amd64<br/>cp311-win32<br/>cp311-win_arm64 | cp311-manylinux_x86_64<br/>cp311-manylinux_i686<br/>cp311-musllinux_x86_64<br/>cp311-musllinux_i686 | cp311-manylinux_aarch64<br/>cp311-manylinux_ppc64le<br/>cp311-manylinux_s390x<br/>cp311-manylinux_armv7l<br/>cp311-manylinux_riscv64<br/>cp311-musllinux_aarch64<br/>cp311-musllinux_ppc64le<br/>cp311-musllinux_s390x<br/>cp311-musllinux_armv7l<br/>cp311-musllinux_riscv64 | | | |
| Python 3.12 | cp312-macosx_x86_64<br/>cp312-macosx_universal2<br/>cp312-macosx_arm64 | cp312-win_amd64<br/>cp312-win32<br/>cp312-win_arm64 | cp312-manylinux_x86_64<br/>cp312-manylinux_i686<br/>cp312-musllinux_x86_64<br/>cp312-musllinux_i686 | cp312-manylinux_aarch64<br/>cp312-manylinux_ppc64le<br/>cp312-manylinux_s390x<br/>cp312-manylinux_armv7l<br/>cp312-manylinux_riscv64<br/>cp312-musllinux_aarch64<br/>cp312-musllinux_ppc64le<br/>cp312-musllinux_s390x<br/>cp312-musllinux_armv7l<br/>cp312-musllinux_riscv64 | | cp312-pyodide_wasm32 | | Python 3.12 | cp312-macosx_x86_64<br/>cp312-macosx_universal2<br/>cp312-macosx_arm64 | cp312-win_amd64<br/>cp312-win32<br/>cp312-win_arm64 | cp312-manylinux_x86_64<br/>cp312-manylinux_i686<br/>cp312-musllinux_x86_64<br/>cp312-musllinux_i686 | cp312-manylinux_aarch64<br/>cp312-manylinux_ppc64le<br/>cp312-manylinux_s390x<br/>cp312-manylinux_armv7l<br/>cp312-manylinux_riscv64<br/>cp312-musllinux_aarch64<br/>cp312-musllinux_ppc64le<br/>cp312-musllinux_s390x<br/>cp312-musllinux_armv7l<br/>cp312-musllinux_riscv64 | | | cp312-pyodide_wasm32 |
| Python 3.13 | cp313-macosx_x86_64<br/>cp313-macosx_universal2<br/>cp313-macosx_arm64 | cp313-win_amd64<br/>cp313-win32<br/>cp313-win_arm64 | cp313-manylinux_x86_64<br/>cp313-manylinux_i686<br/>cp313-musllinux_x86_64<br/>cp313-musllinux_i686 | cp313-manylinux_aarch64<br/>cp313-manylinux_ppc64le<br/>cp313-manylinux_s390x<br/>cp313-manylinux_armv7l<br/>cp313-manylinux_riscv64<br/>cp313-musllinux_aarch64<br/>cp313-musllinux_ppc64le<br/>cp313-musllinux_s390x<br/>cp313-musllinux_armv7l<br/>cp313-musllinux_riscv64 | cp313-ios_arm64_iphoneos<br/>cp313-ios_arm64_iphonesimulator<br/>cp313-ios_x86_64_iphonesimulator | cp313-pyodide_wasm32 | | Python 3.13 | cp313-macosx_x86_64<br/>cp313-macosx_universal2<br/>cp313-macosx_arm64 | cp313-win_amd64<br/>cp313-win32<br/>cp313-win_arm64 | cp313-manylinux_x86_64<br/>cp313-manylinux_i686<br/>cp313-musllinux_x86_64<br/>cp313-musllinux_i686 | cp313-manylinux_aarch64<br/>cp313-manylinux_ppc64le<br/>cp313-manylinux_s390x<br/>cp313-manylinux_armv7l<br/>cp313-manylinux_riscv64<br/>cp313-musllinux_aarch64<br/>cp313-musllinux_ppc64le<br/>cp313-musllinux_s390x<br/>cp313-musllinux_armv7l<br/>cp313-musllinux_riscv64 | cp313-android_arm64_v8a<br/>cp313-android_x86_64 | cp313-ios_arm64_iphoneos<br/>cp313-ios_arm64_iphonesimulator<br/>cp313-ios_x86_64_iphonesimulator | cp313-pyodide_wasm32 |
| Python 3.14 | cp314-macosx_x86_64<br/>cp314-macosx_universal2<br/>cp314-macosx_arm64 | cp314-win_amd64<br/>cp314-win32<br/>cp314-win_arm64 | cp314-manylinux_x86_64<br/>cp314-manylinux_i686<br/>cp314-musllinux_x86_64<br/>cp314-musllinux_i686 | cp314-manylinux_aarch64<br/>cp314-manylinux_ppc64le<br/>cp314-manylinux_s390x<br/>cp314-manylinux_armv7l<br/>cp314-manylinux_riscv64<br/>cp314-musllinux_aarch64<br/>cp314-musllinux_ppc64le<br/>cp314-musllinux_s390x<br/>cp314-musllinux_armv7l<br/>cp314-musllinux_riscv64 | | | | Python 3.14 | cp314-macosx_x86_64<br/>cp314-macosx_universal2<br/>cp314-macosx_arm64 | cp314-win_amd64<br/>cp314-win32<br/>cp314-win_arm64 | cp314-manylinux_x86_64<br/>cp314-manylinux_i686<br/>cp314-musllinux_x86_64<br/>cp314-musllinux_i686 | cp314-manylinux_aarch64<br/>cp314-manylinux_ppc64le<br/>cp314-manylinux_s390x<br/>cp314-manylinux_armv7l<br/>cp314-manylinux_riscv64<br/>cp314-musllinux_aarch64<br/>cp314-musllinux_ppc64le<br/>cp314-musllinux_s390x<br/>cp314-musllinux_armv7l<br/>cp314-musllinux_riscv64 | cp314-ios_arm64_iphoneos<br/>cp314-ios_arm64_iphonesimulator<br/>cp314-ios_x86_64_iphonesimulator | |
| PyPy3.8 v7.3 | pp38-macosx_x86_64<br/>pp38-macosx_arm64 | pp38-win_amd64 | pp38-manylinux_x86_64<br/>pp38-manylinux_i686 | pp38-manylinux_aarch64 | | | | PyPy3.8 v7.3 | pp38-macosx_x86_64<br/>pp38-macosx_arm64 | pp38-win_amd64 | pp38-manylinux_x86_64<br/>pp38-manylinux_i686 | pp38-manylinux_aarch64 | | | |
| PyPy3.9 v7.3 | pp39-macosx_x86_64<br/>pp39-macosx_arm64 | pp39-win_amd64 | pp39-manylinux_x86_64<br/>pp39-manylinux_i686 | pp39-manylinux_aarch64 | | | | PyPy3.9 v7.3 | pp39-macosx_x86_64<br/>pp39-macosx_arm64 | pp39-win_amd64 | pp39-manylinux_x86_64<br/>pp39-manylinux_i686 | pp39-manylinux_aarch64 | | | |
| PyPy3.10 v7.3 | pp310-macosx_x86_64<br/>pp310-macosx_arm64 | pp310-win_amd64 | pp310-manylinux_x86_64<br/>pp310-manylinux_i686 | pp310-manylinux_aarch64 | | | | PyPy3.10 v7.3 | pp310-macosx_x86_64<br/>pp310-macosx_arm64 | pp310-win_amd64 | pp310-manylinux_x86_64<br/>pp310-manylinux_i686 | pp310-manylinux_aarch64 | | | |
| PyPy3.11 v7.3 | pp311-macosx_x86_64<br/>pp311-macosx_arm64 | pp311-win_amd64 | pp311-manylinux_x86_64<br/>pp311-manylinux_i686 | pp311-manylinux_aarch64 | | | | PyPy3.11 v7.3 | pp311-macosx_x86_64<br/>pp311-macosx_arm64 | pp311-win_amd64 | pp311-manylinux_x86_64<br/>pp311-manylinux_i686 | pp311-manylinux_aarch64 | | | |
| GraalPy 3.11 v24.2 | gp311_242-macosx_x86_64<br/>gp311_242-macosx_arm64 | gp311_242-win_amd64 | gp311_242-manylinux_x86_64 | gp311_242-manylinux_aarch64 | | | | GraalPy 3.11 v24.2 | gp311_242-macosx_x86_64<br/>gp311_242-macosx_arm64 | gp311_242-win_amd64 | gp311_242-manylinux_x86_64 | gp311_242-manylinux_aarch64 | | |
The list of supported and currently selected build identifiers can also be retrieved by passing the `--print-build-identifiers` flag to cibuildwheel. The list of supported and currently selected build identifiers can also be retrieved by passing the `--print-build-identifiers` flag to cibuildwheel.
The format is `python_tag-platform_tag`, with tags similar to those in [PEP 425](https://www.python.org/dev/peps/pep-0425/#details). The format is `python_tag-platform_tag`, with tags similar to those in [PEP 425](https://www.python.org/dev/peps/pep-0425/#details).
Windows arm64 platform support is experimental. Windows arm64 platform support is experimental.
Linux riscv64 platform support is experimental and requires an explicit opt-in through [`enable`](#enable).
See the [cibuildwheel 2 documentation](https://cibuildwheel.pypa.io/en/2.x/) for past end-of-life versions of Python. See the [cibuildwheel 2 documentation](https://cibuildwheel.pypa.io/en/2.x/) for past end-of-life versions of Python.
@@ -190,36 +190,53 @@ Options:
- macOS: `x86_64` `arm64` `universal2` - macOS: `x86_64` `arm64` `universal2`
- Windows: `AMD64` `x86` `ARM64` - Windows: `AMD64` `x86` `ARM64`
- Pyodide: `wasm32` - Pyodide: `wasm32`
- Android: `arm64_v8a` `x86_64`
- iOS: `arm64_iphoneos` `arm64_iphonesimulator` `x86_64_iphonesimulator` - iOS: `arm64_iphoneos` `arm64_iphonesimulator` `x86_64_iphonesimulator`
- `auto`: The default archs for your machine - see the table below. - `auto`: The recommended archs for your machine - see the table below.
- `auto64`: Just the 64-bit auto archs - `auto64`: The 64-bit arch(s) supported by your machine (includes device and simulator for iOS)
- `auto32`: Just the 32-bit auto archs - `auto32`: The 32-bit arch supported by your machine
- `native`: the native arch of the build machine - Matches [`platform.machine()`](https://docs.python.org/3/library/platform.html#platform.machine). - `native`: the native arch of the build machine - matches [`platform.machine()`](https://docs.python.org/3/library/platform.html#platform.machine).
- `all` : expands to all the architectures supported on this OS. You may want - `all` : expands to all the architectures supported on this OS. You may want
to use [`build`](#build-skip) with this option to target specific to use [`build`](#build-skip) with this option to target specific
architectures via build selectors. architectures via build selectors.
Linux riscv64 platform support is experimental and requires an explicit opt-in through [`enable`](#enable).
Default: `auto` Default: `auto`
| Runner | `native` | `auto` | `auto64` | `auto32` | | Runner | `native` | `auto` | `auto64` | `auto32` |
|---|---|---|---|---| |---|---|---|---|---|
| Linux / Intel | `x86_64` | `x86_64` `i686` | `x86_64` | `i686` | | Linux / Intel 64-bit | `x86_64` | `x86_64` | `x86_64` | `i686` |
| Windows / Intel | `AMD64` | `AMD64` `x86` | `AMD64` | `x86` | | Linux / Intel 32-bit | `i686` | `i686` | | `i686` |
| Linux / Arm 64-bit | `aarch64` | `aarch64` | `aarch64` | `armv7l`¹ |
| Linux / Arm 32-bit | `armv7l` | `armv7l` | | `armv7l` |
| Windows / Intel 64-bit | `AMD64` | `AMD64` `x86` | `AMD64` | `x86` |
| Windows / Intel 32-bit | `x86` | `x86` | | `x86` |
| Windows / ARM64 | `ARM64` | `ARM64` | `ARM64` | | | Windows / ARM64 | `ARM64` | `ARM64` | `ARM64` | |
| macOS / Intel | `x86_64` | `x86_64` | `x86_64` | | | macOS / Intel | `x86_64` | `x86_64` | `x86_64` | |
| macOS / Apple Silicon | `arm64` | `arm64` | `arm64` | | | macOS / Apple Silicon | `arm64` | `arm64` | `arm64` | |
| iOS on macOS / Intel | `x86_64_iphonesimulator` | `x86_64_iphonesimulator` | `x86_64_iphonesimulator` | | | iOS on macOS / Intel | `x86_64_iphonesimulator` | `x86_64_iphonesimulator` | `x86_64_iphonesimulator` | |
| iOS on macOS / Apple Silicon | `arm64_iphonesimulator` | `arm64_iphoneos` `arm64_iphonesimulator` | `arm64_iphoneos` `arm64_iphonesimulator` | | iOS on macOS / Apple Silicon | `arm64_iphonesimulator` | `arm64_iphoneos` `arm64_iphonesimulator` | `arm64_iphoneos` `arm64_iphonesimulator` |
¹: This will only be included if the runner supports it.
If not listed above, `auto` is the same as `native`. If not listed above, `auto` is the same as `native`.
!!! warning
The `auto` option only includes 32-bit architectures if they are
commonly built. `cibuildwheel` 3.0 removed 32-bit Linux builds from `auto`,
and a future release may remove 32-bit Windows builds from `auto` as well.
If you know you need them, please include `auto32`. Note that modern
manylinux image do not support 32-bit builds. If you want to avoid 32-bit
Windows builds today, feel free to use `auto64`.
!!! note
Pyodide currently ignores the architecture setting, as it always builds for
`wasm32`.
[setup-qemu-action]: https://github.com/docker/setup-qemu-action [setup-qemu-action]: https://github.com/docker/setup-qemu-action
[binfmt]: https://hub.docker.com/r/tonistiigi/binfmt [binfmt]: https://hub.docker.com/r/tonistiigi/binfmt
Platform-specific environment variables are also available:<br/> Platform-specific environment variables are also available:<br/>
`CIBW_ARCHS_MACOS` | `CIBW_ARCHS_WINDOWS` | `CIBW_ARCHS_LINUX` | `CIBW_ARCHS_IOS` `CIBW_ARCHS_MACOS` | `CIBW_ARCHS_WINDOWS` | `CIBW_ARCHS_LINUX` | `CIBW_ARCHS_ANDROID` | `CIBW_ARCHS_IOS`
This option can also be set using the [command-line option](#command-line) This option can also be set using the [command-line option](#command-line)
`--archs`. This option cannot be set in an `overrides` section in `pyproject.toml`. `--archs`. This option cannot be set in an `overrides` section in `pyproject.toml`.
@@ -238,6 +255,10 @@ This option can also be set using the [command-line option](#command-line)
# On an Linux Intel runner with qemu installed, build Intel and ARM wheels # On an Linux Intel runner with qemu installed, build Intel and ARM wheels
[tool.cibuildwheel.linux] [tool.cibuildwheel.linux]
archs = ["auto", "aarch64"] archs = ["auto", "aarch64"]
# Build all 32-bit and 64-bit wheels natively buildable on the image
[tool.cibuildwheel]
archs = ["auto64", "auto32"]
``` ```
!!! tab examples "Environment variables" !!! tab examples "Environment variables"
@@ -250,12 +271,13 @@ This option can also be set using the [command-line option](#command-line)
# On an Linux Intel runner with qemu installed, build Intel and ARM wheels # On an Linux Intel runner with qemu installed, build Intel and ARM wheels
CIBW_ARCHS_LINUX: "auto aarch64" CIBW_ARCHS_LINUX: "auto aarch64"
# Build all 32-bit and 64-bit wheels natively buildable on the image
CIBW_ARCHS: "auto64 auto32"
``` ```
Separate multiple archs with a space. Separate multiple archs with a space.
It is generally recommended to use the environment variable or It is generally recommended to use the environment variable or
command-line option for Linux, as selecting archs often depends command-line option for Linux, as selecting archs often depends
on your specific runner having qemu installed. on your specific runner having qemu installed.
@@ -325,14 +347,12 @@ values are:
- `cpython-freethreading`: [PEP 703](https://www.python.org/dev/peps/pep-0703) - `cpython-freethreading`: [PEP 703](https://www.python.org/dev/peps/pep-0703)
introduced variants of CPython that can be built without the Global introduced variants of CPython that can be built without the Global
Interpreter Lock (GIL). Those variants are also known as free-threaded / Interpreter Lock (GIL). Those variants are also known as free-threaded /
no-gil. This will enable building these wheels while they are experimental. no-gil. This will enable building these wheels for 3.13, which was considered
The build identifiers for those variants have a `t` suffix in their experimental. The build identifiers for those variants have a `t` suffix in
`python_tag` (e.g. `cp313t-manylinux_x86_64`). their `python_tag` (e.g. `cp313t-manylinux_x86_64`). Starting in 3.14,
free-threaded builds are no longer experimental, so this enable is not needed.
- `pypy`: Enable PyPy. - `pypy`: Enable PyPy.
- `pypy-eol`: Enable PyPy versions that have passed end of life (if still available). - `pypy-eol`: Enable PyPy versions that have passed end of life (if still available).
- `cpython-experimental-riscv64`: Enable experimental riscv64 builds. Those builds
are disabled by default as they can't be uploaded to PyPI and a PEP will most likely
be required before this can happen.
- `graalpy`: Enable GraalPy. - `graalpy`: Enable GraalPy.
- `pyodide-prerelease`: Pyodide versions that haven't released yet, if one is - `pyodide-prerelease`: Pyodide versions that haven't released yet, if one is
available. Safe if you are shipping a site with an early build, not for available. Safe if you are shipping a site with an early build, not for
@@ -348,7 +368,7 @@ values are:
that version of Python will become available without this flag. that version of Python will become available without this flag.
!!! note !!! note
Free threading is experimental: [Whats New In Python 3.13](https://docs.python.org/3.13/whatsnew/3.13.html#free-threaded-cpython) Free threading was experimental in 3.13: [Whats New In Python 3.13](https://docs.python.org/3.13/whatsnew/3.13.html#free-threaded-cpython)
Default: empty. Default: empty.
@@ -448,11 +468,11 @@ possible, both through `--installer=uv` passed to build, as well as when making
all build and test environments. This will generally speed up cibuildwheel. all build and test environments. This will generally speed up cibuildwheel.
Make sure you have an external uv on Windows and macOS, either by Make sure you have an external uv on Windows and macOS, either by
pre-installing it, or installing cibuildwheel with the uv extra, pre-installing it, or installing cibuildwheel with the uv extra,
`cibuildwheel[uv]`. You cannot use uv currently on Windows for ARM, for `cibuildwheel[uv]`. uv currently does not support Windows on ARM,
musllinux on s390x, or for iOS, as binaries are not provided by uv. Legacy dependencies like musllinux on s390x, Android, or iOS. Legacy dependencies like
setuptools on Python < 3.12 and pip are not installed if using uv. setuptools on Python < 3.12 and pip are not installed if using uv.
Pyodide ignores this setting, as only "build" is supported. On Android and Pyodide, only "build" is supported.
You can specify extra arguments to pass to the build frontend using the You can specify extra arguments to pass to the build frontend using the
optional `args` option. optional `args` option.
@@ -516,7 +536,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_IOS` | `CIBW_CONFIG_SETTINGS_PYODIDE` `CIBW_CONFIG_SETTINGS_MACOS` | `CIBW_CONFIG_SETTINGS_WINDOWS` | `CIBW_CONFIG_SETTINGS_LINUX` | `CIBW_CONFIG_SETTINGS_ANDROID` | `CIBW_CONFIG_SETTINGS_IOS` | `CIBW_CONFIG_SETTINGS_PYODIDE`
#### Examples #### Examples
@@ -549,7 +569,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_IOS` | `CIBW_ENVIRONMENT_PYODIDE` `CIBW_ENVIRONMENT_MACOS` | `CIBW_ENVIRONMENT_WINDOWS` | `CIBW_ENVIRONMENT_LINUX` | `CIBW_ENVIRONMENT_ANDROID` | `CIBW_ENVIRONMENT_IOS` | `CIBW_ENVIRONMENT_PYODIDE`
#### Examples #### Examples
@@ -681,7 +701,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_IOS` | `CIBW_BEFORE_ALL_PYODIDE` `CIBW_BEFORE_ALL_MACOS` | `CIBW_BEFORE_ALL_WINDOWS` | `CIBW_BEFORE_ALL_LINUX` | `CIBW_BEFORE_ALL_ANDROID` | `CIBW_BEFORE_ALL_IOS` | `CIBW_BEFORE_ALL_PYODIDE`
!!! note !!! note
@@ -747,7 +767,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_IOS` | `CIBW_BEFORE_BUILD_PYODIDE` `CIBW_BEFORE_BUILD_MACOS` | `CIBW_BEFORE_BUILD_WINDOWS` | `CIBW_BEFORE_BUILD_LINUX` | `CIBW_BEFORE_BUILD_ANDROID` | `CIBW_BEFORE_BUILD_IOS` | `CIBW_BEFORE_BUILD_PYODIDE`
#### Examples #### Examples
@@ -865,9 +885,9 @@ 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 Android: There is no default command, but cibuildwheel will add `libc++` to the
- on iOS: `''` wheel if anything links against it. Setting a command will replace this behavior.
- on Pyodide: `''` - on other platforms: `''`
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.
@@ -881,7 +901,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_IOS` | `CIBW_REPAIR_WHEEL_COMMAND_PYODIDE` `CIBW_REPAIR_WHEEL_COMMAND_MACOS` | `CIBW_REPAIR_WHEEL_COMMAND_WINDOWS` | `CIBW_REPAIR_WHEEL_COMMAND_LINUX` | `CIBW_REPAIR_WHEEL_COMMAND_ANDROID` | `CIBW_REPAIR_WHEEL_COMMAND_IOS` | `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.
@@ -986,30 +1006,31 @@ The available options are:
| Option | Default | | Option | Default |
|--------------------------------|-----------------------------------------------------------------| |--------------------------------|-----------------------------------------------------------------|
| `manylinux_x86_64-image` | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_x86_64) | | `manylinux_x86_64-image` | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_x86_64) |
| `manylinux-i686-image` | [`manylinux2014`](https://quay.io/pypa/manylinux2014_i686) | | `manylinux-i686-image` | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_i686) |
| `manylinux-pypy_x86_64-image` | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_x86_64) | | `manylinux-pypy_x86_64-image` | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_x86_64) |
| `manylinux-aarch64-image` | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_aarch64) | | `manylinux-aarch64-image` | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_aarch64) |
| `manylinux-ppc64le-image` | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_ppc64le) | | `manylinux-ppc64le-image` | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_ppc64le) |
| `manylinux-s390x-image` | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_s390x) | | `manylinux-s390x-image` | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_s390x) |
| `manylinux-armv7l-image` | [`manylinux_2_31`](https://quay.io/pypa/manylinux_2_31_armv7l) | | `manylinux-armv7l-image` | [`manylinux_2_31`](https://quay.io/pypa/manylinux_2_31_armv7l) |
| `manylinux-riscv64-image` | No default | | `manylinux-riscv64-image` | [`manylinux_2_39`](https://quay.io/pypa/manylinux_2_39_riscv64) |
| `manylinux-pypy_aarch64-image` | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_aarch64) | | `manylinux-pypy_aarch64-image` | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_aarch64) |
| `manylinux-pypy_i686-image` | [`manylinux2014`](https://quay.io/pypa/manylinux2014_i686) | | `manylinux-pypy_i686-image` | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_i686) |
| `musllinux_x86_64-image` | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_x86_64) | | `musllinux_x86_64-image` | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_x86_64) |
| `musllinux-i686-image` | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_i686) | | `musllinux-i686-image` | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_i686) |
| `musllinux-aarch64-image` | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_aarch64) | | `musllinux-aarch64-image` | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_aarch64) |
| `musllinux-ppc64le-image` | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_ppc64le) | | `musllinux-ppc64le-image` | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_ppc64le) |
| `musllinux-s390x-image` | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_s390x) | | `musllinux-s390x-image` | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_s390x) |
| `musllinux-armv7l-image` | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_armv7l) | | `musllinux-armv7l-image` | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_armv7l) |
| `musllinux-riscv64-image` | No default | | `musllinux-riscv64-image` | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_riscv64) |
Set the Docker image to be used for building [manylinux / musllinux](https://github.com/pypa/manylinux) wheels. Set the Docker image to be used for building [manylinux / musllinux](https://github.com/pypa/manylinux) wheels.
For `manylinux-*-image`, except `manylinux-armv7l-image`, the value of this option can either be set to `manylinux2014`, `manylinux_2_28` or `manylinux_2_34` to use a pinned version of the [official manylinux images](https://github.com/pypa/manylinux). Alternatively, set these options to any other valid Docker image name. For `manylinux-*-image`, except `manylinux-armv7l-image` and `manylinux-riscv64-image`, the value of this option can either be set to `manylinux2014`, `manylinux_2_28` or `manylinux_2_34` to use a pinned version of the [official manylinux images](https://github.com/pypa/manylinux). Alternatively, set these options to any other valid Docker image name.
`manylinux_2_28` and `manylinux_2_34` are not supported for `i686` architecture.
For `manylinux-armv7l-image`, the value of this option can either be set to `manylinux_2_31` or a custom image. Support is experimental for now. The `manylinux_2_31` value is only available for `armv7`. For `manylinux-armv7l-image`, the value of this option can either be set to `manylinux_2_31` or a custom image. Support is experimental for now. The `manylinux_2_31` value is only available for `armv7`.
For `manylinux-riscv64-image`, the value of this option can either be set to `manylinux_2_39` or a custom image. Support is experimental for now. The `manylinux_2_39` value is only available for `riscv64`.
For `musllinux-*-image`, the value of this option can either be set to `musllinux_1_2` or a custom image. For `musllinux-*-image`, the value of this option can either be set to `musllinux_1_2` or a custom image.
If this option is blank, it will fall though to the next available definition (environment variable -> pyproject.toml -> default). If this option is blank, it will fall though to the next available definition (environment variable -> pyproject.toml -> default).
@@ -1182,7 +1203,7 @@ specifiers inline with the `packages: SPECIFIER...` syntax.
`./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_IOS` | `CIBW_DEPENDENCY_VERSIONS_PYODIDE` `CIBW_DEPENDENCY_VERSIONS_MACOS` | `CIBW_DEPENDENCY_VERSIONS_WINDOWS` | `CIBW_DEPENDENCY_VERSIONS_ANDROID` | `CIBW_DEPENDENCY_VERSIONS_IOS` | `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
@@ -1253,28 +1274,34 @@ The available Pyodide versions are determined by the version of `pyodide-build`
!!! tip !!! tip
You can set the version of `pyodide-build` using the [`dependency-versions`](#dependency-versions) option. You can set the version of `pyodide-build` using the [`dependency-versions`](#dependency-versions) option.
!!! warning
This option is considered experimental, and might be converted to a more general mechanism in a future minor cibuildwheel release.
!!! warning
Make sure to scope it to one specific pyodide identifier with overrides if using the `pyodide-prerelease` enable.
#### Examples #### Examples
!!! tab examples "pyproject.toml" !!! tab examples "pyproject.toml"
```toml ```toml
[tool.cibuildwheel.pyodide] [tool.cibuildwheel.pyodide]
# Build Pyodide wheels using Pyodide version 0.27.6 # Build Pyodide wheels using Pyodide version 0.28.0
pyodide-version = "0.27.6" pyodide-version = "0.28.0"
[tool.cibuildwheel.pyodide] [tool.cibuildwheel.pyodide]
# Build Pyodide wheels using a specific alpha release # Build Pyodide wheels using a specific, previously released alpha release of 0.28.0
pyodide-version = "0.28.0a2" pyodide-version = "0.28.0a3"
``` ```
!!! tab examples "Environment variables" !!! tab examples "Environment variables"
```yaml ```yaml
# Build Pyodide wheels using Pyodide version 0.27.6 # Build Pyodide wheels using Pyodide version 0.28.0
CIBW_PYODIDE_VERSION: 0.27.6 CIBW_PYODIDE_VERSION: 0.28.0
# Build Pyodide wheels using a specific alpha release # Build Pyodide wheels using a specific, previously released alpha release of 0.28.0
CIBW_PYODIDE_VERSION: 0.28.0a2 CIBW_PYODIDE_VERSION: 0.28.0a3
``` ```
@@ -1287,8 +1314,8 @@ Shell command to run tests after the build. The wheel will be installed
automatically and available for import from the tests. If this variable is not automatically and available for import from the tests. If this variable is not
set, your wheel will not be installed after building. set, your wheel will not be installed after building.
To ensure the wheel is imported by your tests (instead of your source copy), To ensure your tests import the wheel (instead of your source tree),
**Tests are executed from a temporary directory**, outside of your source **tests are executed from a temporary working directory**, outside of your source
tree. To access your test code, you have a couple of options: tree. To access your test code, you have a couple of options:
- You can use the [`test-sources`](#test-sources) setting to copy specific - You can use the [`test-sources`](#test-sources) setting to copy specific
@@ -1305,14 +1332,20 @@ tree. To access your test code, you have a couple of options:
- `{project}` is an absolute path to the project root - the working - `{project}` is an absolute path to the project root - the working
directory where cibuildwheel was called. directory where cibuildwheel was called.
On all platforms other than iOS, the command is run in a shell, so you can write things like `cmd1 && cmd2`. These placeholders are not available on Android and iOS, because those
platforms run tests in a virtual machine that does not have access to
the build machine's filesystem.
On iOS, the value of the `test-command` setting must follow the format `python On all platforms other than Android and iOS, the command is run in a shell, so you can write things like `cmd1 && cmd2`.
-m MODULE [ARGS...]` - where MODULE is a Python module name, followed by
arguments that will be assigned to `sys.argv`. Other commands cannot be used. On Android and iOS, the command is parsed by `shlex.split`, and is required to
be in one of the following forms:
* `python -c command ...` (Android only)
* `python -m module-name ...`
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_IOS` | `CIBW_TEST_COMMAND_PYODIDE` `CIBW_TEST_COMMAND_MACOS` | `CIBW_TEST_COMMAND_WINDOWS` | `CIBW_TEST_COMMAND_LINUX` | `CIBW_TEST_COMMAND_ANDROID` | `CIBW_TEST_COMMAND_IOS` | `CIBW_TEST_COMMAND_PYODIDE`
#### Examples #### Examples
@@ -1370,7 +1403,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_IOS` | `CIBW_BEFORE_TEST_PYODIDE` `CIBW_BEFORE_TEST_MACOS` | `CIBW_BEFORE_TEST_WINDOWS` | `CIBW_BEFORE_TEST_LINUX` | `CIBW_BEFORE_TEST_ANDROID` | `CIBW_BEFORE_TEST_IOS` | `CIBW_BEFORE_TEST_PYODIDE`
#### Examples #### Examples
@@ -1425,20 +1458,15 @@ Platform-specific environment variables are also available:<br/>
### `test-sources` {: #test-sources env-var toml} ### `test-sources` {: #test-sources env-var toml}
> Files and folders from the source tree that are copied into an isolated tree before running the tests > Paths that are copied into the working directory of the tests
A space-separated list of files and folders, relative to the root of the A space-separated list of files and folders, relative to the root of the
project, required for running the tests. If specified, these files and folders project, required for running the tests. If specified, these files and folders
will be copied into a temporary folder, and that temporary folder will be used will be copied into the temporary folder which is used as the working directory
as the working directory for running the test suite. for running the test suite. For more details, see [`test-command`](#test-command).
The use of `test-sources` is *required* for iOS builds. This is because the
simulator does not have access to the project directory, as it is not stored on
the simulator device. On iOS, the files will be copied into the test application,
rather than a temporary folder.
Platform-specific environment variables are also available:<br/> Platform-specific environment variables are also available:<br/>
`CIBW_TEST_SOURCES_MACOS` | `CIBW_TEST_SOURCES_WINDOWS` | `CIBW_TEST_SOURCES_LINUX` | `CIBW_TEST_SOURCES_IOS` | `CIBW_TEST_SOURCES_PYODIDE` `CIBW_TEST_SOURCES_MACOS` | `CIBW_TEST_SOURCES_WINDOWS` | `CIBW_TEST_SOURCES_LINUX` | `CIBW_TEST_SOURCES_ANDROID` | `CIBW_TEST_SOURCES_IOS` | `CIBW_TEST_SOURCES_PYODIDE`
#### Examples #### Examples
@@ -1466,7 +1494,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_IOS` | `CIBW_TEST_REQUIRES_PYODIDE` `CIBW_TEST_REQUIRES_MACOS` | `CIBW_TEST_REQUIRES_WINDOWS` | `CIBW_TEST_REQUIRES_LINUX` | `CIBW_TEST_REQUIRES_ANDROID` | `CIBW_TEST_REQUIRES_IOS` | `CIBW_TEST_REQUIRES_PYODIDE`
#### Examples #### Examples
@@ -1507,7 +1535,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_IOS` | `CIBW_TEST_EXTRAS_PYODIDE` `CIBW_TEST_EXTRAS_MACOS` | `CIBW_TEST_EXTRAS_WINDOWS` | `CIBW_TEST_EXTRAS_LINUX` | `CIBW_TEST_EXTRAS_ANDROID` | `CIBW_TEST_EXTRAS_IOS` | `CIBW_TEST_EXTRAS_PYODIDE`
#### Examples #### Examples
@@ -1608,7 +1636,7 @@ A space-separated list of environment variables to set in the test environment.
The syntax is the same as for [`environment`](#environment). The syntax is the same as for [`environment`](#environment).
Platform-specific environment variables are also available:<br/> Platform-specific environment variables are also available:<br/>
`CIBW_TEST_ENVIRONMENT_MACOS` | `CIBW_TEST_ENVIRONMENT_WINDOWS` | `CIBW_TEST_ENVIRONMENT_LINUX` | `CIBW_TEST_ENVIRONMENT_IOS` | `CIBW_TEST_ENVIRONMENT_PYODIDE` `CIBW_TEST_ENVIRONMENT_MACOS` | `CIBW_TEST_ENVIRONMENT_WINDOWS` | `CIBW_TEST_ENVIRONMENT_LINUX` | `CIBW_TEST_ENVIRONMENT_ANDROID` |`CIBW_TEST_ENVIRONMENT_IOS` | `CIBW_TEST_ENVIRONMENT_PYODIDE`
#### Examples #### Examples
@@ -1691,7 +1719,7 @@ The default build frontend is `build`, which does show build backend output by
default. 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_IOS` | `CIBW_BUILD_VERBOSITY_PYODIDE` `CIBW_BUILD_VERBOSITY_MACOS` | `CIBW_BUILD_VERBOSITY_WINDOWS` | `CIBW_BUILD_VERBOSITY_LINUX` | `CIBW_BUILD_VERBOSITY_ANDROID` | `CIBW_BUILD_VERBOSITY_IOS` | `CIBW_BUILD_VERBOSITY_PYODIDE`
#### Examples #### Examples
@@ -1791,6 +1819,7 @@ Some options support placeholders, like `{project}`, `{package}` or `{wheel}`, t
padding-left: 10px; padding-left: 10px;
padding-right: 10px; padding-right: 10px;
line-height: normal; line-height: normal;
overflow: visible;
} }
.rst-content h3 .badges code.cmd-line:before, .rst-content h3 .badges code.toml:before, .rst-content h3 .badges code.env-var:before { .rst-content h3 .badges code.cmd-line:before, .rst-content h3 .badges code.toml:before, .rst-content h3 .badges code.env-var:before {
content: ' '; content: ' ';
+76 -3
View File
@@ -172,12 +172,85 @@ You must target pyodide with `--platform pyodide` (or use `--only` on the identi
It is also possible to target a specific Pyodide version by setting the [`pyodide-version`](options.md#pyodide-version) option to the desired version. Users are responsible for setting an appropriate Pyodide version according to the `pyodide-build` version. A list is available in Pyodide's [cross-build environments metadata file](https://github.com/pyodide/pyodide/blob/main/pyodide-cross-build-environments.json), which can be viewed more easily by installing `pyodide-build` from PyPI and using `pyodide xbuildenv search --all` to see a compatibility table. It is also possible to target a specific Pyodide version by setting the [`pyodide-version`](options.md#pyodide-version) option to the desired version. Users are responsible for setting an appropriate Pyodide version according to the `pyodide-build` version. A list is available in Pyodide's [cross-build environments metadata file](https://github.com/pyodide/pyodide/blob/main/pyodide-cross-build-environments.json), which can be viewed more easily by installing `pyodide-build` from PyPI and using `pyodide xbuildenv search --all` to see a compatibility table.
If there are pre-releases available for a newer Python version, the `pyodide-prerelease` [`enable`](options.md#enable) can be used to include pre-release versions. If there are pre-releases available for a newer Pyodide version, the `pyodide-prerelease` [`enable`](options.md#enable) can be used to include pre-release versions.
### Running tests ### Running tests
Currently, it's recommended to run tests using a `python -m` entrypoint, rather than a command line entrypoint, or a shell script. This is because custom entrypoints have some issues in the Pyodide virtual environment. For example, `pytest` may not work as a command line entrypoint, but will work as a `python -m pytest` entrypoint. Currently, it's recommended to run tests using a `python -m` entrypoint, rather than a command line entrypoint, or a shell script. This is because custom entrypoints have some issues in the Pyodide virtual environment. For example, `pytest` may not work as a command line entrypoint, but will work as a `python -m pytest` entrypoint.
## Android {: android}
### Prerequisites
cibuildwheel can build Android wheels on any POSIX platform supported by the Android
development tools, which currently means Linux x86_64, macOS ARM64 or macOS x86_64. Any
of these platforms can be used to build wheels for any Android architecture supported by
Python. However, *testing* wheels has additional requirements: see the section below.
If you already have an Android SDK, export the `ANDROID_HOME` environment variable to
point at its location. Otherwise, here's how to install it:
* Download the "Command line tools" from <https://developer.android.com/studio>.
* Create a directory `android-sdk/cmdline-tools`, and unzip the command line
tools package into it.
* Rename `android-sdk/cmdline-tools/cmdline-tools` to
`android-sdk/cmdline-tools/latest`.
* `export ANDROID_HOME=/path/to/android-sdk`
cibuildwheel will automatically use the SDK's `sdkmanager` to install any packages it
needs.
It also requires the following commands to be on the `PATH`:
* `curl`
* `java` (or set the `JAVA_HOME` environment variable)
* `patchelf` (if the wheel links against any external libraries)
### Android version compatibility
Android builds will honor the `ANDROID_API_LEVEL` environment variable to set the
minimum supported [API level](https://developer.android.com/tools/releases/platforms)
for generated wheels. This will default to the minimum API level of the selected Python
version.
### Build frontend support
Android builds only support the `build` frontend. In principle, support for the
`build[uv]` frontend should be possible, but `uv` [doesn't currently have support for
cross-platform builds](https://github.com/astral-sh/uv/issues/7957), and [doesn't have
support for iOS or Android wheel tags](https://github.com/astral-sh/uv/issues/8029).
### Tests
Tests are executed on a Gradle-managed emulator matching the architecture of the build
machine for example, if you're building on an ARM64 machine, then you can test an
ARM64 wheel. Wheels of other architectures can still be built, but testing will
automatically be skipped.
Running an emulator requires the build machine to either be bare-metal or support
nested virtualization. CI platforms known to meet this requirement are:
* GitHub Actions Linux x86_64
On Linux, the emulator needs access to the KVM virtualization interface. This may
require adding your user to a group, or [changing your udev
rules](https://github.blog/changelog/2024-04-02-github-actions-hardware-accelerated-android-virtualization-now-available/).
If the emulator fails to start, try running `$ANDROID_HOME/emulator/emulator
-accel-check`.
The Android test environment can't support running shell scripts, so the
[`test-command`](options.md#test-command) must be a Python command  see its
documentation for details.
If your package has dependencies which haven't been released on PyPI yet, you may want
to use the [`environment`](options.md#environment) option to set `PIP_EXTRA_INDEX_URL`
to one of the following URLs:
* https://chaquo.com/pypi-13.1
* https://pypi.anaconda.org/scientific-python-nightly-wheels/simple
## iOS {: #ios} ## iOS {: #ios}
### System requirements ### System requirements
@@ -192,7 +265,7 @@ iOS is effectively 2 platforms - physical devices, and simulators. While the API
* `arm64_iphoneos` (for physical iOS devices); * `arm64_iphoneos` (for physical iOS devices);
* `arm64_iphonesimulator` (for iOS simulators running on Apple Silicon macOS machines); and * `arm64_iphonesimulator` (for iOS simulators running on Apple Silicon macOS machines); and
* `x64_64_iphonesimulator` (for iOS simulators running on Intel macOS machines). * `x86_64_iphonesimulator` (for iOS simulators running on Intel macOS machines).
By default, cibuildwheel will build all wheels for the CPU architecture of the build machine. You can build all wheels for all architectures by specifying `--archs all`. By default, cibuildwheel will build all wheels for the CPU architecture of the build machine. You can build all wheels for all architectures by specifying `--archs all`.
@@ -241,6 +314,6 @@ If your project requires additional tools to build (such as `cmake`, `ninja`, or
If tests have been configured, the test suite will be executed on the simulator matching the architecture of the build machine - that is, if you're building on an ARM64 macOS machine, the ARM64 wheel will be tested on an ARM64 simulator. It is not possible to use cibuildwheel to test wheels on other simulators, or on physical devices. If tests have been configured, the test suite will be executed on the simulator matching the architecture of the build machine - that is, if you're building on an ARM64 macOS machine, the ARM64 wheel will be tested on an ARM64 simulator. It is not possible to use cibuildwheel to test wheels on other simulators, or on physical devices.
The iOS test environment can't support running shell scripts, so the [`test-command`](options.md#test-command) value must be specified as if it were a command line being passed to `python -m ...`. In addition, the project must use [`test-sources`](options.md#test-sources) to specify the minimum subset of files that should be copied to the test environment. This is because the test must be run "on device", and the simulator device will not have access to the local project directory. The iOS test environment can't support running shell scripts, so the [`test-command`](options.md#test-command) value must be specified as if it were a command line being passed to `python -m ...`.
The test process uses the same testbed used by CPython itself to run the CPython test suite. It is an Xcode project that has been configured to have a single Xcode "XCUnit" test - the result of which reports the success or failure of running `python -m <test-command>`. The test process uses the same testbed used by CPython itself to run the CPython test suite. It is an Xcode project that has been configured to have a single Xcode "XCUnit" test - the result of which reports the success or failure of running `python -m <test-command>`.
+2
View File
@@ -23,6 +23,8 @@ cibuildwheel
!!!tip !!!tip
You can pass the `--platform linux` option to cibuildwheel to build Linux wheels, even if you're not on Linux. On most machines, the easiest builds to try are the Linux builds. You don't need any software installed except a Docker daemon, such as [Docker Desktop](https://www.docker.com/get-started/). Each platform that cibuildwheel supports has its own system requirements and platform-specific behaviors. See the [platforms page](platforms.md) for details. You can pass the `--platform linux` option to cibuildwheel to build Linux wheels, even if you're not on Linux. On most machines, the easiest builds to try are the Linux builds. You don't need any software installed except a Docker daemon, such as [Docker Desktop](https://www.docker.com/get-started/). Each platform that cibuildwheel supports has its own system requirements and platform-specific behaviors. See the [platforms page](platforms.md) for details.
When building locally for macOS or iOS, regardless of the Python you use to invoke cibuildwheel, you'll need [official Python.org](https://www.python.org/downloads/) installations on your machine. `cibuildwheel` won't perform the build using unofficial Python installations such as from `brew` or `uv`.
You should see the builds taking place. You can experiment with [options](options.md) using pyproject.toml or environment variables. You should see the builds taking place. You can experiment with [options](options.md) using pyproject.toml or environment variables.
!!! tab "pyproject.toml" !!! tab "pyproject.toml"
+17
View File
@@ -126,6 +126,23 @@ function ThemeNav () {
}); });
link.prepend(expand); link.prepend(expand);
}); });
// EDIT by joerick
//
// workaround a bug with the site in safari. safari navigates to the
// anchor before the above code has run, specifically wrap with
// .wy-table-responsive activates css rules that change the size of
// tables, making anchor points move around.
console.log('Document ready, checking for anchor in URL');
if (window.location.hash) {
const anchorEl = document.querySelector(window.location.hash);
anchorEl.getBoundingClientRect(); // Force layout to ensure scrollIntoView works correctly
if (anchorEl) {
console.log('Anchor element:', anchorEl);
anchorEl.scrollIntoView({ behavior: 'instant', block: 'start' });
}
}
// end edit by joerick
}; };
nav.reset = function () { nav.reset = function () {
+16 -16
View File
@@ -31,15 +31,15 @@ title: Working examples
| [pyinstrument][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Python profiler with a C extension. No external dependencies. | | [pyinstrument][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Python profiler with a C extension. No external dependencies. |
| [scikit-image][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Image processing library. Uses cibuildwheel to build and test a project that uses Cython with platform-native code. | | [scikit-image][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Image processing library. Uses cibuildwheel to build and test a project that uses Cython with platform-native code. |
| [PyOxidizer][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A modern Python application packaging and distribution tool | | [PyOxidizer][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A modern Python application packaging and distribution tool |
| [twisted-iocpsupport][] | ![github icon][] | ![windows icon][] | A submodule of Twisted that hooks into native C APIs using Cython. |
| [cvxpy][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A Python-embedded modeling language for convex optimization problems. | | [cvxpy][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A Python-embedded modeling language for convex optimization problems. |
| [twisted-iocpsupport][] | ![github icon][] | ![windows icon][] | A submodule of Twisted that hooks into native C APIs using Cython. |
| [pedalboard][] | ![github icon][] | ![windows icon][] ![linux icon][] ![apple icon][] | A Python library for working with audio data and audio plugins by wrapping the [JUCE](https://github.com/juce-framework/JUCE/) C++ framework. Uses cibuildwheel to deploy on as many operating systems and Python versions as possible with only one dependency (any NumPy). | | [pedalboard][] | ![github icon][] | ![windows icon][] ![linux icon][] ![apple icon][] | A Python library for working with audio data and audio plugins by wrapping the [JUCE](https://github.com/juce-framework/JUCE/) C++ framework. Uses cibuildwheel to deploy on as many operating systems and Python versions as possible with only one dependency (any NumPy). |
| [websockets][] | ![travisci icon][] | ![apple icon][] ![linux icon][] | Library for building WebSocket servers and clients. Mostly written in Python, with a small C 'speedups' extension module. | | [websockets][] | ![travisci icon][] | ![apple icon][] ![linux icon][] | Library for building WebSocket servers and clients. Mostly written in Python, with a small C 'speedups' extension module. |
| [River][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | 🌊 Online machine learning in Python | | [River][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | 🌊 Online machine learning in Python |
| [aiortc][] | ![github icon][] | ![apple icon][] ![linux icon][] | WebRTC and ORTC implementation for Python using asyncio. | | [aiortc][] | ![github icon][] | ![apple icon][] ![linux icon][] | WebRTC and ORTC implementation for Python using asyncio. |
| [OpenSpiel][] | ![github icon][] | ![apple icon][] ![linux icon][] | OpenSpiel is a collection of environments and algorithms for research in general reinforcement learning and search/planning in games. | | [OpenSpiel][] | ![github icon][] | ![apple icon][] ![linux icon][] | OpenSpiel is a collection of environments and algorithms for research in general reinforcement learning and search/planning in games. |
| [UltraJSON][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Ultra fast JSON decoder and encoder written in C with Python bindings |
| [Dependency Injector][] | ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Dependency injection framework for Python, uses Windows TravisCI | | [Dependency Injector][] | ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Dependency injection framework for Python, uses Windows TravisCI |
| [UltraJSON][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Ultra fast JSON decoder and encoder written in C with Python bindings |
| [pyzmq][] | ![github icon][] ![circleci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Python bindings for zeromq, the networking library. Uses Cython on CPython and CFFI on PyPy. ARM wheels for linux are built natively on CircleCI. | | [pyzmq][] | ![github icon][] ![circleci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Python bindings for zeromq, the networking library. Uses Cython on CPython and CFFI on PyPy. ARM wheels for linux are built natively on CircleCI. |
| [CTranslate2][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Includes libraries from the [Intel oneAPI toolkit](https://www.intel.com/content/www/us/en/developer/tools/oneapi/base-toolkit.html) and CUDA kernels compiled for multiple GPU architectures. | | [CTranslate2][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Includes libraries from the [Intel oneAPI toolkit](https://www.intel.com/content/www/us/en/developer/tools/oneapi/base-toolkit.html) and CUDA kernels compiled for multiple GPU architectures. |
| [Implicit][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Includes GPU support for linux wheels | | [Implicit][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Includes GPU support for linux wheels |
@@ -71,22 +71,22 @@ title: Working examples
| [mosec][] | ![github icon][] | ![linux icon][] ![apple icon][] | A machine learning model serving framework powered by Rust | | [mosec][] | ![github icon][] | ![linux icon][] ![apple icon][] | A machine learning model serving framework powered by Rust |
| [Picologging][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A high-performance logging library for Python. | | [Picologging][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A high-performance logging library for Python. |
| [pybind11 cmake_example][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Example pybind11 module built with a CMake-based build system | | [pybind11 cmake_example][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Example pybind11 module built with a CMake-based build system |
| [Rtree][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Rtree: spatial index for Python GIS |
| [markupsafe][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Safely add untrusted strings to HTML/XML markup. | | [markupsafe][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Safely add untrusted strings to HTML/XML markup. |
| [Rtree][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Rtree: spatial index for Python GIS |
| [KDEpy][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Kernel Density Estimation in Python | | [KDEpy][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Kernel Density Estimation in Python |
| [dd-trace-py][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Uses custom alternate arch emulation on GitHub | | [dd-trace-py][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Uses custom alternate arch emulation on GitHub |
| [tgcalls][] | ![github icon][] | ![apple icon][] ![windows icon][] | Python `pybind11` binding to Telegram's WebRTC library with third party dependencies like `OpenSSL`, `MozJPEG`, `FFmpeg`, etc. | | [tgcalls][] | ![github icon][] | ![apple icon][] ![windows icon][] | Python `pybind11` binding to Telegram's WebRTC library with third party dependencies like `OpenSSL`, `MozJPEG`, `FFmpeg`, etc. |
| [pybind11 python_example][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Example pybind11 module built with a Python-based build system |
| [python-rapidjson][] | ![travisci icon][] ![gitlab icon][] | ![windows icon][] ![linux icon][] | Python wrapper around rapidjson | | [python-rapidjson][] | ![travisci icon][] ![gitlab icon][] | ![windows icon][] ![linux icon][] | Python wrapper around rapidjson |
| [pybind11 python_example][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Example pybind11 module built with a Python-based build system |
| [sourmash][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Quickly search, compare, and analyze genomic and metagenomic data sets. | | [sourmash][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Quickly search, compare, and analyze genomic and metagenomic data sets. |
| [abess][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A fast best-subset selection library. It uses cibuildwheel to build a large project with C++ extensions. | | [abess][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A fast best-subset selection library. It uses cibuildwheel to build a large project with C++ extensions. |
| [python-snappy][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Python bindings for the snappy google library | | [python-snappy][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Python bindings for the snappy google library |
| [jq.py][] | ![travisci icon][] | ![apple icon][] ![linux icon][] | Python bindings for jq |
| [cyvcf2][] | ![github icon][] | ![apple icon][] ![linux icon][] | cython + htslib == fast VCF and BCF processing | | [cyvcf2][] | ![github icon][] | ![apple icon][] ![linux icon][] | cython + htslib == fast VCF and BCF processing |
| [jq.py][] | ![travisci icon][] | ![apple icon][] ![linux icon][] | Python bindings for jq |
| [matrixprofile][] | ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A Python 3 library making time series data mining tasks, utilizing matrix profile algorithms, accessible to everyone. | | [matrixprofile][] | ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A Python 3 library making time series data mining tasks, utilizing matrix profile algorithms, accessible to everyone. |
| [Tokenizer][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Fast and customizable text tokenization library with BPE and SentencePiece support | | [Tokenizer][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Fast and customizable text tokenization library with BPE and SentencePiece support |
| [iminuit][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Jupyter-friendly Python interface for C++ MINUIT2 |
| [Confluent client for Kafka][] | ![travisci icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | setup in `tools/wheels/build-wheels.bat` | | [Confluent client for Kafka][] | ![travisci icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | setup in `tools/wheels/build-wheels.bat` |
| [iminuit][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Jupyter-friendly Python interface for C++ MINUIT2 |
| [pillow-heif][] | ![github icon][] ![cirrusci icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Bindings to libheif library with third party dependencies. Fully automated CI for tests and publishing including Apple Silicon builds. | | [pillow-heif][] | ![github icon][] ![cirrusci icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Bindings to libheif library with third party dependencies. Fully automated CI for tests and publishing including Apple Silicon builds. |
| [keyvi][] | ![github icon][] | ![linux icon][] ![apple icon][] | FST based key value index highly optimized for size and lookup performance, utilizes ccache action for improved runtime | | [keyvi][] | ![github icon][] | ![linux icon][] ![apple icon][] | FST based key value index highly optimized for size and lookup performance, utilizes ccache action for improved runtime |
| [PyGLM][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Fast OpenGL Mathematics (GLM) for Python | | [PyGLM][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Fast OpenGL Mathematics (GLM) for Python |
@@ -94,8 +94,8 @@ title: Working examples
| [iDynTree][] | ![github icon][] | ![linux icon][] | Uses manylinux_2_24 | | [iDynTree][] | ![github icon][] | ![linux icon][] | Uses manylinux_2_24 |
| [streaming-form-data][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Streaming parser for multipart/form-data written in Cython | | [streaming-form-data][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Streaming parser for multipart/form-data written in Cython |
| [power-grid-model][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Python/C++ library for distribution power system analysis | | [power-grid-model][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Python/C++ library for distribution power system analysis |
| [bx-python][] | ![travisci icon][] | ![apple icon][] ![linux icon][] | A library that includes Cython extensions. |
| [pybase64][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Fast Base64 encoding/decoding in Python | | [pybase64][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Fast Base64 encoding/decoding in Python |
| [bx-python][] | ![travisci icon][] | ![apple icon][] ![linux icon][] | A library that includes Cython extensions. |
| [boost-histogram][] | ![github icon][] ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Supports full range of wheels, including PyPy and alternate archs. | | [boost-histogram][] | ![github icon][] ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Supports full range of wheels, including PyPy and alternate archs. |
| [Imagecodecs (fork)][] | ![azurepipelines icon][] | ![apple icon][] ![linux icon][] | Over 20 external dependencies in compiled libraries, custom docker image, `libomp`, `openblas` and `install_name_tool` for macOS. | | [Imagecodecs (fork)][] | ![azurepipelines icon][] | ![apple icon][] ![linux icon][] | Over 20 external dependencies in compiled libraries, custom docker image, `libomp`, `openblas` and `install_name_tool` for macOS. |
| [Python-WebRTC][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | a Python extension that provides bindings to WebRTC M92 | | [Python-WebRTC][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | a Python extension that provides bindings to WebRTC M92 |
@@ -111,8 +111,8 @@ title: Working examples
| [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. |
| [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. |
| [aalink][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Async Python interface for Ableton Link. |
| [GSD][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Cython and NumPy project with 64-bit wheels. | | [GSD][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Cython and NumPy project with 64-bit wheels. |
| [aalink][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Async Python interface for Ableton Link. |
| [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. |
| [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. |
| [werpy][] | ![github icon][] | ![windows icon][] ![linux icon][] ![apple icon][] | An ultra-fast python package using optimized dynamic programming to compute the Word Error Rate (WER). | | [werpy][] | ![github icon][] | ![windows icon][] ![linux icon][] ![apple icon][] | An ultra-fast python package using optimized dynamic programming to compute the Word Error Rate (WER). |
@@ -141,15 +141,15 @@ title: Working examples
[pyinstrument]: https://github.com/joerick/pyinstrument [pyinstrument]: https://github.com/joerick/pyinstrument
[scikit-image]: https://github.com/scikit-image/scikit-image [scikit-image]: https://github.com/scikit-image/scikit-image
[PyOxidizer]: https://github.com/indygreg/PyOxidizer [PyOxidizer]: https://github.com/indygreg/PyOxidizer
[twisted-iocpsupport]: https://github.com/twisted/twisted-iocpsupport
[cvxpy]: https://github.com/cvxpy/cvxpy [cvxpy]: https://github.com/cvxpy/cvxpy
[twisted-iocpsupport]: https://github.com/twisted/twisted-iocpsupport
[pedalboard]: https://github.com/spotify/pedalboard [pedalboard]: https://github.com/spotify/pedalboard
[websockets]: https://github.com/python-websockets/websockets [websockets]: https://github.com/python-websockets/websockets
[River]: https://github.com/online-ml/river [River]: https://github.com/online-ml/river
[aiortc]: https://github.com/aiortc/aiortc [aiortc]: https://github.com/aiortc/aiortc
[OpenSpiel]: https://github.com/google-deepmind/open_spiel [OpenSpiel]: https://github.com/google-deepmind/open_spiel
[UltraJSON]: https://github.com/ultrajson/ultrajson
[Dependency Injector]: https://github.com/ets-labs/python-dependency-injector [Dependency Injector]: https://github.com/ets-labs/python-dependency-injector
[UltraJSON]: https://github.com/ultrajson/ultrajson
[pyzmq]: https://github.com/zeromq/pyzmq [pyzmq]: https://github.com/zeromq/pyzmq
[CTranslate2]: https://github.com/OpenNMT/CTranslate2 [CTranslate2]: https://github.com/OpenNMT/CTranslate2
[Implicit]: https://github.com/benfred/implicit [Implicit]: https://github.com/benfred/implicit
@@ -181,22 +181,22 @@ title: Working examples
[mosec]: https://github.com/mosecorg/mosec [mosec]: https://github.com/mosecorg/mosec
[Picologging]: https://github.com/microsoft/picologging [Picologging]: https://github.com/microsoft/picologging
[pybind11 cmake_example]: https://github.com/pybind/cmake_example [pybind11 cmake_example]: https://github.com/pybind/cmake_example
[Rtree]: https://github.com/Toblerity/rtree
[markupsafe]: https://github.com/pallets/markupsafe [markupsafe]: https://github.com/pallets/markupsafe
[Rtree]: https://github.com/Toblerity/rtree
[KDEpy]: https://github.com/tommyod/KDEpy [KDEpy]: https://github.com/tommyod/KDEpy
[dd-trace-py]: https://github.com/DataDog/dd-trace-py [dd-trace-py]: https://github.com/DataDog/dd-trace-py
[tgcalls]: https://github.com/MarshalX/tgcalls [tgcalls]: https://github.com/MarshalX/tgcalls
[pybind11 python_example]: https://github.com/pybind/python_example
[python-rapidjson]: https://github.com/python-rapidjson/python-rapidjson [python-rapidjson]: https://github.com/python-rapidjson/python-rapidjson
[pybind11 python_example]: https://github.com/pybind/python_example
[sourmash]: https://github.com/sourmash-bio/sourmash [sourmash]: https://github.com/sourmash-bio/sourmash
[abess]: https://github.com/abess-team/abess [abess]: https://github.com/abess-team/abess
[python-snappy]: https://github.com/intake/python-snappy [python-snappy]: https://github.com/intake/python-snappy
[jq.py]: https://github.com/mwilliamson/jq.py
[cyvcf2]: https://github.com/brentp/cyvcf2 [cyvcf2]: https://github.com/brentp/cyvcf2
[jq.py]: https://github.com/mwilliamson/jq.py
[matrixprofile]: https://github.com/matrix-profile-foundation/matrixprofile [matrixprofile]: https://github.com/matrix-profile-foundation/matrixprofile
[Tokenizer]: https://github.com/OpenNMT/Tokenizer [Tokenizer]: https://github.com/OpenNMT/Tokenizer
[iminuit]: https://github.com/scikit-hep/iminuit
[Confluent client for Kafka]: https://github.com/confluentinc/confluent-kafka-python [Confluent client for Kafka]: https://github.com/confluentinc/confluent-kafka-python
[iminuit]: https://github.com/scikit-hep/iminuit
[pillow-heif]: https://github.com/bigcat88/pillow_heif [pillow-heif]: https://github.com/bigcat88/pillow_heif
[keyvi]: https://github.com/KeyviDev/keyvi [keyvi]: https://github.com/KeyviDev/keyvi
[PyGLM]: https://github.com/Zuzu-Typ/PyGLM [PyGLM]: https://github.com/Zuzu-Typ/PyGLM
@@ -204,8 +204,8 @@ title: Working examples
[iDynTree]: https://github.com/robotology/idyntree [iDynTree]: https://github.com/robotology/idyntree
[streaming-form-data]: https://github.com/siddhantgoel/streaming-form-data [streaming-form-data]: https://github.com/siddhantgoel/streaming-form-data
[power-grid-model]: https://github.com/PowerGridModel/power-grid-model [power-grid-model]: https://github.com/PowerGridModel/power-grid-model
[bx-python]: https://github.com/bxlab/bx-python
[pybase64]: https://github.com/mayeut/pybase64 [pybase64]: https://github.com/mayeut/pybase64
[bx-python]: https://github.com/bxlab/bx-python
[boost-histogram]: https://github.com/scikit-hep/boost-histogram [boost-histogram]: https://github.com/scikit-hep/boost-histogram
[Imagecodecs (fork)]: https://github.com/czaki/imagecodecs_build [Imagecodecs (fork)]: https://github.com/czaki/imagecodecs_build
[Python-WebRTC]: https://github.com/MarshalX/python-webrtc [Python-WebRTC]: https://github.com/MarshalX/python-webrtc
@@ -221,8 +221,8 @@ title: Working examples
[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
[SiPM]: https://github.com/EdoPro98/SimSiPM [SiPM]: https://github.com/EdoPro98/SimSiPM
[aalink]: https://github.com/artfwo/aalink
[GSD]: https://github.com/glotzerlab/gsd [GSD]: https://github.com/glotzerlab/gsd
[aalink]: https://github.com/artfwo/aalink
[CorrectionLib]: https://github.com/cms-nanoAOD/correctionlib [CorrectionLib]: https://github.com/cms-nanoAOD/correctionlib
[xmlstarlet]: https://github.com/dimitern/xmlstarlet [xmlstarlet]: https://github.com/dimitern/xmlstarlet
[werpy]: https://github.com/analyticsinmotion/werpy [werpy]: https://github.com/analyticsinmotion/werpy
+6 -6
View File
@@ -1,12 +1,12 @@
jobs: jobs:
- job: linux - job: linux
pool: {vmImage: 'Ubuntu-20.04'} pool: {vmImage: 'ubuntu-latest'}
steps: steps:
- task: UsePythonVersion@0 - task: UsePythonVersion@0
- bash: | - bash: |
set -o errexit set -o errexit
python3 -m pip install --upgrade pip python3 -m pip install --upgrade pip
pip3 install cibuildwheel==3.0.0b5 pip3 install cibuildwheel==3.1.2
displayName: Install dependencies displayName: Install dependencies
- bash: cibuildwheel --output-dir wheelhouse . - bash: cibuildwheel --output-dir wheelhouse .
displayName: Build wheels displayName: Build wheels
@@ -14,13 +14,13 @@ jobs:
inputs: {pathtoPublish: 'wheelhouse'} inputs: {pathtoPublish: 'wheelhouse'}
- job: macos - job: macos
pool: {vmImage: 'macOS-13'} pool: {vmImage: 'macOS-latest'}
steps: steps:
- task: UsePythonVersion@0 - task: UsePythonVersion@0
- 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==3.0.0b5 python3 -m pip install cibuildwheel==3.1.2
displayName: Install dependencies displayName: Install dependencies
- bash: cibuildwheel --output-dir wheelhouse . - bash: cibuildwheel --output-dir wheelhouse .
displayName: Build wheels displayName: Build wheels
@@ -28,13 +28,13 @@ jobs:
inputs: {pathtoPublish: wheelhouse} inputs: {pathtoPublish: wheelhouse}
- job: windows - job: windows
pool: {vmImage: 'windows-2019'} pool: {vmImage: 'windows-latest'}
steps: steps:
- task: UsePythonVersion@0 - task: UsePythonVersion@0
- bash: | - bash: |
set -o errexit set -o errexit
python -m pip install --upgrade pip python -m pip install --upgrade pip
pip install cibuildwheel==3.0.0b5 pip install cibuildwheel==3.1.2
displayName: Install dependencies displayName: Install dependencies
- bash: cibuildwheel --output-dir wheelhouse . - bash: cibuildwheel --output-dir wheelhouse .
displayName: Build wheels displayName: Build wheels
+3 -3
View File
@@ -11,7 +11,7 @@ jobs:
- run: - run:
name: Build the Linux wheels. name: Build the Linux wheels.
command: | command: |
python3 -m pip install --user cibuildwheel==3.0.0b5 python3 -m pip install --user cibuildwheel==3.1.2
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==3.0.0b5 python3 -m pip install --user cibuildwheel==3.1.2
python3 -m cibuildwheel --output-dir wheelhouse python3 -m cibuildwheel --output-dir wheelhouse
- store_artifacts: - store_artifacts:
path: wheelhouse/ path: wheelhouse/
@@ -44,7 +44,7 @@ jobs:
name: Build the OS X wheels. name: Build the OS X wheels.
command: | command: |
sudo softwareupdate --install-rosetta --agree-to-license # for python<=3.8 or x86_64/universal2 tests sudo softwareupdate --install-rosetta --agree-to-license # for python<=3.8 or x86_64/universal2 tests
pip3 install cibuildwheel==3.0.0b5 pip3 install cibuildwheel==3.1.2
cibuildwheel --output-dir wheelhouse cibuildwheel --output-dir wheelhouse
- store_artifacts: - store_artifacts:
path: wheelhouse/ path: wheelhouse/
+1 -1
View File
@@ -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==3.0.0b5 - python -m pip install cibuildwheel==3.1.2
run_cibuildwheel_script: run_cibuildwheel_script:
- cibuildwheel - cibuildwheel
wheels_artifacts: wheels_artifacts:
+6 -2
View File
@@ -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==3.0.0b5 - python -m pip install cibuildwheel==3.1.2
run_cibuildwheel_script: run_cibuildwheel_script:
- cibuildwheel - cibuildwheel
wheels_artifacts: wheels_artifacts:
@@ -52,9 +52,13 @@ windows_x86_task:
memory: 4G memory: 4G
install_pre_requirements_script: install_pre_requirements_script:
- certutil -generateSSTFromWU roots.sst
- certutil -addstore -f root roots.sst
- del roots.sst
- choco install -y --no-progress python3 --version 3.12.4 - choco install -y --no-progress python3 --version 3.12.4
- refreshenv - refreshenv
- echo PATH=%PATH% >> "%CIRRUS_ENV%" - powershell -Command "$cleanPath = $env:PATH -replace ';+$', ''; Add-Content -Path $env:CIRRUS_ENV -Value ('PATH=' + $cleanPath)"
<<: *BUILD_AND_STORE_WHEELS <<: *BUILD_AND_STORE_WHEELS
macos_arm64_task: macos_arm64_task:
+24 -8
View File
@@ -16,23 +16,27 @@ jobs:
runs-on: ${{ matrix.runs-on }} runs-on: ${{ matrix.runs-on }}
strategy: strategy:
matrix: matrix:
os: [ linux-intel, linux-arm, windows, macOS-intel, macOS-arm, iOS, pyodide ]
include: include:
- archs: auto
platform: auto
- os: linux-intel - os: linux-intel
runs-on: ubuntu-latest runs-on: ubuntu-latest
- os: linux-arm - os: linux-arm
runs-on: ubuntu-24.04-arm runs-on: ubuntu-24.04-arm
- os: windows - os: windows-intel
runs-on: windows-latest runs-on: windows-latest
- os: windows-arm
runs-on: windows-11-arm
- os: macos-intel - os: macos-intel
# macos-13 was the last x86_64 runner # macos-13 was the last x86_64 runner
runs-on: macos-13 runs-on: macos-13
- os: macos-arm - os: macos-arm
# macos-14+ (including latest) are ARM64 runners # macos-14+ (including latest) are ARM64 runners
runs-on: macos-latest runs-on: macos-latest
archs: auto,universal2 - os: android-intel
runs-on: ubuntu-latest
platform: android
- os: android-arm
runs-on: macos-latest
platform: android
- os: ios - os: ios
runs-on: macos-latest runs-on: macos-latest
platform: ios platform: ios
@@ -43,11 +47,23 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
# GitHub Actions can't currently run the Android emulator on macOS.
- name: Skip Android tests on macOS
if: matrix.os == 'android-arm'
run: echo "CIBW_TEST_COMMAND=" >> "$GITHUB_ENV"
# https://github.blog/changelog/2024-04-02-github-actions-hardware-accelerated-android-virtualization-now-available/
- name: Enable KVM for Android emulator
if: matrix.os == 'android-intel'
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- name: Build wheels - name: Build wheels
uses: pypa/cibuildwheel@v3.0.0b5 uses: pypa/cibuildwheel@v3.1.2
env: env:
CIBW_PLATFORM: ${{ matrix.platform }} CIBW_PLATFORM: ${{ matrix.platform || 'auto' }}
CIBW_ARCHS: ${{ matrix.archs }}
# Can also be configured directly, using `with:` # Can also be configured directly, using `with:`
# with: # with:
# package-dir: . # package-dir: .
+2 -2
View File
@@ -9,13 +9,13 @@ jobs:
strategy: strategy:
matrix: matrix:
# macos-13 is an intel runner, macos-14 is apple silicon # macos-13 is an intel runner, macos-14 is apple silicon
os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, macos-13, macos-14] os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, windows-11-arm, macos-13, macos-14]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Build wheels - name: Build wheels
uses: pypa/cibuildwheel@v3.0.0b5 uses: pypa/cibuildwheel@v3.1.2
# env: # env:
# CIBW_SOME_OPTION: value # CIBW_SOME_OPTION: value
# ... # ...
+2 -2
View File
@@ -9,13 +9,13 @@ jobs:
strategy: strategy:
matrix: matrix:
# macos-13 is an intel runner, macos-14 is apple silicon # macos-13 is an intel runner, macos-14 is apple silicon
os: [ubuntu-latest, windows-latest, macos-13, macos-14] os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, windows-11-arm, macos-13, macos-14]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Build wheels - name: Build wheels
run: pipx run cibuildwheel==3.0.0b5 run: pipx run cibuildwheel==3.1.2
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
with: with:
+2 -2
View File
@@ -9,7 +9,7 @@ jobs:
strategy: strategy:
matrix: matrix:
# macos-13 is an intel runner, macos-14 is apple silicon # macos-13 is an intel runner, macos-14 is apple silicon
os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, macos-13, macos-14] os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, windows-11-arm, macos-13, macos-14]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -21,7 +21,7 @@ jobs:
platforms: all platforms: all
- name: Build wheels - name: Build wheels
uses: pypa/cibuildwheel@v3.0.0b5 uses: pypa/cibuildwheel@v3.1.2
env: env:
# configure cibuildwheel on Linux to build native archs ('auto'), # configure cibuildwheel on Linux to build native archs ('auto'),
# and to split the remaining architectures between the x86_64 and # and to split the remaining architectures between the x86_64 and
+3 -3
View File
@@ -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==3.0.0b5 - python -m pip install cibuildwheel==3.1.2
- 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 --allow-downgrade --version 3.12.4 - choco install python -y --allow-downgrade --version 3.12.4
- choco install git.install -y - choco install git.install -y
- py -m pip install cibuildwheel==3.0.0b5 - py -m pip install cibuildwheel==3.1.2
script: script:
- py -m cibuildwheel --output-dir wheelhouse --platform windows - py -m cibuildwheel --output-dir wheelhouse --platform windows
artifacts: artifacts:
@@ -35,7 +35,7 @@ windows:
macos: macos:
image: macos-14-xcode-15 image: macos-14-xcode-15
before_script: before_script:
- python3 -m pip install cibuildwheel==3.0.0b5 - python3 -m pip install cibuildwheel==3.1.2
script: script:
- python3 -m cibuildwheel --output-dir wheelhouse - python3 -m cibuildwheel --output-dir wheelhouse
artifacts: artifacts:
+1 -1
View File
@@ -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==3.0.0b5 - python -m pip install cibuildwheel==3.1.2
# 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:
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
- ln -s /c/Python312/python.exe /c/Python312/python3.exe - ln -s /c/Python312/python.exe /c/Python312/python3.exe
install: install:
- python3 -m pip install cibuildwheel==3.0.0b5 - python3 -m pip install cibuildwheel==3.1.2
script: script:
# build the wheels, put them into './dist' # build the wheels, put them into './dist'
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
- ln -s /c/Python312/python.exe /c/Python312/python3.exe - ln -s /c/Python312/python.exe /c/Python312/python3.exe
install: install:
- python3 -m pip install cibuildwheel==3.0.0b5 - python3 -m pip install cibuildwheel==3.1.2
script: script:
# build the wheels, put them into './wheelhouse' # build the wheels, put them into './wheelhouse'
+2 -2
View File
@@ -52,7 +52,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==3.0.0b5 twine install: python3 -m pip install cibuildwheel==3.1.2 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
@@ -60,7 +60,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==3.0.0b5 twine install: python3 -m pip install cibuildwheel==3.1.2 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
+2 -2
View File
@@ -27,9 +27,9 @@ nav:
- faq.md - faq.md
- cpp_standards.md - cpp_standards.md
- Reference: - Reference:
- platforms.md
- configuration.md
- options.md - options.md
- configuration.md
- platforms.md
- working-examples.md - working-examples.md
- About: - About:
- contributing.md - contributing.md
+11 -2
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project] [project]
name = "cibuildwheel" name = "cibuildwheel"
version = "3.0.0b5" version = "3.1.2"
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"
@@ -39,11 +39,15 @@ classifiers = [
dependencies = [ dependencies = [
"bashlex!=0.13", "bashlex!=0.13",
"bracex", "bracex",
"build>=1.0.0",
"certifi", "certifi",
"dependency-groups>=1.2", "dependency-groups>=1.2",
"filelock", "filelock",
"humanize",
"packaging>=20.9", "packaging>=20.9",
"platformdirs" "platformdirs",
"pyelftools>=0.29",
"wheel>=0.33.6",
] ]
[project.optional-dependencies] [project.optional-dependencies]
@@ -105,6 +109,9 @@ filterwarnings = ["error"]
log_cli_level = "INFO" log_cli_level = "INFO"
markers = [ markers = [
"serial: tests that must *not* be run in parallel (deselect with '-m \"not serial\"')", "serial: tests that must *not* be run in parallel (deselect with '-m \"not serial\"')",
"ios: tests that run on iOS",
"android: tests that run on Android",
"pyodide: tests that run on Pyodide",
] ]
[tool.mypy] [tool.mypy]
@@ -144,6 +151,7 @@ py-version = "3.11"
jobs = "0" jobs = "0"
fail-on = ["E", "F"] fail-on = ["E", "F"]
fail-under = "9.8" fail-under = "9.8"
max-positional-arguments = "7"
reports.output-format = "colorized" reports.output-format = "colorized"
messages_control.enable = [ messages_control.enable = [
"useless-suppression", "useless-suppression",
@@ -172,6 +180,7 @@ messages_control.disable = [
"unsubscriptable-object", "unsubscriptable-object",
"wrong-import-position", "wrong-import-position",
"unused-argument", # Handled by Ruff "unused-argument", # Handled by Ruff
"import-outside-toplevel", # 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" "consider-using-in", # MyPy can't narrow "in"
] ]
+384
View File
@@ -0,0 +1,384 @@
import os
import platform
import re
from dataclasses import dataclass
from shutil import rmtree
from subprocess import CalledProcessError
from textwrap import dedent
from zipfile import ZipFile
import pytest
from .test_projects import new_c_project
from .utils import cibuildwheel_run, expected_wheels
pytestmark = pytest.mark.android
CIBW_PLATFORM = os.environ.get("CIBW_PLATFORM", "android")
if CIBW_PLATFORM != "android":
pytest.skip(f"{CIBW_PLATFORM=}", allow_module_level=True)
if (platform.system(), platform.machine()) not in [
("Linux", "x86_64"),
("Darwin", "arm64"),
("Darwin", "x86_64"),
]:
pytest.skip(
f"cibuildwheel does not support building Android wheels on "
f"{platform.system()} {platform.machine()}",
allow_module_level=True,
)
# Detect CI services which have the Android SDK pre-installed.
ci_supports_build = (
("CIRRUS_CI" in os.environ and platform.system() == "Darwin")
or "GITHUB_ACTIONS" in os.environ
or "TF_BUILD" in os.environ # Azure Pipelines
)
if "ANDROID_HOME" not in os.environ:
msg = "ANDROID_HOME environment variable is not set"
if ci_supports_build:
pytest.fail(msg)
else:
pytest.skip(msg, allow_module_level=True)
# Many CI services don't support running the Android emulator: see platforms.md.
ci_supports_emulator = "GITHUB_ACTIONS" in os.environ and platform.system() == "Linux"
def needs_emulator(test):
# All copies of the testbed app run on the same emulator with the same
# application ID, so these tests must be run serially.
test = pytest.mark.serial(test)
if ci_supports_build and not ci_supports_emulator:
test = pytest.mark.skip("This CI platform doesn't support the emulator")(test)
return test
@dataclass
class Architecture:
linux_machine: str
macos_machine: str
android_abi: str
archs = [
Architecture("aarch64", "arm64", "arm64_v8a"),
Architecture("x86_64", "x86_64", "x86_64"),
]
native_arch = next(
arch for arch in archs if platform.machine() in [arch.linux_machine, arch.macos_machine]
)
cp313_env = {
"CIBW_PLATFORM": "android",
"CIBW_BUILD": "cp313-*",
}
def test_android_home(tmp_path, capfd):
new_c_project().generate(tmp_path)
env = os.environ.copy()
del env["ANDROID_HOME"]
with pytest.raises(CalledProcessError):
cibuildwheel_run(tmp_path, env={**env, **cp313_env})
assert "ANDROID_HOME environment variable is not set" in capfd.readouterr().err
def test_frontend_good(tmp_path):
new_c_project().generate(tmp_path)
wheels = cibuildwheel_run(
tmp_path,
add_env={**cp313_env, "CIBW_BUILD_FRONTEND": "build"},
)
assert wheels == [f"spam-0.1.0-cp313-cp313-android_21_{native_arch.android_abi}.whl"]
@pytest.mark.parametrize("frontend", ["build[uv]", "pip"])
def test_frontend_bad(frontend, tmp_path, capfd):
new_c_project().generate(tmp_path)
with pytest.raises(CalledProcessError):
cibuildwheel_run(
tmp_path,
add_env={**cp313_env, "CIBW_BUILD_FRONTEND": frontend},
)
assert "Android requires the build frontend to be 'build'" in capfd.readouterr().err
def test_expected_wheels(tmp_path):
new_c_project().generate(tmp_path)
wheels = cibuildwheel_run(tmp_path, add_env={"CIBW_PLATFORM": "android"})
assert wheels == expected_wheels(
"spam", "0.1.0", platform="android", machine_arch=native_arch.android_abi
)
@needs_emulator
def test_archs(tmp_path, capfd):
new_c_project().generate(tmp_path)
# Build all architectures while checking the handling of the `before` commands.
command_pattern = 'echo "Hello from {0}, package={{package}}, python=$(which python)"'
output_pattern = (
f"Hello from {{0}}, package={tmp_path}, python=/.+/cp313-android_{{1}}/venv/bin/python"
)
wheels = cibuildwheel_run(
tmp_path,
add_env={
**cp313_env,
"CIBW_ARCHS": "all",
"CIBW_BEFORE_ALL": "echo 'Hello from before_all'",
"CIBW_BEFORE_BUILD": command_pattern.format("before_build"),
"CIBW_BEFORE_TEST": command_pattern.format("before_test"),
"CIBW_TEST_COMMAND": (
"python -c 'import platform; print(f\"Hello from {platform.machine()}\")'"
),
},
)
assert wheels == [f"spam-0.1.0-cp313-cp313-android_21_{arch.android_abi}.whl" for arch in archs]
stdout, stderr = capfd.readouterr()
lines = (line for line in stdout.splitlines() if line.startswith("Hello from"))
assert next(lines) == "Hello from before_all"
# All architectures should be built, but only the native architecture should run tests.
for arch in archs:
abi = arch.android_abi
assert re.fullmatch(output_pattern.format("before_build", abi), next(lines))
if arch == native_arch:
assert re.fullmatch(output_pattern.format("before_test", abi), next(lines))
assert next(lines) == f"Hello from {arch.linux_machine}"
else:
assert (
f"Skipping tests for {arch.android_abi}, as the build machine "
f"only supports {native_arch.android_abi}"
) in stderr
try:
line = next(lines)
except StopIteration:
pass
else:
pytest.fail(f"Unexpected line: {line!r}")
def test_build_requires(tmp_path, capfd):
# Build-time requirements should be installed for the build platform, not for Android. Prove
# this by installing some non-pure-Python requirements and using them in setup.py.
#
# setup_requires is installed via ProjectBuilder.get_requires_for_build.
project = new_c_project(
setup_py_setup_args_add="setup_requires=['cmake==3.31.4']",
setup_py_add=dedent(
"""\
if "egg_info" not in sys.argv:
import subprocess
subprocess.run(["cmake", "--version"], check=True)
from bitarray import bitarray
print(f"{bitarray('10110').count()=}")
"""
),
)
# [build_system] requires is installed via ProjectBuilder.build_system_requires.
project.files["pyproject.toml"] = dedent(
"""\
[build-system]
requires = ["setuptools", "wheel", "bitarray==3.3.2"]
"""
)
project.generate(tmp_path)
cibuildwheel_run(tmp_path, add_env={**cp313_env})
# Test for a specific version to minimize the chance that we ran a system cmake.
stdout = capfd.readouterr().out
assert "cmake version 3.31.4" in stdout
assert "bitarray('10110').count()=3" in stdout
@pytest.fixture
def spam_env(tmp_path):
project = new_c_project()
project.files["test_spam.py"] = dedent(
"""\
import spam
def test_spam():
assert spam.filter("ham")
assert not spam.filter("spam")
print("Spam test passed")
"""
)
project.generate(tmp_path)
return {
**cp313_env,
"CIBW_TEST_SOURCES": "test_spam.py",
"CIBW_TEST_REQUIRES": "pytest==8.3.5",
}
@needs_emulator
@pytest.mark.parametrize(
("command", "expected_output"),
[
("python -c 'import test_spam; test_spam.test_spam()'", "Spam test passed"),
("python -m pytest test_spam.py", "=== 1 passed in "),
("pytest test_spam.py", "=== 1 passed in "),
],
)
def test_test_command_good(command, expected_output, tmp_path, spam_env, capfd):
cibuildwheel_run(tmp_path, add_env={**spam_env, "CIBW_TEST_COMMAND": command})
stdout, stderr = capfd.readouterr()
assert expected_output in stdout
if not command.startswith("python"):
assert (
f"Test command {command!r} is not supported on Android. cibuildwheel "
"will try to execute it as if it started with 'python -m'."
) in stderr
@needs_emulator
@pytest.mark.parametrize(
("command", "expected_output"),
[
# Build-time failure: unrecognized command
(
"./test_spam.py",
"Test command './test_spam.py' is not supported on Android. "
"Supported commands are 'python -m' and 'python -c'.",
),
# Build-time failure: unrecognized placeholder
(
"pytest {project}",
"Test command 'pytest {project}' with a '{project}' or '{package}' "
"placeholder is not supported on Android",
),
(
"pytest {package}",
"Test command 'pytest {package}' with a '{project}' or '{package}' "
"placeholder is not supported on Android",
),
# Runtime failure
("pytest test_ham.py", "not found: test_ham.py"),
],
)
def test_test_command_bad(command, expected_output, tmp_path, spam_env, capfd):
with pytest.raises(CalledProcessError):
cibuildwheel_run(tmp_path, add_env={**spam_env, "CIBW_TEST_COMMAND": command})
assert expected_output in capfd.readouterr().err
@needs_emulator
def test_package_subdir(tmp_path, spam_env, capfd):
spam_paths = list(tmp_path.iterdir())
package_dir = tmp_path / "package"
package_dir.mkdir()
for path in spam_paths:
path.rename(package_dir / path.name)
test_filename = "package/" + spam_env["CIBW_TEST_SOURCES"]
cibuildwheel_run(
tmp_path,
package_dir,
add_env={
**spam_env,
"CIBW_TEST_SOURCES": test_filename,
"CIBW_TEST_COMMAND": f"python -m pytest {test_filename}",
},
)
assert "=== 1 passed in " in capfd.readouterr().out
@needs_emulator
def test_no_test_sources(tmp_path, capfd):
new_c_project().generate(tmp_path)
with pytest.raises(CalledProcessError):
cibuildwheel_run(
tmp_path,
add_env={**cp313_env, "CIBW_TEST_COMMAND": "python -m unittest discover"},
)
assert (
"On this platform, you must copy your test files to the testbed app by "
"setting the `test-sources` option"
) in capfd.readouterr().err
@needs_emulator
def test_api_level(tmp_path, capfd):
project = new_c_project()
project.files["pyproject.toml"] = dedent(
"""\
[build-system]
requires = ["setuptools"]
[tool.cibuildwheel]
android.environment.ANDROID_API_LEVEL = "33"
android.environment.PIP_EXTRA_INDEX_URL = "https://chaquo.com/pypi-13.1"
"""
)
project.generate(tmp_path)
wheels = cibuildwheel_run(
tmp_path,
add_env={
**cp313_env,
# Verify that Android dependencies can be installed from the Chaquopy repository, and
# that wheels tagged with an older version of Android (in this case 24) are still
# accepted.
"CIBW_TEST_REQUIRES": "bitarray==3.0.0",
"CIBW_TEST_COMMAND": (
"python -c 'from bitarray import bitarray; print(~bitarray(\"01100\"))'"
),
},
)
assert wheels == [f"spam-0.1.0-cp313-cp313-android_33_{native_arch.android_abi}.whl"]
assert "bitarray('10011')" in capfd.readouterr().out
@needs_emulator
def test_libcxx(tmp_path, capfd):
project_dir = tmp_path / "project"
output_dir = tmp_path / "output"
# A C++ package should include libc++, and the extension module should be able to
# find it using DT_RUNPATH.
new_c_project(setup_py_extension_args_add="language='c++'").generate(project_dir)
script = 'import spam; print(", ".join(f"{s}: {spam.filter(s)}" for s in ["ham", "spam"]))'
cp313_test_env = {**cp313_env, "CIBW_TEST_COMMAND": f"python -c '{script}'"}
# Including external libraries requires API level 24.
with pytest.raises(CalledProcessError):
cibuildwheel_run(project_dir, add_env=cp313_test_env, output_dir=output_dir)
assert "libc++_shared.so requires ANDROID_API_LEVEL to be at least 24" in capfd.readouterr().err
wheels = cibuildwheel_run(
project_dir,
add_env={**cp313_test_env, "ANDROID_API_LEVEL": "24"},
output_dir=output_dir,
)
assert len(wheels) == 1
names = ZipFile(output_dir / wheels[0]).namelist()
libcxx_names = [
name for name in names if re.fullmatch(r"spam\.libs/libc\+\+_shared-[0-9a-f]{8}\.so", name)
]
assert len(libcxx_names) == 1
assert "ham: 1, spam: 0" in capfd.readouterr().out
# A C package should not include libc++.
rmtree(project_dir)
rmtree(output_dir)
new_c_project().generate(project_dir)
wheels = cibuildwheel_run(project_dir, add_env=cp313_env, output_dir=output_dir)
assert len(wheels) == 1
for name in ZipFile(output_dir / wheels[0]).namelist():
assert ".libs" not in name
+3 -10
View File
@@ -34,21 +34,14 @@ def test(tmp_path, request):
"CIBW_TEST_REQUIRES": "pytest", "CIBW_TEST_REQUIRES": "pytest",
"CIBW_TEST_COMMAND": "pytest {project}/test", "CIBW_TEST_COMMAND": "pytest {project}/test",
"CIBW_ARCHS": archs, "CIBW_ARCHS": archs,
# TODO remove me once proper support is added
"CIBW_MANYLINUX_RISCV64_IMAGE": "ghcr.io/mayeut/manylinux_2_35:2025.05.11-1",
"CIBW_SKIP": "*-musllinux_riscv64",
}, },
) )
# also check that we got the right wheels # also check that we got the right wheels
expected_wheels = list( expected_wheels = itertools.chain.from_iterable(
itertools.chain.from_iterable( utils.expected_wheels("spam", "0.1.0", machine_arch=arch, single_arch=True)
utils.expected_wheels("spam", "0.1.0", machine_arch=arch, single_arch=True) for arch in archs.split(" ")
for arch in archs.split(" ")
)
) )
# TODO remove me once proper support is added
expected_wheels = [wheel for wheel in expected_wheels if "musllinux_1_2_riscv64" not in wheel]
assert set(actual_wheels) == set(expected_wheels) assert set(actual_wheels) == set(expected_wheels)
+7 -6
View File
@@ -12,6 +12,12 @@ from cibuildwheel.ci import CIProvider, detect_ci_provider
from . import test_projects, utils from . import test_projects, utils
pytestmark = pytest.mark.ios
CIBW_PLATFORM = os.environ.get("CIBW_PLATFORM", "ios")
if CIBW_PLATFORM != "ios":
pytest.skip(f"{CIBW_PLATFORM=}", allow_module_level=True)
basic_project_files = { basic_project_files = {
"tests/test_platform.py": f""" "tests/test_platform.py": f"""
import platform import platform
@@ -83,7 +89,6 @@ def test_ios_platforms(tmp_path, build_config, monkeypatch, capfd):
project_dir, project_dir,
add_env={ add_env={
"CIBW_BEFORE_BUILD": "does-exist", "CIBW_BEFORE_BUILD": "does-exist",
"CIBW_BUILD": "cp313-*",
"CIBW_XBUILD_TOOLS": "does-exist", "CIBW_XBUILD_TOOLS": "does-exist",
"CIBW_TEST_SOURCES": "tests", "CIBW_TEST_SOURCES": "tests",
"CIBW_TEST_COMMAND": "python -m this && python -m unittest discover tests test_platform.py", "CIBW_TEST_COMMAND": "python -m this && python -m unittest discover tests test_platform.py",
@@ -94,7 +99,7 @@ def test_ios_platforms(tmp_path, build_config, monkeypatch, capfd):
# The expected wheels were produced. # The expected wheels were produced.
expected_wheels = utils.expected_wheels( expected_wheels = utils.expected_wheels(
"spam", "0.1.0", platform="ios", python_abi_tags=["cp313-cp313"] "spam", "0.1.0", platform="ios", python_abi_tags=["cp313-cp313", "cp314-cp314"]
) )
assert set(actual_wheels) == set(expected_wheels) assert set(actual_wheels) == set(expected_wheels)
@@ -121,7 +126,6 @@ def test_no_test_sources(tmp_path, capfd):
project_dir, project_dir,
add_env={ add_env={
"CIBW_PLATFORM": "ios", "CIBW_PLATFORM": "ios",
"CIBW_BUILD": "cp313-*",
"CIBW_TEST_REQUIRES": "pytest", "CIBW_TEST_REQUIRES": "pytest",
"CIBW_TEST_COMMAND": "python -m pytest", "CIBW_TEST_COMMAND": "python -m pytest",
"CIBW_XBUILD_TOOLS": "", "CIBW_XBUILD_TOOLS": "",
@@ -153,7 +157,6 @@ def test_ios_testing_with_placeholder(tmp_path, capfd):
project_dir, project_dir,
add_env={ add_env={
"CIBW_PLATFORM": "ios", "CIBW_PLATFORM": "ios",
"CIBW_BUILD": "cp313-*",
"CIBW_TEST_REQUIRES": "pytest", "CIBW_TEST_REQUIRES": "pytest",
"CIBW_TEST_COMMAND": "pytest {project}/tests", "CIBW_TEST_COMMAND": "pytest {project}/tests",
"CIBW_XBUILD_TOOLS": "", "CIBW_XBUILD_TOOLS": "",
@@ -180,7 +183,6 @@ def test_ios_test_command_short_circuit(tmp_path, capfd):
project_dir, project_dir,
add_env={ add_env={
"CIBW_PLATFORM": "ios", "CIBW_PLATFORM": "ios",
"CIBW_BUILD": "cp313-*",
"CIBW_XBUILD_TOOLS": "", "CIBW_XBUILD_TOOLS": "",
"CIBW_TEST_SOURCES": "tests", "CIBW_TEST_SOURCES": "tests",
"CIBW_TEST_COMMAND": "python -m not_a_module && python -m this", "CIBW_TEST_COMMAND": "python -m not_a_module && python -m this",
@@ -209,7 +211,6 @@ def test_missing_xbuild_tool(tmp_path, capfd):
project_dir, project_dir,
add_env={ add_env={
"CIBW_PLATFORM": "ios", "CIBW_PLATFORM": "ios",
"CIBW_BUILD": "cp313-*",
"CIBW_TEST_COMMAND": "python -m tests", "CIBW_TEST_COMMAND": "python -m tests",
"CIBW_XBUILD_TOOLS": "does-not-exist", "CIBW_XBUILD_TOOLS": "does-not-exist",
}, },
-9
View File
@@ -64,8 +64,6 @@ project_with_manylinux_symbols = test_projects.new_c_project(
def test(manylinux_image, tmp_path): def test(manylinux_image, tmp_path):
if utils.get_platform() != "linux": if utils.get_platform() != "linux":
pytest.skip("the container image test is only relevant to the linux build") pytest.skip("the container image test is only relevant to the linux build")
elif manylinux_image in {"manylinux_2_28", "manylinux_2_34"} and platform.machine() == "i686":
pytest.skip(f"{manylinux_image} doesn't exist for i686 architecture")
project_dir = tmp_path / "project" project_dir = tmp_path / "project"
project_with_manylinux_symbols.generate(project_dir) project_with_manylinux_symbols.generate(project_dir)
@@ -85,9 +83,6 @@ def test(manylinux_image, tmp_path):
"CIBW_MANYLINUX_PYPY_AARCH64_IMAGE": manylinux_image, "CIBW_MANYLINUX_PYPY_AARCH64_IMAGE": manylinux_image,
"CIBW_MANYLINUX_PYPY_I686_IMAGE": manylinux_image, "CIBW_MANYLINUX_PYPY_I686_IMAGE": manylinux_image,
} }
if manylinux_image in {"manylinux_2_28", "manylinux_2_34"} and platform.machine() == "x86_64":
# We don't have a manylinux_2_28+ image for i686
add_env["CIBW_ARCHS"] = "x86_64"
if platform.machine() == "aarch64": if platform.machine() == "aarch64":
# We just have a manylinux_2_31 image for armv7l # We just have a manylinux_2_31 image for armv7l
add_env["CIBW_ARCHS"] = "aarch64" add_env["CIBW_ARCHS"] = "aarch64"
@@ -104,10 +99,6 @@ def test(manylinux_image, tmp_path):
musllinux_versions=[], musllinux_versions=[],
) )
if manylinux_image in {"manylinux_2_28", "manylinux_2_34"} and platform.machine() == "x86_64":
# We don't have a manylinux_2_28+ image for i686
expected_wheels = [w for w in expected_wheels if "i686" not in w]
if platform.machine() == "aarch64": if platform.machine() == "aarch64":
# We just have a manylinux_2_31 image for armv7l # We just have a manylinux_2_31 image for armv7l
expected_wheels = [w for w in expected_wheels if "armv7l" not in w] expected_wheels = [w for w in expected_wheels if "armv7l" not in w]
+1 -1
View File
@@ -17,7 +17,7 @@ spam_filter(PyObject *self, PyObject *args)
return NULL; return NULL;
// Spam should not be allowed through the filter. // Spam should not be allowed through the filter.
sts = strcmp(content, "spam"); sts = strcmp(content, "spam") != 0;
{{ spam_c_function_add | indent(4) }} {{ spam_c_function_add | indent(4) }}
+7
View File
@@ -1,4 +1,5 @@
import contextlib import contextlib
import os
import subprocess import subprocess
import sys import sys
import textwrap import textwrap
@@ -9,6 +10,12 @@ from cibuildwheel.util.file import CIBW_CACHE_PATH
from . import test_projects, utils from . import test_projects, utils
pytestmark = pytest.mark.pyodide
CIBW_PLATFORM = os.environ.get("CIBW_PLATFORM", "pyodide")
if CIBW_PLATFORM != "pyodide":
pytest.skip(f"{CIBW_PLATFORM=}", allow_module_level=True)
basic_project = test_projects.new_c_project() basic_project = test_projects.new_c_project()
basic_project.files["check_node.py"] = r""" basic_project.files["check_node.py"] = r"""
import sys import sys
+4
View File
@@ -1,3 +1,4 @@
import socket
import textwrap import textwrap
from . import test_projects, utils from . import test_projects, utils
@@ -24,6 +25,9 @@ def test(tmp_path):
project_dir = tmp_path / "project" project_dir = tmp_path / "project"
project_with_ssl_tests.generate(project_dir) project_with_ssl_tests.generate(project_dir)
# warm up connection
socket.getaddrinfo("tls-v1-2.badssl.com", 443)
actual_wheels = utils.cibuildwheel_run(project_dir) actual_wheels = utils.cibuildwheel_run(project_dir)
expected_wheels = utils.expected_wheels("spam", "0.1.0") expected_wheels = utils.expected_wheels("spam", "0.1.0")
+1 -1
View File
@@ -189,7 +189,7 @@ def test_bare_pytest_invocation(
tmp_path: Path, capfd: pytest.CaptureFixture[str], test_runner: str tmp_path: Path, capfd: pytest.CaptureFixture[str], test_runner: str
) -> None: ) -> None:
""" """
Check that if a user runs pytest in the the test cwd without setting Check that if a user runs pytest in the test cwd without setting
test-sources, it raises a helpful error test-sources, it raises a helpful error
""" """
project_dir = tmp_path / "project" project_dir = tmp_path / "project"
+18 -12
View File
@@ -51,7 +51,7 @@ def get_platform() -> str:
raise Exception(msg) raise Exception(msg)
DEFAULT_CIBW_ENABLE = "cpython-freethreading cpython-prerelease cpython-experimental-riscv64" DEFAULT_CIBW_ENABLE = "cpython-freethreading cpython-prerelease"
def get_enable_groups() -> frozenset[EnableGroup]: def get_enable_groups() -> frozenset[EnableGroup]:
@@ -151,7 +151,7 @@ def cibuildwheel_run(
cwd=project_path, cwd=project_path,
check=True, check=True,
) )
wheels = [p.name for p in (output_dir or Path(tmp_output_dir)).iterdir()] wheels = sorted(p.name for p in (output_dir or Path(tmp_output_dir)).iterdir())
return wheels return wheels
@@ -175,6 +175,7 @@ def expected_wheels(
include_universal2: bool = False, include_universal2: bool = False,
single_python: bool = False, single_python: bool = False,
single_arch: bool = False, single_arch: bool = False,
full_auto: bool = False,
) -> list[str]: ) -> list[str]:
""" """
Returns the expected wheels from a run of cibuildwheel. Returns the expected wheels from a run of cibuildwheel.
@@ -194,7 +195,7 @@ def expected_wheels(
architectures = [machine_arch] architectures = [machine_arch]
if not single_arch: if not single_arch:
if platform == "linux": if platform == "linux" and full_auto:
if machine_arch == "x86_64": if machine_arch == "x86_64":
architectures.append("i686") architectures.append("i686")
elif ( elif (
@@ -251,19 +252,17 @@ def _expected_wheels(
if manylinux_versions is None: if manylinux_versions is None:
manylinux_versions = { manylinux_versions = {
"armv7l": ["manylinux2014", "manylinux_2_17", "manylinux_2_31"], "armv7l": ["manylinux2014", "manylinux_2_17", "manylinux_2_31"],
"i686": ["manylinux1", "manylinux2014", "manylinux_2_17", "manylinux_2_5"], "i686": ["manylinux1", "manylinux_2_28", "manylinux_2_5"],
"x86_64": ["manylinux1", "manylinux_2_28", "manylinux_2_5"], "x86_64": ["manylinux1", "manylinux_2_28", "manylinux_2_5"],
"riscv64": ["manylinux_2_31", "manylinux_2_35"], "riscv64": ["manylinux_2_31", "manylinux_2_39"],
}.get(machine_arch, ["manylinux2014", "manylinux_2_17", "manylinux_2_28"]) }.get(machine_arch, ["manylinux2014", "manylinux_2_17", "manylinux_2_28"])
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: if platform == "pyodide" and python_abi_tags is None:
python_abi_tags = ["cp312-cp312"] python_abi_tags = ["cp312-cp312", "cp313-cp313"]
if EnableGroup.PyodidePrerelease in enable_groups: elif platform in {"android", "ios"} and python_abi_tags is None:
python_abi_tags.append("cp313-cp313")
elif platform == "ios" and python_abi_tags is None:
python_abi_tags = ["cp313-cp313"] python_abi_tags = ["cp313-cp313"]
elif python_abi_tags is None: elif python_abi_tags is None:
python_abi_tags = [ python_abi_tags = [
@@ -273,6 +272,8 @@ def _expected_wheels(
"cp311-cp311", "cp311-cp311",
"cp312-cp312", "cp312-cp312",
"cp313-cp313", "cp313-cp313",
"cp314-cp314",
"cp314-cp314t",
] ]
enable_groups = get_enable_groups() enable_groups = get_enable_groups()
@@ -280,9 +281,7 @@ def _expected_wheels(
python_abi_tags.append("cp313-cp313t") python_abi_tags.append("cp313-cp313t")
if EnableGroup.CPythonPrerelease in enable_groups: if EnableGroup.CPythonPrerelease in enable_groups:
python_abi_tags.append("cp314-cp314") ... # Add cp315 here when available
if EnableGroup.CPythonFreeThreading in enable_groups:
python_abi_tags.append("cp314-cp314t")
if EnableGroup.PyPyEoL in enable_groups: if EnableGroup.PyPyEoL in enable_groups:
python_abi_tags += [ python_abi_tags += [
@@ -369,6 +368,13 @@ def _expected_wheels(
if include_universal2: if include_universal2:
platform_tags.append(f"macosx_{min_macosx.replace('.', '_')}_universal2") platform_tags.append(f"macosx_{min_macosx.replace('.', '_')}_universal2")
elif platform == "android":
api_level = {
"cp313-cp313": 21,
"cp314-cp314": 24,
}[python_abi_tag]
platform_tags = [f"android_{api_level}_{machine_arch}"]
elif platform == "ios": elif platform == "ios":
if machine_arch == "x86_64": if machine_arch == "x86_64":
platform_tags = [ platform_tags = [
+40 -4
View File
@@ -5,7 +5,7 @@ import sys
import pytest import pytest
import cibuildwheel.architecture import cibuildwheel.architecture
from cibuildwheel.architecture import Architecture from cibuildwheel.architecture import Architecture, arch_synonym
@pytest.fixture( @pytest.fixture(
@@ -34,8 +34,8 @@ def test_arch_auto(platform_machine):
arch_set = Architecture.auto_archs("linux") arch_set = Architecture.auto_archs("linux")
expected = { expected = {
"32": {Architecture.i686}, "32": {Architecture.i686},
"64": {Architecture.x86_64, Architecture.i686}, "64": {Architecture.x86_64},
"arm": {Architecture.aarch64, Architecture.armv7l}, "arm": {Architecture.aarch64},
} }
assert arch_set == expected[machine_name] assert arch_set == expected[machine_name]
@@ -94,5 +94,41 @@ def test_arch_auto_no_aarch32(monkeypatch):
arch_set = Architecture.parse_config("auto64", "linux") arch_set = Architecture.parse_config("auto64", "linux")
assert arch_set == {Architecture.aarch64} assert arch_set == {Architecture.aarch64}
monkeypatch.setattr(cibuildwheel.architecture, "_check_aarch32_el0", lambda: True)
arch_set = Architecture.parse_config("auto32", "linux") arch_set = Architecture.parse_config("auto32", "linux")
assert len(arch_set) == 0 assert arch_set == {Architecture.armv7l}
monkeypatch.setattr(cibuildwheel.architecture, "_check_aarch32_el0", lambda: False)
arch_set = Architecture.parse_config("auto32", "linux")
assert arch_set == set()
def test_arch_native_on_ios(monkeypatch):
monkeypatch.setattr(sys, "platform", "darwin")
monkeypatch.setattr(platform_module, "machine", lambda: "arm64")
arch_set = Architecture.parse_config("native", platform="ios")
assert arch_set == {Architecture.arm64_iphonesimulator}
def test_arch_auto_on_ios(monkeypatch):
monkeypatch.setattr(sys, "platform", "darwin")
monkeypatch.setattr(platform_module, "machine", lambda: "arm64")
arch_set = Architecture.parse_config("auto", platform="ios")
assert arch_set == {Architecture.arm64_iphonesimulator, Architecture.arm64_iphoneos}
@pytest.mark.parametrize(
("arch", "from_platform", "to_platform", "expected"),
[
("x86_64", "linux", "macos", "x86_64"),
("x86_64", "macos", "linux", "x86_64"),
("x86_64", "linux", "windows", "AMD64"),
("AMD64", "windows", "linux", "x86_64"),
("x86_64", "linux", "nonexistent", "x86_64"),
("x86_64", "nonexistent", "linux", "x86_64"),
("nonexistent", "linux", "windows", "nonexistent"),
("x86", "windows", "macos", None),
],
)
def test_arch_synonym(arch, from_platform, to_platform, expected):
assert arch_synonym(arch, from_platform, to_platform) == expected
+6 -14
View File
@@ -1,5 +1,6 @@
from packaging.specifiers import SpecifierSet from packaging.specifiers import SpecifierSet
import cibuildwheel.selector
from cibuildwheel.selector import BuildSelector, EnableGroup from cibuildwheel.selector import BuildSelector, EnableGroup
@@ -14,6 +15,7 @@ def test_build():
assert build_selector("cp311-manylinux_x86_64") assert build_selector("cp311-manylinux_x86_64")
assert build_selector("cp312-manylinux_x86_64") assert build_selector("cp312-manylinux_x86_64")
assert build_selector("cp313-manylinux_x86_64") assert build_selector("cp313-manylinux_x86_64")
assert build_selector("cp314-manylinux_x86_64")
assert build_selector("pp310-manylinux_x86_64") assert build_selector("pp310-manylinux_x86_64")
assert build_selector("pp311-manylinux_x86_64") assert build_selector("pp311-manylinux_x86_64")
assert build_selector("cp36-manylinux_i686") assert build_selector("cp36-manylinux_i686")
@@ -35,6 +37,7 @@ def test_build():
assert build_selector("cp311-win_amd64") assert build_selector("cp311-win_amd64")
assert build_selector("cp312-win_amd64") assert build_selector("cp312-win_amd64")
assert build_selector("cp313-win_amd64") assert build_selector("cp313-win_amd64")
assert build_selector("cp314-win_amd64")
assert not build_selector("pp310-win_amd64") assert not build_selector("pp310-win_amd64")
assert not build_selector("pp311-win_amd64") assert not build_selector("pp311-win_amd64")
@@ -106,7 +109,7 @@ def test_build_filter_pyodide():
enable=frozenset(), enable=frozenset(),
) )
assert build_selector("cp312-pyodide_wasm32") assert build_selector("cp312-pyodide_wasm32")
assert not build_selector("cp313-pyodide_wasm32") assert build_selector("cp313-pyodide_wasm32")
def test_skip(): def test_skip():
@@ -214,20 +217,9 @@ def test_build_free_threaded_python():
assert build_selector("cp313t-manylinux_x86_64") assert build_selector("cp313t-manylinux_x86_64")
def test_build_riscv64_enable():
build_selector = BuildSelector(build_config="*", skip_config="")
assert not build_selector("cp313-manylinux_riscv64")
build_selector = BuildSelector(
build_config="*", skip_config="", enable=frozenset([EnableGroup.CPythonExperimentalRiscV64])
)
assert build_selector("cp313-manylinux_riscv64")
def test_testing_selector(): def test_testing_selector():
# local import to avoid pytest trying to collect this as a test class! # This is not a global import to keep pytest from collecting it as a test
from cibuildwheel.selector import TestSelector test_selector = cibuildwheel.selector.TestSelector(skip_config="cp36-*")
test_selector = TestSelector(skip_config="cp36-*")
assert not test_selector("cp36-win_amd64") assert not test_selector("cp36-win_amd64")
assert test_selector("cp37-manylinux_x86_64") assert test_selector("cp37-manylinux_x86_64")
+4 -2
View File
@@ -90,6 +90,8 @@ def test_linux_container_split(tmp_path: Path, monkeypatch: pytest.MonkeyPatch)
"cp311-manylinux_x86_64", "cp311-manylinux_x86_64",
"cp312-manylinux_x86_64", "cp312-manylinux_x86_64",
"cp313-manylinux_x86_64", "cp313-manylinux_x86_64",
"cp314-manylinux_x86_64",
"cp314t-manylinux_x86_64",
] ]
assert before_alls(build_steps[3]) == [""] * 3 assert before_alls(build_steps[3]) == [""] * 5
assert container_engines(build_steps[3]) == [default_container_engine] * 3 assert container_engines(build_steps[3]) == [default_container_engine] * 5
+3 -2
View File
@@ -6,7 +6,8 @@ from pathlib import Path
import pytest import pytest
from cibuildwheel import __main__, architecture from cibuildwheel import architecture
from cibuildwheel.logger import Logger
from cibuildwheel.platforms import linux, macos, pyodide, windows from cibuildwheel.platforms import linux, macos, pyodide, windows
from cibuildwheel.util import file from cibuildwheel.util import file
@@ -58,7 +59,7 @@ def disable_print_wheels(monkeypatch):
def empty_cm(*args, **kwargs): def empty_cm(*args, **kwargs):
yield yield
monkeypatch.setattr(__main__, "print_new_wheels", empty_cm) monkeypatch.setattr(Logger, "print_summary", empty_cm)
@pytest.fixture @pytest.fixture
+119 -4
View File
@@ -1,3 +1,4 @@
import os
import sys import sys
import tomllib import tomllib
from fnmatch import fnmatch from fnmatch import fnmatch
@@ -7,7 +8,7 @@ import pytest
from cibuildwheel.__main__ import main from cibuildwheel.__main__ import main
from cibuildwheel.environment import ParsedEnvironment from cibuildwheel.environment import ParsedEnvironment
from cibuildwheel.frontend import _split_config_settings from cibuildwheel.frontend import _split_config_settings, parse_config_settings
from cibuildwheel.options import BuildOptions, _get_pinned_container_images from cibuildwheel.options import BuildOptions, _get_pinned_container_images
from cibuildwheel.selector import BuildSelector, EnableGroup from cibuildwheel.selector import BuildSelector, EnableGroup
from cibuildwheel.util import resources from cibuildwheel.util import resources
@@ -16,6 +17,18 @@ from cibuildwheel.util.packaging import DependencyConstraints
# CIBW_PLATFORM is tested in main_platform_test.py # CIBW_PLATFORM is tested in main_platform_test.py
def test_old_free_threaded(monkeypatch, capsys):
monkeypatch.setenv("CIBW_FREE_THREADED_SUPPORT", "ON")
with pytest.raises(SystemExit):
main()
assert (
"CIBW_FREE_THREADED_SUPPORT environment variable is no longer supported."
in capsys.readouterr().err
)
@pytest.mark.usefixtures("platform") @pytest.mark.usefixtures("platform")
def test_output_dir(intercepted_build_args, monkeypatch): def test_output_dir(intercepted_build_args, monkeypatch):
OUTPUT_DIR = Path("some_output_dir") OUTPUT_DIR = Path("some_output_dir")
@@ -93,6 +106,32 @@ def test_invalid_skip_selector(monkeypatch, capsys, option_name, option_env_var)
assert f"Invalid {option_name} selector" in err assert f"Invalid {option_name} selector" in err
@pytest.mark.parametrize(
"selector", ["*-macosx_universal2:arm64", "*-macosx_universal2:x86_64", "*-macosx_arm64"]
)
@pytest.mark.usefixtures("platform", "intercepted_build_args")
def test_valid_test_skip_selector(monkeypatch, capsys, selector):
monkeypatch.setenv("CIBW_TEST_SKIP", selector)
main()
_, err = capsys.readouterr()
print(err)
assert "Invalid test_skip selector" not in err
@pytest.mark.parametrize("selector", ["*-macosx_universal2:invalid", "*-macosx_arm64:arm64"])
@pytest.mark.usefixtures("platform", "intercepted_build_args")
def test_invalid_test_skip_selector(monkeypatch, capsys, selector):
monkeypatch.setenv("CIBW_TEST_SKIP", selector)
main()
_, err = capsys.readouterr()
print(err)
assert "Invalid test_skip selector" in err
@pytest.mark.usefixtures("platform", "intercepted_build_args") @pytest.mark.usefixtures("platform", "intercepted_build_args")
def test_empty_selector(monkeypatch): def test_empty_selector(monkeypatch):
monkeypatch.setenv("CIBW_SKIP", "*") monkeypatch.setenv("CIBW_SKIP", "*")
@@ -103,6 +142,70 @@ def test_empty_selector(monkeypatch):
assert e.value.code == 3 assert e.value.code == 3
@pytest.mark.usefixtures("platform", "intercepted_build_args")
def test_riscv64_warning1(monkeypatch, capsys):
monkeypatch.setenv("CIBW_ENABLE", "cpython-experimental-riscv64")
main()
_, err = capsys.readouterr()
print(err)
assert "'cpython-experimental-riscv64' enable is deprecated" in err
@pytest.mark.usefixtures("platform", "intercepted_build_args")
def test_riscv64_warning2(monkeypatch, capsys, tmp_path):
local_path = tmp_path / "tmp_project"
os.mkdir(local_path) # noqa:PTH102 Path.mkdir has been monkeypatched already
local_path.joinpath("setup.py").touch()
monkeypatch.setattr(
sys, "argv", ["cibuildwheel", "--only", "cp313-manylinux_riscv64", str(local_path)]
)
monkeypatch.setenv("CIBW_ENABLE", "cpython-experimental-riscv64")
main()
_, err = capsys.readouterr()
print(err)
assert "'cpython-experimental-riscv64' enable is deprecated" in err
@pytest.mark.usefixtures("platform", "intercepted_build_args")
def test_riscv64_no_warning(monkeypatch, capsys, tmp_path):
local_path = tmp_path / "tmp_project"
os.mkdir(local_path) # noqa:PTH102 Path.mkdir has been monkeypatched already
local_path.joinpath("setup.py").touch()
monkeypatch.setattr(
sys, "argv", ["cibuildwheel", "--only", "cp313-manylinux_riscv64", str(local_path)]
)
main()
_, err = capsys.readouterr()
print(err)
assert "'cpython-experimental-riscv64' enable is deprecated" not in err
@pytest.mark.usefixtures("platform", "intercepted_build_args")
def test_riscv64_no_warning2(monkeypatch, capsys, tmp_path):
local_path = tmp_path / "tmp_project"
os.mkdir(local_path) # noqa:PTH102 Path.mkdir has been monkeypatched already
local_path.joinpath("setup.py").touch()
monkeypatch.setattr(
sys, "argv", ["cibuildwheel", "--only", "cp313-manylinux_riscv64", str(local_path)]
)
monkeypatch.setenv("CIBW_ENABLE", "all")
main()
_, err = capsys.readouterr()
print(err)
assert "'cpython-experimental-riscv64' enable is deprecated" not in err
@pytest.mark.parametrize( @pytest.mark.parametrize(
("architecture", "image", "full_image"), ("architecture", "image", "full_image"),
[ [
@@ -111,8 +214,9 @@ def test_empty_selector(monkeypatch):
("x86_64", "manylinux_2_28", "quay.io/pypa/manylinux_2_28_x86_64:*"), ("x86_64", "manylinux_2_28", "quay.io/pypa/manylinux_2_28_x86_64:*"),
("x86_64", "manylinux_2_34", "quay.io/pypa/manylinux_2_34_x86_64:*"), ("x86_64", "manylinux_2_34", "quay.io/pypa/manylinux_2_34_x86_64:*"),
("x86_64", "custom_image", "custom_image"), ("x86_64", "custom_image", "custom_image"),
("i686", None, "quay.io/pypa/manylinux2014_i686:*"), ("i686", None, "quay.io/pypa/manylinux_2_28_i686:*"),
("i686", "manylinux2014", "quay.io/pypa/manylinux2014_i686:*"), ("i686", "manylinux2014", "quay.io/pypa/manylinux2014_i686:*"),
("i686", "manylinux_2_28", "quay.io/pypa/manylinux_2_28_i686:*"),
("i686", "custom_image", "custom_image"), ("i686", "custom_image", "custom_image"),
("pypy_x86_64", None, "quay.io/pypa/manylinux_2_28_x86_64:*"), ("pypy_x86_64", None, "quay.io/pypa/manylinux_2_28_x86_64:*"),
("pypy_x86_64", "manylinux2014", "quay.io/pypa/manylinux2014_x86_64:*"), ("pypy_x86_64", "manylinux2014", "quay.io/pypa/manylinux2014_x86_64:*"),
@@ -288,7 +392,9 @@ def test_build_verbosity(
@pytest.mark.parametrize("platform_specific", [False, True]) @pytest.mark.parametrize("platform_specific", [False, True])
def test_config_settings(platform_specific, platform, intercepted_build_args, monkeypatch): def test_config_settings(platform_specific, platform, intercepted_build_args, monkeypatch):
config_settings = 'setting=value setting=value2 other="something else"' config_settings = (
'setting=value setting=value2 triplet=1 triplet=2 triplet=3 other="something else"'
)
if platform_specific: if platform_specific:
monkeypatch.setenv("CIBW_CONFIG_SETTINGS_" + platform.upper(), config_settings) monkeypatch.setenv("CIBW_CONFIG_SETTINGS_" + platform.upper(), config_settings)
monkeypatch.setenv("CIBW_CONFIG_SETTINGS", "a=b") monkeypatch.setenv("CIBW_CONFIG_SETTINGS", "a=b")
@@ -303,8 +409,16 @@ def test_config_settings(platform_specific, platform, intercepted_build_args, mo
assert _split_config_settings(config_settings) == [ assert _split_config_settings(config_settings) == [
"-Csetting=value", "-Csetting=value",
"-Csetting=value2", "-Csetting=value2",
"-Ctriplet=1",
"-Ctriplet=2",
"-Ctriplet=3",
"-Cother=something else", "-Cother=something else",
] ]
assert parse_config_settings(config_settings) == {
"setting": ["value", "value2"],
"triplet": ["1", "2", "3"],
"other": "something else",
}
@pytest.mark.parametrize( @pytest.mark.parametrize(
@@ -493,7 +607,8 @@ def test_defaults(platform, intercepted_build_args):
if isinstance(repair_wheel_default, list): if isinstance(repair_wheel_default, list):
repair_wheel_default = " && ".join(repair_wheel_default) repair_wheel_default = " && ".join(repair_wheel_default)
assert build_options.repair_command == repair_wheel_default assert build_options.repair_command == repair_wheel_default
assert build_options.build_frontend is None assert build_options.build_frontend.name == "build"
assert build_options.build_frontend.args == ()
if platform == "linux": if platform == "linux":
assert build_options.manylinux_images assert build_options.manylinux_images

Some files were not shown because too many files have changed in this diff Show More