Compare commits

..
101 Commits
Author SHA1 Message Date
Joe Rickerby 5514dd28f0 Bump version: v1.9.0 2021-02-05 21:44:41 +00:00
Joe Rickerby bdf765c212 Update syntax in apple silicon example 2021-02-05 21:33:57 +00:00
Joe Rickerby 31838d3c74 Update bump version script 2021-02-05 21:25:40 +00:00
Joe Rickerby c5912134d5 Some release notes 2021-02-05 21:18:21 +00:00
Joe Rickerby 3fa7e596af Merge pull request #577 from joerick/docs/u2
docs: Universal2 discussion and more updates
2021-02-05 21:12:29 +00:00
Joe Rickerby efc97b80ac Update docs/faq.md 2021-02-05 20:25:03 +00:00
Joe Rickerby 3940d38b4d Fix GHA example syntax 2021-02-05 20:17:53 +00:00
Joe Rickerby c7ad0c7654 Review tweaks from henryiii 2021-02-05 19:43:19 +00:00
Joe Rickerby 566754fdba README: add Apple Silicon and emulation references + a few minor tweaks 2021-02-05 19:20:11 +00:00
Joe Rickerby b6eca6ca48 Use the composite-steps action in more examples 2021-02-05 19:19:19 +00:00
Joe Rickerby adc6af9e7c Combine the two Apple Silicon FAQ entries into one and make a few tweaks 2021-02-05 19:18:48 +00:00
Henry Schreiner 160d1db6be docs: add GHA badge, normalize CircleCI badge 2021-02-05 08:48:35 -05:00
Henry Schreiner 3163743cba docs: some README touchup 2021-02-04 22:35:42 -05:00
Henry Schreiner 8fe6520ef4 examples: move github-minimal to action 2021-02-04 22:27:18 -05:00
Henry Schreiner 0c30df5c96 docs: update msg about running on a development machine 2021-02-04 22:22:35 -05:00
Henry Schreiner ca9d8d5d2c examples: bump versions in a few places 2021-02-04 22:22:35 -05:00
Henry Schreiner d871a15e3c examples: update GitHub examples to be a bit cleaner 2021-02-04 22:22:22 -05:00
Henry Schreiner 49738c0cee docs: Github -> GitHub, and ensure it doesn't happen again 2021-02-04 22:13:56 -05:00
Henry Schreiner 837ea5f59c docs: tips and tricks for macOS AS 2021-02-04 22:13:56 -05:00
Henry Schreiner 99acc0abac docs: cleanup (#567)
* fix: support docs/tests extras, simpler RtD install

Use oldest-supported-numpy from SciPy in example

* chore: bump to ubuntu-20.04 images

* fix: move extras to setup so we can do combinations
2021-02-04 22:02:53 -05:00
Henry Schreiner e12ce2d647 feat: print new files after building (#570) 2021-02-04 14:27:54 -05:00
github-actions[bot]andhenryiii 08401d6608 Update dependencies (#572)
Co-authored-by: henryiii <henryiii@users.noreply.github.com>
2021-02-02 21:44:16 -05:00
Henry Schreiner f953acc65a fix: updates should include universal2 Pythons (#573)
* fix: updates should include universal2 Pythons

* refactor: rename to macos_cpython
2021-02-02 16:30:50 -05:00
Joe Rickerby cdfab24c5d Merge pull request #574 from joerick/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2021-02-01 21:53:16 +00:00
Joe Rickerby fe002efba9 Merge pull request #571 from henryiii/docs/install_note
docs: note on installing for the TEST_COMMAND
2021-02-01 21:48:18 +00:00
Yannick Jadoul 9119bf048c Fix flake8 complaints about unused Optional 2021-02-01 17:55:28 +01:00
pre-commit-ci[bot] 08f72f276d [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2021-02-01 16:37:49 +00:00
pre-commit-ci[bot] 2cb3e21191 [pre-commit.ci] pre-commit autoupdate 2021-02-01 16:37:03 +00:00
Henry Fredrick Schreiner 0daee3a408 docs: note on installing
Closes #219.
2021-01-31 23:29:35 -05:00
Henry Schreiner 70d9e1d969 feat: auto32/auto64 (#553)
* feat: auto32/auto64

* refactor: cleanup from @joerick

* refactor: spelling fix
2021-01-31 20:41:18 -05:00
Henry Schreiner eb700a164c feat: requires-python (#536)
* feat: requires-python

refactor: updating to current proposal

fix: add tests and fix a few issues

docs: Update mostly from @joerick

docs: update README from readthedocs

* refactor: pull out config file reading

* fix: always assume highest Python patch version

* refactor: try new design

* refactor: function and bump MyPy to 0.800

* fix: tighten AST to call only, add docs

* fix: address review points from @joerick
2021-01-31 17:14:15 -05:00
Joe Rickerby b95dca50aa Merge pull request #484 from joerick/macos-universal2
Universal2 wheels on macOS
2021-01-31 12:16:24 +00:00
Joe Rickerby 2def20d75e Remove unused function 2021-01-30 18:12:09 +00:00
Joe Rickerby a79f0ef95e Remove wheel rename hack 2021-01-30 17:22:26 +00:00
Joe Rickerby be55ebe178 Merge remote-tracking branch 'origin/master' into macos-universal2 2021-01-30 17:17:57 +00:00
Joe Rickerby 0de4608193 Merge pull request #559 from joerick/update-dependencies-pr
[Bot] Update dependencies
2021-01-30 17:17:34 +00:00
Joe Rickerby 74170bab4a Get SDK version dynamically 2021-01-30 17:15:44 +00:00
joerick a09b9aeb76 Update dependencies 2021-01-30 13:53:22 +00:00
Joe Rickerby 56653f3b4c Fix typing import 2021-01-30 12:42:42 +00:00
Joe Rickerby 32ab09b2ed Tidy up and correct a few things 2021-01-30 10:52:00 +00:00
Joe Rickerby 4ff52ae8d0 Add ARCHFLAGS workaround for building universal2 on macOS 10.15 2021-01-29 15:22:32 +00:00
Joe Rickerby c8fde72aff Select the correct macos 11 SDK for the version of xcode available 2021-01-26 22:17:34 +00:00
Joe Rickerby 93ba19d00f Make the sample build do some cross compile for manual testing 2021-01-26 21:53:57 +00:00
Joe Rickerby b6f325c21a Implement CIBW_TEST_SKIP for individual archs of a universal2 wheel 2021-01-26 21:52:02 +00:00
Joe Rickerby d21b7cfc2e Add SDKROOT setting for building arm64, universal2 on 10.15 2021-01-24 19:18:23 +00:00
Joe Rickerby e40d53db19 Add manual debugging hook 2021-01-24 18:48:59 +00:00
Joe Rickerby f1638e7c6f The macos-10.15 image contains Xcode 12, which should be sufficient... let's find out 2021-01-24 17:50:59 +00:00
Joe Rickerby 8c68e4882c Add FAQ entry explaining Apple Silicon support 2021-01-24 17:03:46 +00:00
Joe Rickerby ef1baac8e2 Indent subheads in TOC on all pages 2021-01-24 16:15:33 +00:00
Joe Rickerby 4b491ceb4a Docs edits 2021-01-24 16:07:59 +00:00
Joe Rickerby 381ea7dbad Add docs for CIBW_ARCHS 2021-01-24 15:54:56 +00:00
Joe Rickerby 4dddd9e648 Silence the pip version warnings 2021-01-24 15:08:46 +00:00
Joe Rickerby 00eed5aac5 Fix crashing test 2021-01-24 14:50:47 +00:00
Joe Rickerby bd45b1c901 Remove pinned pip install, virtualenv should bundle this now 2021-01-24 14:46:46 +00:00
Joe Rickerby 3f8fcb01ad Fix unit tests 2021-01-23 18:15:29 +00:00
Joe Rickerby d2312cdb3f Merge remote-tracking branch 'origin/master' into macos-universal2
# Conflicts:
#	bin/sample_build.py
#	cibuildwheel/__main__.py
#	cibuildwheel/macos.py
#	cibuildwheel/util.py
#	cibuildwheel/windows.py
2021-01-23 13:28:06 +00:00
github-actions[bot]andhenryiii 9cffb96935 chore: update dependencies (#554)
Co-authored-by: henryiii <henryiii@users.noreply.github.com>
2021-01-22 20:26:59 -05:00
Joe Rickerby e83b770929 Add back other platforms to GHA CI 2021-01-09 13:41:37 +00:00
Joe Rickerby af8ab0af2b Update test to look in stderr for warnings 2021-01-09 01:20:49 +00:00
Joe Rickerby dbb6d80a72 Route warnings and error messages to stderr, not stdout. 2021-01-08 17:25:07 +00:00
Joe Rickerby a99726f477 Neaten comments 2021-01-08 15:18:37 +00:00
Joe Rickerby 9113dfe5da Merge remote-tracking branch 'origin/master' into macos-universal2 2021-01-08 15:02:20 +00:00
Joe Rickerby 8512e69733 Improve expected_wheels correctness 2021-01-08 14:59:16 +00:00
Joe Rickerby f43b3f50c5 Fix warning lines counting as build identifiers in test 2021-01-08 14:57:15 +00:00
Joe Rickerby c4b5b167ca Add clarifying comments 2021-01-08 14:29:07 +00:00
Joe Rickerby 1606b79749 Fix macOS Big Sur version checks 2021-01-08 14:28:39 +00:00
Joe Rickerby 07681239e4 Fix allowed archs check 2021-01-06 21:42:34 +00:00
Joe Rickerby 9bf5fb10e2 Merge remote-tracking branch 'origin/master' into macos-universal2
# Conflicts:
#	cibuildwheel/macos.py
#	cibuildwheel/util.py
2021-01-06 21:39:32 +00:00
Joe Rickerby e880aca155 Tidy up 2021-01-06 21:33:41 +00:00
Joe Rickerby e29fab0e2b Add test for cross-compile testing behaviour 2021-01-05 19:26:12 +00:00
Joe Rickerby ffba89dc64 Improve output formatting
Github annotations require all output to be on a single line, so let's
make the log messages do that
2021-01-05 19:02:15 +00:00
Joe Rickerby 0df5e1295f Only print testing warnings if test command is set 2021-01-05 18:56:07 +00:00
Joe Rickerby e215446283 Increase test timeout value 2021-01-05 17:40:15 +00:00
Joe Rickerby a6ff55ed5b Update pip in the test virtualenv 2021-01-05 17:37:55 +00:00
Joe Rickerby bf272fe9a6 Use same interpreter for sample_build.py (for local testing of arm64) 2021-01-05 17:01:47 +00:00
Joe Rickerby 121715a568 Skip cross-compile test on macOS <11 2021-01-05 17:01:23 +00:00
Joe Rickerby 65b530a5df On arm64, build native before cross-compile 2021-01-05 17:01:08 +00:00
Joe Rickerby 16d96fa33d It does not. 2021-01-05 16:18:17 +00:00
Joe Rickerby 7a11143be8 Like this? 2021-01-05 16:14:07 +00:00
Joe Rickerby afd08c11f2 Maybe Azure secretly supports macOS 11.0? 2021-01-05 16:12:24 +00:00
Joe Rickerby 914f74a0f0 Interim fix for archs comparison
This will change when #507 is merged
2021-01-05 16:03:24 +00:00
Joe Rickerby 577fd5e8e3 Implement testing for universal2/arm64 wheels 2021-01-05 15:58:53 +00:00
Joe Rickerby df60457001 Use the ARCHS option to select universal2/arm64 builds 2021-01-05 15:14:32 +00:00
Joe Rickerby e405f3495e Print warnings when builds are deselected because of build OS 2021-01-05 14:29:38 +00:00
Joe Rickerby 46eb28791f Refactor for clarity 2021-01-05 13:50:21 +00:00
Joe Rickerby a973ba9716 Merge remote-tracking branch 'origin/master' into macos-universal2 2021-01-05 13:35:39 +00:00
Joe Rickerby a1ac43e9de Override buildable archs on macOS 11 2021-01-05 10:23:36 +00:00
Joe Rickerby 7cab712592 Fix unit test 2021-01-02 19:25:09 +00:00
Joe Rickerby 8d045992fa Merge remote-tracking branch 'origin/master' into macos-universal2 2021-01-01 18:58:23 +00:00
Joe Rickerby 09fef56a46 Make tests expect arm64 and universal2 2021-01-01 18:56:36 +00:00
Joe Rickerby 4d89e524eb Add rename hack to make the universal wheel work on both archs 2021-01-01 18:55:50 +00:00
Joe Rickerby 76d5f3d3e0 Call delocate with the correct archs 2020-12-31 16:50:44 +00:00
Joe Rickerby 2b800bbb3a Build some sample wheels 2020-12-31 16:40:10 +00:00
Joe Rickerby 2e8e806be0 Merge remote-tracking branch 'origin/master' into macos-universal2 2020-12-28 01:08:14 +00:00
Joe Rickerby 31d0351ddf Upload some sample wheels as artifacts 2020-12-28 00:57:53 +00:00
Henry Schreiner 985ef4dfef wip: drop pypy for now 2020-12-18 11:32:28 -05:00
Joe Rickerby 9906cf7105 Fix logger crash 2020-12-18 14:16:28 +00:00
Joe Rickerby 7fb032d70f Try using _PYTHON_HOST_PLATFORM to create single-arch builds 2020-12-18 12:53:37 +00:00
Henry Schreiner 609c0f3986 WIP: try new Python build 2020-12-16 22:16:56 -05:00
Joe Rickerby 59801d4c98 Disable macOS CPython 3.5 build for now 2020-12-16 20:05:31 +00:00
Joe Rickerby 90ce9d9844 Update dependencies and macOS 11 CI on Github 2020-12-16 16:21:49 +00:00
61 changed files with 1578 additions and 309 deletions
+12
View File
@@ -52,6 +52,18 @@ jobs:
run: |
python bin/sample_build.py
- name: Get some sample wheels
run: |
python -m test.test_projects test.test_0_basic.basic_project sample_proj
cibuildwheel --output-dir wheelhouse sample_proj
env:
CIBW_ARCHS_MACOS: x86_64 universal2 arm64
- uses: actions/upload-artifact@v2
with:
name: sample_wheels
path: wheelhouse
- name: Test cibuildwheel
run: |
python ./bin/run_tests.py
+9
View File
@@ -105,3 +105,12 @@ env3?/
# MyPy cache
.mypy_cache/
# Output cache for setup.py checker
all_known_setup.yaml
# mkdocs
site/
# Virtual environments
venv*
+22 -7
View File
@@ -20,19 +20,23 @@ repos:
rev: 20.8b1
hooks:
- id: black
files: ^bin/update_pythons.py$
args: ["--line-length=120"]
files: ^bin/update_pythons.py|setup.py$
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.790
rev: v0.800
hooks:
- id: mypy
files: ^(cibuildwheel/|test/|bin/projects.py|bin/update_pythons.py|unit_test/)
pass_filenames: false
additional_dependencies: [packaging, click]
exclude: ^(bin/|cibuildwheel/resources/).*py$
additional_dependencies: [packaging]
args: ["--python-version=3.6", "--ignore-missing-imports", "--scripts-are-modules"]
- id: mypy
name: mypy 3.7+ on bin/
files: ^bin/.*py$
additional_dependencies: [packaging]
args: ["--python-version=3.7", "--ignore-missing-imports", "--scripts-are-modules"]
- repo: https://github.com/asottile/pyupgrade
rev: v2.7.4
rev: v2.9.0
hooks:
- id: pyupgrade
args: ["--py36-plus"]
@@ -43,3 +47,14 @@ repos:
- id: flake8
exclude: cibuildwheel/resources/
additional_dependencies: [flake8-bugbear]
# The original pybind11 checks for a few C++ style items
- repo: local
hooks:
- id: disallow-caps
name: Disallow improper capitalization
language: pygrep
entry: PyBind|Numpy|Cmake|Github
types:
- markdown
+2 -2
View File
@@ -6,9 +6,9 @@ mkdocs:
configuration: mkdocs.yml
python:
version: 3.7
version: 3.8
install:
- method: pip
path: .
extra_requirements:
- dev
- docs
+83 -34
View File
@@ -1,7 +1,14 @@
cibuildwheel
============
[![PyPI](https://img.shields.io/pypi/v/cibuildwheel.svg)](https://pypi.python.org/pypi/cibuildwheel) [![Documentation Status](https://readthedocs.org/projects/cibuildwheel/badge/?version=stable)](https://cibuildwheel.readthedocs.io/en/stable/?badge=stable) [![Build Status](https://travis-ci.org/joerick/cibuildwheel.svg?branch=master)](https://travis-ci.org/joerick/cibuildwheel) [![Build status](https://ci.appveyor.com/api/projects/status/wbsgxshp05tt1tif/branch/master?svg=true)](https://ci.appveyor.com/project/joerick/cibuildwheel/branch/master) [![CircleCI](https://circleci.com/gh/joerick/cibuildwheel.svg?style=svg)](https://circleci.com/gh/joerick/cibuildwheel) [![Build Status](https://dev.azure.com/joerick0429/cibuildwheel/_apis/build/status/joerick.cibuildwheel?branchName=master)](https://dev.azure.com/joerick0429/cibuildwheel/_build/latest?definitionId=2&branchName=master)
[![PyPI](https://img.shields.io/pypi/v/cibuildwheel.svg)](https://pypi.python.org/pypi/cibuildwheel)
[![Documentation Status](https://readthedocs.org/projects/cibuildwheel/badge/?version=stable)](https://cibuildwheel.readthedocs.io/en/stable/?badge=stable)
[![Actions Status](https://github.com/joerick/cibuildwheel/workflows/Test/badge.svg)](https://github.com/joerick/cibuildwheel/actions)
[![Travis Status](https://img.shields.io/travis/joerick/cibuildwheel/master?logo=travis)](https://travis-ci.org/joerick/cibuildwheel)
[![AppVeyor status](https://ci.appveyor.com/api/projects/status/wbsgxshp05tt1tif/branch/master?svg=true)](https://ci.appveyor.com/project/joerick/cibuildwheel/branch/master)
[![CircleCI Status](https://img.shields.io/circleci/build/gh/joerick/cibuildwheel/master?logo=circleci)](https://circleci.com/gh/joerick/cibuildwheel)
[![Azure Status](https://dev.azure.com/joerick0429/cibuildwheel/_apis/build/status/joerick.cibuildwheel?branchName=master)](https://dev.azure.com/joerick0429/cibuildwheel/_build/latest?definitionId=2&branchName=master)
[Documentation](https://cibuildwheel.readthedocs.org)
@@ -15,26 +22,24 @@ Python wheels are great. Building them across **Mac, Linux, Windows**, on **mult
What does it do?
----------------
| | macOS x86_64 | Windows 64bit | Windows 32bit | manylinux x86_64 | manylinux i686 | manylinux aarch64 | manylinux ppc64le | manylinux s390x |
|---|---|---|---|---|---|---|---|---|
| CPython 2.7 | ✅ | ✅¹ | ✅¹ | ✅ | ✅ | | | |
| CPython 3.5 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅² | ✅² | ✅³ |
| CPython 3.6 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅² | ✅² | ✅³ |
| CPython 3.7 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅² | ✅² | ✅³ |
| CPython 3.8 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅² | ✅² | ✅³ |
| CPython 3.9 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅² | ✅² | ✅³ |
| PyPy 2.7 v7.3.3 | ✅ | | ✅ | ✅ | | | | |
| PyPy 3.6 v7.3.3 | ✅ | | ✅ | ✅ | | | | |
| PyPy 3.7 (beta) v7.3.3 | ✅ | | ✅ | ✅ | | | | |
| | macOS Intel | macOS Apple Silicon | Windows 64bit | Windows 32bit | manylinux x86_64 | manylinux i686 | manylinux aarch64 | manylinux ppc64le | manylinux s390x |
|---|---|---|---|---|---|---|---|---|---|
| CPython 2.7 | ✅ | | ✅¹ | ✅¹ | ✅ | ✅ | | | |
| CPython 3.5 | ✅ | | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| CPython 3.6 | ✅ | | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| CPython 3.7 | ✅ | | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| CPython 3.8 | ✅ | | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| CPython 3.9 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| PyPy 2.7 v7.3.3 | ✅ | | | ✅ | ✅ | | | | |
| PyPy 3.6 v7.3.3 | ✅ | | | ✅ | ✅ | | | | |
| PyPy 3.7 (beta) v7.3.3 | ✅ | | | ✅ | ✅ | | | | |
<sup>¹ Not supported on Travis</sup><br>
<sup>² Only supported on Travis</sup><br>
<sup>³ Beta support until Travis CI fixes <a href="https://travis-ci.community/t/no-space-left-on-device-for-system-z/5954/11">a bug</a></sup><br>
- Builds manylinux, macOS, and Windows wheels for CPython and PyPy
- Builds manylinux, macOS 10.9+, and Windows wheels for CPython and PyPy
- Works on GitHub Actions, Azure Pipelines, Travis CI, AppVeyor, CircleCI, and GitLab CI
- Bundles shared library dependencies on Linux and macOS through [auditwheel](https://github.com/pypa/auditwheel) and [delocate](https://github.com/matthew-brett/delocate)
- Runs the library test suite against the wheel-installed version of your library
- Runs your library's tests against the wheel-installed version of your library
Usage
-----
@@ -43,23 +48,23 @@ Usage
| | Linux | macOS | Windows | Linux ARM |
|-----------------|-------|-------|---------|--------------|
| GitHub Actions | ✅ | ✅ | ✅ | ✴️¹ |
| Azure Pipelines | ✅ | ✅ | ✅ | ✴️¹ |
| GitHub Actions | ✅ | ✅ | ✅ | ¹ |
| Azure Pipelines | ✅ | ✅ | ✅ | |
| Travis CI | ✅ | ✅ | ✅ | ✅ |
| AppVeyor | ✅ | ✅ | ✅ | |
| CircleCI | ✅ | ✅ | | |
| Gitlab CI | ✅ | | | |
<sup>¹ Requires a "third-party build host"; expected to work with cibuildwheel but not directly tested by our CI.</sup><br>
<sup>¹ [Requires emulation](https://cibuildwheel.readthedocs.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>
`cibuildwheel` is not intended to run on your development machine. Because it uses system Python from Python.org it will try to install packages globally - not what you expect from a build tool! Instead, isolated CI services like those mentioned above are ideal.
`cibuildwheel` is not intended to run on your development machine. Because it uses system Python from Python.org on macOS and Windows, it will try to install packages globally - not what you expect from a build tool! Instead, isolated CI services like those mentioned above are ideal. For Linux builds, it uses manylinux docker images, so those can be done locally for testing in a pinch.
<!--intro-end-->
Example setup
-------------
To build manylinux, macOS, and Windows wheels on Github Actions, you could use this `.github/workflows/wheels.yml`:
To build manylinux, macOS, and Windows wheels on GitHub Actions, you could use this `.github/workflows/wheels.yml`:
```yaml
name: Build
@@ -77,23 +82,24 @@ jobs:
steps:
- uses: actions/checkout@v2
# Used to host cibuildwheel
- uses: actions/setup-python@v2
name: Install Python
- name: Install cibuildwheel
run: python -m pip install cibuildwheel==1.8.0
run: python -m pip install cibuildwheel==1.9.0
- name: Build wheels
run: python -m cibuildwheel --output-dir wheelhouse
env:
CIBW_SKIP: "cp27-* pp27-*" # skip Python 2.7 wheels
# to supply options, put them in 'env', like:
# env:
# CIBW_SOME_OPTION: value
- uses: actions/upload-artifact@v2
with:
path: ./wheelhouse/*.whl
```
For more information, including building on Python 2, PyPI deployment, and the use of other CI services, check out the [documentation](https://cibuildwheel.readthedocs.org) and the [examples](https://github.com/joerick/cibuildwheel/tree/master/examples).
For more information, including building on Python 2, PyPI deployment, and the use of other CI services or the dedicated GitHub Action, check out the [documentation](https://cibuildwheel.readthedocs.org) and the [examples](https://github.com/joerick/cibuildwheel/tree/master/examples).
Options
-------
@@ -102,7 +108,8 @@ Options
|---|--------|-------------|
| **Build selection** | [`CIBW_PLATFORM`](https://cibuildwheel.readthedocs.io/en/stable/options/#platform) | Override the auto-detected target platform |
| | [`CIBW_BUILD`](https://cibuildwheel.readthedocs.io/en/stable/options/#build-skip) <br> [`CIBW_SKIP`](https://cibuildwheel.readthedocs.io/en/stable/options/#build-skip) | Choose the Python versions to build |
| | [`CIBW_ARCHS_LINUX`](https://cibuildwheel.readthedocs.io/en/stable/options/#archs) | Build non-native architectures |
| | [`CIBW_ARCHS`](https://cibuildwheel.readthedocs.io/en/stable/options/#archs) | Change the architectures built on your machine by default |
| | [`CIBW_PROJECT_REQUIRES_PYTHON`](https://cibuildwheel.readthedocs.io/en/stable/options/#requires-python) | Manually set the Python compatibility of your project |
| **Build customization** | [`CIBW_ENVIRONMENT`](https://cibuildwheel.readthedocs.io/en/stable/options/#environment) | Set environment variables needed during the build |
| | [`CIBW_BEFORE_ALL`](https://cibuildwheel.readthedocs.io/en/stable/options/#before-all) | Execute a shell command on the build system before any wheels are built. |
| | [`CIBW_BEFORE_BUILD`](https://cibuildwheel.readthedocs.io/en/stable/options/#before-build) | Execute a shell command preparing each wheel's build |
@@ -303,6 +310,48 @@ Changelog
<!--changelog-start-->
### v1.9.0
_5 February 2021_
- 🌟 Added support for Apple Silicon wheels on macOS! You can now
cross-compile `universal2` and `arm64` wheels on your existing macOS Intel
runners, by setting
[CIBW_ARCHS_MACOS](https://cibuildwheel.readthedocs.io/en/stable/options/#archs).
Xcode 12.2 or later is required, but you don't need macOS 11.0 - you can
still build on macOS 10.15. See
[this FAQ entry](https://cibuildwheel.readthedocs.io/en/stable/faq/#apple-silicon)
for more information. (#484)
- 🌟 Added auto-detection of your package's Python compatibility, via declared
[`requires-python`](https://www.python.org/dev/peps/pep-0621/#requires-python)
in your `pyproject.toml`, or
[`python_requires`](https://packaging.python.org/guides/distributing-packages-using-setuptools/#python-requires)
in `setup.cfg` or `setup.py`. If your project has these set, cibuildwheel
will automatically skip builds on versions of Python that your package
doesn't support. Hopefully this makes the first-run experience of
cibuildwheel a bit easier. If you need to override this for any reason,
look at [`CIBW_PROJECT_REQUIRES_PYTHON`](https://cibuildwheel.readthedocs.io/en/stable/options/#requires-python).
(#536)
- 🌟 cibuildwheel can now be invoked as a native GitHub Action! You can now
invoke cibuildwheel in a GHA build step like:
```yaml
- name: Build wheels
uses: joerick/cibuildwheel@v1.9.0
with:
output-dir: wheelhouse
# env:
# CIBW_SOME_OPTION: value
```
This saves a bit of boilerplate, and you can [use Dependabot to keep the
pinned version up-to-date](https://cibuildwheel.readthedocs.io/en/stable/faq/#automatic-updates).
- ✨ Added `auto64` and `auto32` shortcuts to the
[CIBW_ARCHS](https://cibuildwheel.readthedocs.io/en/stable/options/#archs)
option. (#553)
- ✨ cibuildwheel now prints a list of the wheels built at the end of each
run. (#570)
- 📚 Lots of minor docs improvements.
### 1.8.0
_22 January 2021_
@@ -310,9 +359,9 @@ _22 January 2021_
- 🌟 Added support for emulated builds! You can now build manylinux wheels on
ARM64`aarch64`, as well as `ppc64le` and 's390x'. To build under emulation,
register QEMU via binfmt_misc and set the
[`CIBW_ARCHS_LINUX`](https://cibuildwheel.readthedocs.io/en/latest/options/#archs)
[`CIBW_ARCHS_LINUX`](https://cibuildwheel.readthedocs.io/en/stable/options/#archs)
option to the architectures you want to run. See
[this FAQ entry](https://cibuildwheel.readthedocs.io/en/latest/faq/#emulation)
[this FAQ entry](https://cibuildwheel.readthedocs.io/en/stable/faq/#emulation)
for more information. (#482)
- ✨ Added `CIBW_TEST_SKIP` option. This allows you to choose certain builds
whose tests you'd like to skip. This might be useful when running a slow
@@ -336,7 +385,7 @@ _1 January 2021_
- 🛠 Added a patch for Pypy to ensure header files are available for building
in a virtualenv. (#502)
- 🛠 Some preparatory work towards using cibuildwheel as a Github Action.
- 🛠 Some preparatory work towards using cibuildwheel as a GitHub Action.
Check out
[the FAQ](https://cibuildwheel.readthedocs.io/en/stable/faq/#option-1-github-action)
for information on how to use it. We'll be fully updating the docs to this
@@ -348,7 +397,7 @@ _21 December 2020_
- 🛠 Update dependencies, notably wheel==0.36.2 and pip==20.3.3, and CPython to
their latest bugfix releases (#489)
- 📚 Switch to a Github example in the README (#479)
- 📚 Switch to a GitHub example in the README (#479)
- 📚 Create Working Examples table, with many projects that use cibuildwheel (#474)
- 📚 Import Working Examples table and Changelog to docs
@@ -453,7 +502,7 @@ _8 July 2020_
_25 June 2020_
- 🐛 Fix "OSError: [WinError 17] The system cannot move the file to a different
disk drive" on Github Actions (#388, #389)
disk drive" on GitHub Actions (#388, #389)
### 1.5.0
@@ -467,7 +516,7 @@ _24 June 2020_
(#360, #358)
- ✨ Added workaround to allow Python 3.5 on Windows to pull dependencies from
pyproject.toml. (#358)
- 📚 Improved Github Actions examples and docs (#354, #362)
- 📚 Improved GitHub Actions examples and docs (#354, #362)
- 🐛 Ensure pip wheel uses the specified package, and doesn't build a wheel
from PyPI (#369)
- 🛠 Internal changes: using pathlib.Path, precommit hooks, testing
@@ -525,7 +574,7 @@ _2 May 2020_
_12 March 2020_
- 🌟 Add support for building on Github Actions! Check out the
- 🌟 Add support for building on GitHub Actions! Check out the
[docs](https://cibuildwheel.readthedocs.io/en/stable/setup/#github-actions)
for information on how to set it up. (#194)
- ✨ Add the `CIBW_BEFORE_TEST` option, which lets you run a command to
+1
View File
@@ -21,6 +21,7 @@ config = [
('docs/faq.md', "cibuildwheel@v{}"),
('docs/setup.md', "cibuildwheel@v{}"),
('examples/*', "cibuildwheel=={}"),
('examples/*', "cibuildwheel@v{}"),
('setup.cfg', "version = {}"),
]
+110
View File
@@ -0,0 +1,110 @@
#!/usr/bin/env python3
from __future__ import annotations
import ast
from pathlib import Path
from typing import Iterator
import click
import yaml
from ghapi.core import GhApi, HTTP404NotFoundError # type: ignore
from rich import print
from cibuildwheel.projectfiles import Analyzer
DIR = Path(__file__).parent.resolve()
def parse(contents: str) -> str | None:
try:
tree = ast.parse(contents)
analyzer = Analyzer()
analyzer.visit(tree)
return analyzer.requires_python or ""
except Exception:
return None
def check_repo(name: str, contents: str) -> str:
s = f" {name}: "
if name == "setup.py":
if "python_requires" not in contents:
s += ""
res = parse(contents)
if res is None:
s += "⚠️ "
elif res:
s += "" + res
elif "python_requires" in contents:
s += "☑️"
elif name == "setup.cfg":
s += "" if "python_requires" in contents else ""
else:
s += "" if "requires-python" in contents else ""
return s
class MaybeRemote:
def __init__(self, cached_file: Path | str, *, online: bool) -> None:
self.online = online
if self.online:
self.contents: dict[str, dict[str, str | None]] = {
"setup.py": {},
"setup.cfg": {},
"pyproject.toml": {},
}
else:
with open(cached_file) as f:
self.contents = yaml.safe_load(f)
def get(self, repo: str, filename: str) -> str | None:
if self.online:
try:
self.contents[filename][repo] = (
GhApi(*repo.split("/")).get_content(filename).decode()
)
except HTTP404NotFoundError:
self.contents[filename][repo] = None
return self.contents[filename][repo]
elif repo in self.contents[filename]:
return self.contents[filename][repo]
else:
raise RuntimeError(
f"Trying to access {repo}:{filename} and not in cache, rebuild cache"
)
def save(self, filename: Path | str) -> None:
with open(filename, "w") as f:
yaml.safe_dump(self.contents, f, default_flow_style=False)
def on_each(self, repos: list[str]) -> Iterator[tuple[str, str, str | None]]:
for repo in repos:
print(f"[bold]{repo}:")
for filename in sorted(self.contents, reverse=True):
yield repo, filename, self.get(repo, filename)
@click.command()
@click.option("--online", is_flag=True, help="Remember to set GITHUB_TOKEN")
def main(online: bool) -> None:
with open(DIR / "../docs/data/projects.yml") as f:
known = yaml.safe_load(f)
repos = [x["gh"] for x in known]
ghinfo = MaybeRemote("all_known_setup.yaml", online=online)
for _, filename, contents in ghinfo.on_each(repos):
if contents is None:
print(f"[red] {filename}: Not found")
else:
print(check_repo(filename, contents))
if online:
ghinfo.save("all_known_setup.yaml")
if __name__ == "__main__":
main()
+1 -1
View File
@@ -17,4 +17,4 @@ if __name__ == '__main__':
subprocess.check_call(unit_test_args)
# run the integration tests
subprocess.check_call([sys.executable, '-m', 'pytest', '-x', '--durations', '0', '--timeout=1200', 'test'])
subprocess.check_call([sys.executable, '-m', 'pytest', '-x', '--durations', '0', '--timeout=2400', 'test'])
+1 -1
View File
@@ -22,4 +22,4 @@ if __name__ == '__main__':
options.project_python_path, project_dir
], check=True)
sys.exit(subprocess.run(['cibuildwheel'], cwd=project_dir).returncode)
sys.exit(subprocess.run([sys.executable, '-m', 'cibuildwheel'], cwd=project_dir).returncode)
+15 -18
View File
@@ -164,7 +164,7 @@ class PyPyVersions:
class CPythonVersions:
def __init__(self, plat_arch: str, file_ident: str) -> None:
def __init__(self) -> None:
response = requests.get("https://www.python.org/api/v2/downloads/release/?is_published=true")
response.raise_for_status()
@@ -180,11 +180,8 @@ class CPythonVersions:
uri = int(release["resource_uri"].rstrip("/").split("/")[-1])
self.versions_dict[version] = uri
self.file_ident = file_ident
self.plat_arch = plat_arch
def update_version_macos(self, spec: Specifier) -> Optional[ConfigMacOS]:
def update_version_macos(self, identifier: str, spec: Specifier) -> Optional[ConfigMacOS]:
file_idents = ("macos11.0.pkg", "macosx10.9.pkg", "macosx10.6.pkg")
sorted_versions = sorted(v for v in self.versions_dict if spec.contains(v))
for version in reversed(sorted_versions):
@@ -194,13 +191,14 @@ class CPythonVersions:
response.raise_for_status()
file_info = response.json()
urls = [rf["url"] for rf in file_info if self.file_ident in rf["url"]]
if urls:
return ConfigMacOS(
identifier=f"cp{version.major}{version.minor}-{self.plat_arch}",
version=f"{version.major}.{version.minor}",
url=urls[0],
)
for file_ident in file_idents:
urls = [rf["url"] for rf in file_info if file_ident in rf["url"]]
if urls:
return ConfigMacOS(
identifier=identifier,
version=f"{version.major}.{version.minor}",
url=urls[0],
)
return None
@@ -215,9 +213,7 @@ class AllVersions:
self.windows_64 = WindowsVersions("64")
self.windows_pypy = PyPyVersions("32")
self.macos_6 = CPythonVersions(plat_arch="macosx_x86_64", file_ident="macosx10.6.pkg")
self.macos_9 = CPythonVersions(plat_arch="macosx_x86_64", file_ident="macosx10.9.pkg")
self.macos_u2 = CPythonVersions(plat_arch="macosx_universal2", file_ident="macos11.0.pkg")
self.macos_cpython = CPythonVersions()
self.macos_pypy = PyPyVersions("64")
def update_config(self, config: Dict[str, str]) -> None:
@@ -229,11 +225,12 @@ class AllVersions:
config_update: Optional[AnyConfig]
# We need to use ** in update due to MyPy (probably a bug)
if "macosx_x86_64" in identifier:
if "macos" in identifier:
if identifier.startswith("pp"):
config_update = self.macos_pypy.update_version_macos(spec)
else:
config_update = self.macos_9.update_version_macos(spec) or self.macos_6.update_version_macos(spec)
config_update = self.macos_cpython.update_version_macos(identifier, spec)
assert config_update is not None, f"MacOS {spec} not found!"
config.update(**config_update)
elif "win32" in identifier:
+1 -1
View File
@@ -1 +1 @@
__version__ = '1.8.0'
__version__ = '1.9.0'
+33 -20
View File
@@ -7,12 +7,16 @@ from configparser import ConfigParser
from pathlib import Path
from typing import Dict, List, Optional, Set, Union, overload
from packaging.specifiers import SpecifierSet
import cibuildwheel
import cibuildwheel.linux
import cibuildwheel.macos
import cibuildwheel.util
import cibuildwheel.windows
from cibuildwheel.architecture import Architecture, allowed_architectures_check
from cibuildwheel.environment import EnvironmentParseError, parse_environment
from cibuildwheel.projectfiles import get_requires_python_str
from cibuildwheel.typing import PLATFORMS, PlatformName, assert_never
from cibuildwheel.util import (
BuildOptions,
@@ -26,10 +30,10 @@ from cibuildwheel.util import (
@overload
def get_option_from_environment(option_name: str, platform: Optional[str], default: str) -> str: ... # noqa: E704
def get_option_from_environment(option_name: str, *, platform: Optional[str] = None, default: str) -> str: ... # noqa: E704
@overload
def get_option_from_environment(option_name: str, platform: Optional[str] = None, default: None = None) -> Optional[str]: ... # noqa: E704 E302
def get_option_from_environment(option_name: str, platform: Optional[str] = None, default: Optional[str] = None) -> Optional[str]: # noqa: E302
def get_option_from_environment(option_name: str, *, platform: Optional[str] = None, default: None = None) -> Optional[str]: ... # noqa: E704 E302
def get_option_from_environment(option_name: str, *, platform: Optional[str] = None, default: Optional[str] = None) -> Optional[str]: # noqa: E302
'''
Returns an option from the environment, optionally scoped by the platform.
@@ -77,7 +81,7 @@ def main() -> None:
on this machine. Set this option to build an architecture
via emulation, for example, using binfmt_misc and QEMU.
Default: auto.
Choices: auto, native, all, {}
Choices: auto, auto64, auto32, native, all, {}
'''.format(", ".join(a.name for a in Architecture)))
parser.add_argument('--output-dir',
default=os.environ.get('CIBW_OUTPUT_DIR', 'wheelhouse'),
@@ -137,7 +141,7 @@ def main() -> None:
if platform == 'linux':
repair_command_default = 'auditwheel repair -w {dest_dir} {wheel}'
elif platform == 'macos':
repair_command_default = 'delocate-listdeps {wheel} && delocate-wheel --require-archs x86_64 -w {dest_dir} {wheel}'
repair_command_default = 'delocate-listdeps {wheel} && delocate-wheel --require-archs {delocate_archs} -w {dest_dir} {wheel}'
elif platform == 'windows':
repair_command_default = ''
else:
@@ -156,7 +160,18 @@ def main() -> None:
test_extras = get_option_from_environment('CIBW_TEST_EXTRAS', platform=platform, default='')
build_verbosity_str = get_option_from_environment('CIBW_BUILD_VERBOSITY', platform=platform, default='')
build_selector = BuildSelector(build_config=build_config, skip_config=skip_config)
package_files = {'setup.py', 'setup.cfg', 'pyproject.toml'}
if not any(package_dir.joinpath(name).exists() for name in package_files):
names = ', '.join(sorted(package_files, reverse=True))
print(f'cibuildwheel: Could not find any of {{{names}}} at root of package', file=sys.stderr)
sys.exit(2)
# Passing this in as an environment variable will override pyproject.toml, setup.cfg, or setup.py
requires_python_str: Optional[str] = os.environ.get('CIBW_PROJECT_REQUIRES_PYTHON') or get_requires_python_str(package_dir)
requires_python = None if requires_python_str is None else SpecifierSet(requires_python_str)
build_selector = BuildSelector(build_config=build_config, skip_config=skip_config, requires_python=requires_python)
test_selector = TestSelector(skip_config=test_skip)
try:
@@ -186,15 +201,11 @@ def main() -> None:
# This needs to be passed on to the docker container in linux.py
os.environ['CIBUILDWHEEL'] = '1'
if not any((package_dir / name).exists()
for name in ["setup.py", "setup.cfg", "pyproject.toml"]):
print('cibuildwheel: Could not find setup.py, setup.cfg or pyproject.toml at root of package', file=sys.stderr)
sys.exit(2)
if args.archs is not None:
archs_config_str = args.archs
else:
archs_config_str = get_option_from_environment('CIBW_ARCHS', platform=platform, default='auto')
archs = Architecture.parse_config(archs_config_str, platform=platform)
identifiers = get_build_identifiers(platform, build_selector, archs)
@@ -271,14 +282,15 @@ def main() -> None:
if not output_dir.exists():
output_dir.mkdir(parents=True)
if platform == 'linux':
cibuildwheel.linux.build(build_options)
elif platform == 'windows':
cibuildwheel.windows.build(build_options)
elif platform == 'macos':
cibuildwheel.macos.build(build_options)
else:
assert_never(platform)
with cibuildwheel.util.print_new_wheels("{n} wheels produced in {m:.0f} minutes:", output_dir):
if platform == 'linux':
cibuildwheel.linux.build(build_options)
elif platform == 'windows':
cibuildwheel.windows.build(build_options)
elif platform == 'macos':
cibuildwheel.macos.build(build_options)
else:
assert_never(platform)
def detect_obsolete_options() -> None:
@@ -334,12 +346,13 @@ def get_build_identifiers(
python_configurations: Union[List[cibuildwheel.linux.PythonConfiguration],
List[cibuildwheel.windows.PythonConfiguration],
List[cibuildwheel.macos.PythonConfiguration]]
if platform == 'linux':
python_configurations = cibuildwheel.linux.get_python_configurations(build_selector, architectures)
elif platform == 'windows':
python_configurations = cibuildwheel.windows.get_python_configurations(build_selector, architectures)
elif platform == 'macos':
python_configurations = cibuildwheel.macos.get_python_configurations(build_selector)
python_configurations = cibuildwheel.macos.get_python_configurations(build_selector, architectures)
else:
assert_never(platform)
+31 -2
View File
@@ -4,7 +4,7 @@ import re
from enum import Enum
from typing import Set
from .typing import PlatformName, assert_never
from .typing import Literal, PlatformName, assert_never
PRETTY_NAMES = {'linux': 'Linux', 'macos': 'macOS', 'windows': 'Windows'}
@@ -15,11 +15,17 @@ class Architecture(Enum):
# mac/linux archs
x86_64 = 'x86_64'
# linux archs
i686 = 'i686'
aarch64 = 'aarch64'
ppc64le = 'ppc64le'
s390x = 's390x'
# mac archs
universal2 = 'universal2'
arm64 = 'arm64'
# windows archs
x86 = 'x86'
AMD64 = 'AMD64'
@@ -38,6 +44,10 @@ class Architecture(Enum):
result.add(Architecture(platform_module.machine()))
elif arch_str == 'all':
result |= Architecture.all_archs(platform=platform)
elif arch_str == 'auto64':
result |= Architecture.bitness_archs(platform=platform, bitness="64")
elif arch_str == 'auto32':
result |= Architecture.bitness_archs(platform=platform, bitness="32")
else:
result.add(Architecture(arch_str))
return result
@@ -46,11 +56,18 @@ class Architecture(Enum):
def auto_archs(platform: PlatformName) -> 'Set[Architecture]':
native_architecture = Architecture(platform_module.machine())
result = {native_architecture}
if platform == 'linux' and native_architecture == Architecture.x86_64:
# x86_64 machines can run i686 docker containers
result.add(Architecture.i686)
if platform == 'windows' and native_architecture == Architecture.AMD64:
result.add(Architecture.x86)
if platform == 'macos' and native_architecture == Architecture.arm64:
# arm64 can build and test both archs of a universal2 wheel.
result.add(Architecture.universal2)
return result
@staticmethod
@@ -58,12 +75,24 @@ class Architecture(Enum):
if platform == 'linux':
return {Architecture.x86_64, Architecture.i686, Architecture.aarch64, Architecture.ppc64le, Architecture.s390x}
elif platform == 'macos':
return {Architecture.x86_64}
return {Architecture.x86_64, Architecture.arm64, Architecture.universal2}
elif platform == 'windows':
return {Architecture.x86, Architecture.AMD64}
else:
assert_never(platform)
@staticmethod
def bitness_archs(platform: PlatformName, bitness: Literal['64', '32']) -> 'Set[Architecture]':
archs_32 = {Architecture.i686, Architecture.x86}
auto_archs = Architecture.auto_archs(platform)
if bitness == '64':
return auto_archs - archs_32
elif bitness == '32':
return auto_archs & archs_32
else:
assert_never(bitness)
def allowed_architectures_check(
platform: PlatformName,
+2 -1
View File
@@ -94,6 +94,7 @@ def build(options: BuildOptions) -> None:
env = docker.get_environment()
env['PATH'] = f'/opt/python/cp38-cp38/bin:{env["PATH"]}'
env['PIP_DISABLE_PIP_VERSION_CHECK'] = '1'
env = options.environment.as_dictionary(env, executor=docker.environment_executor)
before_all_prepared = prepare_command(options.before_all, project=container_project_path, package=container_package_dir)
@@ -228,7 +229,7 @@ def build(options: BuildOptions) -> None:
docker.copy_out(container_output_dir, options.output_dir)
log.step_end()
except subprocess.CalledProcessError as error:
log.error(f'Command {error.cmd} failed with code {error.returncode}. {error.stdout}')
log.step_end_with_error(f'Command {error.cmd} failed with code {error.returncode}. {error.stdout}')
troubleshoot(options.package_dir, error)
sys.exit(1)
+14 -4
View File
@@ -23,6 +23,8 @@ PLATFORM_IDENTIFIER_DESCIPTIONS = {
'win32': 'Windows 32bit',
'win_amd64': 'Windows 64bit',
'macosx_x86_64': 'macOS x86_64',
'macosx_universal2': 'macOS Universal 2 - x86_64 and arm64',
'macosx_arm64': 'macOS arm64 - Apple Silicon',
}
@@ -109,15 +111,23 @@ class Logger:
self.step_start_time = None
def error(self, error: Union[BaseException, str]) -> None:
def step_end_with_error(self, error: Union[BaseException, str]) -> None:
self.step_end(success=False)
print()
self.error(error)
def warning(self, message: str) -> None:
if self.fold_mode == 'github':
print(f'::error::{error}')
print(f'::warning::{message}\n', file=sys.stderr)
else:
c = self.colors
print(f'{c.bright_red}Error{c.end} {error}')
print(f'{c.yellow}Warning{c.end}: {message}\n', file=sys.stderr)
def error(self, error: Union[BaseException, str]) -> None:
if self.fold_mode == 'github':
print(f'::error::{error}\n', file=sys.stderr)
else:
c = self.colors
print(f'{c.bright_red}Error{c.end}: {error}\n', file=sys.stderr)
def _start_fold_group(self, name: str) -> None:
self._end_fold_group()
+206 -43
View File
@@ -1,15 +1,18 @@
import os
import platform
import re
import shlex
import shutil
import subprocess
import sys
import tempfile
from pathlib import Path
from typing import Dict, List, NamedTuple, Optional, Sequence
from typing import Any, Dict, List, NamedTuple, Optional, Sequence, Set, Tuple, cast
from .architecture import Architecture
from .environment import ParsedEnvironment
from .logger import log
from .typing import PathOrStr
from .typing import Literal, PathOrStr
from .util import (
BuildOptions,
BuildSelector,
@@ -21,6 +24,7 @@ from .util import (
prepare_command,
read_python_configs,
resources_dir,
unwrap,
)
@@ -34,21 +38,72 @@ def call(args: Sequence[PathOrStr], env: Optional[Dict[str, str]] = None, cwd: O
return subprocess.check_call(args, env=env, cwd=cwd, shell=shell)
def get_macos_version() -> Tuple[int, int]:
'''
Returns the macOS major/minor version, as a tuple, e.g. (10, 15) or (11, 0)
These tuples can be used in comparisons, e.g.
(10, 14) <= (11, 0) == True
(10, 14) <= (10, 16) == True
(11, 2) <= (11, 0) != True
'''
version_str, _, _ = platform.mac_ver()
version = tuple(map(int, version_str.split(".")[:2]))
return cast(Tuple[int, int], version)
def get_macos_sdks() -> List[str]:
output = subprocess.check_output(
['xcodebuild', '-showsdks'],
universal_newlines=True,
)
return [m.group(1) for m in re.finditer(r'-sdk (macosx\S+)', output)]
class PythonConfiguration(NamedTuple):
version: str
identifier: str
url: str
def get_python_configurations(
build_selector: BuildSelector) -> List[PythonConfiguration]:
def get_python_configurations(build_selector: BuildSelector,
architectures: Set[Architecture]) -> List[PythonConfiguration]:
full_python_configs = read_python_configs('macos')
python_configurations = [PythonConfiguration(**item) for item in full_python_configs]
# skip builds as required
return [c for c in python_configurations if build_selector(c.identifier)]
# filter out configs that don't match any of the selected architectures
python_configurations = [c for c in python_configurations
if any(c.identifier.endswith(a.value) for a in architectures)]
# skip builds as required by BUILD/SKIP
python_configurations = [c for c in python_configurations if build_selector(c.identifier)]
# When running on macOS 11 and x86_64, the reported OS is '10.16', but
# there is no such OS - it really means macOS 11.
if get_macos_version() >= (10, 16):
if any(c.identifier.startswith('pp') for c in python_configurations):
# pypy doesn't work on macOS 11 yet
# See https://foss.heptapod.net/pypy/pypy/-/issues/3314
log.warning(unwrap('''
PyPy is currently unsupported when building on macOS 11. To build macOS PyPy wheels,
build on an older OS, such as macOS 10.15. To silence this warning, deselect PyPy by
adding "pp*-macosx*" to your CIBW_SKIP option.
'''))
python_configurations = [c for c in python_configurations if not c.identifier.startswith('pp')]
if any(c.identifier.startswith('cp35') for c in python_configurations):
# CPython 3.5 doesn't work on macOS 11
log.warning(unwrap('''
CPython 3.5 is unsupported when building on macOS 11. To build CPython 3.5 wheels,
build on an older OS, such as macOS 10.15. To silence this warning, deselect CPython
3.5 by adding "cp35-macosx_x86_64" to your CIBW_SKIP option.
'''))
python_configurations = [c for c in python_configurations if not c.identifier.startswith('cp35')]
return python_configurations
SYMLINKS_DIR = Path('/tmp/cibw_bin')
@@ -150,6 +205,9 @@ def setup_python(python_configuration: PythonConfiguration,
env.pop('__PYVENV_LAUNCHER__', None)
env = environment.as_dictionary(prev_environment=env)
# we version pip ourselves, so we don't care about pip version checking
env['PIP_DISABLE_PIP_VERSION_CHECK'] = '1'
# check what version we're on
call(['which', 'python'], env=env)
call(['python', '--version'], env=env)
@@ -180,6 +238,41 @@ def setup_python(python_configuration: PythonConfiguration,
# https://github.com/python/cpython/blob/a5ed2fe0eedefa1649aa93ee74a0bafc8e628a10/Lib/_osx_support.py#L260
env.setdefault('ARCHFLAGS', '-arch x86_64')
if python_configuration.version == '3.9':
if python_configuration.identifier.endswith('x86_64'):
# even on the macos11.0 Python installer, on the x86_64 side it's
# compatible back to 10.9.
env.setdefault('_PYTHON_HOST_PLATFORM', 'macosx-10.9-x86_64')
env.setdefault('ARCHFLAGS', '-arch x86_64')
elif python_configuration.identifier.endswith('arm64'):
# macOS 11 is the first OS with arm64 support, so the wheels
# have that as a minimum.
env.setdefault('_PYTHON_HOST_PLATFORM', 'macosx-11.0-arm64')
env.setdefault('ARCHFLAGS', '-arch arm64')
elif python_configuration.identifier.endswith('universal2'):
if get_macos_version() < (10, 16):
# we can do universal2 builds on macos 10.15, but we need to
# set ARCHFLAGS otherwise CPython sets it to `-arch x86_64`
env.setdefault('ARCHFLAGS', '-arch arm64 -arch x86_64')
if python_configuration.identifier.endswith('arm64') or python_configuration.identifier.endswith('universal2'):
if get_macos_version() < (10, 16) and 'SDKROOT' not in env:
# xcode 12.2 or higher can build arm64 on macos 10.15 or below, but
# needs the correct SDK selected.
sdks = get_macos_sdks()
# Different versions of Xcode contain different SDK versions...
# we're happy with anything newer than macOS 11.0
arm64_compatible_sdks = [s for s in sdks if not s.startswith('macosx10.')]
if not arm64_compatible_sdks:
log.warning(unwrap('''
SDK for building arm64-compatible wheels not found. You need Xcode 12.2 or later
to build universal2 or arm64 wheels.
'''))
else:
env.setdefault('SDKROOT', arm64_compatible_sdks[0])
log.step('Installing build tools...')
call(['pip', 'install', '--upgrade', 'setuptools', 'wheel', 'delocate', *dependency_constraint_flags], env=env)
@@ -198,7 +291,7 @@ def build(options: BuildOptions) -> None:
before_all_prepared = prepare_command(options.before_all, project='.', package=options.package_dir)
call([before_all_prepared], shell=True, env=env)
python_configurations = get_python_configurations(options.build_selector)
python_configurations = get_python_configurations(options.build_selector, options.architectures)
for config in python_configurations:
log.build_start(config.identifier)
@@ -242,60 +335,130 @@ def build(options: BuildOptions) -> None:
if options.repair_command:
log.step('Repairing wheel...')
repair_command_prepared = prepare_command(options.repair_command, wheel=built_wheel, dest_dir=repaired_wheel_dir)
if config.identifier.endswith('universal2'):
delocate_archs = 'x86_64,arm64'
elif config.identifier.endswith('arm64'):
delocate_archs = 'arm64'
else:
delocate_archs = 'x86_64'
repair_command_prepared = prepare_command(
options.repair_command,
wheel=built_wheel,
dest_dir=repaired_wheel_dir,
delocate_archs=delocate_archs,
)
call(repair_command_prepared, env=env, shell=True)
else:
shutil.move(str(built_wheel), repaired_wheel_dir)
repaired_wheel = next(repaired_wheel_dir.glob('*.whl'))
log.step_end()
if options.test_command and options.test_selector(config.identifier):
log.step('Testing wheel...')
# set up a virtual environment to install and test from, to make sure
# there are no dependencies that were pulled in at build time.
call(['pip', 'install', 'virtualenv', *dependency_constraint_flags], env=env)
venv_dir = Path(tempfile.mkdtemp())
machine_arch = platform.machine()
testing_archs: List[Literal['x86_64', 'arm64']] = []
# Use --no-download to ensure determinism by using seed libraries
# built into virtualenv
call(['python', '-m', 'virtualenv', '--no-download', venv_dir], env=env)
if config.identifier.endswith('_arm64'):
testing_archs = ['arm64']
elif config.identifier.endswith('_universal2'):
testing_archs = ['x86_64', 'arm64']
else:
testing_archs = ['x86_64']
virtualenv_env = env.copy()
virtualenv_env['PATH'] = os.pathsep.join([
str(venv_dir / 'bin'),
virtualenv_env['PATH'],
])
for testing_arch in testing_archs:
if config.identifier.endswith('_universal2'):
arch_specific_identifier = f'{config.identifier}:{testing_arch}'
if not options.test_selector(arch_specific_identifier):
continue
# check that we are using the Python from the virtual environment
call(['which', 'python'], env=virtualenv_env)
if machine_arch == 'x86_64' and testing_arch == 'arm64':
if config.identifier.endswith('_arm64'):
log.warning(unwrap('''
While arm64 wheels can be built on x86_64, they cannot be tested. The
ability to test the arm64 wheels will be added in a future release of
cibuildwheel, once Apple Silicon CI runners are widely available. To
silence this warning, set `CIBW_TEST_SKIP: *-macosx_arm64`.
'''))
elif config.identifier.endswith('_universal2'):
log.warning(unwrap('''
While universal2 wheels can be built on x86_64, the arm64 part of them
cannot currently be tested. The ability to test the arm64 part of a
universal2 wheel will be added in a future release of cibuildwheel, once
Apple Silicon CI runners are widely available. To silence this warning,
set `CIBW_TEST_SKIP: *-macosx_universal2:arm64`.
'''))
else:
raise RuntimeError('unreachable')
if options.before_test:
before_test_prepared = prepare_command(options.before_test, project='.', package=options.package_dir)
call(before_test_prepared, env=virtualenv_env, shell=True)
# skip this test
continue
# install the wheel
call(['pip', 'install', str(repaired_wheel) + options.test_extras], env=virtualenv_env)
log.step('Testing wheel...' if testing_arch == machine_arch else f'Testing wheel on {testing_arch}...')
# test the wheel
if options.test_requires:
call(['pip', 'install'] + options.test_requires, env=virtualenv_env)
# set up a virtual environment to install and test from, to make sure
# there are no dependencies that were pulled in at build time.
call(['pip', 'install', 'virtualenv', *dependency_constraint_flags], env=env)
venv_dir = Path(tempfile.mkdtemp())
# run the tests from $HOME, with an absolute path in the command
# (this ensures that Python runs the tests against the installed wheel
# and not the repo code)
test_command_prepared = prepare_command(
options.test_command,
project=Path('.').resolve(),
package=options.package_dir.resolve()
)
call(test_command_prepared, cwd=os.environ['HOME'], env=virtualenv_env, shell=True)
arch_prefix = []
if testing_arch != machine_arch:
if machine_arch == 'arm64' and testing_arch == 'x86_64':
# rosetta2 will provide the emulation with just the arch prefix.
arch_prefix = ['arch', '-x86_64']
else:
raise RuntimeError("don't know how to emulate {testing_arch} on {machine_arch}")
# clean up
shutil.rmtree(venv_dir)
# define a custom 'call' function that adds the arch prefix each time
def call_with_arch(args: Sequence[PathOrStr], **kwargs: Any) -> int:
if isinstance(args, str):
args = ' '.join(arch_prefix) + ' ' + args
else:
args = [*arch_prefix, *args]
return call(args, **kwargs)
# Use --no-download to ensure determinism by using seed libraries
# built into virtualenv
call_with_arch(['python', '-m', 'virtualenv', '--no-download', venv_dir], env=env)
virtualenv_env = env.copy()
virtualenv_env['PATH'] = os.pathsep.join([
str(venv_dir / 'bin'),
virtualenv_env['PATH'],
])
# check that we are using the Python from the virtual environment
call_with_arch(['which', 'python'], env=virtualenv_env)
if options.before_test:
before_test_prepared = prepare_command(options.before_test, project='.', package=options.package_dir)
call_with_arch(before_test_prepared, env=virtualenv_env, shell=True)
# install the wheel
call_with_arch(['pip', 'install', str(repaired_wheel) + options.test_extras], env=virtualenv_env)
# test the wheel
if options.test_requires:
call_with_arch(['pip', 'install'] + options.test_requires, env=virtualenv_env)
# run the tests from $HOME, with an absolute path in the command
# (this ensures that Python runs the tests against the installed wheel
# and not the repo code)
test_command_prepared = prepare_command(
options.test_command,
project=Path('.').resolve(),
package=options.package_dir.resolve()
)
call_with_arch(test_command_prepared, cwd=os.environ['HOME'], env=virtualenv_env, shell=True)
# clean up
shutil.rmtree(venv_dir)
# we're all done here; move it to output (overwrite existing)
shutil.move(str(repaired_wheel), options.output_dir)
log.build_end()
except subprocess.CalledProcessError as error:
log.error(f'Command {error.cmd} failed with code {error.returncode}. {error.stdout}')
log.step_end_with_error(f'Command {error.cmd} failed with code {error.returncode}. {error.stdout}')
sys.exit(1)
+79
View File
@@ -0,0 +1,79 @@
import ast
import sys
from configparser import ConfigParser
from pathlib import Path
from typing import Any, Optional
import toml
if sys.version_info < (3, 8):
Constant = ast.Str
def get_constant(x: ast.Str) -> str:
return x.s
else:
Constant = ast.Constant
def get_constant(x: ast.Constant) -> Any:
return x.value
class Analyzer(ast.NodeVisitor):
def __init__(self) -> None:
self.requires_python: Optional[str] = None
def visit(self, content: ast.AST) -> None:
for node in ast.walk(content):
for child in ast.iter_child_nodes(node):
child.parent = node # type: ignore
super().visit(content)
def visit_keyword(self, node: ast.keyword) -> None:
self.generic_visit(node)
if node.arg == "python_requires":
# Must not be nested in an if or other structure
# This will be Module -> Expr -> Call -> keyword
if (
not hasattr(node.parent.parent.parent, "parent") # type: ignore
and isinstance(node.value, Constant)
):
self.requires_python = get_constant(node.value)
def setup_py_python_requires(content: str) -> Optional[str]:
try:
tree = ast.parse(content)
analyzer = Analyzer()
analyzer.visit(tree)
return analyzer.requires_python or None
except Exception:
return None
def get_requires_python_str(package_dir: Path) -> Optional[str]:
"Return the python requires string from the most canonical source available, or None"
# Read in from pyproject.toml:project.requires-python
try:
info = toml.load(package_dir / 'pyproject.toml')
return str(info['project']['requires-python'])
except (FileNotFoundError, KeyError, IndexError, TypeError):
pass
# Read in from setup.cfg:options.python_requires
try:
config = ConfigParser()
config.read(package_dir / 'setup.cfg')
return str(config['options']['python_requires'])
except (FileNotFoundError, KeyError, IndexError, TypeError):
pass
try:
with open(package_dir / 'setup.py') as f:
return setup_py_python_requires(f.read())
except FileNotFoundError:
pass
return None
+3 -1
View File
@@ -41,7 +41,9 @@ python_configurations = [
{ identifier = "cp36-macosx_x86_64", version = "3.6", url = "https://www.python.org/ftp/python/3.6.8/python-3.6.8-macosx10.9.pkg" },
{ identifier = "cp37-macosx_x86_64", version = "3.7", url = "https://www.python.org/ftp/python/3.7.9/python-3.7.9-macosx10.9.pkg" },
{ identifier = "cp38-macosx_x86_64", version = "3.8", url = "https://www.python.org/ftp/python/3.8.7/python-3.8.7-macosx10.9.pkg" },
{ identifier = "cp39-macosx_x86_64", version = "3.9", url = "https://www.python.org/ftp/python/3.9.1/python-3.9.1-macosx10.9.pkg" },
{ identifier = "cp39-macosx_x86_64", version = "3.9", url = "https://www.python.org/ftp/python/3.9.1/python-3.9.1-macos11.0.pkg" },
{ identifier = "cp39-macosx_arm64", version = "3.9", url = "https://www.python.org/ftp/python/3.9.1/python-3.9.1-macos11.0.pkg" },
{ identifier = "cp39-macosx_universal2", version = "3.9", url = "https://www.python.org/ftp/python/3.9.1/python-3.9.1-macos11.0.pkg" },
{ identifier = "pp27-macosx_x86_64", version = "2.7", url = "https://downloads.python.org/pypy/pypy2.7-v7.3.3-osx64.tar.bz2" },
{ identifier = "pp36-macosx_x86_64", version = "3.6", url = "https://downloads.python.org/pypy/pypy3.6-v7.3.3-osx64.tar.bz2" },
{ identifier = "pp37-macosx_x86_64", version = "3.7", url = "https://downloads.python.org/pypy/pypy3.7-v7.3.3-osx64.tar.bz2" },
@@ -37,7 +37,7 @@ six==1.15.0
# virtualenv
typing==3.7.4.3
# via importlib-resources
virtualenv==20.3.1
virtualenv==20.4.2
# via -r cibuildwheel/resources/constraints.in
wheel==0.36.2
# via
@@ -49,7 +49,7 @@ zipp==1.2.0
# importlib-resources
# The following packages are considered to be unsafe in a requirements file:
pip==20.3.3
pip==20.3.4
# via -r cibuildwheel/resources/constraints.in
setuptools==44.1.1
# via -r cibuildwheel/resources/constraints.in
@@ -18,7 +18,7 @@ importlib-resources==3.2.1
# via virtualenv
six==1.15.0
# via virtualenv
virtualenv==20.3.1
virtualenv==20.4.2
# via -r cibuildwheel/resources/constraints.in
wheel==0.36.2
# via
@@ -30,7 +30,7 @@ zipp==1.2.0
# importlib-resources
# The following packages are considered to be unsafe in a requirements file:
pip==20.3.3
pip==20.3.4
# via -r cibuildwheel/resources/constraints.in
setuptools==50.3.2
# via -r cibuildwheel/resources/constraints.in
@@ -14,13 +14,13 @@ filelock==3.0.12
# via virtualenv
importlib-metadata==3.4.0
# via virtualenv
importlib-resources==5.0.0
importlib-resources==5.1.0
# via virtualenv
six==1.15.0
# via virtualenv
typing-extensions==3.7.4.3
# via importlib-metadata
virtualenv==20.3.1
virtualenv==20.4.2
# via -r cibuildwheel/resources/constraints.in
wheel==0.36.2
# via
@@ -32,7 +32,7 @@ zipp==3.4.0
# importlib-resources
# The following packages are considered to be unsafe in a requirements file:
pip==20.3.3
pip==21.0.1
# via -r cibuildwheel/resources/constraints.in
setuptools==51.3.3
setuptools==53.0.0
# via -r cibuildwheel/resources/constraints.in
@@ -18,7 +18,7 @@ six==1.15.0
# via virtualenv
typing-extensions==3.7.4.3
# via importlib-metadata
virtualenv==20.3.1
virtualenv==20.4.2
# via -r cibuildwheel/resources/constraints.in
wheel==0.36.2
# via
@@ -28,7 +28,7 @@ zipp==3.4.0
# via importlib-metadata
# The following packages are considered to be unsafe in a requirements file:
pip==20.3.3
pip==21.0.1
# via -r cibuildwheel/resources/constraints.in
setuptools==51.3.3
setuptools==53.0.0
# via -r cibuildwheel/resources/constraints.in
@@ -14,7 +14,7 @@ filelock==3.0.12
# via virtualenv
six==1.15.0
# via virtualenv
virtualenv==20.3.1
virtualenv==20.4.2
# via -r cibuildwheel/resources/constraints.in
wheel==0.36.2
# via
@@ -22,7 +22,7 @@ wheel==0.36.2
# delocate
# The following packages are considered to be unsafe in a requirements file:
pip==20.3.3
pip==21.0.1
# via -r cibuildwheel/resources/constraints.in
setuptools==51.3.3
setuptools==53.0.0
# via -r cibuildwheel/resources/constraints.in
@@ -14,7 +14,7 @@ filelock==3.0.12
# via virtualenv
six==1.15.0
# via virtualenv
virtualenv==20.3.1
virtualenv==20.4.2
# via -r cibuildwheel/resources/constraints.in
wheel==0.36.2
# via
@@ -22,7 +22,7 @@ wheel==0.36.2
# delocate
# The following packages are considered to be unsafe in a requirements file:
pip==20.3.3
pip==21.0.1
# via -r cibuildwheel/resources/constraints.in
setuptools==51.3.3
setuptools==53.0.0
# via -r cibuildwheel/resources/constraints.in
+3 -3
View File
@@ -14,7 +14,7 @@ filelock==3.0.12
# via virtualenv
six==1.15.0
# via virtualenv
virtualenv==20.3.1
virtualenv==20.4.2
# via -r cibuildwheel/resources/constraints.in
wheel==0.36.2
# via
@@ -22,7 +22,7 @@ wheel==0.36.2
# delocate
# The following packages are considered to be unsafe in a requirements file:
pip==20.3.3
pip==21.0.1
# via -r cibuildwheel/resources/constraints.in
setuptools==51.3.3
setuptools==53.0.0
# via -r cibuildwheel/resources/constraints.in
@@ -1,22 +1,22 @@
[x86_64]
manylinux1 = quay.io/pypa/manylinux1_x86_64:2021-01-11-28ab838
manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2021-01-12-ff3830d
manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2021-01-12-c8250d8
manylinux1 = quay.io/pypa/manylinux1_x86_64:2021-01-24-153e846
manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2021-01-24-4117e7c
manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2021-01-24-91ce2b8
[i686]
manylinux1 = quay.io/pypa/manylinux1_i686:2021-01-11-28ab838
manylinux2010 = quay.io/pypa/manylinux2010_i686:2021-01-12-ff3830d
manylinux2014 = quay.io/pypa/manylinux2014_i686:2021-01-12-c8250d8
manylinux1 = quay.io/pypa/manylinux1_i686:2021-01-24-153e846
manylinux2010 = quay.io/pypa/manylinux2010_i686:2021-01-24-4117e7c
manylinux2014 = quay.io/pypa/manylinux2014_i686:2021-01-24-91ce2b8
[pypy_x86_64]
manylinux2010 = pypywheels/manylinux2010-pypy_x86_64:2020-12-11-f1e0e80
[aarch64]
manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2021-01-12-c8250d8
manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2021-01-24-91ce2b8
[ppc64le]
manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2021-01-12-c8250d8
manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2021-01-24-91ce2b8
[s390x]
manylinux2014 = quay.io/pypa/manylinux2014_s390x:2021-01-12-c8250d8
manylinux2014 = quay.io/pypa/manylinux2014_s390x:2021-01-24-91ce2b8
+55 -10
View File
@@ -1,30 +1,29 @@
import contextlib
import fnmatch
import itertools
import os
import re
import ssl
import sys
import textwrap
import time
import urllib.request
from enum import Enum
from pathlib import Path
from time import sleep
from typing import Dict, List, NamedTuple, Optional, Set
from typing import Dict, Iterator, List, NamedTuple, Optional, Set
import bracex
import certifi
import toml
from packaging.specifiers import SpecifierSet
from packaging.version import Version
from .architecture import Architecture
from .environment import ParsedEnvironment
from .typing import PathOrStr, PlatformName
if sys.version_info < (3, 9):
from importlib_resources import files
else:
from importlib.resources import files
resources_dir = Path(__file__).parent / 'resources'
resources_dir = files('cibuildwheel') / 'resources'
get_pip_script = resources_dir / 'get-pip.py'
install_certifi_script = resources_dir / "install_certifi.py"
@@ -59,13 +58,25 @@ class IdentifierSelector:
"""
This class holds a set of build/skip patterns. You call an instance with a
build identifier, and it returns True if that identifier should be
included.
included. Only call this on valid identifiers, ones that have at least 2
numeric digits before the first dash.
"""
def __init__(self, *, build_config: str, skip_config: str):
def __init__(self, *, build_config: str, skip_config: str, requires_python: Optional[SpecifierSet] = None):
self.build_patterns = build_config.split()
self.skip_patterns = skip_config.split()
self.requires_python = requires_python
def __call__(self, build_id: str) -> bool:
# Filter build selectors by python_requires if set
if self.requires_python is not None:
py_ver_str = build_id.split('-')[0]
major = int(py_ver_str[2])
minor = int(py_ver_str[3:])
version = Version(f"{major}.{minor}.99")
if not self.requires_python.contains(version):
return False
build_patterns = itertools.chain.from_iterable(bracex.expand(p) for p in self.build_patterns)
skip_patterns = itertools.chain.from_iterable(bracex.expand(p) for p in self.skip_patterns)
@@ -84,6 +95,8 @@ class BuildSelector(IdentifierSelector):
pass
# Note that requires-python is not needed for TestSelector, as you can't test
# what you can't build.
class TestSelector(IdentifierSelector):
def __init__(self, *, skip_config: str):
super().__init__(build_config="*", skip_config=skip_config)
@@ -228,3 +241,35 @@ def detect_ci_provider() -> Optional[CIProvider]:
return CIProvider.other
else:
return None
def unwrap(text: str) -> str:
'''
Unwraps multi-line text to a single line
'''
# remove initial line indent
text = textwrap.dedent(text)
# remove leading/trailing whitespace
text = text.strip()
# remove consecutive whitespace
return re.sub(r'\s+', ' ', text)
@contextlib.contextmanager
def print_new_wheels(msg: str, output_dir: Path) -> Iterator[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 = final_contents - existing_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}" for f in new_contents), sep="\n")
+3 -1
View File
@@ -143,6 +143,8 @@ def setup_python(python_configuration: PythonConfiguration, dependency_constrain
str(installation_path / 'Scripts'),
env['PATH']
])
env['PIP_DISABLE_PIP_VERSION_CHECK'] = '1'
# update env with results from CIBW_ENVIRONMENT
env = environment.as_dictionary(prev_environment=env)
@@ -327,5 +329,5 @@ def build(options: BuildOptions) -> None:
shutil.move(str(repaired_wheel), options.output_dir)
log.build_end()
except subprocess.CalledProcessError as error:
log.error(f'Command {error.cmd} failed with code {error.returncode}. {error.stdout}')
log.step_end_with_error(f'Command {error.cmd} failed with code {error.returncode}. {error.stdout}')
sys.exit(1)
+1 -1
View File
@@ -111,7 +111,7 @@ bin/bump_version.py
You'll be prompted to enter the new version number. Update the changelog when prompted. The script will create a 'bump version' commit and version tag.
Finally, cut the release and upload to PyPI/Github.
Finally, cut the release and upload to PyPI/GitHub.
```bash
rm -rf dist
+2 -2
View File
@@ -1,7 +1,7 @@
# Fields:
# name: required, printed name
# gh: Github repo (eventually a url: could be added for non-github projects)
# stars: Github repo (optional, if different from package, such as for Twisted)
# gh: GitHub repo (eventually a url: could be added for non-github projects)
# stars: GitHub repo (optional, if different from package, such as for Twisted)
# os: Operating system list, [windows, apple, linux] (optional)
# ci: [appveyor, github, azurepipelines, circleci, gitlab, travisci] (optional)
# notes: (text, optional)
+4
View File
@@ -83,5 +83,9 @@ h1, h2, h3, h4, h5, h6 {
padding-top: 0.9em;
}
.toctree-l3 {
border-left: 10px solid transparent;
}
/* import font awesome 4 for icons */
@import url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css);
+95 -5
View File
@@ -12,7 +12,7 @@ If your wheel didn't compile, check the list below for some debugging tips.
- Windows: missing C feature. The Windows C compiler doesn't support C language features invented after 1990, so you'll have to backport your C code to C90. For me, this mostly involved putting my variable declarations at the top of the function like an animal.
- MacOS: calling cibuildwheel from a python3 script and getting a `ModuleNotFoundError`? Due to a [bug](https://bugs.python.org/issue22490) in CPython, you'll need to [unset the `__PYVENV_LAUNCHER__` variable](https://github.com/joerick/cibuildwheel/issues/133#issuecomment-478288597) before activating a venv.
- MacOS: calling cibuildwheel from a python3 script and getting a `ModuleNotFoundError`? Due to a (fixed) [bug](https://bugs.python.org/issue22490) in CPython, you'll need to [unset the `__PYVENV_LAUNCHER__` variable](https://github.com/joerick/cibuildwheel/issues/133#issuecomment-478288597) before activating a venv.
### Linux builds on Docker
@@ -24,17 +24,82 @@ Linux wheels are built in the [`manylinux` docker images](https://github.com/pyp
- Alternative dockers images can be specified with the `CIBW_MANYLINUX_X86_64_IMAGE`, `CIBW_MANYLINUX_I686_IMAGE`, and `CIBW_MANYLINUX_PYPY_X86_64_IMAGE` options to allow for a custom, preconfigured build environment for the Linux builds. See [options](options.md#manylinux-image) for more details.
### Building macOS wheels for Apple Silicon {: #apple-silicon}
`cibuildwheel` supports cross-compiling `universal2` and `arm64` wheels on `x86_64` runners. With the introduction of Apple Silicon, you now have several choices for wheels for Python 3.9+:
#### `x86_64`
The traditional wheel for Apple, loads on Intel machines, and on
Apple Silicon when running Python under Rosetta 2 emulation.
Due to a change in naming, Pip 20.3+ (or an installer using packaging 20.5+)
is required to install a binary wheel on macOS Big Sur.
#### `arm64`
The native wheel for macOS on Apple Silicon.
Requires Pip 20.3+ (or packaging 20.5+) to install.
#### `universal2`
This wheel contains both architectures, causing it to be up to twice the
size (data files do not get doubled, only compiled code). It requires
Pip 20.3 (Packaging 20.6+) to load on Intel, and Pip 21.0.1 (Packaging 20.9+)
to load on Apple Silicon.
!!! note
The dual-architecture `universal2` has a few benefits, but a key benefit
to a universal wheel is that a user can bundle these wheels into an
application and ship a single binary.
However, if you have a large library, then you might prefer to ship
the two single-arch wheels instead - `x86_64` and `arm64`. In rare cases,
you might want to build all three, but in that case, pip will not download
the universal wheels, because it prefers the most specific wheel
available.
Generally speaking, because Pip 20.3 is required for the `universal2` wheel,
most packages should provide both `x86_64` and `universal2` wheels for now.
Once Pip 20.3+ is common on macOS, then it should be possible to ship only the
`universal2` wheel.
**Apple Silicon wheels are not built by default**, but can be enabled by adding extra archs to the [`CIBW_ARCHS_MACOS` option](options.md#archs) - e.g. `x86_64 arm64 universal2`. Cross-compilation is provided by the Xcode toolchain.
!!! important
When cross-compiling on Intel, it is not possible to test `arm64` and the `arm64` part of a `universal2` wheel.
`cibuildwheel` will raise a warning to notify you of this - these warnings be be silenced by skipping testing on these platforms: `CIBW_TEST_SKIP: *_arm64 *_universal2:arm64`.
Hopefully, cross-compilation is a temporary situation. Once we have widely
available Apple Silicon CI runners, we can build and test `arm64` and
`universal2` wheels natively. That's why `universal2` wheels are not yet built
by default, and require opt-in by setting `CIBW_ARCHS_MACOS`.
!!! note
Your runner needs Xcode Command Line Tools 12.2 or later to build `universal2` or `arm64`.
So far, only CPython 3.9 supports `universal2` and `arm64` wheels.
Here's an example GitHub Actions workflow with a job that builds for Apple Silicon:
> .github/workflows/build_macos.yml
```yml
{% include "../examples/github-apple-silicon.yml" %}
```
### Building non-native architectures using emulation {: #emulation}
cibuildwheel supports building non-native architectures on Linux, via
emulation through the binfmt_misc kernel feature. The easiest way to use this
is via the [docker/setup-qemu-action][setup-qemu-action] on Github Actions or
is via the [docker/setup-qemu-action][setup-qemu-action] on GitHub Actions or
[tonistiigi/binfmt][binfmt].
[setup-qemu-action]: https://github.com/docker/setup-qemu-action
[binfmt]: https://hub.docker.com/r/tonistiigi/binfmt
Check out the following config for an example of how to set it up on Github
Check out the following config for an example of how to set it up on GitHub
Actions. Once QEMU is set up and registered, you just need to set the
`CIBW_ARCHS_LINUX` environment variable (or use the `--archs` option on
Linux), and the other architectures are emulated automatically.
@@ -44,6 +109,31 @@ Linux), and the other architectures are emulated automatically.
{% include "../examples/github-with-qemu.yml" %}
```
### Building Apple Silicon wheels on Intel {: #apple-silicon}
`cibuildwheel` supports cross-compiling `universal2` and `arm64` wheels on `x86_64` runners.
These wheels are not built by default, but can be enabled by setting the [`CIBW_ARCHS_MACOS` option](options.md#archs) to `x86_64 arm64 universal2`. Cross-compilation is provided by the Xcode toolchain.
!!! important
When cross-compiling on Intel, it is not possible to test `arm64` and the `arm64` part of a `universal2` wheel.
`cibuildwheel` will raise a warning to notify you of this - these warnings be be silenced by skipping testing on these platforms: `CIBW_TEST_SKIP: *_arm64 *_universal2:arm64`.
Hopefully, this is a temporary situation. Once we have widely available Apple Silicon CI runners, we can build and test `arm64` and `universal2` wheels more natively. That's why `universal2` wheels are not yet built by default, and require opt-in by setting `CIBW_ARCHS_MACOS`.
!!! note
Your runner image needs Xcode Command Line Tools 12.2 or later to build `universal2` and `arm64`.
So far, only CPython 3.9 supports `universal2` and `arm64` wheels.
Here's an example GitHub Actions workflow with a job that builds for Apple Silicon:
> .github/workflows/build_macos.yml
```yml
{% include "../examples/github-apple-silicon.yml" %}
```
### Building packages with optional C extensions
`cibuildwheel` defines the environment variable `CIBUILDWHEEL` to the value `1` allowing projects for which the C extension is optional to make it mandatory when building wheels.
@@ -113,7 +203,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:
```yaml
uses: joerick/cibuildwheel@v1.8.0
uses: joerick/cibuildwheel@v1.9.0
```
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.
@@ -139,7 +229,7 @@ The second option, and the only one that supports other CI systems, is using a `
```bash
# requirements-cibw.txt
cibuildwheel==1.8.0
cibuildwheel==1.9.0
```
Then your install step would have `python -m pip install -r requirements-cibw.txt` in it. Your `dependabot.yml` file could look like this:
+129 -36
View File
@@ -64,7 +64,7 @@ Default: `auto`
For `linux` you need Docker running, on macOS or Linux. For `macos`, you need a Mac machine, and note that this script is going to automatically install MacPython on your system, so don't run on your development machine. For `windows`, you need to run in Windows, and `cibuildwheel` will install required versions of Python to `C:\cibw\python` using NuGet.
This option can also be set using the command-line option `--platform`.
This option can also be set using the [command-line option](#command-line) `--platform`.
### `CIBW_BUILD`, `CIBW_SKIP` {: #build-skip}
@@ -155,35 +155,117 @@ CIBW_SKIP: pp*
}
</style>
### `CIBW_ARCHS_LINUX` {: #archs}
> Build non-native architectures
### `CIBW_ARCHS` {: #archs}
> Change the architectures built on your machine by default.
A space-separated list of architectures to build. Use this in conjunction with
emulation, such as that provided by [docker/setup-qemu-action][setup-qemu-action]
or [tonistiigi/binfmt][binfmt], to build architectures other than those your
machine natively supports.
A space-separated list of architectures to build.
Options: `auto` `native` `all` `x86_64` `i686` `aarch64` `ppc64le` `s390x`
On macOS, this option can be used to cross-compile between `x86_64`,
`universal2` and `arm64` for Apple Silicon support.
Default: `auto`, meaning the native archs supported on the build machine. For
example, on an `x86_64` machine, `auto` expands to `x86_64` and `i686`.
On Linux, this option can be used to build non-native architectures under
emulation. See [this guide](faq.md#emulation) for more information.
`native` will only build on the exact architecture you currently are on; it will
not add `i686` for `x86_64`.
Options:
`all` will expand to all known architectures; remember to use build selectors
to limit builds for each job; this list could grow in the future.
- Linux: `x86_64` `i686` `aarch64` `ppc64le` `s390x`
- macOS: `x86_64` `arm64` `universal2`
- Windows: `AMD64` `x86`
- `auto`: The default archs for your machine - see the table below.
- `auto64`: Just the 64-bit auto archs
- `auto32`: Just the 32-bit auto archs
- `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
to use [CIBW_BUILD](#build-skip) with this option to target specific
architectures via build selectors.
Default: `auto`
| Runner | `native` | `auto` | `auto64` | `auto32` |
|---|---|---|---|---|
| Linux / Intel | `x86_64` | `x86_64` `i686` | `x86_64` | `i686` |
| Windows / Intel | `AMD64` | `AMD64` `x86` | `AMD64` | `x86` |
| macOS / Intel | `x86_64` | `x86_64` | `x86_64` | |
| macOS / Apple Silicon | `arm64` | `arm64` `universal2` | `arm64` `universal2`| |
If not listed above, `auto` is the same as `native`.
[setup-qemu-action]: https://github.com/docker/setup-qemu-action
[binfmt]: https://hub.docker.com/r/tonistiigi/binfmt
Platform-specific variants also available:<br/>
`CIBW_ARCHS_MACOS` | `CIBW_ARCHS_WINDOWS` | `CIBW_ARCHS_LINUX`
This option can also be set using the [command-line option](#command-line) `--archs`.
#### Examples
```yaml
# On an intel runner with qemu installed, build Intel and ARM wheels
# Build `universal2` and `arm64` wheels on an Intel runner.
# Note that the `arm64` wheel and the `arm64` part of the `universal2`
# wheel cannot be tested in this configuration.
CIBW_ARCHS_MACOS: "x86_64 universal2 arm64"
# On an Linux Intel runner with qemu installed, build Intel and ARM wheels
CIBW_ARCHS_LINUX: "auto aarch64"
```
### `CIBW_PROJECT_REQUIRES_PYTHON` {: #requires-python}
> Manually set the Python compatibility of your project
By default, cibuildwheel reads your package's Python compatibility from
`pyproject.toml` following [PEP621](https://www.python.org/dev/peps/pep-0621/)
or from `setup.cfg`; finally it will try to inspect the AST of `setup.py` for a
simple keyword assignment in a top level function call. If you need to override
this behaviour for some reason, you can use this option.
When setting this option, the syntax is the same as `project.requires-python`,
using 'version specifiers' like `>=3.6`, according to
[PEP440](https://www.python.org/dev/peps/pep-0440/#version-specifiers).
Default: reads your package's Python compatibility from `pyproject.toml`
(`project.requires-python`) or `setup.cfg` (`options.python_requires`) or
`setup.py` `setup(python_requires="...")`. If not found, cibuildwheel assumes
the package is compatible with all versions of Python that it can build.
!!! note
Rather than using this option, it's recommended you set
`project.requires-python` in `pyproject.toml` instead:
Example `pyproject.toml`:
[project]
requires-python = ">=3.6"
# Aside - in pyproject.toml you should always specify minimal build
# system options, like this:
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
Currently, setuptools has not yet added support for reading this value from
pyproject.toml yet, and so does not copy it to Requires-Python in the wheel
metadata. This mechanism is used by `pip` to scan through older versions of
your package until it finds a release compatible with the curernt version
of Python compatible when installing, so it is an important value to set if
you plan to drop support for a version of Python in the future.
If you don't want to list this value twice, you can also use the setuptools
specific location in `setup.cfg` and cibuildwheel will detect it from
there. Example `setup.cfg`:
[options]
python_requires = ">=3.6"
#### Examples
```yaml
CIBW_PROJECT_REQUIRES_PYTHON: ">=3.6"
```
## Build customization
### `CIBW_ENVIRONMENT` {: #environment}
@@ -274,27 +356,26 @@ CIBW_BEFORE_BUILD: "{package}/script/prepare_for_build.sh"
```
!!! note
If you need dependencies installed for the build, we recommend using pyproject.toml. This is an example pyproject.toml file:
If you need dependencies installed for the build, we recommend using
`pyproject.toml`. This is an example `pyproject.toml` file:
[build-system]
requires = [
"setuptools>=42",
"wheel",
"Cython",
"numpy==1.11.3; python_version<='3.6'",
"numpy==1.14.5; python_version=='3.7'",
"numpy==1.17.3; python_version=='3.8'",
"numpy==1.19.4; python_version>='3.9'",
"numpy==1.13.3; python_version<'3.5'",
"oldest-supported-numpy; python_version>='3.5'",
]
build-backend = "setuptools.build_meta"
This [PEP 517][]/[PEP 518][] style build allows you to completely control the
build environment in cibuildwheel, [PyPA-build][], and pip, doesn't force
downstream users to install anything they don't need, and lets you do more
complex pinning (Cython, for example, requires a wheel to be built with an
equal or earlier version of NumPy; pinning in this way is the only way to
ensure your module works on all available NumPy versions).
This [PEP 517][]/[PEP 518][] style build allows you to completely control
the build environment in cibuildwheel, [PyPA-build][], and pip, doesn't
force downstream users to install anything they don't need, and lets you do
more complex pinning (Cython, for example, requires a wheel to be built
with an equal or earlier version of NumPy; pinning in this way is the only
way to ensure your module works on all available NumPy versions).
[PyPA-build]: https://pypa-build.readthedocs.io/en/latest/
[PEP 517]: https://www.python.org/dev/peps/pep-0517/
@@ -306,7 +387,7 @@ CIBW_BEFORE_BUILD: "{package}/script/prepare_for_build.sh"
Default:
- on Linux: `'auditwheel repair -w {dest_dir} {wheel}'`
- on macOS: `'delocate-listdeps {wheel} && delocate-wheel --require-archs x86_64 -w {dest_dir} {wheel}'`
- on macOS: `'delocate-listdeps {wheel} && delocate-wheel --require-archs {delocate_archs} -w {dest_dir} {wheel}'`
- on Windows: `''`
A shell command to repair a built wheel by copying external library dependencies into the wheel tree and relinking them.
@@ -315,7 +396,8 @@ The command is run on each built wheel (except for pure Python ones) before test
The following placeholders must be used inside the command and will be replaced by `cibuildwheel`:
- `{wheel}` for the absolute path to the built wheel
- `{dest_dir}` for the absolute path of the directory where to create the repaired wheel.
- `{dest_dir}` for the absolute path of the directory where to create the repaired wheel
- `{delocate_archs}` (macOS only) comma-separated list of architectures in the wheel.
The command is run in a shell, so you can run multiple commands like `cmd1 && cmd2`.
@@ -427,10 +509,17 @@ CIBW_DEPENDENCY_VERSIONS: ./constraints.txt
### `CIBW_TEST_COMMAND` {: #test-command}
> Execute a shell command to test each built wheel
Shell command to run tests after the build. The wheel will be installed automatically and available for import from the tests. To ensure the wheel is imported by your tests (instead of your source copy), tests are run from a different directory. Use the placeholders `{project}` and `{package}` when specifying paths in your project.
Shell command to run tests after the build. The wheel will be installed
automatically and available for import from the tests. To ensure the wheel is
imported by your tests (instead of your source copy), tests are run from a
different directory. Use the placeholders `{project}` and `{package}` when
specifying paths in your project. If this variable is not set, your wheel will
not be installed after building.
- `{project}` is an absolute path to the project root - the working directory where cibuildwheel was called.
- `{package}` is the path to the package being built - the `package_dir` argument supplied to cibuildwheel on the command line.
- `{project}` is an absolute path to the project root - the working directory
where cibuildwheel was called.
- `{package}` is the path to the package being built - the `package_dir`
argument supplied to cibuildwheel on the command line.
The command is run in a shell, so you can write things like `cmd1 && cmd2`.
@@ -445,6 +534,9 @@ CIBW_TEST_COMMAND: nosetests {project}/tests
# run the package tests using `pytest`
CIBW_TEST_COMMAND: pytest {package}/tests
# trigger an install of the package, but run nothing of note
CIBW_TEST_COMMAND: "echo Wheel installed"
```
@@ -521,11 +613,16 @@ CIBW_TEST_EXTRAS: test,qt
This will skip testing on any identifiers that match the given skip patterns (see [`CIBW_SKIP`](#build-skip)). This can be used to mask out tests for wheels that have missing dependencies upstream that are slow or hard to build, or to mask up slow tests on emulated architectures.
With macOS `universal2` wheels, you can also skip the the individual archs inside the wheel using an `:arch` suffix. For example, `cp39-macosx_universal2:x86_64` or `cp39-macosx_universal2:arm64`.
#### Examples
```yaml
# Will avoid testing on emulated architectures
CIBW_TEST_SKIP: "*-manylinux_{aarch64,ppc64le,s390x}"
# Skip trying to test arm64 builds on Intel Macs
CIBW_TEST_SKIP: "*-macosx_arm64 *-macosx_universal2:arm64"
```
@@ -547,7 +644,7 @@ CIBW_BUILD_VERBOSITY: 1
```
## Command line options
## Command line options {: #command-line}
```text
usage: cibuildwheel [-h] [--platform {auto,linux,macos,windows}]
@@ -591,10 +688,6 @@ optional arguments:
```
<style>
.toctree-l3 {
border-left: 10px solid transparent;
}
.options-toc {
display: grid;
grid-auto-columns: fit-content(20%) 1fr;
+3 -3
View File
@@ -4,20 +4,20 @@ title: 'Setup'
# GitHub Actions [linux/mac/windows] {: #github-actions}
To build Linux, Mac, and Windows wheels using Github Actions, create a `.github/workflows/build.yml` file in your repo.
To build Linux, Mac, and Windows wheels using GitHub Actions, create a `.github/workflows/build.yml` file in your repo.
> build.yml
```yaml
{% include "../examples/github-minimal.yml" %}
```
Commit this file, and push to Github - either to your default branch, or to a PR branch. The build should start automatically.
Commit this file, and push to GitHub - either to your default branch, or to a PR branch. The build should start automatically.
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/joerick/cibuildwheel/blob/master/examples/github-deploy.yml) extends this minimal example with a demonstration of how to automatically upload the built wheels to PyPI.
You can also use cibuildwheel directly as an action with `uses: joerick/cibuildwheel@v1.8.0`; this combines the download and run steps into a single action, and command line arguments are available via `with:`. This makes it easy to manage cibuildwheel updates via normal actions update mechanisms like dependabot, see [Automatic updates](faq.md#automatic-updates).
You can also use cibuildwheel directly as an action with `uses: joerick/cibuildwheel@v1.9.0`; this combines the download and run steps into a single action, and command line arguments are available via `with:`. This makes it easy to manage cibuildwheel updates via normal actions update mechanisms like dependabot, see [Automatic updates](faq.md#automatic-updates).
# Azure Pipelines [linux/mac/windows] {: #azure-pipelines}
+1 -1
View File
@@ -12,7 +12,7 @@ stack: python 3.7
init:
- cmd: set PATH=C:\Python37;C:\Python37\Scripts;%PATH%
install: python -m pip install cibuildwheel==1.8.0
install: python -m pip install cibuildwheel==1.9.0
build_script: python -m cibuildwheel --output-dir wheelhouse
+3 -3
View File
@@ -6,7 +6,7 @@ jobs:
- bash: |
set -o errexit
python3 -m pip install --upgrade pip
pip3 install cibuildwheel==1.8.0
pip3 install cibuildwheel==1.9.0
displayName: Install dependencies
- bash: cibuildwheel --output-dir wheelhouse .
displayName: Build wheels
@@ -20,7 +20,7 @@ jobs:
- bash: |
set -o errexit
python3 -m pip install --upgrade pip
python3 -m pip install cibuildwheel==1.8.0
python3 -m pip install cibuildwheel==1.9.0
displayName: Install dependencies
- bash: cibuildwheel --output-dir wheelhouse .
displayName: Build wheels
@@ -36,7 +36,7 @@ jobs:
- bash: |
set -o errexit
python -m pip install --upgrade pip
pip install cibuildwheel==1.8.0
pip install cibuildwheel==1.9.0
displayName: Install dependencies
- bash: cibuildwheel --output-dir wheelhouse .
displayName: Build wheels
+2 -2
View File
@@ -11,7 +11,7 @@ jobs:
- run:
name: Build the Linux wheels.
command: |
pip3 install --user cibuildwheel==1.8.0
pip3 install --user cibuildwheel==1.9.0
cibuildwheel --output-dir wheelhouse
- store_artifacts:
path: wheelhouse/
@@ -25,7 +25,7 @@ jobs:
- run:
name: Build the OS X wheels.
command: |
pip3 install cibuildwheel==1.8.0
pip3 install cibuildwheel==1.9.0
cibuildwheel --output-dir wheelhouse
- store_artifacts:
path: wheelhouse/
+19
View File
@@ -0,0 +1,19 @@
name: Build
on: [push, pull_request]
jobs:
build_wheels_macos:
name: Build wheels on macos-10.15
runs-on: macos-10.15
steps:
- uses: actions/checkout@v2
- name: Build wheels
uses: joerick/cibuildwheel@v1.9.0
env:
CIBW_ARCHS_MACOS: x86_64 universal2
- uses: actions/upload-artifact@v2
with:
path: ./wheelhouse/*.whl
+5 -11
View File
@@ -16,7 +16,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-18.04, windows-2019, macos-10.15]
os: [ubuntu-20.04, windows-2019, macos-10.15]
steps:
- uses: actions/checkout@v2
@@ -24,20 +24,14 @@ jobs:
- uses: actions/setup-python@v2
name: Install Python
with:
python-version: '3.7'
- name: Install cibuildwheel
run: |
python -m pip install cibuildwheel==1.8.0
python-version: '3.8'
- name: Install Visual C++ for Python 2.7
if: runner.os == 'Windows'
run: |
choco install vcpython27 -f -y
run: choco install vcpython27 -f -y
- name: Build wheels
run: |
python -m cibuildwheel --output-dir wheelhouse
uses: joerick/cibuildwheel@v1.9.0
- uses: actions/upload-artifact@v2
with:
@@ -52,7 +46,7 @@ jobs:
- uses: actions/setup-python@v2
name: Install Python
with:
python-version: '3.7'
python-version: '3.8'
- name: Build sdist
run: python setup.py sdist
+6 -14
View File
@@ -8,28 +8,20 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-18.04, windows-2019, macos-10.15]
os: [ubuntu-20.04, windows-2019, macos-10.15]
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
name: Install Python
with:
python-version: '3.7'
- name: Install cibuildwheel
run: |
python -m pip install cibuildwheel==1.8.0
- name: Install Visual C++ for Python 2.7
if: runner.os == 'Windows'
run: |
choco install vcpython27 -f -y
run: choco install vcpython27 -f -y
- name: Build wheels
run: |
python -m cibuildwheel --output-dir wheelhouse
uses: joerick/cibuildwheel@v1.9.0
# to supply options, put them in 'env', like:
# env:
# CIBW_SOME_OPTION: value
- uses: actions/upload-artifact@v2
with:
+3 -9
View File
@@ -8,7 +8,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-18.04, windows-2019, macos-10.15]
os: [ubuntu-20.04, windows-2019, macos-10.15]
steps:
- uses: actions/checkout@v2
@@ -18,14 +18,9 @@ jobs:
with:
python-version: '3.7'
- name: Install cibuildwheel
run: |
python -m pip install cibuildwheel==1.8.0
- name: Install Visual C++ for Python 2.7
if: runner.os == 'Windows'
run: |
choco install vcpython27 -f -y
run: choco install vcpython27 -f -y
- name: Set up QEMU
if: runner.os == 'Linux'
@@ -34,8 +29,7 @@ jobs:
platforms: all
- name: Build wheels
run: |
python -m cibuildwheel --output-dir wheelhouse
uses: joerick/cibuildwheel@v1.9.0
env:
# configure cibuildwheel to build native archs ('auto'), and some
# emulated ones
+2 -2
View File
@@ -14,13 +14,13 @@ jobs:
- os: windows
language: shell
before_install:
- choco install python --version 3.8.0
- choco install python --version 3.8.6
- export PATH="/c/Python38:/c/Python38/Scripts:$PATH"
# make sure it's on PATH as 'python3'
- ln -s /c/Python38/python.exe /c/Python38/python3.exe
install:
- python3 -m pip install cibuildwheel==1.8.0
- python3 -m pip install cibuildwheel==1.9.0
script:
# build the wheels, put them into './wheelhouse'
+2 -2
View File
@@ -20,13 +20,13 @@ jobs:
- os: windows
language: shell
before_install:
- choco install python --version 3.8.0
- choco install python --version 3.8.6
- export PATH="/c/Python38:/c/Python38/Scripts:$PATH"
# make sure it's on PATH as 'python3'
- ln -s /c/Python38/python.exe /c/Python38/python3.exe
install:
- python3 -m pip install cibuildwheel==1.8.0
- python3 -m pip install cibuildwheel==1.9.0
script:
# build the wheels, put them into './wheelhouse'
+4 -4
View File
@@ -14,7 +14,7 @@ python:
before_install:
- |
if [[ "$TRAVIS_OS_NAME" = windows ]]; then
choco install python --version 3.8.0
choco install python --version 3.8.6
export PATH="/c/Python38:/c/Python38/Scripts:$PATH"
# make sure it's on PATH as 'python3'
ln -s /c/Python38/python.exe /c/Python38/python3.exe
@@ -55,7 +55,7 @@ jobs:
- stage: deploy
name: Build and deploy Linux wheels
services: docker
install: python3 -m pip install cibuildwheel==1.8.0
install: python3 -m pip install cibuildwheel==1.9.0
script: python3 -m cibuildwheel --output-dir wheelhouse
after_success: |
python3 -m pip install twine
@@ -65,7 +65,7 @@ jobs:
name: Build and deploy macOS wheels
os: osx
language: shell
install: python3 -m pip install cibuildwheel==1.8.0
install: python3 -m pip install cibuildwheel==1.9.0
script: python3 -m cibuildwheel --output-dir wheelhouse
after_success: |
python3 -m pip install twine
@@ -75,7 +75,7 @@ jobs:
name: Build and deploy Windows wheels
os: windows
language: shell
install: python3 -m pip install cibuildwheel==1.8.0
install: python3 -m pip install cibuildwheel==1.9.0
script: python3 -m cibuildwheel --output-dir wheelhouse
after_success: |
python3 -m pip install twine
+4
View File
@@ -5,3 +5,7 @@ requires = [
]
build-backend = "setuptools.build_meta"
[tool.black]
line-length = 120
target-version = ['py36']
+1 -1
View File
@@ -1 +1 @@
-e .[dev]
-e .[dev,docs]
+9 -23
View File
@@ -1,7 +1,7 @@
[metadata]
name = cibuildwheel
version = 1.8.0
version = 1.9.0
description = Build Python wheels on CI with minimal configuration.
long_description = file: README.md
long_description_content_type = text/markdown
@@ -32,29 +32,12 @@ install_requires =
toml
certifi
bracex
packaging
typing_extensions; python_version < '3.8'
importlib_resources>=1.4; python_version < '3.9'
[options.package_data]
cibuildwheel = resources/*
[options.extras_require]
dev =
click
mkdocs-include-markdown-plugin==2.1.1
mkdocs==1.0.4
mypy
pip-tools
pygithub
pymdown-extensions
pytest
pytest-timeout
pyyaml
requests
typing-extensions
packaging>=20.8
rich>=9.6
[options.packages.find]
include =
cibuildwheel
@@ -83,15 +66,15 @@ exclude =
junit_family=xunit2
[mypy]
python_version = 3.6
files = cibuildwheel,test,unit_test,bin
python_version = 3.7
files = cibuildwheel/*.py,test/**/*.py,unit_test/**/*.py,bin/*.py
warn_unused_configs = True
warn_redundant_casts = True
[mypy-test.*]
[mypy-test.*,mypy-unit_test.*]
check_untyped_defs = True
[mypy-cibuildwheel.*,unit_test.*]
[mypy-cibuildwheel.*]
disallow_any_generics = True
disallow_subclassing_any = True
disallow_untyped_calls = True
@@ -123,6 +106,9 @@ ignore_missing_imports = True
[mypy-bracex.*]
ignore_missing_imports = True
[mypy-importlib_resources.*]
ignore_missing_imports = True
[tool:isort]
profile=black
multi_line_output=3
+29 -1
View File
@@ -1,3 +1,31 @@
from setuptools import setup
setup()
extras = {
"docs": [
"mkdocs-include-markdown-plugin==2.1.1",
"mkdocs==1.0.4",
"pymdown-extensions",
],
"test": [
"jinja2",
"pytest>=4",
"pytest-timeout",
],
"dev": [
"click",
"ghapi",
"mypy>=0.800",
"packaging>=20.8",
"pip-tools",
"pygithub",
"pyyaml",
"requests",
"rich>=9.6",
"typing-extensions",
],
}
extras["all"] = sum(extras.values(), [])
extras["dev"] += extras["test"]
setup(extras_require=extras)
+7
View File
@@ -55,6 +55,13 @@ def test_pinned_versions(tmp_path, python_version):
if utils.IS_WINDOWS_RUNNING_ON_TRAVIS and python_version == '2.7':
pytest.skip('Windows + Travis CI requires a workaround')
is_running_on_macos_11_or_later = (
utils.platform == 'macos' and utils.get_macos_version() >= (10, 16)
)
if is_running_on_macos_11_or_later and python_version == '3.5':
pytest.skip('CPython 3.5 doesn\'t work on macOS Big Sur+')
project_dir = tmp_path / 'project'
project_with_expected_version_checks.generate(project_dir)
+119
View File
@@ -0,0 +1,119 @@
import platform
import subprocess
from typing import Tuple
import pytest
from . import test_projects, utils
basic_project = test_projects.new_c_project()
ALL_MACOS_WHEELS = (
utils.expected_wheels('spam', '0.1.0', machine_arch='x86_64')
+ utils.expected_wheels('spam', '0.1.0', machine_arch='arm64')
)
def get_xcode_version() -> Tuple[int, int]:
output = subprocess.check_output(
['xcodebuild', '-version'],
universal_newlines=True,
)
lines = output.splitlines()
_, version_str = lines[0].split()
version_parts = version_str.split('.')
return (int(version_parts[0]), int(version_parts[1]))
def test_cross_compiled_build(tmp_path):
if utils.platform != 'macos':
pytest.skip('this test is only relevant to macos')
if get_xcode_version() < (12, 2):
pytest.skip('this test only works with Xcode 12.2 or greater')
project_dir = tmp_path / 'project'
basic_project.generate(project_dir)
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
'CIBW_BUILD': 'cp39-*',
'CIBW_ARCHS': 'x86_64, universal2, arm64',
})
expected_wheels = [w for w in ALL_MACOS_WHEELS if 'cp39' in w]
assert set(actual_wheels) == set(expected_wheels)
@pytest.mark.parametrize('build_universal2', [False, True])
def test_cross_compiled_test(tmp_path, capfd, build_universal2):
if utils.platform != 'macos':
pytest.skip('this test is only relevant to macos')
if get_xcode_version() < (12, 2):
pytest.skip('this test only works with Xcode 12.2 or greater')
project_dir = tmp_path / 'project'
basic_project.generate(project_dir)
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
'CIBW_BUILD': 'cp39-*',
'CIBW_TEST_COMMAND': '''python -c "import platform; print('running tests on ' + platform.machine())"''',
'CIBW_ARCHS': 'universal2' if build_universal2 else 'x86_64 arm64',
})
captured = capfd.readouterr()
if platform.machine() == 'x86_64':
# ensure that tests were run on only x86_64
assert 'running tests on x86_64' in captured.out
assert 'running tests on arm64' not in captured.out
if build_universal2:
assert 'While universal2 wheels can be built on x86_64, the arm64 part of them cannot currently be tested' in captured.err
else:
assert 'While arm64 wheels can be built on x86_64, they cannot be tested' in captured.err
elif platform.machine() == 'arm64':
# ensure that tests were run on both x86_64 and arm64
assert 'running tests on x86_64' in captured.out
assert 'running tests on arm64' in captured.out
if build_universal2:
expected_wheels = [w for w in ALL_MACOS_WHEELS if 'cp39' in w and 'universal2' in w]
else:
expected_wheels = [w for w in ALL_MACOS_WHEELS if 'cp39' in w and 'universal2' not in w]
assert set(actual_wheels) == set(expected_wheels)
@pytest.mark.parametrize('skip_arm64_test', [False, True])
def test_universal2_testing(tmp_path, capfd, skip_arm64_test):
if utils.platform != 'macos':
pytest.skip('this test is only relevant to macos')
if get_xcode_version() < (12, 2):
pytest.skip('this test only works with Xcode 12.2 or greater')
if platform.machine() != 'x86_64':
pytest.skip('this test only works on x86_64')
project_dir = tmp_path / 'project'
basic_project.generate(project_dir)
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
'CIBW_BUILD': 'cp39-*',
'CIBW_TEST_COMMAND': '''python -c "import platform; print('running tests on ' + platform.machine())"''',
'CIBW_ARCHS': 'universal2',
'CIBW_TEST_SKIP': '*_universal2:arm64' if skip_arm64_test else '',
})
captured = capfd.readouterr()
if platform.machine() == 'x86_64':
assert 'running tests on x86_64' in captured.out
assert 'running tests on arm64' not in captured.out
warning_message = 'While universal2 wheels can be built on x86_64, the arm64 part of them cannot currently be tested'
if skip_arm64_test:
assert warning_message not in captured.err
else:
assert warning_message in captured.err
expected_wheels = [w for w in ALL_MACOS_WHEELS if 'cp39' in w and 'universal2' in w]
assert set(actual_wheels) == set(expected_wheels)
+46 -3
View File
@@ -10,10 +10,8 @@ import shutil
import subprocess
import sys
from contextlib import contextmanager
from pathlib import Path
from tempfile import mkdtemp
IS_WINDOWS_RUNNING_ON_AZURE = Path('C:\\hostedtoolcache').exists()
IS_WINDOWS_RUNNING_ON_TRAVIS = os.environ.get('TRAVIS_OS_NAME') == 'windows'
@@ -75,6 +73,19 @@ def cibuildwheel_run(project_path, package_dir='.', env=None, add_env=None, outp
return wheels
def _get_arm64_macosx_deployment_target(macosx_deployment_target: str) -> str:
'''
The first version of macOS that supports arm is 11.0. So the wheel tag
cannot contain an earlier deployment target, even if
MACOSX_DEPLOYMENT_TARGET sets it.
'''
version_tuple = tuple(map(int, macosx_deployment_target.split('.')))
if version_tuple <= (11, 0):
return '11.0'
else:
return macosx_deployment_target
def expected_wheels(package_name, package_version, manylinux_versions=None,
macosx_deployment_target='10.9', machine_arch=None, *,
exclude_27=IS_WINDOWS_RUNNING_ON_TRAVIS):
@@ -103,6 +114,17 @@ def expected_wheels(package_name, package_version, manylinux_versions=None,
if platform == 'linux':
python_abi_tags.append('cp27-cp27mu') # python 2.7 has 2 different ABI on manylinux
if platform == 'macos' and get_macos_version() >= (10, 16):
# 10.16 is sometimes reported as the macOS version on macOS 11.
# CPython 3.5 doesn't work on macOS 11.
python_abi_tags.remove('cp35-cp35m')
# pypy not supported on macOS 11.
python_abi_tags = [t for t in python_abi_tags if not t.startswith('pp')]
if platform == 'macos' and machine_arch == 'arm64':
# currently, arm64 macs are only supported by cp39
python_abi_tags = ['cp39-cp39']
wheels = []
for python_abi_tag in python_abi_tags:
@@ -127,7 +149,16 @@ def expected_wheels(package_name, package_version, manylinux_versions=None,
platform_tags = ['win32']
elif platform == 'macos':
platform_tags = [f'macosx_{macosx_deployment_target.replace(".", "_")}_x86_64']
if python_abi_tag == 'cp39-cp39' and machine_arch == 'arm64':
arm64_macosx_deployment_target = _get_arm64_macosx_deployment_target(macosx_deployment_target)
platform_tags = [
f'macosx_{macosx_deployment_target.replace(".", "_")}_universal2',
f'macosx_{arm64_macosx_deployment_target.replace(".", "_")}_arm64',
]
else:
platform_tags = [
f'macosx_{macosx_deployment_target.replace(".", "_")}_x86_64',
]
else:
raise Exception('unsupported platform')
@@ -143,6 +174,18 @@ def expected_wheels(package_name, package_version, manylinux_versions=None,
return wheels
def get_macos_version():
'''
Returns the macOS major/minor version, as a tuple, e.g. (10, 15) or (11, 0)
These tuples can be used in comparisons, e.g.
(10, 14) <= (11, 0) == True
(11, 2) <= (11, 0) != True
'''
version_str, _, _ = pm.mac_ver()
return tuple(map(int, version_str.split(".")[:2]))
platform = None
if 'CIBW_PLATFORM' in os.environ:
View File
+2 -5
View File
@@ -1,11 +1,8 @@
import pytest
import toml
from cibuildwheel.util import resources_dir
Version = pytest.importorskip("packaging.version").Version
from packaging.version import Version
from cibuildwheel.extra import InlineArrayDictEncoder # noqa: E402
from cibuildwheel.util import resources_dir
def test_compare_configs():
+35
View File
@@ -1,3 +1,5 @@
from packaging.specifiers import SpecifierSet
from cibuildwheel.util import BuildSelector
@@ -69,3 +71,36 @@ def test_build_braces():
assert build_selector('cp37-manylinux1_x86_64')
assert not build_selector('cp38-manylinux1_x86_64')
assert not build_selector('cp39-manylinux1_x86_64')
def test_build_limited_python():
build_selector = BuildSelector(build_config="*", skip_config="", requires_python=SpecifierSet(">=3.6"))
assert not build_selector('cp27-manylinux1_x86_64')
assert build_selector('cp36-manylinux1_x86_64')
assert build_selector('cp37-manylinux1_x86_64')
assert not build_selector('cp27-manylinux1_i686')
assert build_selector('cp36-manylinux1_i686')
assert build_selector('cp37-manylinux1_i686')
assert not build_selector('cp27-win32')
assert build_selector('cp36-win32')
assert build_selector('cp37-win32')
assert not build_selector('pp27-win32')
assert build_selector('pp36-win32')
assert build_selector('pp37-win32')
def test_build_limited_python_partial():
build_selector = BuildSelector(build_config="*", skip_config="", requires_python=SpecifierSet(">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"))
assert build_selector('cp27-manylinux1_x86_64')
assert not build_selector('cp35-manylinux1_x86_64')
assert build_selector('cp36-manylinux1_x86_64')
def test_build_limited_python_patch():
build_selector = BuildSelector(build_config="*", skip_config="", requires_python=SpecifierSet(">=2.7.9"))
assert build_selector('cp27-manylinux1_x86_64')
assert build_selector('cp36-manylinux1_x86_64')
assert build_selector('cp37-manylinux1_x86_64')
View File
+13
View File
@@ -1,3 +1,4 @@
import contextlib
import platform as platform_module
import subprocess
import sys
@@ -58,6 +59,15 @@ def fake_package_dir(monkeypatch):
return args
@pytest.fixture(autouse=True)
def disable_print_wheels(monkeypatch):
@contextlib.contextmanager
def empty_cm(*args, **kwargs):
yield
monkeypatch.setattr(util, 'print_new_wheels', empty_cm)
@pytest.fixture
def allow_empty(request, monkeypatch, fake_package_dir):
monkeypatch.setattr(sys, 'argv', fake_package_dir + ['--allow-empty'])
@@ -73,6 +83,9 @@ def platform(request, monkeypatch):
else:
monkeypatch.setattr(platform_module, 'machine', lambda: 'x86_64')
if platform_value == 'macos':
monkeypatch.setattr(macos, 'get_macos_version', lambda: (11, 1))
return platform_value
+3 -3
View File
@@ -51,8 +51,8 @@ def test_build_selector(platform, intercepted_build_args, monkeypatch, allow_emp
intercepted_build_selector = intercepted_build_args.args[0].build_selector
assert isinstance(intercepted_build_selector, BuildSelector)
assert intercepted_build_selector('build-this')
assert not intercepted_build_selector('skip-that')
assert intercepted_build_selector('build24-this')
assert not intercepted_build_selector('skip65-that')
# This unit test is just testing the options of 'main'
# Unit tests for BuildSelector are in build_selector_test.py
@@ -102,7 +102,7 @@ def get_default_repair_command(platform):
if platform == 'linux':
return 'auditwheel repair -w {dest_dir} {wheel}'
elif platform == 'macos':
return 'delocate-listdeps {wheel} && delocate-wheel --require-archs x86_64 -w {dest_dir} {wheel}'
return 'delocate-listdeps {wheel} && delocate-wheel --require-archs {delocate_archs} -w {dest_dir} {wheel}'
elif platform == 'windows':
return ''
else:
+37 -3
View File
@@ -1,10 +1,11 @@
import sys
import pytest
from conftest import MOCK_PACKAGE_DIR # noqa: I100
from cibuildwheel.__main__ import main
from cibuildwheel.util import Architecture
from cibuildwheel.architecture import Architecture
from .conftest import MOCK_PACKAGE_DIR
def test_unknown_platform_non_ci(monkeypatch, capsys):
@@ -130,6 +131,39 @@ def test_archs_platform_native(platform, intercepted_build_args, monkeypatch):
assert build_options.architectures == {Architecture.x86_64}
def test_archs_platform_auto64(platform, intercepted_build_args, monkeypatch):
monkeypatch.setenv('CIBW_ARCHS', 'auto64')
main()
build_options = intercepted_build_args.args[0]
if platform == 'linux':
assert build_options.architectures == {Architecture.x86_64}
elif platform == 'windows':
assert build_options.architectures == {Architecture.AMD64}
elif platform == 'macos':
assert build_options.architectures == {Architecture.x86_64}
def test_archs_platform_auto32(platform, intercepted_build_args, monkeypatch):
monkeypatch.setenv('CIBW_ARCHS', 'auto32')
if platform == 'macos':
with pytest.raises(SystemExit) as exit:
main()
assert exit.value.args == (4,)
else:
main()
build_options = intercepted_build_args.args[0]
if platform == 'linux':
assert build_options.architectures == {Architecture.i686}
elif platform == 'windows':
assert build_options.architectures == {Architecture.x86}
def test_archs_platform_all(platform, intercepted_build_args, monkeypatch):
monkeypatch.setenv('CIBW_ARCHS', 'all')
@@ -141,4 +175,4 @@ def test_archs_platform_all(platform, intercepted_build_args, monkeypatch):
elif platform == 'windows':
assert build_options.architectures == {Architecture.x86, Architecture.AMD64}
elif platform == 'macos':
assert build_options.architectures == {Architecture.x86_64}
assert build_options.architectures == {Architecture.x86_64, Architecture.arm64, Architecture.universal2}
@@ -0,0 +1,103 @@
import sys
import textwrap
import pytest
from packaging.specifiers import SpecifierSet
from cibuildwheel.__main__ import main
@pytest.fixture(autouse=True, scope="function")
def fake_package_dir(monkeypatch, tmp_path):
'''
Set up a fake project
'''
local_path = tmp_path / "tmp_project"
local_path.mkdir()
local_path.joinpath("setup.py").touch()
monkeypatch.setattr(sys, 'argv', ['cibuildwheel', str(local_path)])
return local_path
def test_no_override(platform, monkeypatch, intercepted_build_args):
main()
intercepted_build_selector = intercepted_build_args.args[0].build_selector
assert intercepted_build_selector('cp39-win32')
assert intercepted_build_selector('cp36-win32')
assert intercepted_build_selector.requires_python is None
def test_override_env(platform, monkeypatch, intercepted_build_args):
monkeypatch.setenv('CIBW_PROJECT_REQUIRES_PYTHON', '>=3.8')
main()
intercepted_build_selector = intercepted_build_args.args[0].build_selector
assert intercepted_build_selector.requires_python == SpecifierSet(">=3.8")
assert intercepted_build_selector('cp39-win32')
assert not intercepted_build_selector('cp36-win32')
def test_override_setup_cfg(platform, monkeypatch, intercepted_build_args, fake_package_dir):
fake_package_dir.joinpath("setup.cfg").write_text(textwrap.dedent("""
[options]
python_requires = >=3.8
"""))
main()
intercepted_build_selector = intercepted_build_args.args[0].build_selector
assert intercepted_build_selector.requires_python == SpecifierSet(">=3.8")
assert intercepted_build_selector('cp39-win32')
assert not intercepted_build_selector('cp36-win32')
def test_override_pyproject_toml(platform, monkeypatch, intercepted_build_args, fake_package_dir):
fake_package_dir.joinpath("pyproject.toml").write_text(textwrap.dedent("""
[project]
requires-python = ">=3.8"
"""))
main()
intercepted_build_selector = intercepted_build_args.args[0].build_selector
assert intercepted_build_selector.requires_python == SpecifierSet(">=3.8")
assert intercepted_build_selector('cp39-win32')
assert not intercepted_build_selector('cp36-win32')
def test_override_setup_py_simple(platform, monkeypatch, intercepted_build_args, fake_package_dir):
fake_package_dir.joinpath("setup.py").write_text(textwrap.dedent("""
from setuptools import setup
setup(
name = "other",
python_requires = ">=3.7",
)
"""))
main()
intercepted_build_selector = intercepted_build_args.args[0].build_selector
assert intercepted_build_selector.requires_python == SpecifierSet(">=3.7")
assert intercepted_build_selector('cp39-win32')
assert not intercepted_build_selector('cp36-win32')
+142
View File
@@ -0,0 +1,142 @@
from textwrap import dedent
from cibuildwheel.projectfiles import get_requires_python_str, setup_py_python_requires
def test_read_setup_py_simple(tmp_path):
with open(tmp_path / "setup.py", "w") as f:
f.write(dedent("""
from setuptools import setup
setup(
name = "hello",
other = 23,
example = ["item", "other"],
python_requires = "1.23",
)
"""))
assert setup_py_python_requires(tmp_path.joinpath("setup.py").read_text()) == "1.23"
assert get_requires_python_str(tmp_path) == "1.23"
def test_read_setup_py_full(tmp_path):
with open(tmp_path / "setup.py", "w") as f:
f.write(dedent("""
import setuptools
setuptools.randomfunc()
setuptools.setup(
name = "hello",
other = 23,
example = ["item", "other"],
python_requires = "1.24",
)
"""))
assert setup_py_python_requires(tmp_path.joinpath("setup.py").read_text()) == "1.24"
assert get_requires_python_str(tmp_path) == "1.24"
def test_read_setup_py_assign(tmp_path):
with open(tmp_path / "setup.py", "w") as f:
f.write(dedent("""
from setuptools import setup
REQUIRES = "3.21"
setuptools.setup(
name = "hello",
other = 23,
example = ["item", "other"],
python_requires = REQUIRES,
)
"""))
assert setup_py_python_requires(tmp_path.joinpath("setup.py").read_text()) is None
assert get_requires_python_str(tmp_path) is None
def test_read_setup_py_None(tmp_path):
with open(tmp_path / "setup.py", "w") as f:
f.write(dedent("""
from setuptools import setup
REQUIRES = None
setuptools.setup(
name = "hello",
other = 23,
example = ["item", "other"],
python_requires = None,
)
"""))
assert setup_py_python_requires(tmp_path.joinpath("setup.py").read_text()) is None
assert get_requires_python_str(tmp_path) is None
def test_read_setup_py_empty(tmp_path):
with open(tmp_path / "setup.py", "w") as f:
f.write(dedent("""
from setuptools import setup
REQUIRES = "3.21"
setuptools.setup(
name = "hello",
other = 23,
example = ["item", "other"],
)
"""))
assert setup_py_python_requires(tmp_path.joinpath("setup.py").read_text()) is None
assert get_requires_python_str(tmp_path) is None
def test_read_setup_cfg(tmp_path):
with open(tmp_path / "setup.cfg", "w") as f:
f.write(dedent("""
[options]
python_requires = 1.234
[metadata]
something = other
"""))
assert get_requires_python_str(tmp_path) == "1.234"
def test_read_setup_cfg_empty(tmp_path):
with open(tmp_path / "setup.cfg", "w") as f:
f.write(dedent("""
[options]
other = 1.234
[metadata]
something = other
"""))
assert get_requires_python_str(tmp_path) is None
def test_read_pyproject_toml(tmp_path):
with open(tmp_path / "pyproject.toml", "w") as f:
f.write(dedent("""
[project]
requires-python = "1.654"
[tool.cibuildwheel]
something = "other"
"""))
assert get_requires_python_str(tmp_path) == "1.654"
def test_read_pyproject_toml_empty(tmp_path):
with open(tmp_path / "pyproject.toml", "w") as f:
f.write(dedent("""
[project]
other = 1.234
"""))
assert get_requires_python_str(tmp_path) is None
+35
View File
@@ -0,0 +1,35 @@
import pytest
from cibuildwheel.util import print_new_wheels
def test_printout_wheels(tmp_path, capsys):
tmp_path.joinpath("example.0").touch()
with print_new_wheels("TEST_MSG: {n}", tmp_path):
tmp_path.joinpath("example.1").touch()
tmp_path.joinpath("example.2").touch()
captured = capsys.readouterr()
assert captured.err == ""
assert "example.0" not in captured.out
assert "example.1\n" in captured.out
assert "example.2\n" in captured.out
assert "TEST_MSG:" in captured.out
assert "TEST_MSG: 2\n" in captured.out
def test_no_printout_on_error(tmp_path, capsys):
tmp_path.joinpath("example.0").touch()
with pytest.raises(RuntimeError):
with print_new_wheels("TEST_MSG: {n}", tmp_path):
tmp_path.joinpath("example.1").touch()
raise RuntimeError()
captured = capsys.readouterr()
assert captured.err == ""
assert "example.0" not in captured.out
assert "example.1" not in captured.out
assert "example.2" not in captured.out
assert "TEST_MSG:" not in captured.out