Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5514dd28f0 | ||
|
|
bdf765c212 | ||
|
|
31838d3c74 | ||
|
|
c5912134d5 | ||
|
|
3fa7e596af | ||
|
|
efc97b80ac | ||
|
|
3940d38b4d | ||
|
|
c7ad0c7654 | ||
|
|
566754fdba | ||
|
|
b6eca6ca48 | ||
|
|
adc6af9e7c | ||
|
|
160d1db6be | ||
|
|
3163743cba | ||
|
|
8fe6520ef4 | ||
|
|
0c30df5c96 | ||
|
|
ca9d8d5d2c | ||
|
|
d871a15e3c | ||
|
|
49738c0cee | ||
|
|
837ea5f59c | ||
|
|
99acc0abac | ||
|
|
e12ce2d647 | ||
|
|
08401d6608 | ||
|
|
f953acc65a | ||
|
|
cdfab24c5d | ||
|
|
fe002efba9 | ||
|
|
9119bf048c | ||
|
|
08f72f276d | ||
|
|
2cb3e21191 | ||
|
|
0daee3a408 | ||
|
|
70d9e1d969 | ||
|
|
eb700a164c | ||
|
|
b95dca50aa | ||
|
|
2def20d75e | ||
|
|
a79f0ef95e | ||
|
|
be55ebe178 | ||
|
|
0de4608193 | ||
|
|
74170bab4a | ||
|
|
a09b9aeb76 | ||
|
|
56653f3b4c | ||
|
|
32ab09b2ed | ||
|
|
4ff52ae8d0 | ||
|
|
c8fde72aff | ||
|
|
93ba19d00f | ||
|
|
b6f325c21a | ||
|
|
d21b7cfc2e | ||
|
|
e40d53db19 | ||
|
|
f1638e7c6f | ||
|
|
8c68e4882c | ||
|
|
ef1baac8e2 | ||
|
|
4b491ceb4a | ||
|
|
381ea7dbad | ||
|
|
4dddd9e648 | ||
|
|
00eed5aac5 | ||
|
|
bd45b1c901 | ||
|
|
3f8fcb01ad | ||
|
|
d2312cdb3f | ||
|
|
9cffb96935 | ||
|
|
e83b770929 | ||
|
|
af8ab0af2b | ||
|
|
dbb6d80a72 | ||
|
|
a99726f477 | ||
|
|
9113dfe5da | ||
|
|
8512e69733 | ||
|
|
f43b3f50c5 | ||
|
|
c4b5b167ca | ||
|
|
1606b79749 | ||
|
|
07681239e4 | ||
|
|
9bf5fb10e2 | ||
|
|
e880aca155 | ||
|
|
e29fab0e2b | ||
|
|
ffba89dc64 | ||
|
|
0df5e1295f | ||
|
|
e215446283 | ||
|
|
a6ff55ed5b | ||
|
|
bf272fe9a6 | ||
|
|
121715a568 | ||
|
|
65b530a5df | ||
|
|
16d96fa33d | ||
|
|
7a11143be8 | ||
|
|
afd08c11f2 | ||
|
|
914f74a0f0 | ||
|
|
577fd5e8e3 | ||
|
|
df60457001 | ||
|
|
e405f3495e | ||
|
|
46eb28791f | ||
|
|
a973ba9716 | ||
|
|
a1ac43e9de | ||
|
|
7cab712592 | ||
|
|
8d045992fa | ||
|
|
09fef56a46 | ||
|
|
4d89e524eb | ||
|
|
76d5f3d3e0 | ||
|
|
2b800bbb3a | ||
|
|
2e8e806be0 | ||
|
|
31d0351ddf | ||
|
|
985ef4dfef | ||
|
|
9906cf7105 | ||
|
|
7fb032d70f | ||
|
|
609c0f3986 | ||
|
|
59801d4c98 | ||
|
|
90ce9d9844 |
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -6,9 +6,9 @@ mkdocs:
|
||||
configuration: mkdocs.yml
|
||||
|
||||
python:
|
||||
version: 3.7
|
||||
version: 3.8
|
||||
install:
|
||||
- method: pip
|
||||
path: .
|
||||
extra_requirements:
|
||||
- dev
|
||||
- docs
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
cibuildwheel
|
||||
============
|
||||
|
||||
[](https://pypi.python.org/pypi/cibuildwheel) [](https://cibuildwheel.readthedocs.io/en/stable/?badge=stable) [](https://travis-ci.org/joerick/cibuildwheel) [](https://ci.appveyor.com/project/joerick/cibuildwheel/branch/master) [](https://circleci.com/gh/joerick/cibuildwheel) [](https://dev.azure.com/joerick0429/cibuildwheel/_build/latest?definitionId=2&branchName=master)
|
||||
[](https://pypi.python.org/pypi/cibuildwheel)
|
||||
[](https://cibuildwheel.readthedocs.io/en/stable/?badge=stable)
|
||||
[](https://github.com/joerick/cibuildwheel/actions)
|
||||
[](https://travis-ci.org/joerick/cibuildwheel)
|
||||
[](https://ci.appveyor.com/project/joerick/cibuildwheel/branch/master)
|
||||
[](https://circleci.com/gh/joerick/cibuildwheel)
|
||||
[](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
|
||||
|
||||
@@ -21,6 +21,7 @@ config = [
|
||||
('docs/faq.md', "cibuildwheel@v{}"),
|
||||
('docs/setup.md', "cibuildwheel@v{}"),
|
||||
('examples/*', "cibuildwheel=={}"),
|
||||
('examples/*', "cibuildwheel@v{}"),
|
||||
('setup.cfg', "version = {}"),
|
||||
]
|
||||
|
||||
|
||||
Executable
+110
@@ -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
@@ -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
@@ -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
@@ -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 @@
|
||||
__version__ = '1.8.0'
|
||||
__version__ = '1.9.0'
|
||||
|
||||
+33
-20
@@ -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)
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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/
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -5,3 +5,7 @@ requires = [
|
||||
]
|
||||
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.black]
|
||||
line-length = 120
|
||||
target-version = ['py36']
|
||||
|
||||
@@ -1 +1 @@
|
||||
-e .[dev]
|
||||
-e .[dev,docs]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
@@ -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:
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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')
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user