Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
97dd4ee1c0 | ||
|
|
f6eaa9feec | ||
|
|
acedefdda5 | ||
|
|
9fbe8186f9 | ||
|
|
015f382418 | ||
|
|
0154a8f992 | ||
|
|
cd53552b4b | ||
|
|
630653b786 | ||
|
|
870bf3f740 | ||
|
|
6ef2f92dfc | ||
|
|
b3a16d58cf | ||
|
|
7f95eb3d8f | ||
|
|
a7b69cc16d | ||
|
|
87dfa75dcb | ||
|
|
61c45248bd | ||
|
|
acd9300077 | ||
|
|
37a6504351 | ||
|
|
452631eb60 | ||
|
|
aacd0fe33c | ||
|
|
26705eef1b | ||
|
|
ff277fd345 | ||
|
|
3d9fef458c | ||
|
|
fac9b13307 | ||
|
|
31a4e3c86b | ||
|
|
bd0aca510c | ||
|
|
93b2ce39f0 | ||
|
|
d696481714 | ||
|
|
42b81ab47f | ||
|
|
c013e0d0e6 | ||
|
|
2fd18793c1 | ||
|
|
200d4af89c | ||
|
|
48d77c7edf | ||
|
|
0b9a3e8afd | ||
|
|
a2a8655c43 | ||
|
|
4b5ac4ecbb | ||
|
|
8a434b1759 | ||
|
|
2a62198b47 | ||
|
|
28de36ea6f | ||
|
|
c3caa5f26f | ||
|
|
6840c9cea1 | ||
|
|
c67da48510 | ||
|
|
9a444b1ab8 | ||
|
|
10585d2882 | ||
|
|
2fbaa36286 | ||
|
|
4433ea4de1 | ||
|
|
5c5d1ef10a | ||
|
|
734dea7526 | ||
|
|
188eaa8a93 | ||
|
|
8e0ee0a4ab | ||
|
|
84446ed1a9 | ||
|
|
a4699f4cc8 | ||
|
|
ac90f2b9ac | ||
|
|
3a984346dd | ||
|
|
b448271640 | ||
|
|
e0dd32dea8 | ||
|
|
fbb4e28b44 | ||
|
|
aafdff7ec7 | ||
|
|
e6e8cb79fc | ||
|
|
a0d67a7a90 | ||
|
|
fd2b5d3270 | ||
|
|
416c6f3a3c | ||
|
|
9df68df525 | ||
|
|
45074a9b3a | ||
|
|
448d6538d7 | ||
|
|
17c83ad78b | ||
|
|
6d6d277ea5 | ||
|
|
d36b2d35c6 | ||
|
|
efa1958023 | ||
|
|
a345008626 | ||
|
|
43abc35259 | ||
|
|
2942c99ce4 | ||
|
|
3c577ab036 | ||
|
|
8ce553b2ea | ||
|
|
48c23b10d0 | ||
|
|
442174157a | ||
|
|
f19c606573 | ||
|
|
ac3523132d | ||
|
|
a7ea9f42cb | ||
|
|
1059249f4d | ||
|
|
516f4b9d7e | ||
|
|
ebf56281ed | ||
|
|
0896566743 | ||
|
|
fd92707823 | ||
|
|
cfec077f05 | ||
|
|
5c4767899d | ||
|
|
a15ccb6ef0 | ||
|
|
ae6d65e62e | ||
|
|
3cd3a9dc77 | ||
|
|
9e40094cfc | ||
|
|
c91d669a90 | ||
|
|
f81c9474d6 | ||
|
|
2610f3220a | ||
|
|
29b0cdb08a | ||
|
|
2b7da3fcbe | ||
|
|
726bcdf372 | ||
|
|
3142806f4b | ||
|
|
e28e43e7f9 | ||
|
|
c0cb43800b | ||
|
|
cc4e8975c8 | ||
|
|
c5a9837784 | ||
|
|
ceecee0e68 | ||
|
|
28af49a006 | ||
|
|
3a2d7a21b1 | ||
|
|
44fd8b5c26 | ||
|
|
fddfaba0a4 | ||
|
|
c884530a1d | ||
|
|
acf0320f68 | ||
|
|
5368b3bc64 | ||
|
|
acf60364c0 | ||
|
|
7d983cdf8c | ||
|
|
6847454850 | ||
|
|
af2a171994 | ||
|
|
b90c4035b3 | ||
|
|
56cd6c965d | ||
|
|
5df97a4121 | ||
|
|
69fe085ba3 | ||
|
|
08eddeff8b | ||
|
|
9fb748bfb0 | ||
|
|
dc446d3cac | ||
|
|
9f71900637 | ||
|
|
e098e1450d | ||
|
|
ed0077a995 | ||
|
|
5842a75927 | ||
|
|
44e3198880 | ||
|
|
4c0af9f152 | ||
|
|
8ddc6d57a2 | ||
|
|
9e35d65de4 | ||
|
|
58fa77b73b | ||
|
|
048ddc4af3 | ||
|
|
c521dca40b | ||
|
|
ac2b391790 | ||
|
|
47fe71efb4 | ||
|
|
53ba9bd0d0 | ||
|
|
317397b39a | ||
|
|
8466194576 | ||
|
|
c68be58e78 | ||
|
|
fc24230cb4 | ||
|
|
c36be04ee8 | ||
|
|
7a27ae941c | ||
|
|
737ad6b650 | ||
|
|
833a069207 | ||
|
|
cc913ee7cf | ||
|
|
d0b6401129 | ||
|
|
472eb898ed | ||
|
|
141b3e031e | ||
|
|
0beb9518c9 | ||
|
|
ad51e796c0 | ||
|
|
b12a560e87 | ||
|
|
e31c24d3bb | ||
|
|
27d97daf13 | ||
|
|
faf6a76375 | ||
|
|
91c3e90017 | ||
|
|
ad871f886a | ||
|
|
c956f118f6 | ||
|
|
762dc807cd | ||
|
|
2edb33662b | ||
|
|
df69dda44a | ||
|
|
1d9333689f | ||
|
|
68d8f89fb3 | ||
|
|
db9d658b51 | ||
|
|
1d88c27530 | ||
|
|
fefe128a15 | ||
|
|
a92959f191 | ||
|
|
321335e756 | ||
|
|
c88a73b40b | ||
|
|
dd4fc69e7c | ||
|
|
4dd5c0e11c | ||
|
|
a2f4d2aa47 | ||
|
|
850d109582 | ||
|
|
a052c71aec | ||
|
|
31339ba30e | ||
|
|
53c00a0af6 | ||
|
|
41891277d3 | ||
|
|
78ac83ff7e | ||
|
|
50a263df21 | ||
|
|
e417791ae4 | ||
|
|
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 | ||
|
|
98db24f92c | ||
|
|
d552d29eae | ||
|
|
aa99405a9c | ||
|
|
39101ae8ea | ||
|
|
1a83967261 | ||
|
|
d43817b49a | ||
|
|
3b1f0dd453 | ||
|
|
a1db0f7ebb | ||
|
|
29b25a3ab6 | ||
|
|
5a6f35fe76 | ||
|
|
acf5c24881 | ||
|
|
03ae918ec5 | ||
|
|
94dead9440 | ||
|
|
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 |
+1
-32
@@ -1,38 +1,9 @@
|
||||
version: 2
|
||||
|
||||
jobs:
|
||||
flake8:
|
||||
docker:
|
||||
- image: circleci/python:3.6
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
- run:
|
||||
name: Install flake8
|
||||
command: sudo python -m pip install flake8
|
||||
- run:
|
||||
name: Test.
|
||||
command: flake8 .
|
||||
|
||||
osx-python3.6:
|
||||
macos:
|
||||
xcode: "9.4.1"
|
||||
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
|
||||
|
||||
osx-python3.7:
|
||||
macos:
|
||||
xcode: "10.0.0"
|
||||
xcode: 10.2.1
|
||||
environment:
|
||||
PYTHON: python3
|
||||
steps:
|
||||
@@ -67,7 +38,5 @@ workflows:
|
||||
version: 2
|
||||
all-tests:
|
||||
jobs:
|
||||
- flake8
|
||||
- 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
|
||||
@@ -7,6 +7,14 @@ on:
|
||||
- 'docs/**'
|
||||
|
||||
jobs:
|
||||
pre-commit:
|
||||
name: Pre-commit checks (mypy, flake8, etc.)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v2
|
||||
- uses: pre-commit/action@v2.0.0
|
||||
|
||||
test:
|
||||
# skip branch builds on joerick/cibuildwheel, but always build `master` and pull requests
|
||||
if: github.repository != 'joerick/cibuildwheel' || github.ref == 'refs/heads/master' || github.event_name == 'pull_request'
|
||||
@@ -17,9 +25,10 @@ jobs:
|
||||
matrix:
|
||||
os: [ubuntu-18.04, windows-latest, macos-latest]
|
||||
python_version: ['3.7']
|
||||
timeout-minutes: 180
|
||||
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 +37,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,15 @@
|
||||
linux:
|
||||
image: python:3.8
|
||||
services:
|
||||
- name: docker:dind
|
||||
entrypoint: ["env", "-u", "DOCKER_HOST"]
|
||||
command: ["dockerd-entrypoint.sh"]
|
||||
variables:
|
||||
DOCKER_HOST: tcp://docker:2375/
|
||||
DOCKER_DRIVER: overlay2
|
||||
# See https://github.com/docker-library/docker/pull/166
|
||||
DOCKER_TLS_CERTDIR: ""
|
||||
script:
|
||||
- curl -sSL https://get.docker.com/ | sh
|
||||
- python -m pip install -r requirements-dev.txt
|
||||
- python ./bin/run_tests.py
|
||||
@@ -0,0 +1,25 @@
|
||||
repos:
|
||||
- repo: https://gitlab.com/pycqa/flake8
|
||||
rev: 3.8.3
|
||||
hooks:
|
||||
- id: flake8
|
||||
exclude: cibuildwheel/resources/
|
||||
additional_dependencies: [flake8-bugbear]
|
||||
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v3.2.0
|
||||
hooks:
|
||||
- id: check-case-conflict
|
||||
- id: check-merge-conflict
|
||||
- id: check-yaml
|
||||
- id: end-of-file-fixer
|
||||
exclude: cibuildwheel/resources/pinned_docker_images.cfg
|
||||
- id: mixed-line-ending
|
||||
- id: trailing-whitespace
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: v0.782
|
||||
hooks:
|
||||
- id: mypy
|
||||
files: cibuildwheel/|test/
|
||||
pass_filenames: false
|
||||
+20
-15
@@ -6,50 +6,55 @@ 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
|
||||
|
||||
- name: macOS | x86_64 | Python 3.x
|
||||
os: osx
|
||||
env: PYTHON=python3
|
||||
osx_image: xcode10.2
|
||||
env:
|
||||
- PYTHON=python3
|
||||
- PYTEST_ADDOPTS='-k "unit_test or test_0_basic" --suppress-no-test-exit-code'
|
||||
|
||||
- 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
|
||||
install: $PYTHON -m pip install -r requirements-dev.txt pytest-custom-exit-code
|
||||
|
||||
script: $PYTHON ./bin/run_tests.py
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -17,16 +17,19 @@ What does it do?
|
||||
|
||||
| | macOS x86_64 | Windows 64bit | Windows 32bit | manylinux x86_64 | manylinux i686 | manylinux aarch64 | manylinux ppc64le | manylinux s390x |
|
||||
|---|---|---|---|---|---|---|---|---|
|
||||
| CPython 2.7 | ✅ | ✅¹ | ✅¹ | ✅ | ✅ | | | |
|
||||
| CPython 3.5 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅² |
|
||||
| CPython 3.6 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅² |
|
||||
| CPython 3.7 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅² |
|
||||
| CPython 3.8 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅² |
|
||||
| PyPy 2.7 v7.3.0 | ✅ | | ✅ | ✅ | | | | |
|
||||
| PyPy 3.6 v7.3.0 | ✅ | | ✅ | ✅ | | | | |
|
||||
| CPython 2.7 | ✅ | ✅¹ | ✅¹ | ✅ | ✅ | | | |
|
||||
| CPython 3.5 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅² | ✅² | ✅³ |
|
||||
| CPython 3.6 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅² | ✅² | ✅³ |
|
||||
| CPython 3.7 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅² | ✅² | ✅³ |
|
||||
| CPython 3.8 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅² | ✅² | ✅³ |
|
||||
| CPython 3.9 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅² | ✅² | ✅³ |
|
||||
| PyPy 2.7 v7.3.2 | ✅ | | ✅ | ✅ | | | | |
|
||||
| PyPy 3.6 v7.3.2 | ✅ | | ✅ | ✅ | | | | |
|
||||
| PyPy 3.7 (alpha) v7.3.2 | ✅ | | ✅ | ✅ | | | | |
|
||||
|
||||
<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>² Only supported on Travis</sup><br>
|
||||
<sup>³ Beta support until Travis CI fixes <a href="https://travis-ci.community/t/no-space-left-on-device-for-system-z/5954/11">a bug</a></sup><br>
|
||||
|
||||
- Builds manylinux, macOS and Windows wheels for CPython and PyPy 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)
|
||||
@@ -35,7 +38,7 @@ What does it do?
|
||||
Usage
|
||||
-----
|
||||
|
||||
`cibuildwheel` currently works on **Travis CI**, **Azure Pipelines**, **AppVeyor** and **GitHub Actions** to build wheels for all three supported platforms (Linux, macOS, Windows). On **CircleCI** Linux and macOS wheels can be built.
|
||||
`cibuildwheel` currently works on **Travis CI**, **Azure Pipelines**, **AppVeyor**, **GitHub Actions**, **CircleCI**, and **Gitlab CI**. Check the table below for supported platforms on each service:
|
||||
|
||||
| | Linux | macOS | Windows |
|
||||
|-----------------|-------|-------|---------|
|
||||
@@ -44,8 +47,9 @@ Usage
|
||||
| AppVeyor | ✅ | ✅ | ✅ |
|
||||
| GitHub Actions | ✅ | ✅ | ✅ |
|
||||
| CircleCI | ✅ | ✅ | |
|
||||
| Gitlab CI | ✅ | | |
|
||||
|
||||
`cibuildwheel` is not intended to run on your development machine. Because it uses system Python from Python.org it will try to install packages globally - not what you expect from a build tool! Instead, isolated CI services like Travis CI, CircleCI, Azure Pipelines and AppVeyor are ideal.
|
||||
`cibuildwheel` is not intended to run on your development machine. Because it uses system Python from Python.org it will try to install packages globally - not what you expect from a build tool! Instead, isolated CI services like those mentioned above are ideal.
|
||||
|
||||
<!--intro-end-->
|
||||
|
||||
@@ -63,6 +67,8 @@ jobs:
|
||||
- services: docker
|
||||
# and a mac build
|
||||
- os: osx
|
||||
# PyPy 7.3.2 needs macOS >= 10.14
|
||||
osx_image: xcode10.2
|
||||
language: shell
|
||||
# and a windows build
|
||||
- os: windows
|
||||
@@ -70,6 +76,8 @@ jobs:
|
||||
before_install:
|
||||
- choco install python --version 3.8.0
|
||||
- export PATH="/c/Python38:/c/Python38/Scripts:$PATH"
|
||||
# make sure it's on PATH as 'python3'
|
||||
- ln -s /c/Python38/python.exe /c/Python38/python3.exe
|
||||
|
||||
env:
|
||||
global:
|
||||
@@ -77,7 +85,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.6.2
|
||||
|
||||
script:
|
||||
# build the wheels, put them into './wheelhouse'
|
||||
@@ -92,7 +100,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,23 +108,26 @@ 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
|
||||
----------------
|
||||
|
||||
Here are some repos that use cibuildwheel.
|
||||
|
||||
- [pyinstrument_cext](https://github.com/joerick/pyinstrument_cext)
|
||||
- [pyinstrument_cext](https://github.com/joerick/pyinstrument_cext) - A simple C extension, without external dependencies, built on Travis CI/Appveyor.
|
||||
- [websockets](https://github.com/aaugustin/websockets)
|
||||
- [Parselmouth](https://github.com/YannickJadoul/Parselmouth)
|
||||
- [python-admesh](https://github.com/admesh/python-admesh)
|
||||
@@ -128,8 +139,16 @@ 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)
|
||||
- [fathon](https://github.com/stfbnc/fathon)
|
||||
- [etebase-py](https://github.com/etesync/etebase-py) - Python bindings to a Rust library using `setuptools-rust`, and `sccache` for improved speed, built on Travis CI.
|
||||
|
||||
> Add your repo here! Send a PR.
|
||||
>
|
||||
> <sup>I'd like to include notes here to indicate why an example might be interesting to cibuildwheel users - the styles/technologies/techniques used in each. Please include that in future additions!</sup>
|
||||
|
||||
Legal note
|
||||
----------
|
||||
@@ -143,6 +162,106 @@ This is similar to static linking, so it might have some licence implications. C
|
||||
Changelog
|
||||
=========
|
||||
|
||||
### 1.6.2
|
||||
|
||||
_9 October 2020_
|
||||
|
||||
- ✨ Python 3.9 updated to the final release version - v3.9.0 (#440)
|
||||
- 🛠 Pypy updated to v7.3.2, adding alpha support for Python 3.7 (#430)
|
||||
|
||||
### 1.6.1
|
||||
|
||||
_20 September 2020_
|
||||
|
||||
- 🛠 Add PPC64LE manylinux image supporting Python 3.9. (#436)
|
||||
- 📚 Add project URLs to PyPI listing (#428)
|
||||
|
||||
### 1.6.0
|
||||
|
||||
_9 September 2020_
|
||||
|
||||
- 🌟 Add Python 3.9 support! This initial support uses release candidate
|
||||
builds. You can start publishing wheels for Python 3.9 now, ahead of
|
||||
the official release. (#382)
|
||||
|
||||
Minor note - if you're building PPC64LE wheels, the manylinux image pinned
|
||||
by this version is
|
||||
[still on Python 3.9b3](https://github.com/pypa/manylinux/issues/758), not a
|
||||
release candidate. We'd advise holding off on distributing 3.9 ppc64le wheels
|
||||
until a subsequent version of cibuildwheel.
|
||||
- 🌟 Add Gitlab CI support. Gitlab CI can now build Linux wheels, using
|
||||
cibuildwheel. (#419)
|
||||
- 🐛 Fix a bug that causes pyproject.toml dependencies to fail to install on
|
||||
Windows (#420)
|
||||
- 📚 Added some information about Windows VC++ runtimes and how they relate
|
||||
to wheels.
|
||||
|
||||
### 1.5.5
|
||||
|
||||
_22 July 2020_
|
||||
|
||||
- 🐛 Fix a bug that would cause command substitutions in CIBW_ENVIRONMENT to
|
||||
produce no output on Linux (#411)
|
||||
- 🐛 Fix regression (introduced in 1.5.3) which caused BEFORE_BUILD and
|
||||
BEFORE_ALL to be executed in the wrong directory (#410)
|
||||
|
||||
### 1.5.4
|
||||
|
||||
_19 June 2020_
|
||||
|
||||
- 🐛 Fix a bug that would cause command substitutions in CIBW_ENVIRONMENT
|
||||
variables to not interpret quotes in commands correctly (#406, #408)
|
||||
|
||||
### 1.5.3
|
||||
|
||||
_19 July 2020_
|
||||
|
||||
- 🛠 Update CPython 3.8 to 3.8.3 (#405)
|
||||
- 🛠 Internal refactoring of Linux build, to move control flow into Python (#386)
|
||||
|
||||
### 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 +274,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 +304,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 +314,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 +337,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 +350,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 +387,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 +417,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)
|
||||
@@ -430,6 +549,7 @@ Maintainers
|
||||
- Joe Rickerby [@joerick](https://github.com/joerick)
|
||||
- Yannick Jadoul [@YannickJadoul](https://github.com/YannickJadoul)
|
||||
- Matthieu Darbois [@mayeut](https://github.com/mayeut)
|
||||
- Henry Schreiner [@henryiii](https://github.com/henryiii)
|
||||
|
||||
Credits
|
||||
-------
|
||||
|
||||
+1
-4
@@ -1,7 +1,4 @@
|
||||
environment:
|
||||
# workaround an appveyor bug on macos
|
||||
# see https://github.com/appveyor/ci/issues/3376#issuecomment-611314299
|
||||
APPVEYOR_CONSOLE_DISABLE_PTY: true
|
||||
matrix:
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Ubuntu
|
||||
APPVEYOR_JOB_NAME: "python37-x64-ubuntu"
|
||||
@@ -19,7 +16,7 @@ init:
|
||||
- ps: |
|
||||
$BRANCH = if ($env:APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH) { $env:APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH } else { $env:APPVEYOR_REPO_BRANCH }
|
||||
if (-not ($BRANCH -eq 'master' -or $BRANCH.ToLower().StartsWith('appveyor-'))) {
|
||||
$env:PYTEST_ADDOPTS='-k "unit_test or _basic" --suppress-no-test-exit-code'
|
||||
$env:PYTEST_ADDOPTS='-k "unit_test or test_0_basic" --suppress-no-test-exit-code'
|
||||
}
|
||||
|
||||
install: python -m pip install -r requirements-dev.txt pytest-custom-exit-code
|
||||
|
||||
+6
-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,11 +16,12 @@ 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
|
||||
pool: {vmImage: 'vs2017-win2016'}
|
||||
timeoutInMinutes: 180
|
||||
steps:
|
||||
- task: UsePythonVersion@0
|
||||
inputs:
|
||||
@@ -38,11 +29,12 @@ 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
|
||||
pool: {vmImage: 'vs2017-win2016'}
|
||||
timeoutInMinutes: 180
|
||||
steps:
|
||||
- task: UsePythonVersion@0
|
||||
inputs:
|
||||
@@ -50,6 +42,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 "$@"
|
||||
|
||||
Executable
+137
@@ -0,0 +1,137 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
from subprocess import run
|
||||
import sys
|
||||
import textwrap
|
||||
import time
|
||||
import click
|
||||
from collections import namedtuple
|
||||
from urllib.parse import quote
|
||||
|
||||
|
||||
def shell(cmd, **kwargs):
|
||||
return run([cmd], shell=True, **kwargs)
|
||||
|
||||
|
||||
def git_repo_has_changes():
|
||||
unstaged_changes = shell('git diff-index --quiet HEAD --').returncode != 0
|
||||
staged_changes = shell('git diff-index --quiet --cached HEAD --').returncode != 0
|
||||
return unstaged_changes or staged_changes
|
||||
|
||||
|
||||
def generate_basic_project(path):
|
||||
sys.path.insert(0, '')
|
||||
from test.test_projects.c import new_c_project
|
||||
|
||||
project = new_c_project()
|
||||
project.generate(path)
|
||||
|
||||
|
||||
CIService = namedtuple('CIService', 'name src_config_path dst_config_path badge_md')
|
||||
services = [
|
||||
CIService(
|
||||
name='appveyor',
|
||||
src_config_path='examples/appveyor-minimal.yml',
|
||||
dst_config_path='appveyor.yml',
|
||||
badge_md='[](https://ci.appveyor.com/project/joerick/cibuildwheel/branch/{branch})',
|
||||
),
|
||||
CIService(
|
||||
name='azure-pipelines',
|
||||
src_config_path='examples/azure-pipelines-minimal.yml',
|
||||
dst_config_path='azure-pipelines.yml',
|
||||
badge_md='[](https://dev.azure.com/joerick0429/cibuildwheel/_build/latest?definitionId=2&branchName={branch})',
|
||||
),
|
||||
CIService(
|
||||
name='circle-ci',
|
||||
src_config_path='examples/circleci-minimal.yml',
|
||||
dst_config_path='.circleci/config.yml',
|
||||
badge_md='[](https://circleci.com/gh/joerick/cibuildwheel/tree/{branch})',
|
||||
),
|
||||
CIService(
|
||||
name='github',
|
||||
src_config_path='examples/github-minimal.yml',
|
||||
dst_config_path='.github/workflows/example.yml',
|
||||
badge_md='[](https://github.com/joerick/cibuildwheel/actions)',
|
||||
),
|
||||
CIService(
|
||||
name='travis-ci',
|
||||
src_config_path='examples/travis-ci-minimal.yml',
|
||||
dst_config_path='.travis.yml',
|
||||
badge_md='[](https://travis-ci.org/joerick/cibuildwheel)',
|
||||
),
|
||||
CIService(
|
||||
name='gitlab',
|
||||
src_config_path='examples/gitlab-minimal.yml',
|
||||
dst_config_path='.gitlab-ci.yml',
|
||||
badge_md='[](https://gitlab.com/joerick/cibuildwheel/-/commits/{branch})'
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
@click.command()
|
||||
def run_example_ci_configs():
|
||||
if git_repo_has_changes():
|
||||
print('Your git repo has uncommitted changes. Commit or stash before continuing.')
|
||||
exit(1)
|
||||
|
||||
previous_branch = shell('git rev-parse --abbrev-ref HEAD',
|
||||
check=True,
|
||||
capture_output=True,
|
||||
encoding='utf8').stdout.strip()
|
||||
|
||||
timestamp = time.strftime('%Y-%m-%dT%H-%M-%S', time.gmtime())
|
||||
branch_name = f'example-config-test---{previous_branch}-{timestamp}'
|
||||
|
||||
try:
|
||||
shell(f'git checkout --orphan {branch_name}', check=True)
|
||||
|
||||
example_project = Path('example_root')
|
||||
generate_basic_project(example_project)
|
||||
|
||||
for service in services:
|
||||
src_config_file = Path(service.src_config_path)
|
||||
dst_config_file = example_project / service.dst_config_path
|
||||
|
||||
dst_config_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copyfile(src_config_file, dst_config_file)
|
||||
|
||||
run(['git', 'add', example_project], check=True)
|
||||
run(['git', 'commit', '-m', textwrap.dedent(f'''
|
||||
Test example minimal configs
|
||||
|
||||
Testing files: {[s.src_config_path for s in services]}
|
||||
Generated from branch: {previous_branch}
|
||||
Time: {timestamp}
|
||||
''')], check=True)
|
||||
shell(f'git subtree --prefix={example_project} push origin {branch_name}', check=True)
|
||||
|
||||
print('---')
|
||||
print()
|
||||
print('> **Examples test run**')
|
||||
print('> ')
|
||||
print(f'> Branch: [{branch_name}](https://github.com/joerick/cibuildwheel/tree/{branch_name})')
|
||||
print('> ')
|
||||
print('> | Service | Config | Status |')
|
||||
print('> |---|---|---|')
|
||||
for service in services:
|
||||
badge = service.badge_md.format(branch=branch_name, branch_escaped=quote(branch_name, safe=''))
|
||||
print(f'> | {service.name} | `{service.src_config_path}` | {badge} |')
|
||||
print('> ')
|
||||
print('> Generated by `bin/run_example_ci_config.py`')
|
||||
print()
|
||||
print('---')
|
||||
finally:
|
||||
# remove any local changes
|
||||
shutil.rmtree(example_project, ignore_errors=True)
|
||||
shell('git checkout -- .')
|
||||
shell(f'git checkout {previous_branch}', check=True)
|
||||
shell(f'git branch -D --force {branch_name}', check=True)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
os.chdir(os.path.dirname(__file__))
|
||||
os.chdir('..')
|
||||
run_example_ci_configs(standalone_mode=True)
|
||||
@@ -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)
|
||||
+8
-18
@@ -3,28 +3,18 @@
|
||||
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'])
|
||||
unit_test_args = [sys.executable, '-m', 'pytest', 'unit_test']
|
||||
# run the docker unit tests only on Linux
|
||||
if sys.platform.startswith('linux'):
|
||||
unit_test_args += ['--run-docker']
|
||||
subprocess.check_call(unit_test_args)
|
||||
|
||||
# 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'])
|
||||
|
||||
@@ -19,7 +19,7 @@ subprocess.check_call([
|
||||
'--upgrade',
|
||||
'cibuildwheel/resources/constraints.in',
|
||||
])
|
||||
for python_version in ['27', '35', '36']:
|
||||
for python_version in ['27', '35', '36', '37']:
|
||||
subprocess.check_call([
|
||||
f'./env{python_version}/bin/pip-compile',
|
||||
'--allow-unsafe',
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = '1.4.1'
|
||||
__version__ = '1.6.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,23 @@
|
||||
import shlex
|
||||
import subprocess
|
||||
from collections import namedtuple
|
||||
from typing import Callable, Dict, List, NamedTuple, Optional, Sequence
|
||||
|
||||
import bashlex
|
||||
import bashlex # type: ignore
|
||||
|
||||
NodeExecutionContext = namedtuple('NodeExecutionContext', ['environment', 'input'])
|
||||
# a function that takes a command and the environment, and returns the result
|
||||
EnvironmentExecutor = Callable[[List[str], Dict[str, str]], str]
|
||||
|
||||
|
||||
def evaluate(value, environment):
|
||||
def local_environment_executor(command: List[str], env: Dict[str, str]) -> str:
|
||||
return subprocess.check_output(command, env=env, universal_newlines=True)
|
||||
|
||||
|
||||
class NodeExecutionContext(NamedTuple):
|
||||
environment: Dict[str, str]
|
||||
input: str
|
||||
executor: EnvironmentExecutor
|
||||
|
||||
|
||||
def evaluate(value: str, environment: Dict[str, str], executor: Optional[EnvironmentExecutor] = None) -> str:
|
||||
if not value:
|
||||
# empty string evaluates to empty string
|
||||
# (but trips up bashlex)
|
||||
@@ -16,55 +26,79 @@ 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]
|
||||
|
||||
return evaluate_node(
|
||||
value_word_node,
|
||||
context=NodeExecutionContext(environment=environment, input=value)
|
||||
context=NodeExecutionContext(environment=environment, input=value, executor=executor or local_environment_executor)
|
||||
)
|
||||
|
||||
|
||||
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':
|
||||
return evaluate_command_node(node.command, context=context)
|
||||
node_result = evaluate_command_node(node.command, context=context)
|
||||
# bash removes training newlines in command substitution
|
||||
return node_result.rstrip()
|
||||
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.kind}"')
|
||||
|
||||
|
||||
def evaluate_word_node(node, context):
|
||||
word_start = node.pos[0]
|
||||
word_end = node.pos[1]
|
||||
word_string = context.input[word_start:word_end]
|
||||
letters = list(word_string)
|
||||
def evaluate_word_node(node: bashlex.ast.node, context: NodeExecutionContext) -> str:
|
||||
value = node.word
|
||||
|
||||
for part in node.parts:
|
||||
part_start = part.pos[0] - word_start
|
||||
part_end = part.pos[1] - word_start
|
||||
part_string = context.input[part.pos[0]:part.pos[1]]
|
||||
part_value = evaluate_node(part, context=context)
|
||||
|
||||
# Set all the characters in the part to None
|
||||
for i in range(part_start, part_end):
|
||||
letters[i] = None
|
||||
if part_string not in value:
|
||||
raise RuntimeError(
|
||||
'bash parse failed. part "{}" not found in "{}". Word was "{}". Full input was "{}"'.format(
|
||||
part_string, value, node.word, context.input,
|
||||
)
|
||||
)
|
||||
|
||||
letters[part_start] = evaluate_node(part, context=context)
|
||||
value = value.replace(part_string, part_value, 1)
|
||||
|
||||
# remove the None letters and concat
|
||||
value = ''.join(l for l in letters if l is not None)
|
||||
|
||||
# apply bash-like quotes/whitespace treatment
|
||||
return ' '.join(word.strip() for word in shlex.split(value))
|
||||
return value
|
||||
|
||||
|
||||
def evaluate_command_node(node, context):
|
||||
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_command_node(node: bashlex.ast.node, context: NodeExecutionContext) -> str:
|
||||
if any(n.kind == 'operator' for n in node.parts):
|
||||
return evaluate_nodes_as_compound_command(node.parts, context=context)
|
||||
else:
|
||||
return evaluate_nodes_as_simple_command(node.parts, context=context)
|
||||
|
||||
|
||||
def evaluate_parameter_node(node, context):
|
||||
def evaluate_nodes_as_compound_command(nodes: Sequence[bashlex.ast.node], context: NodeExecutionContext) -> str:
|
||||
# bashlex doesn't support any operators besides ';' inside command
|
||||
# substitutions, so we only need to handle that case. We do so assuming
|
||||
# that `set -o errexit` is on, because it's easier to code!
|
||||
|
||||
result = ''
|
||||
for node in nodes:
|
||||
if node.kind == 'command':
|
||||
result += evaluate_command_node(node, context=context)
|
||||
elif node.kind == 'operator':
|
||||
if node.op == ';':
|
||||
pass
|
||||
else:
|
||||
raise ValueError(f'Unsupported bash operator: "{node.op}"')
|
||||
else:
|
||||
raise ValueError(f'Unsupported bash node in compound command: "{node.kind}"')
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def evaluate_nodes_as_simple_command(nodes: List[bashlex.ast.node], context: NodeExecutionContext):
|
||||
command = [evaluate_node(part, context=context) for part in nodes]
|
||||
return context.executor(command, context.environment)
|
||||
|
||||
|
||||
def evaluate_parameter_node(node: bashlex.ast.node, context: NodeExecutionContext) -> str:
|
||||
return context.environment.get(node.value, '')
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
import uuid
|
||||
from os import PathLike
|
||||
from pathlib import Path, PurePath
|
||||
from typing import IO, Dict, List, Optional, Sequence, Union
|
||||
|
||||
|
||||
class DockerContainer:
|
||||
'''
|
||||
An object that represents a running Docker container.
|
||||
|
||||
Intended for use as a context manager e.g.
|
||||
`with DockerContainer('ubuntu') as docker:`
|
||||
|
||||
A bash shell is running in the remote container. When `call()` is invoked,
|
||||
the command is relayed to the remote shell, and the results are streamed
|
||||
back to cibuildwheel.
|
||||
'''
|
||||
UTILITY_PYTHON = '/opt/python/cp38-cp38/bin/python'
|
||||
|
||||
process: subprocess.Popen
|
||||
bash_stdin: IO[bytes]
|
||||
bash_stdout: IO[bytes]
|
||||
|
||||
def __init__(self, docker_image: str, simulate_32_bit: bool = False, cwd: Optional[Union[str, PathLike]] = None):
|
||||
self.docker_image = docker_image
|
||||
self.simulate_32_bit = simulate_32_bit
|
||||
self.cwd = cwd
|
||||
|
||||
def __enter__(self) -> 'DockerContainer':
|
||||
self.name = f'cibuildwheel-{uuid.uuid4()}'
|
||||
cwd_args = ['-w', str(self.cwd)] if self.cwd else []
|
||||
shell_args = ['linux32', '/bin/bash'] if self.simulate_32_bit else ['/bin/bash']
|
||||
subprocess.run(
|
||||
[
|
||||
'docker', 'create',
|
||||
'--env', 'CIBUILDWHEEL',
|
||||
'--name', self.name,
|
||||
'-i',
|
||||
'-v', '/:/host', # ignored on CircleCI
|
||||
*cwd_args,
|
||||
self.docker_image,
|
||||
*shell_args
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
self.process = subprocess.Popen(
|
||||
[
|
||||
'docker', 'start',
|
||||
'--attach', '--interactive',
|
||||
self.name,
|
||||
],
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
)
|
||||
|
||||
assert self.process.stdin and self.process.stdout
|
||||
self.bash_stdin = self.process.stdin
|
||||
self.bash_stdout = self.process.stdout
|
||||
|
||||
# run a noop command to block until the container is responding
|
||||
self.call(['/bin/true'])
|
||||
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self.bash_stdin.close()
|
||||
self.process.terminate()
|
||||
self.process.wait()
|
||||
|
||||
subprocess.run(['docker', 'rm', '--force', '-v', self.name])
|
||||
self.name = None
|
||||
|
||||
def copy_into(self, from_path: Path, to_path: PurePath) -> None:
|
||||
# `docker cp` causes 'no space left on device' error when
|
||||
# a container is running and the host filesystem is
|
||||
# mounted. https://github.com/moby/moby/issues/38995
|
||||
# Use `docker exec` instead.
|
||||
|
||||
if from_path.is_dir():
|
||||
self.call(['mkdir', '-p', to_path])
|
||||
subprocess.run(
|
||||
f'tar cf - . | docker exec -i {self.name} tar -xC {shell_quote(to_path)} -f -',
|
||||
shell=True,
|
||||
check=True,
|
||||
cwd=from_path)
|
||||
else:
|
||||
subprocess.run(
|
||||
f'cat {shell_quote(from_path)} | docker exec -i {self.name} sh -c "cat > {shell_quote(to_path)}"',
|
||||
shell=True,
|
||||
check=True)
|
||||
|
||||
def copy_out(self, from_path: PurePath, to_path: Path) -> None:
|
||||
# note: we assume from_path is a dir
|
||||
to_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
subprocess.run(
|
||||
f'docker exec -i {self.name} tar -cC {shell_quote(from_path)} -f - . | tar -xf -',
|
||||
shell=True,
|
||||
check=True,
|
||||
cwd=to_path
|
||||
)
|
||||
|
||||
def glob(self, path: PurePath, pattern: str) -> List[PurePath]:
|
||||
glob_pattern = os.path.join(str(path), pattern)
|
||||
|
||||
path_strs = json.loads(self.call([
|
||||
self.UTILITY_PYTHON,
|
||||
'-c',
|
||||
f'import sys, json, glob; json.dump(glob.glob({glob_pattern!r}), sys.stdout)'
|
||||
], capture_output=True))
|
||||
|
||||
return [PurePath(p) for p in path_strs]
|
||||
|
||||
def call(self, args: Sequence[Union[str, PathLike]], env: Optional[Dict[str, str]] = None,
|
||||
capture_output=False, cwd: Optional[Union[str, PathLike]] = None) -> str:
|
||||
|
||||
chdir = f'cd {cwd}' if cwd else ''
|
||||
env_assignments = ' '.join(f'{shlex.quote(k)}={shlex.quote(v)}'
|
||||
for k, v in env.items()) if env is not None else ''
|
||||
command = ' '.join(shlex.quote(str(a)) for a in args)
|
||||
end_of_message = str(uuid.uuid4())
|
||||
|
||||
# log the command we're executing
|
||||
print(f' + {command}')
|
||||
|
||||
# Write a command to the remote shell. First we change the
|
||||
# cwd, if that's required. Then, we use the `env` utility to run
|
||||
# `command` inside the specified environment. We use `env` because it
|
||||
# can cope with spaces and strange characters in the name or value.
|
||||
# Finally, the remote shell is told to write a footer - this will show
|
||||
# up in the output so we know when to stop reading, and will include
|
||||
# the returncode of `command`.
|
||||
self.bash_stdin.write(bytes(f'''(
|
||||
{chdir}
|
||||
env {env_assignments} {command}
|
||||
printf "%04d%s\n" $? {end_of_message}
|
||||
)
|
||||
''', encoding='utf8', errors='surrogateescape'))
|
||||
self.bash_stdin.flush()
|
||||
|
||||
if capture_output:
|
||||
output_io: IO[bytes] = io.BytesIO()
|
||||
else:
|
||||
output_io = sys.stdout.buffer
|
||||
|
||||
while True:
|
||||
line = self.bash_stdout.readline()
|
||||
|
||||
if line.endswith(b'%s\n' % (bytes(end_of_message, encoding='utf8'))):
|
||||
footer_offset = (
|
||||
len(line)
|
||||
- 1 # newline character
|
||||
- len(end_of_message) # delimiter
|
||||
- 4 # 4 returncode decimals
|
||||
)
|
||||
returncode_str = line[footer_offset:footer_offset+4]
|
||||
returncode = int(returncode_str)
|
||||
# add the last line to output, without the footer
|
||||
output_io.write(line[0:footer_offset])
|
||||
break
|
||||
else:
|
||||
output_io.write(line)
|
||||
|
||||
if isinstance(output_io, io.BytesIO):
|
||||
output = str(output_io.getvalue(), encoding='utf8', errors='surrogateescape')
|
||||
else:
|
||||
output = ''
|
||||
|
||||
if returncode != 0:
|
||||
raise subprocess.CalledProcessError(returncode, args, output)
|
||||
|
||||
return output
|
||||
|
||||
def get_environment(self) -> Dict[str, str]:
|
||||
return json.loads(self.call([
|
||||
self.UTILITY_PYTHON,
|
||||
'-c',
|
||||
'import sys, json, os; json.dump(os.environ.copy(), sys.stdout)'
|
||||
], capture_output=True))
|
||||
|
||||
def environment_executor(self, command: List[str], environment: Dict[str, str]) -> str:
|
||||
# used as an EnvironmentExecutor to evaluate commands and capture output
|
||||
return self.call(command, env=environment, capture_output=True)
|
||||
|
||||
|
||||
def shell_quote(path: PurePath) -> str:
|
||||
return shlex.quote(str(path))
|
||||
+26
-22
@@ -1,4 +1,6 @@
|
||||
import bashlex
|
||||
import bashlex # type: ignore
|
||||
|
||||
from typing import Dict, List, Mapping, Optional
|
||||
|
||||
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,39 +39,47 @@ 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], executor: Optional[bashlex_eval.EnvironmentExecutor] = None) -> str:
|
||||
'''Returns the value of this assignment, as evaluated in the environment'''
|
||||
return bashlex_eval.evaluate(self.value, environment=environment)
|
||||
return bashlex_eval.evaluate(self.value, environment=environment, executor=executor)
|
||||
|
||||
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],
|
||||
executor: Optional[bashlex_eval.EnvironmentExecutor] = None) -> Dict[str, str]:
|
||||
environment = dict(**prev_environment)
|
||||
|
||||
for assignment in self.assignments:
|
||||
value = assignment.evaluated_value(environment=environment)
|
||||
value = assignment.evaluated_value(environment=environment, executor=executor)
|
||||
environment[assignment.name] = value
|
||||
|
||||
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)
|
||||
|
||||
+158
-199
@@ -1,27 +1,17 @@
|
||||
import os
|
||||
import platform
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
import textwrap
|
||||
import uuid
|
||||
from collections import namedtuple
|
||||
from os import PathLike
|
||||
from pathlib import Path, PurePath
|
||||
from typing import List, NamedTuple, Union
|
||||
|
||||
from .util import (
|
||||
get_build_verbosity_extra_flags,
|
||||
prepare_command,
|
||||
BuildOptions
|
||||
)
|
||||
from .docker_container import DockerContainer
|
||||
from .util import (BuildOptions, BuildSelector, NonPlatformWheelError,
|
||||
get_build_verbosity_extra_flags, prepare_command)
|
||||
|
||||
|
||||
def call(args, input=None, universal_newlines=False):
|
||||
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,41 +32,56 @@ 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: str
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
return PurePath(self.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'),
|
||||
PythonConfiguration(version='3.5', identifier='cp35-manylinux_x86_64', path='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(version='3.6', identifier='cp36-manylinux_x86_64', path='/opt/python/cp36-cp36m'),
|
||||
PythonConfiguration(version='3.7', identifier='cp37-manylinux_x86_64', path='/opt/python/cp37-cp37m'),
|
||||
PythonConfiguration(version='3.8', identifier='cp38-manylinux_x86_64', path='/opt/python/cp38-cp38'),
|
||||
PythonConfiguration(version='2.7', identifier='cp27-manylinux_i686', path='/opt/python/cp27-cp27m'),
|
||||
PythonConfiguration(version='2.7', identifier='cp27-manylinux_i686', path='/opt/python/cp27-cp27mu'),
|
||||
PythonConfiguration(version='3.5', identifier='cp35-manylinux_i686', path='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(version='3.6', identifier='cp36-manylinux_i686', path='/opt/python/cp36-cp36m'),
|
||||
PythonConfiguration(version='3.7', identifier='cp37-manylinux_i686', path='/opt/python/cp37-cp37m'),
|
||||
PythonConfiguration(version='3.8', identifier='cp38-manylinux_i686', path='/opt/python/cp38-cp38'),
|
||||
PythonConfiguration(version='2.7', identifier='pp27-manylinux_x86_64', path='/opt/python/pp27-pypy_73'),
|
||||
PythonConfiguration(version='3.6', identifier='pp36-manylinux_x86_64', path='/opt/python/pp36-pypy36_pp73'),
|
||||
PythonConfiguration(version='3.5', identifier='cp35-manylinux_aarch64', path='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(version='3.6', identifier='cp36-manylinux_aarch64', path='/opt/python/cp36-cp36m'),
|
||||
PythonConfiguration(version='3.7', identifier='cp37-manylinux_aarch64', path='/opt/python/cp37-cp37m'),
|
||||
PythonConfiguration(version='3.8', identifier='cp38-manylinux_aarch64', path='/opt/python/cp38-cp38'),
|
||||
PythonConfiguration(version='3.5', identifier='cp35-manylinux_ppc64le', path='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(version='3.6', identifier='cp36-manylinux_ppc64le', path='/opt/python/cp36-cp36m'),
|
||||
PythonConfiguration(version='3.7', identifier='cp37-manylinux_ppc64le', path='/opt/python/cp37-cp37m'),
|
||||
PythonConfiguration(version='3.8', identifier='cp38-manylinux_ppc64le', path='/opt/python/cp38-cp38'),
|
||||
PythonConfiguration(version='3.5', identifier='cp35-manylinux_s390x', path='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(version='3.6', identifier='cp36-manylinux_s390x', path='/opt/python/cp36-cp36m'),
|
||||
PythonConfiguration(version='3.7', identifier='cp37-manylinux_s390x', path='/opt/python/cp37-cp37m'),
|
||||
PythonConfiguration(version='3.8', identifier='cp38-manylinux_s390x', path='/opt/python/cp38-cp38'),
|
||||
PythonConfiguration(version='2.7', identifier='cp27-manylinux_x86_64', path_str='/opt/python/cp27-cp27m'),
|
||||
PythonConfiguration(version='2.7', identifier='cp27-manylinux_x86_64', path_str='/opt/python/cp27-cp27mu'),
|
||||
PythonConfiguration(version='3.5', identifier='cp35-manylinux_x86_64', path_str='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(version='3.6', identifier='cp36-manylinux_x86_64', path_str='/opt/python/cp36-cp36m'),
|
||||
PythonConfiguration(version='3.7', identifier='cp37-manylinux_x86_64', path_str='/opt/python/cp37-cp37m'),
|
||||
PythonConfiguration(version='3.8', identifier='cp38-manylinux_x86_64', path_str='/opt/python/cp38-cp38'),
|
||||
PythonConfiguration(version='3.9', identifier='cp39-manylinux_x86_64', path_str='/opt/python/cp39-cp39'),
|
||||
PythonConfiguration(version='2.7', identifier='cp27-manylinux_i686', path_str='/opt/python/cp27-cp27m'),
|
||||
PythonConfiguration(version='2.7', identifier='cp27-manylinux_i686', path_str='/opt/python/cp27-cp27mu'),
|
||||
PythonConfiguration(version='3.5', identifier='cp35-manylinux_i686', path_str='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(version='3.6', identifier='cp36-manylinux_i686', path_str='/opt/python/cp36-cp36m'),
|
||||
PythonConfiguration(version='3.7', identifier='cp37-manylinux_i686', path_str='/opt/python/cp37-cp37m'),
|
||||
PythonConfiguration(version='3.8', identifier='cp38-manylinux_i686', path_str='/opt/python/cp38-cp38'),
|
||||
PythonConfiguration(version='3.9', identifier='cp39-manylinux_i686', path_str='/opt/python/cp39-cp39'),
|
||||
PythonConfiguration(version='2.7', identifier='pp27-manylinux_x86_64', path_str='/opt/python/pp27-pypy_73'),
|
||||
PythonConfiguration(version='3.6', identifier='pp36-manylinux_x86_64', path_str='/opt/python/pp36-pypy36_pp73'),
|
||||
PythonConfiguration(version='3.7', identifier='pp37-manylinux_x86_64', path_str='/opt/python/pp37-pypy37_pp73'),
|
||||
PythonConfiguration(version='3.5', identifier='cp35-manylinux_aarch64', path_str='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(version='3.6', identifier='cp36-manylinux_aarch64', path_str='/opt/python/cp36-cp36m'),
|
||||
PythonConfiguration(version='3.7', identifier='cp37-manylinux_aarch64', path_str='/opt/python/cp37-cp37m'),
|
||||
PythonConfiguration(version='3.8', identifier='cp38-manylinux_aarch64', path_str='/opt/python/cp38-cp38'),
|
||||
PythonConfiguration(version='3.9', identifier='cp39-manylinux_aarch64', path_str='/opt/python/cp39-cp39'),
|
||||
PythonConfiguration(version='3.5', identifier='cp35-manylinux_ppc64le', path_str='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(version='3.6', identifier='cp36-manylinux_ppc64le', path_str='/opt/python/cp36-cp36m'),
|
||||
PythonConfiguration(version='3.7', identifier='cp37-manylinux_ppc64le', path_str='/opt/python/cp37-cp37m'),
|
||||
PythonConfiguration(version='3.8', identifier='cp38-manylinux_ppc64le', path_str='/opt/python/cp38-cp38'),
|
||||
PythonConfiguration(version='3.9', identifier='cp39-manylinux_ppc64le', path_str='/opt/python/cp39-cp39'),
|
||||
PythonConfiguration(version='3.5', identifier='cp35-manylinux_s390x', path_str='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(version='3.6', identifier='cp36-manylinux_s390x', path_str='/opt/python/cp36-cp36m'),
|
||||
PythonConfiguration(version='3.7', identifier='cp37-manylinux_s390x', path_str='/opt/python/cp37-cp37m'),
|
||||
PythonConfiguration(version='3.8', identifier='cp38-manylinux_s390x', path_str='/opt/python/cp38-cp38'),
|
||||
PythonConfiguration(version='3.9', identifier='cp39-manylinux_s390x', path_str='/opt/python/cp39-cp39'),
|
||||
]
|
||||
# skip builds as required
|
||||
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 +91,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,194 +102,147 @@ 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_project_path = PurePath('/project')
|
||||
container_package_dir = container_project_path / abs_package_dir.relative_to(cwd)
|
||||
container_output_dir = PurePath('/output')
|
||||
|
||||
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())
|
||||
|
||||
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'])
|
||||
with DockerContainer(docker_image, simulate_32_bit=platform_tag.endswith('i686'), cwd=container_project_path) as docker:
|
||||
docker.copy_into(Path.cwd(), container_project_path)
|
||||
|
||||
call(['docker', 'cp', '.', container_name + ':/project'])
|
||||
if options.before_all:
|
||||
env = docker.get_environment()
|
||||
env['PATH'] = f'/opt/python/cp38-cp38:{env["PATH"]}'
|
||||
env = options.environment.as_dictionary(env, executor=docker.environment_executor)
|
||||
|
||||
call(['docker', 'start', container_name])
|
||||
before_all_prepared = prepare_command(options.before_all, project=container_project_path, package=container_package_dir)
|
||||
docker.call(['sh', '-c', before_all_prepared], env=env)
|
||||
|
||||
for config in platform_configs:
|
||||
if options.dependency_constraints:
|
||||
constraints_file = options.dependency_constraints.get_for_python_version(config.version)
|
||||
for config in platform_configs:
|
||||
dependency_constraint_flags: List[Union[str, PathLike]] = []
|
||||
|
||||
# `docker cp` causes 'no space left on device' error when
|
||||
# a container is running and the host filesystem is
|
||||
# mounted. https://github.com/moby/moby/issues/38995
|
||||
# Use `docker exec` instead.
|
||||
with open(constraints_file, 'rb') as f:
|
||||
call(
|
||||
['docker', 'exec', '-i', container_name, 'sh', '-c', 'cat > /constraints.txt'],
|
||||
input=f.read(),
|
||||
)
|
||||
if options.dependency_constraints:
|
||||
constraints_file = options.dependency_constraints.get_for_python_version(config.version)
|
||||
container_constraints_file = PurePath('/constraints.txt')
|
||||
|
||||
call(
|
||||
['docker', 'exec', '-i', container_name] + shell_cmd,
|
||||
universal_newlines=True,
|
||||
input='''
|
||||
# give xtrace output an extra level of indent inside docker
|
||||
PS4=' + '
|
||||
docker.copy_into(constraints_file, container_constraints_file)
|
||||
dependency_constraint_flags = ['-c', container_constraints_file]
|
||||
|
||||
set -o errexit
|
||||
set -o xtrace
|
||||
mkdir -p /output
|
||||
cd /project
|
||||
env = docker.get_environment()
|
||||
|
||||
PYBIN="{config_python_bin}"
|
||||
# put this config's python top of the list
|
||||
python_bin = config.path / 'bin'
|
||||
env['PATH'] = f'{python_bin}:{env["PATH"]}'
|
||||
|
||||
export PATH="$PYBIN:$PATH"
|
||||
{environment_exports}
|
||||
env = options.environment.as_dictionary(env, executor=docker.environment_executor)
|
||||
|
||||
# check the active python and pip are in PYBIN
|
||||
if [ "$(which pip)" != "$PYBIN/pip" ]; then
|
||||
echo "cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it."
|
||||
exit 1
|
||||
fi
|
||||
if [ "$(which python)" != "$PYBIN/python" ]; then
|
||||
echo "cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it."
|
||||
exit 1
|
||||
fi
|
||||
# check config python and pip are still on PATH
|
||||
which_python = docker.call(['which', 'python'], env=env, capture_output=True).strip()
|
||||
if PurePath(which_python) != python_bin / 'python':
|
||||
print("cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it.", file=sys.stderr)
|
||||
exit(1)
|
||||
|
||||
if [ ! -z {before_build} ]; then
|
||||
sh -c {before_build}
|
||||
fi
|
||||
which_pip = docker.call(['which', 'pip'], env=env, capture_output=True).strip()
|
||||
if PurePath(which_pip) != python_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)
|
||||
|
||||
# Build the wheel
|
||||
rm -rf /tmp/built_wheel
|
||||
mkdir /tmp/built_wheel
|
||||
pip wheel {package_dir} -w /tmp/built_wheel --no-deps {build_verbosity_flag}
|
||||
built_wheel=(/tmp/built_wheel/*.whl)
|
||||
if options.before_build:
|
||||
before_build_prepared = prepare_command(options.before_build, project=container_project_path, package=container_package_dir)
|
||||
docker.call(['sh', '-c', before_build_prepared], env=env)
|
||||
|
||||
# repair the wheel
|
||||
rm -rf /tmp/repaired_wheels
|
||||
mkdir /tmp/repaired_wheels
|
||||
# NOTE: 'built_wheel' here is a bash array of glob matches; "$built_wheel" returns
|
||||
# the first element
|
||||
if [[ "$built_wheel" == *none-any.whl ]] || [ -z {repair_command} ]; then
|
||||
# pure Python wheel or empty repair command
|
||||
mv "$built_wheel" /tmp/repaired_wheels
|
||||
else
|
||||
sh -c {repair_command} repair_command "$built_wheel"
|
||||
fi
|
||||
repaired_wheels=(/tmp/repaired_wheels/*.whl)
|
||||
temp_dir = PurePath('/tmp/cibuildwheel')
|
||||
built_wheel_dir = temp_dir / 'built_wheel'
|
||||
docker.call(['rm', '-rf', built_wheel_dir])
|
||||
docker.call(['mkdir', '-p', built_wheel_dir])
|
||||
|
||||
if [ ! -z {test_command} ]; then
|
||||
# Set up a virtual environment to install and test from, to make sure
|
||||
# there are no dependencies that were pulled in at build time.
|
||||
pip install {dependency_install_flags} virtualenv
|
||||
venv_dir=`mktemp -d`/venv
|
||||
python -m virtualenv --no-download "$venv_dir"
|
||||
export __CIBW_VIRTUALENV_PATH__=$venv_dir
|
||||
docker.call([
|
||||
'pip', 'wheel',
|
||||
container_package_dir,
|
||||
'-w', built_wheel_dir,
|
||||
'--no-deps',
|
||||
*get_build_verbosity_extra_flags(options.build_verbosity)
|
||||
], env=env)
|
||||
|
||||
# run the tests in a subshell to keep that `activate`
|
||||
# script from polluting the env
|
||||
(
|
||||
source "$venv_dir/bin/activate"
|
||||
built_wheel = docker.glob(built_wheel_dir, '*.whl')[0]
|
||||
|
||||
echo "Running tests using `which python`"
|
||||
repaired_wheel_dir = temp_dir / 'repaired_wheel'
|
||||
docker.call(['rm', '-rf', repaired_wheel_dir])
|
||||
docker.call(['mkdir', '-p', repaired_wheel_dir])
|
||||
|
||||
if [ ! -z {before_test} ]; then
|
||||
sh -c {before_test}
|
||||
fi
|
||||
if built_wheel.name.endswith('none-any.whl'):
|
||||
raise NonPlatformWheelError()
|
||||
|
||||
# Install the wheel we just built
|
||||
# Note: If auditwheel produced two wheels, it's because the earlier produced wheel
|
||||
# conforms to multiple manylinux standards. These multiple versions of the wheel are
|
||||
# functionally the same, differing only in name, wheel metadata, and possibly include
|
||||
# different external shared libraries. so it doesn't matter which one we run the tests on.
|
||||
# Let's just pick the first one.
|
||||
pip install "${{repaired_wheels[0]}}"{test_extras}
|
||||
if options.repair_command:
|
||||
repair_command_prepared = prepare_command(options.repair_command, wheel=built_wheel, dest_dir=repaired_wheel_dir)
|
||||
docker.call(['sh', '-c', repair_command_prepared], env=env)
|
||||
else:
|
||||
docker.call(['mv', built_wheel, repaired_wheel_dir])
|
||||
|
||||
# Install any requirements to run the tests
|
||||
if [ ! -z "{test_requires}" ]; then
|
||||
pip install {test_requires}
|
||||
fi
|
||||
repaired_wheels = docker.glob(repaired_wheel_dir, '*.whl')
|
||||
|
||||
# Run the tests from a different directory
|
||||
pushd $HOME
|
||||
sh -c {test_command}
|
||||
popd
|
||||
)
|
||||
# exit if tests failed (needed for older bash versions)
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1;
|
||||
fi
|
||||
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.
|
||||
docker.call(['pip', 'install', 'virtualenv', *dependency_constraint_flags], env=env)
|
||||
venv_dir = PurePath(docker.call(['mktemp', '-d'], capture_output=True).strip()) / 'venv'
|
||||
|
||||
# clean up
|
||||
rm -rf "$venv_dir"
|
||||
fi
|
||||
docker.call(['python', '-m', 'virtualenv', '--no-download', venv_dir], env=env)
|
||||
|
||||
# we're all done here; move it to output
|
||||
mv "${{repaired_wheels[@]}}" /output
|
||||
for repaired_wheel in "${{repaired_wheels[@]}}"; do
|
||||
chown {uid}:{gid} "/output/$(basename "$repaired_wheel")"
|
||||
done
|
||||
'''.format(
|
||||
config_python_bin=config.path + '/bin',
|
||||
package_dir=container_package_dir,
|
||||
test_requires=' '.join(options.test_requires),
|
||||
test_extras=options.test_extras,
|
||||
test_command=shlex.quote(
|
||||
prepare_command(options.test_command, project='/project', package=container_package_dir) if options.test_command else ''
|
||||
),
|
||||
before_build=shlex.quote(
|
||||
prepare_command(options.before_build, project='/project', package=container_package_dir) if options.before_build else ''
|
||||
),
|
||||
build_verbosity_flag=' '.join(get_build_verbosity_extra_flags(options.build_verbosity)),
|
||||
repair_command=shlex.quote(
|
||||
prepare_command(options.repair_command, wheel='"$1"', dest_dir='/tmp/repaired_wheels') if options.repair_command else ''
|
||||
),
|
||||
environment_exports='\n'.join(options.environment.as_shell_commands()),
|
||||
uid=os.getuid(),
|
||||
gid=os.getgid(),
|
||||
before_test=shlex.quote(
|
||||
prepare_command(options.before_test, project='/project', package=container_package_dir) if options.before_test else ''
|
||||
),
|
||||
dependency_install_flags='-c /constraints.txt' if options.dependency_constraints else '',
|
||||
)
|
||||
)
|
||||
virtualenv_env = env.copy()
|
||||
virtualenv_env['PATH'] = f"{venv_dir / 'bin'}:{virtualenv_env['PATH']}"
|
||||
|
||||
# copy the output back into the host
|
||||
call(['docker', 'cp',
|
||||
container_name + ':/output/.',
|
||||
os.path.abspath(options.output_dir)])
|
||||
if options.before_test:
|
||||
before_test_prepared = prepare_command(options.before_test, project=container_project_path, package=container_package_dir)
|
||||
docker.call(['sh', '-c', before_test_prepared], env=virtualenv_env)
|
||||
|
||||
# Install the wheel we just built
|
||||
# Note: If auditwheel produced two wheels, it's because the earlier produced wheel
|
||||
# conforms to multiple manylinux standards. These multiple versions of the wheel are
|
||||
# functionally the same, differing only in name, wheel metadata, and possibly include
|
||||
# different external shared libraries. so it doesn't matter which one we run the tests on.
|
||||
# Let's just pick the first one.
|
||||
wheel_to_test = repaired_wheels[0]
|
||||
docker.call(['pip', 'install', str(wheel_to_test) + options.test_extras], env=virtualenv_env)
|
||||
|
||||
# Install any requirements to run the tests
|
||||
if options.test_requires:
|
||||
docker.call(['pip', 'install', *options.test_requires], env=virtualenv_env)
|
||||
|
||||
# Run the tests from a different directory
|
||||
test_command_prepared = prepare_command(options.test_command, project=container_project_path, package=container_package_dir)
|
||||
docker.call(['sh', '-c', test_command_prepared], cwd='/root', env=virtualenv_env)
|
||||
|
||||
# clean up test environment
|
||||
docker.call(['rm', '-rf', venv_dir])
|
||||
|
||||
# move repaired wheels to output
|
||||
docker.call(['mkdir', '-p', container_output_dir])
|
||||
docker.call(['mv', *repaired_wheels, container_output_dir])
|
||||
|
||||
# copy the output back into the host
|
||||
docker.copy_out(container_output_dir, options.output_dir)
|
||||
except subprocess.CalledProcessError as error:
|
||||
print(f'Command {error.cmd} failed with code {error.returncode}. {error.stdout}')
|
||||
troubleshoot(options.package_dir, error)
|
||||
exit(1)
|
||||
finally:
|
||||
# Still gets executed, even when 'exit(1)' gets called
|
||||
call(['docker', 'rm', '--force', '-v', container_name])
|
||||
|
||||
|
||||
def troubleshoot(package_dir, error):
|
||||
if (isinstance(error, subprocess.CalledProcessError) and 'exec' in error.cmd):
|
||||
# the bash script failed
|
||||
def troubleshoot(package_dir: Path, error: Exception) -> None:
|
||||
if (isinstance(error, subprocess.CalledProcessError) and error.cmd[0:2] == ['pip', 'wheel']):
|
||||
# the 'pip wheel' step 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('''
|
||||
@@ -294,8 +253,8 @@ def troubleshoot(package_dir, error):
|
||||
|
||||
If you're using Cython and have previously done an in-place build,
|
||||
remove those build files (*.so and *.c) before starting cibuildwheel.
|
||||
'''))
|
||||
'''), file=sys.stderr)
|
||||
|
||||
print(' Files detected:')
|
||||
print('\n'.join([' ' + f for f in so_files]))
|
||||
print('\n'.join([f' {f}' for f in so_files]))
|
||||
print('')
|
||||
|
||||
+98
-72
@@ -4,81 +4,86 @@ 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, NonPlatformWheelError, 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.4/python-3.8.4-macosx10.9.pkg'),
|
||||
PythonConfiguration(version='3.9', identifier='cp39-macosx_x86_64', url='https://www.python.org/ftp/python/3.9.0/python-3.9.0-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'),
|
||||
PythonConfiguration(version='2.7', identifier='pp27-macosx_x86_64', url='https://downloads.python.org/pypy/pypy2.7-v7.3.2-osx64.tar.bz2'),
|
||||
PythonConfiguration(version='3.6', identifier='pp36-macosx_x86_64', url='https://downloads.python.org/pypy/pypy3.6-v7.3.2-osx64.tar.bz2'),
|
||||
PythonConfiguration(version='3.7', identifier='pp37-macosx_x86_64', url='https://downloads.python.org/pypy/pypy3.7-v7.3.2-osx64.tar.bz2'),
|
||||
]
|
||||
|
||||
# skip builds as required
|
||||
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 +91,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 +110,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 +122,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 +145,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 +170,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 +197,43 @@ 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:
|
||||
# pure Python wheel or empty repair command
|
||||
shutil.move(built_wheel, repaired_wheel_dir)
|
||||
else:
|
||||
repaired_wheel_dir.mkdir(parents=True)
|
||||
|
||||
if built_wheel.name.endswith('none-any.whl'):
|
||||
raise NonPlatformWheelError()
|
||||
|
||||
if options.repair_command:
|
||||
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]
|
||||
else:
|
||||
shutil.move(str(built_wheel), repaired_wheel_dir)
|
||||
|
||||
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 +241,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 +253,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 +264,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 +273,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,23 +4,23 @@
|
||||
#
|
||||
# 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
|
||||
distlib==0.3.0 # via virtualenv
|
||||
contextlib2==0.6.0.post1 # via importlib-metadata, importlib-resources
|
||||
delocate==0.8.2 # via -r cibuildwheel/resources/constraints.in
|
||||
distlib==0.3.1 # via virtualenv
|
||||
filelock==3.0.12 # via virtualenv
|
||||
importlib-metadata==1.6.0 # via importlib-resources, virtualenv
|
||||
importlib-resources==1.5.0 # via virtualenv
|
||||
importlib-metadata==2.0.0 # via virtualenv
|
||||
importlib-resources==3.0.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
|
||||
typing==3.7.4.1 # via importlib-resources
|
||||
virtualenv==20.0.18 # via -r cibuildwheel/resources/constraints.in
|
||||
wheel==0.34.2 # via -r cibuildwheel/resources/constraints.in, delocate
|
||||
six==1.15.0 # via pathlib2, virtualenv
|
||||
typing==3.7.4.3 # via importlib-resources
|
||||
virtualenv==20.0.33 # via -r cibuildwheel/resources/constraints.in
|
||||
wheel==0.35.1 # 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==44.1.0 # via -r cibuildwheel/resources/constraints.in
|
||||
pip==20.2.3 # via -r cibuildwheel/resources/constraints.in
|
||||
setuptools==44.1.1 # via -r cibuildwheel/resources/constraints.in
|
||||
|
||||
@@ -4,17 +4,17 @@
|
||||
#
|
||||
# bin/update_constraints.py
|
||||
#
|
||||
appdirs==1.4.3 # via virtualenv
|
||||
delocate==0.8.0 # via -r cibuildwheel/resources/constraints.in
|
||||
distlib==0.3.0 # via virtualenv
|
||||
appdirs==1.4.4 # via virtualenv
|
||||
delocate==0.8.2 # via -r cibuildwheel/resources/constraints.in
|
||||
distlib==0.3.1 # 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
|
||||
wheel==0.34.2 # via -r cibuildwheel/resources/constraints.in, delocate
|
||||
importlib-metadata==2.0.0 # via virtualenv
|
||||
importlib-resources==3.0.0 # via virtualenv
|
||||
six==1.15.0 # via virtualenv
|
||||
virtualenv==20.0.33 # via -r cibuildwheel/resources/constraints.in
|
||||
wheel==0.35.1 # 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.2.3 # via -r cibuildwheel/resources/constraints.in
|
||||
setuptools==50.3.0 # via -r cibuildwheel/resources/constraints.in
|
||||
|
||||
@@ -4,17 +4,17 @@
|
||||
#
|
||||
# bin/update_constraints.py
|
||||
#
|
||||
appdirs==1.4.3 # via virtualenv
|
||||
delocate==0.8.0 # via -r cibuildwheel/resources/constraints.in
|
||||
distlib==0.3.0 # via virtualenv
|
||||
appdirs==1.4.4 # via virtualenv
|
||||
delocate==0.8.2 # via -r cibuildwheel/resources/constraints.in
|
||||
distlib==0.3.1 # 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
|
||||
wheel==0.34.2 # via -r cibuildwheel/resources/constraints.in, delocate
|
||||
zipp==3.1.0 # via importlib-metadata, importlib-resources
|
||||
importlib-metadata==2.0.0 # via virtualenv
|
||||
importlib-resources==3.0.0 # via virtualenv
|
||||
six==1.15.0 # via virtualenv
|
||||
virtualenv==20.0.33 # via -r cibuildwheel/resources/constraints.in
|
||||
wheel==0.35.1 # via -r cibuildwheel/resources/constraints.in, delocate
|
||||
zipp==3.3.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.2.3 # via -r cibuildwheel/resources/constraints.in
|
||||
setuptools==50.3.0 # via -r cibuildwheel/resources/constraints.in
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
#
|
||||
# This file is autogenerated by pip-compile
|
||||
# To update, run:
|
||||
#
|
||||
# bin/update_constraints.py
|
||||
#
|
||||
appdirs==1.4.4 # via virtualenv
|
||||
delocate==0.8.2 # via -r cibuildwheel/resources/constraints.in
|
||||
distlib==0.3.1 # via virtualenv
|
||||
filelock==3.0.12 # via virtualenv
|
||||
importlib-metadata==2.0.0 # via virtualenv
|
||||
six==1.15.0 # via virtualenv
|
||||
virtualenv==20.0.33 # via -r cibuildwheel/resources/constraints.in
|
||||
wheel==0.35.1 # via -r cibuildwheel/resources/constraints.in, delocate
|
||||
zipp==3.3.0 # via importlib-metadata
|
||||
|
||||
# The following packages are considered to be unsafe in a requirements file:
|
||||
pip==20.2.3 # via -r cibuildwheel/resources/constraints.in
|
||||
setuptools==50.3.0 # via -r cibuildwheel/resources/constraints.in
|
||||
@@ -4,16 +4,14 @@
|
||||
#
|
||||
# bin/update_constraints.py
|
||||
#
|
||||
appdirs==1.4.3 # via virtualenv
|
||||
delocate==0.8.0 # via -r cibuildwheel/resources/constraints.in
|
||||
distlib==0.3.0 # via virtualenv
|
||||
appdirs==1.4.4 # via virtualenv
|
||||
delocate==0.8.2 # via -r cibuildwheel/resources/constraints.in
|
||||
distlib==0.3.1 # 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
|
||||
wheel==0.34.2 # via -r cibuildwheel/resources/constraints.in, delocate
|
||||
zipp==3.1.0 # via importlib-metadata
|
||||
six==1.15.0 # via virtualenv
|
||||
virtualenv==20.0.33 # via -r cibuildwheel/resources/constraints.in
|
||||
wheel==0.35.1 # via -r cibuildwheel/resources/constraints.in, delocate
|
||||
|
||||
# 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.2.3 # via -r cibuildwheel/resources/constraints.in
|
||||
setuptools==50.3.0 # via -r cibuildwheel/resources/constraints.in
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
[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-10-06-c4a4a7c
|
||||
manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2020-10-06-786a931
|
||||
manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2020-10-06-b2ca7a1
|
||||
|
||||
[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-10-06-c4a4a7c
|
||||
manylinux2010 = quay.io/pypa/manylinux2010_i686:2020-10-06-786a931
|
||||
manylinux2014 = quay.io/pypa/manylinux2014_i686:2020-10-06-b2ca7a1
|
||||
|
||||
[pypy_x86_64]
|
||||
manylinux2010 = pypywheels/manylinux2010-pypy_x86_64:2020-04-25-eb2cdff
|
||||
manylinux2010 = pypywheels/manylinux2010-pypy_x86_64:2020-09-26-8f210b5
|
||||
|
||||
[aarch64]
|
||||
manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2020-05-01-b37d76b
|
||||
manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2020-10-06-b2ca7a1
|
||||
|
||||
[ppc64le]
|
||||
manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2020-05-01-b37d76b
|
||||
manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2020-10-06-b2ca7a1
|
||||
|
||||
[s390x]
|
||||
manylinux2014 = quay.io/pypa/manylinux2014_s390x:2020-05-01-b37d76b
|
||||
|
||||
manylinux2014 = quay.io/pypa/manylinux2014_s390x:2020-10-06-b2ca7a1
|
||||
|
||||
@@ -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'
|
||||
+65
-52
@@ -1,13 +1,16 @@
|
||||
import os
|
||||
import textwrap
|
||||
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 +20,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 +30,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 +78,67 @@ 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'
|
||||
|
||||
|
||||
class NonPlatformWheelError(Exception):
|
||||
def __init__(self) -> None:
|
||||
message = textwrap.dedent('''
|
||||
cibuildwheel: Build failed because a pure Python wheel was generated.
|
||||
|
||||
If you intend to build a pure-Python wheel, you don't need cibuildwheel - use
|
||||
`pip wheel -w DEST_DIR .` instead.
|
||||
|
||||
If you expected a platform wheel, check your project configuration, or run
|
||||
cibuildwheel with CIBW_BUILD_VERBOSITY=1 to view build logs.
|
||||
''')
|
||||
|
||||
super().__init__(message)
|
||||
|
||||
+153
-90
@@ -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, NonPlatformWheelError, 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,13 +54,16 @@ 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.4', arch='32', identifier='cp38-win32', url=None),
|
||||
PythonConfiguration(version='3.8.4', arch='64', identifier='cp38-win_amd64', url=None),
|
||||
PythonConfiguration(version='3.9.0', arch='32', identifier='cp39-win32', url=None),
|
||||
PythonConfiguration(version='3.9.0', arch='64', identifier='cp39-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'),
|
||||
PythonConfiguration(version='2.7', arch='32', identifier='pp27-win32', url='https://downloads.python.org/pypy/pypy2.7-v7.3.2-win32.zip'),
|
||||
PythonConfiguration(version='3.6', arch='32', identifier='pp36-win32', url='https://downloads.python.org/pypy/pypy3.6-v7.3.2-win32.zip'),
|
||||
PythonConfiguration(version='3.7', arch='32', identifier='pp37-win32', url='https://downloads.python.org/pypy/pypy3.7-v7.3.2-win32.zip'),
|
||||
]
|
||||
|
||||
if IS_RUNNING_ON_TRAVIS:
|
||||
@@ -62,97 +77,136 @@ 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:
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
reqfile = Path(d) / "requirements.txt"
|
||||
with reqfile.open("w") as f:
|
||||
for r in requirements:
|
||||
print(r, file=f)
|
||||
call(['pip', 'install', '-r', reqfile], 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 +218,63 @@ 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:
|
||||
# pure Python wheel or empty repair command
|
||||
shutil.move(built_wheel, repaired_wheel_dir)
|
||||
else:
|
||||
repaired_wheel_dir.mkdir(parents=True)
|
||||
|
||||
if built_wheel.name.endswith('none-any.whl'):
|
||||
raise NonPlatformWheelError()
|
||||
|
||||
if options.repair_command:
|
||||
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)
|
||||
else:
|
||||
shutil.move(str(built_wheel), repaired_wheel_dir)
|
||||
|
||||
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 +282,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)
|
||||
|
||||
@@ -28,3 +28,79 @@ cibuildwheel doesn't really do anything itself - it's always deferring to other
|
||||
We're not responsible for errors in those tools, for fixing errors/crashes there. But cibuildwheel's job is providing users with an 'integrated' user experience across those tools. We provide an abstraction. The user says 'build me some wheels', not 'open the docker container, build a wheel with pip, fix up the symbols with auditwheel' etc. However, errors have a habit of breaking abstractions. And this is where users get confused, because the mechanism of cibuildwheel is laid bare, and they must understand a little bit how it works to debug.
|
||||
|
||||
So, if we can, I'd like to improve the experience on errors as well. In [this](https://github.com/joerick/cibuildwheel/issues/139) case, it takes a bit of knowledge to understand that the linux builds are happening in a totally different OS via docker, that the linked symbols won't match, that auditwheel will fail because of this. A problem with how the tools fit together, instead of the tools themselves.
|
||||
|
||||
Maintainer notes
|
||||
----------------
|
||||
|
||||
## Testing minimal configs
|
||||
|
||||
cibuildwheel's _minimal_ example configs can be tested on a simple project on cibuildwheel's existing CI. These should be run whenever the minimal configs change.
|
||||
|
||||
To test minimal configs, make sure you have a clean git repo, then run the script:
|
||||
|
||||
```bash
|
||||
bin/run_example_ci_configs.py
|
||||
```
|
||||
|
||||
The script will create an isolated 'orphan' commit containing all the minimal config CI files, and a simple C extension project, and push that to a branch on the `origin` repo. The project's CI is already set up to run on branch push, so will begin testing.
|
||||
|
||||
The script then outputs a Markdown table that can be copy/pasted into a PR to monitor and record the test.
|
||||
|
||||
## Preparing environments
|
||||
|
||||
The dependency update script in the next section requires multiple python versions installed. One way to do this is to use `pyenv`:
|
||||
|
||||
```bash
|
||||
pyenv install 2.7.18
|
||||
pyenv install 3.5.9
|
||||
pyenv install 3.6.11
|
||||
pyenv install 3.7.8
|
||||
# Optionally add 3.8 and make it the local version;
|
||||
# otherwise assuming 3.8+ already is your current python version
|
||||
```
|
||||
|
||||
Then, you need to make the required virtual environments:
|
||||
|
||||
```bash
|
||||
$(pyenv prefix 2.7.18)/bin/python -m pip install virtualenv
|
||||
$(pyenv prefix 2.7.18)/bin/python -m virtualenv env27
|
||||
$(pyenv prefix 3.5.9)/bin/python -m venv env35
|
||||
$(pyenv prefix 3.6.11)/bin/python -m venv env36
|
||||
$(pyenv prefix 3.7.8)/bin/python -m venv env37
|
||||
```
|
||||
|
||||
<!-- Note for fish users: use zsh/bash for these lines for now, there's not a nice one-line fish replacement -->
|
||||
|
||||
And, you need to install the requirements into each environment:
|
||||
|
||||
```bash
|
||||
for f in env*/bin/pip; do $f install pip-tools; done
|
||||
```
|
||||
|
||||
|
||||
## Making a release
|
||||
|
||||
Before making a release, ensure pinned dependencies are up-to-date. Run the script:
|
||||
|
||||
```bash
|
||||
bin/make_dependency_update_pr.py
|
||||
```
|
||||
|
||||
If updates are needed, this will push a PR with those updates for the CI to test. Once green, merge this PR.
|
||||
|
||||
Then, increment the project version number using:
|
||||
|
||||
```bash
|
||||
bin/bump_version.py
|
||||
```
|
||||
|
||||
You'll be prompted to enter the new version number. Update the changelog when prompted. The script will create a 'bump version' commit and version tag.
|
||||
|
||||
Finally, cut the release and upload to PyPI/Github.
|
||||
|
||||
```bash
|
||||
rm -rf dist
|
||||
python setup.py sdist bdist_wheel
|
||||
twine upload dist/*
|
||||
git push && git push --tags
|
||||
```
|
||||
|
||||
+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
|
||||
```
|
||||
|
||||
@@ -9,14 +9,14 @@ After you've built your wheels, you'll probably want to deliver them to PyPI.
|
||||
On your development machine, do the following...
|
||||
|
||||
```bash
|
||||
# Clear out your 'dist' folder.
|
||||
# Clear out your 'dist' folder.
|
||||
rm -rf dist
|
||||
# Make a source distribution
|
||||
python setup.py sdist
|
||||
|
||||
# 🏃🏻
|
||||
# Go and download your wheel files from wherever you put them. e.g. your CI
|
||||
# provider can be configured to store them for you. Put them all into the
|
||||
# provider can be configured to store them for you. Put them all into the
|
||||
# 'dist' folder.
|
||||
|
||||
# Upload using 'twine' (you may need to 'pip install twine')
|
||||
@@ -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 automatically 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.
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@ h1, h2, h3, h4, h5, h6 {
|
||||
background-color: #eff1f3;
|
||||
font-size: 85%;
|
||||
font-weight: bold;
|
||||
|
||||
|
||||
border-top-left-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
|
||||
|
||||
@@ -2,4 +2,3 @@
|
||||
$('.rst-content pre')
|
||||
.prev('blockquote')
|
||||
.addClass('code-block-filename');
|
||||
|
||||
|
||||
+20
@@ -61,3 +61,23 @@ python3 -m cibuildwheel --output-dir wheelhouse
|
||||
python3 -m pip install twine
|
||||
python3 -m twine upload wheelhouse/*.whl
|
||||
```
|
||||
|
||||
### 'ImportError: DLL load failed: The specific module could not be found' error on Windows
|
||||
|
||||
Visual Studio and MSVC link the compiled binary wheels to the Microsoft Visual C++ Runtime. Normally, these are included with Python, but when compiling with a newer version of Visual Studio, it is possible users will run into problems on systems that do not have these runtime libraries installed. The solution is to ask users to download the corresponding Visual C++ Redistributable from the [Microsoft website](https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads) and install it. Since a Python installation normally includes these VC++ Redistributable files for [the version of the MSVC compiler used to compile Python](https://wiki.python.org/moin/WindowsCompilers), this is typically only a problem when compiling a Python 2.7 C extension with a newer compiler, e.g. to support a modern C++ standard (see [the section on modern C++ standards for Python 2.7](cpp_standards.md#windows-and-python-27) for more details).
|
||||
|
||||
Additionally, Visual Studio 2019 started linking to an even newer DLL, `VCRUNTIME140_1.dll`, besides the `VCRUNTIME140.dll` that is included with recent Python versions (starting from Python 3.5; see [here](https://wiki.python.org/moin/WindowsCompilers) for more details on the corresponding Visual Studio & MSVC versions used to compile the different Python versions). To avoid this extra dependency on `VCRUNTIME140_1.dll`, the [`/d2FH4-` flag](https://devblogs.microsoft.com/cppblog/making-cpp-exception-handling-smaller-x64/) can be added to the MSVC invocations (check out [this issue](https://github.com/joerick/cibuildwheel/issues/423) for details and references).
|
||||
|
||||
To add the `/d2FH4-` flag to a standard `setup.py` using `setuptools`, the `extra_compile_args` option can be used:
|
||||
|
||||
```python
|
||||
ext_modules=[
|
||||
Extension(
|
||||
'c_module',
|
||||
sources=['extension.c'],
|
||||
extra_compile_args=['/d2FH4-'] if sys.platform == 'win32' else []
|
||||
)
|
||||
],
|
||||
```
|
||||
|
||||
To investigate the dependencies of a C extension (i.e., the `.pyd` file, a DLL in disguise) on Windows, [Dependency Walker](http://www.dependencywalker.com/) is a great tool.
|
||||
|
||||
+2
-2
@@ -2,9 +2,9 @@
|
||||
title: Home
|
||||
---
|
||||
|
||||
{%
|
||||
{%
|
||||
includemarkdown "../README.md"
|
||||
start="<!--intro-start-->"
|
||||
start="<!--intro-start-->"
|
||||
end="<!--intro-end-->"
|
||||
%}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
+60
-33
@@ -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
|
||||
@@ -86,13 +86,16 @@ When setting the options, you can use shell-style globbing syntax (as per [`fnma
|
||||
| Python 3.6 | cp36-macosx_x86_64 | cp36-manylinux_x86_64 | cp36-manylinux_i686 | cp36-win_amd64 | cp36-win32 | cp36-manylinux_aarch64 | cp36-manylinux_ppc64le | cp36-manylinux_s390x |
|
||||
| Python 3.7 | cp37-macosx_x86_64 | cp37-manylinux_x86_64 | cp37-manylinux_i686 | cp37-win_amd64 | cp37-win32 | cp37-manylinux_aarch64 | cp37-manylinux_ppc64le | cp37-manylinux_s390x |
|
||||
| Python 3.8 | cp38-macosx_x86_64 | cp38-manylinux_x86_64 | cp38-manylinux_i686 | cp38-win_amd64 | cp38-win32 | cp38-manylinux_aarch64 | cp38-manylinux_ppc64le | cp38-manylinux_s390x |
|
||||
| PyPy 2.7 v7.3.0 | pp27-macosx_x86_64 | pp27-manylinux_x86_64 | | | pp27-win32 | | | |
|
||||
| PyPy 3.6 v7.3.0 | pp36-macosx_x86_64 | pp36-manylinux_x86_64 | | | pp36-win32 | | | |
|
||||
| Python 3.9 | cp39-macosx_x86_64 | cp39-manylinux_x86_64 | cp39-manylinux_i686 | cp39-win_amd64 | cp39-win32 | cp39-manylinux_aarch64 | cp39-manylinux_ppc64le | cp39-manylinux_s390x |
|
||||
| PyPy 2.7 v7.3.2 | pp27-macosx_x86_64 | pp27-manylinux_x86_64 | | | pp27-win32 | | | |
|
||||
| PyPy 3.6 v7.3.2 | pp36-macosx_x86_64 | pp36-manylinux_x86_64 | | | pp36-win32 | | | |
|
||||
| PyPy 3.7 (alpha) v7.3.2 | pp37-macosx_x86_64 | pp37-manylinux_x86_64 | | | pp37-win32 | | | |
|
||||
|
||||
|
||||
The list of supported and currently selected build identifiers can also be retrieved by passing the `--print-build-identifiers` flag to `cibuildwheel`.
|
||||
The format is `python_tag-platform_tag`, with tags similar to those in [PEP 425](https://www.python.org/dev/peps/pep-0425/#details).
|
||||
|
||||
For CPython, the minimally supported macOS version is 10.9; for PyPy 2.7 and PyPy 3.6, respectively macOS 10.7 and 10.13 or higher is required.
|
||||
For CPython, the minimally supported macOS version is 10.9; for PyPy 2.7 and PyPy 3.6/3.7, respectively macOS 10.7 and 10.13 or higher is required.
|
||||
|
||||
#### Examples
|
||||
|
||||
@@ -155,7 +158,6 @@ CIBW_SKIP: pp*
|
||||
|
||||
## Build customization
|
||||
|
||||
|
||||
### `CIBW_ENVIRONMENT` {: #environment}
|
||||
> Set environment variables needed during the build
|
||||
|
||||
@@ -191,6 +193,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 +320,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 +397,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 +465,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 +628,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-ci-deploy.yml`](https://github.com/joerick/cibuildwheel/blob/master/examples/travis-ci-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,
|
||||
@@ -62,6 +66,19 @@ Commit this file, enable building of your repo on CircleCI, and push.
|
||||
|
||||
CircleCI will store the built wheels for you - you can access them from the project console. Check out the CircleCI [docs](https://circleci.com/docs/2.0/configuration-reference/#section=configuration) for more info on this config file.
|
||||
|
||||
# Gitlab CI [linux] {: #gitlab-ci}
|
||||
|
||||
To build Linux wheels on Gitlab CI, create a `.gitlab-ci.yml` file in your repo,
|
||||
|
||||
> .gitlab-ci.yml
|
||||
```yaml
|
||||
{% include "../examples/gitlab-minimal.yml" %}
|
||||
```
|
||||
|
||||
Commit this file, and push to Gitlab. The pipeline should start automatically.
|
||||
|
||||
Gitlab will store the built wheels for you - you can access them from the Pipelines view. Check out the Gitlab [docs](https://docs.gitlab.com/ee/ci/yaml/) for more info on this config file.
|
||||
|
||||
# AppVeyor [linux/mac/windows] {: #appveyor}
|
||||
|
||||
To build Linux, Mac, and Windows wheels on AppVeyor, create an `appveyor.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.6.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.6.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.6.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.6.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.6.2
|
||||
cibuildwheel --output-dir wheelhouse
|
||||
- store_artifacts:
|
||||
path: wheelhouse/
|
||||
@@ -19,13 +19,14 @@ jobs:
|
||||
osx-wheels:
|
||||
working_directory: ~/osx-wheels
|
||||
macos:
|
||||
xcode: "10.0.0"
|
||||
# PyPy 7.3.2 needs macOS >= 10.14
|
||||
xcode: 10.2.1
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Build the OS X wheels.
|
||||
command: |
|
||||
pip3 install --user cibuildwheel==1.4.1
|
||||
pip3 install cibuildwheel==1.6.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.6.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.6.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
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
linux:
|
||||
image: python:3.8
|
||||
# make a docker daemon available for cibuildwheel to use
|
||||
services:
|
||||
- name: docker:dind
|
||||
entrypoint: ["env", "-u", "DOCKER_HOST"]
|
||||
command: ["dockerd-entrypoint.sh"]
|
||||
variables:
|
||||
DOCKER_HOST: tcp://docker:2375/
|
||||
DOCKER_DRIVER: overlay2
|
||||
# See https://github.com/docker-library/docker/pull/166
|
||||
DOCKER_TLS_CERTDIR: ""
|
||||
script:
|
||||
- curl -sSL https://get.docker.com/ | sh
|
||||
- python -m pip install cibuildwheel
|
||||
- cibuildwheel --output-dir wheelhouse
|
||||
artifacts:
|
||||
paths:
|
||||
- wheelhouse/
|
||||
@@ -9,6 +9,8 @@ jobs:
|
||||
- services: docker
|
||||
# and a mac build
|
||||
- os: osx
|
||||
# PyPy 7.3.2 needs macOS >= 10.14
|
||||
osx_image: xcode10.2
|
||||
language: shell
|
||||
# and a windows build
|
||||
- os: windows
|
||||
@@ -25,7 +27,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.6.2
|
||||
|
||||
script:
|
||||
# build the wheels, put them into './wheelhouse'
|
||||
@@ -15,6 +15,8 @@ jobs:
|
||||
arch: s390x
|
||||
# and a mac build
|
||||
- os: osx
|
||||
# PyPy 7.3.2 needs macOS >= 10.14
|
||||
osx_image: xcode10.2
|
||||
language: shell
|
||||
# and a windows build
|
||||
- os: windows
|
||||
@@ -26,7 +28,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.6.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.6.2
|
||||
script: python3 -m cibuildwheel --output-dir wheelhouse
|
||||
after_success: |
|
||||
python3 -m pip install twine
|
||||
@@ -65,8 +64,10 @@ jobs:
|
||||
- stage: deploy
|
||||
name: Build and deploy macOS wheels
|
||||
os: osx
|
||||
# PyPy 7.3.2 needs macOS >= 10.14
|
||||
osx_image: xcode10.2
|
||||
language: shell
|
||||
install: python3 -m pip install cibuildwheel==1.4.1
|
||||
install: python3 -m pip install cibuildwheel==1.6.2
|
||||
script: python3 -m cibuildwheel --output-dir wheelhouse
|
||||
after_success: |
|
||||
python3 -m pip install twine
|
||||
@@ -76,7 +77,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.6.2
|
||||
script: python3 -m cibuildwheel --output-dir wheelhouse
|
||||
after_success: |
|
||||
python3 -m pip install twine
|
||||
|
||||
@@ -6,3 +6,5 @@ pymdown-extensions
|
||||
pip-tools
|
||||
requests
|
||||
click
|
||||
mypy
|
||||
typing-extensions
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
[flake8]
|
||||
ignore = E501,W503
|
||||
ignore = E501,W503,E741,E226,B950
|
||||
select = C,E,F,W,B,B9
|
||||
application-import-names = cibuildwheel
|
||||
exclude =
|
||||
cibuildwheel/resources/,
|
||||
@@ -11,3 +12,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,26 +1,28 @@
|
||||
# -*- 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.6.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',
|
||||
author="Joe Rickerby",
|
||||
author_email='joerick@mac.com',
|
||||
url='https://github.com/joerick/cibuildwheel',
|
||||
project_urls={
|
||||
'Changelog': 'https://github.com/joerick/cibuildwheel#changelog',
|
||||
'Documentation': 'https://cibuildwheel.readthedocs.io/',
|
||||
},
|
||||
packages=['cibuildwheel', ],
|
||||
license="BSD",
|
||||
zip_safe=False,
|
||||
@@ -28,7 +30,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,26 +0,0 @@
|
||||
import os
|
||||
import platform
|
||||
|
||||
import pytest
|
||||
|
||||
import utils
|
||||
|
||||
|
||||
def test():
|
||||
project_dir = os.path.dirname(__file__)
|
||||
|
||||
if utils.platform != 'linux':
|
||||
pytest.skip('the test is only relevant to the linux build')
|
||||
if platform.machine() not in ['x86_64', 'i686']:
|
||||
pytest.skip('this test is currently only possible on x86_64/i686 due to availability of alternative images')
|
||||
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
||||
'CIBW_MANYLINUX_X86_64_IMAGE': 'dockcross/manylinux2010-x64',
|
||||
'CIBW_MANYLINUX_I686_IMAGE': 'dockcross/manylinux2010-x86',
|
||||
'CIBW_SKIP': 'pp*',
|
||||
})
|
||||
|
||||
# also check that we got the right wheels built
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if '-pp' not in w]
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
@@ -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'))
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user