Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40ff745321 | ||
|
|
8f9a68fe94 | ||
|
|
840da16d39 | ||
|
|
b797430d21 | ||
|
|
c22d50427c | ||
|
|
ce85c8730d | ||
|
|
34f8427aab | ||
|
|
bb12d3dc31 | ||
|
|
77fb97caf3 | ||
|
|
23870b6780 | ||
|
|
4caee57c21 | ||
|
|
7e566e46ee | ||
|
|
9ae2ae4ac6 | ||
|
|
42489436a2 | ||
|
|
4628c1334e | ||
|
|
18b4d42f83 | ||
|
|
ff99cb37f8 | ||
|
|
d0e5781832 | ||
|
|
7dfc173bca | ||
|
|
79cf7e47ae | ||
|
|
99dc079380 | ||
|
|
696067f19e | ||
|
|
e934c8219e | ||
|
|
8de326a67a | ||
|
|
c33fa2ada0 | ||
|
|
6265d87d09 | ||
|
|
baba7706e0 | ||
|
|
d613a8c1a6 | ||
|
|
01a7c2d6df | ||
|
|
6461074ec9 | ||
|
|
d9e6bbfa4f | ||
|
|
4e9316036c | ||
|
|
93913ef2fa | ||
|
|
7903ff60f9 | ||
|
|
028c1dbf10 | ||
|
|
ce5fc12b5f | ||
|
|
07fae7e0e5 | ||
|
|
d0692932fb | ||
|
|
1238c9d7e5 | ||
|
|
38fef4e1d8 | ||
|
|
863829f67b | ||
|
|
4456d51e03 | ||
|
|
ec42cb6f42 | ||
|
|
850db69eb7 | ||
|
|
4e3cef81d7 | ||
|
|
83452b75dd | ||
|
|
be9eae6828 | ||
|
|
281caf3d2e | ||
|
|
6e41e729fe | ||
|
|
e0c456604d | ||
|
|
01dd18bbf8 | ||
|
|
774af30d71 | ||
|
|
958fa1b824 | ||
|
|
8d8b91a9f6 | ||
|
|
bae011950c | ||
|
|
9275e7431a | ||
|
|
16e23410b4 | ||
|
|
d32055e8a9 | ||
|
|
1199d78d88 | ||
|
|
be72b8b85d | ||
|
|
12f0eff609 | ||
|
|
4bc7e4f40d | ||
|
|
a68f8b23e5 | ||
|
|
6ad0aebaf8 | ||
|
|
7d0cc37b17 | ||
|
|
05df9bbfd4 | ||
|
|
f8d2982166 | ||
|
|
36e0618992 | ||
|
|
549fc8a93e | ||
|
|
5549eb3165 | ||
|
|
7b906fb5dc | ||
|
|
90da096b14 | ||
|
|
43c45b6011 | ||
|
|
6ddac481c0 | ||
|
|
a83ac06c8d | ||
|
|
ec4ea4e011 | ||
|
|
ec5670a476 | ||
|
|
ce76b74251 | ||
|
|
65792bd700 | ||
|
|
d14427d781 | ||
|
|
2ce304d5d9 | ||
|
|
42a231f830 | ||
|
|
4f47ab8052 | ||
|
|
cf73c3355e | ||
|
|
0d2bec3cb8 | ||
|
|
60dd3f0807 | ||
|
|
8c6abf3811 | ||
|
|
fc0dbe15d5 | ||
|
|
0423d79e63 | ||
|
|
fd0a789988 | ||
|
|
1e317895fe | ||
|
|
b743b8ae5b | ||
|
|
176f4aca1f | ||
|
|
60f198c90d | ||
|
|
11423a47f6 | ||
|
|
aac5db8a46 | ||
|
|
17137c3c2f | ||
|
|
bafb4fd294 | ||
|
|
fea54e25ab | ||
|
|
4c718706c8 | ||
|
|
08d3ee1d91 | ||
|
|
e80f7f4774 | ||
|
|
186e09d543 | ||
|
|
ae6832e18d | ||
|
|
743051a453 | ||
|
|
129cf601da | ||
|
|
dd26ef8ac6 | ||
|
|
ef80beb433 | ||
|
|
100fc14411 | ||
|
|
cf8fa693b8 | ||
|
|
d9948da29a | ||
|
|
c738481e82 | ||
|
|
c15d656726 | ||
|
|
cab6ecef24 | ||
|
|
1e420a9b57 | ||
|
|
691174cf11 | ||
|
|
0b0830fbbb | ||
|
|
165e51aec5 | ||
|
|
48055273f1 | ||
|
|
e8d121552c | ||
|
|
67ec8fe99f | ||
|
|
b900b2d51e | ||
|
|
9fe8d29545 | ||
|
|
692c64ffd9 | ||
|
|
67022e620e | ||
|
|
ffec9aecf8 | ||
|
|
17a224c525 | ||
|
|
4bc851ddf2 | ||
|
|
0124d174be | ||
|
|
8236877f02 | ||
|
|
c3f628d03d | ||
|
|
a97b6a13f1 | ||
|
|
a143ff2bfd | ||
|
|
1a7d1d8cb1 | ||
|
|
68d688c016 | ||
|
|
88029d2a28 | ||
|
|
513ef49cd4 | ||
|
|
b779a88fde | ||
|
|
379cf2dc13 | ||
|
|
b64b106fbd | ||
|
|
973ecf53b3 | ||
|
|
186599a1f0 | ||
|
|
aca7611fc4 | ||
|
|
9d44d9edf0 | ||
|
|
2473c405bc | ||
|
|
984df80ec9 | ||
|
|
0d5a2a7843 | ||
|
|
1f784a1a29 | ||
|
|
e64916fe9c | ||
|
|
9cb796edcd | ||
|
|
758f158bbd | ||
|
|
d17517902e | ||
|
|
200d4e3b8c | ||
|
|
9648b44bdf | ||
|
|
492b5f4039 | ||
|
|
651f6a9172 | ||
|
|
fabb82e9cf | ||
|
|
8bfe9ac6cd | ||
|
|
de55153931 | ||
|
|
a9c968868a | ||
|
|
05e7858815 | ||
|
|
5e33011229 | ||
|
|
0b707b22ed | ||
|
|
d66e201c3f | ||
|
|
6732812ad7 | ||
|
|
cbb7eafd1f | ||
|
|
be721153af | ||
|
|
ef67ddd351 | ||
|
|
0af635fb86 | ||
|
|
32182f3edc | ||
|
|
ebcda4d049 | ||
|
|
dd9b665c1d | ||
|
|
e00f03047a | ||
|
|
e0b385a520 | ||
|
|
a1832ae6c6 | ||
|
|
795413e5c0 | ||
|
|
eda3dc1a4f | ||
|
|
472fa32d9f | ||
|
|
2f41683e85 |
+21
-25
@@ -1,11 +1,23 @@
|
||||
version: 2
|
||||
|
||||
jobs:
|
||||
osx-python2:
|
||||
flake8:
|
||||
docker:
|
||||
- image: circleci/python:3.6
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
- run:
|
||||
name: Install flake8
|
||||
command: sudo python -m pip install flake8
|
||||
- run:
|
||||
name: Test.
|
||||
command: flake8 .
|
||||
osx-python3.6:
|
||||
macos:
|
||||
xcode: "10.0.0"
|
||||
xcode: "9.4.1"
|
||||
environment:
|
||||
PYTHON: python2
|
||||
PYTHON: python3
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
@@ -16,7 +28,7 @@ jobs:
|
||||
name: Test.
|
||||
command: venv/bin/python ./bin/run_tests.py
|
||||
|
||||
osx-python3:
|
||||
osx-python3.7:
|
||||
macos:
|
||||
xcode: "10.0.0"
|
||||
environment:
|
||||
@@ -31,23 +43,7 @@ jobs:
|
||||
name: Test.
|
||||
command: venv/bin/python ./bin/run_tests.py
|
||||
|
||||
linux-python2:
|
||||
docker:
|
||||
- image: circleci/python:2.7
|
||||
environment:
|
||||
PYTHON: python2
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker
|
||||
|
||||
- run:
|
||||
name: Prepare the environment.
|
||||
command: bash .circleci/prepare.sh
|
||||
- run:
|
||||
name: Test.
|
||||
command: venv/bin/python ./bin/run_tests.py
|
||||
|
||||
linux-python3:
|
||||
linux-python3.6:
|
||||
docker:
|
||||
- image: circleci/python:3.6
|
||||
environment:
|
||||
@@ -67,7 +63,7 @@ workflows:
|
||||
version: 2
|
||||
all-tests:
|
||||
jobs:
|
||||
- osx-python2
|
||||
- osx-python3
|
||||
- linux-python2
|
||||
- linux-python3
|
||||
- flake8
|
||||
- osx-python3.6
|
||||
- osx-python3.7
|
||||
- linux-python3.6
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# http://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
# Use 4 spaces the Python files
|
||||
[*.py]
|
||||
indent_size = 4
|
||||
@@ -0,0 +1,29 @@
|
||||
name: Test
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Test cibuildwheel on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-18.04, windows-latest, macos-latest]
|
||||
python_version: ['3.7']
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/setup-python@v1
|
||||
name: Install Python ${{ matrix.python_version }}
|
||||
with:
|
||||
python-version: ${{ matrix.python_version }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install -r requirements-dev.txt
|
||||
- name: Install Visual C++ for Python 2.7
|
||||
if: startsWith(matrix.os, 'windows')
|
||||
run: |
|
||||
choco install vcpython27 -f -y
|
||||
- name: Test cibuildwheel
|
||||
run: |
|
||||
python ./bin/run_tests.py
|
||||
@@ -86,9 +86,11 @@ celerybeat-schedule
|
||||
.venv
|
||||
venv/
|
||||
venv3/
|
||||
venv2/
|
||||
ENV/
|
||||
env/
|
||||
env3/
|
||||
env2/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
|
||||
+35
-18
@@ -1,33 +1,50 @@
|
||||
language: generic
|
||||
language: minimal
|
||||
|
||||
matrix:
|
||||
jobs:
|
||||
include:
|
||||
# Linux Python 2
|
||||
- sudo: required
|
||||
language: python
|
||||
python: 2.7
|
||||
services: docker
|
||||
env: PYTHON=python
|
||||
|
||||
# Linux Python 3
|
||||
- sudo: required
|
||||
- name: Linux | x86_64 + i686 | Python 3.5
|
||||
language: python
|
||||
python: 3.5
|
||||
services: docker
|
||||
env: PYTHON=python
|
||||
|
||||
# macOS Python 3
|
||||
- os: osx
|
||||
- name: Linux | arm64 | Python 3.5
|
||||
language: python
|
||||
python: 3.5
|
||||
services: docker
|
||||
arch: arm64
|
||||
env: PYTHON=python
|
||||
|
||||
- name: Linux | ppc64le | Python 3.5
|
||||
language: python
|
||||
python: 3.5
|
||||
services: docker
|
||||
arch: ppc64le
|
||||
env: PYTHON=python
|
||||
|
||||
- name: macOS | x86_64 | Python 3.x
|
||||
os: osx
|
||||
env: PYTHON=python3
|
||||
|
||||
- os: windows
|
||||
- name: Windows | x86_64 | Python 3.5
|
||||
os: windows
|
||||
language: shell
|
||||
before_install:
|
||||
- choco install python3 --version 3.5.4 --no-progress -y
|
||||
install:
|
||||
- C:\\Python35\\python -m pip install -r requirements-dev.txt
|
||||
script:
|
||||
- C:\\Python35\\python ./bin/run_tests.py
|
||||
env:
|
||||
- PYTHON=C:\\Python35\\python
|
||||
|
||||
- &linux_s390x_35
|
||||
name: Linux | s390x | Python 3.5
|
||||
language: python
|
||||
python: 3.5
|
||||
services: docker
|
||||
arch: s390x
|
||||
env: PYTHON=python
|
||||
|
||||
allow_failures:
|
||||
# must repeat the s390x job above exactly to match
|
||||
- *linux_s390x_35
|
||||
|
||||
install: $PYTHON -m pip install -r requirements-dev.txt
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
This is a summary of the Python versions and platforms covered by the different CI platforms:
|
||||
|
||||
| | 3.5 | 3.6 | 3.7 | 3.8 |
|
||||
|----------|------------------|------------------|---------------------------------------------------|------------------|
|
||||
| Linux | Travis CI | CircleCI | AppVeyor / GitHub Actions | Azure Pipelines |
|
||||
| macOS | Azure Pipelines | CircleCI | AppVeyor / Travis CI¹ / CircleCI / GitHub Actions | Azure Pipelines |
|
||||
| Windows | TravisCI | Azure Pipelines | AppVeyor / GitHub Actions | Azure Pipelines |
|
||||
|
||||
> ¹ Python version not really pinned, but dependent on the (default) version of image used.
|
||||
|
||||
Non-x86 architectures are covered on Travis CI using Python 3.5.
|
||||
@@ -9,36 +9,40 @@ cibuildwheel
|
||||
|
||||
Python wheels are great. Building them across **Mac, Linux, Windows**, on **multiple versions of Python**, is not.
|
||||
|
||||
`cibuildwheel` is here to help. `cibuildwheel` runs on your CI server - currently it supports Azure Pipelines, Travis CI, AppVeyor, and CircleCI - and it builds and tests your wheels across all of your platforms.
|
||||
`cibuildwheel` is here to help. `cibuildwheel` runs on your CI server - currently it supports Azure Pipelines, Travis CI, AppVeyor, GitHub Actions and CircleCI - and it builds and tests your wheels across all of your platforms.
|
||||
|
||||
|
||||
What does it do?
|
||||
----------------
|
||||
|
||||
| | macOS 10.6+ intel | macOS 10.9+ x86_64 | manylinux i686 | manylinux x86_64 | Windows 32bit | Windows 64bit |
|
||||
|---|---|---|---|---|---|---|
|
||||
| Python 2.7 | ✅ | | ✅ | ✅ | ✅¹ | ✅¹ |
|
||||
| Python 3.5 | ✅ | | ✅ | ✅ | ✅ | ✅ |
|
||||
| Python 3.6 | ✅ | | ✅ | ✅ | ✅ | ✅ |
|
||||
| Python 3.7 | ✅ | | ✅ | ✅ | ✅ | ✅ |
|
||||
| Python 3.8 | | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| | 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 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅² |
|
||||
| PyPy 2.7 v7.3.0 | ✅ | | ✅ | ✅ | | | | |
|
||||
| PyPy 3.6 v7.3.0 | ✅ | | ✅ | ✅ | | | | |
|
||||
|
||||
> ¹ Not supported on Travis
|
||||
<sup>¹ Not 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>
|
||||
|
||||
- Builds manylinux, macOS and Windows (32 and 64bit) wheels using Azure Pipelines, Travis CI, AppVeyor, and CircleCI
|
||||
- Builds manylinux, macOS and Windows wheels for CPython and PyPy using Azure Pipelines, Travis CI, AppVeyor, and CircleCI
|
||||
- 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
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
`cibuildwheel` currently works on **Travis CI** and **Azure Pipelines** to build wheels for all three supported platforms (Linux, macOS, Windows). On **CircleCI** Linux and macOS wheels can be built, and on **AppVeyor** Linux and Windows are supported.
|
||||
`cibuildwheel` currently works on **Travis CI**, **Azure Pipelines**, **AppVeyor** and **GitHub Actions** to build wheels for all three supported platforms (Linux, macOS, Windows). On **CircleCI** Linux and macOS wheels can be built.
|
||||
|
||||
| | Linux | macOS | Windows |
|
||||
|-----------------|-------|-------|---------|
|
||||
| Azure Pipelines | ✅ | ✅ | ✅ |
|
||||
| Travis CI | ✅ | ✅ | ✅ |
|
||||
| AppVeyor | ✅ | | ✅ |
|
||||
| AppVeyor | ✅ | ✅ | ✅ |
|
||||
| GitHub Actions | ✅ | ✅ | ✅ |
|
||||
| CircleCI | ✅ | ✅ | |
|
||||
|
||||
`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 Travis CI, CircleCI, Azure Pipelines and AppVeyor are ideal.
|
||||
@@ -48,7 +52,7 @@ Usage
|
||||
Example setup
|
||||
-------------
|
||||
|
||||
To build manylinux, macOS, and Windows wheels on Travis CI and upload them to PyPI whenever you tag a version, you could use this `.travis.yml`:
|
||||
To build manylinux, macOS, and Windows wheels on Travis CI and upload them to PyPI whenever you tag a version, you could use this `.travis.yml`:
|
||||
|
||||
```yaml
|
||||
language: python
|
||||
@@ -69,19 +73,23 @@ jobs:
|
||||
|
||||
env:
|
||||
global:
|
||||
- TWINE_USERNAME=joerick
|
||||
# Note: TWINE_PASSWORD is set in Travis settings
|
||||
- TWINE_USERNAME=__token__
|
||||
# Note: TWINE_PASSWORD is set to a PyPI API token in Travis settings
|
||||
|
||||
install:
|
||||
- python -m pip install twine cibuildwheel==1.1.0
|
||||
- python3 -m pip install cibuildwheel==1.3.0
|
||||
|
||||
script:
|
||||
# build the wheels, put them into './wheelhouse'
|
||||
- python -m cibuildwheel --output-dir wheelhouse
|
||||
- python3 -m cibuildwheel --output-dir wheelhouse
|
||||
|
||||
after_success:
|
||||
# if the release was tagged, upload them to PyPI
|
||||
- if [[ $TRAVIS_TAG ]]; then python -m twine upload wheelhouse/*.whl; fi
|
||||
- |
|
||||
if [[ $TRAVIS_TAG ]]; then
|
||||
python3 -m pip install twine
|
||||
python3 -m twine upload wheelhouse/*.whl
|
||||
fi
|
||||
```
|
||||
|
||||
For more information, including how to build on Appveyor, Azure, CircleCI, check out the [documentation](https://cibuildwheel.readthedocs.org) and also check out [the examples](https://github.com/joerick/cibuildwheel/tree/master/examples).
|
||||
@@ -96,11 +104,12 @@ Options
|
||||
| **Build environment** | [`CIBW_ENVIRONMENT`](https://cibuildwheel.readthedocs.io/en/stable/options/#environment) | Set environment variables needed during the build |
|
||||
| | [`CIBW_BEFORE_BUILD`](https://cibuildwheel.readthedocs.io/en/stable/options/#before-build) | Execute a shell command preparing each wheel's build |
|
||||
| | [`CIBW_REPAIR_WHEEL_COMMAND`](https://cibuildwheel.readthedocs.io/en/stable/options/#repair-wheel-command) | Execute a shell command to repair each (non-pure Python) built wheel |
|
||||
| | [`CIBW_MANYLINUX_X86_64_IMAGE`](https://cibuildwheel.readthedocs.io/en/stable/options/#manylinux-image) [`CIBW_MANYLINUX_I686_IMAGE`](https://cibuildwheel.readthedocs.io/en/stable/options/#manylinux-image) | Specify alternative manylinux docker images |
|
||||
| | [`CIBW_MANYLINUX_X86_64_IMAGE`](https://cibuildwheel.readthedocs.io/en/stable/options/#manylinux-image) [`CIBW_MANYLINUX_I686_IMAGE`](https://cibuildwheel.readthedocs.io/en/stable/options/#manylinux-image) [`CIBW_MANYLINUX_PYPY_X86_64_IMAGE`](https://cibuildwheel.readthedocs.io/en/stable/options/#manylinux-image) | Specify alternative manylinux docker images |
|
||||
| **Testing** | [`CIBW_TEST_COMMAND`](https://cibuildwheel.readthedocs.io/en/stable/options/#test-command) | Execute a shell command to test each built wheel |
|
||||
| | [`CIBW_BEFORE_TEST`](https://cibuildwheel.readthedocs.io/en/stable/options/#before-test) | Execute shell command to prepare test environment |
|
||||
| | [`CIBW_TEST_REQUIRES`](https://cibuildwheel.readthedocs.io/en/stable/options/#test-requires) | Install Python dependencies before running the tests |
|
||||
| | [`CIBW_TEST_EXTRAS`](https://cibuildwheel.readthedocs.io/en/stable/options/#test-extras) | Install your wheel for testing using extras_require |
|
||||
| **Other** | [`CIBW_BUILD_VERBOSITY`](https://cibuildwheel.readthedocs.io/en/stable/options/#test-extras) | Increase/decrease the output of pip wheel |
|
||||
| **Other** | [`CIBW_BUILD_VERBOSITY`](https://cibuildwheel.readthedocs.io/en/stable/options/#build-verbosity) | Increase/decrease the output of pip wheel |
|
||||
|
||||
Working examples
|
||||
----------------
|
||||
@@ -117,6 +126,7 @@ Here are some repos that use cibuildwheel.
|
||||
- [apriltags2-ethz](https://github.com/safijari/apriltags2_ethz)
|
||||
- [TgCrypto](https://github.com/pyrogram/tgcrypto)
|
||||
- [Twisted](https://github.com/twisted/twisted)
|
||||
- [gmic-py](https://github.com/dtschump/gmic-py)
|
||||
|
||||
> Add your repo here! Send a PR.
|
||||
|
||||
@@ -132,12 +142,45 @@ This is similar to static linking, so it might have some licence implications. C
|
||||
Changelog
|
||||
=========
|
||||
|
||||
### 1.3.0
|
||||
|
||||
_12 March 2020_
|
||||
|
||||
- ✨ 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
|
||||
prepare the environment before your tests are run. (#242)
|
||||
|
||||
### 1.2.0
|
||||
|
||||
_8 March 2020_
|
||||
|
||||
- ✨ Add support for building PyPy wheels, across Manylinux, macOS, and
|
||||
Windows. (#185)
|
||||
- ✨ Added the ability to build ARM64 (aarch64), ppc64le, and s390x wheels,
|
||||
using manylinux2014 and Travis CI. (#273)
|
||||
- ✨ You can now build macOS wheels on Appveyor. (#230)
|
||||
- 🛠 Changed default macOS minimum target to 10.9, from 10.6. This allows the
|
||||
use of more modern C++ libraries, among other things. (#156)
|
||||
- 🛠 Stop building universal binaries on macOS. We now only build x86_64
|
||||
wheels on macOS. (#220)
|
||||
- ✨ Allow chaining of commands using `&&` and `||` on Windows inside
|
||||
CIBW_BEFORE_BUILD and CIBW_TEST_COMMAND. (#293)
|
||||
- 🛠 Improved error reporting for failed Cython builds due to stale .so files
|
||||
(#263)
|
||||
- 🛠 Update CPython from 3.7.5 to 3.7.6 and from 3.8.0 to 3.8.2 on Mac/Windows
|
||||
- 🛠 Improved error messages when a bad config breaks cibuildwheel's PATH
|
||||
variable. (#264)
|
||||
- ⚠️ Removed support for *running* cibuildwheel on Python 2.7. cibuildwheel
|
||||
will continue to build Python 2.7 wheels for a little while. (#265)
|
||||
|
||||
### 1.1.0
|
||||
|
||||
_7 December 2019_
|
||||
|
||||
- ✨ Add support for building manylinux2014 wheels. To use, set
|
||||
`CIBW_MANYLINUX_X86_64_IMAGE` and CIBW_MANYLINUX_I686_IMAGE to
|
||||
- ✨ Add support for building manylinux2014 wheels. To use, set
|
||||
`CIBW_MANYLINUX_X86_64_IMAGE` and CIBW_MANYLINUX_I686_IMAGE to
|
||||
`manylinux2014`.
|
||||
- ✨ Add support for [Linux on Appveyor](https://www.appveyor.com/blog/2018/03/06/appveyor-for-linux/) (#204, #207)
|
||||
- ✨ Add `CIBW_REPAIR_WHEEL_COMMAND` env variable, for changing how
|
||||
|
||||
+20
-10
@@ -1,11 +1,21 @@
|
||||
image:
|
||||
- Ubuntu
|
||||
- Visual Studio 2015
|
||||
environment:
|
||||
matrix:
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Ubuntu
|
||||
APPVEYOR_JOB_NAME: "python37-x64-ubuntu"
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
APPVEYOR_JOB_NAME: "python37-x64-vs2015"
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: macos-mojave
|
||||
APPVEYOR_JOB_NAME: "python37-x64-macos-mojave"
|
||||
|
||||
build_script:
|
||||
- cmd: "C:\\Python27\\python.exe -m pip install -r requirements-dev.txt"
|
||||
- sh: "${HOME}/.localpython3.7.4/bin/python3 -m pip install -r requirements-dev.txt"
|
||||
# the '-u' flag is required so the output is in the correct order.
|
||||
# See https://github.com/joerick/cibuildwheel/pull/24 for more info.
|
||||
- cmd: "C:\\Python27\\python.exe -u ./bin/run_tests.py"
|
||||
- sh: "${HOME}/.localpython3.7.4/bin/python3 ./bin/run_tests.py"
|
||||
stack: python 3.7
|
||||
|
||||
build: off
|
||||
|
||||
init:
|
||||
- cmd: set PATH=C:\Python37;C:\Python37\Scripts;%PATH%
|
||||
|
||||
install: python -m pip install -r requirements-dev.txt
|
||||
|
||||
# the '-u' flag is required so the output is in the correct order.
|
||||
# See https://github.com/joerick/cibuildwheel/pull/24 for more info.
|
||||
test_script: python -u ./bin/run_tests.py
|
||||
|
||||
+26
-4
@@ -1,5 +1,5 @@
|
||||
jobs:
|
||||
- job: linux
|
||||
- job: linux_38
|
||||
pool: {vmImage: 'Ubuntu-18.04'}
|
||||
steps:
|
||||
- task: UsePythonVersion@0
|
||||
@@ -9,8 +9,18 @@ jobs:
|
||||
python -m pip install -r requirements-dev.txt
|
||||
python ./bin/run_tests.py
|
||||
|
||||
- job: macos
|
||||
pool: {vmImage: 'macOS-10.13'}
|
||||
- job: macos_35
|
||||
pool: {vmImage: 'macOS-10.15'}
|
||||
steps:
|
||||
- task: UsePythonVersion@0
|
||||
inputs:
|
||||
versionSpec: '3.5'
|
||||
- bash: |
|
||||
python -m pip install -r requirements-dev.txt
|
||||
python ./bin/run_tests.py
|
||||
|
||||
- job: macos_38
|
||||
pool: {vmImage: 'macOS-10.15'}
|
||||
steps:
|
||||
- task: UsePythonVersion@0
|
||||
inputs:
|
||||
@@ -19,7 +29,19 @@ jobs:
|
||||
python -m pip install -r requirements-dev.txt
|
||||
python ./bin/run_tests.py
|
||||
|
||||
- job: windows
|
||||
- job: windows_36
|
||||
pool: {vmImage: 'vs2017-win2016'}
|
||||
steps:
|
||||
- task: UsePythonVersion@0
|
||||
inputs:
|
||||
versionSpec: '3.6'
|
||||
- script: choco install vcpython27 -f -y
|
||||
displayName: Install Visual C++ for Python 2.7
|
||||
- bash: |
|
||||
python -m pip install -r requirements-dev.txt
|
||||
python ./bin/run_tests.py
|
||||
|
||||
- job: windows_38
|
||||
pool: {vmImage: 'vs2017-win2016'}
|
||||
steps:
|
||||
- task: UsePythonVersion@0
|
||||
|
||||
Executable
+113
@@ -0,0 +1,113 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import click
|
||||
from pathlib import Path
|
||||
import os
|
||||
import cibuildwheel
|
||||
from packaging.version import Version, InvalidVersion
|
||||
import subprocess
|
||||
import glob
|
||||
|
||||
config = [
|
||||
# file path, version find/replace format
|
||||
('cibuildwheel/__init__.py', "__version__ = '{}'"),
|
||||
('setup.py', "version='{}'"),
|
||||
('README.md', "cibuildwheel=={}"),
|
||||
('examples/*', "cibuildwheel=={}"),
|
||||
]
|
||||
|
||||
|
||||
@click.command()
|
||||
def bump_version():
|
||||
git_changes_result = subprocess.run(['git diff-index --quiet HEAD --'], shell=True)
|
||||
repo_has_uncommitted_changes = git_changes_result.returncode != 0
|
||||
|
||||
if repo_has_uncommitted_changes:
|
||||
print('error: Uncommitted changes detected.')
|
||||
exit(1)
|
||||
|
||||
current_version = cibuildwheel.__version__
|
||||
print( 'Current version:', current_version) # noqa
|
||||
new_version = input(' New version: ').strip()
|
||||
|
||||
try:
|
||||
Version(new_version)
|
||||
except InvalidVersion:
|
||||
print("error: This version doesn't conform to PEP440")
|
||||
print(' https://www.python.org/dev/peps/pep-0440/')
|
||||
exit(1)
|
||||
|
||||
actions = []
|
||||
|
||||
for path_pattern, version_pattern in config:
|
||||
paths = [Path(p) for p in glob.glob(path_pattern)]
|
||||
|
||||
if not paths:
|
||||
print(f'error: Pattern {path_pattern} didn’t match any files')
|
||||
exit(1)
|
||||
|
||||
find_pattern = version_pattern.format(current_version)
|
||||
replace_pattern = version_pattern.format(new_version)
|
||||
found_at_least_one_file_needing_update = False
|
||||
|
||||
for path in paths:
|
||||
contents = path.read_text(encoding='utf8')
|
||||
if find_pattern in contents:
|
||||
found_at_least_one_file_needing_update = True
|
||||
actions.append(
|
||||
(path, find_pattern, replace_pattern)
|
||||
)
|
||||
|
||||
if not found_at_least_one_file_needing_update:
|
||||
print('error: Didn’t find any occurences of “{}” in “{}”'.format(find_pattern, path_pattern))
|
||||
exit(1)
|
||||
|
||||
print()
|
||||
print("Here's the plan:")
|
||||
print()
|
||||
|
||||
for action in actions:
|
||||
print('{} {red}{}{off} → {green}{}{off}'.format(
|
||||
*action,
|
||||
red="\u001b[31m", green="\u001b[32m", off="\u001b[0m"
|
||||
))
|
||||
|
||||
print('Then commit, and tag as v{}'.format(new_version))
|
||||
|
||||
answer = input('Proceed? [y/N] ').strip()
|
||||
|
||||
if answer != 'y':
|
||||
print('Aborted')
|
||||
exit(1)
|
||||
|
||||
for path, find, replace in actions:
|
||||
contents = path.read_text(encoding='utf8')
|
||||
contents = contents.replace(find, replace)
|
||||
path.write_text(contents, encoding='utf8')
|
||||
|
||||
print('Files updated. If you want to update the changelog as part of this')
|
||||
print('commit, do that now.')
|
||||
|
||||
while input('Type "done" to continue: ').strip().lower() != 'done':
|
||||
pass
|
||||
|
||||
subprocess.run([
|
||||
'git', 'commit',
|
||||
'-a',
|
||||
'-m', f'Bump version: v{new_version}'
|
||||
], check=True)
|
||||
|
||||
subprocess.run([
|
||||
'git', 'tag',
|
||||
'-a',
|
||||
'-m', f'v{new_version}',
|
||||
f'v{new_version}'
|
||||
], check=True)
|
||||
|
||||
print('Done.')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
os.chdir(os.path.dirname(__file__))
|
||||
os.chdir('..')
|
||||
bump_version()
|
||||
+5
-5
@@ -1,7 +1,9 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from __future__ import print_function
|
||||
import os, sys, subprocess, shutil
|
||||
import argparse
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
def single_run(test_project):
|
||||
@@ -12,8 +14,6 @@ def single_run(test_project):
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("test_project_dir")
|
||||
args = parser.parse_args()
|
||||
|
||||
+6
-5
@@ -1,18 +1,19 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from __future__ import print_function
|
||||
import os, sys, subprocess, shutil, json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from glob import glob
|
||||
|
||||
if __name__ == '__main__':
|
||||
# move cwd to the project root
|
||||
os.chdir(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
### run the unit tests
|
||||
# run the unit tests
|
||||
|
||||
subprocess.check_call([sys.executable, '-m', 'pytest', 'unit_test'])
|
||||
|
||||
### run the integration tests
|
||||
# run the integration tests
|
||||
|
||||
test_projects = sorted(glob('test/??_*'))
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = '1.1.0'
|
||||
__version__ = '1.3.0'
|
||||
|
||||
+52
-21
@@ -1,10 +1,22 @@
|
||||
from __future__ import print_function
|
||||
import argparse, os, subprocess, sys, textwrap
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import textwrap
|
||||
import traceback
|
||||
|
||||
import cibuildwheel
|
||||
import cibuildwheel.linux, cibuildwheel.windows, cibuildwheel.macos
|
||||
from cibuildwheel.environment import parse_environment, EnvironmentParseError
|
||||
from cibuildwheel.util import BuildSelector, Unbuffered
|
||||
import cibuildwheel.linux
|
||||
import cibuildwheel.macos
|
||||
import cibuildwheel.windows
|
||||
from cibuildwheel.environment import (
|
||||
EnvironmentParseError,
|
||||
parse_environment,
|
||||
)
|
||||
from cibuildwheel.util import (
|
||||
BuildSelector,
|
||||
Unbuffered
|
||||
)
|
||||
|
||||
|
||||
def get_option_from_environment(option_name, platform=None, default=None):
|
||||
'''
|
||||
@@ -61,16 +73,16 @@ def main():
|
||||
args = parser.parse_args()
|
||||
|
||||
detect_obsolete_options()
|
||||
|
||||
|
||||
if args.platform != 'auto':
|
||||
platform = args.platform
|
||||
else:
|
||||
ci = strtobool(os.environ.get('CI', 'false')) or 'BITRISE_BUILD_NUMBER' in os.environ or 'AZURE_HTTP_USER_AGENT' in os.environ
|
||||
ci = strtobool(os.environ.get('CI', 'false')) or 'BITRISE_BUILD_NUMBER' in os.environ or 'AZURE_HTTP_USER_AGENT' in os.environ or 'GITHUB_WORKFLOW' in os.environ
|
||||
if not ci:
|
||||
print('cibuildwheel: Unable to detect platform. cibuildwheel should run on your CI server, '
|
||||
'Travis CI, AppVeyor, Azure Pipelines and CircleCI are supported. You can run on your '
|
||||
'development machine or other CI providers using the --platform argument. Check --help '
|
||||
'output for more information.',
|
||||
'Travis CI, AppVeyor, Azure Pipelines, GitHub Actions and CircleCI are supported. You '
|
||||
'can run on your development machine or other CI providers using the --platform argument. '
|
||||
'Check --help output for more information.',
|
||||
file=sys.stderr)
|
||||
exit(2)
|
||||
if sys.platform.startswith('linux'):
|
||||
@@ -85,7 +97,6 @@ def main():
|
||||
file=sys.stderr)
|
||||
exit(2)
|
||||
|
||||
|
||||
output_dir = args.output_dir
|
||||
test_command = get_option_from_environment('CIBW_TEST_COMMAND', platform=platform)
|
||||
test_requires = get_option_from_environment('CIBW_TEST_REQUIRES', platform=platform, default='').split()
|
||||
@@ -97,11 +108,12 @@ def main():
|
||||
if platform == 'linux':
|
||||
repair_command_default = 'auditwheel repair -w {dest_dir} {wheel}'
|
||||
elif platform == 'macos':
|
||||
repair_command_default = 'delocate-listdeps {wheel} && delocate-wheel -w {dest_dir} {wheel}'
|
||||
repair_command_default = 'delocate-listdeps {wheel} && delocate-wheel --require-archs x86_64 -w {dest_dir} {wheel}'
|
||||
else:
|
||||
repair_command_default = ''
|
||||
repair_command = get_option_from_environment('CIBW_REPAIR_WHEEL_COMMAND', platform=platform, default=repair_command_default)
|
||||
environment_config = get_option_from_environment('CIBW_ENVIRONMENT', platform=platform, default='')
|
||||
before_test = get_option_from_environment('CIBW_BEFORE_TEST', platform=platform, default='')
|
||||
|
||||
if test_extras:
|
||||
test_extras = '[{0}]'.format(test_extras)
|
||||
@@ -113,9 +125,8 @@ def main():
|
||||
|
||||
try:
|
||||
environment = parse_environment(environment_config)
|
||||
except (EnvironmentParseError, ValueError) as e:
|
||||
except (EnvironmentParseError, ValueError):
|
||||
print('cibuildwheel: Malformed environment option "%s"' % environment_config, file=sys.stderr)
|
||||
import traceback
|
||||
traceback.print_exc(None, sys.stderr)
|
||||
exit(2)
|
||||
|
||||
@@ -144,11 +155,16 @@ def main():
|
||||
build_selector=build_selector,
|
||||
repair_command=repair_command,
|
||||
environment=environment,
|
||||
before_test=before_test
|
||||
)
|
||||
|
||||
if platform == 'linux':
|
||||
manylinux_x86_64_image = os.environ.get('CIBW_MANYLINUX_X86_64_IMAGE', 'manylinux2010')
|
||||
manylinux_i686_image = os.environ.get('CIBW_MANYLINUX_I686_IMAGE', 'manylinux2010')
|
||||
manylinux_pypy_x86_64_image = os.environ.get('CIBW_MANYLINUX_PYPY_X86_64_IMAGE', 'manylinux2010')
|
||||
manylinux_aarch64_image = os.environ.get('CIBW_MANYLINUX_AARCH64_IMAGE', 'manylinux2014')
|
||||
manylinux_ppc64le_image = os.environ.get('CIBW_MANYLINUX_PPC64LE_IMAGE', 'manylinux2014')
|
||||
manylinux_s390x_image = os.environ.get('CIBW_MANYLINUX_S390X_IMAGE', 'manylinux2014')
|
||||
|
||||
default_manylinux_images_x86_64 = {'manylinux1': 'quay.io/pypa/manylinux1_x86_64',
|
||||
'manylinux2010': 'quay.io/pypa/manylinux2010_x86_64',
|
||||
@@ -156,10 +172,19 @@ def main():
|
||||
default_manylinux_images_i686 = {'manylinux1': 'quay.io/pypa/manylinux1_i686',
|
||||
'manylinux2010': 'quay.io/pypa/manylinux2010_i686',
|
||||
'manylinux2014': 'quay.io/pypa/manylinux2014_i686'}
|
||||
default_manylinux_images_pypy_x86_64 = {'manylinux2010': 'pypywheels/manylinux2010-pypy_x86_64'}
|
||||
default_manylinux_images_aarch64 = {'manylinux2014': 'quay.io/pypa/manylinux2014_aarch64'}
|
||||
default_manylinux_images_ppc64le = {'manylinux2014': 'quay.io/pypa/manylinux2014_ppc64le'}
|
||||
default_manylinux_images_s390x = {'manylinux2014': 'quay.io/pypa/manylinux2014_s390x'}
|
||||
|
||||
build_options.update(
|
||||
manylinux_images={'x86_64': default_manylinux_images_x86_64.get(manylinux_x86_64_image) or manylinux_x86_64_image,
|
||||
'i686': default_manylinux_images_i686.get(manylinux_i686_image) or manylinux_i686_image},
|
||||
'i686': default_manylinux_images_i686.get(manylinux_i686_image) or manylinux_i686_image,
|
||||
'pypy_x86_64': default_manylinux_images_pypy_x86_64.get(manylinux_pypy_x86_64_image) or manylinux_pypy_x86_64_image,
|
||||
'aarch64': default_manylinux_images_aarch64.get(manylinux_aarch64_image) or manylinux_aarch64_image,
|
||||
'ppc64le': default_manylinux_images_ppc64le.get(manylinux_ppc64le_image) or manylinux_ppc64le_image,
|
||||
's390x': default_manylinux_images_s390x.get(manylinux_s390x_image) or manylinux_s390x_image,
|
||||
},
|
||||
)
|
||||
elif platform == 'macos':
|
||||
pass
|
||||
@@ -198,11 +223,18 @@ def detect_obsolete_options():
|
||||
print("Option '{}' is not empty. Please unset '{}'".format(alternative, deprecated))
|
||||
exit(2)
|
||||
|
||||
# Check for 'manylinux1' in the 'CIBW_BUILD' and 'CIBW_SKIP' options
|
||||
for deprecated in ['CIBW_BUILD', 'CIBW_SKIP']:
|
||||
if deprecated in os.environ and 'manylinux1' in os.environ[deprecated]:
|
||||
print("Build identifiers with 'manylinux1' been deprecated. Replacing all occurences of 'manylinux1' by 'manylinux' in the option '{}'".format(deprecated))
|
||||
os.environ[deprecated] = os.environ[deprecated].replace('manylinux1', 'manylinux')
|
||||
# Check for deprecated identifiers in 'CIBW_BUILD' and 'CIBW_SKIP' options
|
||||
for option in ['CIBW_BUILD', 'CIBW_SKIP']:
|
||||
for deprecated, alternative in [('manylinux1', 'manylinux'),
|
||||
('macosx_10_6_intel', 'macosx_x86_64'),
|
||||
('macosx_10_9_x86_64', 'macosx_x86_64')]:
|
||||
if option in os.environ and deprecated in os.environ[option]:
|
||||
print("Build identifiers with '{deprecated}' have been deprecated. Replacing all occurences of '{deprecated}' with '{alternative}' in the option '{option}'".format(
|
||||
deprecated=deprecated,
|
||||
alternative=alternative,
|
||||
option=option,
|
||||
))
|
||||
os.environ[option] = os.environ[option].replace(deprecated, alternative)
|
||||
|
||||
|
||||
def print_preamble(platform, build_options):
|
||||
@@ -215,7 +247,6 @@ def print_preamble(platform, build_options):
|
||||
|
||||
print('cibuildwheel version %s\n' % cibuildwheel.__version__)
|
||||
|
||||
|
||||
print('Build options:')
|
||||
print(' platform: %r' % platform)
|
||||
for option, value in sorted(build_options.items()):
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import subprocess, shlex, sys
|
||||
import shlex
|
||||
import subprocess
|
||||
from collections import namedtuple
|
||||
|
||||
import bashlex
|
||||
|
||||
NodeExecutionContext = namedtuple('NodeExecutionContext', ['environment', 'input'])
|
||||
|
||||
|
||||
def evaluate(value, environment):
|
||||
if not value:
|
||||
# empty string evaluates to empty string
|
||||
@@ -16,9 +19,9 @@ def evaluate(value, environment):
|
||||
raise ValueError('"%s" has too many parts' % value)
|
||||
|
||||
value_word_node = command_node.parts[0]
|
||||
|
||||
|
||||
return evaluate_node(
|
||||
value_word_node,
|
||||
value_word_node,
|
||||
context=NodeExecutionContext(environment=environment, input=value)
|
||||
)
|
||||
|
||||
@@ -60,12 +63,8 @@ def evaluate_word_node(node, context):
|
||||
def evaluate_command_node(node, context):
|
||||
words = [evaluate_node(part, context=context) for part in node.parts]
|
||||
command = ' '.join(words)
|
||||
output = subprocess.check_output(shlex.split(command), env=context.environment)
|
||||
return subprocess.check_output(shlex.split(command), env=context.environment, universal_newlines=True)
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
return output.decode('utf8', 'replace')
|
||||
else:
|
||||
return output
|
||||
|
||||
def evaluate_parameter_node(node, context):
|
||||
return context.environment.get(node.value, '')
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import bashlex
|
||||
|
||||
from . import bashlex_eval
|
||||
|
||||
|
||||
@@ -41,7 +42,7 @@ def split_env_items(env_string):
|
||||
return result
|
||||
|
||||
|
||||
class EnvironmentAssignment(object):
|
||||
class EnvironmentAssignment:
|
||||
def __init__(self, assignment):
|
||||
name, equals, value = assignment.partition('=')
|
||||
if not equals:
|
||||
@@ -60,7 +61,7 @@ class EnvironmentAssignment(object):
|
||||
return '%s=%s' % (self.name, self.value)
|
||||
|
||||
|
||||
class ParsedEnvironment(object):
|
||||
class ParsedEnvironment:
|
||||
def __init__(self, assignments):
|
||||
self.assignments = assignments
|
||||
|
||||
|
||||
+130
-53
@@ -1,12 +1,37 @@
|
||||
from __future__ import print_function
|
||||
import os, subprocess, sys, uuid
|
||||
import os
|
||||
import platform
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
import textwrap
|
||||
import uuid
|
||||
from collections import namedtuple
|
||||
from .util import prepare_command, get_build_verbosity_extra_flags
|
||||
|
||||
try:
|
||||
from shlex import quote as shlex_quote
|
||||
except ImportError:
|
||||
from pipes import quote as shlex_quote
|
||||
from .util import (
|
||||
get_build_verbosity_extra_flags,
|
||||
prepare_command,
|
||||
)
|
||||
|
||||
|
||||
def matches_platform(identifier):
|
||||
pm = platform.machine()
|
||||
if pm == "x86_64":
|
||||
# x86_64 machines can run i686 docker containers
|
||||
if identifier.endswith('x86_64') or identifier.endswith('i686'):
|
||||
return True
|
||||
elif pm == "i686":
|
||||
if identifier.endswith('i686'):
|
||||
return True
|
||||
elif pm == "aarch64":
|
||||
if identifier.endswith('aarch64'):
|
||||
return True
|
||||
elif pm == "ppc64le":
|
||||
if identifier.endswith('ppc64le'):
|
||||
return True
|
||||
elif pm == "s390x":
|
||||
if identifier.endswith('s390x'):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def get_python_configurations(build_selector):
|
||||
@@ -24,16 +49,29 @@ def get_python_configurations(build_selector):
|
||||
PythonConfiguration(identifier='cp36-manylinux_i686', path='/opt/python/cp36-cp36m'),
|
||||
PythonConfiguration(identifier='cp37-manylinux_i686', path='/opt/python/cp37-cp37m'),
|
||||
PythonConfiguration(identifier='cp38-manylinux_i686', path='/opt/python/cp38-cp38'),
|
||||
PythonConfiguration(identifier='pp27-manylinux_x86_64', path='/opt/python/pp27-pypy_73'),
|
||||
PythonConfiguration(identifier='pp36-manylinux_x86_64', path='/opt/python/pp36-pypy36_pp73'),
|
||||
PythonConfiguration(identifier='cp35-manylinux_aarch64', path='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(identifier='cp36-manylinux_aarch64', path='/opt/python/cp36-cp36m'),
|
||||
PythonConfiguration(identifier='cp37-manylinux_aarch64', path='/opt/python/cp37-cp37m'),
|
||||
PythonConfiguration(identifier='cp38-manylinux_aarch64', path='/opt/python/cp38-cp38'),
|
||||
PythonConfiguration(identifier='cp35-manylinux_ppc64le', path='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(identifier='cp36-manylinux_ppc64le', path='/opt/python/cp36-cp36m'),
|
||||
PythonConfiguration(identifier='cp37-manylinux_ppc64le', path='/opt/python/cp37-cp37m'),
|
||||
PythonConfiguration(identifier='cp38-manylinux_ppc64le', path='/opt/python/cp38-cp38'),
|
||||
PythonConfiguration(identifier='cp35-manylinux_s390x', path='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(identifier='cp36-manylinux_s390x', path='/opt/python/cp36-cp36m'),
|
||||
PythonConfiguration(identifier='cp37-manylinux_s390x', path='/opt/python/cp37-cp37m'),
|
||||
PythonConfiguration(identifier='cp38-manylinux_s390x', path='/opt/python/cp38-cp38'),
|
||||
]
|
||||
|
||||
# skip builds as required
|
||||
return [c for c in python_configurations if build_selector(c.identifier)]
|
||||
return [c for c in python_configurations if matches_platform(c.identifier) and build_selector(c.identifier)]
|
||||
|
||||
|
||||
def build(project_dir, output_dir, test_command, test_requires, test_extras, before_build, build_verbosity, build_selector, repair_command, environment, manylinux_images):
|
||||
def build(project_dir, output_dir, test_command, before_test, test_requires, test_extras, before_build, build_verbosity, build_selector, repair_command, environment, manylinux_images):
|
||||
try:
|
||||
subprocess.check_call(['docker', '--version'])
|
||||
except:
|
||||
except Exception:
|
||||
print('cibuildwheel: Docker not found. Docker is required to run Linux builds. '
|
||||
'If you\'re building on Travis CI, add `services: [docker]` to your .travis.yml.'
|
||||
'If you\'re building on Circle CI in Linux, add a `setup_remote_docker` step to your .circleci/config.yml',
|
||||
@@ -42,12 +80,16 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef
|
||||
|
||||
python_configurations = get_python_configurations(build_selector)
|
||||
platforms = [
|
||||
('manylinux_x86_64', manylinux_images['x86_64']),
|
||||
('manylinux_i686', manylinux_images['i686']),
|
||||
('cp', 'manylinux_x86_64', manylinux_images['x86_64']),
|
||||
('cp', 'manylinux_i686', manylinux_images['i686']),
|
||||
('cp', 'manylinux_aarch64', manylinux_images['aarch64']),
|
||||
('cp', 'manylinux_ppc64le', manylinux_images['ppc64le']),
|
||||
('cp', 'manylinux_s390x', manylinux_images['s390x']),
|
||||
('pp', 'manylinux_x86_64', manylinux_images['pypy_x86_64']),
|
||||
]
|
||||
|
||||
for platform_tag, docker_image in platforms:
|
||||
platform_configs = [c for c in python_configurations if c.identifier.endswith(platform_tag)]
|
||||
for implementation, platform_tag, docker_image in platforms:
|
||||
platform_configs = [c for c in python_configurations if c.identifier.startswith(implementation) and c.identifier.endswith(platform_tag)]
|
||||
if not platform_configs:
|
||||
continue
|
||||
|
||||
@@ -57,17 +99,30 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef
|
||||
mkdir /output
|
||||
cd /project
|
||||
|
||||
{environment_exports}
|
||||
for PYBIN in {pybin_paths}; do (
|
||||
# Temporary hack/workaround, putting loop body in subshell; fixed in PR #256
|
||||
|
||||
export PATH="$PYBIN:$PATH"
|
||||
{environment_exports}
|
||||
|
||||
# check the active python and pip are in PYBIN
|
||||
if [ "$(which pip)" != "$PYBIN/pip" ]; then
|
||||
echo "cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it."
|
||||
exit 1
|
||||
fi
|
||||
if [ "$(which python)" != "$PYBIN/python" ]; then
|
||||
echo "cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for PYBIN in {pybin_paths}; do
|
||||
if [ ! -z {before_build} ]; then
|
||||
PATH="$PYBIN:$PATH" sh -c {before_build}
|
||||
sh -c {before_build}
|
||||
fi
|
||||
|
||||
# Build the wheel
|
||||
rm -rf /tmp/built_wheel
|
||||
mkdir /tmp/built_wheel
|
||||
PATH="$PYBIN:$PATH" "$PYBIN/pip" wheel . -w /tmp/built_wheel --no-deps {build_verbosity_flag}
|
||||
pip wheel . -w /tmp/built_wheel --no-deps {build_verbosity_flag}
|
||||
built_wheel=(/tmp/built_wheel/*.whl)
|
||||
|
||||
# repair the wheel
|
||||
@@ -86,9 +141,11 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef
|
||||
if [ ! -z {test_command} ]; then
|
||||
# Set up a virtual environment to install and test from, to make sure
|
||||
# there are no dependencies that were pulled in at build time.
|
||||
"$PYBIN/pip" install virtualenv
|
||||
pip install virtualenv
|
||||
venv_dir=`mktemp -d`/venv
|
||||
"$PYBIN/python" -m virtualenv "$venv_dir"
|
||||
python -m virtualenv "$venv_dir"
|
||||
|
||||
export __CIBW_VIRTUALENV_PATH__=$venv_dir
|
||||
|
||||
# run the tests in a subshell to keep that `activate`
|
||||
# script from polluting the env
|
||||
@@ -97,6 +154,10 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef
|
||||
|
||||
echo "Running tests using `which python`"
|
||||
|
||||
if [ ! -z {before_test} ]; then
|
||||
sh -c {before_test}
|
||||
fi
|
||||
|
||||
# Install the wheel we just built
|
||||
# Note: If auditwheel produced two wheels, it's because the earlier produced wheel
|
||||
# conforms to multiple manylinux standards. These multiple versions of the wheel are
|
||||
@@ -117,7 +178,7 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef
|
||||
)
|
||||
# exit if tests failed (needed for older bash versions)
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1;
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
# clean up
|
||||
@@ -127,54 +188,70 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef
|
||||
# we're all done here; move it to output
|
||||
mv "${{repaired_wheels[@]}}" /output
|
||||
for repaired_wheel in "${{repaired_wheels[@]}}"; do chown {uid}:{gid} "/output/$(basename "$repaired_wheel")"; done
|
||||
done
|
||||
) done
|
||||
'''.format(
|
||||
pybin_paths=' '.join(c.path+'/bin' for c in platform_configs),
|
||||
pybin_paths=' '.join(c.path + '/bin' for c in platform_configs),
|
||||
test_requires=' '.join(test_requires),
|
||||
test_extras=test_extras,
|
||||
test_command=shlex_quote(
|
||||
test_command=shlex.quote(
|
||||
prepare_command(test_command, project='/project') if test_command else ''
|
||||
),
|
||||
before_build=shlex_quote(
|
||||
before_build=shlex.quote(
|
||||
prepare_command(before_build, project='/project') if before_build else ''
|
||||
),
|
||||
build_verbosity_flag=' '.join(get_build_verbosity_extra_flags(build_verbosity)),
|
||||
repair_command=shlex_quote(
|
||||
repair_command=shlex.quote(
|
||||
prepare_command(repair_command, wheel='"$1"', dest_dir='/tmp/repaired_wheels') if repair_command else ''
|
||||
),
|
||||
environment_exports='\n'.join(environment.as_shell_commands()),
|
||||
uid=os.getuid(),
|
||||
gid=os.getgid(),
|
||||
before_test=shlex.quote(
|
||||
prepare_command(before_test, project='/project') if before_test else ''
|
||||
),
|
||||
)
|
||||
|
||||
def run_docker(command, stdin_str=None):
|
||||
print('docker command: docker {}'.format(' '.join(map(shlex_quote, command))))
|
||||
if stdin_str is None:
|
||||
subprocess.check_call(['docker'] + command)
|
||||
else:
|
||||
args = ['docker'] + command
|
||||
process = subprocess.Popen(args, stdin=subprocess.PIPE, universal_newlines=True)
|
||||
try:
|
||||
process.communicate(stdin_str)
|
||||
except KeyboardInterrupt:
|
||||
process.kill()
|
||||
process.wait()
|
||||
if process.returncode != 0:
|
||||
raise subprocess.CalledProcessError(process.returncode, args)
|
||||
|
||||
container_name = 'cibuildwheel-{}'.format(uuid.uuid4())
|
||||
try:
|
||||
run_docker(['create',
|
||||
'--env', 'CIBUILDWHEEL',
|
||||
'--name', container_name,
|
||||
'-i',
|
||||
'-v', '/:/host', # ignored on Circle
|
||||
docker_image, '/bin/bash'])
|
||||
run_docker(['cp', os.path.abspath(project_dir) + '/.', container_name + ':/project'])
|
||||
run_docker(['start', '-i', '-a', container_name], stdin_str=bash_script)
|
||||
run_docker(['cp', container_name + ':/output/.', os.path.abspath(output_dir)])
|
||||
except subprocess.CalledProcessError:
|
||||
subprocess.run(['docker', 'create',
|
||||
'--env', 'CIBUILDWHEEL',
|
||||
'--name', container_name,
|
||||
'-i',
|
||||
'-v', '/:/host', # ignored on CircleCI
|
||||
docker_image, '/bin/bash'], check=True)
|
||||
subprocess.run(['docker', 'cp', os.path.abspath(project_dir) + '/.', container_name + ':/project'], check=True)
|
||||
subprocess.run(['docker', 'start', '-i', '-a', container_name], input=bash_script, universal_newlines=True, check=True)
|
||||
subprocess.run(['docker', 'cp', container_name + ':/output/.', os.path.abspath(output_dir)], check=True)
|
||||
except subprocess.CalledProcessError as error:
|
||||
troubleshoot(project_dir, error)
|
||||
exit(1)
|
||||
finally:
|
||||
# Still gets executed, even when 'exit(1)' gets called
|
||||
run_docker(['rm', '--force', '-v', container_name])
|
||||
subprocess.run(['docker', 'rm', '--force', '-v', container_name], check=True)
|
||||
|
||||
|
||||
def troubleshoot(project_dir, error):
|
||||
if (isinstance(error, subprocess.CalledProcessError) and 'start' in error.cmd):
|
||||
# the bash script failed
|
||||
print('Checking for common errors...')
|
||||
so_files = []
|
||||
for root, dirs, files in os.walk(project_dir):
|
||||
for name in files:
|
||||
_, ext = os.path.splitext(name)
|
||||
if ext == '.so':
|
||||
so_files.append(os.path.join(root, name))
|
||||
|
||||
if so_files:
|
||||
print(textwrap.dedent('''
|
||||
NOTE: Shared object (.so) files found in this project.
|
||||
|
||||
These files might be built against the wrong OS, causing problems with
|
||||
auditwheel.
|
||||
|
||||
If you're using Cython and have previously done an in-place build,
|
||||
remove those build files (*.so and *.c) before starting cibuildwheel.
|
||||
'''))
|
||||
|
||||
print(' Files detected:')
|
||||
print('\n'.join([' ' + f for f in so_files]))
|
||||
print('')
|
||||
|
||||
+136
-58
@@ -1,102 +1,178 @@
|
||||
from __future__ import print_function
|
||||
import os
|
||||
import shlex
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import os, subprocess, shlex, sys, shutil
|
||||
from collections import namedtuple
|
||||
from glob import glob
|
||||
try:
|
||||
from shlex import quote as shlex_quote
|
||||
except ImportError:
|
||||
from pipes import quote as shlex_quote
|
||||
|
||||
from .util import prepare_command, get_build_verbosity_extra_flags
|
||||
from .util import (
|
||||
download,
|
||||
get_build_verbosity_extra_flags,
|
||||
prepare_command,
|
||||
)
|
||||
|
||||
|
||||
def call(args, env=None, cwd=None, shell=False):
|
||||
# print the command executing for the logs
|
||||
if shell:
|
||||
print('+ %s' % args)
|
||||
else:
|
||||
print('+ ' + ' '.join(shlex.quote(a) for a in args))
|
||||
|
||||
return subprocess.check_call(args, env=env, cwd=cwd, shell=shell)
|
||||
|
||||
|
||||
def get_python_configurations(build_selector):
|
||||
PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'identifier', 'url'])
|
||||
python_configurations = [
|
||||
PythonConfiguration(version='2.7', identifier='cp27-macosx_10_6_intel', url='https://www.python.org/ftp/python/2.7.17/python-2.7.17-macosx10.6.pkg'),
|
||||
PythonConfiguration(version='3.5', identifier='cp35-macosx_10_6_intel', url='https://www.python.org/ftp/python/3.5.4/python-3.5.4-macosx10.6.pkg'),
|
||||
PythonConfiguration(version='3.6', identifier='cp36-macosx_10_6_intel', url='https://www.python.org/ftp/python/3.6.8/python-3.6.8-macosx10.6.pkg'),
|
||||
PythonConfiguration(version='3.7', identifier='cp37-macosx_10_6_intel', url='https://www.python.org/ftp/python/3.7.5/python-3.7.5-macosx10.6.pkg'),
|
||||
PythonConfiguration(version='3.8', identifier='cp38-macosx_10_9_x86_64', url='https://www.python.org/ftp/python/3.8.0/python-3.8.0-macosx10.9.pkg'),
|
||||
PythonConfiguration(version='2.7', identifier='cp27-macosx_x86_64', url='https://www.python.org/ftp/python/2.7.17/python-2.7.17-macosx10.9.pkg'),
|
||||
PythonConfiguration(version='3.5', identifier='cp35-macosx_x86_64', url='https://www.python.org/ftp/python/3.5.4/python-3.5.4-macosx10.6.pkg'),
|
||||
PythonConfiguration(version='3.6', identifier='cp36-macosx_x86_64', url='https://www.python.org/ftp/python/3.6.8/python-3.6.8-macosx10.9.pkg'),
|
||||
PythonConfiguration(version='3.7', identifier='cp37-macosx_x86_64', url='https://www.python.org/ftp/python/3.7.6/python-3.7.6-macosx10.9.pkg'),
|
||||
PythonConfiguration(version='3.8', identifier='cp38-macosx_x86_64', url='https://www.python.org/ftp/python/3.8.2/python-3.8.2-macosx10.9.pkg'),
|
||||
PythonConfiguration(version='2.7-v7.3.0', identifier='pp27-macosx_x86_64', url='https://bitbucket.org/pypy/pypy/downloads/pypy2.7-v7.3.0-osx64.tar.bz2'),
|
||||
PythonConfiguration(version='3.6-v7.3.0', identifier='pp36-macosx_x86_64', url='https://bitbucket.org/pypy/pypy/downloads/pypy3.6-v7.3.0-osx64.tar.bz2'),
|
||||
]
|
||||
|
||||
# skip builds as required
|
||||
return [c for c in python_configurations if build_selector(c.identifier)]
|
||||
|
||||
|
||||
def build(project_dir, output_dir, test_command, test_requires, test_extras, before_build, build_verbosity, build_selector, repair_command, environment):
|
||||
SYMLINKS_DIR = '/tmp/cibw_bin'
|
||||
|
||||
|
||||
def make_symlinks(installation_bin_path, python_executable, pip_executable):
|
||||
assert os.path.exists(os.path.join(installation_bin_path, python_executable))
|
||||
|
||||
# Python bin folders on Mac don't symlink `python3` to `python`, and neither
|
||||
# does PyPy for `pypy` or `pypy3`, so we do that so `python` and `pip` always
|
||||
# point to the active configuration.
|
||||
if os.path.exists(SYMLINKS_DIR):
|
||||
shutil.rmtree(SYMLINKS_DIR)
|
||||
os.makedirs(SYMLINKS_DIR)
|
||||
|
||||
os.symlink(os.path.join(installation_bin_path, python_executable), os.path.join(SYMLINKS_DIR, 'python'))
|
||||
os.symlink(os.path.join(installation_bin_path, python_executable + '-config'), os.path.join(SYMLINKS_DIR, 'python-config'))
|
||||
os.symlink(os.path.join(installation_bin_path, pip_executable), os.path.join(SYMLINKS_DIR, 'pip'))
|
||||
|
||||
|
||||
def install_cpython(version, url):
|
||||
installed_system_packages = subprocess.check_output(['pkgutil', '--pkgs'], universal_newlines=True).splitlines()
|
||||
|
||||
# if this version of python isn't installed, get it from python.org and install
|
||||
python_package_identifier = 'org.python.Python.PythonFramework-{}'.format(version)
|
||||
if python_package_identifier not in installed_system_packages:
|
||||
# download the pkg
|
||||
download(url, '/tmp/Python.pkg')
|
||||
# install
|
||||
call(['sudo', 'installer', '-pkg', '/tmp/Python.pkg', '-target', '/'])
|
||||
# patch open ssl
|
||||
if version == '3.5':
|
||||
open_ssl_patch_url = 'https://github.com/mayeut/patch-macos-python-openssl/releases/download/v1.0.2t/patch-macos-python-%s-openssl-v1.0.2t.tar.gz' % version
|
||||
download(open_ssl_patch_url, '/tmp/python-patch.tar.gz')
|
||||
call(['sudo', 'tar', '-C', '/Library/Frameworks/Python.framework/Versions/{}/'.format(version), '-xmf', '/tmp/python-patch.tar.gz'])
|
||||
|
||||
installation_bin_path = '/Library/Frameworks/Python.framework/Versions/{}/bin'.format(version)
|
||||
python_executable = 'python3' if version[0] == '3' else 'python'
|
||||
pip_executable = 'pip3' if version[0] == '3' else 'pip'
|
||||
make_symlinks(installation_bin_path, python_executable, pip_executable)
|
||||
|
||||
return installation_bin_path
|
||||
|
||||
|
||||
def install_pypy(version, url):
|
||||
pypy_tar_bz2 = url.rsplit('/', 1)[-1]
|
||||
assert pypy_tar_bz2.endswith(".tar.bz2")
|
||||
pypy_base_filename = os.path.splitext(os.path.splitext(pypy_tar_bz2)[0])[0]
|
||||
installation_path = os.path.join('/tmp', pypy_base_filename)
|
||||
if not os.path.exists(installation_path):
|
||||
download(url, os.path.join("/tmp", pypy_tar_bz2))
|
||||
call(['tar', '-C', '/tmp', '-xf', os.path.join("/tmp", pypy_tar_bz2)])
|
||||
|
||||
# fix PyPy 7.3.0 bug resulting in wrong macOS platform tag
|
||||
if version.endswith("-v7.3.0") and version[0] == '3':
|
||||
patch_file = os.path.abspath(os.path.join(os.path.dirname(__file__), 'resources', 'pypy3.6.patch'))
|
||||
sysconfigdata_file = os.path.join(installation_path, 'lib_pypy', '_sysconfigdata.py')
|
||||
call(['patch', sysconfigdata_file, patch_file, '-N']) # Always has nonzero return code
|
||||
|
||||
installation_bin_path = os.path.join(installation_path, 'bin')
|
||||
python_executable = 'pypy3' if version[0] == '3' else 'pypy'
|
||||
pip_executable = 'pip3' if version[0] == '3' else 'pip'
|
||||
make_symlinks(installation_bin_path, python_executable, pip_executable)
|
||||
|
||||
return installation_bin_path
|
||||
|
||||
|
||||
def build(project_dir, output_dir, test_command, before_test, test_requires, test_extras, before_build, build_verbosity, build_selector, repair_command, environment):
|
||||
abs_project_dir = os.path.abspath(project_dir)
|
||||
temp_dir = tempfile.mkdtemp(prefix='cibuildwheel')
|
||||
built_wheel_dir = os.path.join(temp_dir, 'built_wheel')
|
||||
repaired_wheel_dir = os.path.join(temp_dir, 'repaired_wheel')
|
||||
|
||||
python_configurations = get_python_configurations(build_selector)
|
||||
|
||||
get_pip_url = 'https://bootstrap.pypa.io/get-pip.py'
|
||||
get_pip_script = '/tmp/get-pip.py'
|
||||
|
||||
pkgs_output = subprocess.check_output(['pkgutil', '--pkgs'])
|
||||
if sys.version_info[0] >= 3:
|
||||
pkgs_output = pkgs_output.decode('utf8')
|
||||
installed_system_packages = pkgs_output.splitlines()
|
||||
|
||||
def call(args, env=None, cwd=None, shell=False):
|
||||
# print the command executing for the logs
|
||||
if shell:
|
||||
print('+ %s' % args)
|
||||
else:
|
||||
print('+ ' + ' '.join(shlex_quote(a) for a in args))
|
||||
|
||||
return subprocess.check_call(args, env=env, cwd=cwd, shell=shell)
|
||||
|
||||
# get latest pip once and for all
|
||||
call(['curl', '-L', '-o', get_pip_script, get_pip_url])
|
||||
download(get_pip_url, get_pip_script)
|
||||
|
||||
for config in python_configurations:
|
||||
# if this version of python isn't installed, get it from python.org and install
|
||||
python_package_identifier = 'org.python.Python.PythonFramework-%s' % config.version
|
||||
if python_package_identifier not in installed_system_packages:
|
||||
# download the pkg
|
||||
call(['curl', '-L', '-o', '/tmp/Python.pkg', config.url])
|
||||
# install
|
||||
call(['sudo', 'installer', '-pkg', '/tmp/Python.pkg', '-target', '/'])
|
||||
# patch open ssl
|
||||
if config.version == '3.5':
|
||||
call(['curl', '-fsSLo', '/tmp/python-patch.tar.gz', 'https://github.com/mayeut/patch-macos-python-openssl/releases/download/v1.0.2t/patch-macos-python-%s-openssl-v1.0.2t.tar.gz' % config.version])
|
||||
call(['sudo', 'tar', '-C', '/Library/Frameworks/Python.framework/Versions/%s/' % config.version, '-xmf', '/tmp/python-patch.tar.gz'])
|
||||
|
||||
installation_bin_path = '/Library/Frameworks/Python.framework/Versions/{}/bin'.format(config.version)
|
||||
assert os.path.exists(os.path.join(installation_bin_path, 'python3' if config.version[0] == '3' else 'python'))
|
||||
|
||||
# Python bin folders on Mac don't symlink python3 to python, so we do that
|
||||
# so `python` and `pip` always point to the active configuration.
|
||||
if os.path.exists('/tmp/cibw_bin'):
|
||||
shutil.rmtree('/tmp/cibw_bin')
|
||||
os.makedirs('/tmp/cibw_bin')
|
||||
|
||||
if config.version[0] == '3':
|
||||
os.symlink(os.path.join(installation_bin_path, 'python3'), '/tmp/cibw_bin/python')
|
||||
os.symlink(os.path.join(installation_bin_path, 'python3-config'), '/tmp/cibw_bin/python-config')
|
||||
os.symlink(os.path.join(installation_bin_path, 'pip3'), '/tmp/cibw_bin/pip')
|
||||
if config.identifier.startswith('cp'):
|
||||
installation_bin_path = install_cpython(config.version, config.url)
|
||||
elif config.identifier.startswith('pp'):
|
||||
installation_bin_path = install_pypy(config.version, config.url)
|
||||
else:
|
||||
raise ValueError("Unknown Python implementation")
|
||||
|
||||
env = os.environ.copy()
|
||||
env['PATH'] = os.pathsep.join([
|
||||
'/tmp/cibw_bin',
|
||||
SYMLINKS_DIR,
|
||||
installation_bin_path,
|
||||
env['PATH'],
|
||||
])
|
||||
|
||||
# Fix issue with site.py setting the wrong `sys.prefix`, `sys.exec_prefix`,
|
||||
# `sys.path`, ... for PyPy: https://foss.heptapod.net/pypy/pypy/issues/3175
|
||||
# Also fix an issue with the shebang of installed scripts inside the
|
||||
# testing virtualenv- see https://github.com/theacodes/nox/issues/44 and
|
||||
# https://github.com/pypa/virtualenv/issues/620
|
||||
# Also see https://github.com/python/cpython/pull/9516
|
||||
env.pop('__PYVENV_LAUNCHER__', None)
|
||||
env = environment.as_dictionary(prev_environment=env)
|
||||
|
||||
# check what version we're on
|
||||
call(['which', 'python'], env=env)
|
||||
call(['python', '--version'], env=env)
|
||||
which_python = subprocess.check_output(['which', 'python'], env=env, universal_newlines=True).strip()
|
||||
if which_python != '/tmp/cibw_bin/python':
|
||||
print("cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it.", file=sys.stderr)
|
||||
exit(1)
|
||||
|
||||
# install pip & wheel
|
||||
call(['python', get_pip_script, '--no-setuptools', '--no-wheel'], env=env, cwd="/tmp")
|
||||
call(['python', get_pip_script], env=env, cwd="/tmp")
|
||||
assert os.path.exists(os.path.join(installation_bin_path, 'pip'))
|
||||
call(['which', 'pip'], env=env)
|
||||
call(['pip', '--version'], env=env)
|
||||
which_pip = subprocess.check_output(['which', 'pip'], env=env, universal_newlines=True).strip()
|
||||
if which_pip != '/tmp/cibw_bin/pip':
|
||||
print("cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it.", file=sys.stderr)
|
||||
exit(1)
|
||||
call(['pip', 'install', '--upgrade', 'setuptools', 'wheel', 'delocate'], env=env)
|
||||
|
||||
# setup target platform, only required for python 3.5
|
||||
if config.version == '3.5':
|
||||
if '_PYTHON_HOST_PLATFORM' not in env:
|
||||
# cross-compilation platform override
|
||||
env['_PYTHON_HOST_PLATFORM'] = 'macosx-10.9-x86_64'
|
||||
if 'ARCHFLAGS' not in env:
|
||||
# https://github.com/python/cpython/blob/a5ed2fe0eedefa1649aa93ee74a0bafc8e628a10/Lib/_osx_support.py#L260
|
||||
env['ARCHFLAGS'] = '-arch x86_64'
|
||||
if 'MACOSX_DEPLOYMENT_TARGET' not in env:
|
||||
env['MACOSX_DEPLOYMENT_TARGET'] = '10.9'
|
||||
|
||||
# run the before_build command
|
||||
if before_build:
|
||||
before_build_prepared = prepare_command(before_build, project=abs_project_dir)
|
||||
@@ -133,13 +209,15 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef
|
||||
os.path.join(venv_dir, 'bin'),
|
||||
virtualenv_env['PATH'],
|
||||
])
|
||||
# Fix some weird issue with the shebang of installed scripts
|
||||
# See https://github.com/theacodes/nox/issues/44 and https://github.com/pypa/virtualenv/issues/620
|
||||
virtualenv_env.pop('__PYVENV_LAUNCHER__', None)
|
||||
virtualenv_env["__CIBW_VIRTUALENV_PATH__"] = venv_dir
|
||||
|
||||
# check that we are using the Python from the virtual environment
|
||||
call(['which', 'python'], env=virtualenv_env)
|
||||
|
||||
if before_test:
|
||||
before_test_prepared = prepare_command(before_test, project=abs_project_dir)
|
||||
call(before_test_prepared, env=virtualenv_env, shell=True)
|
||||
|
||||
# install the wheel
|
||||
call(['pip', 'install', repaired_wheel + test_extras], env=virtualenv_env)
|
||||
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
:: To build extensions for 64 bit Python 3, we need to configure environment
|
||||
:: variables to use the MSVC 2010 C++ compilers from GRMSDKX_EN_DVD.iso of:
|
||||
:: MS Windows SDK for Windows 7 and .NET Framework 4 (SDK v7.1)
|
||||
::
|
||||
:: To build extensions for 64 bit Python 2, we need to configure environment
|
||||
:: variables to use the MSVC 2008 C++ compilers from GRMSDKX_EN_DVD.iso of:
|
||||
:: MS Windows SDK for Windows 7 and .NET Framework 3.5 (SDK v7.0)
|
||||
::
|
||||
:: 32 bit builds, and 64-bit builds for 3.5 and beyond, do not require specific
|
||||
:: environment configurations.
|
||||
::
|
||||
:: Note: this script needs to be run with the /E:ON and /V:ON flags for the
|
||||
:: cmd interpreter, at least for (SDK v7.0)
|
||||
::
|
||||
:: More details at:
|
||||
:: https://github.com/cython/cython/wiki/64BitCythonExtensionsOnWindows
|
||||
:: http://stackoverflow.com/a/13751649/163740
|
||||
::
|
||||
:: Author: Olivier Grisel
|
||||
:: License: CC0 1.0 Universal: http://creativecommons.org/publicdomain/zero/1.0/
|
||||
::
|
||||
:: Notes about batch files for Python people:
|
||||
::
|
||||
:: Quotes in values are literally part of the values:
|
||||
:: SET FOO="bar"
|
||||
:: FOO is now five characters long: " b a r "
|
||||
:: If you don't want quotes, don't include them on the right-hand side.
|
||||
::
|
||||
:: The CALL lines at the end of this file look redundant, but if you move them
|
||||
:: outside of the IF clauses, they do not run properly in the SET_SDK_64==Y
|
||||
:: case, I don't know why.
|
||||
@ECHO OFF
|
||||
|
||||
SET COMMAND_TO_RUN=%*
|
||||
SET WIN_SDK_ROOT=C:\Program Files\Microsoft SDKs\Windows
|
||||
SET WIN_WDK=c:\Program Files (x86)\Windows Kits\10\Include\wdf
|
||||
|
||||
:: Extract the major and minor versions, and allow for the minor version to be
|
||||
:: more than 9. This requires the version number to have two dots in it.
|
||||
SET MAJOR_PYTHON_VERSION=%PYTHON_VERSION:~0,1%
|
||||
IF "%PYTHON_VERSION:~3,1%" == "." (
|
||||
SET MINOR_PYTHON_VERSION=%PYTHON_VERSION:~2,1%
|
||||
) ELSE (
|
||||
SET MINOR_PYTHON_VERSION=%PYTHON_VERSION:~2,2%
|
||||
)
|
||||
|
||||
:: Based on the Python version, determine what SDK version to use, and whether
|
||||
:: to set the SDK for 64-bit.
|
||||
IF %MAJOR_PYTHON_VERSION% == 2 (
|
||||
SET WINDOWS_SDK_VERSION="v7.0"
|
||||
SET SET_SDK_64=Y
|
||||
) ELSE (
|
||||
IF %MAJOR_PYTHON_VERSION% == 3 (
|
||||
SET WINDOWS_SDK_VERSION="v7.1"
|
||||
IF %MINOR_PYTHON_VERSION% LEQ 4 (
|
||||
SET SET_SDK_64=Y
|
||||
) ELSE (
|
||||
SET SET_SDK_64=N
|
||||
IF EXIST "%WIN_WDK%" (
|
||||
:: See: https://connect.microsoft.com/VisualStudio/feedback/details/1610302/
|
||||
REN "%WIN_WDK%" 0wdf
|
||||
)
|
||||
)
|
||||
) ELSE (
|
||||
ECHO Unsupported Python version: "%MAJOR_PYTHON_VERSION%"
|
||||
EXIT 1
|
||||
)
|
||||
)
|
||||
|
||||
IF %PYTHON_ARCH% == 64 (
|
||||
IF %SET_SDK_64% == Y (
|
||||
ECHO Configuring Windows SDK %WINDOWS_SDK_VERSION% for Python %MAJOR_PYTHON_VERSION% on a 64 bit architecture
|
||||
SET DISTUTILS_USE_SDK=1
|
||||
SET MSSdk=1
|
||||
"%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Setup\WindowsSdkVer.exe" -q -version:%WINDOWS_SDK_VERSION%
|
||||
"%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Bin\SetEnv.cmd" /x64 /release
|
||||
ECHO Executing: %COMMAND_TO_RUN%
|
||||
call %COMMAND_TO_RUN% || EXIT 1
|
||||
) ELSE (
|
||||
ECHO Using default MSVC build environment for 64 bit architecture
|
||||
ECHO Executing: %COMMAND_TO_RUN%
|
||||
call %COMMAND_TO_RUN% || EXIT 1
|
||||
)
|
||||
) ELSE (
|
||||
ECHO Using default MSVC build environment for 32 bit architecture
|
||||
ECHO Executing: %COMMAND_TO_RUN%
|
||||
call %COMMAND_TO_RUN% || EXIT 1
|
||||
)
|
||||
@@ -0,0 +1,7 @@
|
||||
--- a/lib_pypy/_sysconfigdata.py Tue Jan 28 22:54:59 2020 +0200
|
||||
+++ b/lib_pypy/_sysconfigdata.py Wed Jan 29 19:21:23 2020 +0200
|
||||
@@ -47,4 +47,5 @@
|
||||
build_time_vars['CC'] += ' -arch %s' % (arch,)
|
||||
if "CXX" in build_time_vars:
|
||||
build_time_vars['CXX'] += ' -arch %s' % (arch,)
|
||||
+ build_time_vars['MACOSX_DEPLOYMENT_TARGET'] = '10.7'
|
||||
+29
-3
@@ -1,5 +1,7 @@
|
||||
import os
|
||||
import urllib.request
|
||||
from fnmatch import fnmatch
|
||||
import warnings
|
||||
from time import sleep
|
||||
|
||||
|
||||
def prepare_command(command, **kwargs):
|
||||
@@ -21,7 +23,7 @@ def get_build_verbosity_extra_flags(level):
|
||||
return []
|
||||
|
||||
|
||||
class BuildSelector(object):
|
||||
class BuildSelector:
|
||||
def __init__(self, build_config, skip_config):
|
||||
self.build_patterns = build_config.split()
|
||||
self.skip_patterns = skip_config.split()
|
||||
@@ -36,7 +38,7 @@ class BuildSelector(object):
|
||||
|
||||
|
||||
# Taken from https://stackoverflow.com/a/107717
|
||||
class Unbuffered(object):
|
||||
class Unbuffered:
|
||||
def __init__(self, stream):
|
||||
self.stream = stream
|
||||
|
||||
@@ -50,3 +52,27 @@ class Unbuffered(object):
|
||||
|
||||
def __getattr__(self, attr):
|
||||
return getattr(self.stream, attr)
|
||||
|
||||
|
||||
def download(url, dest):
|
||||
print('+ Download ' + url + ' to ' + dest)
|
||||
dest_dir = os.path.dirname(dest)
|
||||
if not os.path.exists(dest_dir):
|
||||
os.makedirs(dest_dir)
|
||||
|
||||
repeat_num = 3
|
||||
for i in range(repeat_num):
|
||||
try:
|
||||
response = urllib.request.urlopen(url)
|
||||
except Exception:
|
||||
if i == repeat_num - 1:
|
||||
raise
|
||||
sleep(3)
|
||||
continue
|
||||
break
|
||||
|
||||
try:
|
||||
with open(dest, 'wb') as file:
|
||||
file.write(response.read())
|
||||
finally:
|
||||
response.close()
|
||||
|
||||
+100
-76
@@ -1,91 +1,92 @@
|
||||
from __future__ import print_function
|
||||
import os, tempfile, subprocess, shutil, sys
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from collections import namedtuple
|
||||
from glob import glob
|
||||
from zipfile import ZipFile
|
||||
|
||||
try:
|
||||
from shlex import quote as shlex_quote
|
||||
except ImportError:
|
||||
from pipes import quote as shlex_quote
|
||||
|
||||
try:
|
||||
from urllib.request import urlopen
|
||||
except ImportError:
|
||||
from urllib2 import urlopen
|
||||
|
||||
from .util import prepare_command, get_build_verbosity_extra_flags
|
||||
from .util import (
|
||||
download,
|
||||
get_build_verbosity_extra_flags,
|
||||
prepare_command,
|
||||
)
|
||||
|
||||
|
||||
IS_RUNNING_ON_AZURE = os.path.exists('C:\\hostedtoolcache')
|
||||
IS_RUNNING_ON_TRAVIS = os.environ.get('TRAVIS_OS_NAME') == 'windows'
|
||||
|
||||
|
||||
def get_python_path(config):
|
||||
nuget_args = get_nuget_args(config)
|
||||
return os.path.join(nuget_args[-1], nuget_args[0] + "." + config.version, "tools")
|
||||
def shell(args, env=None, cwd=None):
|
||||
print('+ ' + ' '.join(args))
|
||||
return subprocess.check_call(' '.join(args), env=env, cwd=cwd, shell=True)
|
||||
|
||||
|
||||
def get_nuget_args(configuration):
|
||||
python_name = "python" if configuration.version[0] == '3' else "python2"
|
||||
if configuration.arch == "32":
|
||||
python_name = python_name + "x86"
|
||||
return [python_name, "-Version", configuration.version, "-OutputDirectory", "C:/cibw/python"]
|
||||
def get_nuget_args(version, arch):
|
||||
python_name = 'python' if version[0] == '3' else 'python2'
|
||||
if arch == '32':
|
||||
python_name = python_name + 'x86'
|
||||
return [python_name, '-Version', version, '-OutputDirectory', 'C:\\cibw\\python']
|
||||
|
||||
|
||||
def get_python_configurations(build_selector):
|
||||
PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'arch', 'identifier'])
|
||||
PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'arch', 'identifier', 'url'])
|
||||
python_configurations = [
|
||||
PythonConfiguration(version='2.7.17', arch="32", identifier='cp27-win32'),
|
||||
PythonConfiguration(version='2.7.17', arch="64", identifier='cp27-win_amd64'),
|
||||
PythonConfiguration(version='3.5.4', arch="32", identifier='cp35-win32'),
|
||||
PythonConfiguration(version='3.5.4', arch="64", identifier='cp35-win_amd64'),
|
||||
PythonConfiguration(version='3.6.8', arch="32", identifier='cp36-win32'),
|
||||
PythonConfiguration(version='3.6.8', arch="64", identifier='cp36-win_amd64'),
|
||||
PythonConfiguration(version='3.7.5', arch="32", identifier='cp37-win32'),
|
||||
PythonConfiguration(version='3.7.5', arch="64", identifier='cp37-win_amd64'),
|
||||
PythonConfiguration(version='3.8.0', arch="32", identifier='cp38-win32'),
|
||||
PythonConfiguration(version='3.8.0', arch="64", identifier='cp38-win_amd64'),
|
||||
PythonConfiguration(version='2.7.17', arch='32', identifier='cp27-win32', url=None),
|
||||
PythonConfiguration(version='2.7.17', arch='64', identifier='cp27-win_amd64', url=None),
|
||||
PythonConfiguration(version='3.5.4', arch='32', identifier='cp35-win32', url=None),
|
||||
PythonConfiguration(version='3.5.4', arch='64', identifier='cp35-win_amd64', url=None),
|
||||
PythonConfiguration(version='3.6.8', arch='32', identifier='cp36-win32', url=None),
|
||||
PythonConfiguration(version='3.6.8', arch='64', identifier='cp36-win_amd64', url=None),
|
||||
PythonConfiguration(version='3.7.6', arch='32', identifier='cp37-win32', url=None),
|
||||
PythonConfiguration(version='3.7.6', arch='64', identifier='cp37-win_amd64', url=None),
|
||||
PythonConfiguration(version='3.8.2', arch='32', identifier='cp38-win32', url=None),
|
||||
PythonConfiguration(version='3.8.2', arch='64', identifier='cp38-win_amd64', url=None),
|
||||
PythonConfiguration(version='2.7-v7.3.0', arch='32', identifier='pp27-win32', url='https://bitbucket.org/pypy/pypy/downloads/pypy2.7-v7.3.0-win32.zip'),
|
||||
PythonConfiguration(version='3.6-v7.3.0', arch='32', identifier='pp36-win32', url='https://bitbucket.org/pypy/pypy/downloads/pypy3.6-v7.3.0-win32.zip'),
|
||||
]
|
||||
|
||||
if IS_RUNNING_ON_TRAVIS:
|
||||
# cannot install VCForPython27.msi which is needed for compiling C software
|
||||
# try with (and similar): msiexec /i VCForPython27.msi ALLUSERS=1 ACCEPT=YES /passive
|
||||
python_configurations = [c for c in python_configurations if not c.version.startswith('2.7.')]
|
||||
python_configurations = [c for c in python_configurations if not c.version.startswith('2.7')]
|
||||
|
||||
# skip builds as required
|
||||
# skip builds as required
|
||||
python_configurations = [c for c in python_configurations if build_selector(c.identifier)]
|
||||
|
||||
return python_configurations
|
||||
|
||||
|
||||
def build(project_dir, output_dir, test_command, test_requires, test_extras, before_build, build_verbosity, build_selector, repair_command, environment):
|
||||
def simple_shell(args, env=None, cwd=None):
|
||||
print('+ ' + ' '.join(args))
|
||||
args = ['cmd', '/E:ON', '/V:ON', '/C'] + args
|
||||
return subprocess.check_call(' '.join(args), env=env, cwd=cwd)
|
||||
def download(url, dest):
|
||||
print('+ Download ' + url + ' to ' + dest)
|
||||
dest_dir = os.path.dirname(dest)
|
||||
if not os.path.exists(dest_dir):
|
||||
os.makedirs(dest_dir)
|
||||
response = urlopen(url)
|
||||
try:
|
||||
with open(dest, 'wb') as file:
|
||||
file.write(response.read())
|
||||
finally:
|
||||
response.close()
|
||||
if IS_RUNNING_ON_AZURE or IS_RUNNING_ON_TRAVIS:
|
||||
shell = simple_shell
|
||||
else:
|
||||
run_with_env = os.path.abspath(os.path.join(os.path.dirname(__file__), 'resources', 'appveyor_run_with_env.cmd'))
|
||||
def extract_zip(zip_src, dest):
|
||||
with ZipFile(zip_src) as zip:
|
||||
zip.extractall(dest)
|
||||
|
||||
# run_with_env is a cmd file that sets the right environment variables
|
||||
# to build on AppVeyor.
|
||||
def shell(args, env=None, cwd=None):
|
||||
# print the command executing for the logs
|
||||
print('+ ' + ' '.join(args))
|
||||
args = ['cmd', '/E:ON', '/V:ON', '/C', run_with_env] + args
|
||||
return subprocess.check_call(' '.join(args), env=env, cwd=cwd)
|
||||
|
||||
def install_cpython(version, arch, nuget):
|
||||
nuget_args = get_nuget_args(version, arch)
|
||||
installation_path = os.path.join(nuget_args[-1], nuget_args[0] + '.' + version, 'tools')
|
||||
shell([nuget, 'install'] + nuget_args)
|
||||
return installation_path
|
||||
|
||||
|
||||
def install_pypy(version, arch, url):
|
||||
assert arch == '32'
|
||||
# Inside the PyPy zip file is a directory with the same name
|
||||
zip_filename = url.rsplit('/', 1)[-1]
|
||||
installation_path = os.path.join('C:\\cibw', os.path.splitext(zip_filename)[0])
|
||||
if not os.path.exists(installation_path):
|
||||
pypy_zip = os.path.join('C:\\cibw', zip_filename)
|
||||
download(url, pypy_zip)
|
||||
# Extract to the parent directory because the zip file still contains a directory
|
||||
extract_zip(pypy_zip, os.path.dirname(installation_path))
|
||||
pypy_exe = 'pypy3.exe' if version[0] == '3' else 'pypy.exe'
|
||||
shell(['mklink', os.path.join(installation_path, 'python.exe'), os.path.join(installation_path, pypy_exe)])
|
||||
shell(['mklink', '/d', os.path.join(installation_path, 'Scripts'), os.path.join(installation_path, 'bin')])
|
||||
return installation_path
|
||||
|
||||
|
||||
def build(project_dir, output_dir, test_command, before_test, test_requires, test_extras, before_build, build_verbosity, build_selector, repair_command, environment):
|
||||
abs_project_dir = os.path.abspath(project_dir)
|
||||
temp_dir = tempfile.mkdtemp(prefix='cibuildwheel')
|
||||
built_wheel_dir = os.path.join(temp_dir, 'built_wheel')
|
||||
@@ -101,36 +102,49 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef
|
||||
python_configurations = get_python_configurations(build_selector)
|
||||
for config in python_configurations:
|
||||
# install Python
|
||||
config_python_path = get_python_path(config)
|
||||
simple_shell([nuget, "install"] + get_nuget_args(config))
|
||||
assert os.path.exists(os.path.join(config_python_path, 'python.exe'))
|
||||
if config.identifier.startswith('cp'):
|
||||
installation_path = install_cpython(config.version, config.arch, nuget)
|
||||
elif config.identifier.startswith('pp'):
|
||||
installation_path = install_pypy(config.version, config.arch, config.url)
|
||||
else:
|
||||
raise ValueError("Unknown Python implementation")
|
||||
|
||||
assert os.path.exists(os.path.join(installation_path, 'python.exe'))
|
||||
|
||||
# set up PATH and environment variables for run_with_env
|
||||
env = os.environ.copy()
|
||||
env['PYTHON_VERSION'] = config.version
|
||||
env['PYTHON_ARCH'] = config.arch
|
||||
env['PATH'] = os.pathsep.join([
|
||||
config_python_path,
|
||||
os.path.join(config_python_path, 'Scripts'),
|
||||
installation_path,
|
||||
os.path.join(installation_path, 'Scripts'),
|
||||
env['PATH']
|
||||
])
|
||||
# update env with results from CIBW_ENVIRONMENT
|
||||
env = environment.as_dictionary(prev_environment=env)
|
||||
|
||||
# for the logs - check we're running the right version of python
|
||||
simple_shell(['where', 'python'], env=env)
|
||||
simple_shell(['python', '--version'], env=env)
|
||||
simple_shell(['python', '-c', '"import struct; print(struct.calcsize(\'P\') * 8)\"'], env=env)
|
||||
shell(['where', 'python'], env=env)
|
||||
shell(['python', '--version'], env=env)
|
||||
shell(['python', '-c', '"import struct; print(struct.calcsize(\'P\') * 8)"'], env=env)
|
||||
where_python = subprocess.check_output(['where', 'python'], env=env, universal_newlines=True).splitlines()[0].strip()
|
||||
if where_python != os.path.join(installation_path, 'python.exe'):
|
||||
print("cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it.", file=sys.stderr)
|
||||
exit(1)
|
||||
|
||||
# make sure pip is installed
|
||||
if not os.path.exists(os.path.join(config_python_path, 'Scripts', 'pip.exe')):
|
||||
simple_shell(['python', get_pip_script], env=env, cwd="C:\\cibw")
|
||||
assert os.path.exists(os.path.join(config_python_path, 'Scripts', 'pip.exe'))
|
||||
if not os.path.exists(os.path.join(installation_path, 'Scripts', 'pip.exe')):
|
||||
shell(['python', get_pip_script], env=env, cwd="C:\\cibw")
|
||||
assert os.path.exists(os.path.join(installation_path, 'Scripts', 'pip.exe'))
|
||||
where_pip = subprocess.check_output(['where', 'pip'], env=env, universal_newlines=True).splitlines()[0].strip()
|
||||
if where_pip.strip() != os.path.join(installation_path, 'Scripts', 'pip.exe'):
|
||||
print("cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it.", file=sys.stderr)
|
||||
exit(1)
|
||||
|
||||
# prepare the Python environment
|
||||
simple_shell(['python', '-m', 'pip', 'install', '--upgrade', 'pip'], env=env)
|
||||
simple_shell(['pip', '--version'], env=env)
|
||||
simple_shell(['pip', 'install', '--upgrade', 'setuptools', 'wheel'], env=env)
|
||||
shell(['python', '-m', 'pip', 'install', '--upgrade', 'pip'], env=env)
|
||||
shell(['pip', '--version'], env=env)
|
||||
shell(['pip', 'install', '--upgrade', 'setuptools', 'wheel'], env=env)
|
||||
|
||||
# run the before_build command
|
||||
if before_build:
|
||||
@@ -164,14 +178,24 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef
|
||||
shell(['python', '-m', 'virtualenv', venv_dir], env=env)
|
||||
|
||||
virtualenv_env = env.copy()
|
||||
|
||||
venv_script_path = os.path.join(venv_dir, 'Scripts')
|
||||
if os.path.exists(os.path.join(venv_dir, 'bin')):
|
||||
# pypy2.7 bugfix
|
||||
venv_script_path = os.pathsep.join([venv_script_path, os.path.join(venv_dir, 'bin')])
|
||||
virtualenv_env['PATH'] = os.pathsep.join([
|
||||
os.path.join(venv_dir, 'Scripts'),
|
||||
venv_script_path,
|
||||
virtualenv_env['PATH'],
|
||||
])
|
||||
virtualenv_env["__CIBW_VIRTUALENV_PATH__"] = venv_dir
|
||||
|
||||
# check that we are using the Python from the virtual environment
|
||||
shell(['which', 'python'], env=virtualenv_env)
|
||||
|
||||
if before_test:
|
||||
before_test_prepared = prepare_command(before_test, project=abs_project_dir)
|
||||
shell([before_test_prepared], env=virtualenv_env)
|
||||
|
||||
# install the wheel
|
||||
shell(['pip', 'install', repaired_wheel + test_extras], env=virtualenv_env)
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
---
|
||||
title: Modern C++ standards
|
||||
---
|
||||
|
||||
Building Python wheels with modern C++ standards (C++11 and later) requires a few tricks.
|
||||
|
||||
|
||||
## Python 2.7 and C++17
|
||||
|
||||
The Python 2.7 header files use the `register` keyword, which is [reserved and unused from C+17 onwards](https://en.cppreference.com/w/cpp/keyword/register). Compiling a wheel for Python 2.7 with the C++17 standard is still possible to allow usage of `register` using proper flag `-Wno-register` for gcc/clang and `/wd5033` for MSVC.
|
||||
|
||||
## manylinux1 and C++14
|
||||
The default `manylinux1` image (based on CentOS 5) contains a version of GCC and libstdc++ that only supports C++11 and earlier standards. There are however ways to compile wheels with the C++14 standard (and later): https://github.com/pypa/manylinux/issues/118
|
||||
|
||||
`manylinux2010` and `manylinux2014` are newer and support all C++ standards (up to C++17).
|
||||
|
||||
## macOS and deployment target versions
|
||||
|
||||
OS X/macOS allows you to specify a so-called "deployment target" version that will ensure backwards compatibility with older versions of macOS. One way to do this is by setting the `MACOSX_DEPLOYMENT_TARGET` environment variable. If not set, Python will set this variable to the version the Python distribution itself was compiled on (10.6 or 10.9, for the python.org packages), when creating the wheel.
|
||||
|
||||
However, to enable modern C++ standards, the deploment target needs to be set high enough (since older OS X/macOS versions did not have the necessary modern C++ standard library).
|
||||
|
||||
To get C++11 and C++14 support, `MACOSX_DEPLOYMENT_TARGET` needs to be set to (at least) `"10.9"`. By default, `cibuildwheel` already does this, building 64-bit-only wheels for macOS 10.9 and later.
|
||||
|
||||
To get C++17 support, set `MACOSX_DEPLOYMENT_TARGET` to (at least) `"10.13"` or `"10.14"` (macOS 10.13 offers partial C++17 support; e.g., the filesystem header is in experimental, offering `#include <experimental/filesystem>` instead of `#include <filesystem>`; macOS 10.14 has full C++17 support).
|
||||
|
||||
For more details see https://en.cppreference.com/w/cpp/compiler_support and https://xcodereleases.com/: Xcode 10 needs macOS 10.13 and Xcode 11 needs macOS 10.14.
|
||||
|
||||
## Windows and Python 2.7
|
||||
|
||||
Visual C++ for Python 2.7 does not support modern standards of C++. When building on Appveyor, you will need to either use the "Visual Studio 2017" or "Visual Studio 2019" image, but Python 2.7 is not supported on these images - skip it by setting `CIBW_SKIP=cp27-win*`.
|
||||
|
||||
There is an optional workaround for this, though: the pybind11 project argues and shows that it is [possible to compile Python 2.7 extension with a newer compiler](https://pybind11.readthedocs.io/en/stable/faq.html#working-with-ancient-visual-studio-2008-builds-on-windows) and has an example project showing how to do this: https://github.com/pybind/python_example. The main catch is that a user might need to install a newer "Microsoft Visual C++ Redistributable", since the newer C++ standard libraries are not included by default with the Python 2.7 installation.
|
||||
+25
-1
@@ -22,7 +22,7 @@ Linux wheels are built in the [`manylinux` docker images](https://github.com/pyp
|
||||
|
||||
- The project directory is mounted in the running Docker instance as `/project`, the output directory for the wheels as `/output`. In general, this is handled transparently by `cibuildwheel`. For a more finegrained level of control however, the root of the host file system is mounted as `/host`, allowing for example to access shared files, caches, etc. on the host file system. Note that this is not available on CircleCI due to their Docker policies.
|
||||
|
||||
- Alternative dockers images can be specified with the `CIBW_MANYLINUX_X86_64_IMAGE` and `CIBW_MANYLINUX_I686_IMAGE` options to allow for a custom, preconfigured build environment for the Linux builds. See [options](options.md#manylinux-image) for more details.
|
||||
- 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 packages with optional C extensions
|
||||
|
||||
@@ -37,3 +37,27 @@ myextension = Extension(
|
||||
optional=os.environ.get('CIBUILDWHEEL', '0') != '1',
|
||||
)
|
||||
```
|
||||
|
||||
### 'No module named XYZ' errors after running cibuildwheel on macOS
|
||||
|
||||
`cibuildwheel` on Mac installs the distributions from Python.org system-wide during its operation. This is necessary, but it can cause some confusing errors after cibuildwheel has finished.
|
||||
|
||||
Consider the build script:
|
||||
|
||||
```bash
|
||||
python3 -m pip install twine cibuildwheel
|
||||
python3 -m cibuildwheel --output-dir wheelhouse
|
||||
python3 -m twine upload wheelhouse/*.whl
|
||||
# error: no module named 'twine'
|
||||
```
|
||||
|
||||
This doesn't work because while `cibuildwheel` was running, it installed a few new versions of 'python3', so the `python3` run on line 3 isn't the same as the `python3` that ran on line 1.
|
||||
|
||||
Solutions to this vary, but the simplest is to install tools immediately before they're used:
|
||||
|
||||
```bash
|
||||
python3 -m pip install cibuildwheel
|
||||
python3 -m cibuildwheel --output-dir wheelhouse
|
||||
python3 -m pip install twine
|
||||
python3 -m twine upload wheelhouse/*.whl
|
||||
```
|
||||
|
||||
@@ -1,6 +1,24 @@
|
||||
import mkdocs, re, os, io, cgi
|
||||
import cgi
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
|
||||
TAG_REGEX_PATTERN = re.compile(
|
||||
import mkdocs
|
||||
|
||||
INCLUDE_TAG_REGEX = re.compile(
|
||||
r'''
|
||||
{% # opening tag
|
||||
\s*
|
||||
include # directive name
|
||||
\s+
|
||||
"(?P<filename>[^"]+)" # "filename"
|
||||
\s*
|
||||
%} # closing tag
|
||||
''',
|
||||
flags=re.VERBOSE,
|
||||
)
|
||||
|
||||
INCLUDEMARKDOWN_TAG_REGEX = re.compile(
|
||||
r'''
|
||||
{% # opening tag
|
||||
\s*
|
||||
@@ -15,11 +33,29 @@ TAG_REGEX_PATTERN = re.compile(
|
||||
flags=re.VERBOSE,
|
||||
)
|
||||
|
||||
|
||||
class ImportMarkdownPlugin(mkdocs.plugins.BasePlugin):
|
||||
def on_page_markdown(self, markdown, page, **kwargs):
|
||||
page_src_path = page.file.abs_src_path
|
||||
|
||||
def found_import_markdown_tag(match):
|
||||
def found_include_tag(match):
|
||||
filename = match.group('filename')
|
||||
|
||||
file_path_abs = os.path.join(os.path.dirname(page_src_path), filename)
|
||||
|
||||
if not os.path.exists(file_path_abs):
|
||||
raise ValueError('file not found', filename)
|
||||
|
||||
with io.open(file_path_abs, encoding='utf8') as f:
|
||||
text_to_include = f.read()
|
||||
|
||||
# Allow good practice of having a final newline in the file
|
||||
if text_to_include.endswith('\n'):
|
||||
text_to_include = text_to_include[:-1]
|
||||
|
||||
return text_to_include
|
||||
|
||||
def found_includemarkdown_tag(match):
|
||||
filename = match.group('filename')
|
||||
start = match.group('start')
|
||||
end = match.group('end')
|
||||
@@ -31,21 +67,21 @@ class ImportMarkdownPlugin(mkdocs.plugins.BasePlugin):
|
||||
|
||||
with io.open(file_path_abs, encoding='utf8') as f:
|
||||
text_to_include = f.read()
|
||||
|
||||
|
||||
if start:
|
||||
_, _, text_to_include = text_to_include.partition(start)
|
||||
|
||||
if end:
|
||||
text_to_include, _, _ = text_to_include.partition(end)
|
||||
|
||||
|
||||
return (
|
||||
'<!-- BEGIN INCLUDE %s %s %s -->' % (
|
||||
filename, cgi.escape(start), cgi.escape(end)
|
||||
'<!-- BEGIN INCLUDE %s %s %s -->\n' % (
|
||||
filename, cgi.escape(start or ''), cgi.escape(end or '')
|
||||
)
|
||||
+ text_to_include
|
||||
+ '<!-- END INCLUDE -->'
|
||||
+ '\n<!-- END INCLUDE -->'
|
||||
)
|
||||
|
||||
markdown = re.sub(TAG_REGEX_PATTERN, found_import_markdown_tag, markdown)
|
||||
markdown = re.sub(INCLUDE_TAG_REGEX, found_include_tag, markdown)
|
||||
markdown = re.sub(INCLUDEMARKDOWN_TAG_REGEX, found_includemarkdown_tag, markdown)
|
||||
return markdown
|
||||
|
||||
+65
-22
@@ -56,7 +56,7 @@ Default: `auto`
|
||||
|
||||
`auto` will auto-detect platform using environment variables, such as `TRAVIS_OS_NAME`/`APPVEYOR`/`CIRCLECI`.
|
||||
|
||||
For `linux` you need Docker running, on Mac 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 it will build and test for all versions of Python at `C:\PythonXX[-x64]`.
|
||||
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`.
|
||||
|
||||
@@ -64,25 +64,29 @@ This option can also be set using the command-line option `--platform`.
|
||||
|
||||
> Choose the Python versions to build
|
||||
|
||||
Space-separated list of builds to build and skip. Each build has an identifier like `cp27-manylinux_x86_64` or `cp35-macosx_10_6_intel` - you can list specific ones to build and `cibuildwheel` will only build those, and/or list ones to skip and `cibuildwheel` won't try to build them.
|
||||
Space-separated list of builds to build and skip. Each build has an identifier like `cp27-manylinux_x86_64` or `cp35-macosx_x86_64` - you can list specific ones to build and `cibuildwheel` will only build those, and/or list ones to skip and `cibuildwheel` won't try to build them.
|
||||
|
||||
When both options are specified, both conditions are applied and only builds with a tag that matches `CIBW_BUILD` and does not match `CIBW_SKIP` will be built.
|
||||
|
||||
When setting the options, you can use shell-style globbing syntax (as per `fnmatch`). All the build identifiers supported by cibuildwheel are shown below:
|
||||
When setting the options, you can use shell-style globbing syntax (as per [`fnmatch`](https://docs.python.org/3/library/fnmatch.html)). All the build identifiers supported by cibuildwheel are shown below:
|
||||
|
||||
<div class="build-id-table-marker"></div>
|
||||
|
||||
| | macOS 64bit | macOS 32/64bit | Manylinux 64bit | Manylinux 32bit | Windows 64bit | Windows 32bit |
|
||||
|------------|-------------------------|------------------------|------------------------|----------------------|-----------------|----------------|
|
||||
| Python 2.7 | | cp27-macosx_10_6_intel | cp27-manylinux_x86_64 | cp27-manylinux_i686 | cp27-win_amd64 | cp27-win32 |
|
||||
| Python 3.5 | | cp35-macosx_10_6_intel | cp35-manylinux_x86_64 | cp35-manylinux_i686 | cp35-win_amd64 | cp35-win32 |
|
||||
| Python 3.6 | | cp36-macosx_10_6_intel | cp36-manylinux_x86_64 | cp36-manylinux_i686 | cp36-win_amd64 | cp36-win32 |
|
||||
| Python 3.7 | | cp37-macosx_10_6_intel | cp37-manylinux_x86_64 | cp37-manylinux_i686 | cp37-win_amd64 | cp37-win32 |
|
||||
| Python 3.8 | cp38-macosx_10_9_x86_64 | | cp38-manylinux_x86_64 | cp38-manylinux_i686 | cp38-win_amd64 | cp38-win32 |
|
||||
| | macOS 64bit | Manylinux x86 64bit | Manylinux x86 32bit | Windows 64bit | Windows 32bit | Manylinux Armv8 64bit | Manylinux PPC64LE | Manylinux s390x |
|
||||
|-----------------|---------------------|------------------------|----------------------|-----------------|----------------|------------------------|------------------------|----------------------|
|
||||
| Python 2.7 | cp27-macosx_x86_64 | cp27-manylinux_x86_64 | cp27-manylinux_i686 | cp27-win_amd64 | cp27-win32 | | | |
|
||||
| Python 3.5 | cp35-macosx_x86_64 | cp35-manylinux_x86_64 | cp35-manylinux_i686 | cp35-win_amd64 | cp35-win32 | cp35-manylinux_aarch64 | cp35-manylinux_ppc64le | cp35-manylinux_s390x |
|
||||
| Python 3.6 | cp36-macosx_x86_64 | cp36-manylinux_x86_64 | cp36-manylinux_i686 | cp36-win_amd64 | cp36-win32 | cp36-manylinux_aarch64 | cp36-manylinux_ppc64le | cp36-manylinux_s390x |
|
||||
| Python 3.7 | cp37-macosx_x86_64 | cp37-manylinux_x86_64 | cp37-manylinux_i686 | cp37-win_amd64 | cp37-win32 | cp37-manylinux_aarch64 | cp37-manylinux_ppc64le | cp37-manylinux_s390x |
|
||||
| Python 3.8 | cp38-macosx_x86_64 | cp38-manylinux_x86_64 | cp38-manylinux_i686 | cp38-win_amd64 | cp38-win32 | cp38-manylinux_aarch64 | cp38-manylinux_ppc64le | cp38-manylinux_s390x |
|
||||
| PyPy 2.7 v7.3.0 | pp27-macosx_x86_64 | pp27-manylinux_x86_64 | | | pp27-win32 | | | |
|
||||
| PyPy 3.6 v7.3.0 | pp36-macosx_x86_64 | pp36-manylinux_x86_64 | | | pp36-win32 | | | |
|
||||
|
||||
The list of supported and currently selected build identifiers can also be retrieved by passing the `--print-build-identifiers` flag to `cibuildwheel`.
|
||||
The format is `python_tag-platform_tag`, with tags similar to those in [PEP 425](https://www.python.org/dev/peps/pep-0425/#details).
|
||||
|
||||
For CPython, the minimally supported macOS version is 10.9; for PyPy 2.7 and PyPy 3.6, respectively macOS 10.7 and 10.13 or higher is required.
|
||||
|
||||
#### Examples
|
||||
|
||||
```yaml
|
||||
@@ -90,10 +94,10 @@ The format is `python_tag-platform_tag`, with tags similar to those in [PEP 425]
|
||||
CIBW_BUILD: cp36-*
|
||||
|
||||
# Skip building on Python 2.7 on the Mac
|
||||
CIBW_SKIP: cp27-macosx_10_6_intel
|
||||
CIBW_SKIP: cp27-macosx_x86_64
|
||||
|
||||
# Skip building on Python 3.8 on the Mac
|
||||
CIBW_SKIP: cp38-macosx_10_9_x86_64
|
||||
CIBW_SKIP: cp38-macosx_x86_64
|
||||
|
||||
# Skip building on Python 2.7 on all platforms
|
||||
CIBW_SKIP: cp27-*
|
||||
@@ -113,6 +117,12 @@ CIBW_SKIP: cp36-manylinux*
|
||||
# Only build on Python 3 and skip 32-bit builds
|
||||
CIBW_BUILD: cp3?-*
|
||||
CIBW_SKIP: "*-win32 *-manylinux_i686"
|
||||
|
||||
# Only build PyPy and CPython 3
|
||||
CIBW_BUILD: pp* cp3?-*
|
||||
|
||||
# Disable building PyPy wheels on all platforms
|
||||
CIBW_SKIP: pp*
|
||||
```
|
||||
|
||||
<style>
|
||||
@@ -148,7 +158,7 @@ You must set this variable to pass variables to Linux builds (since they execute
|
||||
|
||||
You can use `$PATH` syntax to insert other variables, or the `$(pwd)` syntax to insert the output of other shell commands.
|
||||
|
||||
To specify more than one environment variable separate it by space.
|
||||
To specify more than one environment variable, separate the assignments by spaces.
|
||||
|
||||
Platform-specific variants also available:<br/>
|
||||
`CIBW_ENVIRONMENT_MACOS` | `CIBW_ENVIRONMENT_WINDOWS` | `CIBW_ENVIRONMENT_LINUX`
|
||||
@@ -184,7 +194,7 @@ If dependencies are required to build your wheel (for example if you include a h
|
||||
|
||||
The active Python binary can be accessed using `python`, and pip with `pip`; `cibuildwheel` makes sure the right version of Python and pip will be executed. `{project}` can be used as a placeholder for the absolute path to the project's root and will be replaced by `cibuildwheel`.
|
||||
|
||||
On Linux and macOS, the command is run in a shell, so you can write things like `cmd1 && cmd2`.
|
||||
The command is run in a shell, so you can write things like `cmd1 && cmd2`.
|
||||
|
||||
Platform-specific variants also available:<br/>
|
||||
`CIBW_BEFORE_BUILD_MACOS` | `CIBW_BEFORE_BUILD_WINDOWS` | `CIBW_BEFORE_BUILD_LINUX`
|
||||
@@ -199,6 +209,9 @@ CIBW_BEFORE_BUILD: pip install pybind11
|
||||
|
||||
# chain commands using &&
|
||||
CIBW_BEFORE_BUILD: yum install -y libffi-dev && pip install .
|
||||
|
||||
# run a script that's inside your repo
|
||||
CIBW_BEFORE_BUILD: bash scripts/prepare_for_build.sh
|
||||
```
|
||||
|
||||
|
||||
@@ -208,7 +221,7 @@ CIBW_BEFORE_BUILD: yum install -y libffi-dev && pip install .
|
||||
Default:
|
||||
|
||||
- on Linux: `'auditwheel repair -w {dest_dir} {wheel}'`
|
||||
- on macOS: `'delocate-listdeps {wheel} && delocate-wheel -w {dest_dir} {wheel}'`
|
||||
- on macOS: `'delocate-listdeps {wheel} && delocate-wheel --require-archs x86_64 -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.
|
||||
@@ -219,7 +232,7 @@ The following placeholders must be used inside the command and will be replaced
|
||||
- `{wheel}` for the absolute path to the built wheel
|
||||
- `{dest_dir}` for the absolute path of the directory where to create the repaired wheel.
|
||||
|
||||
On Linux and macOS, the command is run in a shell, so you can write things like `cmd1 && cmd2`.
|
||||
The command is run in a shell, so you can run multiple commands like `cmd1 && cmd2`.
|
||||
|
||||
Platform-specific variants also available:<br/>
|
||||
`CIBW_REPAIR_WHEEL_COMMAND_MACOS` | `CIBW_REPAIR_WHEEL_COMMAND_WINDOWS` | `CIBW_REPAIR_WHEEL_COMMAND_LINUX`
|
||||
@@ -235,16 +248,17 @@ CIBW_REPAIR_WHEEL_COMMAND_LINUX: "auditwheel repair --lib-sdir . -w {dest_dir} {
|
||||
```
|
||||
|
||||
|
||||
### `CIBW_MANYLINUX_X86_64_IMAGE`, `CIBW_MANYLINUX_I686_IMAGE` {: #manylinux-image}
|
||||
### `CIBW_MANYLINUX_X86_64_IMAGE`, `CIBW_MANYLINUX_I686_IMAGE`, `CIBW_MANYLINUX_PYPY_X86_64_IMAGE`, `CIBW_MANYLINUX_AARCH64_IMAGE`, `CIBW_MANYLINUX_PPC64LE_IMAGE`, `CIBW_MANYLINUX_S390X_IMAGE` {: #manylinux-image}
|
||||
> Specify alternative manylinux docker images
|
||||
|
||||
An alternative Docker image to be used for building [`manylinux`](https://github.com/pypa/manylinux) wheels. `cibuildwheel` will then pull these instead of the default images, [`quay.io/pypa/manylinux2010_x86_64`](https://quay.io/pypa/manylinux2010_x86_64) and [`quay.io/pypa/manylinux2010_i686`](https://quay.io/pypa/manylinux2010_i686).
|
||||
An alternative Docker image to be used for building [`manylinux`](https://github.com/pypa/manylinux) wheels. `cibuildwheel` will then pull these instead of the default images, [`quay.io/pypa/manylinux2010_x86_64`](https://quay.io/pypa/manylinux2010_x86_64), [`quay.io/pypa/manylinux2010_i686`](https://quay.io/pypa/manylinux2010_i686), [`pypywheels/manylinux2010-pypy_x86_64`](https://hub.docker.com/r/pypywheels/manylinux2010-pypy_x86_64), [`quay.io/pypa/manylinux2014_aarch64`](https://quay.io/pypa/manylinux2014_aarch64), [`quay.io/pypa/manylinux2014_ppc64le`](https://quay.io/pypa/manylinux2014_ppc64le), and [`quay.io/pypa/manylinux2014_s390x`](https://quay.io/pypa/manylinux2010_s390x).
|
||||
|
||||
The value of this option can either be set to `manylinux1`, `manylinux2010` or `manylinux2014` to use the [official `manylinux` images](https://github.com/pypa/manylinux), or any other valid Docker image name.
|
||||
The value of this option can either be set to `manylinux1`, `manylinux2010` or `manylinux2014` to use the [official `manylinux` images](https://github.com/pypa/manylinux) and [PyPy `manylinux` images](https://github.com/pypy/manylinux), or any other valid Docker image name. Note that for PyPy, only the official `manylinux2010` image is currently available. For architectures other
|
||||
than x86 (x86\_64 and i686) manylinux2014 must be used because this is the first version of the manylinux specification that supports additional architectures.
|
||||
|
||||
Beware to specify a valid Docker image that can be used in the same way as the official, default Docker images: all necessary Python and pip versions need to be present in `/opt/python/`, and the `auditwheel` tool needs to be present for `cibuildwheel` to work. Apart from that, the architecture and relevant shared system libraries need to be manylinux1-, manylinux2010- or manylinux2014-compatible in order to produce valid `manylinux1`/`manylinux2010`/`manylinux2014` wheels (see https://github.com/pypa/manylinux, [PEP 513](https://www.python.org/dev/peps/pep-0513/), [PEP 571](https://www.python.org/dev/peps/pep-0571/ and [PEP 599](https://www.python.org/dev/peps/pep-0599/) for more details).
|
||||
Beware to specify a valid Docker image that can be used in the same way as the official, default Docker images: all necessary Python and pip versions need to be present in `/opt/python/`, and the `auditwheel` tool needs to be present for `cibuildwheel` to work. Apart from that, the architecture and relevant shared system libraries need to be manylinux1-, manylinux2010- or manylinux2014-compatible in order to produce valid `manylinux1`/`manylinux2010`/`manylinux2014` wheels (see [pypa/manylinux on GitHub](https://github.com/pypa/manylinux), [PEP 513](https://www.python.org/dev/peps/pep-0513/), [PEP 571](https://www.python.org/dev/peps/pep-0571/) and [PEP 599](https://www.python.org/dev/peps/pep-0599/) for more details).
|
||||
|
||||
Note that `auditwheel` detects the version of the `manylinux` standard in the Docker image through the `AUDITWHEEL_PLAT` environment variable, as `cibuildwheel` has no way of detecting the correct `--plat` command line argument to pass to `auditwheel` for a custom image. If a Docker image does not correctly set this `AUDITWHEEL_PLAT` environment variable, the `CIBW_ENVIRONMENT` option can be used to do so (e.g., `CIBW_ENVIRONMENT="manylinux2010_$(uname -m)"`).
|
||||
Note that `auditwheel` detects the version of the `manylinux` standard in the Docker image through the `AUDITWHEEL_PLAT` environment variable, as `cibuildwheel` has no way of detecting the correct `--plat` command line argument to pass to `auditwheel` for a custom image. If a Docker image does not correctly set this `AUDITWHEEL_PLAT` environment variable, the `CIBW_ENVIRONMENT` option can be used to do so (e.g., `CIBW_ENVIRONMENT='AUDITWHEEL_PLAT="manylinux2010_$(uname -m)"'`).
|
||||
|
||||
Note that `manylinux2014` doesn't support builds with Python 2.7 - when building with `manylinux2014`, skip Python 2.7 using `CIBW_SKIP` (see example below).
|
||||
|
||||
@@ -252,8 +266,10 @@ Note that `manylinux2014` doesn't support builds with Python 2.7 - when building
|
||||
|
||||
```yaml
|
||||
# build using the manylinux1 image to ensure manylinux1 wheels are produced
|
||||
# skip PyPy, since there is no PyPy manylinux1 image
|
||||
CIBW_MANYLINUX_X86_64_IMAGE: manylinux1
|
||||
CIBW_MANYLINUX_I686_IMAGE: manylinux1
|
||||
CIBW_SKIP: pp*
|
||||
|
||||
# build using the manylinux2014 image
|
||||
CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014
|
||||
@@ -272,7 +288,7 @@ CIBW_MANYLINUX_I686_IMAGE: dockcross/manylinux-x86
|
||||
|
||||
Shell command to run tests after the build. The wheel will be installed automatically and available for import from the tests. `{project}` can be used as a placeholder for the absolute path to the project's root and will be replaced by `cibuildwheel`.
|
||||
|
||||
On Linux and macOS, the command is run in a shell, so you can write things like `cmd1 && cmd2`.
|
||||
The command is run in a shell, so you can write things like `cmd1 && cmd2`.
|
||||
|
||||
Platform-specific variants also available:<br/>
|
||||
`CIBW_TEST_COMMAND_MACOS` | `CIBW_TEST_COMMAND_WINDOWS` | `CIBW_TEST_COMMAND_LINUX`
|
||||
@@ -327,6 +343,33 @@ Platform-specific variants also available:<br/>
|
||||
CIBW_TEST_EXTRAS: test,qt
|
||||
```
|
||||
|
||||
### `CIBW_BEFORE_TEST` {: #before-test}
|
||||
> Execute a shell command before testing each wheel
|
||||
|
||||
A shell command to run in **each** test virtual environment, before your wheel is installed and tested. This is useful if you need to install a non pip package, change values of environment variables
|
||||
or perform multi step pip installation (e.g. installing `scikit-build` or `cython` before install test package)
|
||||
|
||||
The active Python binary can be accessed using `python`, and pip with `pip`; `cibuildwheel` makes sure the right version of Python and pip will be executed. `{project}` can be used as a placeholder for the absolute path to the project's root and will be replaced by `cibuildwheel`.
|
||||
|
||||
The command is run in a shell, so you can write things like `cmd1 && cmd2`.
|
||||
|
||||
Platform-specific variants also available:<br/>
|
||||
`CIBW_BEFORE_TEST_MACOS` | `CIBW_BEFORE_TEST_WINDOWS` | `CIBW_BEFORE_TEST_LINUX`
|
||||
|
||||
#### Examples
|
||||
```yaml
|
||||
# install test dependencies with overwritten environment variables.
|
||||
CIBW_BEFORE_TEST: CC=gcc CXX=g++ pip install -r requirements.txt
|
||||
|
||||
# chain commands using &&
|
||||
CIBW_BEFORE_TEST: rm -rf ./data/cache && mkdir -p ./data/cache
|
||||
|
||||
# install non pip python package
|
||||
CIBW_BEFORE_TEST: cd some_dir; ./configure; make; make install
|
||||
|
||||
# install python packages that are required to install test dependencies
|
||||
CIBW_BEFORE_TEST: pip install cmake scikit-build
|
||||
```
|
||||
|
||||
## Other
|
||||
|
||||
|
||||
+23
-114
@@ -2,47 +2,31 @@
|
||||
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.
|
||||
|
||||
> 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.
|
||||
|
||||
For more info on this file, check out the [docs](https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions).
|
||||
|
||||
# Azure Pipelines [linux/mac/windows] {: #azure-pipelines}
|
||||
|
||||
Using Azure pipelines, you can build all three platforms on the same service. Create a `azure-pipelines.yml` file in your repo.
|
||||
To build Linux, Mac, and Windows wheels on Azure Pipelines, create a `azure-pipelines.yml` file in your repo.
|
||||
|
||||
> azure-pipelines.yml
|
||||
```yaml
|
||||
jobs:
|
||||
- job: linux
|
||||
pool: {vmImage: 'Ubuntu-16.04'}
|
||||
steps:
|
||||
- task: UsePythonVersion@0
|
||||
- bash: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install cibuildwheel==1.1.0
|
||||
cibuildwheel --output-dir wheelhouse .
|
||||
- task: PublishBuildArtifacts@1
|
||||
inputs: {pathtoPublish: 'wheelhouse'}
|
||||
- job: macos
|
||||
pool: {vmImage: 'macOS-10.13'}
|
||||
steps:
|
||||
- task: UsePythonVersion@0
|
||||
- bash: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install cibuildwheel==1.1.0
|
||||
cibuildwheel --output-dir wheelhouse .
|
||||
- task: PublishBuildArtifacts@1
|
||||
inputs: {pathtoPublish: 'wheelhouse'}
|
||||
- job: windows
|
||||
pool: {vmImage: 'vs2017-win2016'}
|
||||
steps:
|
||||
- task: UsePythonVersion@0
|
||||
- script: choco install vcpython27 -f -y
|
||||
displayName: Install Visual C++ for Python 2.7
|
||||
- bash: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install cibuildwheel==1.1.0
|
||||
cibuildwheel --output-dir wheelhouse .
|
||||
- task: PublishBuildArtifacts@1
|
||||
inputs: {pathtoPublish: 'wheelhouse'}
|
||||
{% include "../examples/azure-pipelines-minimal.yml" %}
|
||||
```
|
||||
|
||||
!!! note
|
||||
To support Python 3.5 on Windows, make sure to specify the use of `{vmImage: 'vs2017-win2016'}` on Windows, to ensure the required toolchain is available.
|
||||
|
||||
Commit this file, enable building of your repo on Azure Pipelines, and push.
|
||||
|
||||
Wheels will be stored for you and available through the Pipelines interface. For more info on this file, check out the [docs](https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema).
|
||||
@@ -53,33 +37,7 @@ To build Linux, Mac, and Windows wheels on Travis CI, create a `.travis.yml` fil
|
||||
|
||||
> .travis.yml
|
||||
```yaml
|
||||
language: python
|
||||
|
||||
jobs:
|
||||
include:
|
||||
# perform a linux build
|
||||
- services: docker
|
||||
# and a mac build
|
||||
- os: osx
|
||||
language: shell
|
||||
# and a windows build
|
||||
- os: windows
|
||||
language: shell
|
||||
before_install:
|
||||
- choco install python --version 3.8.0
|
||||
- export PATH="/c/Python38:/c/Python38/Scripts:$PATH"
|
||||
|
||||
env:
|
||||
global:
|
||||
- TWINE_USERNAME=joerick
|
||||
# Note: TWINE_PASSWORD is set in Travis settings
|
||||
|
||||
install:
|
||||
- python -m pip install twine cibuildwheel==1.1.0
|
||||
|
||||
script:
|
||||
# build the wheels, put them into './wheelhouse'
|
||||
- python -m cibuildwheel --output-dir wheelhouse
|
||||
{% include "../examples/travis-ci-minimal.yml" %}
|
||||
```
|
||||
|
||||
Note that building Windows Python 2.7 wheels on Travis is unsupported.
|
||||
@@ -94,44 +52,7 @@ To build Linux and Mac wheels on CircleCI, create a `.circleci/config.yml` file
|
||||
|
||||
> .circleci/config.yml
|
||||
```yaml
|
||||
version: 2
|
||||
|
||||
jobs:
|
||||
linux-wheels:
|
||||
working_directory: ~/linux-wheels
|
||||
docker:
|
||||
- image: circleci/python:3.6
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker
|
||||
- run:
|
||||
name: Build the Linux wheels.
|
||||
command: |
|
||||
pip install --user cibuildwheel==1.1.0
|
||||
cibuildwheel --output-dir wheelhouse
|
||||
- store_artifacts:
|
||||
path: wheelhouse/
|
||||
|
||||
osx-wheels:
|
||||
working_directory: ~/osx-wheels
|
||||
macos:
|
||||
xcode: "10.0.0"
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Build the OS X wheels.
|
||||
command: |
|
||||
pip install --user cibuildwheel==1.1.0
|
||||
cibuildwheel --output-dir wheelhouse
|
||||
- store_artifacts:
|
||||
path: wheelhouse/
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
all-tests:
|
||||
jobs:
|
||||
- linux-wheels
|
||||
- osx-wheels
|
||||
{% include "../examples/circleci-minimal.yml" %}
|
||||
```
|
||||
|
||||
Commit this file, enable building of your repo on CircleCI, and push.
|
||||
@@ -141,26 +62,14 @@ Commit this file, enable building of your repo on CircleCI, and push.
|
||||
|
||||
CircleCI will store the built wheels for you - you can access them from the project console. Check out the CircleCI [docs](https://circleci.com/docs/2.0/configuration-reference/#section=configuration) for more info on this config file.
|
||||
|
||||
# AppVeyor [linux/windows] {: #appveyor}
|
||||
# AppVeyor [linux/mac/windows] {: #appveyor}
|
||||
|
||||
To build Linux and Windows wheels on AppVeyor, create an `appveyor.yml` file in your repo.
|
||||
To build Linux, Mac, and Windows wheels on AppVeyor, create an `appveyor.yml` file in your repo.
|
||||
|
||||
> appveyor.yml
|
||||
|
||||
```yaml
|
||||
image:
|
||||
- Ubuntu
|
||||
- Visual Studio 2015
|
||||
build_script:
|
||||
# windows
|
||||
- cmd: pip install cibuildwheel==1.1.0
|
||||
- cmd: cibuildwheel --output-dir wheelhouse
|
||||
# linux
|
||||
- sh: "${HOME}/.localpython3.7.4/bin/python3 -m pip install cibuildwheel==1.1.0"
|
||||
- sh: "${HOME}/.localpython3.7.4/bin/python3 -m cibuildwheel --output-dir wheelhouse"
|
||||
artifacts:
|
||||
- path: "wheelhouse\\*.whl"
|
||||
name: Wheels
|
||||
{% include "../examples/appveyor-minimal.yml" %}
|
||||
```
|
||||
|
||||
Commit this file, enable building of your repo on AppVeyor, and push.
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
environment:
|
||||
matrix:
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Ubuntu
|
||||
APPVEYOR_JOB_NAME: "python37-x64-ubuntu"
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
APPVEYOR_JOB_NAME: "python37-x64-vs2015"
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: macos-mojave
|
||||
APPVEYOR_JOB_NAME: "python37-x64-macos-mojave"
|
||||
|
||||
stack: python 3.7
|
||||
|
||||
init:
|
||||
- cmd: set PATH=C:\Python37;C:\Python37\Scripts;%PATH%
|
||||
|
||||
install: python -m pip install cibuildwheel==1.3.0
|
||||
|
||||
build_script: python -m cibuildwheel --output-dir wheelhouse
|
||||
|
||||
artifacts:
|
||||
- path: "wheelhouse\\*.whl"
|
||||
name: Wheels
|
||||
@@ -0,0 +1,35 @@
|
||||
jobs:
|
||||
- job: linux
|
||||
pool: {vmImage: 'Ubuntu-16.04'}
|
||||
steps:
|
||||
- task: UsePythonVersion@0
|
||||
- bash: |
|
||||
python3 -m pip install --upgrade pip
|
||||
pip3 install cibuildwheel==1.3.0
|
||||
cibuildwheel --output-dir wheelhouse .
|
||||
- task: PublishBuildArtifacts@1
|
||||
inputs: {pathtoPublish: 'wheelhouse'}
|
||||
|
||||
- job: macos
|
||||
pool: {vmImage: 'macOS-10.15'}
|
||||
steps:
|
||||
- task: UsePythonVersion@0
|
||||
- bash: |
|
||||
python3 -m pip install --upgrade pip
|
||||
pip3 install cibuildwheel==1.3.0
|
||||
cibuildwheel --output-dir wheelhouse .
|
||||
- task: PublishBuildArtifacts@1
|
||||
inputs: {pathtoPublish: 'wheelhouse'}
|
||||
|
||||
- job: windows
|
||||
pool: {vmImage: 'vs2017-win2016'}
|
||||
steps:
|
||||
- task: UsePythonVersion@0
|
||||
- script: choco install vcpython27 -f -y
|
||||
displayName: Install Visual C++ for Python 2.7
|
||||
- bash: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install cibuildwheel==1.3.0
|
||||
cibuildwheel --output-dir wheelhouse .
|
||||
- task: PublishBuildArtifacts@1
|
||||
inputs: {pathtoPublish: 'wheelhouse'}
|
||||
@@ -0,0 +1,38 @@
|
||||
version: 2
|
||||
|
||||
jobs:
|
||||
linux-wheels:
|
||||
working_directory: ~/linux-wheels
|
||||
docker:
|
||||
- image: circleci/python:3.6
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker
|
||||
- run:
|
||||
name: Build the Linux wheels.
|
||||
command: |
|
||||
pip3 install --user cibuildwheel==1.3.0
|
||||
cibuildwheel --output-dir wheelhouse
|
||||
- store_artifacts:
|
||||
path: wheelhouse/
|
||||
|
||||
osx-wheels:
|
||||
working_directory: ~/osx-wheels
|
||||
macos:
|
||||
xcode: "10.0.0"
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Build the OS X wheels.
|
||||
command: |
|
||||
pip3 install --user cibuildwheel==1.3.0
|
||||
cibuildwheel --output-dir wheelhouse
|
||||
- store_artifacts:
|
||||
path: wheelhouse/
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
all-tests:
|
||||
jobs:
|
||||
- linux-wheels
|
||||
- osx-wheels
|
||||
@@ -0,0 +1,37 @@
|
||||
name: Build
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build_wheels:
|
||||
name: Build wheel on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-18.04, windows-latest, macos-latest]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
|
||||
- uses: actions/setup-python@v1
|
||||
name: Install Python
|
||||
with:
|
||||
python-version: '3.7'
|
||||
|
||||
- name: Install cibuildwheel
|
||||
run: |
|
||||
python -m pip install cibuildwheel==1.3.0
|
||||
|
||||
- name: Install Visual C++ for Python 2.7
|
||||
if: startsWith(matrix.os, 'windows')
|
||||
run: |
|
||||
choco install vcpython27 -f -y
|
||||
|
||||
- name: Build wheel
|
||||
run: |
|
||||
python -m cibuildwheel --output-dir wheelhouse
|
||||
|
||||
- uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: wheels
|
||||
path: ./wheelhouse
|
||||
@@ -16,19 +16,25 @@ jobs:
|
||||
before_install:
|
||||
- choco install python --version 3.8.0
|
||||
- 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
|
||||
|
||||
env:
|
||||
global:
|
||||
- TWINE_USERNAME=joerick
|
||||
# Note: TWINE_PASSWORD is set in Travis settings
|
||||
- TWINE_USERNAME=__token__
|
||||
# Note: TWINE_PASSWORD is set to a PyPI API token in Travis settings
|
||||
|
||||
install:
|
||||
- python -m pip install twine cibuildwheel==1.0.0
|
||||
- python3 -m pip install cibuildwheel==1.3.0
|
||||
|
||||
script:
|
||||
# build the wheels, put them into './wheelhouse'
|
||||
- python -m cibuildwheel --output-dir wheelhouse
|
||||
- python3 -m cibuildwheel --output-dir wheelhouse
|
||||
|
||||
after_success:
|
||||
# if the release was tagged, upload them to PyPI
|
||||
- if [[ $TRAVIS_TAG ]]; then python -m twine upload wheelhouse/*.whl; fi
|
||||
- |
|
||||
if [[ $TRAVIS_TAG ]]; then
|
||||
python3 -m pip install twine
|
||||
python3 -m twine upload wheelhouse/*.whl
|
||||
fi
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
language: python
|
||||
|
||||
jobs:
|
||||
include:
|
||||
# perform a linux build
|
||||
- services: docker
|
||||
# perform a linux ARMv8 build
|
||||
- services: docker
|
||||
arch: aarch64
|
||||
# perform a linux PPC64LE build
|
||||
- services: docker
|
||||
arch: ppc64le
|
||||
# perform a linux S390X build
|
||||
- services: docker
|
||||
arch: s390x
|
||||
# and a mac build
|
||||
- os: osx
|
||||
language: shell
|
||||
# and a windows build
|
||||
- os: windows
|
||||
language: shell
|
||||
before_install:
|
||||
- choco install python --version 3.8.0
|
||||
- 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.3.0
|
||||
|
||||
script:
|
||||
# build the wheels, put them into './wheelhouse'
|
||||
- python3 -m cibuildwheel --output-dir wheelhouse
|
||||
@@ -17,21 +17,25 @@ before_install:
|
||||
if [[ "$TRAVIS_OS_NAME" = windows ]]; then
|
||||
choco install python --version 3.8.0
|
||||
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
|
||||
fi
|
||||
|
||||
install:
|
||||
- python -m pip install pytest
|
||||
- python3 -m pip install pytest
|
||||
|
||||
script:
|
||||
- python setup.py install
|
||||
- python3 setup.py install
|
||||
- pytest
|
||||
|
||||
stages:
|
||||
- test
|
||||
# Only execute deployment stage on master branch, tagged commits,
|
||||
# and from your repository (e.g. not PRs). Replace with your repo name.
|
||||
# Only execute deployment stage on tagged commits, and from your repository
|
||||
# (e.g. not PRs). Replace with your repo name.
|
||||
- name: deploy
|
||||
if: branch = master AND tag IS PRESENT AND repo = joerick/cibuildwheel
|
||||
if: tag IS PRESENT AND repo = joerick/cibuildwheel
|
||||
# To only build tags that look like vX.Y.Z:
|
||||
# if: tag =~ ^v\d+\.\d+\.\d+$ AND repo = joerick/cibuildwheel
|
||||
|
||||
jobs:
|
||||
include:
|
||||
@@ -44,34 +48,41 @@ jobs:
|
||||
- stage: deploy
|
||||
name: Deploy source distribution
|
||||
install: skip
|
||||
script: python setup.py sdist --formats=gztar
|
||||
after_success: python -m twine upload --skip-existing dist/*.tar.gz
|
||||
script: python3 setup.py sdist --formats=gztar
|
||||
after_success: |
|
||||
python3 -m pip install twine
|
||||
python3 -m twine upload --skip-existing dist/*.tar.gz
|
||||
# Deploy on linux
|
||||
- stage: deploy
|
||||
name: Build and deploy Linux wheels
|
||||
services: docker
|
||||
install: python -m pip install twine cibuildwheel==1.0.0
|
||||
script: python -m cibuildwheel --output-dir wheelhouse
|
||||
after_success: python -m twine upload --skip-existing wheelhouse/*.whl
|
||||
install: python3 -m pip install cibuildwheel==1.3.0
|
||||
script: python3 -m cibuildwheel --output-dir wheelhouse
|
||||
after_success: |
|
||||
python3 -m pip install twine
|
||||
python3 -m twine upload --skip-existing wheelhouse/*.whl
|
||||
# Deploy on mac
|
||||
- stage: deploy
|
||||
name: Build and deploy macOS wheels
|
||||
os: osx
|
||||
language: shell
|
||||
install: python -m pip install twine cibuildwheel==1.0.0
|
||||
script: python -m cibuildwheel --output-dir wheelhouse
|
||||
after_success: python -m twine upload --skip-existing wheelhouse/*.whl
|
||||
install: python3 -m pip install cibuildwheel==1.3.0
|
||||
script: python3 -m cibuildwheel --output-dir wheelhouse
|
||||
after_success: |
|
||||
python3 -m pip install twine
|
||||
python3 -m twine upload --skip-existing wheelhouse/*.whl
|
||||
# Deploy on windows
|
||||
- stage: deploy
|
||||
name: Build and deploy Windows wheels
|
||||
os: windows
|
||||
language: shell
|
||||
install: python -m pip install twine cibuildwheel==1.0.0
|
||||
script: python -m cibuildwheel --output-dir wheelhouse
|
||||
after_success: python -m twine upload --skip-existing wheelhouse/*.whl
|
||||
install: python3 -m pip install cibuildwheel==1.3.0
|
||||
script: python3 -m cibuildwheel --output-dir wheelhouse
|
||||
after_success: |
|
||||
python3 -m pip install twine
|
||||
python3 -m twine upload --skip-existing wheelhouse/*.whl
|
||||
|
||||
env:
|
||||
global:
|
||||
- TWINE_USERNAME=joerick
|
||||
# Note: TWINE_PASSWORD is set in Travis settings
|
||||
|
||||
- TWINE_USERNAME=__token__
|
||||
# Note: TWINE_PASSWORD is set to a PyPI API token in Travis settings
|
||||
|
||||
@@ -18,6 +18,7 @@ nav:
|
||||
- setup.md
|
||||
- options.md
|
||||
- deliver-to-pypi.md
|
||||
- cpp_standards.md
|
||||
- faq.md
|
||||
- contributing.md
|
||||
|
||||
|
||||
@@ -1,21 +1,12 @@
|
||||
[bumpversion]
|
||||
current_version = 1.1.0
|
||||
commit = True
|
||||
tag = True
|
||||
message = Bump version
|
||||
|
||||
[bumpversion:file:setup.py]
|
||||
|
||||
[bumpversion:file:cibuildwheel/__init__.py]
|
||||
|
||||
[bumpversion:file:README.md]
|
||||
search = cibuildwheel=={current_version}
|
||||
replace = cibuildwheel=={new_version}
|
||||
|
||||
[bumpversion:file:docs/setup.md]
|
||||
search = cibuildwheel=={current_version}
|
||||
replace = cibuildwheel=={new_version}
|
||||
|
||||
[bdist_wheel]
|
||||
universal = 1
|
||||
|
||||
[flake8]
|
||||
ignore = E501,W503
|
||||
application-import-names = cibuildwheel
|
||||
exclude =
|
||||
cibuildwheel/resources/,
|
||||
dist/,
|
||||
build/,
|
||||
.git/,
|
||||
env/,
|
||||
env2/,
|
||||
.venv/,
|
||||
site/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
import os, io
|
||||
import io
|
||||
import os
|
||||
|
||||
try:
|
||||
from setuptools import setup
|
||||
@@ -13,7 +13,7 @@ with io.open(os.path.join(this_directory, 'README.md'), encoding='utf-8') as f:
|
||||
|
||||
setup(
|
||||
name='cibuildwheel',
|
||||
version='1.1.0',
|
||||
version='1.3.0',
|
||||
install_requires=['bashlex!=0.13'],
|
||||
description="Build Python wheels on CI with minimal configuration.",
|
||||
long_description=long_description,
|
||||
@@ -21,19 +21,18 @@ setup(
|
||||
author="Joe Rickerby",
|
||||
author_email='joerick@mac.com',
|
||||
url='https://github.com/joerick/cibuildwheel',
|
||||
packages=['cibuildwheel',],
|
||||
packages=['cibuildwheel', ],
|
||||
license="BSD",
|
||||
zip_safe=False,
|
||||
package_data={
|
||||
'cibuildwheel': ['resources/*'],
|
||||
},
|
||||
# Supported python versions
|
||||
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*',
|
||||
python_requires='>=3.5',
|
||||
keywords='ci wheel packaging pypi travis appveyor macos linux windows',
|
||||
classifiers=[
|
||||
'Intended Audience :: Developers',
|
||||
'Natural Language :: English',
|
||||
'Programming Language :: Python :: 2',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Development Status :: 5 - Production/Stable',
|
||||
'License :: OSI Approved :: BSD License',
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import os
|
||||
import platform
|
||||
|
||||
import utils
|
||||
|
||||
|
||||
project_dir = os.path.dirname(__file__)
|
||||
|
||||
|
||||
def test():
|
||||
# build the wheels
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir)
|
||||
@@ -19,7 +21,10 @@ def test_build_identifiers():
|
||||
# after adding CIBW_MANYLINUX_IMAGE to support manylinux2010, there
|
||||
# can be multiple wheels for each wheel, though, so we need to limit
|
||||
# the expected wheels
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if not '-manylinux' in w or '-manylinux1' in w]
|
||||
if platform.machine() in ['x86_64', 'i686']:
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if '-manylinux' not in w or '-manylinux1' in w]
|
||||
else:
|
||||
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
||||
build_identifiers = utils.cibuildwheel_get_build_identifiers(project_dir)
|
||||
assert len(expected_wheels) == len(build_identifiers)
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import os
|
||||
|
||||
from setuptools import setup, Extension
|
||||
from setuptools import (
|
||||
Extension,
|
||||
setup,
|
||||
)
|
||||
|
||||
if os.environ.get('CIBUILDWHEEL', '0') != '1':
|
||||
raise Exception('CIBUILDWHEEL environment variable is not set to 1')
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import os, subprocess
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
import pytest
|
||||
|
||||
import utils
|
||||
|
||||
|
||||
def test():
|
||||
project_dir = os.path.dirname(__file__)
|
||||
|
||||
@@ -11,7 +15,7 @@ def test():
|
||||
# the 'false ||' bit is to ensure this command runs in a shell on
|
||||
# mac/linux.
|
||||
'CIBW_TEST_COMMAND': 'false || nosetests {project}/test',
|
||||
'CIBW_TEST_COMMAND_WINDOWS': 'nosetests {project}/test',
|
||||
'CIBW_TEST_COMMAND_WINDOWS': 'COLOR 00 || nosetests {project}/test',
|
||||
})
|
||||
|
||||
# also check that we got the right wheels
|
||||
@@ -28,7 +32,7 @@ def test_extras_require():
|
||||
# the 'false ||' bit is to ensure this command runs in a shell on
|
||||
# mac/linux.
|
||||
'CIBW_TEST_COMMAND': 'false || nosetests {project}/test',
|
||||
'CIBW_TEST_COMMAND_WINDOWS': 'nosetests {project}/test',
|
||||
'CIBW_TEST_COMMAND_WINDOWS': 'COLOR 00 || nosetests {project}/test',
|
||||
})
|
||||
|
||||
# also check that we got the right wheels
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
from setuptools import setup, Extension
|
||||
from setuptools import (
|
||||
Extension,
|
||||
setup,
|
||||
)
|
||||
|
||||
setup(
|
||||
name="spam",
|
||||
|
||||
@@ -1,8 +1,45 @@
|
||||
from __future__ import print_function
|
||||
import os
|
||||
import sys
|
||||
from unittest import TestCase
|
||||
|
||||
import spam
|
||||
|
||||
|
||||
def path_contains(parent, child):
|
||||
''' returns True if `child` is inside `parent`.
|
||||
|
||||
Works around path-comparison bugs caused by short-paths on Windows e.g.
|
||||
vssadm~1 instead of vssadministrator
|
||||
'''
|
||||
parent = os.path.abspath(parent)
|
||||
child = os.path.abspath(child)
|
||||
|
||||
while child != os.path.dirname(child):
|
||||
child = os.path.dirname(child)
|
||||
if os.stat(parent) == os.stat(child):
|
||||
# parent and child refer to the same directory on the filesystem
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class TestSpam(TestCase):
|
||||
def test_system(self):
|
||||
self.assertEqual(0, spam.system('python -c "exit(0)"'))
|
||||
self.assertNotEqual(0, spam.system('python -c "exit(1)"'))
|
||||
|
||||
def test_virtualenv(self):
|
||||
virtualenv_path = os.environ.get("__CIBW_VIRTUALENV_PATH__")
|
||||
if not virtualenv_path:
|
||||
self.fail("No virtualenv path defined in environment variable __CIBW_VIRTUALENV_PATH__")
|
||||
|
||||
print("=[executable]", sys.executable)
|
||||
print("=[spam location]", spam.__file__)
|
||||
print("=[virtualenv path]", virtualenv_path)
|
||||
print("=[listdir]", os.listdir(virtualenv_path))
|
||||
if os.path.exists(os.path.join(virtualenv_path, 'Scripts')):
|
||||
print("=[listdir]2", os.listdir(os.path.join(virtualenv_path, 'Scripts')))
|
||||
if os.path.exists(os.path.join(virtualenv_path, 'bin')):
|
||||
print("=[listdir]2", os.listdir(os.path.join(virtualenv_path, 'bin')))
|
||||
self.assertTrue(path_contains(virtualenv_path, sys.executable))
|
||||
self.assertTrue(path_contains(virtualenv_path, spam.__file__))
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import os
|
||||
|
||||
import utils
|
||||
|
||||
|
||||
def test():
|
||||
project_dir = os.path.dirname(__file__)
|
||||
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
from setuptools import setup, Extension
|
||||
import sys, os
|
||||
import os
|
||||
import sys
|
||||
|
||||
from setuptools import (
|
||||
Extension,
|
||||
setup,
|
||||
)
|
||||
|
||||
# assert that the Python version as written to pythonversion.txt in the CIBW_BEFORE_BUILD step
|
||||
# is the same one as is currently running.
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import os
|
||||
|
||||
import utils
|
||||
|
||||
|
||||
def test():
|
||||
project_dir = os.path.dirname(__file__)
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
from setuptools import setup, Extension
|
||||
import sys
|
||||
|
||||
from setuptools import (
|
||||
Extension,
|
||||
setup,
|
||||
)
|
||||
|
||||
# explode if run on Python 2.7 or Python 3.4 (these should be skipped)
|
||||
if sys.version_info[0:2] == (2, 7):
|
||||
raise Exception('Python 2.7 should not be built')
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import os
|
||||
import pytest
|
||||
import subprocess
|
||||
import utils
|
||||
|
||||
|
||||
def test():
|
||||
project_dir = os.path.dirname(__file__)
|
||||
|
||||
@@ -15,3 +18,15 @@ def test():
|
||||
# also check that we got the right wheels built
|
||||
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
|
||||
|
||||
def test_overridden_path(tmp_path):
|
||||
project_dir = os.path.dirname(__file__)
|
||||
|
||||
# mess up PATH, somehow
|
||||
with pytest.raises(subprocess.CalledProcessError):
|
||||
utils.cibuildwheel_run(project_dir, output_dir=tmp_path, add_env={
|
||||
'CIBW_ENVIRONMENT': '''SOMETHING="$(mkdir new_path && touch new_path/python)" PATH="$(realpath new_path):$PATH"''',
|
||||
'CIBW_ENVIRONMENT_WINDOWS': '''SOMETHING="$(mkdir new_path && type nul > new_path/python.exe)" PATH="$CD\\new_path;$PATH"''',
|
||||
})
|
||||
assert len(os.listdir(str(tmp_path))) == 0
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
from setuptools import setup, Extension
|
||||
import sys, os
|
||||
import os
|
||||
|
||||
from setuptools import (
|
||||
Extension,
|
||||
setup,
|
||||
)
|
||||
|
||||
# explode if environment isn't correct, as set in CIBW_ENVIRONMENT
|
||||
CIBW_TEST_VAR = os.environ.get('CIBW_TEST_VAR')
|
||||
@@ -15,7 +19,6 @@ if '/opt/cibw_test_path' not in PATH:
|
||||
if '$PATH' in PATH:
|
||||
raise Exception('$PATH should be expanded in PATH. It was "%s"' % PATH)
|
||||
|
||||
|
||||
setup(
|
||||
name="spam",
|
||||
ext_modules=[Extension('spam', sources=['spam.c'])],
|
||||
|
||||
@@ -1,20 +1,26 @@
|
||||
import os, pytest
|
||||
import os
|
||||
import platform
|
||||
|
||||
import pytest
|
||||
|
||||
import utils
|
||||
|
||||
|
||||
def test():
|
||||
project_dir = os.path.dirname(__file__)
|
||||
|
||||
if utils.platform != 'linux':
|
||||
pytest.skip('the test is only relevant to the linux build')
|
||||
if platform.machine() not in ['x86_64', 'i686']:
|
||||
pytest.skip('this test is currently only possible on x86_64/i686 due to availability of alternative images')
|
||||
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
||||
'CIBW_MANYLINUX_X86_64_IMAGE': 'dockcross/manylinux2010-x64',
|
||||
'CIBW_MANYLINUX_I686_IMAGE': 'dockcross/manylinux1-x86',
|
||||
'CIBW_BEFORE_BUILD': '/opt/python/cp36-cp36m/bin/pip install -U auditwheel', # Currently necessary on dockcross images to get auditwheel 2.1 supporting AUDITWHEEL_PLAT
|
||||
'CIBW_ENVIRONMENT': 'AUDITWHEEL_PLAT=`if [ $(uname -i) == "x86_64" ]; then echo "manylinux2010_x86_64"; else echo "manylinux1_i686"; fi`',
|
||||
'CIBW_MANYLINUX_I686_IMAGE': 'dockcross/manylinux2010-x86',
|
||||
'CIBW_SKIP': 'pp*',
|
||||
})
|
||||
|
||||
# also check that we got the right wheels built
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if '-manylinux2010_i686' not in w]
|
||||
if '-pp' not in w]
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import os, sys
|
||||
import os
|
||||
import sys
|
||||
|
||||
from setuptools import setup, Extension
|
||||
from setuptools import (
|
||||
Extension,
|
||||
setup,
|
||||
)
|
||||
|
||||
# check that we're running in the correct docker image as specified in the
|
||||
# environment options CIBW_MANYLINUX1_*_IMAGE
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import os
|
||||
|
||||
import utils
|
||||
|
||||
|
||||
def test():
|
||||
project_dir = os.path.dirname(__file__)
|
||||
# this test checks that SSL is working in the build environment using
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import ssl
|
||||
import sys
|
||||
from setuptools import setup, Extension
|
||||
|
||||
from setuptools import (
|
||||
Extension,
|
||||
setup,
|
||||
)
|
||||
|
||||
if sys.version_info[0] == 2:
|
||||
from urllib2 import urlopen
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import os, pytest
|
||||
import os
|
||||
import platform
|
||||
|
||||
import pytest
|
||||
|
||||
import utils
|
||||
|
||||
|
||||
@@ -8,6 +12,9 @@ def test(manylinux_image):
|
||||
|
||||
if utils.platform != 'linux':
|
||||
pytest.skip('the docker test is only relevant to the linux build')
|
||||
elif platform.machine not in ['x86_64', 'i686']:
|
||||
if manylinux_image in ['manylinux1', 'manylinux2010']:
|
||||
pytest.skip("manylinux1 and 2010 doesn't exist for non-x86 architectures")
|
||||
|
||||
# build the wheels
|
||||
# CFLAGS environment variable is necessary to fail on 'malloc_info' (on manylinux1) during compilation/linking,
|
||||
@@ -16,12 +23,22 @@ def test(manylinux_image):
|
||||
'CIBW_ENVIRONMENT': 'CFLAGS="$CFLAGS -Werror=implicit-function-declaration"',
|
||||
'CIBW_MANYLINUX_X86_64_IMAGE': manylinux_image,
|
||||
'CIBW_MANYLINUX_I686_IMAGE': manylinux_image,
|
||||
'CIBW_MANYLINUX_PYPY_X86_64_IMAGE': manylinux_image,
|
||||
'CIBW_MANYLINUX_AARCH64_IMAGE': manylinux_image,
|
||||
'CIBW_MANYLINUX_PPC64LE_IMAGE': manylinux_image,
|
||||
'CIBW_MANYLINUX_S390X_IMAGE': manylinux_image,
|
||||
}
|
||||
if manylinux_image == 'manylinux2014':
|
||||
add_env['CIBW_SKIP'] = 'cp27*' # not available on manylinux2014
|
||||
if manylinux_image == 'manylinux1':
|
||||
# We don't have a manylinux1 image for PyPy
|
||||
add_env['CIBW_SKIP'] = 'pp*'
|
||||
elif manylinux_image == 'manylinux2014':
|
||||
# We don't have a manylinux2014 image for PyPy (yet?)
|
||||
add_env['CIBW_SKIP'] = 'cp27* pp*' # Python 2.7 not available on manylinux2014
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
|
||||
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0', manylinux_versions=[manylinux_image])]
|
||||
if manylinux_image == 'manylinux2014':
|
||||
expected_wheels = [w for w in expected_wheels if '-cp27' not in w]
|
||||
if manylinux_image in ['manylinux1', 'manylinux2014']:
|
||||
expected_wheels = [w for w in expected_wheels if '-pp' not in w]
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
from setuptools import setup, Extension
|
||||
from setuptools import (
|
||||
Extension,
|
||||
setup,
|
||||
)
|
||||
|
||||
setup(
|
||||
name="spam",
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import os
|
||||
|
||||
import utils
|
||||
|
||||
|
||||
project_dir = os.path.dirname(__file__)
|
||||
|
||||
|
||||
def test():
|
||||
# build the wheels
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import os
|
||||
|
||||
from setuptools import setup, Extension
|
||||
|
||||
from setuptools import (
|
||||
Extension,
|
||||
setup,
|
||||
)
|
||||
|
||||
setup(
|
||||
ext_modules=[Extension('spam.spam', sources=['spam/spam.c'])],
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "0.1.0"
|
||||
__version__ = "0.1.0"
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import os
|
||||
|
||||
import pytest
|
||||
|
||||
import utils
|
||||
|
||||
project_dir = os.path.dirname(__file__)
|
||||
|
||||
|
||||
def test_cpp11(tmp_path):
|
||||
# This test checks that the C++11 standard is supported
|
||||
|
||||
add_env = {'CIBW_SKIP': 'cp27-win* pp27-win32', 'CIBW_ENVIRONMENT': 'STANDARD=11'}
|
||||
# VC++ for Python 2.7 does not support modern standards
|
||||
if utils.platform == 'macos':
|
||||
add_env['MACOSX_DEPLOYMENT_TARGET'] = '10.9'
|
||||
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
|
||||
expected_wheels = [w for w in utils.expected_wheels(
|
||||
'spam', '0.1.0', macosx_deployment_target='10.9')
|
||||
if 'cp27-cp27m-win' not in w
|
||||
and 'pp27-pypy_73-win32' not in w]
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
|
||||
|
||||
def test_cpp14():
|
||||
# This test checks that the C++14 standard is supported
|
||||
|
||||
add_env = {'CIBW_SKIP': 'cp27-win* pp27-win32 cp35-win*', 'CIBW_ENVIRONMENT': 'STANDARD=14'}
|
||||
# VC++ for Python 2.7 does not support modern standards
|
||||
# The manylinux1 docker image does not have a compiler which supports C++11
|
||||
# Python 3.4 and 3.5 are compiled with MSVC 10, which does not support C++14
|
||||
if utils.platform == 'macos':
|
||||
add_env['MACOSX_DEPLOYMENT_TARGET'] = '10.9'
|
||||
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
|
||||
expected_wheels = [w for w in utils.expected_wheels(
|
||||
'spam', '0.1.0', macosx_deployment_target='10.9')
|
||||
if 'cp27-cp27m-win' not in w
|
||||
and 'pp27-pypy_73-win32' not in w
|
||||
and 'cp35-cp35m-win' not in w]
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
|
||||
|
||||
def test_cpp17():
|
||||
# This test checks that the C++17 standard is supported
|
||||
|
||||
# Python and PyPy 2.7 use the `register` keyword which is forbidden in the C++17 standard
|
||||
# The manylinux1 docker image does not have a compiler which supports C++11
|
||||
# Python 3.5 and PyPy 3.6 are compiled with MSVC 10, which does not support C++17
|
||||
if os.environ.get('APPVEYOR_BUILD_WORKER_IMAGE', '') == 'Visual Studio 2015':
|
||||
pytest.skip('Visual Studio 2015 does not support C++17')
|
||||
|
||||
add_env = {'CIBW_SKIP': 'cp27-win* pp27-win32 cp35-win* pp36-win32', 'CIBW_ENVIRONMENT': 'STANDARD=17'}
|
||||
if utils.platform == 'macos':
|
||||
add_env['MACOSX_DEPLOYMENT_TARGET'] = '10.13'
|
||||
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
|
||||
expected_wheels = [w for w in utils.expected_wheels(
|
||||
'spam', '0.1.0', macosx_deployment_target='10.13')
|
||||
if 'cp27-cp27m-win' not in w
|
||||
and 'pp27-pypy_73-win32' not in w
|
||||
and 'cp35-cp35m-win' not in w
|
||||
and 'pp36-pypy36_pp73-win32' not in w]
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
@@ -0,0 +1,25 @@
|
||||
import os
|
||||
import platform
|
||||
|
||||
from setuptools import (
|
||||
Extension,
|
||||
setup,
|
||||
)
|
||||
|
||||
standard = os.environ["STANDARD"]
|
||||
|
||||
language_standard = "/std:c++" + standard if platform.system() == "Windows" else "-std=c++" + standard
|
||||
|
||||
extra_compile_args = [language_standard, "-DSTANDARD=" + standard]
|
||||
|
||||
if standard == "17":
|
||||
if platform.system() == "Windows":
|
||||
extra_compile_args.append("/wd5033")
|
||||
else:
|
||||
extra_compile_args.append("-Wno-register")
|
||||
|
||||
setup(
|
||||
name="spam",
|
||||
ext_modules=[Extension('spam', sources=['spam.cpp'], language="c++", extra_compile_args=extra_compile_args)],
|
||||
version="0.1.0",
|
||||
)
|
||||
@@ -0,0 +1,62 @@
|
||||
#include <Python.h>
|
||||
#include <string>
|
||||
|
||||
// Depending on the requested standard, use a modern C++ feature
|
||||
// that was introduced in that standard.
|
||||
#if STANDARD == 11
|
||||
#include <array>
|
||||
#elif STANDARD == 14
|
||||
int a = 100'000;
|
||||
#elif STANDARD == 17
|
||||
#include <utility>
|
||||
auto a = std::pair(5.0, false);
|
||||
#else
|
||||
#error Standard needed
|
||||
#endif
|
||||
|
||||
static PyObject *
|
||||
spam_system(PyObject *self, PyObject *args)
|
||||
{
|
||||
const char *command;
|
||||
int sts;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s", &command))
|
||||
return NULL;
|
||||
sts = system(command);
|
||||
return PyLong_FromLong(sts);
|
||||
}
|
||||
|
||||
/* Module initialization */
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
#define MOD_INIT(name) PyMODINIT_FUNC PyInit_##name(void)
|
||||
#define MOD_DEF(m, name, doc, methods, module_state_size) \
|
||||
static struct PyModuleDef moduledef = { \
|
||||
PyModuleDef_HEAD_INIT, name, doc, module_state_size, methods, }; \
|
||||
m = PyModule_Create(&moduledef);
|
||||
#define MOD_RETURN(m) return m;
|
||||
#else
|
||||
#define MOD_INIT(name) PyMODINIT_FUNC init##name(void)
|
||||
#define MOD_DEF(m, name, doc, methods, module_state_size) \
|
||||
m = Py_InitModule3(name, methods, doc);
|
||||
#define MOD_RETURN(m) return;
|
||||
#endif
|
||||
|
||||
static PyMethodDef module_methods[] = {
|
||||
{"system", (PyCFunction)spam_system, METH_VARARGS,
|
||||
"Execute a shell command."},
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
MOD_INIT(spam)
|
||||
{
|
||||
PyObject* m;
|
||||
|
||||
MOD_DEF(m,
|
||||
"spam",
|
||||
"Example module",
|
||||
module_methods,
|
||||
-1)
|
||||
|
||||
MOD_RETURN(m)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import os
|
||||
import utils
|
||||
|
||||
|
||||
def test():
|
||||
project_dir = os.path.dirname(__file__)
|
||||
|
||||
# build the wheels
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
||||
# write python version information to a temporary file, this is
|
||||
# checked in setup.py
|
||||
'CIBW_BEFORE_TEST': '''python -c "import sys; open('/tmp/pythonversion.txt', 'w').write(sys.version)" && python -c "import sys; open('/tmp/pythonprefix.txt', 'w').write(sys.prefix)"''',
|
||||
'CIBW_BEFORE_TEST_WINDOWS': '''python -c "import sys; open('c:\\pythonversion.txt', 'w').write(sys.version)" && python -c "import sys; open('c:\\pythonprefix.txt', 'w').write(sys.prefix)"''',
|
||||
'CIBW_TEST_REQUIRES': 'nose',
|
||||
# the 'false ||' bit is to ensure this command runs in a shell on
|
||||
# mac/linux.
|
||||
'CIBW_TEST_COMMAND': 'false || nosetests {project}/test',
|
||||
'CIBW_TEST_COMMAND_WINDOWS': 'nosetests {project}/test',
|
||||
})
|
||||
|
||||
# also check that we got the right wheels
|
||||
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
@@ -0,0 +1,8 @@
|
||||
from setuptools import setup, Extension
|
||||
|
||||
|
||||
setup(
|
||||
name="spam",
|
||||
ext_modules=[Extension('spam', sources=['spam.c'])],
|
||||
version="0.1.0",
|
||||
)
|
||||
@@ -0,0 +1,48 @@
|
||||
#include <Python.h>
|
||||
|
||||
static PyObject *
|
||||
spam_system(PyObject *self, PyObject *args)
|
||||
{
|
||||
const char *command;
|
||||
int sts;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s", &command))
|
||||
return NULL;
|
||||
sts = system(command);
|
||||
return PyLong_FromLong(sts);
|
||||
}
|
||||
|
||||
/* Module initialization */
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
#define MOD_INIT(name) PyMODINIT_FUNC PyInit_##name(void)
|
||||
#define MOD_DEF(m, name, doc, methods, module_state_size) \
|
||||
static struct PyModuleDef moduledef = { \
|
||||
PyModuleDef_HEAD_INIT, name, doc, module_state_size, methods, }; \
|
||||
m = PyModule_Create(&moduledef);
|
||||
#define MOD_RETURN(m) return m;
|
||||
#else
|
||||
#define MOD_INIT(name) PyMODINIT_FUNC init##name(void)
|
||||
#define MOD_DEF(m, name, doc, methods, module_state_size) \
|
||||
m = Py_InitModule3(name, methods, doc);
|
||||
#define MOD_RETURN(m) return;
|
||||
#endif
|
||||
|
||||
static PyMethodDef module_methods[] = {
|
||||
{"system", (PyCFunction)spam_system, METH_VARARGS,
|
||||
"Execute a shell command."},
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
MOD_INIT(spam)
|
||||
{
|
||||
PyObject* m;
|
||||
|
||||
MOD_DEF(m,
|
||||
"spam",
|
||||
"Example module",
|
||||
module_methods,
|
||||
-1)
|
||||
|
||||
MOD_RETURN(m)
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import sys
|
||||
import os
|
||||
from unittest import TestCase
|
||||
|
||||
|
||||
class TestBeforeTest(TestCase):
|
||||
def test_version(self):
|
||||
# assert that the Python version as written to pythonversion.txt in the CIBW_BEFORE_TEST step
|
||||
# is the same one as is currently running.
|
||||
# because of use symlinks in MacOS run this test is also need
|
||||
version_file = 'c:\\pythonversion.txt' if sys.platform == 'win32' else '/tmp/pythonversion.txt'
|
||||
with open(version_file) as f:
|
||||
stored_version = f.read()
|
||||
print('stored_version', stored_version)
|
||||
print('sys.version', sys.version)
|
||||
assert stored_version == sys.version
|
||||
|
||||
def test_prefix(self):
|
||||
# check that the prefix also was written
|
||||
prefix_file = 'c:\\pythonprefix.txt' if sys.platform == 'win32' else '/tmp/pythonprefix.txt'
|
||||
with open(prefix_file) as f:
|
||||
stored_prefix = f.read()
|
||||
print('stored_prefix', stored_prefix)
|
||||
print('sys.prefix', sys.prefix)
|
||||
# Works around path-comparison bugs caused by short-paths on Windows e.g.
|
||||
# vssadm~1 instead of vssadministrator
|
||||
|
||||
assert os.stat(stored_prefix) == os.stat(sys.prefix)
|
||||
+44
-19
@@ -4,10 +4,13 @@ Utility functions used by the cibuildwheel tests.
|
||||
This file is added to the PYTHONPATH in the test runner at bin/run_test.py.
|
||||
'''
|
||||
|
||||
import subprocess, sys, os, shutil
|
||||
from tempfile import mkdtemp
|
||||
import os
|
||||
import platform as pm
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from contextlib import contextmanager
|
||||
|
||||
from tempfile import mkdtemp
|
||||
|
||||
IS_WINDOWS_RUNNING_ON_AZURE = os.path.exists('C:\\hostedtoolcache')
|
||||
IS_WINDOWS_RUNNING_ON_TRAVIS = os.environ.get('TRAVIS_OS_NAME') == 'windows'
|
||||
@@ -53,6 +56,8 @@ def cibuildwheel_run(project_path, env=None, add_env=None, output_dir=None):
|
||||
'''
|
||||
if env is None:
|
||||
env = os.environ.copy()
|
||||
# If present in the host environment, remove the MACOSX_DEPLOYMENT_TARGET for consistency
|
||||
env.pop('MACOSX_DEPLOYMENT_TARGET', None)
|
||||
|
||||
if add_env is not None:
|
||||
env.update(add_env)
|
||||
@@ -66,7 +71,8 @@ def cibuildwheel_run(project_path, env=None, add_env=None, output_dir=None):
|
||||
return wheels
|
||||
|
||||
|
||||
def expected_wheels(package_name, package_version, manylinux_versions=['manylinux1', 'manylinux2010']):
|
||||
def expected_wheels(package_name, package_version, manylinux_versions=None,
|
||||
macosx_deployment_target=None):
|
||||
'''
|
||||
Returns a list of expected wheels from a run of cibuildwheel.
|
||||
'''
|
||||
@@ -74,26 +80,45 @@ def expected_wheels(package_name, package_version, manylinux_versions=['manylinu
|
||||
# {distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-{platform tag}.whl
|
||||
# {python tag} and {abi tag} are closely related to the python interpreter used to build the wheel
|
||||
# so we'll merge them below as python_abi_tag
|
||||
python_abi_tags = ['cp27-cp27m', 'cp35-cp35m', 'cp36-cp36m', 'cp37-cp37m', 'cp38-cp38']
|
||||
|
||||
python_abi_tags = ['cp35-cp35m', 'cp36-cp36m', 'cp37-cp37m', 'cp38-cp38']
|
||||
extra_x86_python_abi_tags = ['cp27-cp27m', 'pp27-pypy_73', 'pp36-pypy36_pp73']
|
||||
|
||||
if platform == 'linux':
|
||||
python_abi_tags.append('cp27-cp27mu') # python 2.7 has 2 different ABI on manylinux
|
||||
platform_tags = []
|
||||
for architecture in ['x86_64', 'i686']:
|
||||
for manylinux_version in manylinux_versions:
|
||||
platform_tags.append('{manylinux_version}_{architecture}'.format(
|
||||
manylinux_version=manylinux_version, architecture=architecture
|
||||
))
|
||||
if pm.machine() not in ['x86_64', 'i686']:
|
||||
if manylinux_versions is None:
|
||||
manylinux_versions = ['manylinux2014']
|
||||
architectures = {'cp': [pm.machine()]}
|
||||
else:
|
||||
if manylinux_versions is None:
|
||||
manylinux_versions = ['manylinux1', 'manylinux2010']
|
||||
python_abi_tags += extra_x86_python_abi_tags
|
||||
python_abi_tags.append('cp27-cp27mu') # python 2.7 has 2 different ABI on manylinux
|
||||
architectures = {'cp': ['x86_64', 'i686'], 'pp': ['x86_64']}
|
||||
platform_tags = {}
|
||||
for python_implemention in architectures:
|
||||
platform_tags[python_implemention] = [
|
||||
'{manylinux_version}_{architecture}'.format(
|
||||
manylinux_version=manylinux_version, architecture=architecture)
|
||||
for architecture in architectures[python_implemention]
|
||||
for manylinux_version in manylinux_versions
|
||||
]
|
||||
|
||||
def get_platform_tags(python_abi_tag):
|
||||
return platform_tags
|
||||
return platform_tags[python_abi_tag[:2]]
|
||||
elif platform == 'windows':
|
||||
python_abi_tags += extra_x86_python_abi_tags
|
||||
platform_tags = {'cp': ['win32', 'win_amd64'], 'pp': ['win32']}
|
||||
|
||||
def get_platform_tags(python_abi_tag):
|
||||
return ['win32', 'win_amd64']
|
||||
return platform_tags[python_abi_tag[:2]]
|
||||
|
||||
elif platform == 'macos':
|
||||
python_abi_tags += extra_x86_python_abi_tags
|
||||
|
||||
def get_platform_tags(python_abi_tag):
|
||||
if python_abi_tag == 'cp38-cp38':
|
||||
return ['macosx_10_9_x86_64']
|
||||
else:
|
||||
return ['macosx_10_6_intel']
|
||||
default_version = '10.7' if python_abi_tag.startswith('pp') else '10.9'
|
||||
return ['macosx_{}_x86_64'.format((macosx_deployment_target or default_version).replace('.', '_'))]
|
||||
else:
|
||||
raise Exception('unsupported platform')
|
||||
|
||||
@@ -107,7 +132,7 @@ def expected_wheels(package_name, package_version, manylinux_versions=['manylinu
|
||||
|
||||
if IS_WINDOWS_RUNNING_ON_TRAVIS:
|
||||
# Python 2.7 isn't supported on Travis.
|
||||
templates = [t for t in templates if '-cp27-' not in t]
|
||||
templates = [t for t in templates if '-cp27-' not in t and '-pp2' not in t]
|
||||
|
||||
return templates
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import os
|
||||
|
||||
from cibuildwheel.environment import parse_environment
|
||||
|
||||
|
||||
@@ -13,6 +14,7 @@ def test_basic_parsing():
|
||||
assert environment_dict == {'VAR': '1', 'VBR': '2'}
|
||||
assert environment_cmds == ['export VAR=1', 'export VBR=2']
|
||||
|
||||
|
||||
def test_quotes():
|
||||
environment_recipe = parse_environment('A=1 VAR="1 NOT_A_VAR=2" VBR=\'vbr\'')
|
||||
|
||||
@@ -24,6 +26,7 @@ def test_quotes():
|
||||
assert environment_dict == {'A': '1', 'VAR': '1 NOT_A_VAR=2', 'VBR': 'vbr'}
|
||||
assert environment_cmds == ['export A=1', 'export VAR="1 NOT_A_VAR=2"', 'export VBR=\'vbr\'']
|
||||
|
||||
|
||||
def test_inheritance():
|
||||
environment_recipe = parse_environment('PATH=$PATH:/usr/local/bin')
|
||||
|
||||
@@ -35,6 +38,7 @@ def test_inheritance():
|
||||
assert environment_dict == {'PATH': '/usr/bin:/usr/local/bin'}
|
||||
assert environment_cmds == ['export PATH=$PATH:/usr/local/bin']
|
||||
|
||||
|
||||
def test_shell_eval():
|
||||
environment_recipe = parse_environment('VAR="$(echo "a test" string)"')
|
||||
|
||||
@@ -49,6 +53,7 @@ def test_shell_eval():
|
||||
assert environment_dict['VAR'] == 'a test string'
|
||||
assert environment_cmds == ['export VAR="$(echo "a test" string)"']
|
||||
|
||||
|
||||
def test_shell_eval_and_env():
|
||||
environment_recipe = parse_environment('VAR="$(echo "$PREV_VAR" string)"')
|
||||
|
||||
@@ -60,6 +65,7 @@ def test_shell_eval_and_env():
|
||||
assert environment_dict == {'PREV_VAR': '1 2 3', 'VAR': '1 2 3 string'}
|
||||
assert environment_cmds == ['export VAR="$(echo "$PREV_VAR" string)"']
|
||||
|
||||
|
||||
def test_empty_var():
|
||||
environment_recipe = parse_environment('CFLAGS=')
|
||||
|
||||
@@ -71,6 +77,7 @@ def test_empty_var():
|
||||
assert environment_dict == {'CFLAGS': ''}
|
||||
assert environment_cmds == ['export CFLAGS=']
|
||||
|
||||
|
||||
def test_no_vars():
|
||||
environment_recipe = parse_environment('')
|
||||
|
||||
@@ -80,6 +87,7 @@ def test_no_vars():
|
||||
assert environment_dict == {}
|
||||
assert environment_cmds == []
|
||||
|
||||
|
||||
def test_no_vars_pass_through():
|
||||
environment_recipe = parse_environment('')
|
||||
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
from cibuildwheel import (
|
||||
linux,
|
||||
macos,
|
||||
util,
|
||||
windows,
|
||||
)
|
||||
|
||||
|
||||
class ArgsInterceptor:
|
||||
def __call__(self, *args, **kwargs):
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
|
||||
|
||||
MOCK_PROJECT_DIR = 'some_project_dir'
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_protection(monkeypatch):
|
||||
'''
|
||||
Ensure that a unit test will never actually run a cibuildwheel 'build'
|
||||
function, which shouldn't be run on a developer's machine
|
||||
'''
|
||||
|
||||
def fail_on_call(*args, **kwargs):
|
||||
raise RuntimeError("This should never be called")
|
||||
|
||||
monkeypatch.setattr(subprocess, 'Popen', fail_on_call)
|
||||
monkeypatch.setattr(util, 'download', fail_on_call)
|
||||
monkeypatch.setattr(windows, 'build', fail_on_call)
|
||||
monkeypatch.setattr(linux, 'build', fail_on_call)
|
||||
monkeypatch.setattr(macos, 'build', fail_on_call)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def fake_project_dir(monkeypatch):
|
||||
'''
|
||||
Monkey-patch enough for the main() function to run
|
||||
'''
|
||||
real_os_path_exists = os.path.exists
|
||||
|
||||
def mock_os_path_exists(path):
|
||||
if path == os.path.join(MOCK_PROJECT_DIR, 'setup.py'):
|
||||
return True
|
||||
else:
|
||||
return real_os_path_exists(path)
|
||||
|
||||
monkeypatch.setattr(os.path, 'exists', mock_os_path_exists)
|
||||
monkeypatch.setattr(sys, 'argv', ['cibuildwheel', MOCK_PROJECT_DIR])
|
||||
|
||||
|
||||
@pytest.fixture(params=['linux', 'macos', 'windows'])
|
||||
def platform(request, monkeypatch):
|
||||
platform_value = request.param
|
||||
monkeypatch.setenv('CIBW_PLATFORM', platform_value)
|
||||
return platform_value
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def intercepted_build_args(platform, monkeypatch):
|
||||
intercepted = ArgsInterceptor()
|
||||
monkeypatch.setattr(globals()[platform], 'build', intercepted)
|
||||
return intercepted
|
||||
@@ -0,0 +1,230 @@
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
from cibuildwheel.__main__ import main
|
||||
from cibuildwheel.environment import ParsedEnvironment
|
||||
from cibuildwheel.util import BuildSelector
|
||||
|
||||
|
||||
# CIBW_PLATFORM is tested in main_platform_test.py
|
||||
|
||||
|
||||
def test_output_dir(platform, intercepted_build_args, monkeypatch):
|
||||
OUTPUT_DIR = 'some_output_dir'
|
||||
|
||||
monkeypatch.setenv('CIBW_OUTPUT_DIR', OUTPUT_DIR)
|
||||
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.kwargs['output_dir'] == OUTPUT_DIR
|
||||
|
||||
|
||||
def test_output_dir_default(platform, intercepted_build_args, monkeypatch):
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.kwargs['output_dir'] == 'wheelhouse'
|
||||
|
||||
|
||||
@pytest.mark.parametrize('also_set_environment', [False, True])
|
||||
def test_output_dir_argument(also_set_environment, platform, intercepted_build_args, monkeypatch):
|
||||
OUTPUT_DIR = 'some_output_dir'
|
||||
|
||||
monkeypatch.setattr(sys, 'argv', sys.argv + ['--output-dir', OUTPUT_DIR])
|
||||
if also_set_environment:
|
||||
monkeypatch.setenv('CIBW_OUTPUT_DIR', 'not_this_output_dir')
|
||||
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.kwargs['output_dir'] == OUTPUT_DIR
|
||||
|
||||
|
||||
def test_build_selector(platform, intercepted_build_args, monkeypatch):
|
||||
BUILD = 'some build* *-selector'
|
||||
SKIP = 'some skip* *-selector'
|
||||
|
||||
monkeypatch.setenv('CIBW_BUILD', BUILD)
|
||||
monkeypatch.setenv('CIBW_SKIP', SKIP)
|
||||
|
||||
main()
|
||||
|
||||
intercepted_build_selector = intercepted_build_args.kwargs['build_selector']
|
||||
assert isinstance(intercepted_build_selector, BuildSelector)
|
||||
assert intercepted_build_selector('build-this')
|
||||
assert not intercepted_build_selector('skip-that')
|
||||
# This unit test is just testing the options of 'main'
|
||||
# Unit tests for BuildSelector are in build_selector_test.py
|
||||
|
||||
|
||||
@pytest.mark.parametrize('architecture, image, full_image', [
|
||||
('x86_64', None, 'quay.io/pypa/manylinux2010_x86_64'),
|
||||
('x86_64', 'manylinux1', 'quay.io/pypa/manylinux1_x86_64'),
|
||||
('x86_64', 'manylinux2010', 'quay.io/pypa/manylinux2010_x86_64'),
|
||||
('x86_64', 'manylinux2014', 'quay.io/pypa/manylinux2014_x86_64'),
|
||||
('x86_64', 'custom_image', 'custom_image'),
|
||||
('i686', None, 'quay.io/pypa/manylinux2010_i686'),
|
||||
('i686', 'manylinux1', 'quay.io/pypa/manylinux1_i686'),
|
||||
('i686', 'manylinux2010', 'quay.io/pypa/manylinux2010_i686'),
|
||||
('i686', 'manylinux2014', 'quay.io/pypa/manylinux2014_i686'),
|
||||
('i686', 'custom_image', 'custom_image'),
|
||||
('pypy_x86_64', None, 'pypywheels/manylinux2010-pypy_x86_64'),
|
||||
('pypy_x86_64', 'manylinux1', 'manylinux1'), # Does not exist
|
||||
('pypy_x86_64', 'manylinux2010', 'pypywheels/manylinux2010-pypy_x86_64'),
|
||||
('pypy_x86_64', 'manylinux2014', 'manylinux2014'), # Does not exist (yet)
|
||||
('pypy_x86_64', 'custom_image', 'custom_image'),
|
||||
])
|
||||
def test_manylinux_images(architecture, image, full_image, platform, intercepted_build_args, monkeypatch):
|
||||
if image is not None:
|
||||
monkeypatch.setenv('CIBW_MANYLINUX_' + architecture.upper() + '_IMAGE', image)
|
||||
|
||||
main()
|
||||
|
||||
if platform == 'linux':
|
||||
assert intercepted_build_args.kwargs['manylinux_images'][architecture] == full_image
|
||||
else:
|
||||
assert 'manylinux_images' not in intercepted_build_args.kwargs
|
||||
|
||||
|
||||
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}'
|
||||
elif platform == 'windows':
|
||||
return ''
|
||||
else:
|
||||
raise ValueError('Unknown platform', platform)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('repair_command', [None, 'repair', 'repair -w {dest_dir} {wheel}'])
|
||||
@pytest.mark.parametrize('platform_specific', [False, True])
|
||||
def test_repair_command(repair_command, platform_specific, platform, intercepted_build_args, monkeypatch):
|
||||
if repair_command is not None:
|
||||
if platform_specific:
|
||||
monkeypatch.setenv('CIBW_REPAIR_WHEEL_COMMAND_' + platform.upper(), repair_command)
|
||||
monkeypatch.setenv('CIBW_REPAIR_WHEEL_COMMAND', 'overwritten')
|
||||
else:
|
||||
monkeypatch.setenv('CIBW_REPAIR_WHEEL_COMMAND', repair_command)
|
||||
|
||||
main()
|
||||
|
||||
expected_repair = repair_command or get_default_repair_command(platform)
|
||||
assert intercepted_build_args.kwargs['repair_command'] == expected_repair
|
||||
|
||||
|
||||
@pytest.mark.parametrize('environment', [
|
||||
{},
|
||||
{'something': 'value'},
|
||||
{'something': 'value', 'something_else': 'other_value'}
|
||||
])
|
||||
@pytest.mark.parametrize('platform_specific', [False, True])
|
||||
def test_environment(environment, platform_specific, platform, intercepted_build_args, monkeypatch):
|
||||
env_string = ' '.join(['{}={}'.format(k, v) for k, v in environment.items()])
|
||||
if platform_specific:
|
||||
monkeypatch.setenv('CIBW_ENVIRONMENT_' + platform.upper(), env_string)
|
||||
monkeypatch.setenv('CIBW_ENVIRONMENT', 'overwritten')
|
||||
else:
|
||||
monkeypatch.setenv('CIBW_ENVIRONMENT', env_string)
|
||||
|
||||
main()
|
||||
|
||||
intercepted_environment = intercepted_build_args.kwargs['environment']
|
||||
assert isinstance(intercepted_environment, ParsedEnvironment)
|
||||
assert intercepted_environment.as_dictionary(prev_environment={}) == environment
|
||||
|
||||
|
||||
@pytest.mark.parametrize('test_requires', [None, 'requirement other_requirement'])
|
||||
@pytest.mark.parametrize('platform_specific', [False, True])
|
||||
def test_test_requires(test_requires, platform_specific, platform, intercepted_build_args, monkeypatch):
|
||||
if test_requires is not None:
|
||||
if platform_specific:
|
||||
monkeypatch.setenv('CIBW_TEST_REQUIRES_' + platform.upper(), test_requires)
|
||||
monkeypatch.setenv('CIBW_TEST_REQUIRES', 'overwritten')
|
||||
else:
|
||||
monkeypatch.setenv('CIBW_TEST_REQUIRES', test_requires)
|
||||
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.kwargs['test_requires'] == (test_requires or '').split()
|
||||
|
||||
|
||||
@pytest.mark.parametrize('test_extras', [None, 'extras'])
|
||||
@pytest.mark.parametrize('platform_specific', [False, True])
|
||||
def test_test_extras(test_extras, platform_specific, platform, intercepted_build_args, monkeypatch):
|
||||
if test_extras is not None:
|
||||
if platform_specific:
|
||||
monkeypatch.setenv('CIBW_TEST_EXTRAS_' + platform.upper(), test_extras)
|
||||
monkeypatch.setenv('CIBW_TEST_EXTRAS', 'overwritten')
|
||||
else:
|
||||
monkeypatch.setenv('CIBW_TEST_EXTRAS', test_extras)
|
||||
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.kwargs['test_extras'] == ('[' + test_extras + ']' if test_extras else '')
|
||||
|
||||
|
||||
@pytest.mark.parametrize('test_command', [None, 'test --command'])
|
||||
@pytest.mark.parametrize('platform_specific', [False, True])
|
||||
def test_test_command(test_command, platform_specific, platform, intercepted_build_args, monkeypatch):
|
||||
if test_command is not None:
|
||||
if platform_specific:
|
||||
monkeypatch.setenv('CIBW_TEST_COMMAND_' + platform.upper(), test_command)
|
||||
monkeypatch.setenv('CIBW_TEST_COMMAND', 'overwritten')
|
||||
else:
|
||||
monkeypatch.setenv('CIBW_TEST_COMMAND', test_command)
|
||||
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.kwargs['test_command'] == test_command
|
||||
|
||||
|
||||
@pytest.mark.parametrize('before_build', [None, 'before --build'])
|
||||
@pytest.mark.parametrize('platform_specific', [False, True])
|
||||
def test_before_build(before_build, platform_specific, platform, intercepted_build_args, monkeypatch):
|
||||
if before_build is not None:
|
||||
if platform_specific:
|
||||
monkeypatch.setenv('CIBW_BEFORE_BUILD_' + platform.upper(), before_build)
|
||||
monkeypatch.setenv('CIBW_BEFORE_BUILD', 'overwritten')
|
||||
else:
|
||||
monkeypatch.setenv('CIBW_BEFORE_BUILD', before_build)
|
||||
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.kwargs['before_build'] == before_build
|
||||
|
||||
|
||||
@pytest.mark.parametrize('build_verbosity', [None, 0, 2, -2, 4, -4])
|
||||
@pytest.mark.parametrize('platform_specific', [False, True])
|
||||
def test_build_verbosity(build_verbosity, platform_specific, platform, intercepted_build_args, monkeypatch):
|
||||
if build_verbosity is not None:
|
||||
if platform_specific:
|
||||
monkeypatch.setenv('CIBW_BUILD_VERBOSITY_' + platform.upper(), str(build_verbosity))
|
||||
monkeypatch.setenv('CIBW_BUILD_VERBOSITY', 'overwritten')
|
||||
else:
|
||||
monkeypatch.setenv('CIBW_BUILD_VERBOSITY', str(build_verbosity))
|
||||
|
||||
main()
|
||||
|
||||
expected_verbosity = max(-3, min(3, int(build_verbosity or 0)))
|
||||
assert intercepted_build_args.kwargs['build_verbosity'] == expected_verbosity
|
||||
|
||||
|
||||
@pytest.mark.parametrize('option_name', ['CIBW_BUILD', 'CIBW_SKIP'])
|
||||
@pytest.mark.parametrize('option_value, build_selector_patterns', [
|
||||
('*-manylinux1_*', ['*-manylinux_*']),
|
||||
('*-macosx_10_6_intel', ['*-macosx_x86_64']),
|
||||
('*-macosx_10_9_x86_64', ['*-macosx_x86_64']),
|
||||
('cp37-macosx_10_9_x86_64', ['cp37-macosx_x86_64']),
|
||||
])
|
||||
def test_build_selector_migrations(intercepted_build_args, monkeypatch, option_name, option_value, build_selector_patterns):
|
||||
monkeypatch.setenv(option_name, option_value)
|
||||
|
||||
main()
|
||||
|
||||
intercepted_build_selector = intercepted_build_args.kwargs['build_selector']
|
||||
assert isinstance(intercepted_build_selector, BuildSelector)
|
||||
|
||||
if option_name == 'CIBW_BUILD':
|
||||
assert intercepted_build_selector.build_patterns == build_selector_patterns
|
||||
else:
|
||||
assert intercepted_build_selector.skip_patterns == build_selector_patterns
|
||||
@@ -0,0 +1,60 @@
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
from cibuildwheel.__main__ import main
|
||||
|
||||
from conftest import MOCK_PROJECT_DIR # noqa: I100
|
||||
|
||||
|
||||
def test_unknown_platform_non_ci(monkeypatch, capsys):
|
||||
monkeypatch.delenv('CI', raising=False)
|
||||
monkeypatch.delenv('BITRISE_BUILD_NUMBER', raising=False)
|
||||
monkeypatch.delenv('AZURE_HTTP_USER_AGENT', raising=False)
|
||||
monkeypatch.delenv('GITHUB_WORKFLOW', raising=False)
|
||||
|
||||
with pytest.raises(SystemExit) as exit:
|
||||
main()
|
||||
assert exit.value.code == 2
|
||||
_, err = capsys.readouterr()
|
||||
|
||||
assert 'cibuildwheel: Unable to detect platform.' in err
|
||||
assert 'cibuildwheel should run on your CI server' in err
|
||||
|
||||
|
||||
def test_unknown_platform_on_ci(monkeypatch, capsys):
|
||||
monkeypatch.setenv('CI', 'true')
|
||||
monkeypatch.setattr(sys, 'platform', 'nonexistent')
|
||||
|
||||
with pytest.raises(SystemExit) as exit:
|
||||
main()
|
||||
assert exit.value.code == 2
|
||||
_, err = capsys.readouterr()
|
||||
|
||||
assert 'cibuildwheel: Unable to detect platform from "sys.platform"' in err
|
||||
|
||||
|
||||
def test_unknown_platform(monkeypatch, capsys):
|
||||
monkeypatch.setenv('CIBW_PLATFORM', 'nonexistent')
|
||||
|
||||
with pytest.raises(SystemExit) as exit:
|
||||
main()
|
||||
_, err = capsys.readouterr()
|
||||
|
||||
assert exit.value.code == 2
|
||||
assert 'cibuildwheel: Unsupported platform: nonexistent' in err
|
||||
|
||||
|
||||
def test_platform_argument(platform, intercepted_build_args, monkeypatch):
|
||||
monkeypatch.setenv('CIBW_PLATFORM', 'nonexistent')
|
||||
monkeypatch.setattr(sys, 'argv', sys.argv + ['--platform', platform])
|
||||
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.kwargs['project_dir'] == MOCK_PROJECT_DIR
|
||||
|
||||
|
||||
def test_platform_environment(platform, intercepted_build_args, monkeypatch):
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.kwargs['project_dir'] == MOCK_PROJECT_DIR
|
||||
@@ -1,59 +0,0 @@
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
import pytest
|
||||
|
||||
from cibuildwheel.__main__ import main
|
||||
from cibuildwheel import windows, linux, macos
|
||||
|
||||
|
||||
def not_call_mock(*args, **kwargs):
|
||||
raise RuntimeError("This should never be called")
|
||||
|
||||
|
||||
def apply_mock_protection(monkeypatch):
|
||||
monkeypatch.setattr(subprocess, "Popen", not_call_mock)
|
||||
monkeypatch.setattr(windows, "urlopen", not_call_mock)
|
||||
monkeypatch.setattr(windows, "build", not_call_mock)
|
||||
monkeypatch.setattr(linux, "build", not_call_mock)
|
||||
monkeypatch.setattr(macos, "build", not_call_mock)
|
||||
|
||||
|
||||
def test_unknown_platform_non_ci(monkeypatch, capsys, tmp_path):
|
||||
monkeypatch.setattr(os, 'environ', {})
|
||||
monkeypatch.setattr(sys, "argv", ["python", str(tmp_path)])
|
||||
apply_mock_protection(monkeypatch)
|
||||
with pytest.raises(SystemExit) as exit:
|
||||
main()
|
||||
assert exit.value.code == 2
|
||||
_, err = capsys.readouterr()
|
||||
assert 'cibuildwheel: Unable to detect platform.' in err
|
||||
assert "cibuildwheel should run on your CI server" in err
|
||||
|
||||
|
||||
def test_unknown_platform_on_ci(monkeypatch, capsys, tmp_path):
|
||||
monkeypatch.setattr(os, 'environ', {"CI": "true"})
|
||||
monkeypatch.setattr(sys, "argv", ["python", str(tmp_path)])
|
||||
apply_mock_protection(monkeypatch)
|
||||
monkeypatch.setattr(sys, "platform", "Something")
|
||||
|
||||
with pytest.raises(SystemExit) as exit:
|
||||
main()
|
||||
_, err = capsys.readouterr()
|
||||
assert exit.value.code == 2
|
||||
assert 'cibuildwheel: Unable to detect platform from "sys.platform"' in err
|
||||
|
||||
|
||||
def test_unknown_platform(monkeypatch, capsys, tmp_path):
|
||||
monkeypatch.setattr(os, 'environ', {"CIBW_PLATFORM": "Something"})
|
||||
monkeypatch.setattr(sys, "argv", ["python", str(tmp_path)])
|
||||
apply_mock_protection(monkeypatch)
|
||||
with open(str(tmp_path / "setup.py"), "w") as f:
|
||||
f.write('from setuptools import setup\nsetup(name="spam", version="0.1.0",)')
|
||||
|
||||
with pytest.raises(SystemExit) as exit:
|
||||
main()
|
||||
_, err = capsys.readouterr()
|
||||
assert exit.value.code == 2
|
||||
assert 'cibuildwheel: Unsupported platform: Something' in err
|
||||
Reference in New Issue
Block a user