Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ddc6d57a2 | ||
|
|
58fa77b73b | ||
|
|
048ddc4af3 | ||
|
|
c521dca40b | ||
|
|
ac2b391790 | ||
|
|
47fe71efb4 | ||
|
|
53ba9bd0d0 | ||
|
|
317397b39a | ||
|
|
8466194576 | ||
|
|
c68be58e78 | ||
|
|
fc24230cb4 | ||
|
|
c36be04ee8 | ||
|
|
7a27ae941c | ||
|
|
d0b6401129 | ||
|
|
141b3e031e | ||
|
|
0beb9518c9 | ||
|
|
ad51e796c0 | ||
|
|
db9d658b51 | ||
|
|
1d88c27530 | ||
|
|
a92959f191 | ||
|
|
321335e756 | ||
|
|
c88a73b40b | ||
|
|
4dd5c0e11c | ||
|
|
a2f4d2aa47 | ||
|
|
41891277d3 | ||
|
|
78ac83ff7e | ||
|
|
50a263df21 | ||
|
|
3ff84c52f9 | ||
|
|
e72e4683db | ||
|
|
dfd8f4809a | ||
|
|
666699a51e | ||
|
|
947059bbf4 | ||
|
|
4302090f08 | ||
|
|
c68d50a073 | ||
|
|
a03a26c90e | ||
|
|
e3b272201e | ||
|
|
43fcb31231 | ||
|
|
b4b46a1f06 | ||
|
|
2057bba07f | ||
|
|
46352c8e30 | ||
|
|
2e15fdddfd | ||
|
|
fc52c5e7b6 | ||
|
|
7b7e24d36d | ||
|
|
9f6c91404d | ||
|
|
219a65d5e3 | ||
|
|
36b8f7fff2 | ||
|
|
4d5cf4629e | ||
|
|
c1f155c0f2 | ||
|
|
3fe560cf14 | ||
|
|
75a0384d02 | ||
|
|
6501470357 | ||
|
|
8bfc313d02 | ||
|
|
e6ac527416 | ||
|
|
c27c5feadb | ||
|
|
2b22ffda9b | ||
|
|
9a1f8a99e0 | ||
|
|
d552d29eae | ||
|
|
aa99405a9c | ||
|
|
39101ae8ea | ||
|
|
1a83967261 | ||
|
|
d43817b49a | ||
|
|
3b1f0dd453 | ||
|
|
a1db0f7ebb | ||
|
|
acf5c24881 | ||
|
|
f012fa1af0 | ||
|
|
b62b17a223 | ||
|
|
265708854a | ||
|
|
e58aed1731 | ||
|
|
b3ffb04a2c | ||
|
|
c53e7824a7 | ||
|
|
46a39b06cb | ||
|
|
a7f73f1593 | ||
|
|
47323761dd | ||
|
|
43fbcfe008 | ||
|
|
5c19ff3f95 | ||
|
|
02e9ee3e96 | ||
|
|
df07aafae1 | ||
|
|
78edb0a06a | ||
|
|
901ac79dbc | ||
|
|
0597088f64 | ||
|
|
f1c4ebbed0 | ||
|
|
fd9e3202a0 | ||
|
|
7afdd40714 | ||
|
|
ed491d0e22 | ||
|
|
e12ca14006 | ||
|
|
58fc418563 | ||
|
|
57e6f0f56d | ||
|
|
cc8dbbe8b3 | ||
|
|
bb5aac3d69 | ||
|
|
374e88382b | ||
|
|
c74acd6363 | ||
|
|
90023a3a70 | ||
|
|
0b82ab6674 | ||
|
|
fb0e192681 | ||
|
|
eedc5c6b79 | ||
|
|
31c9247921 | ||
|
|
4c3cbf702c | ||
|
|
a06998e2f6 | ||
|
|
4e15e202c8 | ||
|
|
35ea7b6307 | ||
|
|
46688ae3f9 | ||
|
|
9c3f44d086 | ||
|
|
896337cbb1 | ||
|
|
a0dab888a7 | ||
|
|
d7cffde00f | ||
|
|
562d81c6ea | ||
|
|
66b392f4ee | ||
|
|
dd3c87ece2 | ||
|
|
63f7a2ddc4 | ||
|
|
ca49d86ed8 | ||
|
|
d9a317c370 | ||
|
|
add792de30 | ||
|
|
004fc03899 | ||
|
|
15f1678439 | ||
|
|
3de38fc679 | ||
|
|
63eda18cf9 | ||
|
|
a15a5f1c0f | ||
|
|
d41cf65173 | ||
|
|
e97b103148 | ||
|
|
4e6246d2a7 | ||
|
|
ab4f5209b5 | ||
|
|
8a25718c71 | ||
|
|
a8bdac07e6 | ||
|
|
2790fd1dd7 | ||
|
|
f9401d94c2 | ||
|
|
4fb7874ca3 | ||
|
|
bb1b050ab5 | ||
|
|
e8d857db21 | ||
|
|
f3a136be78 | ||
|
|
2c564dfb9c | ||
|
|
901f708d4c | ||
|
|
ecbdc2e3d3 | ||
|
|
60a3d51a50 | ||
|
|
bdc86ba539 | ||
|
|
04c1a2aee5 | ||
|
|
e76ebbcdec | ||
|
|
edd461ad42 | ||
|
|
0c323cfb5b | ||
|
|
2e41a4f94a | ||
|
|
81f960b17e | ||
|
|
cf60df5491 | ||
|
|
fd8507212c | ||
|
|
420995f567 | ||
|
|
29084bd5bf | ||
|
|
13907188a1 | ||
|
|
d606aaa85e | ||
|
|
2e8fe841ec | ||
|
|
0411fcc2ae | ||
|
|
6a9ab683a6 | ||
|
|
e9592b2d53 | ||
|
|
db6b1f3541 | ||
|
|
5e767fcc9a | ||
|
|
ac0012e3da | ||
|
|
71150a1cb5 | ||
|
|
0368d58065 | ||
|
|
9daad5c60c | ||
|
|
4683ac3d6c | ||
|
|
7cc85c2e4f | ||
|
|
891e2dd6aa | ||
|
|
575e229514 | ||
|
|
1a765ac431 | ||
|
|
5bdb21b705 | ||
|
|
40967a6572 | ||
|
|
c128018590 | ||
|
|
618f1cefae | ||
|
|
a679a09212 | ||
|
|
cc1ae4a963 | ||
|
|
dbd7006704 | ||
|
|
da4bc0225c | ||
|
|
f145c7c148 | ||
|
|
73425d4bb8 | ||
|
|
7927836489 | ||
|
|
185a1c09ad | ||
|
|
3d4a6b0765 | ||
|
|
7eb82bf90c | ||
|
|
5986c91937 | ||
|
|
63421d40f0 | ||
|
|
e11c0c2dd1 | ||
|
|
362b4314cb | ||
|
|
ba60f1865f | ||
|
|
f0e65d9f5a | ||
|
|
e1881976e8 | ||
|
|
953839be59 | ||
|
|
aaf1bba1cf | ||
|
|
196a0323a4 | ||
|
|
729bab42c8 | ||
|
|
a9c475a910 | ||
|
|
566a880099 | ||
|
|
bf882b065d | ||
|
|
1b38974c60 | ||
|
|
c568f7ba89 | ||
|
|
e9803e8fb3 | ||
|
|
dafe5cd1f7 | ||
|
|
33f210b85c | ||
|
|
e2cafa487b | ||
|
|
ffd5d44cdc | ||
|
|
5d665aac39 | ||
|
|
662612685c | ||
|
|
a204df819b | ||
|
|
9bb075aa0b | ||
|
|
007be7aea7 | ||
|
|
3b35882f87 | ||
|
|
616e9dd6eb | ||
|
|
c38bc9064a | ||
|
|
4f75eaabcd |
+9
-23
@@ -1,18 +1,21 @@
|
||||
version: 2
|
||||
|
||||
jobs:
|
||||
flake8:
|
||||
flake8-mypy:
|
||||
docker:
|
||||
- image: circleci/python:3.6
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
- run:
|
||||
name: Install flake8
|
||||
command: sudo python -m pip install flake8
|
||||
name: Install flake8 & mypy
|
||||
command: sudo python -m pip install flake8 mypy
|
||||
- run:
|
||||
name: Test.
|
||||
command: flake8 .
|
||||
name: flake8
|
||||
command: flake8
|
||||
- run:
|
||||
name: mypy
|
||||
command: mypy
|
||||
|
||||
osx-python3.6:
|
||||
macos:
|
||||
@@ -30,22 +33,6 @@ jobs:
|
||||
command: venv/bin/python ./bin/run_tests.py
|
||||
no_output_timeout: 30m
|
||||
|
||||
osx-python3.7:
|
||||
macos:
|
||||
xcode: "10.0.0"
|
||||
environment:
|
||||
PYTHON: python3
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
- run:
|
||||
name: Prepare the environment.
|
||||
command: bash .circleci/prepare.sh
|
||||
- run:
|
||||
name: Test.
|
||||
command: venv/bin/python ./bin/run_tests.py
|
||||
no_output_timeout: 30m
|
||||
|
||||
linux-python3.6:
|
||||
docker:
|
||||
- image: circleci/python:3.6
|
||||
@@ -67,7 +54,6 @@ workflows:
|
||||
version: 2
|
||||
all-tests:
|
||||
jobs:
|
||||
- flake8
|
||||
- flake8-mypy
|
||||
- osx-python3.6
|
||||
- osx-python3.7
|
||||
- linux-python3.6
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
set -o errexit
|
||||
set -o xtrace
|
||||
|
||||
$PYTHON --version
|
||||
$PYTHON -m pip --version
|
||||
$PYTHON -m pip install -q --user --ignore-installed --upgrade virtualenv
|
||||
$PYTHON -m virtualenv -p $PYTHON venv
|
||||
venv/bin/python -m pip install -r requirements-dev.txt
|
||||
venv/bin/python -m pip freeze
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# config file for https://github.com/bobvanderlinden/probot-auto-merge
|
||||
|
||||
# no approvals required, just the label is enough
|
||||
minApprovals: {}
|
||||
|
||||
requiredLabels:
|
||||
- merge when green
|
||||
@@ -18,8 +18,8 @@ jobs:
|
||||
os: [ubuntu-18.04, windows-latest, macos-latest]
|
||||
python_version: ['3.7']
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/setup-python@v1
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v2
|
||||
name: Install Python ${{ matrix.python_version }}
|
||||
with:
|
||||
python-version: ${{ matrix.python_version }}
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
run: |
|
||||
python -m pip install -r requirements-dev.txt
|
||||
- name: Install Visual C++ for Python 2.7
|
||||
if: startsWith(matrix.os, 'windows')
|
||||
if: runner.os == 'Windows'
|
||||
run: |
|
||||
choco install vcpython27 -f -y
|
||||
- name: Test cibuildwheel
|
||||
|
||||
@@ -102,3 +102,6 @@ env3?/
|
||||
|
||||
# VSCode project settings
|
||||
/.vscode
|
||||
|
||||
# MyPy cache
|
||||
.mypy_cache/
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
repos:
|
||||
- repo: https://gitlab.com/pycqa/flake8
|
||||
rev: 3.8.1
|
||||
hooks:
|
||||
- id: flake8
|
||||
pass_filenames: true
|
||||
# this seems to need to be here in addition to setup.cfg
|
||||
exclude: cibuildwheel/resources/
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v2.3.0
|
||||
hooks:
|
||||
- id: check-yaml
|
||||
- id: check-merge-conflict
|
||||
- id: end-of-file-fixer
|
||||
exclude: cibuildwheel/resources/pinned_docker_images.cfg
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: '' # Use the sha / tag you want to point at
|
||||
hooks:
|
||||
- id: mypy
|
||||
files: cibuildwheel|test
|
||||
exclude: cibuildwheel/resources
|
||||
+15
-13
@@ -6,22 +6,22 @@ branches:
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- name: Linux | x86_64 + i686 | Python 3.5
|
||||
- name: Linux | x86_64 + i686 | Python 3.6
|
||||
language: python
|
||||
python: 3.5
|
||||
python: 3.6
|
||||
services: docker
|
||||
env: PYTHON=python
|
||||
|
||||
- name: Linux | arm64 | Python 3.5
|
||||
- name: Linux | arm64 | Python 3.6
|
||||
language: python
|
||||
python: 3.5
|
||||
python: 3.6
|
||||
services: docker
|
||||
arch: arm64
|
||||
env: PYTHON=python
|
||||
|
||||
- name: Linux | ppc64le | Python 3.5
|
||||
- name: Linux | ppc64le | Python 3.6
|
||||
language: python
|
||||
python: 3.5
|
||||
python: 3.6
|
||||
services: docker
|
||||
arch: ppc64le
|
||||
env: PYTHON=python
|
||||
@@ -30,25 +30,27 @@ jobs:
|
||||
os: osx
|
||||
env: PYTHON=python3
|
||||
|
||||
- name: Windows | x86_64 | Python 3.5
|
||||
- name: Windows | x86_64 | Python 3.6
|
||||
os: windows
|
||||
language: shell
|
||||
before_install:
|
||||
- choco install python3 --version 3.5.4 --no-progress -y
|
||||
- choco install python3 --version 3.6.8 --no-progress -y
|
||||
# Update root certificates to fix SSL error; see http://www.chawn.com/RootCerts.htm
|
||||
- powershell "md C:\temp\certs; CertUtil -generateSSTFromWU C:\temp\certs\RootStore.sst; Get-ChildItem -Path C:\\temp\certs\Rootstore.sst | Import-Certificate -CertStoreLocation Cert:\\LocalMachine\\Root\\ | out-null"
|
||||
env:
|
||||
- PYTHON=C:\\Python35\\python
|
||||
- PYTHON=C:\\Python36\\python
|
||||
|
||||
- &linux_s390x_35
|
||||
name: Linux | s390x | Python 3.5
|
||||
- &linux_s390x_36
|
||||
name: Linux | s390x | Python 3.6
|
||||
language: python
|
||||
python: 3.5
|
||||
python: 3.6
|
||||
services: docker
|
||||
arch: s390x
|
||||
env: PYTHON=python
|
||||
|
||||
allow_failures:
|
||||
# must repeat the s390x job above exactly to match
|
||||
- *linux_s390x_35
|
||||
- *linux_s390x_36
|
||||
|
||||
install: $PYTHON -m pip install -r requirements-dev.txt
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
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 |
|
||||
| | 3.6 | 3.7 | 3.8 |
|
||||
|----------|------------------------------|------------------------------------------|------------------|
|
||||
| Linux | Travis CI / CircleCI | AppVeyor² / GitHub Actions | Azure Pipelines |
|
||||
| macOS | CircleCI | AppVeyor² / Travis CI¹ / GitHub Actions | Azure Pipelines |
|
||||
| Windows | Travis CI / Azure Pipelines | AppVeyor² / GitHub Actions | Azure Pipelines |
|
||||
|
||||
> ¹ Python version not really pinned, but dependent on the (default) version of image used.
|
||||
> ² AppVeyor only runs the "basic" test to reduce load.
|
||||
|
||||
Non-x86 architectures are covered on Travis CI using Python 3.5.
|
||||
Non-x86 architectures are covered on Travis CI using Python 3.6.
|
||||
|
||||
@@ -22,11 +22,13 @@ What does it do?
|
||||
| CPython 3.6 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅² |
|
||||
| CPython 3.7 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅² |
|
||||
| CPython 3.8 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅² |
|
||||
| CPython 3.9³ | 🛠 | 🛠 | 🛠 | 🛠 | 🛠 | 🛠 | 🛠 | |
|
||||
| PyPy 2.7 v7.3.0 | ✅ | | ✅ | ✅ | | | | |
|
||||
| PyPy 3.6 v7.3.0 | ✅ | | ✅ | ✅ | | | | |
|
||||
|
||||
<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>
|
||||
<sup>² Beta support until Travis CI fixes <a href="https://travis-ci.community/t/no-space-left-on-device-for-system-z/5954/11">a bug</a></sup><br>
|
||||
<sup>³ Python 3.9 is not yet ABI stable, so you shouldn't publish wheels with it yet. But if you want to check that your wheels build on Python 3.9, try our [`python3.9` branch](https://github.com/joerick/cibuildwheel/pull/382)!</sup>
|
||||
|
||||
- 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)
|
||||
@@ -77,7 +79,7 @@ env:
|
||||
# Note: TWINE_PASSWORD is set to a PyPI API token in Travis settings
|
||||
|
||||
install:
|
||||
- python3 -m pip install cibuildwheel==1.4.1
|
||||
- python3 -m pip install cibuildwheel==1.5.2
|
||||
|
||||
script:
|
||||
# build the wheels, put them into './wheelhouse'
|
||||
@@ -92,7 +94,7 @@ after_success:
|
||||
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).
|
||||
For more information, including how to build on GitHub Actions, Appveyor, Azure Pipelines, or CircleCI, check out the [documentation](https://cibuildwheel.readthedocs.org) and the [examples](https://github.com/joerick/cibuildwheel/tree/master/examples).
|
||||
|
||||
Options
|
||||
-------
|
||||
@@ -100,17 +102,20 @@ Options
|
||||
| | Option | Description |
|
||||
|---|--------|-------------|
|
||||
| **Build selection** | [`CIBW_PLATFORM`](https://cibuildwheel.readthedocs.io/en/stable/options/#platform) | Override the auto-detected target platform |
|
||||
| | [`CIBW_BUILD`](https://cibuildwheel.readthedocs.io/en/stable/options/#build-skip) [`CIBW_SKIP`](https://cibuildwheel.readthedocs.io/en/stable/options/#build-skip) | Choose the Python versions to build |
|
||||
| **Build environment** | [`CIBW_ENVIRONMENT`](https://cibuildwheel.readthedocs.io/en/stable/options/#environment) | Set environment variables needed during the build |
|
||||
| | [`CIBW_BUILD`](https://cibuildwheel.readthedocs.io/en/stable/options/#build-skip) <br> [`CIBW_SKIP`](https://cibuildwheel.readthedocs.io/en/stable/options/#build-skip) | Choose the Python versions to build |
|
||||
| **Build customization** | [`CIBW_ENVIRONMENT`](https://cibuildwheel.readthedocs.io/en/stable/options/#environment) | Set environment variables needed during the build |
|
||||
| | [`CIBW_BEFORE_ALL`](https://cibuildwheel.readthedocs.io/en/stable/options/#before-all) | Execute a shell command on the build system before any wheels are built. |
|
||||
| | [`CIBW_BEFORE_BUILD`](https://cibuildwheel.readthedocs.io/en/stable/options/#before-build) | Execute a shell command preparing each wheel's build |
|
||||
| | [`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) [`CIBW_MANYLINUX_PYPY_X86_64_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) <br> [`CIBW_MANYLINUX_I686_IMAGE`](https://cibuildwheel.readthedocs.io/en/stable/options/#manylinux-image) <br> [`CIBW_MANYLINUX_PYPY_X86_64_IMAGE`](https://cibuildwheel.readthedocs.io/en/stable/options/#manylinux-image) <br> [`CIBW_MANYLINUX_AARCH64_IMAGE`](https://cibuildwheel.readthedocs.io/en/stable/options/#manylinux-image) <br> [`CIBW_MANYLINUX_PPC64LE_IMAGE`](https://cibuildwheel.readthedocs.io/en/stable/options/#manylinux-image) <br> [`CIBW_MANYLINUX_S390X_IMAGE`](https://cibuildwheel.readthedocs.io/en/stable/options/#manylinux-image) | Specify alternative manylinux docker images |
|
||||
| | [`CIBW_DEPENDENCY_VERSIONS`](https://cibuildwheel.readthedocs.io/en/stable/options/#dependency-versions) | Specify how cibuildwheel controls the versions of the tools it uses |
|
||||
| **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_BEFORE_TEST`](https://cibuildwheel.readthedocs.io/en/stable/options/#before-test) | Execute a shell command before testing each wheel |
|
||||
| | [`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/#build-verbosity) | Increase/decrease the output of pip wheel |
|
||||
|
||||
|
||||
Working examples
|
||||
----------------
|
||||
|
||||
@@ -128,6 +133,10 @@ Here are some repos that use cibuildwheel.
|
||||
- [Twisted](https://github.com/twisted/twisted)
|
||||
- [gmic-py](https://github.com/dtschump/gmic-py)
|
||||
- [creme](https://github.com/creme-ml/creme)
|
||||
- [PyAV](https://github.com/PyAV-Org/PyAV)
|
||||
- [aiortc](https://github.com/aiortc/aiortc)
|
||||
- [aioquic](https://github.com/aiortc/aioquic)
|
||||
- [pikepdf](https://github.com/pikepdf/pikepdf)
|
||||
|
||||
> Add your repo here! Send a PR.
|
||||
|
||||
@@ -143,6 +152,49 @@ This is similar to static linking, so it might have some licence implications. C
|
||||
Changelog
|
||||
=========
|
||||
|
||||
### 1.5.2
|
||||
|
||||
_8 July 2020_
|
||||
|
||||
- 🐛 Fix an issue on Windows where pyproject.toml would cause an error when
|
||||
some requirements formats were used. (#401)
|
||||
- 🛠 Update CPython 3.7 to 3.7.8 (#394)
|
||||
|
||||
### 1.5.1
|
||||
|
||||
_25 June 2020_
|
||||
|
||||
- 🐛 Fix "OSError: [WinError 17] The system cannot move the file to a different
|
||||
disk drive" on Github Actions (#388, #389)
|
||||
|
||||
### 1.5.0
|
||||
|
||||
_24 June 2020_
|
||||
|
||||
- 🌟 Add [`CIBW_BEFORE_ALL`](https://cibuildwheel.readthedocs.io/en/stable/options/#before-all)
|
||||
option, which lets you run a command on the build machine before any wheels
|
||||
are built. This is especially useful when building on Linux, to `make`
|
||||
something external to Python, or to `yum install` a dependency. (#342)
|
||||
- ✨ Added support for projects using pyproject.toml instead of setup.py
|
||||
(#360, #358)
|
||||
- ✨ Added workaround to allow Python 3.5 on Windows to pull dependencies from
|
||||
pyproject.toml. (#358)
|
||||
- 📚 Improved Github Actions examples and docs (#354, #362)
|
||||
- 🐛 Ensure pip wheel uses the specified package, and doesn't build a wheel
|
||||
from PyPI (#369)
|
||||
- 🛠 Internal changes: using pathlib.Path, precommit hooks, testing
|
||||
improvements.
|
||||
|
||||
### 1.4.2
|
||||
|
||||
_25 May 2020_
|
||||
|
||||
- 🛠 Dependency updates, including CPython 3.8.3 & manylinux images.
|
||||
- 🛠 Lots of internal updates - type annotations and checking using mypy, and
|
||||
a new integration testing system.
|
||||
- ⚠️ Removed support for *running* cibuildwheel using Python 3.5. cibuildwheel
|
||||
will continue to build Python 3.5 wheels until EOL.
|
||||
|
||||
### 1.4.1
|
||||
|
||||
_4 May 2020_
|
||||
@@ -155,7 +207,7 @@ _4 May 2020_
|
||||
|
||||
_2 May 2020_
|
||||
|
||||
- ✨ Deterministic builds. cibuildwheel now locks the versions of the tools it
|
||||
- 🌟 Deterministic builds. cibuildwheel now locks the versions of the tools it
|
||||
uses. This means that pinning your version of cibuildwheel pins the versions
|
||||
of pip, setuptools, manylinux etc. that are used under the hood. This should
|
||||
make things more reliable. But note that we don't control the entire build
|
||||
@@ -185,7 +237,7 @@ _2 May 2020_
|
||||
|
||||
_12 March 2020_
|
||||
|
||||
- ✨ Add support for building on Github Actions! Check out the
|
||||
- 🌟 Add support for building on Github Actions! Check out the
|
||||
[docs](https://cibuildwheel.readthedocs.io/en/stable/setup/#github-actions)
|
||||
for information on how to set it up. (#194)
|
||||
- ✨ Add the `CIBW_BEFORE_TEST` option, which lets you run a command to
|
||||
@@ -195,9 +247,9 @@ _12 March 2020_
|
||||
|
||||
_8 March 2020_
|
||||
|
||||
- ✨ Add support for building PyPy wheels, across Manylinux, macOS, and
|
||||
- 🌟 Add support for building PyPy wheels, across Manylinux, macOS, and
|
||||
Windows. (#185)
|
||||
- ✨ Added the ability to build ARM64 (aarch64), ppc64le, and s390x wheels,
|
||||
- 🌟 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
|
||||
@@ -218,7 +270,7 @@ _8 March 2020_
|
||||
|
||||
_7 December 2019_
|
||||
|
||||
- ✨ Add support for building manylinux2014 wheels. To use, set
|
||||
- 🌟 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)
|
||||
@@ -231,8 +283,8 @@ _7 December 2019_
|
||||
|
||||
_10 November 2019_
|
||||
|
||||
- ✨ Add support for building Python 3.8 wheels! (#180)
|
||||
- ✨ Add support for building manylinux2010 wheels. cibuildwheel will now
|
||||
- 🌟 Add support for building Python 3.8 wheels! (#180)
|
||||
- 🌟 Add support for building manylinux2010 wheels. cibuildwheel will now
|
||||
build using the manylinux2010 images by default. If your project is still
|
||||
manylinux1 compatible, you should get both manylinux1 and manylinux2010
|
||||
wheels - you can upload both to PyPI. If you always require manylinux1 wheels, you can
|
||||
@@ -268,7 +320,7 @@ _28 May 2019_
|
||||
|
||||
_26 May 2019_
|
||||
|
||||
- ✨ Add support for building on Azure pipelines! This lets you build all
|
||||
- 🌟 Add support for building on Azure pipelines! This lets you build all
|
||||
Linux, Mac and Windows wheels on one service, so it promises to be the
|
||||
easiest to set up! Check out the quickstart in the docs, or
|
||||
[cibuildwheel-azure-example](https://github.com/joerick/cibuildwheel-azure-example)
|
||||
@@ -298,8 +350,8 @@ _3 February 2019_
|
||||
|
||||
_23 September 2018_
|
||||
|
||||
- ✨ Add `CIBW_BUILD` option, for specifying which specific builds to perform (#101)
|
||||
- ✨ Add support for building Mac and Linux on CircleCI (#91, #97)
|
||||
- 🌟 Add `CIBW_BUILD` option, for specifying which specific builds to perform (#101)
|
||||
- 🌟 Add support for building Mac and Linux on CircleCI (#91, #97)
|
||||
- 🛠 Improved support for building universal wheels (#95)
|
||||
- 🛠 Ensure log output is unbuffered and therefore in the correct order (#92)
|
||||
- 🛠 Improved error reporting for errors that occur inside a package's setup.py (#88)
|
||||
|
||||
@@ -10,6 +10,11 @@ environment:
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: macos-mojave
|
||||
APPVEYOR_JOB_NAME: "python37-x64-macos-mojave"
|
||||
|
||||
# while pypy libffi dependency is failing the build, allow failures on macos
|
||||
matrix:
|
||||
allow_failures:
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: macos-mojave
|
||||
|
||||
stack: python 3.7
|
||||
|
||||
build: off
|
||||
|
||||
+4
-15
@@ -6,17 +6,7 @@ jobs:
|
||||
inputs:
|
||||
versionSpec: '3.8'
|
||||
- bash: |
|
||||
python -m pip install -r requirements-dev.txt
|
||||
python ./bin/run_tests.py
|
||||
|
||||
- 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 -m pip install -r requirements-dev.txt pytest-azurepipelines
|
||||
python ./bin/run_tests.py
|
||||
|
||||
- job: macos_38
|
||||
@@ -26,7 +16,7 @@ jobs:
|
||||
inputs:
|
||||
versionSpec: '3.8'
|
||||
- bash: |
|
||||
python -m pip install -r requirements-dev.txt
|
||||
python -m pip install -r requirements-dev.txt pytest-azurepipelines
|
||||
python ./bin/run_tests.py
|
||||
|
||||
- job: windows_36
|
||||
@@ -38,7 +28,7 @@ jobs:
|
||||
- script: choco install vcpython27 -f -y
|
||||
displayName: Install Visual C++ for Python 2.7
|
||||
- bash: |
|
||||
python -m pip install -r requirements-dev.txt
|
||||
python -m pip install -r requirements-dev.txt pytest-azurepipelines
|
||||
python ./bin/run_tests.py
|
||||
|
||||
- job: windows_38
|
||||
@@ -50,6 +40,5 @@ jobs:
|
||||
- script: choco install vcpython27 -f -y
|
||||
displayName: Install Visual C++ for Python 2.7
|
||||
- bash: |
|
||||
python -m pip install -r requirements-dev.txt
|
||||
python -m pip install -r requirements-dev.txt pytest-azurepipelines
|
||||
python ./bin/run_tests.py
|
||||
|
||||
|
||||
+1
-4
@@ -1,6 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
cd ..
|
||||
|
||||
CIBW_PLATFORM=linux ./bin/run_test.py $1
|
||||
CIBW_PLATFORM=linux pytest "$@"
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
def single_run(test_project):
|
||||
# run the test
|
||||
subprocess.check_call(
|
||||
[sys.executable, '-m', 'pytest', '-vv', os.path.join(test_project, 'cibuildwheel_test.py')],
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("test_project_dir")
|
||||
args = parser.parse_args()
|
||||
|
||||
project_path = os.path.abspath(args.test_project_dir)
|
||||
|
||||
if not os.path.exists(project_path):
|
||||
print('No test project not found.', file=sys.stderr)
|
||||
exit(2)
|
||||
|
||||
single_run(project_path)
|
||||
+3
-17
@@ -3,28 +3,14 @@
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from glob import glob
|
||||
from pathlib import Path
|
||||
|
||||
if __name__ == '__main__':
|
||||
# move cwd to the project root
|
||||
os.chdir(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
os.chdir(Path(__file__).resolve().parents[1])
|
||||
|
||||
# run the unit tests
|
||||
|
||||
subprocess.check_call([sys.executable, '-m', 'pytest', 'unit_test'])
|
||||
|
||||
# run the integration tests
|
||||
|
||||
test_projects = sorted(glob('test/??_*'))
|
||||
|
||||
if len(test_projects) == 0:
|
||||
print('No test projects found. Aborting.', file=sys.stderr)
|
||||
exit(2)
|
||||
|
||||
print('Testing projects:', test_projects)
|
||||
|
||||
run_test_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'run_test.py')
|
||||
for project_path in test_projects:
|
||||
subprocess.check_call([sys.executable, run_test_path, project_path])
|
||||
|
||||
print('%d projects built successfully.' % len(test_projects))
|
||||
subprocess.check_call([sys.executable, '-m', 'pytest', '-x', '--durations', '0', 'test'])
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = '1.4.1'
|
||||
__version__ = '1.5.2'
|
||||
|
||||
+67
-62
@@ -4,6 +4,9 @@ import sys
|
||||
import textwrap
|
||||
import traceback
|
||||
from configparser import ConfigParser
|
||||
from pathlib import Path
|
||||
|
||||
from typing import Any, Dict, List, Optional, overload
|
||||
|
||||
import cibuildwheel
|
||||
import cibuildwheel.linux
|
||||
@@ -14,14 +17,19 @@ from cibuildwheel.environment import (
|
||||
parse_environment,
|
||||
)
|
||||
from cibuildwheel.util import (
|
||||
BuildOptions,
|
||||
BuildSelector,
|
||||
DependencyConstraints,
|
||||
Unbuffered,
|
||||
BuildOptions
|
||||
resources_dir,
|
||||
)
|
||||
|
||||
|
||||
def get_option_from_environment(option_name, platform=None, default=None):
|
||||
@overload
|
||||
def get_option_from_environment(option_name: str, platform: Optional[str], default: str) -> str: ... # noqa: E704
|
||||
@overload
|
||||
def get_option_from_environment(option_name: str, platform: Optional[str] = None, default: None = None) -> Optional[str]: ... # noqa: E704 E302
|
||||
def get_option_from_environment(option_name: str, platform: Optional[str] = None, default: Optional[str] = None) -> Optional[str]: # noqa: E302
|
||||
'''
|
||||
Returns an option from the environment, optionally scoped by the platform.
|
||||
|
||||
@@ -32,20 +40,20 @@ def get_option_from_environment(option_name, platform=None, default=None):
|
||||
CIBW_COLOR.
|
||||
'''
|
||||
if platform:
|
||||
option = os.environ.get('%s_%s' % (option_name, platform.upper()))
|
||||
option = os.environ.get(f'{option_name}_{platform.upper()}')
|
||||
if option is not None:
|
||||
return option
|
||||
|
||||
return os.environ.get(option_name, default)
|
||||
|
||||
|
||||
def strtobool(val):
|
||||
def strtobool(val: str) -> bool:
|
||||
if val.lower() in ('y', 'yes', 't', 'true', 'on', '1'):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Build wheels for all the platforms.',
|
||||
epilog='''
|
||||
@@ -108,65 +116,69 @@ 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()
|
||||
test_extras = get_option_from_environment('CIBW_TEST_EXTRAS', platform=platform, default='')
|
||||
package_dir = args.package_dir
|
||||
before_build = get_option_from_environment('CIBW_BEFORE_BUILD', platform=platform)
|
||||
build_verbosity = get_option_from_environment('CIBW_BUILD_VERBOSITY', platform=platform, default='')
|
||||
build_config, skip_config = os.environ.get('CIBW_BUILD', '*'), os.environ.get('CIBW_SKIP', '')
|
||||
package_dir = Path(args.package_dir)
|
||||
output_dir = Path(args.output_dir)
|
||||
|
||||
if platform == 'linux':
|
||||
repair_command_default = 'auditwheel repair -w {dest_dir} {wheel}'
|
||||
elif platform == 'macos':
|
||||
repair_command_default = 'delocate-listdeps {wheel} && delocate-wheel --require-archs x86_64 -w {dest_dir} {wheel}'
|
||||
else:
|
||||
repair_command_default = ''
|
||||
repair_command = get_option_from_environment('CIBW_REPAIR_WHEEL_COMMAND', platform=platform, default=repair_command_default)
|
||||
|
||||
build_config, skip_config = os.environ.get('CIBW_BUILD', '*'), os.environ.get('CIBW_SKIP', '')
|
||||
environment_config = get_option_from_environment('CIBW_ENVIRONMENT', platform=platform, default='')
|
||||
before_test = get_option_from_environment('CIBW_BEFORE_TEST', platform=platform, default='')
|
||||
|
||||
before_all = get_option_from_environment('CIBW_BEFORE_ALL', platform=platform, default='')
|
||||
before_build = get_option_from_environment('CIBW_BEFORE_BUILD', platform=platform)
|
||||
repair_command = get_option_from_environment('CIBW_REPAIR_WHEEL_COMMAND', platform=platform, default=repair_command_default)
|
||||
dependency_versions = get_option_from_environment('CIBW_DEPENDENCY_VERSIONS', platform=platform, default='pinned')
|
||||
if dependency_versions == 'pinned':
|
||||
dependency_constraints = DependencyConstraints.with_defaults()
|
||||
elif dependency_versions == 'latest':
|
||||
dependency_constraints = None
|
||||
else:
|
||||
dependency_constraints = DependencyConstraints(dependency_versions)
|
||||
test_command = get_option_from_environment('CIBW_TEST_COMMAND', platform=platform)
|
||||
before_test = get_option_from_environment('CIBW_BEFORE_TEST', platform=platform)
|
||||
test_requires = get_option_from_environment('CIBW_TEST_REQUIRES', platform=platform, default='').split()
|
||||
test_extras = get_option_from_environment('CIBW_TEST_EXTRAS', platform=platform, default='')
|
||||
build_verbosity_str = get_option_from_environment('CIBW_BUILD_VERBOSITY', platform=platform, default='')
|
||||
|
||||
if test_extras:
|
||||
test_extras = '[{0}]'.format(test_extras)
|
||||
|
||||
try:
|
||||
build_verbosity = min(3, max(-3, int(build_verbosity)))
|
||||
except ValueError:
|
||||
build_verbosity = 0
|
||||
build_selector = BuildSelector(build_config, skip_config)
|
||||
|
||||
try:
|
||||
environment = parse_environment(environment_config)
|
||||
except (EnvironmentParseError, ValueError):
|
||||
print('cibuildwheel: Malformed environment option "%s"' % environment_config, file=sys.stderr)
|
||||
print(f'cibuildwheel: Malformed environment option "{environment_config}"', file=sys.stderr)
|
||||
traceback.print_exc(None, sys.stderr)
|
||||
exit(2)
|
||||
|
||||
build_selector = BuildSelector(build_config, skip_config)
|
||||
if dependency_versions == 'pinned':
|
||||
dependency_constraints: Optional[DependencyConstraints] = DependencyConstraints.with_defaults()
|
||||
elif dependency_versions == 'latest':
|
||||
dependency_constraints = None
|
||||
else:
|
||||
dependency_versions_path = Path(dependency_versions)
|
||||
dependency_constraints = DependencyConstraints(dependency_versions_path)
|
||||
|
||||
if test_extras:
|
||||
test_extras = f'[{test_extras}]'
|
||||
|
||||
try:
|
||||
build_verbosity = min(3, max(-3, int(build_verbosity_str)))
|
||||
except ValueError:
|
||||
build_verbosity = 0
|
||||
|
||||
# Add CIBUILDWHEEL environment variable
|
||||
# This needs to be passed on to the docker container in linux.py
|
||||
os.environ['CIBUILDWHEEL'] = '1'
|
||||
|
||||
if not os.path.exists(os.path.join(package_dir, 'setup.py')):
|
||||
print('cibuildwheel: Could not find setup.py at root of package', file=sys.stderr)
|
||||
if not any((package_dir / name).exists()
|
||||
for name in ["setup.py", "setup.cfg", "pyproject.toml"]):
|
||||
print('cibuildwheel: Could not find setup.py, setup.cfg or pyproject.toml at root of package', file=sys.stderr)
|
||||
exit(2)
|
||||
|
||||
if args.print_build_identifiers:
|
||||
print_build_identifiers(platform, build_selector)
|
||||
exit(0)
|
||||
|
||||
manylinux_images: Optional[Dict[str, str]] = None
|
||||
if platform == 'linux':
|
||||
pinned_docker_images_file = os.path.join(
|
||||
os.path.dirname(__file__), 'resources', 'pinned_docker_images.cfg'
|
||||
)
|
||||
pinned_docker_images_file = resources_dir / 'pinned_docker_images.cfg'
|
||||
all_pinned_docker_images = ConfigParser()
|
||||
all_pinned_docker_images.read(pinned_docker_images_file)
|
||||
# all_pinned_docker_images looks like a dict of dicts, e.g.
|
||||
@@ -180,7 +192,7 @@ def main():
|
||||
for build_platform in ['x86_64', 'i686', 'pypy_x86_64', 'aarch64', 'ppc64le', 's390x']:
|
||||
pinned_images = all_pinned_docker_images[build_platform]
|
||||
|
||||
config_name = 'CIBW_MANYLINUX_{}_IMAGE'.format(build_platform.upper())
|
||||
config_name = f'CIBW_MANYLINUX_{build_platform.upper()}_IMAGE'
|
||||
config_value = os.environ.get(config_name)
|
||||
|
||||
if config_value is None:
|
||||
@@ -193,32 +205,30 @@ def main():
|
||||
|
||||
manylinux_images[build_platform] = image
|
||||
|
||||
else:
|
||||
manylinux_images = None
|
||||
|
||||
build_options = BuildOptions(
|
||||
package_dir=package_dir,
|
||||
output_dir=output_dir,
|
||||
test_command=test_command,
|
||||
test_requires=test_requires,
|
||||
test_extras=test_extras,
|
||||
before_test=before_test,
|
||||
before_build=before_build,
|
||||
before_all=before_all,
|
||||
build_verbosity=build_verbosity,
|
||||
build_selector=build_selector,
|
||||
repair_command=repair_command,
|
||||
environment=environment,
|
||||
before_test=before_test,
|
||||
dependency_constraints=dependency_constraints,
|
||||
manylinux_images=manylinux_images,
|
||||
)
|
||||
|
||||
# Python is buffering by default when running on the CI platforms, giving problems interleaving subprocess call output with unflushed calls to 'print'
|
||||
sys.stdout = Unbuffered(sys.stdout)
|
||||
sys.stdout = Unbuffered(sys.stdout) # type: ignore
|
||||
|
||||
print_preamble(platform, build_options)
|
||||
|
||||
if not os.path.exists(output_dir):
|
||||
os.makedirs(output_dir)
|
||||
if not output_dir.exists():
|
||||
output_dir.mkdir(parents=True)
|
||||
|
||||
if platform == 'linux':
|
||||
cibuildwheel.linux.build(build_options)
|
||||
@@ -227,21 +237,21 @@ def main():
|
||||
elif platform == 'macos':
|
||||
cibuildwheel.macos.build(build_options)
|
||||
else:
|
||||
print('cibuildwheel: Unsupported platform: {}'.format(platform), file=sys.stderr)
|
||||
print(f'cibuildwheel: Unsupported platform: {platform}', file=sys.stderr)
|
||||
exit(2)
|
||||
|
||||
|
||||
def detect_obsolete_options():
|
||||
def detect_obsolete_options() -> None:
|
||||
# Check the old 'MANYLINUX1_*_IMAGE' options
|
||||
for (deprecated, alternative) in [('CIBW_MANYLINUX1_X86_64_IMAGE', 'CIBW_MANYLINUX_X86_64_IMAGE'),
|
||||
('CIBW_MANYLINUX1_I686_IMAGE', 'CIBW_MANYLINUX_I686_IMAGE')]:
|
||||
if deprecated in os.environ:
|
||||
print("'{}' has been deprecated, and will be removed in a future release. Use the option '{}' instead.".format(deprecated, alternative))
|
||||
print(f"'{deprecated}' has been deprecated, and will be removed in a future release. Use the option '{alternative}' instead.")
|
||||
if alternative not in os.environ:
|
||||
print("Using value of option '{}' as replacement for '{}'".format(deprecated, alternative))
|
||||
print(f"Using value of option '{deprecated}' as replacement for '{alternative}'")
|
||||
os.environ[alternative] = os.environ[deprecated]
|
||||
else:
|
||||
print("Option '{}' is not empty. Please unset '{}'".format(alternative, deprecated))
|
||||
print(f"Option '{alternative}' is not empty. Please unset '{deprecated}'")
|
||||
exit(2)
|
||||
|
||||
# Check for deprecated identifiers in 'CIBW_BUILD' and 'CIBW_SKIP' options
|
||||
@@ -250,15 +260,11 @@ def detect_obsolete_options():
|
||||
('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,
|
||||
))
|
||||
print(f"Build identifiers with '{deprecated}' have been deprecated. Replacing all occurences of '{deprecated}' with '{alternative}' in the option '{option}'")
|
||||
os.environ[option] = os.environ[option].replace(deprecated, alternative)
|
||||
|
||||
|
||||
def print_preamble(platform, build_options):
|
||||
def print_preamble(platform: str, build_options: BuildOptions) -> None:
|
||||
print(textwrap.dedent('''
|
||||
_ _ _ _ _ _ _
|
||||
___|_| |_ _ _|_| |_| |_ _ _| |_ ___ ___| |
|
||||
@@ -266,12 +272,12 @@ def print_preamble(platform, build_options):
|
||||
|___|_|___|___|_|_|___|_____|_|_|___|___|_|
|
||||
'''))
|
||||
|
||||
print('cibuildwheel version %s\n' % cibuildwheel.__version__)
|
||||
print(f'cibuildwheel version {cibuildwheel.__version__}\n')
|
||||
|
||||
print('Build options:')
|
||||
print(' platform: %r' % platform)
|
||||
print(f' platform: {platform!r}')
|
||||
for option, value in sorted(build_options._asdict().items()):
|
||||
print(' %s: %r' % (option, value))
|
||||
print(f' {option}: {value!r}')
|
||||
|
||||
warnings = detect_warnings(platform, build_options)
|
||||
if warnings:
|
||||
@@ -282,21 +288,20 @@ def print_preamble(platform, build_options):
|
||||
print('\nHere we go!\n')
|
||||
|
||||
|
||||
def print_build_identifiers(platform, build_selector):
|
||||
def print_build_identifiers(platform: str, build_selector: BuildSelector) -> None:
|
||||
python_configurations: List[Any] = []
|
||||
if platform == 'linux':
|
||||
python_configurations = cibuildwheel.linux.get_python_configurations(build_selector)
|
||||
elif platform == 'windows':
|
||||
python_configurations = cibuildwheel.windows.get_python_configurations(build_selector)
|
||||
elif platform == 'macos':
|
||||
python_configurations = cibuildwheel.macos.get_python_configurations(build_selector)
|
||||
else:
|
||||
python_configurations = []
|
||||
|
||||
for config in python_configurations:
|
||||
print(config.identifier)
|
||||
|
||||
|
||||
def detect_warnings(platform, build_options):
|
||||
def detect_warnings(platform: str, build_options: BuildOptions) -> List[str]:
|
||||
warnings = []
|
||||
|
||||
# warn about deprecated {python} and {pip}
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import shlex
|
||||
import subprocess
|
||||
from collections import namedtuple
|
||||
|
||||
import bashlex
|
||||
from typing import Dict, NamedTuple
|
||||
|
||||
NodeExecutionContext = namedtuple('NodeExecutionContext', ['environment', 'input'])
|
||||
import bashlex # type: ignore
|
||||
|
||||
|
||||
def evaluate(value, environment):
|
||||
class NodeExecutionContext(NamedTuple):
|
||||
environment: Dict[str, str]
|
||||
input: str
|
||||
|
||||
|
||||
def evaluate(value: str, environment: Dict[str, str]) -> str:
|
||||
if not value:
|
||||
# empty string evaluates to empty string
|
||||
# (but trips up bashlex)
|
||||
@@ -16,7 +20,7 @@ def evaluate(value, environment):
|
||||
command_node = bashlex.parsesingle(value)
|
||||
|
||||
if len(command_node.parts) != 1:
|
||||
raise ValueError('"%s" has too many parts' % value)
|
||||
raise ValueError(f'"{value}" has too many parts')
|
||||
|
||||
value_word_node = command_node.parts[0]
|
||||
|
||||
@@ -26,7 +30,7 @@ def evaluate(value, environment):
|
||||
)
|
||||
|
||||
|
||||
def evaluate_node(node, context):
|
||||
def evaluate_node(node: bashlex.ast.node, context: NodeExecutionContext) -> str:
|
||||
if node.kind == 'word':
|
||||
return evaluate_word_node(node, context=context)
|
||||
elif node.kind == 'commandsubstitution':
|
||||
@@ -34,10 +38,10 @@ def evaluate_node(node, context):
|
||||
elif node.kind == 'parameter':
|
||||
return evaluate_parameter_node(node, context=context)
|
||||
else:
|
||||
raise ValueError('Unsupported bash construct: "%s"' % node.word)
|
||||
raise ValueError(f'Unsupported bash construct: "{node.word}"')
|
||||
|
||||
|
||||
def evaluate_word_node(node, context):
|
||||
def evaluate_word_node(node: bashlex.ast.node, context: NodeExecutionContext) -> str:
|
||||
word_start = node.pos[0]
|
||||
word_end = node.pos[1]
|
||||
word_string = context.input[word_start:word_end]
|
||||
@@ -49,22 +53,22 @@ def evaluate_word_node(node, context):
|
||||
|
||||
# Set all the characters in the part to None
|
||||
for i in range(part_start, part_end):
|
||||
letters[i] = None
|
||||
letters[i] = ''
|
||||
|
||||
letters[part_start] = evaluate_node(part, context=context)
|
||||
|
||||
# remove the None letters and concat
|
||||
value = ''.join(l for l in letters if l is not None)
|
||||
value = ''.join(letters)
|
||||
|
||||
# apply bash-like quotes/whitespace treatment
|
||||
return ' '.join(word.strip() for word in shlex.split(value))
|
||||
|
||||
|
||||
def evaluate_command_node(node, context):
|
||||
def evaluate_command_node(node: bashlex.ast.node, context: NodeExecutionContext) -> str:
|
||||
words = [evaluate_node(part, context=context) for part in node.parts]
|
||||
command = ' '.join(words)
|
||||
return subprocess.check_output(shlex.split(command), env=context.environment, universal_newlines=True)
|
||||
|
||||
|
||||
def evaluate_parameter_node(node, context):
|
||||
def evaluate_parameter_node(node: bashlex.ast.node, context: NodeExecutionContext) -> str:
|
||||
return context.environment.get(node.value, '')
|
||||
|
||||
+22
-20
@@ -1,4 +1,6 @@
|
||||
import bashlex
|
||||
import bashlex # type: ignore
|
||||
|
||||
from typing import Dict, List, Mapping
|
||||
|
||||
from . import bashlex_eval
|
||||
|
||||
@@ -7,13 +9,7 @@ class EnvironmentParseError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def parse_environment(env_string):
|
||||
env_items = split_env_items(env_string)
|
||||
assignments = [EnvironmentAssignment(item) for item in env_items]
|
||||
return ParsedEnvironment(assignments=assignments)
|
||||
|
||||
|
||||
def split_env_items(env_string):
|
||||
def split_env_items(env_string: str) -> List[str]:
|
||||
'''Splits space-separated variable assignments into a list of individual assignments.
|
||||
|
||||
>>> split_env_items('VAR=abc')
|
||||
@@ -43,30 +39,30 @@ def split_env_items(env_string):
|
||||
|
||||
|
||||
class EnvironmentAssignment:
|
||||
def __init__(self, assignment):
|
||||
def __init__(self, assignment: str):
|
||||
name, equals, value = assignment.partition('=')
|
||||
if not equals:
|
||||
raise EnvironmentParseError(assignment)
|
||||
self.name = name
|
||||
self.value = value
|
||||
|
||||
def evaluated_value(self, environment):
|
||||
def evaluated_value(self, environment: Dict[str, str]) -> str:
|
||||
'''Returns the value of this assignment, as evaluated in the environment'''
|
||||
return bashlex_eval.evaluate(self.value, environment=environment)
|
||||
|
||||
def as_shell_assignment(self):
|
||||
return 'export %s=%s' % (self.name, self.value)
|
||||
def as_shell_assignment(self) -> str:
|
||||
return f'export {self.name}={self.value}'
|
||||
|
||||
def __repr__(self):
|
||||
return '%s=%s' % (self.name, self.value)
|
||||
def __repr__(self) -> str:
|
||||
return f'{self.name}={self.value}'
|
||||
|
||||
|
||||
class ParsedEnvironment:
|
||||
def __init__(self, assignments):
|
||||
def __init__(self, assignments: List[EnvironmentAssignment]):
|
||||
self.assignments = assignments
|
||||
|
||||
def as_dictionary(self, prev_environment):
|
||||
environment = prev_environment.copy()
|
||||
def as_dictionary(self, prev_environment: Mapping[str, str]) -> Dict[str, str]:
|
||||
environment = dict(**prev_environment)
|
||||
|
||||
for assignment in self.assignments:
|
||||
value = assignment.evaluated_value(environment=environment)
|
||||
@@ -74,8 +70,14 @@ class ParsedEnvironment:
|
||||
|
||||
return environment
|
||||
|
||||
def as_shell_commands(self):
|
||||
def as_shell_commands(self) -> List[str]:
|
||||
return [a.as_shell_assignment() for a in self.assignments]
|
||||
|
||||
def __repr__(self):
|
||||
return 'ParsedEnvironment(%r)' % [repr(a) for a in self.assignments]
|
||||
def __repr__(self) -> str:
|
||||
return f'ParsedEnvironment({[repr(a) for a in self.assignments]!r})'
|
||||
|
||||
|
||||
def parse_environment(env_string: str) -> ParsedEnvironment:
|
||||
env_items = split_env_items(env_string)
|
||||
assignments = [EnvironmentAssignment(item) for item in env_items]
|
||||
return ParsedEnvironment(assignments=assignments)
|
||||
|
||||
+58
-29
@@ -5,23 +5,26 @@ import subprocess
|
||||
import sys
|
||||
import textwrap
|
||||
import uuid
|
||||
from collections import namedtuple
|
||||
from pathlib import Path, PurePath
|
||||
|
||||
from typing import List, NamedTuple, Optional, Union
|
||||
|
||||
from .util import (
|
||||
BuildOptions,
|
||||
BuildSelector,
|
||||
get_build_verbosity_extra_flags,
|
||||
prepare_command,
|
||||
BuildOptions
|
||||
)
|
||||
|
||||
|
||||
def call(args, input=None, universal_newlines=False):
|
||||
def call(args: List[str], input: Optional[Union[str, bytes]] = None, universal_newlines: bool = False) -> None:
|
||||
print('+ ' + ' '.join(shlex.quote(a) for a in args))
|
||||
subprocess.run(
|
||||
args, input=input, universal_newlines=universal_newlines, check=True
|
||||
)
|
||||
|
||||
|
||||
def matches_platform(identifier):
|
||||
def matches_platform(identifier: str) -> bool:
|
||||
pm = platform.machine()
|
||||
if pm == "x86_64":
|
||||
# x86_64 machines can run i686 docker containers
|
||||
@@ -42,8 +45,13 @@ def matches_platform(identifier):
|
||||
return False
|
||||
|
||||
|
||||
def get_python_configurations(build_selector):
|
||||
PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'identifier', 'path'])
|
||||
class PythonConfiguration(NamedTuple):
|
||||
version: str
|
||||
identifier: str
|
||||
path: str
|
||||
|
||||
|
||||
def get_python_configurations(build_selector: BuildSelector) -> List[PythonConfiguration]:
|
||||
python_configurations = [
|
||||
PythonConfiguration(version='2.7', identifier='cp27-manylinux_x86_64', path='/opt/python/cp27-cp27m'),
|
||||
PythonConfiguration(version='2.7', identifier='cp27-manylinux_x86_64', path='/opt/python/cp27-cp27mu'),
|
||||
@@ -76,7 +84,7 @@ def get_python_configurations(build_selector):
|
||||
return [c for c in python_configurations if matches_platform(c.identifier) and build_selector(c.identifier)]
|
||||
|
||||
|
||||
def build(options: BuildOptions):
|
||||
def build(options: BuildOptions) -> None:
|
||||
try:
|
||||
subprocess.check_call(['docker', '--version'])
|
||||
except Exception:
|
||||
@@ -86,6 +94,7 @@ def build(options: BuildOptions):
|
||||
file=sys.stderr)
|
||||
exit(2)
|
||||
|
||||
assert options.manylinux_images is not None
|
||||
python_configurations = get_python_configurations(options.build_selector)
|
||||
platforms = [
|
||||
('cp', 'manylinux_x86_64', options.manylinux_images['x86_64']),
|
||||
@@ -96,32 +105,58 @@ def build(options: BuildOptions):
|
||||
('pp', 'manylinux_x86_64', options.manylinux_images['pypy_x86_64']),
|
||||
]
|
||||
|
||||
if not os.path.realpath(options.package_dir).startswith(os.path.realpath('.')):
|
||||
cwd = Path.cwd()
|
||||
abs_package_dir = options.package_dir.resolve()
|
||||
if cwd != abs_package_dir and cwd not in abs_package_dir.parents:
|
||||
raise Exception('package_dir must be inside the working directory')
|
||||
|
||||
container_package_dir = os.path.join('/project', os.path.relpath(options.package_dir, '.'))
|
||||
container_package_dir = PurePath('/project') / abs_package_dir.relative_to(cwd)
|
||||
|
||||
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
|
||||
|
||||
container_name = 'cibuildwheel-{}'.format(uuid.uuid4())
|
||||
shell_cmd = ['linux32', '/bin/bash'] if platform_tag.endswith("i686") else ['/bin/bash']
|
||||
|
||||
container_name = f'cibuildwheel-{uuid.uuid4()}'
|
||||
call(['docker', 'create',
|
||||
'--env', 'CIBUILDWHEEL',
|
||||
'--name', container_name,
|
||||
'-i',
|
||||
'-v', '/:/host', # ignored on CircleCI
|
||||
docker_image,
|
||||
'/bin/bash'])
|
||||
|
||||
try:
|
||||
shell_cmd = ['linux32', '/bin/bash'] if platform_tag.endswith("i686") else ['/bin/bash']
|
||||
call(['docker', 'create',
|
||||
'--env', 'CIBUILDWHEEL',
|
||||
'--name', container_name,
|
||||
'-i',
|
||||
'-v', '/:/host', # ignored on CircleCI
|
||||
docker_image,
|
||||
'/bin/bash'])
|
||||
|
||||
call(['docker', 'cp', '.', container_name + ':/project'])
|
||||
|
||||
call(['docker', 'start', container_name])
|
||||
|
||||
if options.before_all:
|
||||
call(
|
||||
['docker', 'exec', '-i', container_name] + shell_cmd,
|
||||
universal_newlines=True,
|
||||
input='''
|
||||
PS4=' + '
|
||||
|
||||
set -o errexit
|
||||
set -o xtrace
|
||||
|
||||
# add a modern Python interpreter to PATH so it can be used by BEFORE_ALL
|
||||
# commands
|
||||
export PATH=/opt/python/cp38-cp38:$PATH
|
||||
|
||||
{environment_exports}
|
||||
|
||||
sh -c {before_all}
|
||||
|
||||
'''.format(
|
||||
environment_exports='\n'.join(options.environment.as_shell_commands()),
|
||||
before_all=shlex.quote(prepare_command(options.before_all, project='/project', package=container_package_dir))
|
||||
)
|
||||
)
|
||||
|
||||
for config in platform_configs:
|
||||
if options.dependency_constraints:
|
||||
constraints_file = options.dependency_constraints.get_for_python_version(config.version)
|
||||
@@ -192,7 +227,6 @@ def build(options: BuildOptions):
|
||||
pip install {dependency_install_flags} virtualenv
|
||||
venv_dir=`mktemp -d`/venv
|
||||
python -m virtualenv --no-download "$venv_dir"
|
||||
export __CIBW_VIRTUALENV_PATH__=$venv_dir
|
||||
|
||||
# run the tests in a subshell to keep that `activate`
|
||||
# script from polluting the env
|
||||
@@ -265,7 +299,7 @@ def build(options: BuildOptions):
|
||||
# copy the output back into the host
|
||||
call(['docker', 'cp',
|
||||
container_name + ':/output/.',
|
||||
os.path.abspath(options.output_dir)])
|
||||
str(options.output_dir.resolve())])
|
||||
except subprocess.CalledProcessError as error:
|
||||
troubleshoot(options.package_dir, error)
|
||||
exit(1)
|
||||
@@ -274,16 +308,11 @@ def build(options: BuildOptions):
|
||||
call(['docker', 'rm', '--force', '-v', container_name])
|
||||
|
||||
|
||||
def troubleshoot(package_dir, error):
|
||||
def troubleshoot(package_dir: Path, error: Exception) -> None:
|
||||
if (isinstance(error, subprocess.CalledProcessError) and 'exec' in error.cmd):
|
||||
# the bash script failed
|
||||
print('Checking for common errors...')
|
||||
so_files = []
|
||||
for root, dirs, files in os.walk(package_dir):
|
||||
for name in files:
|
||||
_, ext = os.path.splitext(name)
|
||||
if ext == '.so':
|
||||
so_files.append(os.path.join(root, name))
|
||||
so_files = list(package_dir.glob('**/*.so'))
|
||||
|
||||
if so_files:
|
||||
print(textwrap.dedent('''
|
||||
@@ -297,5 +326,5 @@ def troubleshoot(package_dir, error):
|
||||
'''))
|
||||
|
||||
print(' Files detected:')
|
||||
print('\n'.join([' ' + f for f in so_files]))
|
||||
print('\n'.join([f' {f}' for f in so_files]))
|
||||
print('')
|
||||
|
||||
+88
-68
@@ -4,37 +4,40 @@ import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from collections import namedtuple
|
||||
from glob import glob
|
||||
from os import PathLike
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, NamedTuple, Optional, Sequence, Union
|
||||
|
||||
from .util import (
|
||||
download,
|
||||
get_build_verbosity_extra_flags,
|
||||
prepare_command,
|
||||
get_pip_script,
|
||||
BuildOptions
|
||||
)
|
||||
from .environment import ParsedEnvironment
|
||||
from .util import (BuildOptions, BuildSelector, download,
|
||||
get_build_verbosity_extra_flags, get_pip_script,
|
||||
prepare_command)
|
||||
|
||||
|
||||
def call(args, env=None, cwd=None, shell=False):
|
||||
def call(args: Union[str, Sequence[Union[str, PathLike]]], env: Optional[Dict[str, str]] = None, cwd: Optional[str] = None, shell: bool = False) -> int:
|
||||
# print the command executing for the logs
|
||||
if shell:
|
||||
print('+ %s' % args)
|
||||
print(f'+ {args}')
|
||||
else:
|
||||
print('+ ' + ' '.join(shlex.quote(a) for a in args))
|
||||
print('+ ' + ' '.join(shlex.quote(str(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'])
|
||||
class PythonConfiguration(NamedTuple):
|
||||
version: str
|
||||
identifier: str
|
||||
url: str
|
||||
|
||||
|
||||
def get_python_configurations(build_selector: BuildSelector) -> List[PythonConfiguration]:
|
||||
python_configurations = [
|
||||
# CPython
|
||||
PythonConfiguration(version='2.7', identifier='cp27-macosx_x86_64', url='https://www.python.org/ftp/python/2.7.18/python-2.7.18-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.7/python-3.7.7-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='3.7', identifier='cp37-macosx_x86_64', url='https://www.python.org/ftp/python/3.7.8/python-3.7.8-macosx10.9.pkg'),
|
||||
PythonConfiguration(version='3.8', identifier='cp38-macosx_x86_64', url='https://www.python.org/ftp/python/3.8.3/python-3.8.3-macosx10.9.pkg'),
|
||||
# PyPy
|
||||
PythonConfiguration(version='2.7', identifier='pp27-macosx_x86_64', url='https://downloads.python.org/pypy/pypy2.7-v7.3.1-osx64.tar.bz2'),
|
||||
PythonConfiguration(version='3.6', identifier='pp36-macosx_x86_64', url='https://downloads.python.org/pypy/pypy3.6-v7.3.1-osx64.tar.bz2'),
|
||||
@@ -44,41 +47,41 @@ def get_python_configurations(build_selector):
|
||||
return [c for c in python_configurations if build_selector(c.identifier)]
|
||||
|
||||
|
||||
SYMLINKS_DIR = '/tmp/cibw_bin'
|
||||
SYMLINKS_DIR = Path('/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))
|
||||
def make_symlinks(installation_bin_path: Path, python_executable: str, pip_executable: str) -> None:
|
||||
assert (installation_bin_path / python_executable).exists()
|
||||
|
||||
# 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):
|
||||
if SYMLINKS_DIR.exists():
|
||||
shutil.rmtree(SYMLINKS_DIR)
|
||||
os.makedirs(SYMLINKS_DIR)
|
||||
SYMLINKS_DIR.mkdir(parents=True)
|
||||
|
||||
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'))
|
||||
(SYMLINKS_DIR / 'python').symlink_to(installation_bin_path / python_executable)
|
||||
(SYMLINKS_DIR / 'python-config').symlink_to(installation_bin_path / (python_executable + '-config'))
|
||||
(SYMLINKS_DIR / 'pip').symlink_to(installation_bin_path / pip_executable)
|
||||
|
||||
|
||||
def install_cpython(version, url):
|
||||
def install_cpython(version: str, url: str) -> Path:
|
||||
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)
|
||||
python_package_identifier = f'org.python.Python.PythonFramework-{version}'
|
||||
if python_package_identifier not in installed_system_packages:
|
||||
# download the pkg
|
||||
download(url, '/tmp/Python.pkg')
|
||||
download(url, Path('/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.2u/patch-macos-python-%s-openssl-v1.0.2u.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'])
|
||||
open_ssl_patch_url = f'https://github.com/mayeut/patch-macos-python-openssl/releases/download/v1.0.2u/patch-macos-python-{version}-openssl-v1.0.2u.tar.gz'
|
||||
download(open_ssl_patch_url, Path('/tmp/python-patch.tar.gz'))
|
||||
call(['sudo', 'tar', '-C', f'/Library/Frameworks/Python.framework/Versions/{version}/', '-xmf', '/tmp/python-patch.tar.gz'])
|
||||
|
||||
installation_bin_path = '/Library/Frameworks/Python.framework/Versions/{}/bin'.format(version)
|
||||
installation_bin_path = Path(f'/Library/Frameworks/Python.framework/Versions/{version}/bin')
|
||||
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)
|
||||
@@ -86,16 +89,18 @@ def install_cpython(version, url):
|
||||
return installation_bin_path
|
||||
|
||||
|
||||
def install_pypy(version, url):
|
||||
def install_pypy(version: str, url: str) -> Path:
|
||||
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)])
|
||||
extension = ".tar.bz2"
|
||||
assert pypy_tar_bz2.endswith(extension)
|
||||
pypy_base_filename = pypy_tar_bz2[:-len(extension)]
|
||||
installation_path = Path('/tmp') / pypy_base_filename
|
||||
if not installation_path.exists():
|
||||
downloaded_tar_bz2 = Path("/tmp") / pypy_tar_bz2
|
||||
download(url, downloaded_tar_bz2)
|
||||
call(['tar', '-C', '/tmp', '-xf', downloaded_tar_bz2])
|
||||
|
||||
installation_bin_path = os.path.join(installation_path, 'bin')
|
||||
installation_bin_path = 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)
|
||||
@@ -103,7 +108,9 @@ def install_pypy(version, url):
|
||||
return installation_bin_path
|
||||
|
||||
|
||||
def setup_python(python_configuration, dependency_constraint_flags, environment):
|
||||
def setup_python(python_configuration: PythonConfiguration,
|
||||
dependency_constraint_flags: Sequence[Union[str, PathLike]],
|
||||
environment: ParsedEnvironment) -> Dict[str, str]:
|
||||
if python_configuration.identifier.startswith('cp'):
|
||||
installation_bin_path = install_cpython(python_configuration.version, python_configuration.url)
|
||||
elif python_configuration.identifier.startswith('pp'):
|
||||
@@ -113,8 +120,8 @@ def setup_python(python_configuration, dependency_constraint_flags, environment)
|
||||
|
||||
env = os.environ.copy()
|
||||
env['PATH'] = os.pathsep.join([
|
||||
SYMLINKS_DIR,
|
||||
installation_bin_path,
|
||||
str(SYMLINKS_DIR),
|
||||
str(installation_bin_path),
|
||||
env['PATH'],
|
||||
])
|
||||
|
||||
@@ -136,15 +143,15 @@ def setup_python(python_configuration, dependency_constraint_flags, environment)
|
||||
exit(1)
|
||||
|
||||
# install pip & wheel
|
||||
call(['python', get_pip_script] + dependency_constraint_flags, env=env, cwd="/tmp")
|
||||
assert os.path.exists(os.path.join(installation_bin_path, 'pip'))
|
||||
call(['python', get_pip_script, *dependency_constraint_flags], env=env, cwd="/tmp")
|
||||
assert (installation_bin_path / 'pip').exists()
|
||||
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'] + dependency_constraint_flags, env=env)
|
||||
call(['pip', 'install', '--upgrade', 'setuptools', 'wheel', 'delocate', *dependency_constraint_flags], env=env)
|
||||
|
||||
# Set MACOSX_DEPLOYMENT_TARGET to 10.9, if the user didn't set it.
|
||||
# CPython 3.5 defaults to 10.6, and pypy defaults to 10.7, causing
|
||||
@@ -161,15 +168,20 @@ def setup_python(python_configuration, dependency_constraint_flags, environment)
|
||||
return env
|
||||
|
||||
|
||||
def build(options: BuildOptions):
|
||||
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')
|
||||
def build(options: BuildOptions) -> None:
|
||||
temp_dir = Path(tempfile.mkdtemp(prefix='cibuildwheel'))
|
||||
built_wheel_dir = temp_dir / 'built_wheel'
|
||||
repaired_wheel_dir = temp_dir / 'repaired_wheel'
|
||||
|
||||
if options.before_all:
|
||||
env = options.environment.as_dictionary(prev_environment=os.environ)
|
||||
before_all_prepared = prepare_command(options.before_all, project='.', package=options.package_dir)
|
||||
call([before_all_prepared], shell=True, env=env)
|
||||
|
||||
python_configurations = get_python_configurations(options.build_selector)
|
||||
|
||||
for config in python_configurations:
|
||||
dependency_constraint_flags = []
|
||||
dependency_constraint_flags: Sequence[Union[str, PathLike]] = []
|
||||
if options.dependency_constraints:
|
||||
dependency_constraint_flags = [
|
||||
'-c', options.dependency_constraints.get_for_python_version(config.version)
|
||||
@@ -183,29 +195,39 @@ def build(options: BuildOptions):
|
||||
call(before_build_prepared, env=env, shell=True)
|
||||
|
||||
# build the wheel
|
||||
if os.path.exists(built_wheel_dir):
|
||||
if built_wheel_dir.exists():
|
||||
shutil.rmtree(built_wheel_dir)
|
||||
os.makedirs(built_wheel_dir)
|
||||
call(['pip', 'wheel', options.package_dir, '-w', built_wheel_dir, '--no-deps'] + get_build_verbosity_extra_flags(options.build_verbosity), env=env)
|
||||
built_wheel = glob(os.path.join(built_wheel_dir, '*.whl'))[0]
|
||||
built_wheel_dir.mkdir(parents=True)
|
||||
|
||||
# Path.resolve() is needed. Without it pip wheel may try to fetch package from pypi.org
|
||||
# see https://github.com/joerick/cibuildwheel/pull/369
|
||||
call([
|
||||
'pip', 'wheel',
|
||||
options.package_dir.resolve(),
|
||||
'-w', built_wheel_dir,
|
||||
'--no-deps',
|
||||
*get_build_verbosity_extra_flags(options.build_verbosity)
|
||||
], env=env)
|
||||
|
||||
built_wheel = next(built_wheel_dir.glob('*.whl'))
|
||||
|
||||
# repair the wheel
|
||||
if os.path.exists(repaired_wheel_dir):
|
||||
if repaired_wheel_dir.exists():
|
||||
shutil.rmtree(repaired_wheel_dir)
|
||||
os.makedirs(repaired_wheel_dir)
|
||||
if built_wheel.endswith('none-any.whl') or not options.repair_command:
|
||||
repaired_wheel_dir.mkdir(parents=True)
|
||||
if built_wheel.name.endswith('none-any.whl') or not options.repair_command:
|
||||
# pure Python wheel or empty repair command
|
||||
shutil.move(built_wheel, repaired_wheel_dir)
|
||||
shutil.move(str(built_wheel), repaired_wheel_dir)
|
||||
else:
|
||||
repair_command_prepared = prepare_command(options.repair_command, wheel=built_wheel, dest_dir=repaired_wheel_dir)
|
||||
call(repair_command_prepared, env=env, shell=True)
|
||||
repaired_wheel = glob(os.path.join(repaired_wheel_dir, '*.whl'))[0]
|
||||
repaired_wheel = next(repaired_wheel_dir.glob('*.whl'))
|
||||
|
||||
if options.test_command:
|
||||
# set up a virtual environment to install and test from, to make sure
|
||||
# there are no dependencies that were pulled in at build time.
|
||||
call(['pip', 'install', 'virtualenv'] + dependency_constraint_flags, env=env)
|
||||
venv_dir = tempfile.mkdtemp()
|
||||
call(['pip', 'install', 'virtualenv', *dependency_constraint_flags], env=env)
|
||||
venv_dir = Path(tempfile.mkdtemp())
|
||||
|
||||
# Use --no-download to ensure determinism by using seed libraries
|
||||
# built into virtualenv
|
||||
@@ -213,10 +235,9 @@ def build(options: BuildOptions):
|
||||
|
||||
virtualenv_env = env.copy()
|
||||
virtualenv_env['PATH'] = os.pathsep.join([
|
||||
os.path.join(venv_dir, 'bin'),
|
||||
str(venv_dir / 'bin'),
|
||||
virtualenv_env['PATH'],
|
||||
])
|
||||
virtualenv_env["__CIBW_VIRTUALENV_PATH__"] = venv_dir
|
||||
|
||||
# check that we are using the Python from the virtual environment
|
||||
call(['which', 'python'], env=virtualenv_env)
|
||||
@@ -226,7 +247,7 @@ def build(options: BuildOptions):
|
||||
call(before_test_prepared, env=virtualenv_env, shell=True)
|
||||
|
||||
# install the wheel
|
||||
call(['pip', 'install', repaired_wheel + options.test_extras], env=virtualenv_env)
|
||||
call(['pip', 'install', str(repaired_wheel) + options.test_extras], env=virtualenv_env)
|
||||
|
||||
# test the wheel
|
||||
if options.test_requires:
|
||||
@@ -237,8 +258,8 @@ def build(options: BuildOptions):
|
||||
# and not the repo code)
|
||||
test_command_prepared = prepare_command(
|
||||
options.test_command,
|
||||
project=os.path.abspath('.'),
|
||||
package=os.path.abspath(options.package_dir)
|
||||
project=Path('.').resolve(),
|
||||
package=options.package_dir.resolve()
|
||||
)
|
||||
call(test_command_prepared, cwd=os.environ['HOME'], env=virtualenv_env, shell=True)
|
||||
|
||||
@@ -246,5 +267,4 @@ def build(options: BuildOptions):
|
||||
shutil.rmtree(venv_dir)
|
||||
|
||||
# we're all done here; move it to output (overwrite existing)
|
||||
dst = os.path.join(options.output_dir, os.path.basename(repaired_wheel))
|
||||
shutil.move(repaired_wheel, dst)
|
||||
shutil.move(str(repaired_wheel), options.output_dir)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#
|
||||
# bin/update_constraints.py
|
||||
#
|
||||
appdirs==1.4.3 # via virtualenv
|
||||
appdirs==1.4.4 # via virtualenv
|
||||
configparser==4.0.2 # via importlib-metadata
|
||||
contextlib2==0.6.0.post1 # via importlib-metadata, importlib-resources, virtualenv
|
||||
delocate==0.8.0 # via -r cibuildwheel/resources/constraints.in
|
||||
@@ -15,12 +15,12 @@ importlib-resources==1.5.0 # via virtualenv
|
||||
pathlib2==2.3.5 # via importlib-metadata, importlib-resources, virtualenv
|
||||
scandir==1.10.0 # via pathlib2
|
||||
singledispatch==3.4.0.3 # via importlib-resources
|
||||
six==1.14.0 # via pathlib2, virtualenv
|
||||
six==1.15.0 # via pathlib2, virtualenv
|
||||
typing==3.7.4.1 # via importlib-resources
|
||||
virtualenv==20.0.18 # via -r cibuildwheel/resources/constraints.in
|
||||
virtualenv==20.0.21 # via -r cibuildwheel/resources/constraints.in
|
||||
wheel==0.34.2 # via -r cibuildwheel/resources/constraints.in, delocate
|
||||
zipp==1.2.0 # via importlib-metadata, importlib-resources
|
||||
|
||||
# The following packages are considered to be unsafe in a requirements file:
|
||||
pip==20.1 # via -r cibuildwheel/resources/constraints.in
|
||||
pip==20.1.1 # via -r cibuildwheel/resources/constraints.in
|
||||
setuptools==44.1.0 # via -r cibuildwheel/resources/constraints.in
|
||||
|
||||
@@ -4,17 +4,17 @@
|
||||
#
|
||||
# bin/update_constraints.py
|
||||
#
|
||||
appdirs==1.4.3 # via virtualenv
|
||||
appdirs==1.4.4 # via virtualenv
|
||||
delocate==0.8.0 # via -r cibuildwheel/resources/constraints.in
|
||||
distlib==0.3.0 # via virtualenv
|
||||
filelock==3.0.12 # via virtualenv
|
||||
importlib-metadata==1.6.0 # via importlib-resources, virtualenv
|
||||
importlib-resources==1.5.0 # via virtualenv
|
||||
six==1.14.0 # via virtualenv
|
||||
virtualenv==20.0.18 # via -r cibuildwheel/resources/constraints.in
|
||||
six==1.15.0 # via virtualenv
|
||||
virtualenv==20.0.21 # via -r cibuildwheel/resources/constraints.in
|
||||
wheel==0.34.2 # via -r cibuildwheel/resources/constraints.in, delocate
|
||||
zipp==1.2.0 # via importlib-metadata, importlib-resources
|
||||
|
||||
# The following packages are considered to be unsafe in a requirements file:
|
||||
pip==20.1 # via -r cibuildwheel/resources/constraints.in
|
||||
setuptools==46.1.3 # via -r cibuildwheel/resources/constraints.in
|
||||
pip==20.1.1 # via -r cibuildwheel/resources/constraints.in
|
||||
setuptools==46.4.0 # via -r cibuildwheel/resources/constraints.in
|
||||
|
||||
@@ -4,17 +4,17 @@
|
||||
#
|
||||
# bin/update_constraints.py
|
||||
#
|
||||
appdirs==1.4.3 # via virtualenv
|
||||
appdirs==1.4.4 # via virtualenv
|
||||
delocate==0.8.0 # via -r cibuildwheel/resources/constraints.in
|
||||
distlib==0.3.0 # via virtualenv
|
||||
filelock==3.0.12 # via virtualenv
|
||||
importlib-metadata==1.6.0 # via importlib-resources, virtualenv
|
||||
importlib-resources==1.5.0 # via virtualenv
|
||||
six==1.14.0 # via virtualenv
|
||||
virtualenv==20.0.18 # via -r cibuildwheel/resources/constraints.in
|
||||
six==1.15.0 # via virtualenv
|
||||
virtualenv==20.0.21 # via -r cibuildwheel/resources/constraints.in
|
||||
wheel==0.34.2 # via -r cibuildwheel/resources/constraints.in, delocate
|
||||
zipp==3.1.0 # via importlib-metadata, importlib-resources
|
||||
|
||||
# The following packages are considered to be unsafe in a requirements file:
|
||||
pip==20.1 # via -r cibuildwheel/resources/constraints.in
|
||||
setuptools==46.1.3 # via -r cibuildwheel/resources/constraints.in
|
||||
pip==20.1.1 # via -r cibuildwheel/resources/constraints.in
|
||||
setuptools==46.4.0 # via -r cibuildwheel/resources/constraints.in
|
||||
|
||||
@@ -4,16 +4,16 @@
|
||||
#
|
||||
# bin/update_constraints.py
|
||||
#
|
||||
appdirs==1.4.3 # via virtualenv
|
||||
appdirs==1.4.4 # via virtualenv
|
||||
delocate==0.8.0 # via -r cibuildwheel/resources/constraints.in
|
||||
distlib==0.3.0 # via virtualenv
|
||||
filelock==3.0.12 # via virtualenv
|
||||
importlib-metadata==1.6.0 # via virtualenv
|
||||
six==1.14.0 # via virtualenv
|
||||
virtualenv==20.0.18 # via -r cibuildwheel/resources/constraints.in
|
||||
six==1.15.0 # via virtualenv
|
||||
virtualenv==20.0.21 # via -r cibuildwheel/resources/constraints.in
|
||||
wheel==0.34.2 # via -r cibuildwheel/resources/constraints.in, delocate
|
||||
zipp==3.1.0 # via importlib-metadata
|
||||
|
||||
# The following packages are considered to be unsafe in a requirements file:
|
||||
pip==20.1 # via -r cibuildwheel/resources/constraints.in
|
||||
setuptools==46.1.3 # via -r cibuildwheel/resources/constraints.in
|
||||
pip==20.1.1 # via -r cibuildwheel/resources/constraints.in
|
||||
setuptools==46.4.0 # via -r cibuildwheel/resources/constraints.in
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
[x86_64]
|
||||
manylinux1 = quay.io/pypa/manylinux1_x86_64:2020-04-25-37c204c
|
||||
manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2020-04-29-0e1afc5
|
||||
manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2020-05-01-b37d76b
|
||||
manylinux1 = quay.io/pypa/manylinux1_x86_64:2020-05-25-b56702d
|
||||
manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2020-05-20-c7bbd11
|
||||
manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2020-05-17-2f8ac3b
|
||||
|
||||
[i686]
|
||||
manylinux1 = quay.io/pypa/manylinux1_i686:2020-04-25-37c204c
|
||||
manylinux2010 = quay.io/pypa/manylinux2010_i686:2020-04-29-0e1afc5
|
||||
manylinux2014 = quay.io/pypa/manylinux2014_i686:2020-05-01-b37d76b
|
||||
manylinux1 = quay.io/pypa/manylinux1_i686:2020-05-25-b56702d
|
||||
manylinux2010 = quay.io/pypa/manylinux2010_i686:2020-05-20-c7bbd11
|
||||
manylinux2014 = quay.io/pypa/manylinux2014_i686:2020-05-17-2f8ac3b
|
||||
|
||||
[pypy_x86_64]
|
||||
manylinux2010 = pypywheels/manylinux2010-pypy_x86_64:2020-04-25-eb2cdff
|
||||
|
||||
[aarch64]
|
||||
manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2020-05-01-b37d76b
|
||||
manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2020-05-17-2f8ac3b
|
||||
|
||||
[ppc64le]
|
||||
manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2020-05-01-b37d76b
|
||||
manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2020-05-17-2f8ac3b
|
||||
|
||||
[s390x]
|
||||
manylinux2014 = quay.io/pypa/manylinux2014_s390x:2020-05-01-b37d76b
|
||||
manylinux2014 = quay.io/pypa/manylinux2014_s390x:2020-05-17-2f8ac3b
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
--- 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'
|
||||
+49
-52
@@ -1,13 +1,15 @@
|
||||
import os
|
||||
import urllib.request
|
||||
from fnmatch import fnmatch
|
||||
from pathlib import Path
|
||||
from time import sleep
|
||||
from typing import NamedTuple, List, Optional, Dict
|
||||
|
||||
from typing import Dict, List, NamedTuple, Optional, Union
|
||||
|
||||
from .environment import ParsedEnvironment
|
||||
|
||||
|
||||
def prepare_command(command, **kwargs):
|
||||
def prepare_command(command: str, **kwargs: Union[str, os.PathLike]) -> str:
|
||||
'''
|
||||
Preprocesses a command by expanding variables like {python}.
|
||||
|
||||
@@ -17,7 +19,7 @@ def prepare_command(command, **kwargs):
|
||||
return command.format(python='python', pip='pip', **kwargs)
|
||||
|
||||
|
||||
def get_build_verbosity_extra_flags(level):
|
||||
def get_build_verbosity_extra_flags(level: int) -> List[str]:
|
||||
if level > 0:
|
||||
return ['-' + level * 'v']
|
||||
elif level < 0:
|
||||
@@ -27,41 +29,41 @@ def get_build_verbosity_extra_flags(level):
|
||||
|
||||
|
||||
class BuildSelector:
|
||||
def __init__(self, build_config, skip_config):
|
||||
def __init__(self, build_config: str, skip_config: str):
|
||||
self.build_patterns = build_config.split()
|
||||
self.skip_patterns = skip_config.split()
|
||||
|
||||
def __call__(self, build_id):
|
||||
def match_any(patterns):
|
||||
def __call__(self, build_id: str) -> bool:
|
||||
def match_any(patterns: List[str]) -> bool:
|
||||
return any(fnmatch(build_id, pattern) for pattern in patterns)
|
||||
return match_any(self.build_patterns) and not match_any(self.skip_patterns)
|
||||
|
||||
def __repr__(self):
|
||||
return 'BuildSelector({!r} - {!r})'.format(' '.join(self.build_patterns), ' '.join(self.skip_patterns))
|
||||
def __repr__(self) -> str:
|
||||
return f'BuildSelector({" ".join(self.build_patterns)!r} - {" ".join(self.skip_patterns)!r})'
|
||||
|
||||
|
||||
# Taken from https://stackoverflow.com/a/107717
|
||||
class Unbuffered:
|
||||
def __init__(self, stream):
|
||||
def __init__(self, stream): # type: ignore
|
||||
self.stream = stream
|
||||
|
||||
def write(self, data):
|
||||
def write(self, data): # type: ignore
|
||||
self.stream.write(data)
|
||||
self.stream.flush()
|
||||
|
||||
def writelines(self, datas):
|
||||
def writelines(self, datas): # type: ignore
|
||||
self.stream.writelines(datas)
|
||||
self.stream.flush()
|
||||
|
||||
def __getattr__(self, attr):
|
||||
def __getattr__(self, attr): # type: ignore
|
||||
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)
|
||||
def download(url: str, dest: Path) -> None:
|
||||
print(f'+ Download {url} to {dest}')
|
||||
dest_dir = dest.parent
|
||||
if not dest_dir.exists():
|
||||
dest_dir.mkdir(parents=True)
|
||||
|
||||
repeat_num = 3
|
||||
for i in range(repeat_num):
|
||||
@@ -75,57 +77,52 @@ def download(url, dest):
|
||||
break
|
||||
|
||||
try:
|
||||
with open(dest, 'wb') as file:
|
||||
file.write(response.read())
|
||||
dest.write_bytes(response.read())
|
||||
finally:
|
||||
response.close()
|
||||
|
||||
|
||||
class DependencyConstraints:
|
||||
def __init__(self, base_file_path):
|
||||
assert os.path.exists(base_file_path)
|
||||
self.base_file_path = os.path.abspath(base_file_path)
|
||||
def __init__(self, base_file_path: Path):
|
||||
assert base_file_path.exists()
|
||||
self.base_file_path = base_file_path.resolve()
|
||||
|
||||
@classmethod
|
||||
def with_defaults(cls):
|
||||
return cls(
|
||||
base_file_path=os.path.join(os.path.dirname(__file__), 'resources', 'constraints.txt')
|
||||
@staticmethod
|
||||
def with_defaults() -> 'DependencyConstraints':
|
||||
return DependencyConstraints(
|
||||
base_file_path=resources_dir / 'constraints.txt'
|
||||
)
|
||||
|
||||
def get_for_python_version(self, version):
|
||||
def get_for_python_version(self, version: str) -> Path:
|
||||
version_parts = version.split('.')
|
||||
|
||||
# try to find a version-specific dependency file e.g. if
|
||||
# ./constraints.txt is the base, look for ./constraints-python27.txt
|
||||
base, ext = os.path.splitext(self.base_file_path)
|
||||
specific = base + '-python{}{}'.format(version_parts[0], version_parts[1])
|
||||
specific_file_path = specific + ext
|
||||
if os.path.exists(specific_file_path):
|
||||
specific_stem = self.base_file_path.stem + f'-python{version_parts[0]}{version_parts[1]}'
|
||||
specific_name = specific_stem + self.base_file_path.suffix
|
||||
specific_file_path = self.base_file_path.with_name(specific_name)
|
||||
if specific_file_path.exists():
|
||||
return specific_file_path
|
||||
else:
|
||||
return self.base_file_path
|
||||
|
||||
|
||||
BuildOptions = NamedTuple("BuildOptions", [
|
||||
("package_dir", str),
|
||||
("output_dir", str),
|
||||
("test_command", Optional[str]),
|
||||
("test_requires", List[str]),
|
||||
("test_extras", str),
|
||||
("before_build", Optional[str]),
|
||||
("build_verbosity", int),
|
||||
("build_selector", BuildSelector),
|
||||
("repair_command", str),
|
||||
("environment", ParsedEnvironment),
|
||||
("before_test", str),
|
||||
("dependency_constraints", Optional[DependencyConstraints]),
|
||||
("manylinux_images", Optional[Dict[str, str]]),
|
||||
])
|
||||
class BuildOptions(NamedTuple):
|
||||
package_dir: Path
|
||||
output_dir: Path
|
||||
build_selector: BuildSelector
|
||||
environment: ParsedEnvironment
|
||||
before_all: str
|
||||
before_build: Optional[str]
|
||||
repair_command: str
|
||||
manylinux_images: Optional[Dict[str, str]]
|
||||
dependency_constraints: Optional[DependencyConstraints]
|
||||
test_command: Optional[str]
|
||||
before_test: Optional[str]
|
||||
test_requires: List[str]
|
||||
test_extras: str
|
||||
build_verbosity: int
|
||||
|
||||
"""
|
||||
Replace this definition with a class-style NamedTuple in the
|
||||
PEP526 style when Python 3.5 host support is dropped
|
||||
"""
|
||||
|
||||
resources_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), 'resources'))
|
||||
get_pip_script = os.path.join(resources_dir, 'get-pip.py')
|
||||
resources_dir = Path(__file__).resolve().parent / 'resources'
|
||||
get_pip_script = resources_dir / 'get-pip.py'
|
||||
|
||||
+136
-86
@@ -3,37 +3,49 @@ import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from collections import namedtuple
|
||||
from glob import glob
|
||||
from os import PathLike
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, NamedTuple, Optional, Sequence, Union
|
||||
from zipfile import ZipFile
|
||||
import toml
|
||||
|
||||
from .util import (
|
||||
download,
|
||||
get_build_verbosity_extra_flags,
|
||||
prepare_command,
|
||||
get_pip_script,
|
||||
BuildOptions
|
||||
)
|
||||
from .environment import ParsedEnvironment
|
||||
from .util import (BuildOptions, BuildSelector, download,
|
||||
get_build_verbosity_extra_flags, get_pip_script,
|
||||
prepare_command)
|
||||
|
||||
|
||||
IS_RUNNING_ON_AZURE = os.path.exists('C:\\hostedtoolcache')
|
||||
IS_RUNNING_ON_AZURE = Path('C:\\hostedtoolcache').exists()
|
||||
IS_RUNNING_ON_TRAVIS = os.environ.get('TRAVIS_OS_NAME') == 'windows'
|
||||
|
||||
|
||||
def shell(args, env=None, cwd=None):
|
||||
print('+ ' + ' '.join(args))
|
||||
return subprocess.check_call(' '.join(args), env=env, cwd=cwd, shell=True)
|
||||
def call(args: Sequence[Union[str, PathLike]], env: Optional[Dict[str, str]] = None,
|
||||
cwd: Optional[str] = None) -> None:
|
||||
print('+ ' + ' '.join(str(a) for a in args))
|
||||
# we use shell=True here, even though we don't need a shell due to a bug
|
||||
# https://bugs.python.org/issue8557
|
||||
subprocess.check_call([str(a) for a in args], env=env, cwd=cwd, shell=True)
|
||||
|
||||
|
||||
def get_nuget_args(version, arch):
|
||||
def shell(command: str, env: Optional[Dict[str, str]] = None, cwd: Optional[str] = None) -> None:
|
||||
print(f'+ {command}')
|
||||
subprocess.check_call(command, env=env, cwd=cwd, shell=True)
|
||||
|
||||
|
||||
def get_nuget_args(version: str, arch: str) -> List[str]:
|
||||
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', 'url'])
|
||||
class PythonConfiguration(NamedTuple):
|
||||
version: str
|
||||
arch: str
|
||||
identifier: str
|
||||
url: Optional[str]
|
||||
|
||||
|
||||
def get_python_configurations(build_selector: BuildSelector) -> List[PythonConfiguration]:
|
||||
python_configurations = [
|
||||
# CPython
|
||||
PythonConfiguration(version='2.7.18', arch='32', identifier='cp27-win32', url=None),
|
||||
@@ -42,10 +54,10 @@ def get_python_configurations(build_selector):
|
||||
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.7', arch='32', identifier='cp37-win32', url=None),
|
||||
PythonConfiguration(version='3.7.7', 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='3.7.8', arch='32', identifier='cp37-win32', url=None),
|
||||
PythonConfiguration(version='3.7.8', arch='64', identifier='cp37-win_amd64', url=None),
|
||||
PythonConfiguration(version='3.8.3', arch='32', identifier='cp38-win32', url=None),
|
||||
PythonConfiguration(version='3.8.3', arch='64', identifier='cp38-win_amd64', url=None),
|
||||
# PyPy
|
||||
PythonConfiguration(version='2.7', arch='32', identifier='pp27-win32', url='https://downloads.python.org/pypy/pypy2.7-v7.3.1-win32.zip'),
|
||||
PythonConfiguration(version='3.6', arch='32', identifier='pp36-win32', url='https://downloads.python.org/pypy/pypy3.6-v7.3.1-win32.zip'),
|
||||
@@ -62,97 +74,130 @@ def get_python_configurations(build_selector):
|
||||
return python_configurations
|
||||
|
||||
|
||||
def extract_zip(zip_src, dest):
|
||||
def extract_zip(zip_src: Path, dest: Path) -> None:
|
||||
with ZipFile(zip_src) as zip:
|
||||
zip.extractall(dest)
|
||||
|
||||
|
||||
def install_cpython(version, arch, nuget):
|
||||
def install_cpython(version: str, arch: str, nuget: Path) -> Path:
|
||||
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)
|
||||
installation_path = Path(nuget_args[-1]) / (nuget_args[0] + '.' + version) / 'tools'
|
||||
call([nuget, 'install', *nuget_args])
|
||||
return installation_path
|
||||
|
||||
|
||||
def install_pypy(version, arch, url):
|
||||
def install_pypy(version: str, arch: str, url: str) -> Path:
|
||||
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)
|
||||
extension = ".zip"
|
||||
assert zip_filename.endswith(extension)
|
||||
installation_path = Path('C:\\cibw') / zip_filename[:-len(extension)]
|
||||
if not installation_path.exists():
|
||||
pypy_zip = Path('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))
|
||||
extract_zip(pypy_zip, installation_path.parent)
|
||||
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)])
|
||||
(installation_path / 'python.exe').symlink_to(installation_path / pypy_exe)
|
||||
return installation_path
|
||||
|
||||
|
||||
def setup_python(python_configuration, dependency_constraint_flags, environment):
|
||||
nuget = 'C:\\cibw\\nuget.exe'
|
||||
if not os.path.exists(nuget):
|
||||
def setup_python(python_configuration: PythonConfiguration, dependency_constraint_flags: Sequence[Union[str, PathLike]], environment: ParsedEnvironment) -> Dict[str, str]:
|
||||
nuget = Path('C:\\cibw\\nuget.exe')
|
||||
if not nuget.exists():
|
||||
download('https://dist.nuget.org/win-x86-commandline/latest/nuget.exe', nuget)
|
||||
|
||||
if python_configuration.identifier.startswith('cp'):
|
||||
installation_path = install_cpython(python_configuration.version, python_configuration.arch, nuget)
|
||||
elif python_configuration.identifier.startswith('pp'):
|
||||
assert python_configuration.url is not None
|
||||
installation_path = install_pypy(python_configuration.version, python_configuration.arch, python_configuration.url)
|
||||
else:
|
||||
raise ValueError("Unknown Python implementation")
|
||||
|
||||
assert os.path.exists(os.path.join(installation_path, 'python.exe'))
|
||||
assert (installation_path / 'python.exe').exists()
|
||||
|
||||
# set up PATH and environment variables for run_with_env
|
||||
env = os.environ.copy()
|
||||
env['PYTHON_VERSION'] = python_configuration.version
|
||||
env['PYTHON_ARCH'] = python_configuration.arch
|
||||
env['PATH'] = os.pathsep.join([
|
||||
installation_path,
|
||||
os.path.join(installation_path, 'Scripts'),
|
||||
str(installation_path),
|
||||
str(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
|
||||
shell(['where', 'python'], env=env)
|
||||
shell(['python', '--version'], env=env)
|
||||
shell(['python', '-c', '"import struct; print(struct.calcsize(\'P\') * 8)"'], env=env)
|
||||
call(['where', 'python'], env=env)
|
||||
call(['python', '--version'], env=env)
|
||||
call(['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'):
|
||||
if where_python != str(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(installation_path, 'Scripts', 'pip.exe')):
|
||||
shell(['python', get_pip_script] + dependency_constraint_flags, env=env, cwd="C:\\cibw")
|
||||
assert os.path.exists(os.path.join(installation_path, 'Scripts', 'pip.exe'))
|
||||
if not (installation_path / 'Scripts' / 'pip.exe').exists():
|
||||
call(['python', get_pip_script, *dependency_constraint_flags], env=env, cwd="C:\\cibw")
|
||||
assert (installation_path / 'Scripts' / 'pip.exe').exists()
|
||||
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'):
|
||||
if where_pip.strip() != str(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
|
||||
shell(['python', '-m', 'pip', 'install', '--upgrade', 'pip'] + dependency_constraint_flags, env=env)
|
||||
shell(['pip', '--version'], env=env)
|
||||
shell(['pip', 'install', '--upgrade', 'setuptools', 'wheel'] + dependency_constraint_flags, env=env)
|
||||
call(['python', '-m', 'pip', 'install', '--upgrade', 'pip', *dependency_constraint_flags], env=env)
|
||||
call(['pip', '--version'], env=env)
|
||||
call(['pip', 'install', '--upgrade', 'setuptools', 'wheel', *dependency_constraint_flags], env=env)
|
||||
|
||||
return env
|
||||
|
||||
|
||||
def build(options: BuildOptions):
|
||||
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')
|
||||
def pep_518_cp35_workaround(package_dir: Path, env: Dict[str, str]) -> None:
|
||||
"""
|
||||
Python 3.5 PEP 518 hack (see https://github.com/pypa/pip/issues/8392#issuecomment-639563494)
|
||||
Basically, nuget's Python is an embedded Python distribution, which is not supported by pip.
|
||||
Before version 3.6, there was no way to disable the "embedded" behavior, including the ignoring
|
||||
of environment variables, including the ones pip uses to setup PEP 518 builds.
|
||||
|
||||
The fix here is as suggested in that issue; we manually setup the PEP 518 requirements. Since we
|
||||
are in a fresh environment (except for pinned cibuildweel dependencies), the build is already
|
||||
mostly "isolated".
|
||||
"""
|
||||
|
||||
pyproject_path = package_dir / 'pyproject.toml'
|
||||
|
||||
if pyproject_path.exists():
|
||||
data = toml.load(pyproject_path)
|
||||
requirements = (
|
||||
data['build-system'].get('requires', [])
|
||||
if 'build-system' in data
|
||||
else []
|
||||
)
|
||||
if requirements:
|
||||
shell(['pip', 'install'] + requirements, env=env)
|
||||
|
||||
|
||||
def build(options: BuildOptions) -> None:
|
||||
temp_dir = Path(tempfile.mkdtemp(prefix='cibuildwheel'))
|
||||
built_wheel_dir = temp_dir / 'built_wheel'
|
||||
repaired_wheel_dir = temp_dir / 'repaired_wheel'
|
||||
|
||||
# install nuget as best way to provide python
|
||||
nuget = 'C:\\cibw\\nuget.exe'
|
||||
nuget = Path('C:\\cibw\\nuget.exe')
|
||||
download('https://dist.nuget.org/win-x86-commandline/latest/nuget.exe', nuget)
|
||||
|
||||
if options.before_all:
|
||||
env = options.environment.as_dictionary(prev_environment=os.environ)
|
||||
before_all_prepared = prepare_command(options.before_all, project='.', package=options.package_dir)
|
||||
shell(before_all_prepared, env=env)
|
||||
|
||||
python_configurations = get_python_configurations(options.build_selector)
|
||||
for config in python_configurations:
|
||||
dependency_constraint_flags = []
|
||||
dependency_constraint_flags: Sequence[Union[str, PathLike]] = []
|
||||
if options.dependency_constraints:
|
||||
dependency_constraint_flags = [
|
||||
'-c', options.dependency_constraints.get_for_python_version(config.version)
|
||||
@@ -164,51 +209,59 @@ def build(options: BuildOptions):
|
||||
# run the before_build command
|
||||
if options.before_build:
|
||||
before_build_prepared = prepare_command(options.before_build, project='.', package=options.package_dir)
|
||||
shell([before_build_prepared], env=env)
|
||||
shell(before_build_prepared, env=env)
|
||||
|
||||
# activate the PEP 518 patch if on Windows Python 3.5
|
||||
# (will only have an effect if PEP 517 builds are used):
|
||||
if config.version.startswith('3.5'):
|
||||
pep_518_cp35_workaround(options.package_dir, env)
|
||||
|
||||
# build the wheel
|
||||
if os.path.exists(built_wheel_dir):
|
||||
if built_wheel_dir.exists():
|
||||
shutil.rmtree(built_wheel_dir)
|
||||
os.makedirs(built_wheel_dir)
|
||||
shell(['pip', 'wheel', options.package_dir, '-w', built_wheel_dir, '--no-deps'] + get_build_verbosity_extra_flags(options.build_verbosity), env=env)
|
||||
built_wheel = glob(os.path.join(built_wheel_dir, '*.whl'))[0]
|
||||
built_wheel_dir.mkdir(parents=True)
|
||||
# Path.resolve() is needed. Without it pip wheel may try to fetch package from pypi.org
|
||||
# see https://github.com/joerick/cibuildwheel/pull/369
|
||||
call([
|
||||
'pip', 'wheel',
|
||||
options.package_dir.resolve(),
|
||||
'-w', built_wheel_dir,
|
||||
'--no-deps',
|
||||
*get_build_verbosity_extra_flags(options.build_verbosity)
|
||||
], env=env)
|
||||
|
||||
built_wheel = next(built_wheel_dir.glob('*.whl'))
|
||||
|
||||
# repair the wheel
|
||||
if os.path.exists(repaired_wheel_dir):
|
||||
if repaired_wheel_dir.exists():
|
||||
shutil.rmtree(repaired_wheel_dir)
|
||||
os.makedirs(repaired_wheel_dir)
|
||||
if built_wheel.endswith('none-any.whl') or not options.repair_command:
|
||||
repaired_wheel_dir.mkdir(parents=True)
|
||||
if built_wheel.name.endswith('none-any.whl') or not options.repair_command:
|
||||
# pure Python wheel or empty repair command
|
||||
shutil.move(built_wheel, repaired_wheel_dir)
|
||||
shutil.move(str(built_wheel), repaired_wheel_dir)
|
||||
else:
|
||||
repair_command_prepared = prepare_command(options.repair_command, wheel=built_wheel, dest_dir=repaired_wheel_dir)
|
||||
shell([repair_command_prepared], env=env)
|
||||
repaired_wheel = glob(os.path.join(repaired_wheel_dir, '*.whl'))[0]
|
||||
shell(repair_command_prepared, env=env)
|
||||
repaired_wheel = next(repaired_wheel_dir.glob('*.whl'))
|
||||
|
||||
if options.test_command:
|
||||
# set up a virtual environment to install and test from, to make sure
|
||||
# there are no dependencies that were pulled in at build time.
|
||||
shell(['pip', 'install', 'virtualenv'] + dependency_constraint_flags, env=env)
|
||||
venv_dir = tempfile.mkdtemp()
|
||||
call(['pip', 'install', 'virtualenv', *dependency_constraint_flags], env=env)
|
||||
venv_dir = Path(tempfile.mkdtemp())
|
||||
|
||||
# Use --no-download to ensure determinism by using seed libraries
|
||||
# built into virtualenv
|
||||
shell(['python', '-m', 'virtualenv', '--no-download', venv_dir], env=env)
|
||||
call(['python', '-m', 'virtualenv', '--no-download', 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([
|
||||
venv_script_path,
|
||||
str(venv_dir / 'Scripts'),
|
||||
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)
|
||||
call(['which', 'python'], env=virtualenv_env)
|
||||
|
||||
if options.before_test:
|
||||
before_test_prepared = prepare_command(
|
||||
@@ -216,30 +269,27 @@ def build(options: BuildOptions):
|
||||
project='.',
|
||||
package=options.package_dir
|
||||
)
|
||||
shell([before_test_prepared], env=virtualenv_env)
|
||||
shell(before_test_prepared, env=virtualenv_env)
|
||||
|
||||
# install the wheel
|
||||
shell(['pip', 'install', repaired_wheel + options.test_extras], env=virtualenv_env)
|
||||
call(['pip', 'install', str(repaired_wheel) + options.test_extras], env=virtualenv_env)
|
||||
|
||||
# test the wheel
|
||||
if options.test_requires:
|
||||
shell(['pip', 'install'] + options.test_requires, env=virtualenv_env)
|
||||
call(['pip', 'install'] + options.test_requires, env=virtualenv_env)
|
||||
|
||||
# run the tests from c:\, with an absolute path in the command
|
||||
# (this ensures that Python runs the tests against the installed wheel
|
||||
# and not the repo code)
|
||||
test_command_prepared = prepare_command(
|
||||
options.test_command,
|
||||
project=os.path.abspath('.'),
|
||||
package=os.path.abspath(options.package_dir)
|
||||
project=Path('.').resolve(),
|
||||
package=options.package_dir.resolve()
|
||||
)
|
||||
shell([test_command_prepared], cwd='c:\\', env=virtualenv_env)
|
||||
shell(test_command_prepared, cwd='c:\\', env=virtualenv_env)
|
||||
|
||||
# clean up
|
||||
shutil.rmtree(venv_dir)
|
||||
|
||||
# we're all done here; move it to output (remove if already exists)
|
||||
dst = os.path.join(options.output_dir, os.path.basename(repaired_wheel))
|
||||
if os.path.isfile(dst):
|
||||
os.remove(dst)
|
||||
shutil.move(repaired_wheel, dst)
|
||||
shutil.move(str(repaired_wheel), options.output_dir)
|
||||
|
||||
+36
-2
@@ -30,6 +30,40 @@ For more details see https://en.cppreference.com/w/cpp/compiler_support, https:/
|
||||
|
||||
## 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*`.
|
||||
Visual C++ for Python 2.7 does not support modern C++ standards (i.e., C++11 and later). 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.
|
||||
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"](https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads), since the newer C++ standard library's binaries are not included by default with the Python 2.7 installation.
|
||||
|
||||
Forcing `distutils` or `setuptools` to use a more recent version of MSVC that supports modern C++ can be done in the following way:
|
||||
|
||||
1. Set the environment variables `DISTUTILS_USE_SDK=1` and `MSSdk=1`. These two environment variables will tell `distutils`/`setuptools` to not search and set up a build environment that uses Visual C++ for Python 2.7 (aka. MSVC 9).
|
||||
2. Set up the build Visual Studio build environment you want to use, making sure that e.g. `PATH` contains `cl`, `link`, etc.
|
||||
- Usually, this can be done through `vcvarsall.bat x86` or `vcvarsall.bat x64`. The exact location of this file depends on the installation, but the default path for VS 2019 Community (e.g. used in AppVeyor's `Visual Studio 2019` image) is `C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat`. **Note**: `vcvarsall.bat` changes the environment variables, so this cannot be run in a subprocess/subshell and consequently running `vsvarsall.bat` in `CIBW_BEFORE_BUILD` does not have any effect.
|
||||
- In Azure Pipelines, [a `VSBuild` task is available](https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/build/visual-studio-build)
|
||||
- In GitHub Actions, the default shell is powershell, so you'll need to use `shell: cmd` to source a `.bat` file, and the directory is `"C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat"`. Or you can use an action, such as the [`ilammy/msvc-dev-cmd@v1`](https://github.com/ilammy/msvc-dev-cmd) action to setup the environment (see example below).
|
||||
3. Next, call `cibuildwheel`. Unfortunately, MSVC has separate toolchains for compiling 32-bit and 64-bit, so you will need to run `cibuildwheel` twice: once with `CIBW_BUILD=*-win32` after setting up the `x86` build environment, and once with `CIBW_BUILD=*-win_amd64` in a `x64` environment (see previous step).
|
||||
|
||||
|
||||
**GitHub Action example with ilammy/msvc-dev-cmd**:
|
||||
|
||||
```yaml
|
||||
- uses: ilammy/msvc-dev-cmd@v1
|
||||
|
||||
- name: Build 64-bit wheel
|
||||
run: python -m cibuildwheel --output-dir wheelhouse
|
||||
env:
|
||||
CIBW_BUILD: cp27-win_amd64
|
||||
DISTUTILS_USE_SDK: 1
|
||||
MSSdk: 1
|
||||
|
||||
- uses: ilammy/msvc-dev-cmd@v1
|
||||
with:
|
||||
arch: x86
|
||||
|
||||
- name: Build 32-bit wheel
|
||||
run: python -m cibuildwheel --output-dir wheelhouse
|
||||
env:
|
||||
CIBW_BUILD: cp27-win32
|
||||
DISTUTILS_USE_SDK: 1
|
||||
MSSdk: 1
|
||||
```
|
||||
|
||||
@@ -33,4 +33,4 @@ Obviously, manual steps are for chumps, so we can automate this a little by usin
|
||||
|
||||
If you don't need much control over the release of a package, you can set up cibuildwheel to deliver the wheels straight to PyPI. This doesn't require anycloud storage to work - you just need to bump the version and tag it.
|
||||
|
||||
Check out [this example repo](https://github.com/joerick/cibuildwheel-autopypi-example) for instructions on how to set this up.
|
||||
[`examples/travis-ci-deploy.yml`](https://github.com/joerick/cibuildwheel/blob/master/examples/travis-ci-deploy.yml) and [`examples/github-deploy.yml`](https://github.com/joerick/cibuildwheel/blob/master/examples/github-deploy.yml) are example configurations that automatocially upload wheels to PyPI. Also check out [this example repo](https://github.com/joerick/cibuildwheel-autopypi-example) for more detailed instructions on how to set this up.
|
||||
|
||||
@@ -2,4 +2,3 @@
|
||||
$('.rst-content pre')
|
||||
.prev('blockquote')
|
||||
.addClass('code-block-filename');
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import cgi
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
import mkdocs
|
||||
|
||||
@@ -41,13 +40,12 @@ class ImportMarkdownPlugin(mkdocs.plugins.BasePlugin):
|
||||
def found_include_tag(match):
|
||||
filename = match.group('filename')
|
||||
|
||||
file_path_abs = os.path.join(os.path.dirname(page_src_path), filename)
|
||||
file_path_abs = Path(page_src_path).parent / filename
|
||||
|
||||
if not os.path.exists(file_path_abs):
|
||||
if not file_path_abs.exists():
|
||||
raise ValueError('file not found', filename)
|
||||
|
||||
with io.open(file_path_abs, encoding='utf8') as f:
|
||||
text_to_include = f.read()
|
||||
text_to_include = file_path_abs.read_text(encoding='utf8')
|
||||
|
||||
# Allow good practice of having a final newline in the file
|
||||
if text_to_include.endswith('\n'):
|
||||
@@ -60,13 +58,12 @@ class ImportMarkdownPlugin(mkdocs.plugins.BasePlugin):
|
||||
start = match.group('start')
|
||||
end = match.group('end')
|
||||
|
||||
file_path_abs = os.path.join(os.path.dirname(page_src_path), filename)
|
||||
file_path_abs = Path(page_src_path).parent / filename
|
||||
|
||||
if not os.path.exists(file_path_abs):
|
||||
if not file_path_abs.exists():
|
||||
raise ValueError('file not found', filename)
|
||||
|
||||
with io.open(file_path_abs, encoding='utf8') as f:
|
||||
text_to_include = f.read()
|
||||
text_to_include = file_path_abs.read_text(encoding='utf8')
|
||||
|
||||
if start:
|
||||
_, _, text_to_include = text_to_include.partition(start)
|
||||
|
||||
+54
-30
@@ -52,7 +52,6 @@ env:
|
||||
|
||||
## Build selection
|
||||
|
||||
|
||||
### `CIBW_PLATFORM` {: #platform}
|
||||
|
||||
> Override the auto-detected target platform
|
||||
@@ -67,6 +66,7 @@ For `linux` you need Docker running, on macOS or Linux. For `macos`, you need a
|
||||
|
||||
This option can also be set using the command-line option `--platform`.
|
||||
|
||||
|
||||
### `CIBW_BUILD`, `CIBW_SKIP` {: #build-skip}
|
||||
|
||||
> Choose the Python versions to build
|
||||
@@ -155,7 +155,6 @@ CIBW_SKIP: pp*
|
||||
|
||||
## Build customization
|
||||
|
||||
|
||||
### `CIBW_ENVIRONMENT` {: #environment}
|
||||
> Set environment variables needed during the build
|
||||
|
||||
@@ -191,6 +190,28 @@ CIBW_ENVIRONMENT: "BUILD_TIME=$(date) SAMPLE_TEXT=\"sample text\""
|
||||
!!! note
|
||||
`cibuildwheel` always defines the environment variable `CIBUILDWHEEL=1`. This can be useful for [building wheels with optional extensions](faq.md#building-packages-with-optional-c-extensions).
|
||||
|
||||
### `CIBW_BEFORE_ALL` {: #before-all}
|
||||
> Execute a shell command on the build system before any wheels are built.
|
||||
|
||||
Shell command to prepare a common part of the project (e.g. build or install libraries which does not depend on the specific version of Python).
|
||||
|
||||
This option is very useful for the Linux build, where builds take place in isolated Docker containers managed by cibuildwheel. This command will run inside the container before the wheel builds start. Note, if you're building both x86_64 and i686 wheels (the default), your build uses two different Docker images. In that case, this command will execute twice - once per build container.
|
||||
|
||||
The placeholder `{package}` can be used here; it will be replaced by the path to the package being built by `cibuildwheel`.
|
||||
|
||||
On Windows and macOS, the version of Python available inside `CIBW_BEFORE_ALL` is whatever is available on the host machine. On Linux, a modern Python version is available on PATH.
|
||||
|
||||
Platform-specific variants also available:<br/>
|
||||
`CIBW_BEFORE_ALL_MACOS` | `CIBW_BEFORE_ALL_WINDOWS` | `CIBW_BEFORE_ALL_LINUX`
|
||||
|
||||
#### Examples
|
||||
```yaml
|
||||
# build third party library
|
||||
CIBW_BEFORE_ALL: make -C third_party_lib
|
||||
|
||||
# install system library
|
||||
CIBW_BEFORE_ALL_LINUX: yum install -y libffi-dev
|
||||
```
|
||||
|
||||
### `CIBW_BEFORE_BUILD` {: #before-build}
|
||||
> Execute a shell command preparing each wheel's build
|
||||
@@ -296,6 +317,7 @@ CIBW_MANYLINUX_X86_64_IMAGE: dockcross/manylinux-x64
|
||||
CIBW_MANYLINUX_I686_IMAGE: dockcross/manylinux-x86
|
||||
```
|
||||
|
||||
|
||||
### `CIBW_DEPENDENCY_VERSIONS` {: #dependency-versions}
|
||||
> Specify how cibuildwheel controls the versions of the tools it uses
|
||||
|
||||
@@ -372,6 +394,35 @@ CIBW_TEST_COMMAND: pytest {package}/tests
|
||||
```
|
||||
|
||||
|
||||
### `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. The placeholder `{package}` can be used here; it will be replaced by the path to the package being built 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
|
||||
```
|
||||
|
||||
|
||||
### `CIBW_TEST_REQUIRES` {: #test-requires}
|
||||
> Install Python dependencies before running the tests
|
||||
|
||||
@@ -411,33 +462,6 @@ 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. The placeholder `{package}` can be used here; it will be replaced by the path to the package being built 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
|
||||
|
||||
@@ -601,7 +625,7 @@ optional arguments:
|
||||
var url = 'https://cibuildwheel.readthedocs.io/en/stable/options/#'+option.id;
|
||||
var namesMarkdown = $.map(optionNames, function(n) {
|
||||
return '[`'+n+'`]('+url+') '
|
||||
}).join(' ')
|
||||
}).join(' <br> ')
|
||||
|
||||
markdown += '| '+namesMarkdown+' '
|
||||
markdown += '| '+option.description.trim()+' '
|
||||
|
||||
@@ -15,6 +15,8 @@ Commit this file, and push to Github - either to your default branch, or to a PR
|
||||
|
||||
For more info on this file, check out the [docs](https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions).
|
||||
|
||||
[`examples/github-deploy.yml`](https://github.com/joerick/cibuildwheel/blob/master/examples/github-deploy.yml) extends this minimal example with a demonstration of how to automatically upload the built wheels to PyPI.
|
||||
|
||||
# Azure Pipelines [linux/mac/windows] {: #azure-pipelines}
|
||||
|
||||
To build Linux, Mac, and Windows wheels on Azure Pipelines, create a `azure-pipelines.yml` file in your repo.
|
||||
@@ -46,6 +48,8 @@ Commit this file, enable building of your repo on Travis CI, and push.
|
||||
|
||||
Then setup a deployment method by following the [Travis CI deployment docs](https://docs.travis-ci.com/user/deployment/), or see [Delivering to PyPI](deliver-to-pypi.md). For more info on `.travis.yml`, check out the [docs](https://docs.travis-ci.com/).
|
||||
|
||||
[`examples/travis-deploy.yml`](https://github.com/joerick/cibuildwheel/blob/master/examples/travis-deploy.yml) extends this minimal example with a demonstration of how to automatically upload the built wheels to PyPI.
|
||||
|
||||
# CircleCI [linux/mac] {: #circleci}
|
||||
|
||||
To build Linux and Mac wheels on CircleCI, create a `.circleci/config.yml` file in your repo,
|
||||
|
||||
@@ -12,7 +12,7 @@ stack: python 3.7
|
||||
init:
|
||||
- cmd: set PATH=C:\Python37;C:\Python37\Scripts;%PATH%
|
||||
|
||||
install: python -m pip install cibuildwheel==1.4.1
|
||||
install: python -m pip install cibuildwheel==1.5.2
|
||||
|
||||
build_script: python -m cibuildwheel --output-dir wheelhouse
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ jobs:
|
||||
- task: UsePythonVersion@0
|
||||
- bash: |
|
||||
python3 -m pip install --upgrade pip
|
||||
pip3 install cibuildwheel==1.4.1
|
||||
pip3 install cibuildwheel==1.5.2
|
||||
cibuildwheel --output-dir wheelhouse .
|
||||
- task: PublishBuildArtifacts@1
|
||||
inputs: {pathtoPublish: 'wheelhouse'}
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
- task: UsePythonVersion@0
|
||||
- bash: |
|
||||
python3 -m pip install --upgrade pip
|
||||
pip3 install cibuildwheel==1.4.1
|
||||
pip3 install cibuildwheel==1.5.2
|
||||
cibuildwheel --output-dir wheelhouse .
|
||||
- task: PublishBuildArtifacts@1
|
||||
inputs: {pathtoPublish: 'wheelhouse'}
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
displayName: Install Visual C++ for Python 2.7
|
||||
- bash: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install cibuildwheel==1.4.1
|
||||
pip install cibuildwheel==1.5.2
|
||||
cibuildwheel --output-dir wheelhouse .
|
||||
- task: PublishBuildArtifacts@1
|
||||
inputs: {pathtoPublish: 'wheelhouse'}
|
||||
|
||||
@@ -11,7 +11,7 @@ jobs:
|
||||
- run:
|
||||
name: Build the Linux wheels.
|
||||
command: |
|
||||
pip3 install --user cibuildwheel==1.4.1
|
||||
pip3 install --user cibuildwheel==1.5.2
|
||||
cibuildwheel --output-dir wheelhouse
|
||||
- store_artifacts:
|
||||
path: wheelhouse/
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
- run:
|
||||
name: Build the OS X wheels.
|
||||
command: |
|
||||
pip3 install --user cibuildwheel==1.4.1
|
||||
pip3 install --user cibuildwheel==1.5.2
|
||||
cibuildwheel --output-dir wheelhouse
|
||||
- store_artifacts:
|
||||
path: wheelhouse/
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
name: Build and upload to PyPI
|
||||
|
||||
# Build on every branch push, tag push, and pull request change:
|
||||
on: [push, pull_request]
|
||||
# Alternatively, to publish when a (published) GitHub Release is created, use the following:
|
||||
# on:
|
||||
# push:
|
||||
# pull_request:
|
||||
# release:
|
||||
# types:
|
||||
# - published
|
||||
|
||||
jobs:
|
||||
build_wheels:
|
||||
name: Build wheels on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-18.04, windows-latest, macos-latest]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-python@v2
|
||||
name: Install Python
|
||||
with:
|
||||
python-version: '3.7'
|
||||
|
||||
- name: Install cibuildwheel
|
||||
run: |
|
||||
python -m pip install cibuildwheel==1.5.2
|
||||
|
||||
- name: Install Visual C++ for Python 2.7
|
||||
if: runner.os == 'Windows'
|
||||
run: |
|
||||
choco install vcpython27 -f -y
|
||||
|
||||
- name: Build wheels
|
||||
run: |
|
||||
python -m cibuildwheel --output-dir wheelhouse
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
path: ./wheelhouse/*.whl
|
||||
|
||||
build_sdist:
|
||||
name: Build source distribution
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-python@v2
|
||||
name: Install Python
|
||||
with:
|
||||
python-version: '3.7'
|
||||
|
||||
- name: Build sdist
|
||||
run: python setup.py sdist
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
path: dist/*.tar.gz
|
||||
|
||||
upload_pypi:
|
||||
needs: [build_wheels, build_sdist]
|
||||
runs-on: ubuntu-latest
|
||||
# upload to PyPI on every tag starting with 'v'
|
||||
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/v')
|
||||
# alternatively, to publish when a GitHub Release is created, use the following rule:
|
||||
# if: github.event_name == 'release' && github.event.action == 'published'
|
||||
steps:
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: artifact
|
||||
path: dist
|
||||
|
||||
- uses: pypa/gh-action-pypi-publish@master
|
||||
with:
|
||||
user: __token__
|
||||
password: ${{ secrets.pypi_password }}
|
||||
# To test: repository_url: https://test.pypi.org/legacy/
|
||||
+19
-20
@@ -4,34 +4,33 @@ on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build_wheels:
|
||||
name: Build wheel on ${{ matrix.os }}
|
||||
name: Build wheels on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-18.04, windows-latest, macos-latest]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-python@v1
|
||||
name: Install Python
|
||||
with:
|
||||
python-version: '3.7'
|
||||
- uses: actions/setup-python@v2
|
||||
name: Install Python
|
||||
with:
|
||||
python-version: '3.7'
|
||||
|
||||
- name: Install cibuildwheel
|
||||
run: |
|
||||
python -m pip install cibuildwheel==1.4.1
|
||||
- name: Install cibuildwheel
|
||||
run: |
|
||||
python -m pip install cibuildwheel==1.5.2
|
||||
|
||||
- name: Install Visual C++ for Python 2.7
|
||||
if: startsWith(matrix.os, 'windows')
|
||||
run: |
|
||||
choco install vcpython27 -f -y
|
||||
- name: Install Visual C++ for Python 2.7
|
||||
if: runner.os == 'Windows'
|
||||
run: |
|
||||
choco install vcpython27 -f -y
|
||||
|
||||
- name: Build wheel
|
||||
run: |
|
||||
python -m cibuildwheel --output-dir wheelhouse
|
||||
- name: Build wheels
|
||||
run: |
|
||||
python -m cibuildwheel --output-dir wheelhouse
|
||||
|
||||
- uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: wheels
|
||||
path: ./wheelhouse
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
path: ./wheelhouse/*.whl
|
||||
|
||||
@@ -25,7 +25,7 @@ env:
|
||||
# Note: TWINE_PASSWORD is set to a PyPI API token in Travis settings
|
||||
|
||||
install:
|
||||
- python3 -m pip install cibuildwheel==1.4.1
|
||||
- python3 -m pip install cibuildwheel==1.5.2
|
||||
|
||||
script:
|
||||
# build the wheels, put them into './wheelhouse'
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
- ln -s /c/Python38/python.exe /c/Python38/python3.exe
|
||||
|
||||
install:
|
||||
- python3 -m pip install cibuildwheel==1.4.1
|
||||
- python3 -m pip install cibuildwheel==1.5.2
|
||||
|
||||
script:
|
||||
# build the wheels, put them into './wheelhouse'
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
language: python
|
||||
python:
|
||||
- 3.5
|
||||
- 3.6
|
||||
- 3.7
|
||||
- 3.8
|
||||
@@ -56,7 +55,7 @@ jobs:
|
||||
- stage: deploy
|
||||
name: Build and deploy Linux wheels
|
||||
services: docker
|
||||
install: python3 -m pip install cibuildwheel==1.4.1
|
||||
install: python3 -m pip install cibuildwheel==1.5.2
|
||||
script: python3 -m cibuildwheel --output-dir wheelhouse
|
||||
after_success: |
|
||||
python3 -m pip install twine
|
||||
@@ -66,7 +65,7 @@ jobs:
|
||||
name: Build and deploy macOS wheels
|
||||
os: osx
|
||||
language: shell
|
||||
install: python3 -m pip install cibuildwheel==1.4.1
|
||||
install: python3 -m pip install cibuildwheel==1.5.2
|
||||
script: python3 -m cibuildwheel --output-dir wheelhouse
|
||||
after_success: |
|
||||
python3 -m pip install twine
|
||||
@@ -76,7 +75,7 @@ jobs:
|
||||
name: Build and deploy Windows wheels
|
||||
os: windows
|
||||
language: shell
|
||||
install: python3 -m pip install cibuildwheel==1.4.1
|
||||
install: python3 -m pip install cibuildwheel==1.5.2
|
||||
script: python3 -m cibuildwheel --output-dir wheelhouse
|
||||
after_success: |
|
||||
python3 -m pip install twine
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[flake8]
|
||||
ignore = E501,W503
|
||||
ignore = E501,W503,E741
|
||||
application-import-names = cibuildwheel
|
||||
exclude =
|
||||
cibuildwheel/resources/,
|
||||
@@ -11,3 +11,30 @@ exclude =
|
||||
env??/,
|
||||
.venv/,
|
||||
site/
|
||||
|
||||
[tool:pytest]
|
||||
junit_family=xunit2
|
||||
|
||||
[mypy]
|
||||
python_version = 3.6
|
||||
files = cibuildwheel,test
|
||||
|
||||
warn_unused_configs = True
|
||||
warn_redundant_casts = True
|
||||
|
||||
[mypy-cibuildwheel]
|
||||
disallow_any_generics = True
|
||||
disallow_subclassing_any = True
|
||||
disallow_untyped_calls = True
|
||||
disallow_untyped_defs = True
|
||||
disallow_incomplete_defs = True
|
||||
check_untyped_defs = True
|
||||
disallow_untyped_decorators = True
|
||||
no_implicit_optional = True
|
||||
warn_unused_ignores = True
|
||||
warn_return_any = True
|
||||
no_implicit_reexport = True
|
||||
strict_equality = True
|
||||
|
||||
[mypy-pytest,setuptools]
|
||||
ignore_missing_imports = True
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import io
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
from setuptools import setup
|
||||
except ImportError:
|
||||
from distutils.core import setup
|
||||
|
||||
this_directory = os.path.dirname(__file__)
|
||||
with io.open(os.path.join(this_directory, 'README.md'), encoding='utf-8') as f:
|
||||
long_description = f.read()
|
||||
this_directory = Path(__file__).parent
|
||||
long_description = (this_directory / 'README.md').read_text(encoding='utf-8')
|
||||
|
||||
setup(
|
||||
name='cibuildwheel',
|
||||
version='1.4.1',
|
||||
install_requires=['bashlex!=0.13'],
|
||||
version='1.5.2',
|
||||
install_requires=['bashlex!=0.13', 'toml'],
|
||||
description="Build Python wheels on CI with minimal configuration.",
|
||||
long_description=long_description,
|
||||
long_description_content_type='text/markdown',
|
||||
@@ -28,7 +26,7 @@ setup(
|
||||
'cibuildwheel': ['resources/*'],
|
||||
},
|
||||
# Supported python versions
|
||||
python_requires='>=3.5',
|
||||
python_requires='>=3.6',
|
||||
keywords='ci wheel packaging pypi travis appveyor macos linux windows',
|
||||
classifiers=[
|
||||
'Intended Audience :: Developers',
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import os
|
||||
|
||||
from setuptools import (
|
||||
Extension,
|
||||
setup,
|
||||
)
|
||||
|
||||
if os.environ.get('CIBUILDWHEEL', '0') != '1':
|
||||
raise Exception('CIBUILDWHEEL environment variable is not set to 1')
|
||||
|
||||
setup(
|
||||
name="spam",
|
||||
ext_modules=[Extension('spam', sources=['spam.c'])],
|
||||
version="0.1.0",
|
||||
)
|
||||
@@ -1,48 +0,0 @@
|
||||
#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)
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
import pytest
|
||||
|
||||
import utils
|
||||
|
||||
|
||||
def test():
|
||||
project_dir = os.path.dirname(__file__)
|
||||
|
||||
# build and test the wheels
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
||||
'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': 'COLOR 00 || 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)
|
||||
|
||||
|
||||
def test_extras_require():
|
||||
project_dir = os.path.dirname(__file__)
|
||||
|
||||
# build and test the wheels
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
||||
'CIBW_TEST_EXTRAS': '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': 'COLOR 00 || 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)
|
||||
|
||||
|
||||
def test_failing_test(tmp_path):
|
||||
'''Ensure a failing test causes cibuildwheel to error out and exit'''
|
||||
project_dir = os.path.dirname(__file__)
|
||||
|
||||
with pytest.raises(subprocess.CalledProcessError):
|
||||
utils.cibuildwheel_run(project_dir, output_dir=tmp_path, add_env={
|
||||
'CIBW_TEST_COMMAND': 'false',
|
||||
# manylinux1 has a version of bash that's been shown to have
|
||||
# problems with this, so let's check that.
|
||||
'CIBW_MANYLINUX_I686_IMAGE': 'manylinux1',
|
||||
'CIBW_MANYLINUX_X86_64_IMAGE': 'manylinux1',
|
||||
})
|
||||
assert len(os.listdir(str(tmp_path))) == 0
|
||||
@@ -1,11 +0,0 @@
|
||||
from setuptools import (
|
||||
Extension,
|
||||
setup,
|
||||
)
|
||||
|
||||
setup(
|
||||
name="spam",
|
||||
ext_modules=[Extension('spam', sources=['spam.c'])],
|
||||
extras_require={'test': ['nose']},
|
||||
version="0.1.0",
|
||||
)
|
||||
@@ -1,48 +0,0 @@
|
||||
#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)
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
from __future__ import print_function
|
||||
import os
|
||||
import platform
|
||||
import sys
|
||||
import struct
|
||||
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__))
|
||||
|
||||
def test_uname(self):
|
||||
if platform.system() == "Windows":
|
||||
return
|
||||
# if we're running in 32-bit Python, check that the machine is i686.
|
||||
# See #336 for more info.
|
||||
bits = struct.calcsize("P") * 8
|
||||
if bits == 32:
|
||||
self.assertEqual(platform.machine(), "i686")
|
||||
@@ -1,19 +0,0 @@
|
||||
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_BUILD': '''python -c "import sys; open('/tmp/pythonversion.txt', 'w').write(sys.version)" && python -c "import sys; open('/tmp/pythonexecutable.txt', 'w').write(sys.executable)"''',
|
||||
'CIBW_BEFORE_BUILD_WINDOWS': '''python -c "import sys; open('c:\\pythonversion.txt', 'w').write(sys.version)" && python -c "import sys; open('c:\\pythonexecutable.txt', 'w').write(sys.executable)"''',
|
||||
})
|
||||
|
||||
# also check that we got the right wheels
|
||||
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
@@ -1,31 +0,0 @@
|
||||
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.
|
||||
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
|
||||
|
||||
# check that the executable also was written
|
||||
executable_file = 'c:\\pythonexecutable.txt' if sys.platform == 'win32' else '/tmp/pythonexecutable.txt'
|
||||
with open(executable_file) as f:
|
||||
stored_executable = f.read()
|
||||
print('stored_executable', stored_executable)
|
||||
print('sys.executable', sys.executable)
|
||||
# windows/mac are case insensitive
|
||||
assert os.path.realpath(stored_executable).lower() == os.path.realpath(sys.executable).lower()
|
||||
|
||||
setup(
|
||||
name="spam",
|
||||
ext_modules=[Extension('spam', sources=['spam.c'])],
|
||||
version="0.1.0",
|
||||
)
|
||||
@@ -1,48 +0,0 @@
|
||||
#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)
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
import os
|
||||
|
||||
import utils
|
||||
|
||||
|
||||
def test():
|
||||
project_dir = os.path.dirname(__file__)
|
||||
|
||||
# build the wheels
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
||||
'CIBW_BUILD': 'cp3?-*',
|
||||
'CIBW_SKIP': 'cp37-*',
|
||||
})
|
||||
|
||||
# check that we got the right wheels. There should be no 2.7 or 3.7.
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if ('-cp3' in w) and ('-cp37' not in w)]
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
@@ -1,18 +0,0 @@
|
||||
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')
|
||||
if sys.version_info[0:2] == (3, 4):
|
||||
raise Exception('Python 3.4 should be skipped')
|
||||
|
||||
setup(
|
||||
name="spam",
|
||||
ext_modules=[Extension('spam', sources=['spam.c'])],
|
||||
version="0.1.0",
|
||||
)
|
||||
@@ -1,48 +0,0 @@
|
||||
#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)
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
import os
|
||||
import pytest
|
||||
import subprocess
|
||||
import utils
|
||||
|
||||
|
||||
def test():
|
||||
project_dir = os.path.dirname(__file__)
|
||||
|
||||
# write some information into the CIBW_ENVIRONMENT, for expansion and
|
||||
# insertion into the environment by cibuildwheel. This is checked
|
||||
# in setup.py
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
||||
'CIBW_ENVIRONMENT': '''CIBW_TEST_VAR="a b c" CIBW_TEST_VAR_2=1 CIBW_TEST_VAR_3="$(echo 'test string 3')" PATH=$PATH:/opt/cibw_test_path''',
|
||||
'CIBW_ENVIRONMENT_WINDOWS': '''CIBW_TEST_VAR="a b c" CIBW_TEST_VAR_2=1 CIBW_TEST_VAR_3="$(echo 'test string 3')" PATH="$PATH;/opt/cibw_test_path"''',
|
||||
})
|
||||
|
||||
# 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,26 +0,0 @@
|
||||
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')
|
||||
CIBW_TEST_VAR_2 = os.environ.get('CIBW_TEST_VAR_2')
|
||||
PATH = os.environ.get('PATH')
|
||||
|
||||
if CIBW_TEST_VAR != 'a b c':
|
||||
raise Exception('CIBW_TEST_VAR should equal "a b c". It was "%s"' % CIBW_TEST_VAR)
|
||||
if CIBW_TEST_VAR_2 != '1':
|
||||
raise Exception('CIBW_TEST_VAR_2 should equal "1". It was "%s"' % CIBW_TEST_VAR_2)
|
||||
if '/opt/cibw_test_path' not in PATH:
|
||||
raise Exception('PATH should contain "/opt/cibw_test_path". It was "%s"' % 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'])],
|
||||
version="0.1.0",
|
||||
)
|
||||
@@ -1,48 +0,0 @@
|
||||
#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)
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
from setuptools import (
|
||||
Extension,
|
||||
setup,
|
||||
)
|
||||
|
||||
# check that we're running in the correct docker image as specified in the
|
||||
# environment options CIBW_MANYLINUX1_*_IMAGE
|
||||
if 'linux' in sys.platform and not os.path.exists('/dockcross'):
|
||||
raise Exception('/dockcross directory not found. Is this test running in the correct docker image?')
|
||||
|
||||
setup(
|
||||
name="spam",
|
||||
ext_modules=[Extension('spam', sources=['spam.c'])],
|
||||
version="0.1.0",
|
||||
)
|
||||
@@ -1,48 +0,0 @@
|
||||
#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)
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import os
|
||||
|
||||
import utils
|
||||
|
||||
|
||||
def test():
|
||||
project_dir = os.path.dirname(__file__)
|
||||
# this test checks that SSL is working in the build environment using
|
||||
# some checks in setup.py.
|
||||
|
||||
utils.cibuildwheel_run(project_dir)
|
||||
@@ -1,24 +0,0 @@
|
||||
import ssl
|
||||
import sys
|
||||
|
||||
from setuptools import (
|
||||
Extension,
|
||||
setup,
|
||||
)
|
||||
|
||||
if sys.version_info[0] == 2:
|
||||
from urllib2 import urlopen
|
||||
else:
|
||||
from urllib.request import urlopen
|
||||
|
||||
if sys.version_info[0:2] == (3, 3):
|
||||
data = urlopen('https://www.nist.gov')
|
||||
else:
|
||||
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
|
||||
data = urlopen('https://www.nist.gov', context=context)
|
||||
|
||||
setup(
|
||||
name="spam",
|
||||
ext_modules=[Extension('spam', sources=['spam.c'])],
|
||||
version="0.1.0",
|
||||
)
|
||||
@@ -1,48 +0,0 @@
|
||||
#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)
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
from setuptools import (
|
||||
Extension,
|
||||
setup,
|
||||
)
|
||||
|
||||
setup(
|
||||
name="spam",
|
||||
ext_modules=[Extension('spam', sources=['spam.c'])],
|
||||
version="0.1.0",
|
||||
)
|
||||
@@ -1,68 +0,0 @@
|
||||
#include <Python.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#if !defined(__GLIBC_PREREQ)
|
||||
#error "Must run on a glibc linux environment"
|
||||
#endif
|
||||
|
||||
#if !__GLIBC_PREREQ(2, 5) /* manylinux1 is glibc 2.5 */
|
||||
#error "Must run on a glibc >= 2.5 linux environment"
|
||||
#endif
|
||||
|
||||
static PyObject *
|
||||
spam_system(PyObject *self, PyObject *args)
|
||||
{
|
||||
const char *command;
|
||||
int sts = 0;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s", &command))
|
||||
return NULL;
|
||||
|
||||
#if defined(__GLIBC_PREREQ) && __GLIBC_PREREQ(2, 17) /* manylinux2014 is glibc 2.17 */
|
||||
// secure_getenv is only available in manylinux2014, ensuring
|
||||
// that only a manylinux2014 wheel is produced
|
||||
sts = (int)secure_getenv("NON_EXISTING_ENV_VARIABLE");
|
||||
#elif defined(__GLIBC_PREREQ) && __GLIBC_PREREQ(2, 10) /* manylinux2010 is glibc 2.12 */
|
||||
// malloc_info is only available on manylinux2010+
|
||||
sts = malloc_info(0, stdout);
|
||||
#endif
|
||||
if (sts == 0) {
|
||||
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)
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import os
|
||||
|
||||
import utils
|
||||
|
||||
project_dir = os.path.dirname(__file__)
|
||||
|
||||
|
||||
def test():
|
||||
# build the wheels
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir)
|
||||
|
||||
# check that the expected wheels are produced
|
||||
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
@@ -1,6 +0,0 @@
|
||||
[metadata]
|
||||
name = spam
|
||||
version = attr: spam.__version__
|
||||
|
||||
[options]
|
||||
packages = find:
|
||||
@@ -1,8 +0,0 @@
|
||||
from setuptools import (
|
||||
Extension,
|
||||
setup,
|
||||
)
|
||||
|
||||
setup(
|
||||
ext_modules=[Extension('spam.spam', sources=['spam/spam.c'])],
|
||||
)
|
||||
@@ -1 +0,0 @@
|
||||
__version__ = "0.1.0"
|
||||
@@ -1,48 +0,0 @@
|
||||
#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)
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
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
|
||||
|
||||
# VC++ for Python 2.7 does not support modern standards
|
||||
add_env = {'CIBW_SKIP': 'cp27-win* pp27-win32', 'CIBW_ENVIRONMENT': 'STANDARD=11'}
|
||||
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
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
|
||||
|
||||
# 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
|
||||
add_env = {'CIBW_SKIP': 'cp27-win* pp27-win32 cp35-win*', 'CIBW_ENVIRONMENT': 'STANDARD=14'}
|
||||
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
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)
|
||||
@@ -1,25 +0,0 @@
|
||||
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",
|
||||
)
|
||||
@@ -1,62 +0,0 @@
|
||||
#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)
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
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)
|
||||
@@ -1,8 +0,0 @@
|
||||
from setuptools import setup, Extension
|
||||
|
||||
|
||||
setup(
|
||||
name="spam",
|
||||
ext_modules=[Extension('spam', sources=['spam.c'])],
|
||||
version="0.1.0",
|
||||
)
|
||||
@@ -1,48 +0,0 @@
|
||||
#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)
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
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)
|
||||
@@ -1,31 +0,0 @@
|
||||
import os
|
||||
import subprocess
|
||||
from setuptools import (
|
||||
Extension,
|
||||
setup,
|
||||
)
|
||||
|
||||
versions_output_text = subprocess.check_output(
|
||||
['pip', 'freeze', '--all', '-qq'],
|
||||
universal_newlines=True,
|
||||
)
|
||||
versions = versions_output_text.strip().splitlines()
|
||||
|
||||
# `versions` now looks like:
|
||||
# ['pip==x.x.x', 'setuptools==x.x.x', 'wheel==x.x.x']
|
||||
|
||||
print('Gathered versions', versions)
|
||||
|
||||
for package_name in ['pip', 'setuptools', 'wheel']:
|
||||
env_name = 'EXPECTED_{}_VERSION'.format(package_name.upper())
|
||||
expected_version = os.environ[env_name]
|
||||
|
||||
assert '{}=={}'.format(package_name, expected_version) in versions, (
|
||||
'error: {} version should equal {}'.format(package_name, expected_version)
|
||||
)
|
||||
|
||||
setup(
|
||||
name="spam",
|
||||
ext_modules=[Extension('spam', sources=['spam.c'])],
|
||||
version="0.1.0",
|
||||
)
|
||||
@@ -1,48 +0,0 @@
|
||||
#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)
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
print('before_build.py executed!')
|
||||
@@ -1,25 +0,0 @@
|
||||
import os
|
||||
import utils
|
||||
|
||||
project_dir = os.path.dirname(__file__)
|
||||
|
||||
|
||||
def test(capfd):
|
||||
package_dir = os.path.join(project_dir, 'src', 'spam')
|
||||
# build the wheels
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, package_dir=package_dir, add_env={
|
||||
'CIBW_BEFORE_BUILD': 'python {project}/bin/before_build.py',
|
||||
'CIBW_TEST_COMMAND': 'python {package}/test/run_tests.py',
|
||||
# this shouldn't depend on the version of python, so build only
|
||||
# CPython 3.6
|
||||
'CIBW_BUILD': 'cp36-*',
|
||||
})
|
||||
|
||||
# check that the expected wheels are produced
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if 'cp36' in w]
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
|
||||
captured = capfd.readouterr()
|
||||
assert "before_build.py executed!" in captured.out
|
||||
assert "run_tests.py executed!" in captured.out
|
||||
@@ -1,8 +0,0 @@
|
||||
from setuptools import Extension, setup
|
||||
|
||||
|
||||
setup(
|
||||
name="spam",
|
||||
ext_modules=[Extension('spam', sources=['spam.c'])],
|
||||
version="0.1.0",
|
||||
)
|
||||
@@ -1,48 +0,0 @@
|
||||
#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)
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
print('run_tests.py executed!')
|
||||
+1
-3
@@ -1,3 +1 @@
|
||||
This folder contains repos that are built by cibuildwheel to test cibuildwheel. Confusing, I know.
|
||||
|
||||
Basically, if cibuildwheel can build these projects, we're good.
|
||||
This folder contains integration tests for cibuildwheel.
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), 'shared'))
|
||||
@@ -1,12 +1,22 @@
|
||||
import os
|
||||
import textwrap
|
||||
import platform
|
||||
from . import test_projects
|
||||
from . import utils
|
||||
|
||||
import utils
|
||||
basic_project = test_projects.new_c_project(
|
||||
setup_py_add=textwrap.dedent('''
|
||||
import os
|
||||
|
||||
project_dir = os.path.dirname(__file__)
|
||||
if os.environ.get("CIBUILDWHEEL", "0") != "1":
|
||||
raise Exception("CIBUILDWHEEL environment variable is not set to 1")
|
||||
''')
|
||||
)
|
||||
|
||||
|
||||
def test():
|
||||
def test(tmp_path):
|
||||
project_dir = tmp_path / 'project'
|
||||
basic_project.generate(project_dir)
|
||||
|
||||
# build the wheels
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir)
|
||||
|
||||
@@ -15,7 +25,10 @@ def test():
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
|
||||
|
||||
def test_build_identifiers():
|
||||
def test_build_identifiers(tmp_path):
|
||||
project_dir = tmp_path / 'project'
|
||||
basic_project.generate(project_dir)
|
||||
|
||||
# check that the number of expected wheels matches the number of build
|
||||
# identifiers
|
||||
# after adding CIBW_MANYLINUX_IMAGE to support manylinux2010, there
|
||||
@@ -0,0 +1,38 @@
|
||||
import os
|
||||
import textwrap
|
||||
|
||||
from . import utils
|
||||
from . import test_projects
|
||||
|
||||
project_with_before_build_asserts = test_projects.new_c_project(
|
||||
setup_py_add=textwrap.dedent(r'''
|
||||
# assert that the Python version as written to text_info.txt in the CIBW_BEFORE_ALL step
|
||||
# is the same one as is currently running.
|
||||
with open("text_info.txt") as f:
|
||||
stored_text = f.read()
|
||||
|
||||
print("## stored text: " + stored_text)
|
||||
assert stored_text == "sample text 123"
|
||||
''')
|
||||
)
|
||||
|
||||
|
||||
def test(tmp_path):
|
||||
project_dir = tmp_path / 'project'
|
||||
project_with_before_build_asserts.generate(project_dir)
|
||||
|
||||
with open(os.path.join(project_dir, "text_info.txt"), mode='w') as ff:
|
||||
print("dummy text", file=ff)
|
||||
|
||||
# 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_ALL': '''python -c "import os;open('{project}/text_info.txt', 'w').write('sample text '+os.environ.get('TEST_VAL', ''))"''',
|
||||
'CIBW_ENVIRONMENT': "TEST_VAL='123'"
|
||||
})
|
||||
|
||||
# also check that we got the right wheels
|
||||
os.remove(os.path.join(project_dir, "text_info.txt"))
|
||||
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
@@ -0,0 +1,45 @@
|
||||
import textwrap
|
||||
|
||||
from . import utils
|
||||
from . import test_projects
|
||||
|
||||
project_with_before_build_asserts = test_projects.new_c_project(
|
||||
setup_py_add=textwrap.dedent(r'''
|
||||
import sys, os
|
||||
|
||||
# assert that the Python version as written to pythonversion.txt in the CIBW_BEFORE_BUILD step
|
||||
# is the same one as is currently running.
|
||||
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
|
||||
|
||||
# check that the executable also was written
|
||||
executable_file = 'c:\\pythonexecutable.txt' if sys.platform == 'win32' else '/tmp/pythonexecutable.txt'
|
||||
with open(executable_file) as f:
|
||||
stored_executable = f.read()
|
||||
print('stored_executable', stored_executable)
|
||||
print('sys.executable', sys.executable)
|
||||
# windows/mac are case insensitive
|
||||
assert os.path.realpath(stored_executable).lower() == os.path.realpath(sys.executable).lower()
|
||||
''')
|
||||
)
|
||||
|
||||
|
||||
def test(tmp_path):
|
||||
project_dir = tmp_path / 'project'
|
||||
project_with_before_build_asserts.generate(project_dir)
|
||||
|
||||
# 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_BUILD': '''python -c "import sys; open('/tmp/pythonversion.txt', 'w').write(sys.version)" && python -c "import sys; open('/tmp/pythonexecutable.txt', 'w').write(sys.executable)"''',
|
||||
'CIBW_BEFORE_BUILD_WINDOWS': '''python -c "import sys; open('c:\\pythonversion.txt', 'w').write(sys.version)" && python -c "import sys; open('c:\\pythonexecutable.txt', 'w').write(sys.executable)"''',
|
||||
})
|
||||
|
||||
# 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,56 @@
|
||||
from . import test_projects
|
||||
from . import utils
|
||||
|
||||
before_test_project = test_projects.new_c_project()
|
||||
before_test_project.files['test/spam_test.py'] = r'''
|
||||
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)
|
||||
'''
|
||||
|
||||
|
||||
def test(tmp_path):
|
||||
project_dir = tmp_path / 'project'
|
||||
before_test_project.generate(project_dir)
|
||||
|
||||
# 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,30 @@
|
||||
import textwrap
|
||||
|
||||
from . import utils
|
||||
from . import test_projects
|
||||
|
||||
project_with_skip_asserts = test_projects.new_c_project(
|
||||
setup_py_add=textwrap.dedent(r'''
|
||||
# explode if run on Python 2.7 or Python 3.7 (these should be skipped)
|
||||
if sys.version_info[0:2] == (2, 7):
|
||||
raise Exception("Python 2.7 should not be built")
|
||||
if sys.version_info[0:2] == (3, 7):
|
||||
raise Exception("Python 3.7 should be skipped")
|
||||
''')
|
||||
)
|
||||
|
||||
|
||||
def test(tmp_path):
|
||||
project_dir = tmp_path / 'project'
|
||||
project_with_skip_asserts.generate(project_dir)
|
||||
|
||||
# build the wheels
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
||||
'CIBW_BUILD': 'cp3?-*',
|
||||
'CIBW_SKIP': 'cp37-*',
|
||||
})
|
||||
|
||||
# check that we got the right wheels. There should be no 2.7 or 3.7.
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if ('-cp3' in w) and ('-cp37' not in w)]
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
@@ -0,0 +1,209 @@
|
||||
import os
|
||||
|
||||
import jinja2
|
||||
import pytest
|
||||
|
||||
from . import utils
|
||||
from .test_projects import TestProject
|
||||
|
||||
cpp_test_project = TestProject()
|
||||
|
||||
cpp_test_project.files['setup.py'] = jinja2.Template(r'''
|
||||
from setuptools import Extension, setup
|
||||
|
||||
setup(
|
||||
name="spam",
|
||||
ext_modules=[Extension('spam', sources=['spam.cpp'], language="c++", extra_compile_args={{ extra_compile_args }})],
|
||||
version="0.1.0",
|
||||
)
|
||||
''')
|
||||
|
||||
cpp_test_project.files['spam.cpp'] = jinja2.Template(r'''
|
||||
#include <Python.h>
|
||||
|
||||
{{ spam_cpp_top_level_add }}
|
||||
|
||||
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)
|
||||
}
|
||||
''')
|
||||
|
||||
|
||||
cpp11_project = cpp_test_project.copy()
|
||||
cpp11_project.template_context['extra_compile_args'] = (
|
||||
['/std:c++11'] if utils.platform == 'windows' else ['-std=c++11']
|
||||
)
|
||||
cpp11_project.template_context['spam_cpp_top_level_add'] = '#include <array>'
|
||||
|
||||
|
||||
def test_cpp11(tmp_path):
|
||||
# This test checks that the C++11 standard is supported
|
||||
project_dir = tmp_path / 'project'
|
||||
|
||||
cpp11_project.generate(project_dir)
|
||||
|
||||
# VC++ for Python 2.7 does not support modern standards
|
||||
add_env = {'CIBW_SKIP': 'cp27-win* pp27-win32'}
|
||||
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if 'cp27-cp27m-win' not in w and 'pp27-pypy_73-win32' not in w]
|
||||
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
|
||||
|
||||
cpp14_project = cpp_test_project.copy()
|
||||
cpp14_project.template_context['extra_compile_args'] = (
|
||||
['/std:c++14'] if utils.platform == 'windows' else ['-std=c++14']
|
||||
)
|
||||
cpp14_project.template_context['spam_cpp_top_level_add'] = "int a = 100'000;"
|
||||
|
||||
|
||||
def test_cpp14(tmp_path):
|
||||
# This test checks that the C++14 standard is supported
|
||||
project_dir = tmp_path / 'project'
|
||||
|
||||
cpp14_project.generate(project_dir)
|
||||
|
||||
# VC++ for Python 2.7 does not support modern standards
|
||||
# The manylinux1 docker image does not have a compiler which supports C++11
|
||||
add_env = {'CIBW_SKIP': 'cp27-win* pp27-win32'}
|
||||
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if 'cp27-cp27m-win' not in w
|
||||
and 'pp27-pypy_73-win32' not in w]
|
||||
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
|
||||
|
||||
cpp17_project = cpp_test_project.copy()
|
||||
|
||||
# Python and PyPy 2.7 headers use the `register` keyword, which is forbidden in
|
||||
# the C++17 standard, so we need the -Wno-register or /wd5033 options
|
||||
cpp17_project.template_context['extra_compile_args'] = (
|
||||
['/std:c++17', '/wd5033'] if utils.platform == 'windows' else ['-std=c++17', '-Wno-register']
|
||||
)
|
||||
cpp17_project.template_context['spam_cpp_top_level_add'] = r'''
|
||||
#include <utility>
|
||||
auto a = std::pair(5.0, false);
|
||||
'''
|
||||
|
||||
|
||||
def test_cpp17(tmp_path):
|
||||
# This test checks that the C++17 standard is supported
|
||||
project_dir = tmp_path / 'project'
|
||||
|
||||
cpp17_project.generate(project_dir)
|
||||
|
||||
if os.environ.get('APPVEYOR_BUILD_WORKER_IMAGE', '') == 'Visual Studio 2015':
|
||||
pytest.skip('Visual Studio 2015 does not support C++17')
|
||||
|
||||
# Pypy's distutils sets the default compiler to 'msvc9compiler', which
|
||||
# is too old to support cpp17.
|
||||
add_env = {'CIBW_SKIP': 'cp27-win* pp??-*'}
|
||||
|
||||
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 '-pp' not in w]
|
||||
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
|
||||
|
||||
def test_cpp17_py27_modern_msvc_workaround(tmp_path):
|
||||
# This test checks the workaround for building Python 2.7 wheel with MSVC 14
|
||||
|
||||
if utils.platform != 'windows':
|
||||
pytest.skip('the test is only relevant to the Windows build')
|
||||
|
||||
if os.environ.get('APPVEYOR_BUILD_WORKER_IMAGE', '') == 'Visual Studio 2015':
|
||||
pytest.skip('Visual Studio 2015 does not support C++17')
|
||||
|
||||
project_dir = tmp_path / 'project'
|
||||
cpp17_project.generate(project_dir)
|
||||
|
||||
# VC++ for Python 2.7 (i.e., MSVC 9) does not support modern standards
|
||||
# This is a workaround which forces distutils/setupstools to a newer version
|
||||
# Wheels compiled need a more modern C++ redistributable installed, which is not
|
||||
# included with Python: see documentation for more info
|
||||
# DISTUTILS_USE_SDK and MSSdk=1 tell distutils/setuptools that we are adding
|
||||
# MSVC's compiler, tools, and libraries to PATH ourselves
|
||||
add_env = {'DISTUTILS_USE_SDK': '1', 'MSSdk': '1'}
|
||||
|
||||
# Use existing setuptools code to run Visual Studio's vcvarsall.bat and get the
|
||||
# necessary environment variables, since running vcvarsall.bat in a subprocess
|
||||
# does not keep the relevant environment variables
|
||||
# There are different environment variables for 32-bit/64-bit targets, so we
|
||||
# need to run cibuildwheel twice, once for 32-bit with `vcvarsall.bat x86, and
|
||||
# once for 64-bit with `vcvarsall.bat x64`
|
||||
# In a normal CI setup, just run vcvarsall.bat before running cibuildwheel and set
|
||||
# DISTUTILS_USE_SDK and MSSdk
|
||||
import setuptools
|
||||
|
||||
def add_vcvars(prev_env, platform):
|
||||
vcvarsall_env = setuptools.msvc.msvc14_get_vc_env(platform)
|
||||
env = prev_env.copy()
|
||||
for vcvar in ['path', 'include', 'lib']:
|
||||
env[vcvar] = vcvarsall_env[vcvar]
|
||||
return env
|
||||
|
||||
add_env_x86 = add_vcvars(add_env, 'x86')
|
||||
add_env_x86['CIBW_BUILD'] = '?p27-win32'
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env_x86)
|
||||
|
||||
add_env_x64 = add_vcvars(add_env, 'x64')
|
||||
add_env_x64['CIBW_BUILD'] = 'cp27-win_amd64'
|
||||
actual_wheels += utils.cibuildwheel_run(project_dir, add_env=add_env_x64)
|
||||
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if 'cp27-cp27m-win' in w
|
||||
or 'pp27-pypy_73-win32' in w]
|
||||
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
+48
-14
@@ -1,32 +1,59 @@
|
||||
import os
|
||||
import re
|
||||
import pytest
|
||||
import textwrap
|
||||
import cibuildwheel.util
|
||||
|
||||
import utils
|
||||
from . import utils
|
||||
from . import test_projects
|
||||
|
||||
|
||||
project_with_expected_version_checks = test_projects.new_c_project(
|
||||
setup_py_add=textwrap.dedent(r'''
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
versions_output_text = subprocess.check_output(
|
||||
['pip', 'freeze', '--all', '-qq'],
|
||||
universal_newlines=True,
|
||||
)
|
||||
versions = versions_output_text.strip().splitlines()
|
||||
|
||||
# `versions` now looks like:
|
||||
# ['pip==x.x.x', 'setuptools==x.x.x', 'wheel==x.x.x']
|
||||
|
||||
print('Gathered versions', versions)
|
||||
|
||||
for package_name in ['pip', 'setuptools', 'wheel']:
|
||||
env_name = 'EXPECTED_{}_VERSION'.format(package_name.upper())
|
||||
expected_version = os.environ[env_name]
|
||||
|
||||
assert '{}=={}'.format(package_name, expected_version) in versions, (
|
||||
'error: {} version should equal {}'.format(package_name, expected_version)
|
||||
)
|
||||
''')
|
||||
)
|
||||
|
||||
|
||||
VERSION_REGEX = r'([\w-]+)==([^\s]+)'
|
||||
|
||||
|
||||
def get_versions_from_constraint_file(constraint_file):
|
||||
with open(constraint_file, encoding='utf8') as f:
|
||||
constraint_file_text = f.read()
|
||||
constraint_file_text = constraint_file.read_text(encoding='utf8')
|
||||
|
||||
versions = {}
|
||||
|
||||
for package, version in re.findall(VERSION_REGEX, constraint_file_text):
|
||||
versions[package] = version
|
||||
|
||||
return versions
|
||||
|
||||
|
||||
@pytest.mark.parametrize('python_version', ['2.7', '3.5', '3.8'])
|
||||
def test_pinned_versions(python_version):
|
||||
@pytest.mark.parametrize('python_version', ['2.7', '3.5', '3.6', '3.8'])
|
||||
def test_pinned_versions(tmp_path, python_version):
|
||||
if utils.platform == 'linux':
|
||||
pytest.skip('linux doesn\'t pin individual tool versions, it pins manylinux images instead')
|
||||
|
||||
project_dir = os.path.dirname(__file__)
|
||||
project_dir = tmp_path / 'project'
|
||||
project_with_expected_version_checks.generate(project_dir)
|
||||
|
||||
build_environment = {}
|
||||
|
||||
@@ -36,19 +63,22 @@ def test_pinned_versions(python_version):
|
||||
elif python_version == '3.5':
|
||||
constraint_filename = 'constraints-python35.txt'
|
||||
build_pattern = '[cp]p35-*'
|
||||
elif python_version == '3.6':
|
||||
constraint_filename = 'constraints-python36.txt'
|
||||
build_pattern = '[cp]p36-*'
|
||||
else:
|
||||
constraint_filename = 'constraints.txt'
|
||||
build_pattern = '[cp]p38-*'
|
||||
|
||||
constraint_file = os.path.join(cibuildwheel.util.resources_dir, constraint_filename)
|
||||
constraint_file = cibuildwheel.util.resources_dir / constraint_filename
|
||||
constraint_versions = get_versions_from_constraint_file(constraint_file)
|
||||
|
||||
for package in ['pip', 'setuptools', 'wheel', 'virtualenv']:
|
||||
env_name = 'EXPECTED_{}_VERSION'.format(package.upper())
|
||||
env_name = f'EXPECTED_{package.upper()}_VERSION'
|
||||
build_environment[env_name] = constraint_versions[package]
|
||||
|
||||
cibw_environment_option = ' '.join(
|
||||
['{}={}'.format(k, v) for k, v in build_environment.items()]
|
||||
[f'{k}={v}' for k, v in build_environment.items()]
|
||||
)
|
||||
|
||||
# build and test the wheels
|
||||
@@ -64,6 +94,9 @@ def test_pinned_versions(python_version):
|
||||
elif python_version == '3.5':
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if '-cp35' in w or '-pp35' in w]
|
||||
elif python_version == '3.6':
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if '-cp36' in w or '-pp36' in w]
|
||||
elif python_version == '3.8':
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if '-cp38' in w or '-pp38' in w]
|
||||
@@ -78,7 +111,8 @@ def test_dependency_constraints_file(tmp_path, python_version):
|
||||
if utils.platform == 'linux':
|
||||
pytest.skip('linux doesn\'t pin individual tool versions, it pins manylinux images instead')
|
||||
|
||||
project_dir = os.path.dirname(__file__)
|
||||
project_dir = tmp_path / 'project'
|
||||
project_with_expected_version_checks.generate(project_dir)
|
||||
|
||||
tool_versions = {
|
||||
'pip': '20.0.2',
|
||||
@@ -100,11 +134,11 @@ def test_dependency_constraints_file(tmp_path, python_version):
|
||||
build_environment = {}
|
||||
|
||||
for package_name, version in tool_versions.items():
|
||||
env_name = 'EXPECTED_{}_VERSION'.format(package_name.upper())
|
||||
env_name = f'EXPECTED_{package_name.upper()}_VERSION'
|
||||
build_environment[env_name] = version
|
||||
|
||||
cibw_environment_option = ' '.join(
|
||||
['{}={}'.format(k, v) for k, v in build_environment.items()]
|
||||
[f'{k}={v}' for k, v in build_environment.items()]
|
||||
)
|
||||
|
||||
# build and test the wheels
|
||||
@@ -1,19 +1,34 @@
|
||||
import os
|
||||
import platform
|
||||
import textwrap
|
||||
|
||||
import pytest
|
||||
|
||||
import utils
|
||||
from . import utils
|
||||
from . import test_projects
|
||||
|
||||
dockcross_only_project = test_projects.new_c_project(
|
||||
setup_py_add=textwrap.dedent(r'''
|
||||
import os, sys
|
||||
|
||||
# check that we're running in the correct docker image as specified in the
|
||||
# environment options CIBW_MANYLINUX1_*_IMAGE
|
||||
if "linux" in sys.platform and not os.path.exists("/dockcross"):
|
||||
raise Exception(
|
||||
"/dockcross directory not found. Is this test running in the correct docker image?"
|
||||
)
|
||||
''')
|
||||
)
|
||||
|
||||
|
||||
def test():
|
||||
project_dir = os.path.dirname(__file__)
|
||||
|
||||
def test(tmp_path):
|
||||
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')
|
||||
|
||||
project_dir = tmp_path / 'project'
|
||||
dockcross_only_project.generate(project_dir)
|
||||
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
||||
'CIBW_MANYLINUX_X86_64_IMAGE': 'dockcross/manylinux2010-x64',
|
||||
'CIBW_MANYLINUX_I686_IMAGE': 'dockcross/manylinux2010-x86',
|
||||
@@ -0,0 +1,61 @@
|
||||
import os
|
||||
import pytest
|
||||
import subprocess
|
||||
import textwrap
|
||||
from . import utils
|
||||
from . import test_projects
|
||||
|
||||
|
||||
project_with_environment_asserts = test_projects.new_c_project(
|
||||
setup_py_add=textwrap.dedent(r'''
|
||||
import os
|
||||
|
||||
# explode if environment isn't correct, as set in CIBW_ENVIRONMENT
|
||||
CIBW_TEST_VAR = os.environ.get("CIBW_TEST_VAR")
|
||||
CIBW_TEST_VAR_2 = os.environ.get("CIBW_TEST_VAR_2")
|
||||
PATH = os.environ.get("PATH")
|
||||
|
||||
if CIBW_TEST_VAR != "a b c":
|
||||
raise Exception('CIBW_TEST_VAR should equal "a b c". It was "%s"' % CIBW_TEST_VAR)
|
||||
if CIBW_TEST_VAR_2 != "1":
|
||||
raise Exception('CIBW_TEST_VAR_2 should equal "1". It was "%s"' % CIBW_TEST_VAR_2)
|
||||
if "/opt/cibw_test_path" not in PATH:
|
||||
raise Exception('PATH should contain "/opt/cibw_test_path". It was "%s"' % PATH)
|
||||
if "$PATH" in PATH:
|
||||
raise Exception('$PATH should be expanded in PATH. It was "%s"' % PATH)
|
||||
''')
|
||||
)
|
||||
|
||||
|
||||
def test(tmp_path):
|
||||
project_dir = tmp_path / 'project'
|
||||
project_with_environment_asserts.generate(project_dir)
|
||||
|
||||
# write some information into the CIBW_ENVIRONMENT, for expansion and
|
||||
# insertion into the environment by cibuildwheel. This is checked
|
||||
# in setup.py
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
||||
'CIBW_ENVIRONMENT': '''CIBW_TEST_VAR="a b c" CIBW_TEST_VAR_2=1 CIBW_TEST_VAR_3="$(echo 'test string 3')" PATH=$PATH:/opt/cibw_test_path''',
|
||||
'CIBW_ENVIRONMENT_WINDOWS': '''CIBW_TEST_VAR="a b c" CIBW_TEST_VAR_2=1 CIBW_TEST_VAR_3="$(echo 'test string 3')" PATH="$PATH;/opt/cibw_test_path"''',
|
||||
})
|
||||
|
||||
# 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 = tmp_path / 'project'
|
||||
output_dir = tmp_path / 'output'
|
||||
|
||||
project = test_projects.new_c_project()
|
||||
project.generate(project_dir)
|
||||
output_dir.mkdir()
|
||||
|
||||
# mess up PATH, somehow
|
||||
with pytest.raises(subprocess.CalledProcessError):
|
||||
utils.cibuildwheel_run(project_dir, output_dir=output_dir, 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(output_dir)) == 0
|
||||
@@ -1,21 +1,48 @@
|
||||
import os
|
||||
import platform
|
||||
import textwrap
|
||||
|
||||
import pytest
|
||||
|
||||
import utils
|
||||
from . import utils
|
||||
from . import test_projects
|
||||
|
||||
# TODO: specify these at runtime according to manylinux_image
|
||||
project_with_manylinux_symbols = test_projects.new_c_project(
|
||||
spam_c_top_level_add=textwrap.dedent(r'''
|
||||
#include <malloc.h>
|
||||
|
||||
#if !defined(__GLIBC_PREREQ)
|
||||
#error "Must run on a glibc linux environment"
|
||||
#endif
|
||||
|
||||
#if !__GLIBC_PREREQ(2, 5) /* manylinux1 is glibc 2.5 */
|
||||
#error "Must run on a glibc >= 2.5 linux environment"
|
||||
#endif
|
||||
'''),
|
||||
spam_c_function_add=textwrap.dedent(r'''
|
||||
#if defined(__GLIBC_PREREQ) && __GLIBC_PREREQ(2, 17) /* manylinux2014 is glibc 2.17 */
|
||||
// secure_getenv is only available in manylinux2014, ensuring
|
||||
// that only a manylinux2014 wheel is produced
|
||||
secure_getenv("NON_EXISTING_ENV_VARIABLE");
|
||||
#elif defined(__GLIBC_PREREQ) && __GLIBC_PREREQ(2, 10) /* manylinux2010 is glibc 2.12 */
|
||||
// malloc_info is only available on manylinux2010+
|
||||
malloc_info(0, stdout);
|
||||
#endif
|
||||
'''),
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('manylinux_image', ['manylinux1', 'manylinux2010', 'manylinux2014'])
|
||||
def test(manylinux_image):
|
||||
project_dir = os.path.dirname(__file__)
|
||||
|
||||
def test(manylinux_image, tmp_path):
|
||||
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")
|
||||
|
||||
project_dir = tmp_path / 'project'
|
||||
project_with_manylinux_symbols.generate(project_dir)
|
||||
|
||||
# build the wheels
|
||||
# CFLAGS environment variable is necessary to fail on 'malloc_info' (on manylinux1) during compilation/linking,
|
||||
# rather than when dynamically loading the Python
|
||||
@@ -0,0 +1,49 @@
|
||||
import textwrap
|
||||
from . import test_projects
|
||||
from . import utils
|
||||
|
||||
basic_project = test_projects.new_c_project(
|
||||
setup_py_add=textwrap.dedent(
|
||||
"""
|
||||
# Will fail if PEP 518 does work
|
||||
import sys
|
||||
import requests
|
||||
if sys.version_info < (3, 6, 0):
|
||||
assert requests.__version__ == "2.22.0", "Requests found but wrong version ({0})".format(requests.__version__)
|
||||
else:
|
||||
assert requests.__version__ == "2.23.0", "Requests found but wrong version ({0})".format(requests.__version__)
|
||||
|
||||
# Just making sure environment is still set
|
||||
import os
|
||||
if os.environ.get("CIBUILDWHEEL", "0") != "1":
|
||||
raise Exception("CIBUILDWHEEL environment variable is not set to 1")
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
basic_project.files[
|
||||
"pyproject.toml"
|
||||
] = """
|
||||
[build-system]
|
||||
requires = [
|
||||
"setuptools >= 42",
|
||||
"wheel",
|
||||
"requests==2.22.0; python_version<'3.6'",
|
||||
"requests==2.23.0; python_version>='3.6'"
|
||||
]
|
||||
|
||||
build-backend = "setuptools.build_meta"
|
||||
"""
|
||||
|
||||
|
||||
def test_pep518(tmp_path):
|
||||
|
||||
project_dir = tmp_path / "project"
|
||||
basic_project.generate(project_dir)
|
||||
|
||||
# build the wheels
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir)
|
||||
|
||||
# check that the expected wheels are produced
|
||||
expected_wheels = utils.expected_wheels("spam", "0.1.0")
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
@@ -0,0 +1,2 @@
|
||||
from .base import TestProject # noqa
|
||||
from .c import new_c_project # noqa
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user