Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40ff745321 | ||
|
|
8f9a68fe94 | ||
|
|
840da16d39 | ||
|
|
b797430d21 | ||
|
|
c22d50427c | ||
|
|
ce85c8730d | ||
|
|
34f8427aab | ||
|
|
bb12d3dc31 | ||
|
|
77fb97caf3 | ||
|
|
23870b6780 | ||
|
|
4caee57c21 | ||
|
|
7e566e46ee | ||
|
|
9ae2ae4ac6 | ||
|
|
42489436a2 | ||
|
|
4628c1334e | ||
|
|
18b4d42f83 | ||
|
|
ff99cb37f8 | ||
|
|
d0e5781832 | ||
|
|
7dfc173bca | ||
|
|
79cf7e47ae | ||
|
|
99dc079380 | ||
|
|
696067f19e | ||
|
|
e934c8219e | ||
|
|
8de326a67a | ||
|
|
c33fa2ada0 | ||
|
|
6265d87d09 | ||
|
|
baba7706e0 | ||
|
|
d613a8c1a6 | ||
|
|
01a7c2d6df | ||
|
|
6461074ec9 | ||
|
|
d9e6bbfa4f | ||
|
|
4e9316036c | ||
|
|
93913ef2fa | ||
|
|
7903ff60f9 | ||
|
|
028c1dbf10 | ||
|
|
ce5fc12b5f | ||
|
|
07fae7e0e5 | ||
|
|
d0692932fb | ||
|
|
1238c9d7e5 | ||
|
|
38fef4e1d8 | ||
|
|
863829f67b | ||
|
|
4456d51e03 | ||
|
|
ec42cb6f42 | ||
|
|
850db69eb7 | ||
|
|
4e3cef81d7 | ||
|
|
83452b75dd | ||
|
|
be9eae6828 | ||
|
|
281caf3d2e | ||
|
|
6e41e729fe | ||
|
|
e0c456604d | ||
|
|
01dd18bbf8 | ||
|
|
774af30d71 | ||
|
|
958fa1b824 | ||
|
|
8d8b91a9f6 | ||
|
|
bae011950c | ||
|
|
9275e7431a | ||
|
|
16e23410b4 | ||
|
|
d32055e8a9 | ||
|
|
1199d78d88 | ||
|
|
be72b8b85d | ||
|
|
12f0eff609 | ||
|
|
4bc7e4f40d | ||
|
|
a68f8b23e5 | ||
|
|
6ad0aebaf8 | ||
|
|
7d0cc37b17 | ||
|
|
05df9bbfd4 | ||
|
|
f8d2982166 | ||
|
|
36e0618992 | ||
|
|
549fc8a93e | ||
|
|
5549eb3165 | ||
|
|
7b906fb5dc | ||
|
|
90da096b14 | ||
|
|
43c45b6011 | ||
|
|
6ddac481c0 | ||
|
|
a83ac06c8d | ||
|
|
ec4ea4e011 | ||
|
|
ec5670a476 | ||
|
|
ce76b74251 | ||
|
|
65792bd700 | ||
|
|
d14427d781 | ||
|
|
2ce304d5d9 | ||
|
|
42a231f830 | ||
|
|
4f47ab8052 | ||
|
|
cf73c3355e | ||
|
|
0d2bec3cb8 | ||
|
|
60dd3f0807 | ||
|
|
8c6abf3811 | ||
|
|
fc0dbe15d5 | ||
|
|
0423d79e63 | ||
|
|
fd0a789988 | ||
|
|
1e317895fe | ||
|
|
b743b8ae5b | ||
|
|
176f4aca1f | ||
|
|
60f198c90d | ||
|
|
11423a47f6 | ||
|
|
aac5db8a46 | ||
|
|
17137c3c2f | ||
|
|
bafb4fd294 | ||
|
|
fea54e25ab | ||
|
|
4c718706c8 | ||
|
|
08d3ee1d91 | ||
|
|
e80f7f4774 | ||
|
|
186e09d543 | ||
|
|
ae6832e18d | ||
|
|
743051a453 | ||
|
|
129cf601da | ||
|
|
dd26ef8ac6 | ||
|
|
ef80beb433 | ||
|
|
100fc14411 | ||
|
|
cf8fa693b8 | ||
|
|
d9948da29a | ||
|
|
c738481e82 | ||
|
|
c15d656726 | ||
|
|
cab6ecef24 | ||
|
|
1e420a9b57 | ||
|
|
691174cf11 | ||
|
|
0b0830fbbb | ||
|
|
165e51aec5 | ||
|
|
48055273f1 | ||
|
|
e8d121552c | ||
|
|
67ec8fe99f | ||
|
|
b900b2d51e | ||
|
|
9fe8d29545 | ||
|
|
692c64ffd9 | ||
|
|
67022e620e | ||
|
|
ffec9aecf8 | ||
|
|
17a224c525 | ||
|
|
4bc851ddf2 | ||
|
|
0124d174be | ||
|
|
8236877f02 | ||
|
|
c3f628d03d | ||
|
|
a97b6a13f1 | ||
|
|
a143ff2bfd | ||
|
|
1a7d1d8cb1 | ||
|
|
68d688c016 | ||
|
|
88029d2a28 | ||
|
|
513ef49cd4 | ||
|
|
b779a88fde | ||
|
|
379cf2dc13 | ||
|
|
b64b106fbd | ||
|
|
973ecf53b3 | ||
|
|
186599a1f0 | ||
|
|
aca7611fc4 | ||
|
|
9d44d9edf0 | ||
|
|
2473c405bc | ||
|
|
984df80ec9 | ||
|
|
0d5a2a7843 | ||
|
|
1f784a1a29 | ||
|
|
e64916fe9c | ||
|
|
9cb796edcd | ||
|
|
758f158bbd | ||
|
|
d17517902e | ||
|
|
200d4e3b8c | ||
|
|
9648b44bdf | ||
|
|
492b5f4039 | ||
|
|
651f6a9172 | ||
|
|
fabb82e9cf | ||
|
|
8bfe9ac6cd | ||
|
|
de55153931 | ||
|
|
a9c968868a | ||
|
|
05e7858815 | ||
|
|
5e33011229 | ||
|
|
0b707b22ed | ||
|
|
d66e201c3f | ||
|
|
6732812ad7 | ||
|
|
cbb7eafd1f | ||
|
|
be721153af | ||
|
|
ef67ddd351 | ||
|
|
0af635fb86 | ||
|
|
32182f3edc | ||
|
|
ebcda4d049 | ||
|
|
dd9b665c1d | ||
|
|
e00f03047a | ||
|
|
e0b385a520 | ||
|
|
a1832ae6c6 | ||
|
|
795413e5c0 | ||
|
|
eda3dc1a4f | ||
|
|
472fa32d9f | ||
|
|
2f41683e85 | ||
|
|
7709989603 | ||
|
|
31e67c4f1f | ||
|
|
11bde36627 | ||
|
|
5941ba7daf | ||
|
|
c1df977e11 | ||
|
|
1dbb12daaa | ||
|
|
a005e263fc | ||
|
|
2a1081c5e5 | ||
|
|
9c908daf27 | ||
|
|
054be2eff2 | ||
|
|
20ba988b72 | ||
|
|
190b013c61 | ||
|
|
c966c6d098 | ||
|
|
d2700bc3b4 | ||
|
|
09e72800c6 | ||
|
|
2b62eef9e7 | ||
|
|
85cba9b1eb | ||
|
|
402ec9e4ac | ||
|
|
e9fd9eaead | ||
|
|
0e04fafd3a | ||
|
|
34515b78c6 | ||
|
|
7d99ccfc7b | ||
|
|
dc01231301 | ||
|
|
96a2505e78 | ||
|
|
d3fd087166 | ||
|
|
b3f82d52cc | ||
|
|
7d90a2f16b | ||
|
|
f4318e15b7 | ||
|
|
d3450e84ac | ||
|
|
a3e9b7b8ec | ||
|
|
1a9c6a5f66 | ||
|
|
c44a084a44 | ||
|
|
1e102b9bc5 | ||
|
|
46f1906e2d | ||
|
|
79c480b896 | ||
|
|
734ca6bfbd | ||
|
|
beda529298 | ||
|
|
292bf42cb0 | ||
|
|
a8d1e69783 | ||
|
|
ab53f06bc6 | ||
|
|
053a154d1d | ||
|
|
7d08bac099 | ||
|
|
8e274700f4 | ||
|
|
22ae365ae9 | ||
|
|
ccd22e5a5a | ||
|
|
2692162617 | ||
|
|
d112b343e1 | ||
|
|
7ac8798d26 | ||
|
|
a41ce8e4a0 | ||
|
|
07fd1a3203 | ||
|
|
81ce8116b5 | ||
|
|
c82330b0bc | ||
|
|
50a518eac4 | ||
|
|
42d1610d52 | ||
|
|
ac51d22884 | ||
|
|
c42cca1c86 | ||
|
|
b51434b5a8 | ||
|
|
7dee79a96b | ||
|
|
ce93a03098 | ||
|
|
319aee73f2 | ||
|
|
5210ad3610 | ||
|
|
def8f9a2e3 | ||
|
|
f716f87a04 | ||
|
|
0375e92110 | ||
|
|
560c1adeb7 | ||
|
|
3c9875db19 | ||
|
|
89e2ac7ebf | ||
|
|
05ab66cf97 | ||
|
|
104dcf1d37 | ||
|
|
f2ef7a6169 | ||
|
|
58048b4bb1 | ||
|
|
332674a382 | ||
|
|
88ef908bad | ||
|
|
eb8c926eba | ||
|
|
73cba728a1 | ||
|
|
1446529f7c | ||
|
|
51649fd806 | ||
|
|
f55b663ea7 | ||
|
|
2360c406bf | ||
|
|
5db3763b2d | ||
|
|
bb0cad404b | ||
|
|
a6164f765c | ||
|
|
892c7b4d9c | ||
|
|
3fcb902fd9 | ||
|
|
8c9345fa1f | ||
|
|
5eab3620c9 | ||
|
|
80974e5b39 | ||
|
|
4f705b7cc9 | ||
|
|
e0eb375449 | ||
|
|
bbecbe43ec | ||
|
|
46865a5f40 | ||
|
|
b577c5f00f | ||
|
|
ee2a90880a | ||
|
|
31d89377da | ||
|
|
0c33a58579 | ||
|
|
3842cfd853 | ||
|
|
2e0dc40e3f | ||
|
|
c6f1382813 | ||
|
|
1220fbbd4b | ||
|
|
179148b0e5 | ||
|
|
4536b23a1d | ||
|
|
4571262803 | ||
|
|
27517cd8a0 | ||
|
|
54dd72de66 | ||
|
|
7978247fa7 | ||
|
|
7cadc6374a | ||
|
|
e194dad0da | ||
|
|
5f416b194a | ||
|
|
2a244616ad | ||
|
|
bfa3eebb7f | ||
|
|
b0c2fd8af2 | ||
|
|
cf90846161 | ||
|
|
89bb2d352d | ||
|
|
c1572e4ff4 | ||
|
|
1741d7ce1f | ||
|
|
05385b1cb0 | ||
|
|
3ba7aa3532 | ||
|
|
bb2a5f8d17 | ||
|
|
44d0dcba12 | ||
|
|
4c86af69a5 | ||
|
|
93663d19df | ||
|
|
c006fdab36 | ||
|
|
d280f0f16a | ||
|
|
b49754dc7d | ||
|
|
3302aa136b | ||
|
|
14d9b4091e | ||
|
|
aeeac6725a | ||
|
|
41b4b092b1 | ||
|
|
28186bf142 | ||
|
|
4e10d9d897 | ||
|
|
c963013858 | ||
|
|
9470ca623f | ||
|
|
2e3bc016b4 | ||
|
|
d4732130a7 | ||
|
|
853699c265 | ||
|
|
40968897a8 | ||
|
|
fdc2ba8715 | ||
|
|
3ff59eefc6 | ||
|
|
e3416abeb9 | ||
|
|
2ab2d079ab | ||
|
|
5e529969ea | ||
|
|
b7a689e349 | ||
|
|
4f4fed06e0 | ||
|
|
45fcebf9d8 | ||
|
|
e67b3bc611 | ||
|
|
d8456a0ffa | ||
|
|
73681d9b24 | ||
|
|
02ed7b2262 | ||
|
|
ab86b8cd93 | ||
|
|
3a03df8ec9 | ||
|
|
0e78b2b1f5 | ||
|
|
5b14c8c0ec | ||
|
|
0627bd09d4 | ||
|
|
a5e64e216c | ||
|
|
23cf50614f | ||
|
|
1e08ff03b3 | ||
|
|
2216385c0b | ||
|
|
a1fc0cb117 | ||
|
|
430b71c767 | ||
|
|
14f14a24d3 | ||
|
|
b732026734 | ||
|
|
0d85db7111 | ||
|
|
477fc7e060 | ||
|
|
17566cdee7 | ||
|
|
49bc742add | ||
|
|
4cbb67fac1 | ||
|
|
2e3155decf | ||
|
|
719352b1d2 | ||
|
|
f7d93cc9e4 | ||
|
|
3f5572e956 | ||
|
|
bab8b6668e | ||
|
|
f29697d623 | ||
|
|
e87032c830 | ||
|
|
e360a04dc1 | ||
|
|
9050137d2b | ||
|
|
857aafbd3f | ||
|
|
0cd3e426bc | ||
|
|
8b632bcb6d | ||
|
|
364fc5220e | ||
|
|
0ed37f268a | ||
|
|
f410fbdd03 | ||
|
|
21fb27faf7 | ||
|
|
ac4b62ca07 | ||
|
|
3f7a4d753f | ||
|
|
95d2086ced | ||
|
|
52e8f41b5c | ||
|
|
1e2a2e7d4d | ||
|
|
6377854545 | ||
|
|
be57269d5d | ||
|
|
d5856884a3 | ||
|
|
c2d3dc3b0f | ||
|
|
9116217b60 | ||
|
|
a8f6684bfd | ||
|
|
6852d87d2e | ||
|
|
054c273997 | ||
|
|
652d094e46 | ||
|
|
1bfe6d45bd | ||
|
|
aefde9535b | ||
|
|
d46fb62e95 | ||
|
|
87a9d170b0 | ||
|
|
dcce949d3c | ||
|
|
1d3c59d804 | ||
|
|
0b791dbea5 | ||
|
|
44256e4aa5 | ||
|
|
243103762b | ||
|
|
b579cd5f2a | ||
|
|
8523b25a18 | ||
|
|
637a251b79 | ||
|
|
ac76af4f75 | ||
|
|
a6ff864010 | ||
|
|
79f044314b | ||
|
|
0c0e21aa83 | ||
|
|
f1bba58f86 | ||
|
|
56218d896c | ||
|
|
d79ca07bbb | ||
|
|
c6f4f01732 | ||
|
|
586b3c7197 | ||
|
|
0b4475ed07 | ||
|
|
38b3b0a3da | ||
|
|
5adfce9838 | ||
|
|
bc83839387 | ||
|
|
481b1cb060 | ||
|
|
ccfc3c3aa1 | ||
|
|
3d23cbf2a6 | ||
|
|
b9a296324d | ||
|
|
c4822ebdd6 | ||
|
|
2ca6500bba | ||
|
|
8dd71189b2 | ||
|
|
a448c02d7f | ||
|
|
696866bfe1 | ||
|
|
1faa2fe5df | ||
|
|
602acebcc8 | ||
|
|
c43f1b6e3d | ||
|
|
0d3e67b1f9 | ||
|
|
e3b565320c | ||
|
|
56973d34df | ||
|
|
66e9a1cc19 | ||
|
|
2a91396064 | ||
|
|
1c98a9ed22 | ||
|
|
dbcf5ef64b | ||
|
|
cc030312a3 | ||
|
|
31c162fa59 | ||
|
|
d80636aa22 | ||
|
|
22a6f0c7a9 | ||
|
|
7362075b61 | ||
|
|
c108807e16 | ||
|
|
05a054245a | ||
|
|
a5f89fa7aa | ||
|
|
cf73851c61 | ||
|
|
9372e37b18 | ||
|
|
fbb447c3aa | ||
|
|
1e978d1b35 | ||
|
|
1dbda7af7e | ||
|
|
b64770a560 | ||
|
|
4cc09a35b4 | ||
|
|
d4885fe44a | ||
|
|
fcf1e9cd84 | ||
|
|
f417a91a57 | ||
|
|
1b9c74aabc | ||
|
|
35832c4309 | ||
|
|
c9fffab6ea | ||
|
|
9f7fdbf456 | ||
|
|
7c6b331e15 | ||
|
|
22f69e4ced | ||
|
|
4083a7600a | ||
|
|
20624e44a3 | ||
|
|
332b8ec6ac | ||
|
|
eaf59e385c | ||
|
|
57454e75b0 | ||
|
|
391c95d355 | ||
|
|
8124b14491 | ||
|
|
b3b15f8404 | ||
|
|
9a96910919 | ||
|
|
fce00dd77e | ||
|
|
6da52a544b | ||
|
|
c8037fb237 | ||
|
|
95a81f2216 | ||
|
|
56c9c9864d | ||
|
|
0789842ef0 | ||
|
|
5c47700d31 | ||
|
|
5ef7eeba4d | ||
|
|
bb554acb28 | ||
|
|
ea3c52631f | ||
|
|
b18281867b | ||
|
|
4a0d05df49 | ||
|
|
c1cd99ee4c | ||
|
|
e68400b403 | ||
|
|
2f1f34f693 | ||
|
|
3ef401b19e | ||
|
|
8800ea514b | ||
|
|
f62b9755e1 | ||
|
|
033ba29ec6 | ||
|
|
0627b38402 | ||
|
|
189dc13714 | ||
|
|
793026f3ed | ||
|
|
08b4895b9c | ||
|
|
7c635b4550 | ||
|
|
33433f0b21 | ||
|
|
e3981db2a6 | ||
|
|
1ec652dff8 | ||
|
|
d2394e9a57 | ||
|
|
0b21df216e | ||
|
|
e5b9d1f586 | ||
|
|
9b31b5e6c9 | ||
|
|
1a910257bb | ||
|
|
4f542ca3d4 | ||
|
|
57a2259a00 | ||
|
|
d186b0bbb7 | ||
|
|
57cdb8711c | ||
|
|
7448ca3cf1 | ||
|
|
0156a9a870 | ||
|
|
460a4d9656 | ||
|
|
b47a5c018f | ||
|
|
94e33cd7e3 | ||
|
|
c9c54602ed | ||
|
|
b74333a940 | ||
|
|
4655311a7a | ||
|
|
5099d60584 | ||
|
|
2ebde4c078 | ||
|
|
4d61b08fa3 | ||
|
|
5e009e53c9 | ||
|
|
783a4deec6 | ||
|
|
529e22bfe0 | ||
|
|
418cb9c39d |
@@ -0,0 +1,69 @@
|
||||
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
|
||||
|
||||
osx-python3.7:
|
||||
macos:
|
||||
xcode: "10.0.0"
|
||||
environment:
|
||||
PYTHON: python3
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
- run:
|
||||
name: Prepare the environment.
|
||||
command: bash .circleci/prepare.sh
|
||||
- run:
|
||||
name: Test.
|
||||
command: venv/bin/python ./bin/run_tests.py
|
||||
|
||||
linux-python3.6:
|
||||
docker:
|
||||
- image: circleci/python:3.6
|
||||
environment:
|
||||
PYTHON: python3
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker
|
||||
|
||||
- run:
|
||||
name: Prepare the environment.
|
||||
command: bash .circleci/prepare.sh
|
||||
- run:
|
||||
name: Test.
|
||||
command: venv/bin/python ./bin/run_tests.py
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
all-tests:
|
||||
jobs:
|
||||
- flake8
|
||||
- osx-python3.6
|
||||
- osx-python3.7
|
||||
- linux-python3.6
|
||||
@@ -0,0 +1,7 @@
|
||||
$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
|
||||
venv/bin/python --version
|
||||
@@ -0,0 +1,14 @@
|
||||
# http://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
# Use 4 spaces the Python files
|
||||
[*.py]
|
||||
indent_size = 4
|
||||
@@ -0,0 +1,29 @@
|
||||
name: Test
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Test cibuildwheel on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-18.04, windows-latest, macos-latest]
|
||||
python_version: ['3.7']
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/setup-python@v1
|
||||
name: Install Python ${{ matrix.python_version }}
|
||||
with:
|
||||
python-version: ${{ matrix.python_version }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install -r requirements-dev.txt
|
||||
- name: Install Visual C++ for Python 2.7
|
||||
if: startsWith(matrix.os, 'windows')
|
||||
run: |
|
||||
choco install vcpython27 -f -y
|
||||
- name: Test cibuildwheel
|
||||
run: |
|
||||
python ./bin/run_tests.py
|
||||
@@ -86,9 +86,11 @@ celerybeat-schedule
|
||||
.venv
|
||||
venv/
|
||||
venv3/
|
||||
venv2/
|
||||
ENV/
|
||||
env/
|
||||
env3/
|
||||
env2/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
# Read the Docs configuration file
|
||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||
version: 2
|
||||
|
||||
mkdocs:
|
||||
configuration: mkdocs.yml
|
||||
|
||||
python:
|
||||
version: 3.7
|
||||
install:
|
||||
- requirements: requirements-dev.txt
|
||||
+42
-37
@@ -1,46 +1,51 @@
|
||||
language: generic
|
||||
language: minimal
|
||||
|
||||
matrix:
|
||||
jobs:
|
||||
include:
|
||||
# Linux Python 2
|
||||
- sudo: required
|
||||
- name: Linux | x86_64 + i686 | Python 3.5
|
||||
language: python
|
||||
services:
|
||||
- docker
|
||||
env:
|
||||
- "PYTHON=python2"
|
||||
python: 3.5
|
||||
services: docker
|
||||
env: PYTHON=python
|
||||
|
||||
# Linux Python 3
|
||||
- sudo: required
|
||||
- name: Linux | arm64 | Python 3.5
|
||||
language: python
|
||||
services:
|
||||
- docker
|
||||
env:
|
||||
- "PYTHON=python3"
|
||||
python: 3.5
|
||||
services: docker
|
||||
arch: arm64
|
||||
env: PYTHON=python
|
||||
|
||||
- name: Linux | ppc64le | Python 3.5
|
||||
language: python
|
||||
python: 3.5
|
||||
services: docker
|
||||
arch: ppc64le
|
||||
env: PYTHON=python
|
||||
|
||||
- name: macOS | x86_64 | Python 3.x
|
||||
os: osx
|
||||
env: PYTHON=python3
|
||||
|
||||
- name: Windows | x86_64 | Python 3.5
|
||||
os: windows
|
||||
language: shell
|
||||
before_install:
|
||||
- sudo apt-get -qq update
|
||||
- sudo apt-get install -y python3-pip
|
||||
|
||||
# macOS Python 2
|
||||
- os: osx
|
||||
- choco install python3 --version 3.5.4 --no-progress -y
|
||||
env:
|
||||
- "PYTHON=python2"
|
||||
- PYTHON=C:\\Python35\\python
|
||||
|
||||
# macOS Python 3
|
||||
- os: osx
|
||||
env:
|
||||
- "PYTHON=python3"
|
||||
before_install:
|
||||
- brew update
|
||||
- brew outdated python || brew upgrade python
|
||||
- &linux_s390x_35
|
||||
name: Linux | s390x | Python 3.5
|
||||
language: python
|
||||
python: 3.5
|
||||
services: docker
|
||||
arch: s390x
|
||||
env: PYTHON=python
|
||||
|
||||
script:
|
||||
- |
|
||||
if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
|
||||
$PYTHON -m pip install -r requirements-dev.txt
|
||||
$PYTHON ./bin/run_tests.py
|
||||
else
|
||||
# linux test requires root to clean up the wheelhouse (docker runs as root)
|
||||
sudo $PYTHON -m pip install -r requirements-dev.txt
|
||||
sudo $PYTHON ./bin/run_tests.py
|
||||
fi
|
||||
allow_failures:
|
||||
# must repeat the s390x job above exactly to match
|
||||
- *linux_s390x_35
|
||||
|
||||
install: $PYTHON -m pip install -r requirements-dev.txt
|
||||
|
||||
script: $PYTHON ./bin/run_tests.py
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
This is a summary of the Python versions and platforms covered by the different CI platforms:
|
||||
|
||||
| | 3.5 | 3.6 | 3.7 | 3.8 |
|
||||
|----------|------------------|------------------|---------------------------------------------------|------------------|
|
||||
| Linux | Travis CI | CircleCI | AppVeyor / GitHub Actions | Azure Pipelines |
|
||||
| macOS | Azure Pipelines | CircleCI | AppVeyor / Travis CI¹ / CircleCI / GitHub Actions | Azure Pipelines |
|
||||
| Windows | TravisCI | Azure Pipelines | AppVeyor / GitHub Actions | Azure Pipelines |
|
||||
|
||||
> ¹ Python version not really pinned, but dependent on the (default) version of image used.
|
||||
|
||||
Non-x86 architectures are covered on Travis CI using Python 3.5.
|
||||
@@ -1,6 +1,6 @@
|
||||
This project is licensed under the 'BSD 2-clause license'.
|
||||
|
||||
Copyright (c) 2017, Joe Rickerby. All rights reserved.
|
||||
Copyright (c) 2017-2019, Joe Rickerby and contributors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
@@ -1,431 +1,415 @@
|
||||
cibuildwheel
|
||||
============
|
||||
|
||||
[](https://pypi.python.org/pypi/cibuildwheel) [](https://travis-ci.org/joerick/cibuildwheel) [](https://ci.appveyor.com/project/joerick/cibuildwheel/branch/master)
|
||||
[](https://pypi.python.org/pypi/cibuildwheel) [](https://cibuildwheel.readthedocs.io/en/stable/?badge=stable) [](https://travis-ci.org/joerick/cibuildwheel) [](https://ci.appveyor.com/project/joerick/cibuildwheel/branch/master) [](https://circleci.com/gh/joerick/cibuildwheel) [](https://dev.azure.com/joerick0429/cibuildwheel/_build/latest?definitionId=2&branchName=master)
|
||||
|
||||
[Documentation](https://cibuildwheel.readthedocs.org)
|
||||
|
||||
<!--intro-start-->
|
||||
|
||||
Python wheels are great. Building them across **Mac, Linux, Windows**, on **multiple versions of Python**, is not.
|
||||
|
||||
`cibuildwheel` is here to help. `cibuildwheel` runs on your CI server - currently it supports Travis CI and Appveyor - and it builds and tests your wheels across all of your platforms.
|
||||
`cibuildwheel` is here to help. `cibuildwheel` runs on your CI server - currently it supports Azure Pipelines, Travis CI, AppVeyor, GitHub Actions and CircleCI - and it builds and tests your wheels across all of your platforms.
|
||||
|
||||
**`cibuildwheel` is in beta**. It's brand new - I'd love for you to try it and help make it better!
|
||||
|
||||
What does it do?
|
||||
----------------
|
||||
|
||||
| | macOS 10.6+ | manylinux i686 | manylinux x86_64 | Windows 32bit | Windows 64bit |
|
||||
|---|---|---|---|---|---|
|
||||
| Python 2.7 | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| Python 3.3 | | | | ✅ | ✅ |
|
||||
| Python 3.4 | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| Python 3.5 | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| Python 3.6 | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| | 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 | ✅ | | ✅ | ✅ | | | | |
|
||||
|
||||
- Builds manylinux, macOS and Windows (32 and 64bit) wheels using Travis CI and Appveyor
|
||||
<sup>¹ Not supported on Travis</sup><br>
|
||||
<sup>² Beta support until Travis CI fixes <a href="https://travis-ci.community/t/no-space-left-on-device-for-system-z/5954/11">a bug</a></sup>
|
||||
|
||||
- Builds manylinux, macOS and Windows wheels for CPython and PyPy using Azure Pipelines, Travis CI, AppVeyor, and CircleCI
|
||||
- Bundles shared library dependencies on Linux and macOS through [auditwheel](https://github.com/pypa/auditwheel) and [delocate](https://github.com/matthew-brett/delocate)
|
||||
- Runs the library test suite against the wheel-installed version of your library
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
`cibuildwheel` currently works on **Travis CI** to build Linux and Mac wheels, and **Appveyor** to build Windows wheels.
|
||||
`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` is not intended to run on your development machine. It will try to install packages globally; this is no good. Travis CI and Appveyor run their builds in isolated environments, so are ideal for this kind of script.
|
||||
| | Linux | macOS | Windows |
|
||||
|-----------------|-------|-------|---------|
|
||||
| Azure Pipelines | ✅ | ✅ | ✅ |
|
||||
| Travis CI | ✅ | ✅ | ✅ |
|
||||
| AppVeyor | ✅ | ✅ | ✅ |
|
||||
| GitHub Actions | ✅ | ✅ | ✅ |
|
||||
| CircleCI | ✅ | ✅ | |
|
||||
|
||||
### Minimal setup
|
||||
`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.
|
||||
|
||||
- Create a `.travis.yml` file in your repo.
|
||||
<!--intro-end-->
|
||||
|
||||
```
|
||||
language: python
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- sudo: required
|
||||
services:
|
||||
- docker
|
||||
env: PIP=pip
|
||||
- os: osx
|
||||
language: generic
|
||||
env: PIP=pip2
|
||||
Example setup
|
||||
-------------
|
||||
|
||||
script:
|
||||
- $PIP install cibuildwheel==0.8.0
|
||||
- cibuildwheel --output-dir wheelhouse
|
||||
```
|
||||
To build manylinux, macOS, and Windows wheels on Travis CI and upload them to PyPI whenever you tag a version, you could use this `.travis.yml`:
|
||||
|
||||
Then setup a deployment method by following the [Travis CI deployment docs](https://docs.travis-ci.com/user/deployment/), or see [Delivering to PyPI](#delivering-to-pypi) below.
|
||||
```yaml
|
||||
language: python
|
||||
|
||||
- Create an `appveyor.yml` file in your repo.
|
||||
jobs:
|
||||
include:
|
||||
# perform a linux build
|
||||
- services: docker
|
||||
# and a mac build
|
||||
- os: osx
|
||||
language: shell
|
||||
# and a windows build
|
||||
- os: windows
|
||||
language: shell
|
||||
before_install:
|
||||
- choco install python --version 3.8.0
|
||||
- export PATH="/c/Python38:/c/Python38/Scripts:$PATH"
|
||||
|
||||
```
|
||||
build_script:
|
||||
- pip install cibuildwheel==0.8.0
|
||||
- cibuildwheel --output-dir wheelhouse
|
||||
artifacts:
|
||||
- path: "wheelhouse\\*.whl"
|
||||
name: Wheels
|
||||
```
|
||||
|
||||
Appveyor will store the built wheels for you - you can access them from the project console. Alternatively, you may want to store them in the same place as the Travis CI build. See [Appveyor deployment docs](https://www.appveyor.com/docs/deployment/) for more info, or see [Delivering to PyPI](#delivering-to-pypi) below.
|
||||
|
||||
- Commit those files, enable building of your repo on Travis CI and Appveyor, and push.
|
||||
env:
|
||||
global:
|
||||
- TWINE_USERNAME=__token__
|
||||
# Note: TWINE_PASSWORD is set to a PyPI API token in Travis settings
|
||||
|
||||
All being well, you should get wheels delivered to you in a few minutes.
|
||||
install:
|
||||
- python3 -m pip install cibuildwheel==1.3.0
|
||||
|
||||
> ⚠️ Got an error? Check the [checklist](#it-didnt-work) below.
|
||||
script:
|
||||
# build the wheels, put them into './wheelhouse'
|
||||
- python3 -m cibuildwheel --output-dir wheelhouse
|
||||
|
||||
### Configuration overview
|
||||
|
||||
`cibuildwheel` allows for easy customization of the various phases of the build process demonstrated above:
|
||||
|
||||
| | Option | |
|
||||
|---|---|---|
|
||||
| **Target wheels** | `CIBW_PLATFORM` | Override the auto-detected target platform |
|
||||
| | `CIBW_SKIP` | Skip certain Python versions |
|
||||
| **Build parameters** | `CIBW_BUILD_VERBOSITY` | Increase or decrease the output of `pip wheel` |
|
||||
| **Build environment** | `CIBW_ENVIRONMENT` | Set environment variables needed during the build |
|
||||
| | `CIBW_BEFORE_BUILD` | Execute a shell command preparing each wheel's build |
|
||||
| | `CIBW_MANYLINUX1_X86_64_IMAGE` | Specify an alternative manylinx1 x86_64 docker image |
|
||||
| | `CIBW_MANYLINUX1_I686_IMAGE` | Specify an alternative manylinux1 i686 docker image |
|
||||
| **Tests** | `CIBW_TEST_COMMAND` | Execute a shell command to test all built wheels |
|
||||
| | `CIBW_TEST_REQUIRES` | Install Python dependencies before running the tests |
|
||||
|
||||
A more detailed description of the options, the allowed values, and some examples can be found in the [Options](#options) section.
|
||||
|
||||
### Linux builds on Docker
|
||||
|
||||
Linux wheels are built in the [`manylinux1` docker images](https://github.com/pypa/manylinux) to provide binary compatible wheels on Linux, according to [PEP 513](https://www.python.org/dev/peps/pep-0513/). Because of this, when building with `cibuildwheel` on Linux, a few things should be taken into account:
|
||||
- Progams and libraries cannot be installed on the Travis CI Ubuntu host with `apt-get`, but can be installed inside of the Docker image using `yum` or manually. The same goes for environment variables that are potentially needed to customize the wheel building. `cibuildwheel` supports this by providing the `CIBW_ENVIRONMENT` and `CIBW_BEFORE_BUILD` options to setup the build environment inside the running Docker image. See [below](#options) for details on these options.
|
||||
- The project directory is mounted in the running Docker instance as `/project`, the output directory for the wheels as `/output`. In general, this is handled transparently by `cibuildwheel`. For a more finegrained level of control however, the root of the host file system is mounted as `/host`, allowing for example to access shared files, caches, etc. on the host file system.
|
||||
- Alternative dockers images can be specified with the `CIBW_MANYLINUX1_X86_64_IMAGE` and `CIBW_MANYLINUX1_I686_IMAGE` options to allow for a custom, preconfigured build environment for the Linux builds. See [below](#options) for more details.
|
||||
after_success:
|
||||
# if the release was tagged, upload them to PyPI
|
||||
- |
|
||||
if [[ $TRAVIS_TAG ]]; then
|
||||
python3 -m pip install twine
|
||||
python3 -m twine upload wheelhouse/*.whl
|
||||
fi
|
||||
```
|
||||
|
||||
For more information, including how to build on Appveyor, Azure, CircleCI, check out the [documentation](https://cibuildwheel.readthedocs.org) and also check out [the examples](https://github.com/joerick/cibuildwheel/tree/master/examples).
|
||||
|
||||
Options
|
||||
-------
|
||||
|
||||
```
|
||||
usage: cibuildwheel [-h]
|
||||
[--output-dir OUTPUT_DIR]
|
||||
[--platform PLATFORM]
|
||||
[project_dir]
|
||||
|
||||
Build wheels for all the platforms.
|
||||
|
||||
positional arguments:
|
||||
project_dir Path to the project that you want wheels for.
|
||||
Default: the current directory.
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--platform {auto,linux,macos,windows}
|
||||
Platform to build for. For "linux" you need docker
|
||||
running, on Mac or Linux. For "macos", you need a Mac
|
||||
machine, and note that this script is going to
|
||||
automatically install MacPython on your system, so
|
||||
don't run on your development machine. For "windows",
|
||||
you need to run in Windows, and it will build and test
|
||||
for all versions of Python at C:\PythonXX[-x64].
|
||||
--output-dir OUTPUT_DIR
|
||||
Destination folder for the wheels.
|
||||
|
||||
```
|
||||
|
||||
Most of the config is via environment variables. These go into `.travis.yml` and `appveyor.yml` nicely.
|
||||
|
||||
| Environment variable: `CIBW_PLATFORM` | Command line argument: `--platform`
|
||||
| --- | ---
|
||||
|
||||
Options: `auto` `linux` `macos` `windows`
|
||||
|
||||
Default: `auto`
|
||||
|
||||
`auto` will auto-detect platform using environment variables, such as `TRAVIS_OS_NAME`/`APPVEYOR`.
|
||||
|
||||
For `linux` you need Docker running, on Mac or Linux. For `macos`, you need a Mac machine, and note that this script is going to automatically install MacPython on your system, so don't run on your development machine. For `windows`, you need to run in Windows, and it will build and test for all versions of Python at `C:\PythonXX[-x64]`.
|
||||
|
||||
| Environment variable: `CIBW_SKIP`
|
||||
| ---
|
||||
|
||||
Optional.
|
||||
|
||||
Space-separated list of builds to skip. Each build has an identifier like `cp27-manylinux1_x86_64` or `cp34-macosx_10_6_intel` - you can list ones to skip here and `cibuildwheel` won't try to build them.
|
||||
|
||||
The format is `python_tag-platform_tag`. The tags are as defined in [PEP 0425](https://www.python.org/dev/peps/pep-0425/#details).
|
||||
|
||||
Python tags look like `cp27` `cp34` `cp35` `cp36`
|
||||
|
||||
Platform tags look like `macosx_10_6_intel` `manylinux1_x86_64` `manylinux1_i686` `win32` `win_amd64`
|
||||
|
||||
You can also use shell-style globbing syntax (as per `fnmatch`)
|
||||
|
||||
Examples:
|
||||
- Skip building on Python 2.7 on the Mac: `cp27-macosx_10_6_intel`
|
||||
- Skip building on Python 2.7 on all platforms: `cp27-*`
|
||||
- Skip Python 2.7 on Windows: `cp27-win*`
|
||||
- Skip Python 2.7 on 32bit Windows: `cp27-win32`
|
||||
- Skip Python 3.4 and Python 3.5: `cp34-* cp35-*`
|
||||
- Skip Python 3.6 on Linux: `cp36-manylinux*`
|
||||
- Only build on Python 3.6: `cp27-* cp34-* cp35-*`
|
||||
|
||||
| Environment variable: `CIBW_BUILD_VERBOSITY`
|
||||
| ---
|
||||
|
||||
Optional.
|
||||
|
||||
An number from 1 to 3 to increase the level of verbosity (corresponding to invoking pip with `-v`, `-vv`, and `-vvv`), between -1 and -3 (`-q`, `-qq`, and `-qqq`), or just 0 (default verbosity). These flags are useful while debugging a build when the output of the actual build invoked by `pip wheel` is required.
|
||||
|
||||
Platform-specific variants also available:
|
||||
`CIBW_BUILD_VERBOSITY_MACOS` | `CIBW_BUILD_VERBOSITY_WINDOWS` | `CIBW_BUILD_VERBOSITY_LINUX`
|
||||
|
||||
| Environment variable: `CIBW_ENVIRONMENT`
|
||||
| ---
|
||||
|
||||
Optional.
|
||||
|
||||
A space-separated list of environment variables to set during the build. Bash syntax should be used (even on Windows!).
|
||||
|
||||
You must set this variable to pass variables to Linux builds (since they execute in a Docker container). It also works for the other platforms.
|
||||
|
||||
You can use `$PATH` syntax to insert other variables, or the `$(pwd)` syntax to insert the output of other shell commands.
|
||||
|
||||
Example: `CFLAGS="-g -Wall" CXXFLAGS="-Wall"`
|
||||
Example: `PATH=$PATH:/usr/local/bin`
|
||||
Example: `BUILD_TIME="$(date)"`
|
||||
Example: `PIP_EXTRA_INDEX_URL="https://pypi.myorg.com/simple"`
|
||||
|
||||
Platform-specific variants also available:
|
||||
`CIBW_ENVIRONMENT_MACOS` | `CIBW_ENVIRONMENT_WINDOWS` | `CIBW_ENVIRONMENT_LINUX`
|
||||
|
||||
In addition to the above, `cibuildwheel` always defines the environment variable `CIBUILDWHEEL=1`. This can be useful for [building wheels with optional extensions](https://github.com/joerick/cibuildwheel/wiki/Building-packages-with-optional-C-extensions).
|
||||
|
||||
| Environment variable: `CIBW_BEFORE_BUILD`
|
||||
| ---
|
||||
|
||||
Optional.
|
||||
|
||||
A shell command to run before building the wheel. This option allows you to run a command in **each** Python environment before the `pip wheel` command. This is useful if you need to set up some dependency so it's available during the build.
|
||||
|
||||
If dependencies are required to build your wheel (for example if you include a header from a Python module), set this to `{pip} install .`, and the dependencies will be installed automatically by pip. However, this means your package will be built twice - if your package takes a long time to build, you might wish to manually list the dependencies here instead.
|
||||
|
||||
The active Python binary can be accessed using `{python}`, and pip with `{pip}`. These are useful when you need to write `python3` or `pip3` on a Python 3.x build. `{project}` can be used as a placeholder for the absolute path to the project's root.
|
||||
|
||||
Example: `{pip} install .`
|
||||
Example: `{pip} install pybind11`
|
||||
Example: `yum install -y libffi-dev && {pip} install .`
|
||||
|
||||
Platform-specific variants also available:
|
||||
`CIBW_BEFORE_BUILD_MACOS` | `CIBW_BEFORE_BUILD_WINDOWS` | `CIBW_BEFORE_BUILD_LINUX`
|
||||
|
||||
| Environment variables: `CIBW_MANYLINUX1_X86_64_IMAGE` and `CIBW_MANYLINUX1_I686_IMAGE`
|
||||
| ---
|
||||
|
||||
Optional.
|
||||
|
||||
An alternative docker image to be used for building [`manylinux1`](https://github.com/pypa/manylinux) wheels. `cibuildwheel` will then pull these instead of the official images, [`quay.io/pypa/manylinux1_x86_64`](https://quay.io/pypa/manylinux1_i686) and [`quay.io/pypa/manylinux1_i686`](https://quay.io/pypa/manylinux1_i686).
|
||||
|
||||
Beware to specify a valid docker image that can be used the same as the official, default docker images: all necessary Python and pip versions need to be present in `/opt/python/`, and the `auditwheel` tool needs to be present for `cibuildwheel` to work. Apart from that, the architecture and relevant shared system libraries need to be manylinux1-compatible in order to produce valid `manylinux1` wheels (see https://github.com/pypa/manylinux and [PEP 513](https://www.python.org/dev/peps/pep-0513/) for more details).
|
||||
|
||||
Example: `dockcross/manylinux-x64`
|
||||
Example: `dockcross/manylinux-x86`
|
||||
|
||||
| Environment variable: `CIBW_TEST_COMMAND`
|
||||
| ---
|
||||
|
||||
Optional.
|
||||
|
||||
Shell command to run tests after the build. The wheel will be installed automatically and available for import from the tests. Just like in the `CIBW_BEFORE_BUILD` option, `{python}`, `{pip}`, and `{project}` will respectively be replaced by the Python executable, the pip executable, and the absolute project directory.
|
||||
|
||||
Example: `nosetests {project}/tests`
|
||||
|
||||
| Environment variable: `CIBW_TEST_REQUIRES`
|
||||
| ---
|
||||
|
||||
Optional.
|
||||
|
||||
Space-separated list of dependencies required for running the tests.
|
||||
|
||||
Example: `pytest`
|
||||
Example: `nose==1.3.7 moto==0.4.31`
|
||||
|
||||
--
|
||||
|
||||
#### Example YML syntax
|
||||
|
||||
<table>
|
||||
<tr><td><i>example .travis.yml environment variables</i><pre><code>env:
|
||||
global:
|
||||
- CIBW_TEST_REQUIRES=nose
|
||||
- CIBW_TEST_COMMAND="nosetests {project}/tests"
|
||||
</code></pre></td>
|
||||
<td><i>example appveyor.yml environment variables</i><pre><code>environment:
|
||||
global:
|
||||
CIBW_TEST_REQUIRES: nose
|
||||
CIBW_TEST_COMMAND: "nosetests {project}\\tests"
|
||||
</code></pre></td>
|
||||
</tr></table>
|
||||
|
||||
Delivering to PyPI
|
||||
------------------
|
||||
|
||||
After you've built your wheels, you'll probably want to deliver them to PyPI.
|
||||
|
||||
### Manual method
|
||||
|
||||
On your development machine, do the following...
|
||||
|
||||
```bash
|
||||
# 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. Put
|
||||
# them all into the 'dist' folder.
|
||||
|
||||
# Upload using 'twine' (you may need to 'pip install twine')
|
||||
twine upload dist/*
|
||||
```
|
||||
|
||||
### Semi-automatic method using wheelhouse-uploader
|
||||
|
||||
Obviously, manual steps are for chumps, so we can automate this a little by using [wheelhouse-uploader](https://github.com/ogrisel/wheelhouse-uploader).
|
||||
|
||||
> Quick note from me - using S3 as a storage didn't work due to a [bug](https://issues.apache.org/jira/browse/LIBCLOUD-792) in libcloud. Feel free to use my fork of that package that fixes the bug `pip install https://github.com/joerick/libcloud/archive/v1.5.0-s3fix.zip`
|
||||
|
||||
### Automatic method
|
||||
|
||||
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 any cloud 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.
|
||||
|
||||
It didn't work!
|
||||
---------------
|
||||
|
||||
If your wheel didn't compile, check the list below for some debugging tips.
|
||||
|
||||
- A mistake in your config. To quickly test your config without doing a git push and waiting for your code to build on CI, you can run the Linux build in a Docker container. On Mac or Linux, with Docker running, try `cibuildwheel --platform linux`. You'll have to bring your config into the current environment first.
|
||||
- Missing dependency. You might need to install something on the build machine. You can do this in `.travis.yml` or `appveyor.yml`, with apt-get, brew or whatever Windows uses :P . Given how the Linux build works, we'll probably have to build something into `cibuildwheel`. Let's chat about that over in the issues!
|
||||
- Windows: missing C feature. The Windows C compiler doesn't support C language features invented after 1990, so you'll have to backport your C code to C90. For me, this mostly involved putting my variable declarations at the top of the function like an animal.
|
||||
| | 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_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 |
|
||||
| **Testing** | [`CIBW_TEST_COMMAND`](https://cibuildwheel.readthedocs.io/en/stable/options/#test-command) | Execute a shell command to test each built wheel |
|
||||
| | [`CIBW_BEFORE_TEST`](https://cibuildwheel.readthedocs.io/en/stable/options/#before-test) | Execute shell command to prepare test environment |
|
||||
| | [`CIBW_TEST_REQUIRES`](https://cibuildwheel.readthedocs.io/en/stable/options/#test-requires) | Install Python dependencies before running the tests |
|
||||
| | [`CIBW_TEST_EXTRAS`](https://cibuildwheel.readthedocs.io/en/stable/options/#test-extras) | Install your wheel for testing using extras_require |
|
||||
| **Other** | [`CIBW_BUILD_VERBOSITY`](https://cibuildwheel.readthedocs.io/en/stable/options/#build-verbosity) | Increase/decrease the output of pip wheel |
|
||||
|
||||
Working examples
|
||||
----------------
|
||||
|
||||
Here are some repos that use cibuildwheel.
|
||||
Here are some repos that use cibuildwheel.
|
||||
|
||||
- [pyinstrument_cext](https://github.com/joerick/pyinstrument_cext)
|
||||
- [websockets](https://github.com/aaugustin/websockets)
|
||||
- [Parselmouth](https://github.com/YannickJadoul/Parselmouth)
|
||||
- [python-admesh](https://github.com/admesh/python-admesh)
|
||||
- [pybase64](https://github.com/mayeut/pybase64)
|
||||
- [KDEpy](https://github.com/tommyod/KDEpy)
|
||||
- [AutoPy](https://github.com/autopilot-rs/autopy)
|
||||
- [apriltags2-ethz](https://github.com/safijari/apriltags2_ethz)
|
||||
- [TgCrypto](https://github.com/pyrogram/tgcrypto)
|
||||
- [Twisted](https://github.com/twisted/twisted)
|
||||
- [gmic-py](https://github.com/dtschump/gmic-py)
|
||||
|
||||
> Add repo here! Send a PR.
|
||||
> Add your repo here! Send a PR.
|
||||
|
||||
Legal note
|
||||
----------
|
||||
|
||||
Since `cibuildwheel` runs the wheel through delocate or auditwheel, it will automatically bundle library dependencies. This is similar to static linking, so it might have some licence implications. Check the license for any code you're pulling in to make sure that's allowed.
|
||||
Since `cibuildwheel` repairs the wheel with `delocate` or `auditwheel`, it might automatically bundle dynamically linked libraries from the build machine.
|
||||
|
||||
It helps ensure that the library can run without any dependencies outside of the pip toolchain.
|
||||
|
||||
This is similar to static linking, so it might have some licence implications. Check the license for any code you're pulling in to make sure that's allowed.
|
||||
|
||||
Changelog
|
||||
=========
|
||||
|
||||
### 1.3.0
|
||||
|
||||
_12 March 2020_
|
||||
|
||||
- ✨ Add support for building on Github Actions! Check out the
|
||||
[docs](https://cibuildwheel.readthedocs.io/en/stable/setup/#github-actions)
|
||||
for information on how to set it up. (#194)
|
||||
- ✨ Add the `CIBW_BEFORE_TEST` option, which lets you run a command to
|
||||
prepare the environment before your tests are run. (#242)
|
||||
|
||||
### 1.2.0
|
||||
|
||||
_8 March 2020_
|
||||
|
||||
- ✨ Add support for building PyPy wheels, across Manylinux, macOS, and
|
||||
Windows. (#185)
|
||||
- ✨ Added the ability to build ARM64 (aarch64), ppc64le, and s390x wheels,
|
||||
using manylinux2014 and Travis CI. (#273)
|
||||
- ✨ You can now build macOS wheels on Appveyor. (#230)
|
||||
- 🛠 Changed default macOS minimum target to 10.9, from 10.6. This allows the
|
||||
use of more modern C++ libraries, among other things. (#156)
|
||||
- 🛠 Stop building universal binaries on macOS. We now only build x86_64
|
||||
wheels on macOS. (#220)
|
||||
- ✨ Allow chaining of commands using `&&` and `||` on Windows inside
|
||||
CIBW_BEFORE_BUILD and CIBW_TEST_COMMAND. (#293)
|
||||
- 🛠 Improved error reporting for failed Cython builds due to stale .so files
|
||||
(#263)
|
||||
- 🛠 Update CPython from 3.7.5 to 3.7.6 and from 3.8.0 to 3.8.2 on Mac/Windows
|
||||
- 🛠 Improved error messages when a bad config breaks cibuildwheel's PATH
|
||||
variable. (#264)
|
||||
- ⚠️ Removed support for *running* cibuildwheel on Python 2.7. cibuildwheel
|
||||
will continue to build Python 2.7 wheels for a little while. (#265)
|
||||
|
||||
### 1.1.0
|
||||
|
||||
_7 December 2019_
|
||||
|
||||
- ✨ Add support for building manylinux2014 wheels. To use, set
|
||||
`CIBW_MANYLINUX_X86_64_IMAGE` and CIBW_MANYLINUX_I686_IMAGE to
|
||||
`manylinux2014`.
|
||||
- ✨ Add support for [Linux on Appveyor](https://www.appveyor.com/blog/2018/03/06/appveyor-for-linux/) (#204, #207)
|
||||
- ✨ Add `CIBW_REPAIR_WHEEL_COMMAND` env variable, for changing how
|
||||
`auditwheel` or `delocate` are invoked, or testing an equivalent on
|
||||
Windows. (#211)
|
||||
- 📚 Added some travis example configs - these are available in /examples. (#228)
|
||||
|
||||
### 1.0.0
|
||||
|
||||
_10 November 2019_
|
||||
|
||||
- ✨ 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
|
||||
build using the old manylinux1 image using the [manylinux image](https://cibuildwheel.readthedocs.io/en/stable/options/#manylinux-image) option.
|
||||
(#155)
|
||||
- 📚 Documentation is now on its [own mini-site](https://cibuildwheel.readthedocs.io),
|
||||
rather than on the README (#169)
|
||||
- ✨ Add support for building Windows wheels on Travis CI. (#160)
|
||||
- 🛠 If you set `CIBW_TEST_COMMAND`, your tests now run in a virtualenv. (#164)
|
||||
- 🛠 Windows now uses Python as installed by nuget, rather than the versions
|
||||
installed by the various CI providers. (#180)
|
||||
- 🛠 Update Python from 2.7.16 to 2.7.17 and 3.7.4 to 3.7.5 on macOS (#171)
|
||||
- ⚠️ Removed support for Python 3.4 (#168)
|
||||
|
||||
### 0.12.0
|
||||
|
||||
_29 September 2019_
|
||||
|
||||
- ✨ Add CIBW_TEST_EXTRAS option, to allow testing using extra_require
|
||||
options. For example, set `CIBW_TEST_EXTRAS=test,qt` to make the wheel
|
||||
installed with `pip install <wheel_file>[test,qt]`
|
||||
- 🛠 Update Python from 3.7.2 to 3.7.4 on macOS
|
||||
- 🛠 Update OpenSSL patch to 1.0.2t on macOS
|
||||
|
||||
### 0.11.1
|
||||
|
||||
_28 May 2019_
|
||||
|
||||
- 🐛 Fix missing file in the release tarball, that was causing problems with
|
||||
Windows builds (#141)
|
||||
|
||||
### 0.11.0
|
||||
|
||||
_26 May 2019_
|
||||
|
||||
- ✨ 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)
|
||||
for an example project. (#126, #132)
|
||||
- 🛠 Internal change - the end-to-end test projects format was updated, so we
|
||||
can more precisely assert what should be produced for each one. (#136, #137).
|
||||
|
||||
### 0.10.2
|
||||
|
||||
_10 March 2019_
|
||||
|
||||
- 🛠 Revert temporary fix in macOS, that was working around a bug in pip 19 (#129)
|
||||
- 🛠 Update Python to 2.7.16 on macOS
|
||||
- 🛠 Update OpenSSL patch to 1.0.2r on macOS
|
||||
|
||||
### 0.10.1
|
||||
|
||||
_3 February 2019_
|
||||
|
||||
- 🐛 Fix build stalling on macOS (that was introduced in pip 19) (#122)
|
||||
- 🐛 Fix "AttributeError: 'Popen' object has no attribute 'args'" on Python 2.7 for Linux builds (#108)
|
||||
- 🛠 Update Python from 3.6.7, 3.7.1 to 3.6.8, 3.7.2 on macOS
|
||||
- 🛠 Update openssl patch from 1.0.2p to 1.0.2q on macOS
|
||||
- 🛠 Sorting build options dict items when printing preamble (#114)
|
||||
|
||||
### 0.10.0
|
||||
|
||||
_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)
|
||||
- 🛠 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)
|
||||
- ⚠️ Removed support for Python 3.3 on Windows.
|
||||
|
||||
### 0.9.4
|
||||
|
||||
_29 July 2018_
|
||||
|
||||
- 🛠 CIBW_TEST_COMMAND now runs in a shell on Mac (as well as Linux) (#81)
|
||||
|
||||
### 0.9.3
|
||||
|
||||
_10 July 2018_
|
||||
|
||||
- 🛠 Update to Python 3.6.6 on macOS (#82)
|
||||
- ✨ Add support for building Python 3.7 wheels on Windows (#76)
|
||||
- ⚠️ Deprecated support for Python 3.3 on Windows.
|
||||
|
||||
### 0.9.2
|
||||
|
||||
_1 July 2018_
|
||||
|
||||
- 🛠 Update Python 3.7.0rc1 to 3.7.0 on macOS (#79)
|
||||
|
||||
### 0.9.1
|
||||
|
||||
_18 June 2018_
|
||||
|
||||
- 🛠 Removed the need to use `{python}` and `{pip}` in `CIBW_BEFORE_BUILD` statements, by ensuring the correct version is always on the path at `python` and `pip` instead. (#60)
|
||||
- 🛠 We now patch the _ssl module on Python 3.4 and 3.5 so these versions can still make SSL web requests using TLS 1.2 while building. (#71)
|
||||
|
||||
### 0.9.0
|
||||
|
||||
_18 June 2018_
|
||||
|
||||
- ✨ Add support for Python 3.7 (#73)
|
||||
|
||||
### 0.8.0
|
||||
|
||||
_4 May 2018_
|
||||
|
||||
- ⚠️ Drop support for Python 3.3 on Linux (#67)
|
||||
- 🐛 Fix TLS by updating setuptools (#69)
|
||||
|
||||
### 0.7.1
|
||||
|
||||
- macOS: Fix Pip bugs resulting from PyPI TLS 1.2 enforcement
|
||||
- macOS: Fix brew Python3 version problems in the CI
|
||||
_2 April 2017_
|
||||
|
||||
- 🐛 macOS: Fix Pip bugs resulting from PyPI TLS 1.2 enforcement
|
||||
- 🐛 macOS: Fix brew Python3 version problems in the CI
|
||||
|
||||
### 0.7.0
|
||||
|
||||
- You can now specify a custom docker image using the `CIBW_MANYLINUX1_X86_64_IMAGE` and `CIBW_MANYLINUX1_I686_IMAGE` options. (#46)
|
||||
- Fixed a bug where cibuildwheel would download and build a package from PyPI(!) instead of building the package on the local machine. (#51)
|
||||
_7 January 2018_
|
||||
|
||||
- ✨ You can now specify a custom docker image using the `CIBW_MANYLINUX1_X86_64_IMAGE` and `CIBW_MANYLINUX1_I686_IMAGE` options. (#46)
|
||||
- 🐛 Fixed a bug where cibuildwheel would download and build a package from PyPI(!) instead of building the package on the local machine. (#51)
|
||||
|
||||
### 0.6.0
|
||||
|
||||
- On the Linux build, the host filesystem is now accessible via `/host` (#36)
|
||||
- Fixed a bug where setup.py scripts would run the wrong version of Python when running subprocesses on Linux (#35)
|
||||
_9 October 2017_
|
||||
|
||||
- ✨ On the Linux build, the host filesystem is now accessible via `/host` (#36)
|
||||
- 🐛 Fixed a bug where setup.py scripts would run the wrong version of Python when running subprocesses on Linux (#35)
|
||||
|
||||
### 0.5.1
|
||||
|
||||
- Fixed a couple of bugs on Python 3.
|
||||
- Added experimental support for Mac builds on [Bitrise.io](https://www.bitrise.io)
|
||||
_10 September 2017_
|
||||
|
||||
- 🐛 Fixed a couple of bugs on Python 3.
|
||||
- ✨ Added experimental support for Mac builds on [Bitrise.io](https://www.bitrise.io)
|
||||
|
||||
### 0.5.0
|
||||
|
||||
- `CIBW_ENVIRONMENT` added. You can now set environment variables for each build, even within the Docker container on Linux. This is a big one! (#21)
|
||||
- `CIBW_BEFORE_BUILD` now runs in a system shell on all platforms. You can now do things like `CIBW_BEFORE_BUILD="cmd1 && cmd2"`. (#32)
|
||||
_7 September 2017_
|
||||
|
||||
- ✨ `CIBW_ENVIRONMENT` added. You can now set environment variables for each build, even within the Docker container on Linux. This is a big one! (#21)
|
||||
- ✨ `CIBW_BEFORE_BUILD` now runs in a system shell on all platforms. You can now do things like `CIBW_BEFORE_BUILD="cmd1 && cmd2"`. (#32)
|
||||
|
||||
### 0.4.1
|
||||
|
||||
- Fixed a bug on Windows where subprocess' output was hidden (#23)
|
||||
- Fixed a bug on Appveyor where logs would appear in the wrong order due to output buffering (#24, thanks @YannickJadoul!)
|
||||
_14 August 2017_
|
||||
|
||||
- 🐛 Fixed a bug on Windows where subprocess' output was hidden (#23)
|
||||
- 🐛 Fixed a bug on AppVeyor where logs would appear in the wrong order due to output buffering (#24, thanks @YannickJadoul!)
|
||||
|
||||
### 0.4.0
|
||||
|
||||
- Fixed a bug that was increasing the build time by building the wheel twice. This was a problem for large projects that have a long build time. If you're upgrading and you need the old behaviour, use `CIBW_BEFORE_BUILD={pip} install .`, or install exactly the dependencies you need in `CIBW_BEFORE_BUILD`. See #18.
|
||||
_23 July 2017_
|
||||
|
||||
- 🐛 Fixed a bug that was increasing the build time by building the wheel twice. This was a problem for large projects that have a long build time. If you're upgrading and you need the old behaviour, use `CIBW_BEFORE_BUILD={pip} install .`, or install exactly the dependencies you need in `CIBW_BEFORE_BUILD`. See #18.
|
||||
|
||||
### 0.3.0
|
||||
|
||||
- Removed Python 2.6 support on Linux (#12)
|
||||
_27 June 2017_
|
||||
|
||||
- ⚠️ Removed Python 2.6 support on Linux (#12)
|
||||
|
||||
### 0.2.1
|
||||
|
||||
11 June 2017
|
||||
_11 June 2017_
|
||||
|
||||
- Changed the build process to install the package before building the wheel - this allows direct dependencies to be installed first (#9, thanks @tgarc!)
|
||||
- Added Python 3 support for the main process, for systems where Python 3 is the default (#8, thanks @tgarc).
|
||||
- 🛠 Changed the build process to install the package before building the wheel - this allows direct dependencies to be installed first (#9, thanks @tgarc!)
|
||||
- ✨ Added Python 3 support for the main process, for systems where Python 3 is the default (#8, thanks @tgarc).
|
||||
|
||||
### 0.2.0
|
||||
|
||||
13 April 2017
|
||||
_13 April 2017_
|
||||
|
||||
- Added `CIBW_SKIP` option, letting users explicitly skip a build
|
||||
- Added `CIBW_BEFORE_BUILD` option, letting users run a shell command before the build starts
|
||||
- ✨ Added `CIBW_SKIP` option, letting users explicitly skip a build
|
||||
- ✨ Added `CIBW_BEFORE_BUILD` option, letting users run a shell command before the build starts
|
||||
|
||||
### 0.1.3
|
||||
|
||||
31 March 2017
|
||||
_31 March 2017_
|
||||
|
||||
- First public release!
|
||||
- 🌟 First public release!
|
||||
|
||||
Contributing
|
||||
============
|
||||
|
||||
Wheel-building is pretty complex. I expect users to find many edge-cases - please help the rest of the community out by documenting these, adding features to support them, and reporting bugs.
|
||||
|
||||
I plan to be pretty liberal in accepting pull requests, as long as they align with the design goals below.
|
||||
|
||||
`cibuildwheel` is indie open source. I'm not paid to work on this.
|
||||
|
||||
Design Goals
|
||||
------------
|
||||
|
||||
- `cibuildwheel` should wrap the complexity of wheel building.
|
||||
- The user interface to `cibuildwheel` is the build script (e.g. `.travis.yml`). Feature additions should not increase the complexity of this script.
|
||||
- Options should be environment variables (these lend themselves better to YML config files). They should be prefixed with `CIBW_`.
|
||||
- Options should be generalise to all platforms. If platform-specific options are required, they should be namespaced e.g. `CIBW_TEST_COMMAND_MACOS`
|
||||
|
||||
Other notes:
|
||||
|
||||
- The platforms are very similar, until they're not. I'd rather have straight-forward code than totally DRY code, so let's keep airy platfrom abstractions to a minimum.
|
||||
- I might want to break the options into a shared config file one day, so that config is more easily shared. That has motivated some of the design decisions.
|
||||
For more info on how to contribute to cibuildwheel, see the [docs](https://cibuildwheel.readthedocs.io/en/latest/contributing/).
|
||||
|
||||
Maintainers
|
||||
-----------
|
||||
|
||||
- Joe Rickerby [@joerick](https://github.com/joerick)
|
||||
- Tomas Garcia [@tgarc](https://github.com/tgarc)
|
||||
- Yannick Jadoul [@YannickJadoul](https://github.com/YannickJadoul)
|
||||
- Matthieu Darbois [@mayeut](https://github.com/mayeut)
|
||||
|
||||
Credits
|
||||
-------
|
||||
|
||||
`cibuildwheel` stands on the shoulders of giants. Massive props to-
|
||||
`cibuildwheel` stands on the shoulders of giants.
|
||||
|
||||
- ⭐️ @matthew-brett for [matthew-brett/multibuild](http://github.com/matthew-brett/multibuild) and [matthew-brett/delocate](http://github.com/matthew-brett/delocate)
|
||||
- @PyPA for the manylinux Docker images [pypa/manylinux](https://github.com/pypa/manylinux)
|
||||
- @ogrisel for [wheelhouse-uploader](https://github.com/ogrisel/wheelhouse-uploader) and `run_with_env.cmd`
|
||||
|
||||
Massive props also to-
|
||||
|
||||
- @zfrenchee for [help debugging many issues](https://github.com/joerick/cibuildwheel/issues/2)
|
||||
- @lelit for some great bug reports and [contributions](https://github.com/joerick/cibuildwheel/pull/73)
|
||||
- @mayeut for a [phenomenal PR](https://github.com/joerick/cibuildwheel/pull/71) patching Python itself for better compatibility!
|
||||
|
||||
See also
|
||||
--------
|
||||
========
|
||||
|
||||
If you'd like to keep wheel building separate from the package itself, check out [astrofrog/autowheel](https://github.com/astrofrog/autowheel). It builds packages using cibuildwheel from source distributions on PyPI.
|
||||
|
||||
If `cibuildwheel` is too limited for your needs, consider [matthew-brett/multibuild](http://github.com/matthew-brett/multibuild). `multibuild` is a toolbox for building a wheel on various platforms. It can do a lot more than this project - it's used to build SciPy!
|
||||
|
||||
+18
-7
@@ -1,10 +1,21 @@
|
||||
environment:
|
||||
matrix:
|
||||
- PYTHON: "C:\\Python27\\python.exe"
|
||||
- PYTHON: "C:\\Python35\\python.exe"
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Ubuntu
|
||||
APPVEYOR_JOB_NAME: "python37-x64-ubuntu"
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
APPVEYOR_JOB_NAME: "python37-x64-vs2015"
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: macos-mojave
|
||||
APPVEYOR_JOB_NAME: "python37-x64-macos-mojave"
|
||||
|
||||
build_script:
|
||||
- "%PYTHON% -m pip install -r requirements-dev.txt"
|
||||
# the '-u' flag is required so the output is in the correct order.
|
||||
# See https://github.com/joerick/cibuildwheel/pull/24 for more info.
|
||||
- "%PYTHON% -u ./bin/run_tests.py"
|
||||
stack: python 3.7
|
||||
|
||||
build: off
|
||||
|
||||
init:
|
||||
- cmd: set PATH=C:\Python37;C:\Python37\Scripts;%PATH%
|
||||
|
||||
install: python -m pip install -r requirements-dev.txt
|
||||
|
||||
# the '-u' flag is required so the output is in the correct order.
|
||||
# See https://github.com/joerick/cibuildwheel/pull/24 for more info.
|
||||
test_script: python -u ./bin/run_tests.py
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
jobs:
|
||||
- job: linux_38
|
||||
pool: {vmImage: 'Ubuntu-18.04'}
|
||||
steps:
|
||||
- task: UsePythonVersion@0
|
||||
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 ./bin/run_tests.py
|
||||
|
||||
- job: macos_38
|
||||
pool: {vmImage: 'macOS-10.15'}
|
||||
steps:
|
||||
- task: UsePythonVersion@0
|
||||
inputs:
|
||||
versionSpec: '3.8'
|
||||
- bash: |
|
||||
python -m pip install -r requirements-dev.txt
|
||||
python ./bin/run_tests.py
|
||||
|
||||
- job: windows_36
|
||||
pool: {vmImage: 'vs2017-win2016'}
|
||||
steps:
|
||||
- task: UsePythonVersion@0
|
||||
inputs:
|
||||
versionSpec: '3.6'
|
||||
- script: choco install vcpython27 -f -y
|
||||
displayName: Install Visual C++ for Python 2.7
|
||||
- bash: |
|
||||
python -m pip install -r requirements-dev.txt
|
||||
python ./bin/run_tests.py
|
||||
|
||||
- job: windows_38
|
||||
pool: {vmImage: 'vs2017-win2016'}
|
||||
steps:
|
||||
- task: UsePythonVersion@0
|
||||
inputs:
|
||||
versionSpec: '3.8'
|
||||
- script: choco install vcpython27 -f -y
|
||||
displayName: Install Visual C++ for Python 2.7
|
||||
- bash: |
|
||||
python -m pip install -r requirements-dev.txt
|
||||
python ./bin/run_tests.py
|
||||
|
||||
Executable
+113
@@ -0,0 +1,113 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import click
|
||||
from pathlib import Path
|
||||
import os
|
||||
import cibuildwheel
|
||||
from packaging.version import Version, InvalidVersion
|
||||
import subprocess
|
||||
import glob
|
||||
|
||||
config = [
|
||||
# file path, version find/replace format
|
||||
('cibuildwheel/__init__.py', "__version__ = '{}'"),
|
||||
('setup.py', "version='{}'"),
|
||||
('README.md', "cibuildwheel=={}"),
|
||||
('examples/*', "cibuildwheel=={}"),
|
||||
]
|
||||
|
||||
|
||||
@click.command()
|
||||
def bump_version():
|
||||
git_changes_result = subprocess.run(['git diff-index --quiet HEAD --'], shell=True)
|
||||
repo_has_uncommitted_changes = git_changes_result.returncode != 0
|
||||
|
||||
if repo_has_uncommitted_changes:
|
||||
print('error: Uncommitted changes detected.')
|
||||
exit(1)
|
||||
|
||||
current_version = cibuildwheel.__version__
|
||||
print( 'Current version:', current_version) # noqa
|
||||
new_version = input(' New version: ').strip()
|
||||
|
||||
try:
|
||||
Version(new_version)
|
||||
except InvalidVersion:
|
||||
print("error: This version doesn't conform to PEP440")
|
||||
print(' https://www.python.org/dev/peps/pep-0440/')
|
||||
exit(1)
|
||||
|
||||
actions = []
|
||||
|
||||
for path_pattern, version_pattern in config:
|
||||
paths = [Path(p) for p in glob.glob(path_pattern)]
|
||||
|
||||
if not paths:
|
||||
print(f'error: Pattern {path_pattern} didn’t match any files')
|
||||
exit(1)
|
||||
|
||||
find_pattern = version_pattern.format(current_version)
|
||||
replace_pattern = version_pattern.format(new_version)
|
||||
found_at_least_one_file_needing_update = False
|
||||
|
||||
for path in paths:
|
||||
contents = path.read_text(encoding='utf8')
|
||||
if find_pattern in contents:
|
||||
found_at_least_one_file_needing_update = True
|
||||
actions.append(
|
||||
(path, find_pattern, replace_pattern)
|
||||
)
|
||||
|
||||
if not found_at_least_one_file_needing_update:
|
||||
print('error: Didn’t find any occurences of “{}” in “{}”'.format(find_pattern, path_pattern))
|
||||
exit(1)
|
||||
|
||||
print()
|
||||
print("Here's the plan:")
|
||||
print()
|
||||
|
||||
for action in actions:
|
||||
print('{} {red}{}{off} → {green}{}{off}'.format(
|
||||
*action,
|
||||
red="\u001b[31m", green="\u001b[32m", off="\u001b[0m"
|
||||
))
|
||||
|
||||
print('Then commit, and tag as v{}'.format(new_version))
|
||||
|
||||
answer = input('Proceed? [y/N] ').strip()
|
||||
|
||||
if answer != 'y':
|
||||
print('Aborted')
|
||||
exit(1)
|
||||
|
||||
for path, find, replace in actions:
|
||||
contents = path.read_text(encoding='utf8')
|
||||
contents = contents.replace(find, replace)
|
||||
path.write_text(contents, encoding='utf8')
|
||||
|
||||
print('Files updated. If you want to update the changelog as part of this')
|
||||
print('commit, do that now.')
|
||||
|
||||
while input('Type "done" to continue: ').strip().lower() != 'done':
|
||||
pass
|
||||
|
||||
subprocess.run([
|
||||
'git', 'commit',
|
||||
'-a',
|
||||
'-m', f'Bump version: v{new_version}'
|
||||
], check=True)
|
||||
|
||||
subprocess.run([
|
||||
'git', 'tag',
|
||||
'-a',
|
||||
'-m', f'v{new_version}',
|
||||
f'v{new_version}'
|
||||
], check=True)
|
||||
|
||||
print('Done.')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
os.chdir(os.path.dirname(__file__))
|
||||
os.chdir('..')
|
||||
bump_version()
|
||||
+11
-29
@@ -1,45 +1,27 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from __future__ import print_function
|
||||
import os, sys, subprocess, shutil, json
|
||||
from glob import glob
|
||||
|
||||
def single_run(test_project):
|
||||
# load project settings into environment
|
||||
env_file = os.path.join(test_project, 'environment.json')
|
||||
project_env = {}
|
||||
if os.path.exists(env_file):
|
||||
with open(env_file) as f:
|
||||
project_env = json.load(f)
|
||||
# run the test
|
||||
subprocess.check_call(
|
||||
[sys.executable, '-m', 'pytest', '-vv', os.path.join(test_project, 'cibuildwheel_test.py')],
|
||||
)
|
||||
|
||||
# run the build
|
||||
env = os.environ.copy()
|
||||
project_env = {str(k): str(v) for k, v in project_env.items()} # unicode not allowed in env
|
||||
env.update(project_env)
|
||||
print('Building %s with environment %s' % (test_project, project_env))
|
||||
subprocess.check_call([sys.executable, '-m', 'cibuildwheel', test_project], env=env)
|
||||
wheels = glob('wheelhouse/*.whl')
|
||||
print('%s built successfully. %i wheels built.' % (test_project, len(wheels)))
|
||||
|
||||
# check some wheels were actually built
|
||||
assert len(wheels) >= 4
|
||||
|
||||
# clean up
|
||||
shutil.rmtree('wheelhouse')
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
|
||||
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)
|
||||
|
||||
print('Project built successfully.')
|
||||
|
||||
+7
-6
@@ -1,20 +1,21 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from __future__ import print_function
|
||||
import os, sys, subprocess, shutil, json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from glob import glob
|
||||
|
||||
if __name__ == '__main__':
|
||||
# move cwd to the project root
|
||||
os.chdir(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
### run the unit tests
|
||||
# run the unit tests
|
||||
|
||||
subprocess.check_call([sys.executable, '-m', 'pytest', 'unit_test'])
|
||||
|
||||
### run the integration tests
|
||||
# run the integration tests
|
||||
|
||||
test_projects = glob('test/??_*')
|
||||
test_projects = sorted(glob('test/??_*'))
|
||||
|
||||
if len(test_projects) == 0:
|
||||
print('No test projects found. Aborting.', file=sys.stderr)
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = '0.8.0'
|
||||
__version__ = '1.3.0'
|
||||
|
||||
+170
-54
@@ -1,12 +1,24 @@
|
||||
from __future__ import print_function
|
||||
import argparse, os, subprocess, sys, textwrap
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import textwrap
|
||||
import traceback
|
||||
|
||||
import cibuildwheel
|
||||
import cibuildwheel.linux, cibuildwheel.windows, cibuildwheel.macos
|
||||
from cibuildwheel.environment import parse_environment, EnvironmentParseError
|
||||
from cibuildwheel.util import BuildSkipper
|
||||
import cibuildwheel.linux
|
||||
import cibuildwheel.macos
|
||||
import cibuildwheel.windows
|
||||
from cibuildwheel.environment import (
|
||||
EnvironmentParseError,
|
||||
parse_environment,
|
||||
)
|
||||
from cibuildwheel.util import (
|
||||
BuildSelector,
|
||||
Unbuffered
|
||||
)
|
||||
|
||||
def get_option_from_environment(option_name, platform=None):
|
||||
|
||||
def get_option_from_environment(option_name, platform=None, default=None):
|
||||
'''
|
||||
Returns an option from the environment, optionally scoped by the platform.
|
||||
|
||||
@@ -21,7 +33,13 @@ def get_option_from_environment(option_name, platform=None):
|
||||
if option is not None:
|
||||
return option
|
||||
|
||||
return os.environ.get(option_name)
|
||||
return os.environ.get(option_name, default)
|
||||
|
||||
|
||||
def strtobool(val):
|
||||
if val.lower() in ('y', 'yes', 't', 'true', 'on', '1'):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
@@ -38,7 +56,7 @@ def main():
|
||||
'script is going to automatically install MacPython on your system, '
|
||||
'so don\'t run on your development machine. For "windows", you need to '
|
||||
'run in Windows, and it will build and test for all versions of '
|
||||
'Python at C:\\PythonXX[-x64]. Default: auto.'))
|
||||
'Python. Default: auto.'))
|
||||
parser.add_argument('--output-dir',
|
||||
default=os.environ.get('CIBW_OUTPUT_DIR', 'wheelhouse'),
|
||||
help='Destination folder for the wheels.')
|
||||
@@ -48,35 +66,57 @@ def main():
|
||||
help=('Path to the project that you want wheels for. Default: the current '
|
||||
'directory.'))
|
||||
|
||||
parser.add_argument('--print-build-identifiers',
|
||||
action='store_true',
|
||||
help='Print the build identifiers matched by the current invocation and exit.')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
detect_obsolete_options()
|
||||
|
||||
if args.platform != 'auto':
|
||||
platform = args.platform
|
||||
else:
|
||||
if os.environ.get('TRAVIS_OS_NAME') == 'linux':
|
||||
platform = 'linux'
|
||||
elif os.environ.get('TRAVIS_OS_NAME') == 'osx':
|
||||
platform = 'macos'
|
||||
elif 'APPVEYOR' in os.environ:
|
||||
platform = 'windows'
|
||||
elif 'BITRISE_BUILD_NUMBER' in os.environ:
|
||||
platform = 'macos'
|
||||
else:
|
||||
ci = strtobool(os.environ.get('CI', 'false')) or 'BITRISE_BUILD_NUMBER' in os.environ or 'AZURE_HTTP_USER_AGENT' in os.environ or 'GITHUB_WORKFLOW' in os.environ
|
||||
if not ci:
|
||||
print('cibuildwheel: Unable to detect platform. cibuildwheel should run on your CI server, '
|
||||
'Travis CI and Appveyor are supported. You can run on your development '
|
||||
'machine using the --platform argument. Check --help output for more '
|
||||
'information.',
|
||||
'Travis CI, AppVeyor, Azure Pipelines, GitHub Actions and CircleCI are supported. You '
|
||||
'can run on your development machine or other CI providers using the --platform argument. '
|
||||
'Check --help output for more information.',
|
||||
file=sys.stderr)
|
||||
exit(2)
|
||||
if sys.platform.startswith('linux'):
|
||||
platform = 'linux'
|
||||
elif sys.platform == 'darwin':
|
||||
platform = 'macos'
|
||||
elif sys.platform == 'win32':
|
||||
platform = 'windows'
|
||||
else:
|
||||
print('cibuildwheel: Unable to detect platform from "sys.platform" in a CI environment. You can run '
|
||||
'cibuildwheel using the --platform argument. Check --help output for more information.',
|
||||
file=sys.stderr)
|
||||
exit(2)
|
||||
|
||||
output_dir = args.output_dir
|
||||
test_command = os.environ.get('CIBW_TEST_COMMAND', None)
|
||||
test_requires = os.environ.get('CIBW_TEST_REQUIRES', '').split()
|
||||
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='')
|
||||
project_dir = args.project_dir
|
||||
before_build = get_option_from_environment('CIBW_BEFORE_BUILD', platform=platform)
|
||||
build_verbosity = get_option_from_environment('CIBW_BUILD_VERBOSITY', platform=platform) or ''
|
||||
skip_config = os.environ.get('CIBW_SKIP', '')
|
||||
environment_config = get_option_from_environment('CIBW_ENVIRONMENT', platform=platform) or ''
|
||||
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', '')
|
||||
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)
|
||||
environment_config = get_option_from_environment('CIBW_ENVIRONMENT', platform=platform, default='')
|
||||
before_test = get_option_from_environment('CIBW_BEFORE_TEST', platform=platform, default='')
|
||||
|
||||
if test_extras:
|
||||
test_extras = '[{0}]'.format(test_extras)
|
||||
|
||||
try:
|
||||
build_verbosity = min(3, max(-3, int(build_verbosity)))
|
||||
@@ -85,62 +125,75 @@ def main():
|
||||
|
||||
try:
|
||||
environment = parse_environment(environment_config)
|
||||
except (EnvironmentParseError, ValueError) as e:
|
||||
except (EnvironmentParseError, ValueError):
|
||||
print('cibuildwheel: Malformed environment option "%s"' % environment_config, file=sys.stderr)
|
||||
import traceback
|
||||
traceback.print_exc(None, sys.stderr)
|
||||
exit(2)
|
||||
|
||||
skip = BuildSkipper(skip_config)
|
||||
build_selector = BuildSelector(build_config, skip_config)
|
||||
|
||||
# Add CIBUILDWHEEL environment variable
|
||||
# This needs to be passed on to the docker container in linux.py
|
||||
os.environ['CIBUILDWHEEL'] = '1'
|
||||
|
||||
try:
|
||||
project_setup_py = os.path.join(project_dir, 'setup.py')
|
||||
name_output = subprocess.check_output([sys.executable, project_setup_py, '--name'],
|
||||
universal_newlines=True)
|
||||
# the last line of output is the name
|
||||
package_name = name_output.strip().splitlines()[-1]
|
||||
except subprocess.CalledProcessError as err:
|
||||
if not os.path.exists(project_setup_py):
|
||||
print('cibuildwheel: Could not find setup.py at root of project', file=sys.stderr)
|
||||
exit(2)
|
||||
else:
|
||||
print('cibuildwheel: Failed to get name of the package. Command was %s' % err.cmd,
|
||||
file=sys.stderr)
|
||||
exit(2)
|
||||
|
||||
if package_name == '' or package_name == 'UNKNOWN':
|
||||
print('cibuildwheel: Invalid package name "%s". Check your setup.py' % package_name,
|
||||
file=sys.stderr)
|
||||
if not os.path.exists(os.path.join(project_dir, 'setup.py')):
|
||||
print('cibuildwheel: Could not find setup.py at root of project', file=sys.stderr)
|
||||
exit(2)
|
||||
|
||||
if args.print_build_identifiers:
|
||||
print_build_identifiers(platform, build_selector)
|
||||
exit(0)
|
||||
|
||||
build_options = dict(
|
||||
project_dir=project_dir,
|
||||
package_name=package_name,
|
||||
output_dir=output_dir,
|
||||
test_command=test_command,
|
||||
test_requires=test_requires,
|
||||
test_extras=test_extras,
|
||||
before_build=before_build,
|
||||
build_verbosity=build_verbosity,
|
||||
skip=skip,
|
||||
build_selector=build_selector,
|
||||
repair_command=repair_command,
|
||||
environment=environment,
|
||||
before_test=before_test
|
||||
)
|
||||
|
||||
if platform == 'linux':
|
||||
manylinux1_x86_64_image = os.environ.get('CIBW_MANYLINUX1_X86_64_IMAGE', None)
|
||||
manylinux1_i686_image = os.environ.get('CIBW_MANYLINUX1_I686_IMAGE', None)
|
||||
manylinux_x86_64_image = os.environ.get('CIBW_MANYLINUX_X86_64_IMAGE', 'manylinux2010')
|
||||
manylinux_i686_image = os.environ.get('CIBW_MANYLINUX_I686_IMAGE', 'manylinux2010')
|
||||
manylinux_pypy_x86_64_image = os.environ.get('CIBW_MANYLINUX_PYPY_X86_64_IMAGE', 'manylinux2010')
|
||||
manylinux_aarch64_image = os.environ.get('CIBW_MANYLINUX_AARCH64_IMAGE', 'manylinux2014')
|
||||
manylinux_ppc64le_image = os.environ.get('CIBW_MANYLINUX_PPC64LE_IMAGE', 'manylinux2014')
|
||||
manylinux_s390x_image = os.environ.get('CIBW_MANYLINUX_S390X_IMAGE', 'manylinux2014')
|
||||
|
||||
default_manylinux_images_x86_64 = {'manylinux1': 'quay.io/pypa/manylinux1_x86_64',
|
||||
'manylinux2010': 'quay.io/pypa/manylinux2010_x86_64',
|
||||
'manylinux2014': 'quay.io/pypa/manylinux2014_x86_64'}
|
||||
default_manylinux_images_i686 = {'manylinux1': 'quay.io/pypa/manylinux1_i686',
|
||||
'manylinux2010': 'quay.io/pypa/manylinux2010_i686',
|
||||
'manylinux2014': 'quay.io/pypa/manylinux2014_i686'}
|
||||
default_manylinux_images_pypy_x86_64 = {'manylinux2010': 'pypywheels/manylinux2010-pypy_x86_64'}
|
||||
default_manylinux_images_aarch64 = {'manylinux2014': 'quay.io/pypa/manylinux2014_aarch64'}
|
||||
default_manylinux_images_ppc64le = {'manylinux2014': 'quay.io/pypa/manylinux2014_ppc64le'}
|
||||
default_manylinux_images_s390x = {'manylinux2014': 'quay.io/pypa/manylinux2014_s390x'}
|
||||
|
||||
build_options.update(
|
||||
manylinux1_images={'x86_64': manylinux1_x86_64_image, 'i686': manylinux1_i686_image},
|
||||
manylinux_images={'x86_64': default_manylinux_images_x86_64.get(manylinux_x86_64_image) or manylinux_x86_64_image,
|
||||
'i686': default_manylinux_images_i686.get(manylinux_i686_image) or manylinux_i686_image,
|
||||
'pypy_x86_64': default_manylinux_images_pypy_x86_64.get(manylinux_pypy_x86_64_image) or manylinux_pypy_x86_64_image,
|
||||
'aarch64': default_manylinux_images_aarch64.get(manylinux_aarch64_image) or manylinux_aarch64_image,
|
||||
'ppc64le': default_manylinux_images_ppc64le.get(manylinux_ppc64le_image) or manylinux_ppc64le_image,
|
||||
's390x': default_manylinux_images_s390x.get(manylinux_s390x_image) or manylinux_s390x_image,
|
||||
},
|
||||
)
|
||||
elif platform == 'macos':
|
||||
pass
|
||||
elif platform == 'windows':
|
||||
pass
|
||||
|
||||
# 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)
|
||||
|
||||
print_preamble(platform, build_options)
|
||||
|
||||
if not os.path.exists(output_dir):
|
||||
@@ -153,7 +206,36 @@ def main():
|
||||
elif platform == 'macos':
|
||||
cibuildwheel.macos.build(**build_options)
|
||||
else:
|
||||
raise Exception('Unsupported platform')
|
||||
print('cibuildwheel: Unsupported platform: {}'.format(platform), file=sys.stderr)
|
||||
exit(2)
|
||||
|
||||
|
||||
def detect_obsolete_options():
|
||||
# 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))
|
||||
if alternative not in os.environ:
|
||||
print("Using value of option '{}' as replacement for '{}'".format(deprecated, alternative))
|
||||
os.environ[alternative] = os.environ[deprecated]
|
||||
else:
|
||||
print("Option '{}' is not empty. Please unset '{}'".format(alternative, deprecated))
|
||||
exit(2)
|
||||
|
||||
# Check for deprecated identifiers in 'CIBW_BUILD' and 'CIBW_SKIP' options
|
||||
for option in ['CIBW_BUILD', 'CIBW_SKIP']:
|
||||
for deprecated, alternative in [('manylinux1', 'manylinux'),
|
||||
('macosx_10_6_intel', 'macosx_x86_64'),
|
||||
('macosx_10_9_x86_64', 'macosx_x86_64')]:
|
||||
if option in os.environ and deprecated in os.environ[option]:
|
||||
print("Build identifiers with '{deprecated}' have been deprecated. Replacing all occurences of '{deprecated}' with '{alternative}' in the option '{option}'".format(
|
||||
deprecated=deprecated,
|
||||
alternative=alternative,
|
||||
option=option,
|
||||
))
|
||||
os.environ[option] = os.environ[option].replace(deprecated, alternative)
|
||||
|
||||
|
||||
def print_preamble(platform, build_options):
|
||||
print(textwrap.dedent('''
|
||||
@@ -165,13 +247,47 @@ def print_preamble(platform, build_options):
|
||||
|
||||
print('cibuildwheel version %s\n' % cibuildwheel.__version__)
|
||||
|
||||
|
||||
print('Build options:')
|
||||
print(' platform: %r' % platform)
|
||||
for option, value in build_options.items():
|
||||
for option, value in sorted(build_options.items()):
|
||||
print(' %s: %r' % (option, value))
|
||||
|
||||
warnings = detect_warnings(platform, build_options)
|
||||
if warnings:
|
||||
print('\nWarnings:')
|
||||
for warning in warnings:
|
||||
print(' ' + warning)
|
||||
|
||||
print('\nHere we go!\n')
|
||||
|
||||
|
||||
def print_build_identifiers(platform, build_selector):
|
||||
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):
|
||||
warnings = []
|
||||
|
||||
# warn about deprecated {python} and {pip}
|
||||
for option_name in ['test_command', 'before_build']:
|
||||
option_value = build_options.get(option_name)
|
||||
|
||||
if option_value:
|
||||
if '{python}' in option_value or '{pip}' in option_value:
|
||||
warnings.append(option_name + ": '{python}' and '{pip}' are no longer needed, and will be removed in a future release. Simply use 'python' or 'pip' instead.")
|
||||
|
||||
return warnings
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import subprocess, shlex, sys
|
||||
import shlex
|
||||
import subprocess
|
||||
from collections import namedtuple
|
||||
|
||||
import bashlex
|
||||
|
||||
NodeExecutionContext = namedtuple('NodeExecutionContext', ['environment', 'input'])
|
||||
|
||||
|
||||
def evaluate(value, environment):
|
||||
if not value:
|
||||
# empty string evaluates to empty string
|
||||
@@ -16,9 +19,9 @@ def evaluate(value, environment):
|
||||
raise ValueError('"%s" has too many parts' % value)
|
||||
|
||||
value_word_node = command_node.parts[0]
|
||||
|
||||
|
||||
return evaluate_node(
|
||||
value_word_node,
|
||||
value_word_node,
|
||||
context=NodeExecutionContext(environment=environment, input=value)
|
||||
)
|
||||
|
||||
@@ -60,12 +63,8 @@ def evaluate_word_node(node, context):
|
||||
def evaluate_command_node(node, context):
|
||||
words = [evaluate_node(part, context=context) for part in node.parts]
|
||||
command = ' '.join(words)
|
||||
output = subprocess.check_output(shlex.split(command), env=context.environment)
|
||||
return subprocess.check_output(shlex.split(command), env=context.environment, universal_newlines=True)
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
return output.decode('utf8', 'replace')
|
||||
else:
|
||||
return output
|
||||
|
||||
def evaluate_parameter_node(node, context):
|
||||
return context.environment.get(node.value, '')
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import bashlex
|
||||
|
||||
from . import bashlex_eval
|
||||
|
||||
|
||||
@@ -41,7 +42,7 @@ def split_env_items(env_string):
|
||||
return result
|
||||
|
||||
|
||||
class EnvironmentAssignment(object):
|
||||
class EnvironmentAssignment:
|
||||
def __init__(self, assignment):
|
||||
name, equals, value = assignment.partition('=')
|
||||
if not equals:
|
||||
@@ -60,7 +61,7 @@ class EnvironmentAssignment(object):
|
||||
return '%s=%s' % (self.name, self.value)
|
||||
|
||||
|
||||
class ParsedEnvironment(object):
|
||||
class ParsedEnvironment:
|
||||
def __init__(self, assignments):
|
||||
self.assignments = assignments
|
||||
|
||||
|
||||
+210
-87
@@ -1,134 +1,257 @@
|
||||
from __future__ import print_function
|
||||
import os, subprocess, sys
|
||||
import os
|
||||
import platform
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
import textwrap
|
||||
import uuid
|
||||
from collections import namedtuple
|
||||
from .util import prepare_command, get_build_verbosity_extra_flags
|
||||
|
||||
try:
|
||||
from shlex import quote as shlex_quote
|
||||
except ImportError:
|
||||
from pipes import quote as shlex_quote
|
||||
from .util import (
|
||||
get_build_verbosity_extra_flags,
|
||||
prepare_command,
|
||||
)
|
||||
|
||||
|
||||
def build(project_dir, package_name, output_dir, test_command, test_requires, before_build, build_verbosity, skip, environment, manylinux1_images):
|
||||
def matches_platform(identifier):
|
||||
pm = platform.machine()
|
||||
if pm == "x86_64":
|
||||
# x86_64 machines can run i686 docker containers
|
||||
if identifier.endswith('x86_64') or identifier.endswith('i686'):
|
||||
return True
|
||||
elif pm == "i686":
|
||||
if identifier.endswith('i686'):
|
||||
return True
|
||||
elif pm == "aarch64":
|
||||
if identifier.endswith('aarch64'):
|
||||
return True
|
||||
elif pm == "ppc64le":
|
||||
if identifier.endswith('ppc64le'):
|
||||
return True
|
||||
elif pm == "s390x":
|
||||
if identifier.endswith('s390x'):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def get_python_configurations(build_selector):
|
||||
PythonConfiguration = namedtuple('PythonConfiguration', ['identifier', 'path'])
|
||||
python_configurations = [
|
||||
PythonConfiguration(identifier='cp27-manylinux_x86_64', path='/opt/python/cp27-cp27m'),
|
||||
PythonConfiguration(identifier='cp27-manylinux_x86_64', path='/opt/python/cp27-cp27mu'),
|
||||
PythonConfiguration(identifier='cp35-manylinux_x86_64', path='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(identifier='cp36-manylinux_x86_64', path='/opt/python/cp36-cp36m'),
|
||||
PythonConfiguration(identifier='cp37-manylinux_x86_64', path='/opt/python/cp37-cp37m'),
|
||||
PythonConfiguration(identifier='cp38-manylinux_x86_64', path='/opt/python/cp38-cp38'),
|
||||
PythonConfiguration(identifier='cp27-manylinux_i686', path='/opt/python/cp27-cp27m'),
|
||||
PythonConfiguration(identifier='cp27-manylinux_i686', path='/opt/python/cp27-cp27mu'),
|
||||
PythonConfiguration(identifier='cp35-manylinux_i686', path='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(identifier='cp36-manylinux_i686', path='/opt/python/cp36-cp36m'),
|
||||
PythonConfiguration(identifier='cp37-manylinux_i686', path='/opt/python/cp37-cp37m'),
|
||||
PythonConfiguration(identifier='cp38-manylinux_i686', path='/opt/python/cp38-cp38'),
|
||||
PythonConfiguration(identifier='pp27-manylinux_x86_64', path='/opt/python/pp27-pypy_73'),
|
||||
PythonConfiguration(identifier='pp36-manylinux_x86_64', path='/opt/python/pp36-pypy36_pp73'),
|
||||
PythonConfiguration(identifier='cp35-manylinux_aarch64', path='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(identifier='cp36-manylinux_aarch64', path='/opt/python/cp36-cp36m'),
|
||||
PythonConfiguration(identifier='cp37-manylinux_aarch64', path='/opt/python/cp37-cp37m'),
|
||||
PythonConfiguration(identifier='cp38-manylinux_aarch64', path='/opt/python/cp38-cp38'),
|
||||
PythonConfiguration(identifier='cp35-manylinux_ppc64le', path='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(identifier='cp36-manylinux_ppc64le', path='/opt/python/cp36-cp36m'),
|
||||
PythonConfiguration(identifier='cp37-manylinux_ppc64le', path='/opt/python/cp37-cp37m'),
|
||||
PythonConfiguration(identifier='cp38-manylinux_ppc64le', path='/opt/python/cp38-cp38'),
|
||||
PythonConfiguration(identifier='cp35-manylinux_s390x', path='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(identifier='cp36-manylinux_s390x', path='/opt/python/cp36-cp36m'),
|
||||
PythonConfiguration(identifier='cp37-manylinux_s390x', path='/opt/python/cp37-cp37m'),
|
||||
PythonConfiguration(identifier='cp38-manylinux_s390x', path='/opt/python/cp38-cp38'),
|
||||
]
|
||||
# skip builds as required
|
||||
return [c for c in python_configurations if matches_platform(c.identifier) and build_selector(c.identifier)]
|
||||
|
||||
|
||||
def build(project_dir, output_dir, test_command, before_test, test_requires, test_extras, before_build, build_verbosity, build_selector, repair_command, environment, manylinux_images):
|
||||
try:
|
||||
subprocess.check_call(['docker', '--version'])
|
||||
except:
|
||||
except Exception:
|
||||
print('cibuildwheel: Docker not found. Docker is required to run Linux builds. '
|
||||
'If you\'re building on Travis CI, add `services: [docker]` to your .travis.yml.',
|
||||
'If you\'re building on Travis CI, add `services: [docker]` to your .travis.yml.'
|
||||
'If you\'re building on Circle CI in Linux, add a `setup_remote_docker` step to your .circleci/config.yml',
|
||||
file=sys.stderr)
|
||||
exit(2)
|
||||
|
||||
PythonConfiguration = namedtuple('PythonConfiguration', ['identifier', 'path'])
|
||||
python_configurations = [
|
||||
PythonConfiguration(identifier='cp27-manylinux1_x86_64', path='/opt/python/cp27-cp27m'),
|
||||
PythonConfiguration(identifier='cp27-manylinux1_x86_64', path='/opt/python/cp27-cp27mu'),
|
||||
PythonConfiguration(identifier='cp34-manylinux1_x86_64', path='/opt/python/cp34-cp34m'),
|
||||
PythonConfiguration(identifier='cp35-manylinux1_x86_64', path='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(identifier='cp36-manylinux1_x86_64', path='/opt/python/cp36-cp36m'),
|
||||
PythonConfiguration(identifier='cp27-manylinux1_i686', path='/opt/python/cp27-cp27m'),
|
||||
PythonConfiguration(identifier='cp27-manylinux1_i686', path='/opt/python/cp27-cp27mu'),
|
||||
PythonConfiguration(identifier='cp34-manylinux1_i686', path='/opt/python/cp34-cp34m'),
|
||||
PythonConfiguration(identifier='cp35-manylinux1_i686', path='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(identifier='cp36-manylinux1_i686', path='/opt/python/cp36-cp36m'),
|
||||
]
|
||||
|
||||
# skip builds as required
|
||||
python_configurations = [c for c in python_configurations if not skip(c.identifier)]
|
||||
|
||||
python_configurations = get_python_configurations(build_selector)
|
||||
platforms = [
|
||||
('manylinux1_x86_64', manylinux1_images.get('x86_64') or 'quay.io/pypa/manylinux1_x86_64'),
|
||||
('manylinux1_i686', manylinux1_images.get('i686') or 'quay.io/pypa/manylinux1_i686'),
|
||||
('cp', 'manylinux_x86_64', manylinux_images['x86_64']),
|
||||
('cp', 'manylinux_i686', manylinux_images['i686']),
|
||||
('cp', 'manylinux_aarch64', manylinux_images['aarch64']),
|
||||
('cp', 'manylinux_ppc64le', manylinux_images['ppc64le']),
|
||||
('cp', 'manylinux_s390x', manylinux_images['s390x']),
|
||||
('pp', 'manylinux_x86_64', manylinux_images['pypy_x86_64']),
|
||||
]
|
||||
|
||||
for platform_tag, docker_image in platforms:
|
||||
platform_configs = [c for c in python_configurations if c.identifier.endswith(platform_tag)]
|
||||
for implementation, platform_tag, docker_image in platforms:
|
||||
platform_configs = [c for c in python_configurations if c.identifier.startswith(implementation) and c.identifier.endswith(platform_tag)]
|
||||
if not platform_configs:
|
||||
continue
|
||||
|
||||
bash_script = '''
|
||||
set -o errexit
|
||||
set -o xtrace
|
||||
mkdir /output
|
||||
cd /project
|
||||
|
||||
{environment_exports}
|
||||
for PYBIN in {pybin_paths}; do (
|
||||
# Temporary hack/workaround, putting loop body in subshell; fixed in PR #256
|
||||
|
||||
for PYBIN in {pybin_paths}; do
|
||||
# Setup
|
||||
rm -rf /tmp/built_wheel
|
||||
rm -rf /tmp/delocated_wheel
|
||||
mkdir /tmp/built_wheel
|
||||
mkdir /tmp/delocated_wheel
|
||||
export PATH="$PYBIN:$PATH"
|
||||
{environment_exports}
|
||||
|
||||
# check the active python and pip are in PYBIN
|
||||
if [ "$(which pip)" != "$PYBIN/pip" ]; then
|
||||
echo "cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it."
|
||||
exit 1
|
||||
fi
|
||||
if [ "$(which python)" != "$PYBIN/python" ]; then
|
||||
echo "cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -z {before_build} ]; then
|
||||
PATH="$PYBIN:$PATH" sh -c {before_build}
|
||||
sh -c {before_build}
|
||||
fi
|
||||
|
||||
# Build that wheel
|
||||
PATH="$PYBIN:$PATH" "$PYBIN/pip" wheel . -w /tmp/built_wheel --no-deps {build_verbosity_flag}
|
||||
# Build the wheel
|
||||
rm -rf /tmp/built_wheel
|
||||
mkdir /tmp/built_wheel
|
||||
pip wheel . -w /tmp/built_wheel --no-deps {build_verbosity_flag}
|
||||
built_wheel=(/tmp/built_wheel/*.whl)
|
||||
|
||||
# Delocate the wheel
|
||||
# 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 ]]; then
|
||||
# pure python wheel - just copy
|
||||
mv "$built_wheel" /tmp/delocated_wheel
|
||||
if [[ "$built_wheel" == *none-any.whl ]] || [ -z {repair_command} ]; then
|
||||
# pure Python wheel or empty repair command
|
||||
mv "$built_wheel" /tmp/repaired_wheels
|
||||
else
|
||||
auditwheel repair "$built_wheel" -w /tmp/delocated_wheel
|
||||
sh -c {repair_command} repair_command "$built_wheel"
|
||||
fi
|
||||
delocated_wheel=(/tmp/delocated_wheel/*.whl)
|
||||
repaired_wheels=(/tmp/repaired_wheels/*.whl)
|
||||
|
||||
# Install the wheel we just built
|
||||
"$PYBIN/pip" install "$delocated_wheel"
|
||||
|
||||
# Install any requirements to run the tests
|
||||
if [ ! -z "{test_requires}" ]; then
|
||||
"$PYBIN/pip" install {test_requires}
|
||||
fi
|
||||
|
||||
# Run the tests from a different directory
|
||||
if [ ! -z {test_command} ]; then
|
||||
pushd $HOME
|
||||
PATH="$PYBIN:$PATH" sh -c {test_command}
|
||||
popd
|
||||
# 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 virtualenv
|
||||
venv_dir=`mktemp -d`/venv
|
||||
python -m virtualenv "$venv_dir"
|
||||
|
||||
export __CIBW_VIRTUALENV_PATH__=$venv_dir
|
||||
|
||||
# run the tests in a subshell to keep that `activate`
|
||||
# script from polluting the env
|
||||
(
|
||||
source "$venv_dir/bin/activate"
|
||||
|
||||
echo "Running tests using `which python`"
|
||||
|
||||
if [ ! -z {before_test} ]; then
|
||||
sh -c {before_test}
|
||||
fi
|
||||
|
||||
# Install the wheel we just built
|
||||
# Note: If auditwheel produced two wheels, it's because the earlier produced wheel
|
||||
# conforms to multiple manylinux standards. These multiple versions of the wheel are
|
||||
# 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}
|
||||
|
||||
# Install any requirements to run the tests
|
||||
if [ ! -z "{test_requires}" ]; then
|
||||
pip install {test_requires}
|
||||
fi
|
||||
|
||||
# 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
|
||||
|
||||
# clean up
|
||||
rm -rf "$venv_dir"
|
||||
fi
|
||||
|
||||
# we're all done here; move it to output
|
||||
mv "$delocated_wheel" /output
|
||||
done
|
||||
mv "${{repaired_wheels[@]}}" /output
|
||||
for repaired_wheel in "${{repaired_wheels[@]}}"; do chown {uid}:{gid} "/output/$(basename "$repaired_wheel")"; done
|
||||
) done
|
||||
'''.format(
|
||||
package_name=package_name,
|
||||
pybin_paths=' '.join(c.path+'/bin' for c in platform_configs),
|
||||
pybin_paths=' '.join(c.path + '/bin' for c in platform_configs),
|
||||
test_requires=' '.join(test_requires),
|
||||
test_command=shlex_quote(
|
||||
prepare_command(test_command, python='python', pip='pip', project='/project') if test_command else ''
|
||||
test_extras=test_extras,
|
||||
test_command=shlex.quote(
|
||||
prepare_command(test_command, project='/project') if test_command else ''
|
||||
),
|
||||
before_build=shlex_quote(
|
||||
prepare_command(before_build, python='python', pip='pip', project='/project') if before_build else ''
|
||||
before_build=shlex.quote(
|
||||
prepare_command(before_build, project='/project') if before_build else ''
|
||||
),
|
||||
build_verbosity_flag=' '.join(get_build_verbosity_extra_flags(build_verbosity)),
|
||||
repair_command=shlex.quote(
|
||||
prepare_command(repair_command, wheel='"$1"', dest_dir='/tmp/repaired_wheels') if repair_command else ''
|
||||
),
|
||||
environment_exports='\n'.join(environment.as_shell_commands()),
|
||||
uid=os.getuid(),
|
||||
gid=os.getgid(),
|
||||
before_test=shlex.quote(
|
||||
prepare_command(before_test, project='/project') if before_test else ''
|
||||
),
|
||||
)
|
||||
|
||||
docker_process = subprocess.Popen([
|
||||
'docker',
|
||||
'run',
|
||||
'--env',
|
||||
'CIBUILDWHEEL',
|
||||
'--rm',
|
||||
'-i',
|
||||
'-v', '%s:/project' % os.path.abspath(project_dir),
|
||||
'-v', '%s:/output' % os.path.abspath(output_dir),
|
||||
'-v', '/:/host',
|
||||
docker_image,
|
||||
'/bin/bash'],
|
||||
stdin=subprocess.PIPE, universal_newlines=True)
|
||||
|
||||
container_name = 'cibuildwheel-{}'.format(uuid.uuid4())
|
||||
try:
|
||||
docker_process.communicate(bash_script)
|
||||
except KeyboardInterrupt:
|
||||
docker_process.kill()
|
||||
docker_process.wait()
|
||||
|
||||
if docker_process.returncode != 0:
|
||||
subprocess.run(['docker', 'create',
|
||||
'--env', 'CIBUILDWHEEL',
|
||||
'--name', container_name,
|
||||
'-i',
|
||||
'-v', '/:/host', # ignored on CircleCI
|
||||
docker_image, '/bin/bash'], check=True)
|
||||
subprocess.run(['docker', 'cp', os.path.abspath(project_dir) + '/.', container_name + ':/project'], check=True)
|
||||
subprocess.run(['docker', 'start', '-i', '-a', container_name], input=bash_script, universal_newlines=True, check=True)
|
||||
subprocess.run(['docker', 'cp', container_name + ':/output/.', os.path.abspath(output_dir)], check=True)
|
||||
except subprocess.CalledProcessError as error:
|
||||
troubleshoot(project_dir, error)
|
||||
exit(1)
|
||||
finally:
|
||||
# Still gets executed, even when 'exit(1)' gets called
|
||||
subprocess.run(['docker', 'rm', '--force', '-v', container_name], check=True)
|
||||
|
||||
|
||||
def troubleshoot(project_dir, error):
|
||||
if (isinstance(error, subprocess.CalledProcessError) and 'start' in error.cmd):
|
||||
# the bash script failed
|
||||
print('Checking for common errors...')
|
||||
so_files = []
|
||||
for root, dirs, files in os.walk(project_dir):
|
||||
for name in files:
|
||||
_, ext = os.path.splitext(name)
|
||||
if ext == '.so':
|
||||
so_files.append(os.path.join(root, name))
|
||||
|
||||
if so_files:
|
||||
print(textwrap.dedent('''
|
||||
NOTE: Shared object (.so) files found in this project.
|
||||
|
||||
These files might be built against the wrong OS, causing problems with
|
||||
auditwheel.
|
||||
|
||||
If you're using Cython and have previously done an in-place build,
|
||||
remove those build files (*.so and *.c) before starting cibuildwheel.
|
||||
'''))
|
||||
|
||||
print(' Files detected:')
|
||||
print('\n'.join([' ' + f for f in so_files]))
|
||||
print('')
|
||||
|
||||
+200
-79
@@ -1,118 +1,239 @@
|
||||
from __future__ import print_function
|
||||
import os, subprocess, shlex, sys, shutil
|
||||
import os
|
||||
import shlex
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from collections import namedtuple
|
||||
from glob import glob
|
||||
try:
|
||||
from shlex import quote as shlex_quote
|
||||
except ImportError:
|
||||
from pipes import quote as shlex_quote
|
||||
|
||||
from .util import prepare_command, get_build_verbosity_extra_flags
|
||||
from .util import (
|
||||
download,
|
||||
get_build_verbosity_extra_flags,
|
||||
prepare_command,
|
||||
)
|
||||
|
||||
|
||||
def build(project_dir, package_name, output_dir, test_command, test_requires, before_build, build_verbosity, skip, environment):
|
||||
def call(args, env=None, cwd=None, shell=False):
|
||||
# print the command executing for the logs
|
||||
if shell:
|
||||
print('+ %s' % args)
|
||||
else:
|
||||
print('+ ' + ' '.join(shlex.quote(a) for a in args))
|
||||
|
||||
return subprocess.check_call(args, env=env, cwd=cwd, shell=shell)
|
||||
|
||||
|
||||
def get_python_configurations(build_selector):
|
||||
PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'identifier', 'url'])
|
||||
python_configurations = [
|
||||
PythonConfiguration(version='2.7', identifier='cp27-macosx_10_6_intel', url='https://www.python.org/ftp/python/2.7.15/python-2.7.15-macosx10.6.pkg'),
|
||||
PythonConfiguration(version='3.4', identifier='cp34-macosx_10_6_intel', url='https://www.python.org/ftp/python/3.4.4/python-3.4.4-macosx10.6.pkg'),
|
||||
PythonConfiguration(version='3.5', identifier='cp35-macosx_10_6_intel', url='https://www.python.org/ftp/python/3.5.4/python-3.5.4-macosx10.6.pkg'),
|
||||
PythonConfiguration(version='3.6', identifier='cp36-macosx_10_6_intel', url='https://www.python.org/ftp/python/3.6.5/python-3.6.5-macosx10.6.pkg'),
|
||||
PythonConfiguration(version='2.7', identifier='cp27-macosx_x86_64', url='https://www.python.org/ftp/python/2.7.17/python-2.7.17-macosx10.9.pkg'),
|
||||
PythonConfiguration(version='3.5', identifier='cp35-macosx_x86_64', url='https://www.python.org/ftp/python/3.5.4/python-3.5.4-macosx10.6.pkg'),
|
||||
PythonConfiguration(version='3.6', identifier='cp36-macosx_x86_64', url='https://www.python.org/ftp/python/3.6.8/python-3.6.8-macosx10.9.pkg'),
|
||||
PythonConfiguration(version='3.7', identifier='cp37-macosx_x86_64', url='https://www.python.org/ftp/python/3.7.6/python-3.7.6-macosx10.9.pkg'),
|
||||
PythonConfiguration(version='3.8', identifier='cp38-macosx_x86_64', url='https://www.python.org/ftp/python/3.8.2/python-3.8.2-macosx10.9.pkg'),
|
||||
PythonConfiguration(version='2.7-v7.3.0', identifier='pp27-macosx_x86_64', url='https://bitbucket.org/pypy/pypy/downloads/pypy2.7-v7.3.0-osx64.tar.bz2'),
|
||||
PythonConfiguration(version='3.6-v7.3.0', identifier='pp36-macosx_x86_64', url='https://bitbucket.org/pypy/pypy/downloads/pypy3.6-v7.3.0-osx64.tar.bz2'),
|
||||
]
|
||||
|
||||
# skip builds as required
|
||||
return [c for c in python_configurations if build_selector(c.identifier)]
|
||||
|
||||
|
||||
SYMLINKS_DIR = '/tmp/cibw_bin'
|
||||
|
||||
|
||||
def make_symlinks(installation_bin_path, python_executable, pip_executable):
|
||||
assert os.path.exists(os.path.join(installation_bin_path, python_executable))
|
||||
|
||||
# Python bin folders on Mac don't symlink `python3` to `python`, and neither
|
||||
# does PyPy for `pypy` or `pypy3`, so we do that so `python` and `pip` always
|
||||
# point to the active configuration.
|
||||
if os.path.exists(SYMLINKS_DIR):
|
||||
shutil.rmtree(SYMLINKS_DIR)
|
||||
os.makedirs(SYMLINKS_DIR)
|
||||
|
||||
os.symlink(os.path.join(installation_bin_path, python_executable), os.path.join(SYMLINKS_DIR, 'python'))
|
||||
os.symlink(os.path.join(installation_bin_path, python_executable + '-config'), os.path.join(SYMLINKS_DIR, 'python-config'))
|
||||
os.symlink(os.path.join(installation_bin_path, pip_executable), os.path.join(SYMLINKS_DIR, 'pip'))
|
||||
|
||||
|
||||
def install_cpython(version, url):
|
||||
installed_system_packages = subprocess.check_output(['pkgutil', '--pkgs'], universal_newlines=True).splitlines()
|
||||
|
||||
# if this version of python isn't installed, get it from python.org and install
|
||||
python_package_identifier = 'org.python.Python.PythonFramework-{}'.format(version)
|
||||
if python_package_identifier not in installed_system_packages:
|
||||
# download the pkg
|
||||
download(url, '/tmp/Python.pkg')
|
||||
# install
|
||||
call(['sudo', 'installer', '-pkg', '/tmp/Python.pkg', '-target', '/'])
|
||||
# patch open ssl
|
||||
if version == '3.5':
|
||||
open_ssl_patch_url = 'https://github.com/mayeut/patch-macos-python-openssl/releases/download/v1.0.2t/patch-macos-python-%s-openssl-v1.0.2t.tar.gz' % version
|
||||
download(open_ssl_patch_url, '/tmp/python-patch.tar.gz')
|
||||
call(['sudo', 'tar', '-C', '/Library/Frameworks/Python.framework/Versions/{}/'.format(version), '-xmf', '/tmp/python-patch.tar.gz'])
|
||||
|
||||
installation_bin_path = '/Library/Frameworks/Python.framework/Versions/{}/bin'.format(version)
|
||||
python_executable = 'python3' if version[0] == '3' else 'python'
|
||||
pip_executable = 'pip3' if version[0] == '3' else 'pip'
|
||||
make_symlinks(installation_bin_path, python_executable, pip_executable)
|
||||
|
||||
return installation_bin_path
|
||||
|
||||
|
||||
def install_pypy(version, url):
|
||||
pypy_tar_bz2 = url.rsplit('/', 1)[-1]
|
||||
assert pypy_tar_bz2.endswith(".tar.bz2")
|
||||
pypy_base_filename = os.path.splitext(os.path.splitext(pypy_tar_bz2)[0])[0]
|
||||
installation_path = os.path.join('/tmp', pypy_base_filename)
|
||||
if not os.path.exists(installation_path):
|
||||
download(url, os.path.join("/tmp", pypy_tar_bz2))
|
||||
call(['tar', '-C', '/tmp', '-xf', os.path.join("/tmp", pypy_tar_bz2)])
|
||||
|
||||
# fix PyPy 7.3.0 bug resulting in wrong macOS platform tag
|
||||
if version.endswith("-v7.3.0") and version[0] == '3':
|
||||
patch_file = os.path.abspath(os.path.join(os.path.dirname(__file__), 'resources', 'pypy3.6.patch'))
|
||||
sysconfigdata_file = os.path.join(installation_path, 'lib_pypy', '_sysconfigdata.py')
|
||||
call(['patch', sysconfigdata_file, patch_file, '-N']) # Always has nonzero return code
|
||||
|
||||
installation_bin_path = os.path.join(installation_path, 'bin')
|
||||
python_executable = 'pypy3' if version[0] == '3' else 'pypy'
|
||||
pip_executable = 'pip3' if version[0] == '3' else 'pip'
|
||||
make_symlinks(installation_bin_path, python_executable, pip_executable)
|
||||
|
||||
return installation_bin_path
|
||||
|
||||
|
||||
def build(project_dir, output_dir, test_command, before_test, test_requires, test_extras, before_build, build_verbosity, build_selector, repair_command, environment):
|
||||
abs_project_dir = os.path.abspath(project_dir)
|
||||
temp_dir = tempfile.mkdtemp(prefix='cibuildwheel')
|
||||
built_wheel_dir = os.path.join(temp_dir, 'built_wheel')
|
||||
repaired_wheel_dir = os.path.join(temp_dir, 'repaired_wheel')
|
||||
|
||||
python_configurations = get_python_configurations(build_selector)
|
||||
|
||||
get_pip_url = 'https://bootstrap.pypa.io/get-pip.py'
|
||||
get_pip_script = '/tmp/get-pip.py'
|
||||
|
||||
pkgs_output = subprocess.check_output(['pkgutil', '--pkgs'])
|
||||
if sys.version_info[0] >= 3:
|
||||
pkgs_output = pkgs_output.decode('utf8')
|
||||
installed_system_packages = pkgs_output.splitlines()
|
||||
|
||||
def call(args, env=None, cwd=None, shell=False):
|
||||
# print the command executing for the logs
|
||||
if shell:
|
||||
print('+ %s' % args)
|
||||
else:
|
||||
print('+ ' + ' '.join(shlex_quote(a) for a in args))
|
||||
|
||||
return subprocess.check_call(args, env=env, cwd=cwd, shell=shell)
|
||||
|
||||
abs_project_dir = os.path.abspath(project_dir)
|
||||
|
||||
# get latest pip once and for all
|
||||
call(['curl', '-L', '-o', get_pip_script, get_pip_url])
|
||||
download(get_pip_url, get_pip_script)
|
||||
|
||||
for config in python_configurations:
|
||||
if skip(config.identifier):
|
||||
print('cibuildwheel: Skipping build %s' % config.identifier, file=sys.stderr)
|
||||
continue
|
||||
|
||||
# if this version of python isn't installed, get it from python.org and install
|
||||
python_package_identifier = 'org.python.Python.PythonFramework-%s' % config.version
|
||||
if python_package_identifier not in installed_system_packages:
|
||||
# download the pkg
|
||||
call(['curl', '-L', '-o', '/tmp/Python.pkg', config.url])
|
||||
# install
|
||||
call(['sudo', 'installer', '-pkg', '/tmp/Python.pkg', '-target', '/'])
|
||||
if config.identifier.startswith('cp'):
|
||||
installation_bin_path = install_cpython(config.version, config.url)
|
||||
elif config.identifier.startswith('pp'):
|
||||
installation_bin_path = install_pypy(config.version, config.url)
|
||||
else:
|
||||
raise ValueError("Unknown Python implementation")
|
||||
|
||||
env = os.environ.copy()
|
||||
env['PATH'] = os.pathsep.join([
|
||||
'/Library/Frameworks/Python.framework/Versions/%s/bin' % config.version,
|
||||
SYMLINKS_DIR,
|
||||
installation_bin_path,
|
||||
env['PATH'],
|
||||
])
|
||||
|
||||
# Fix issue with site.py setting the wrong `sys.prefix`, `sys.exec_prefix`,
|
||||
# `sys.path`, ... for PyPy: https://foss.heptapod.net/pypy/pypy/issues/3175
|
||||
# Also fix an issue with the shebang of installed scripts inside the
|
||||
# testing virtualenv- see https://github.com/theacodes/nox/issues/44 and
|
||||
# https://github.com/pypa/virtualenv/issues/620
|
||||
# Also see https://github.com/python/cpython/pull/9516
|
||||
env.pop('__PYVENV_LAUNCHER__', None)
|
||||
env = environment.as_dictionary(prev_environment=env)
|
||||
|
||||
python = 'python3' if config.version[0] == '3' else 'python2'
|
||||
pip = 'pip3' if config.version[0] == '3' else 'pip2'
|
||||
|
||||
# check what version we're on
|
||||
call(['which', python], env=env)
|
||||
call([python, '--version'], env=env)
|
||||
call(['which', 'python'], env=env)
|
||||
call(['python', '--version'], env=env)
|
||||
which_python = subprocess.check_output(['which', 'python'], env=env, universal_newlines=True).strip()
|
||||
if which_python != '/tmp/cibw_bin/python':
|
||||
print("cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it.", file=sys.stderr)
|
||||
exit(1)
|
||||
|
||||
# install pip & wheel
|
||||
call([python, get_pip_script, '--no-setuptools', '--no-wheel'], env=env)
|
||||
call([pip, '--version'], env=env)
|
||||
call([pip, 'install', '--upgrade', 'setuptools'], env=env)
|
||||
call([pip, 'install', 'wheel'], env=env)
|
||||
call([pip, 'install', 'delocate'], env=env)
|
||||
call(['python', get_pip_script], env=env, cwd="/tmp")
|
||||
assert os.path.exists(os.path.join(installation_bin_path, 'pip'))
|
||||
call(['which', 'pip'], env=env)
|
||||
call(['pip', '--version'], env=env)
|
||||
which_pip = subprocess.check_output(['which', 'pip'], env=env, universal_newlines=True).strip()
|
||||
if which_pip != '/tmp/cibw_bin/pip':
|
||||
print("cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it.", file=sys.stderr)
|
||||
exit(1)
|
||||
call(['pip', 'install', '--upgrade', 'setuptools', 'wheel', 'delocate'], env=env)
|
||||
|
||||
# setup dirs
|
||||
if os.path.exists('/tmp/built_wheel'):
|
||||
shutil.rmtree('/tmp/built_wheel')
|
||||
os.makedirs('/tmp/built_wheel')
|
||||
if os.path.exists('/tmp/delocated_wheel'):
|
||||
shutil.rmtree('/tmp/delocated_wheel')
|
||||
os.makedirs('/tmp/delocated_wheel')
|
||||
# setup target platform, only required for python 3.5
|
||||
if config.version == '3.5':
|
||||
if '_PYTHON_HOST_PLATFORM' not in env:
|
||||
# cross-compilation platform override
|
||||
env['_PYTHON_HOST_PLATFORM'] = 'macosx-10.9-x86_64'
|
||||
if 'ARCHFLAGS' not in env:
|
||||
# https://github.com/python/cpython/blob/a5ed2fe0eedefa1649aa93ee74a0bafc8e628a10/Lib/_osx_support.py#L260
|
||||
env['ARCHFLAGS'] = '-arch x86_64'
|
||||
if 'MACOSX_DEPLOYMENT_TARGET' not in env:
|
||||
env['MACOSX_DEPLOYMENT_TARGET'] = '10.9'
|
||||
|
||||
# run the before_build command
|
||||
if before_build:
|
||||
before_build_prepared = prepare_command(before_build, python=python, pip=pip, project=abs_project_dir)
|
||||
before_build_prepared = prepare_command(before_build, project=abs_project_dir)
|
||||
call(before_build_prepared, env=env, shell=True)
|
||||
|
||||
# build the wheel
|
||||
call([pip, 'wheel', abs_project_dir, '-w', '/tmp/built_wheel', '--no-deps'] + get_build_verbosity_extra_flags(build_verbosity), env=env)
|
||||
built_wheel = glob('/tmp/built_wheel/*.whl')[0]
|
||||
if os.path.exists(built_wheel_dir):
|
||||
shutil.rmtree(built_wheel_dir)
|
||||
os.makedirs(built_wheel_dir)
|
||||
call(['pip', 'wheel', abs_project_dir, '-w', built_wheel_dir, '--no-deps'] + get_build_verbosity_extra_flags(build_verbosity), env=env)
|
||||
built_wheel = glob(os.path.join(built_wheel_dir, '*.whl'))[0]
|
||||
|
||||
if built_wheel.endswith('none-any.whl'):
|
||||
# pure python wheel - just move
|
||||
shutil.move(built_wheel, '/tmp/delocated_wheel')
|
||||
# repair the wheel
|
||||
if os.path.exists(repaired_wheel_dir):
|
||||
shutil.rmtree(repaired_wheel_dir)
|
||||
os.makedirs(repaired_wheel_dir)
|
||||
if built_wheel.endswith('none-any.whl') or not repair_command:
|
||||
# pure Python wheel or empty repair command
|
||||
shutil.move(built_wheel, repaired_wheel_dir)
|
||||
else:
|
||||
# list the dependencies
|
||||
call(['delocate-listdeps', built_wheel], env=env)
|
||||
# rebuild the wheel with shared libraries included and place in output dir
|
||||
call(['delocate-wheel', '-w', '/tmp/delocated_wheel', built_wheel], env=env)
|
||||
delocated_wheel = glob('/tmp/delocated_wheel/*.whl')[0]
|
||||
repair_command_prepared = prepare_command(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]
|
||||
|
||||
# install the wheel
|
||||
call([pip, 'install', delocated_wheel], env=env)
|
||||
|
||||
# test the wheel
|
||||
if test_requires:
|
||||
call([pip, 'install'] + test_requires, env=env)
|
||||
if 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'], env=env)
|
||||
venv_dir = tempfile.mkdtemp()
|
||||
call(['python', '-m', 'virtualenv', venv_dir], env=env)
|
||||
|
||||
virtualenv_env = env.copy()
|
||||
virtualenv_env['PATH'] = os.pathsep.join([
|
||||
os.path.join(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)
|
||||
|
||||
if before_test:
|
||||
before_test_prepared = prepare_command(before_test, project=abs_project_dir)
|
||||
call(before_test_prepared, env=virtualenv_env, shell=True)
|
||||
|
||||
# install the wheel
|
||||
call(['pip', 'install', repaired_wheel + test_extras], env=virtualenv_env)
|
||||
|
||||
# test the wheel
|
||||
if test_requires:
|
||||
call(['pip', 'install'] + test_requires, env=virtualenv_env)
|
||||
|
||||
# run the tests from $HOME, with an absolute path in the command
|
||||
# (this ensures that Python runs the tests against the installed wheel
|
||||
# and not the repo code)
|
||||
test_command_prepared = prepare_command(test_command, python=python, pip=pip, project=abs_project_dir)
|
||||
call(shlex.split(test_command_prepared), cwd=os.environ['HOME'], env=env)
|
||||
test_command_prepared = prepare_command(test_command, project=abs_project_dir)
|
||||
call(test_command_prepared, cwd=os.environ['HOME'], env=virtualenv_env, shell=True)
|
||||
|
||||
# we're all done here; move it to output
|
||||
shutil.move(delocated_wheel, output_dir)
|
||||
# clean up
|
||||
shutil.rmtree(venv_dir)
|
||||
|
||||
# we're all done here; move it to output (overwrite existing)
|
||||
dst = os.path.join(output_dir, os.path.basename(repaired_wheel))
|
||||
shutil.move(repaired_wheel, dst)
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
--- a/lib_pypy/_sysconfigdata.py Tue Jan 28 22:54:59 2020 +0200
|
||||
+++ b/lib_pypy/_sysconfigdata.py Wed Jan 29 19:21:23 2020 +0200
|
||||
@@ -47,4 +47,5 @@
|
||||
build_time_vars['CC'] += ' -arch %s' % (arch,)
|
||||
if "CXX" in build_time_vars:
|
||||
build_time_vars['CXX'] += ' -arch %s' % (arch,)
|
||||
+ build_time_vars['MACOSX_DEPLOYMENT_TARGET'] = '10.7'
|
||||
+41
-13
@@ -1,16 +1,17 @@
|
||||
import os
|
||||
import urllib.request
|
||||
from fnmatch import fnmatch
|
||||
from time import sleep
|
||||
|
||||
|
||||
def prepare_command(command, python, pip, project):
|
||||
def prepare_command(command, **kwargs):
|
||||
'''
|
||||
Preprocesses a command by expanding variables like {python} or {pip}.
|
||||
Preprocesses a command by expanding variables like {python}.
|
||||
|
||||
For example, used for the before_build option, where the user would
|
||||
like to run a command like `python setup.py test`. If the command should run on
|
||||
Python 3, the user could write `{python} setup.py test`. This command would expand
|
||||
it out to python2 or python3 as appropriate.
|
||||
For example, used in the test_command option to specify the path to the
|
||||
project's root.
|
||||
'''
|
||||
return command.format(python=python, pip=pip, project=project)
|
||||
return command.format(python='python', pip='pip', **kwargs)
|
||||
|
||||
|
||||
def get_build_verbosity_extra_flags(level):
|
||||
@@ -22,19 +23,22 @@ def get_build_verbosity_extra_flags(level):
|
||||
return []
|
||||
|
||||
|
||||
class BuildSkipper(object):
|
||||
def __init__(self, skip_config):
|
||||
self.patterns = skip_config.split()
|
||||
class BuildSelector:
|
||||
def __init__(self, build_config, skip_config):
|
||||
self.build_patterns = build_config.split()
|
||||
self.skip_patterns = skip_config.split()
|
||||
|
||||
def __call__(self, build_id):
|
||||
return any(fnmatch(build_id, pattern) for pattern in self.patterns)
|
||||
def match_any(patterns):
|
||||
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 'BuildSkipper(%r)' % ' '.join(self.patterns)
|
||||
return 'BuildSelector({!r} - {!r})'.format(' '.join(self.build_patterns), ' '.join(self.skip_patterns))
|
||||
|
||||
|
||||
# Taken from https://stackoverflow.com/a/107717
|
||||
class Unbuffered(object):
|
||||
class Unbuffered:
|
||||
def __init__(self, stream):
|
||||
self.stream = stream
|
||||
|
||||
@@ -48,3 +52,27 @@ class Unbuffered(object):
|
||||
|
||||
def __getattr__(self, attr):
|
||||
return getattr(self.stream, attr)
|
||||
|
||||
|
||||
def download(url, dest):
|
||||
print('+ Download ' + url + ' to ' + dest)
|
||||
dest_dir = os.path.dirname(dest)
|
||||
if not os.path.exists(dest_dir):
|
||||
os.makedirs(dest_dir)
|
||||
|
||||
repeat_num = 3
|
||||
for i in range(repeat_num):
|
||||
try:
|
||||
response = urllib.request.urlopen(url)
|
||||
except Exception:
|
||||
if i == repeat_num - 1:
|
||||
raise
|
||||
sleep(3)
|
||||
continue
|
||||
break
|
||||
|
||||
try:
|
||||
with open(dest, 'wb') as file:
|
||||
file.write(response.read())
|
||||
finally:
|
||||
response.close()
|
||||
|
||||
+175
-59
@@ -1,103 +1,219 @@
|
||||
from __future__ import print_function
|
||||
import os, tempfile, subprocess, sys, shutil
|
||||
try:
|
||||
from urllib2 import urlopen
|
||||
except ImportError:
|
||||
from urllib.request import urlopen
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from collections import namedtuple
|
||||
from glob import glob
|
||||
from zipfile import ZipFile
|
||||
|
||||
from .util import prepare_command, get_build_verbosity_extra_flags, Unbuffered
|
||||
from .util import (
|
||||
download,
|
||||
get_build_verbosity_extra_flags,
|
||||
prepare_command,
|
||||
)
|
||||
|
||||
|
||||
def build(project_dir, package_name, output_dir, test_command, test_requires, before_build, build_verbosity, skip, environment):
|
||||
# Python under AppVeyor/Windows seems to be buffering by default, giving problems interleaving subprocess call output with unflushed calls to 'print'
|
||||
sys.stdout.flush()
|
||||
sys.stdout = Unbuffered(sys.stdout)
|
||||
IS_RUNNING_ON_AZURE = os.path.exists('C:\\hostedtoolcache')
|
||||
IS_RUNNING_ON_TRAVIS = os.environ.get('TRAVIS_OS_NAME') == 'windows'
|
||||
|
||||
# run_with_env is a cmd file that sets the right environment variables to
|
||||
run_with_env = os.path.join(tempfile.gettempdir(), 'appveyor_run_with_env.cmd')
|
||||
if not os.path.exists(run_with_env):
|
||||
with open(run_with_env, 'wb') as f:
|
||||
request = urlopen('https://github.com/ogrisel/python-appveyor-demo/raw/09a1c8672e5015a74d8f69d07add6ee803c176ec/appveyor/run_with_env.cmd')
|
||||
f.write(request.read())
|
||||
|
||||
def shell(args, env=None, cwd=None):
|
||||
# print the command executing for the logs
|
||||
print('+ ' + ' '.join(args))
|
||||
args = ['cmd', '/E:ON', '/V:ON', '/C', run_with_env] + args
|
||||
return subprocess.check_call(' '.join(args), env=env, cwd=cwd)
|
||||
def shell(args, env=None, cwd=None):
|
||||
print('+ ' + ' '.join(args))
|
||||
return subprocess.check_call(' '.join(args), env=env, cwd=cwd, shell=True)
|
||||
|
||||
PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'arch', 'identifier', 'path'])
|
||||
|
||||
def get_nuget_args(version, arch):
|
||||
python_name = 'python' if version[0] == '3' else 'python2'
|
||||
if arch == '32':
|
||||
python_name = python_name + 'x86'
|
||||
return [python_name, '-Version', version, '-OutputDirectory', 'C:\\cibw\\python']
|
||||
|
||||
|
||||
def get_python_configurations(build_selector):
|
||||
PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'arch', 'identifier', 'url'])
|
||||
python_configurations = [
|
||||
PythonConfiguration(version='2.7.x', arch="32", identifier='cp27-win32', path='C:\Python27'),
|
||||
PythonConfiguration(version='2.7.x', arch="64", identifier='cp27-win_amd64', path='C:\Python27-x64'),
|
||||
PythonConfiguration(version='3.3.x', arch="32", identifier='cp33-win32', path='C:\Python33'),
|
||||
PythonConfiguration(version='3.3.x', arch="64", identifier='cp33-win_amd64', path='C:\Python33-x64'),
|
||||
PythonConfiguration(version='3.4.x', arch="32", identifier='cp34-win32', path='C:\Python34'),
|
||||
PythonConfiguration(version='3.4.x', arch="64", identifier='cp34-win_amd64', path='C:\Python34-x64'),
|
||||
PythonConfiguration(version='3.5.x', arch="32", identifier='cp35-win32', path='C:\Python35'),
|
||||
PythonConfiguration(version='3.5.x', arch="64", identifier='cp35-win_amd64', path='C:\Python35-x64'),
|
||||
PythonConfiguration(version='3.6.x', arch="32", identifier='cp36-win32', path='C:\Python36'),
|
||||
PythonConfiguration(version='3.6.x', arch="64", identifier='cp36-win_amd64', path='C:\Python36-x64'),
|
||||
PythonConfiguration(version='2.7.17', arch='32', identifier='cp27-win32', url=None),
|
||||
PythonConfiguration(version='2.7.17', arch='64', identifier='cp27-win_amd64', url=None),
|
||||
PythonConfiguration(version='3.5.4', arch='32', identifier='cp35-win32', url=None),
|
||||
PythonConfiguration(version='3.5.4', arch='64', identifier='cp35-win_amd64', url=None),
|
||||
PythonConfiguration(version='3.6.8', arch='32', identifier='cp36-win32', url=None),
|
||||
PythonConfiguration(version='3.6.8', arch='64', identifier='cp36-win_amd64', url=None),
|
||||
PythonConfiguration(version='3.7.6', arch='32', identifier='cp37-win32', url=None),
|
||||
PythonConfiguration(version='3.7.6', arch='64', identifier='cp37-win_amd64', url=None),
|
||||
PythonConfiguration(version='3.8.2', arch='32', identifier='cp38-win32', url=None),
|
||||
PythonConfiguration(version='3.8.2', arch='64', identifier='cp38-win_amd64', url=None),
|
||||
PythonConfiguration(version='2.7-v7.3.0', arch='32', identifier='pp27-win32', url='https://bitbucket.org/pypy/pypy/downloads/pypy2.7-v7.3.0-win32.zip'),
|
||||
PythonConfiguration(version='3.6-v7.3.0', arch='32', identifier='pp36-win32', url='https://bitbucket.org/pypy/pypy/downloads/pypy3.6-v7.3.0-win32.zip'),
|
||||
]
|
||||
|
||||
if IS_RUNNING_ON_TRAVIS:
|
||||
# cannot install VCForPython27.msi which is needed for compiling C software
|
||||
# try with (and similar): msiexec /i VCForPython27.msi ALLUSERS=1 ACCEPT=YES /passive
|
||||
python_configurations = [c for c in python_configurations if not c.version.startswith('2.7')]
|
||||
|
||||
# skip builds as required
|
||||
python_configurations = [c for c in python_configurations if build_selector(c.identifier)]
|
||||
|
||||
return python_configurations
|
||||
|
||||
|
||||
def extract_zip(zip_src, dest):
|
||||
with ZipFile(zip_src) as zip:
|
||||
zip.extractall(dest)
|
||||
|
||||
|
||||
def install_cpython(version, arch, nuget):
|
||||
nuget_args = get_nuget_args(version, arch)
|
||||
installation_path = os.path.join(nuget_args[-1], nuget_args[0] + '.' + version, 'tools')
|
||||
shell([nuget, 'install'] + nuget_args)
|
||||
return installation_path
|
||||
|
||||
|
||||
def install_pypy(version, arch, url):
|
||||
assert arch == '32'
|
||||
# Inside the PyPy zip file is a directory with the same name
|
||||
zip_filename = url.rsplit('/', 1)[-1]
|
||||
installation_path = os.path.join('C:\\cibw', os.path.splitext(zip_filename)[0])
|
||||
if not os.path.exists(installation_path):
|
||||
pypy_zip = os.path.join('C:\\cibw', zip_filename)
|
||||
download(url, pypy_zip)
|
||||
# Extract to the parent directory because the zip file still contains a directory
|
||||
extract_zip(pypy_zip, os.path.dirname(installation_path))
|
||||
pypy_exe = 'pypy3.exe' if version[0] == '3' else 'pypy.exe'
|
||||
shell(['mklink', os.path.join(installation_path, 'python.exe'), os.path.join(installation_path, pypy_exe)])
|
||||
shell(['mklink', '/d', os.path.join(installation_path, 'Scripts'), os.path.join(installation_path, 'bin')])
|
||||
return installation_path
|
||||
|
||||
|
||||
def build(project_dir, output_dir, test_command, before_test, test_requires, test_extras, before_build, build_verbosity, build_selector, repair_command, environment):
|
||||
abs_project_dir = os.path.abspath(project_dir)
|
||||
temp_dir = tempfile.mkdtemp(prefix='cibuildwheel')
|
||||
built_wheel_dir = os.path.join(temp_dir, 'built_wheel')
|
||||
repaired_wheel_dir = os.path.join(temp_dir, 'repaired_wheel')
|
||||
|
||||
# install nuget as best way to provide python
|
||||
nuget = 'C:\\cibw\\nuget.exe'
|
||||
download('https://dist.nuget.org/win-x86-commandline/latest/nuget.exe', nuget)
|
||||
# get pip fo this installation which not have.
|
||||
get_pip_script = 'C:\\cibw\\get-pip.py'
|
||||
download('https://bootstrap.pypa.io/get-pip.py', get_pip_script)
|
||||
|
||||
python_configurations = get_python_configurations(build_selector)
|
||||
for config in python_configurations:
|
||||
if skip(config.identifier):
|
||||
print('cibuildwheel: Skipping build %s' % config.identifier, file=sys.stderr)
|
||||
continue
|
||||
# install Python
|
||||
if config.identifier.startswith('cp'):
|
||||
installation_path = install_cpython(config.version, config.arch, nuget)
|
||||
elif config.identifier.startswith('pp'):
|
||||
installation_path = install_pypy(config.version, config.arch, config.url)
|
||||
else:
|
||||
raise ValueError("Unknown Python implementation")
|
||||
|
||||
# setup dirs
|
||||
if os.path.exists(built_wheel_dir):
|
||||
shutil.rmtree(built_wheel_dir)
|
||||
os.makedirs(built_wheel_dir)
|
||||
assert os.path.exists(os.path.join(installation_path, 'python.exe'))
|
||||
|
||||
# set up PATH and environment variables for run_with_env
|
||||
env = os.environ.copy()
|
||||
# set up environment variables for run_with_env
|
||||
env['PYTHON_VERSION'] = config.version
|
||||
env['PYTHON_ARCH'] = config.arch
|
||||
env['PATH'] = os.pathsep.join([
|
||||
config.path,
|
||||
os.path.join(config.path, 'Scripts'),
|
||||
installation_path,
|
||||
os.path.join(installation_path, 'Scripts'),
|
||||
env['PATH']
|
||||
])
|
||||
# update env with results from CIBW_ENVIRONMENT
|
||||
env = environment.as_dictionary(prev_environment=env)
|
||||
|
||||
# for the logs - check we're running the right version of python
|
||||
shell(['where', 'python'], env=env)
|
||||
shell(['python', '--version'], env=env)
|
||||
shell(['python', '-c', '"import struct; print(struct.calcsize(\'P\') * 8)\"'], env=env)
|
||||
shell(['python', '-c', '"import struct; print(struct.calcsize(\'P\') * 8)"'], env=env)
|
||||
where_python = subprocess.check_output(['where', 'python'], env=env, universal_newlines=True).splitlines()[0].strip()
|
||||
if where_python != os.path.join(installation_path, 'python.exe'):
|
||||
print("cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it.", file=sys.stderr)
|
||||
exit(1)
|
||||
|
||||
# make sure pip is installed
|
||||
if not os.path.exists(os.path.join(installation_path, 'Scripts', 'pip.exe')):
|
||||
shell(['python', get_pip_script], env=env, cwd="C:\\cibw")
|
||||
assert os.path.exists(os.path.join(installation_path, 'Scripts', 'pip.exe'))
|
||||
where_pip = subprocess.check_output(['where', 'pip'], env=env, universal_newlines=True).splitlines()[0].strip()
|
||||
if where_pip.strip() != os.path.join(installation_path, 'Scripts', 'pip.exe'):
|
||||
print("cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it.", file=sys.stderr)
|
||||
exit(1)
|
||||
|
||||
# prepare the Python environment
|
||||
shell(['python', '-m', 'pip', 'install', '--upgrade', 'pip'],
|
||||
env=env)
|
||||
shell(['pip', 'install', '--upgrade', 'setuptools'], env=env)
|
||||
shell(['pip', 'install', 'wheel'], env=env)
|
||||
shell(['python', '-m', 'pip', 'install', '--upgrade', 'pip'], env=env)
|
||||
shell(['pip', '--version'], env=env)
|
||||
shell(['pip', 'install', '--upgrade', 'setuptools', 'wheel'], env=env)
|
||||
|
||||
# run the before_build command
|
||||
if before_build:
|
||||
before_build_prepared = prepare_command(before_build, python='python', pip='pip', project=abs_project_dir)
|
||||
before_build_prepared = prepare_command(before_build, project=abs_project_dir)
|
||||
shell([before_build_prepared], env=env)
|
||||
|
||||
# build the wheel
|
||||
if os.path.exists(built_wheel_dir):
|
||||
shutil.rmtree(built_wheel_dir)
|
||||
os.makedirs(built_wheel_dir)
|
||||
shell(['pip', 'wheel', abs_project_dir, '-w', built_wheel_dir, '--no-deps'] + get_build_verbosity_extra_flags(build_verbosity), env=env)
|
||||
built_wheel = glob(built_wheel_dir+'/*.whl')[0]
|
||||
built_wheel = glob(os.path.join(built_wheel_dir, '*.whl'))[0]
|
||||
|
||||
# install the wheel
|
||||
shell(['pip', 'install', built_wheel], env=env)
|
||||
# repair the wheel
|
||||
if os.path.exists(repaired_wheel_dir):
|
||||
shutil.rmtree(repaired_wheel_dir)
|
||||
os.makedirs(repaired_wheel_dir)
|
||||
if built_wheel.endswith('none-any.whl') or not repair_command:
|
||||
# pure Python wheel or empty repair command
|
||||
shutil.move(built_wheel, repaired_wheel_dir)
|
||||
else:
|
||||
repair_command_prepared = prepare_command(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]
|
||||
|
||||
# test the wheel
|
||||
if test_requires:
|
||||
shell(['pip', 'install'] + test_requires, env=env)
|
||||
if 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'], env=env)
|
||||
venv_dir = tempfile.mkdtemp()
|
||||
shell(['python', '-m', 'virtualenv', venv_dir], env=env)
|
||||
|
||||
virtualenv_env = env.copy()
|
||||
|
||||
venv_script_path = os.path.join(venv_dir, 'Scripts')
|
||||
if os.path.exists(os.path.join(venv_dir, 'bin')):
|
||||
# pypy2.7 bugfix
|
||||
venv_script_path = os.pathsep.join([venv_script_path, os.path.join(venv_dir, 'bin')])
|
||||
virtualenv_env['PATH'] = os.pathsep.join([
|
||||
venv_script_path,
|
||||
virtualenv_env['PATH'],
|
||||
])
|
||||
virtualenv_env["__CIBW_VIRTUALENV_PATH__"] = venv_dir
|
||||
|
||||
# check that we are using the Python from the virtual environment
|
||||
shell(['which', 'python'], env=virtualenv_env)
|
||||
|
||||
if before_test:
|
||||
before_test_prepared = prepare_command(before_test, project=abs_project_dir)
|
||||
shell([before_test_prepared], env=virtualenv_env)
|
||||
|
||||
# install the wheel
|
||||
shell(['pip', 'install', repaired_wheel + test_extras], env=virtualenv_env)
|
||||
|
||||
# test the wheel
|
||||
if test_requires:
|
||||
shell(['pip', 'install'] + 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(test_command, python='python', pip='pip', project=abs_project_dir)
|
||||
shell([test_command_prepared], cwd='c:\\', env=env)
|
||||
test_command_prepared = prepare_command(test_command, project=abs_project_dir)
|
||||
shell([test_command_prepared], cwd='c:\\', env=virtualenv_env)
|
||||
|
||||
# we're all done here; move it to output
|
||||
shutil.move(built_wheel, output_dir)
|
||||
# clean up
|
||||
shutil.rmtree(venv_dir)
|
||||
|
||||
# we're all done here; move it to output (remove if already exists)
|
||||
dst = os.path.join(output_dir, os.path.basename(repaired_wheel))
|
||||
if os.path.isfile(dst):
|
||||
os.remove(dst)
|
||||
shutil.move(repaired_wheel, dst)
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
---
|
||||
title: Contributing
|
||||
---
|
||||
|
||||
Wheel-building can be pretty complex. I expect users to find many edge-cases - please help the rest of the community out by documenting these, adding features to support them, and reporting bugs.
|
||||
|
||||
I plan to be pretty liberal in accepting pull requests, as long as they align with the design goals below.
|
||||
|
||||
`cibuildwheel` is indie open source. We're not paid to work on this.
|
||||
|
||||
Design Goals
|
||||
------------
|
||||
|
||||
- `cibuildwheel` should wrap the complexity of wheel building.
|
||||
- The user interface to `cibuildwheel` is the build script (e.g. `.travis.yml`). Feature additions should not increase the complexity of this script.
|
||||
- Options should be environment variables (these lend themselves better to YML config files). They should be prefixed with `CIBW_`.
|
||||
- Options should be generalise to all platforms. If platform-specific options are required, they should be namespaced e.g. `CIBW_TEST_COMMAND_MACOS`
|
||||
|
||||
Other notes:
|
||||
|
||||
- The platforms are very similar, until they're not. I'd rather have straight-forward code than totally DRY code, so let's keep airy platfrom abstractions to a minimum.
|
||||
- I might want to break the options into a shared config file one day, so that config is more easily shared. That has motivated some of the design decisions.
|
||||
|
||||
### cibuildwheel's relationship with build errors
|
||||
|
||||
cibuildwheel doesn't really do anything itself - it's always deferring to other tools (pip, wheel, auditwheel, delocate, docker). Without cibuildwheel, the process is really fragmented. Different tools, across different OSs need to be stitched together in just the right way to make it work.
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,33 @@
|
||||
---
|
||||
title: Modern C++ standards
|
||||
---
|
||||
|
||||
Building Python wheels with modern C++ standards (C++11 and later) requires a few tricks.
|
||||
|
||||
|
||||
## Python 2.7 and C++17
|
||||
|
||||
The Python 2.7 header files use the `register` keyword, which is [reserved and unused from C+17 onwards](https://en.cppreference.com/w/cpp/keyword/register). Compiling a wheel for Python 2.7 with the C++17 standard is still possible to allow usage of `register` using proper flag `-Wno-register` for gcc/clang and `/wd5033` for MSVC.
|
||||
|
||||
## manylinux1 and C++14
|
||||
The default `manylinux1` image (based on CentOS 5) contains a version of GCC and libstdc++ that only supports C++11 and earlier standards. There are however ways to compile wheels with the C++14 standard (and later): https://github.com/pypa/manylinux/issues/118
|
||||
|
||||
`manylinux2010` and `manylinux2014` are newer and support all C++ standards (up to C++17).
|
||||
|
||||
## macOS and deployment target versions
|
||||
|
||||
OS X/macOS allows you to specify a so-called "deployment target" version that will ensure backwards compatibility with older versions of macOS. One way to do this is by setting the `MACOSX_DEPLOYMENT_TARGET` environment variable. If not set, Python will set this variable to the version the Python distribution itself was compiled on (10.6 or 10.9, for the python.org packages), when creating the wheel.
|
||||
|
||||
However, to enable modern C++ standards, the deploment target needs to be set high enough (since older OS X/macOS versions did not have the necessary modern C++ standard library).
|
||||
|
||||
To get C++11 and C++14 support, `MACOSX_DEPLOYMENT_TARGET` needs to be set to (at least) `"10.9"`. By default, `cibuildwheel` already does this, building 64-bit-only wheels for macOS 10.9 and later.
|
||||
|
||||
To get C++17 support, set `MACOSX_DEPLOYMENT_TARGET` to (at least) `"10.13"` or `"10.14"` (macOS 10.13 offers partial C++17 support; e.g., the filesystem header is in experimental, offering `#include <experimental/filesystem>` instead of `#include <filesystem>`; macOS 10.14 has full C++17 support).
|
||||
|
||||
For more details see https://en.cppreference.com/w/cpp/compiler_support and https://xcodereleases.com/: Xcode 10 needs macOS 10.13 and Xcode 11 needs macOS 10.14.
|
||||
|
||||
## Windows and Python 2.7
|
||||
|
||||
Visual C++ for Python 2.7 does not support modern standards of C++. When building on Appveyor, you will need to either use the "Visual Studio 2017" or "Visual Studio 2019" image, but Python 2.7 is not supported on these images - skip it by setting `CIBW_SKIP=cp27-win*`.
|
||||
|
||||
There is an optional workaround for this, though: the pybind11 project argues and shows that it is [possible to compile Python 2.7 extension with a newer compiler](https://pybind11.readthedocs.io/en/stable/faq.html#working-with-ancient-visual-studio-2008-builds-on-windows) and has an example project showing how to do this: https://github.com/pybind/python_example. The main catch is that a user might need to install a newer "Microsoft Visual C++ Redistributable", since the newer C++ standard libraries are not included by default with the Python 2.7 installation.
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
title: Delivering to PyPI
|
||||
---
|
||||
|
||||
After you've built your wheels, you'll probably want to deliver them to PyPI.
|
||||
|
||||
### Manual method
|
||||
|
||||
On your development machine, do the following...
|
||||
|
||||
```bash
|
||||
# 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
|
||||
# 'dist' folder.
|
||||
|
||||
# Upload using 'twine' (you may need to 'pip install twine')
|
||||
twine upload dist/*
|
||||
```
|
||||
|
||||
### Semi-automatic method using wheelhouse-uploader
|
||||
|
||||
Obviously, manual steps are for chumps, so we can automate this a little by using [wheelhouse-uploader](https://github.com/ogrisel/wheelhouse-uploader).
|
||||
|
||||
> Quick note from me - using S3 as a storage didn't work due to a [bug](https://issues.apache.org/jira/browse/LIBCLOUD-792) in libcloud. Feel free to use my fork of that package that fixes the bug `pip install https://github.com/joerick/libcloud/archive/v1.5.0-s3fix.zip`
|
||||
|
||||
### Automatic method
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,88 @@
|
||||
|
||||
/* Global styles */
|
||||
|
||||
p {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: 85%;
|
||||
background-color: rgba(27,31,35,.05);
|
||||
border-radius: 3px;
|
||||
padding: .2em .4em;
|
||||
color: inherit;
|
||||
border: none;
|
||||
margin: 0 1px;
|
||||
}
|
||||
|
||||
code, pre, tt {
|
||||
font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
}
|
||||
|
||||
.hljs {
|
||||
padding: 1em;
|
||||
background-color: #f6f8fa;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin-top: 2em;
|
||||
border-bottom: 1px solid #e1e4e5;
|
||||
margin-bottom: 0.8em;
|
||||
padding-bottom: 0.2em;
|
||||
|
||||
}
|
||||
|
||||
.rst-content blockquote {
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.rst-content th {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
/*
|
||||
Code block filename style
|
||||
|
||||
Use by putting a quote before a code block e.g.
|
||||
|
||||
> filename.txt
|
||||
|
||||
```
|
||||
code here
|
||||
```
|
||||
|
||||
JS adds the relevant classes to make the following selectors work.
|
||||
*/
|
||||
.rst-content .code-block-filename {
|
||||
margin: 0;
|
||||
padding: 2px 1em;
|
||||
background-color: #eff1f3;
|
||||
font-size: 85%;
|
||||
font-weight: bold;
|
||||
|
||||
border-top-left-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
|
||||
border-bottom: 1px solid #e6e8e9;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.rst-content .code-block-filename * {
|
||||
font-size: inherit;
|
||||
}
|
||||
.rst-content .code-block-filename :last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.code-block-filename + pre {
|
||||
margin-top: 0;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
||||
.code-block-filename + pre code {
|
||||
padding-top: 0.9em;
|
||||
}
|
||||
|
||||
/* import font awesome 4 for icons */
|
||||
@import url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css);
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
$('.rst-content pre')
|
||||
.prev('blockquote')
|
||||
.addClass('code-block-filename');
|
||||
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
---
|
||||
title: Tips and tricks
|
||||
---
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
If your wheel didn't compile, check the list below for some debugging tips.
|
||||
|
||||
- A mistake in your config. To quickly test your config without doing a git push and waiting for your code to build on CI, you can test the Linux build in a Docker container. On Mac or Linux, with Docker running, try `cibuildwheel --platform linux`. You'll have to bring your config into the current environment first.
|
||||
|
||||
- Missing dependency. You might need to install something on the build machine. You can do this in `.travis.yml`, `appveyor.yml`, or `.circleci/config.yml`, with apt-get, brew or choco. Given how the Linux build works, you'll need to use the [`CIBW_BEFORE_BUILD`](options.md#before-build) option.
|
||||
|
||||
- Windows: missing C feature. The Windows C compiler doesn't support C language features invented after 1990, so you'll have to backport your C code to C90. For me, this mostly involved putting my variable declarations at the top of the function like an animal.
|
||||
|
||||
- MacOS: calling cibuildwheel from a python3 script and getting a `ModuleNotFoundError`? Due to a [bug](https://bugs.python.org/issue22490) in CPython, you'll need to [unset the `__PYVENV_LAUNCHER__` variable](https://github.com/joerick/cibuildwheel/issues/133#issuecomment-478288597) before activating a venv.
|
||||
|
||||
### Linux builds on Docker
|
||||
|
||||
Linux wheels are built in the [`manylinux` docker images](https://github.com/pypa/manylinux) to provide binary compatible wheels on Linux, according to [PEP 571](https://www.python.org/dev/peps/pep-0571/). Because of this, when building with `cibuildwheel` on Linux, a few things should be taken into account:
|
||||
|
||||
- Programs and libraries cannot be installed on the Travis CI Ubuntu host with `apt-get`, but can be installed inside of the Docker image using `yum` or manually. The same goes for environment variables that are potentially needed to customize the wheel building. `cibuildwheel` supports this by providing the `CIBW_ENVIRONMENT` and `CIBW_BEFORE_BUILD` options to setup the build environment inside the running Docker image. See [the options docs](options.md#build-environment) for details on these options.
|
||||
|
||||
- The project directory is mounted in the running Docker instance as `/project`, the output directory for the wheels as `/output`. In general, this is handled transparently by `cibuildwheel`. For a more finegrained level of control however, the root of the host file system is mounted as `/host`, allowing for example to access shared files, caches, etc. on the host file system. Note that this is not available on CircleCI due to their Docker policies.
|
||||
|
||||
- Alternative dockers images can be specified with the `CIBW_MANYLINUX_X86_64_IMAGE`, `CIBW_MANYLINUX_I686_IMAGE`, and `CIBW_MANYLINUX_PYPY_X86_64_IMAGE` options to allow for a custom, preconfigured build environment for the Linux builds. See [options](options.md#manylinux-image) for more details.
|
||||
|
||||
### Building packages with optional C extensions
|
||||
|
||||
`cibuildwheel` defines the environment variable `CIBUILDWHEEL` to the value `1` allowing projects for which the C extension is optional to make it mandatory when building wheels.
|
||||
|
||||
An easy way to do it in Python 3 is through the `optional` named argument of `Extension` constructor in your `setup.py`:
|
||||
|
||||
```python
|
||||
myextension = Extension(
|
||||
"myextension",
|
||||
["myextension.c"],
|
||||
optional=os.environ.get('CIBUILDWHEEL', '0') != '1',
|
||||
)
|
||||
```
|
||||
|
||||
### 'No module named XYZ' errors after running cibuildwheel on macOS
|
||||
|
||||
`cibuildwheel` on Mac installs the distributions from Python.org system-wide during its operation. This is necessary, but it can cause some confusing errors after cibuildwheel has finished.
|
||||
|
||||
Consider the build script:
|
||||
|
||||
```bash
|
||||
python3 -m pip install twine cibuildwheel
|
||||
python3 -m cibuildwheel --output-dir wheelhouse
|
||||
python3 -m twine upload wheelhouse/*.whl
|
||||
# error: no module named 'twine'
|
||||
```
|
||||
|
||||
This doesn't work because while `cibuildwheel` was running, it installed a few new versions of 'python3', so the `python3` run on line 3 isn't the same as the `python3` that ran on line 1.
|
||||
|
||||
Solutions to this vary, but the simplest is to install tools immediately before they're used:
|
||||
|
||||
```bash
|
||||
python3 -m pip install cibuildwheel
|
||||
python3 -m cibuildwheel --output-dir wheelhouse
|
||||
python3 -m pip install twine
|
||||
python3 -m twine upload wheelhouse/*.whl
|
||||
```
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
title: Home
|
||||
---
|
||||
|
||||
{%
|
||||
includemarkdown "../README.md"
|
||||
start="<!--intro-start-->"
|
||||
end="<!--intro-end-->"
|
||||
%}
|
||||
|
||||
To get started, head over to the [setup guide](setup.md).
|
||||
@@ -0,0 +1,87 @@
|
||||
import cgi
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
|
||||
import mkdocs
|
||||
|
||||
INCLUDE_TAG_REGEX = re.compile(
|
||||
r'''
|
||||
{% # opening tag
|
||||
\s*
|
||||
include # directive name
|
||||
\s+
|
||||
"(?P<filename>[^"]+)" # "filename"
|
||||
\s*
|
||||
%} # closing tag
|
||||
''',
|
||||
flags=re.VERBOSE,
|
||||
)
|
||||
|
||||
INCLUDEMARKDOWN_TAG_REGEX = re.compile(
|
||||
r'''
|
||||
{% # opening tag
|
||||
\s*
|
||||
includemarkdown # directive name
|
||||
\s+
|
||||
"(?P<filename>[^"]+)" # "filename"
|
||||
(?:\s+start="(?P<start>[^"]+)")? # optional start expression
|
||||
(?:\s+end="(?P<end>[^"]+)")? # optional end expression
|
||||
\s*
|
||||
%} # closing tag
|
||||
''',
|
||||
flags=re.VERBOSE,
|
||||
)
|
||||
|
||||
|
||||
class ImportMarkdownPlugin(mkdocs.plugins.BasePlugin):
|
||||
def on_page_markdown(self, markdown, page, **kwargs):
|
||||
page_src_path = page.file.abs_src_path
|
||||
|
||||
def found_include_tag(match):
|
||||
filename = match.group('filename')
|
||||
|
||||
file_path_abs = os.path.join(os.path.dirname(page_src_path), filename)
|
||||
|
||||
if not os.path.exists(file_path_abs):
|
||||
raise ValueError('file not found', filename)
|
||||
|
||||
with io.open(file_path_abs, encoding='utf8') as f:
|
||||
text_to_include = f.read()
|
||||
|
||||
# Allow good practice of having a final newline in the file
|
||||
if text_to_include.endswith('\n'):
|
||||
text_to_include = text_to_include[:-1]
|
||||
|
||||
return text_to_include
|
||||
|
||||
def found_includemarkdown_tag(match):
|
||||
filename = match.group('filename')
|
||||
start = match.group('start')
|
||||
end = match.group('end')
|
||||
|
||||
file_path_abs = os.path.join(os.path.dirname(page_src_path), filename)
|
||||
|
||||
if not os.path.exists(file_path_abs):
|
||||
raise ValueError('file not found', filename)
|
||||
|
||||
with io.open(file_path_abs, encoding='utf8') as f:
|
||||
text_to_include = f.read()
|
||||
|
||||
if start:
|
||||
_, _, text_to_include = text_to_include.partition(start)
|
||||
|
||||
if end:
|
||||
text_to_include, _, _ = text_to_include.partition(end)
|
||||
|
||||
return (
|
||||
'<!-- BEGIN INCLUDE %s %s %s -->\n' % (
|
||||
filename, cgi.escape(start or ''), cgi.escape(end or '')
|
||||
)
|
||||
+ text_to_include
|
||||
+ '\n<!-- END INCLUDE -->'
|
||||
)
|
||||
|
||||
markdown = re.sub(INCLUDE_TAG_REGEX, found_include_tag, markdown)
|
||||
markdown = re.sub(INCLUDEMARKDOWN_TAG_REGEX, found_includemarkdown_tag, markdown)
|
||||
return markdown
|
||||
@@ -0,0 +1,15 @@
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name='mkdocs_include_markdown_plugin',
|
||||
version='1.0',
|
||||
author='Joe Rickerby',
|
||||
license='Apache 2',
|
||||
packages=['mkdocs_include_markdown_plugin'],
|
||||
entry_points={
|
||||
'mkdocs.plugins': [
|
||||
'importmarkdown = mkdocs_include_markdown_plugin.plugin:ImportMarkdownPlugin',
|
||||
]
|
||||
},
|
||||
zip_safe=False
|
||||
)
|
||||
+544
@@ -0,0 +1,544 @@
|
||||
## Options summary
|
||||
|
||||
<div class="options-toc"></div>
|
||||
|
||||
## Setting options
|
||||
|
||||
cibuildwheel is configured using environment variables, that can be set using
|
||||
your CI config.
|
||||
|
||||
For example, to configure cibuildwheel to run tests, add the following YAML to
|
||||
your CI config file:
|
||||
|
||||
> .travis.yml ([docs](https://docs.travis-ci.com/user/environment-variables/))
|
||||
```yaml
|
||||
env:
|
||||
global:
|
||||
- CIBW_TEST_REQUIRES=nose
|
||||
- CIBW_TEST_COMMAND="nosetests {project}/tests"
|
||||
```
|
||||
|
||||
> appveyor.yml ([docs](https://www.appveyor.com/docs/build-configuration/#environment-variables))
|
||||
```yaml
|
||||
environment:
|
||||
global:
|
||||
CIBW_TEST_REQUIRES: nose
|
||||
CIBW_TEST_COMMAND: "nosetests {project}\\tests"
|
||||
```
|
||||
|
||||
> .circleci/config.yml ([docs](https://circleci.com/docs/2.0/configuration-reference/#environment))
|
||||
```yaml
|
||||
jobs:
|
||||
job_name:
|
||||
environment:
|
||||
CIBW_TEST_REQUIRES: nose
|
||||
CIBW_TEST_COMMAND: "nosetests {project}/tests"
|
||||
```
|
||||
|
||||
> azure-pipelines.yml ([docs](https://docs.microsoft.com/en-us/azure/devops/pipelines/process/variables))
|
||||
```yaml
|
||||
variables:
|
||||
CIBW_TEST_REQUIRES: nose
|
||||
CIBW_TEST_COMMAND: "nosetests {project}/tests"
|
||||
```
|
||||
|
||||
|
||||
## Build selection
|
||||
|
||||
|
||||
### `CIBW_PLATFORM` {: #platform}
|
||||
|
||||
> Override the auto-detected target platform
|
||||
|
||||
Options: `auto` `linux` `macos` `windows`
|
||||
|
||||
Default: `auto`
|
||||
|
||||
`auto` will auto-detect platform using environment variables, such as `TRAVIS_OS_NAME`/`APPVEYOR`/`CIRCLECI`.
|
||||
|
||||
For `linux` you need Docker running, on macOS or Linux. For `macos`, you need a Mac machine, and note that this script is going to automatically install MacPython on your system, so don't run on your development machine. For `windows`, you need to run in Windows, and `cibuildwheel` will install required versions of Python to `C:\cibw\python` using NuGet.
|
||||
|
||||
This option can also be set using the command-line option `--platform`.
|
||||
|
||||
### `CIBW_BUILD`, `CIBW_SKIP` {: #build-skip}
|
||||
|
||||
> Choose the Python versions to build
|
||||
|
||||
Space-separated list of builds to build and skip. Each build has an identifier like `cp27-manylinux_x86_64` or `cp35-macosx_x86_64` - you can list specific ones to build and `cibuildwheel` will only build those, and/or list ones to skip and `cibuildwheel` won't try to build them.
|
||||
|
||||
When both options are specified, both conditions are applied and only builds with a tag that matches `CIBW_BUILD` and does not match `CIBW_SKIP` will be built.
|
||||
|
||||
When setting the options, you can use shell-style globbing syntax (as per [`fnmatch`](https://docs.python.org/3/library/fnmatch.html)). All the build identifiers supported by cibuildwheel are shown below:
|
||||
|
||||
<div class="build-id-table-marker"></div>
|
||||
|
||||
| | macOS 64bit | Manylinux x86 64bit | Manylinux x86 32bit | Windows 64bit | Windows 32bit | Manylinux Armv8 64bit | Manylinux PPC64LE | Manylinux s390x |
|
||||
|-----------------|---------------------|------------------------|----------------------|-----------------|----------------|------------------------|------------------------|----------------------|
|
||||
| Python 2.7 | cp27-macosx_x86_64 | cp27-manylinux_x86_64 | cp27-manylinux_i686 | cp27-win_amd64 | cp27-win32 | | | |
|
||||
| Python 3.5 | cp35-macosx_x86_64 | cp35-manylinux_x86_64 | cp35-manylinux_i686 | cp35-win_amd64 | cp35-win32 | cp35-manylinux_aarch64 | cp35-manylinux_ppc64le | cp35-manylinux_s390x |
|
||||
| Python 3.6 | cp36-macosx_x86_64 | cp36-manylinux_x86_64 | cp36-manylinux_i686 | cp36-win_amd64 | cp36-win32 | cp36-manylinux_aarch64 | cp36-manylinux_ppc64le | cp36-manylinux_s390x |
|
||||
| Python 3.7 | cp37-macosx_x86_64 | cp37-manylinux_x86_64 | cp37-manylinux_i686 | cp37-win_amd64 | cp37-win32 | cp37-manylinux_aarch64 | cp37-manylinux_ppc64le | cp37-manylinux_s390x |
|
||||
| Python 3.8 | cp38-macosx_x86_64 | cp38-manylinux_x86_64 | cp38-manylinux_i686 | cp38-win_amd64 | cp38-win32 | cp38-manylinux_aarch64 | cp38-manylinux_ppc64le | cp38-manylinux_s390x |
|
||||
| PyPy 2.7 v7.3.0 | pp27-macosx_x86_64 | pp27-manylinux_x86_64 | | | pp27-win32 | | | |
|
||||
| PyPy 3.6 v7.3.0 | pp36-macosx_x86_64 | pp36-manylinux_x86_64 | | | pp36-win32 | | | |
|
||||
|
||||
The list of supported and currently selected build identifiers can also be retrieved by passing the `--print-build-identifiers` flag to `cibuildwheel`.
|
||||
The format is `python_tag-platform_tag`, with tags similar to those in [PEP 425](https://www.python.org/dev/peps/pep-0425/#details).
|
||||
|
||||
For CPython, the minimally supported macOS version is 10.9; for PyPy 2.7 and PyPy 3.6, respectively macOS 10.7 and 10.13 or higher is required.
|
||||
|
||||
#### Examples
|
||||
|
||||
```yaml
|
||||
# Only build on Python 3.6
|
||||
CIBW_BUILD: cp36-*
|
||||
|
||||
# Skip building on Python 2.7 on the Mac
|
||||
CIBW_SKIP: cp27-macosx_x86_64
|
||||
|
||||
# Skip building on Python 3.8 on the Mac
|
||||
CIBW_SKIP: cp38-macosx_x86_64
|
||||
|
||||
# Skip building on Python 2.7 on all platforms
|
||||
CIBW_SKIP: cp27-*
|
||||
|
||||
# Skip Python 2.7 on Windows
|
||||
CIBW_SKIP: cp27-win*
|
||||
|
||||
# Skip Python 2.7 on 32-bit Windows
|
||||
CIBW_SKIP: cp27-win32
|
||||
|
||||
# Skip Python 2.7 and Python 3.5
|
||||
CIBW_SKIP: cp27-* cp35-*
|
||||
|
||||
# Skip Python 3.6 on Linux
|
||||
CIBW_SKIP: cp36-manylinux*
|
||||
|
||||
# Only build on Python 3 and skip 32-bit builds
|
||||
CIBW_BUILD: cp3?-*
|
||||
CIBW_SKIP: "*-win32 *-manylinux_i686"
|
||||
|
||||
# Only build PyPy and CPython 3
|
||||
CIBW_BUILD: pp* cp3?-*
|
||||
|
||||
# Disable building PyPy wheels on all platforms
|
||||
CIBW_SKIP: pp*
|
||||
```
|
||||
|
||||
<style>
|
||||
.build-id-table-marker + table {
|
||||
font-size: 90%;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.rst-content .build-id-table-marker + table td,
|
||||
.rst-content .build-id-table-marker + table th {
|
||||
padding: 4px 4px;
|
||||
}
|
||||
.build-id-table-marker + table td:not(:first-child) {
|
||||
font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
font-size: 85%;
|
||||
}
|
||||
dt code {
|
||||
font-size: 100%;
|
||||
background-color: rgba(41, 128, 185, 0.1);
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
## Build customization
|
||||
|
||||
|
||||
### `CIBW_ENVIRONMENT` {: #environment}
|
||||
> Set environment variables needed during the build
|
||||
|
||||
A space-separated list of environment variables to set during the build. Bash syntax should be used, even on Windows.
|
||||
|
||||
You must set this variable to pass variables to Linux builds (since they execute in a Docker container). It also works for the other platforms.
|
||||
|
||||
You can use `$PATH` syntax to insert other variables, or the `$(pwd)` syntax to insert the output of other shell commands.
|
||||
|
||||
To specify more than one environment variable, separate the assignments by spaces.
|
||||
|
||||
Platform-specific variants also available:<br/>
|
||||
`CIBW_ENVIRONMENT_MACOS` | `CIBW_ENVIRONMENT_WINDOWS` | `CIBW_ENVIRONMENT_LINUX`
|
||||
|
||||
#### Examples
|
||||
```yaml
|
||||
# Set some compiler flags
|
||||
CIBW_ENVIRONMENT: "CFLAGS='-g -Wall' CXXFLAGS='-Wall'"
|
||||
|
||||
# Append a directory to the PATH variable (this is expanded in the build environment)
|
||||
CIBW_ENVIRONMENT: "PATH=$PATH:/usr/local/bin"
|
||||
|
||||
# Set BUILD_TIME to the output of the `date` command
|
||||
CIBW_ENVIRONMENT: "BUILD_TIME=$(date)"
|
||||
|
||||
# Supply options to `pip` to affect how it downloads dependencies
|
||||
CIBW_ENVIRONMENT: "PIP_EXTRA_INDEX_URL=https://pypi.myorg.com/simple"
|
||||
|
||||
# Set two flags
|
||||
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_BUILD` {: #before-build}
|
||||
> Execute a shell command preparing each wheel's build
|
||||
|
||||
A shell command to run before building the wheel. This option allows you to run a command in **each** Python environment before the `pip wheel` command. This is useful if you need to set up some dependency so it's available during the build.
|
||||
|
||||
If dependencies are required to build your wheel (for example if you include a header from a Python module), set this to `pip install .`, and the dependencies will be installed automatically by pip. However, this means your package will be built twice - if your package takes a long time to build, you might wish to manually list the dependencies here instead.
|
||||
|
||||
The active Python binary can be accessed using `python`, and pip with `pip`; `cibuildwheel` makes sure the right version of Python and pip will be executed. `{project}` can be used as a placeholder for the absolute path to the project's root and will be replaced by `cibuildwheel`.
|
||||
|
||||
The command is run in a shell, so you can write things like `cmd1 && cmd2`.
|
||||
|
||||
Platform-specific variants also available:<br/>
|
||||
`CIBW_BEFORE_BUILD_MACOS` | `CIBW_BEFORE_BUILD_WINDOWS` | `CIBW_BEFORE_BUILD_LINUX`
|
||||
|
||||
#### Examples
|
||||
```yaml
|
||||
# install your project and dependencies before building
|
||||
CIBW_BEFORE_BUILD: pip install .
|
||||
|
||||
# install something required for the build
|
||||
CIBW_BEFORE_BUILD: pip install pybind11
|
||||
|
||||
# chain commands using &&
|
||||
CIBW_BEFORE_BUILD: yum install -y libffi-dev && pip install .
|
||||
|
||||
# run a script that's inside your repo
|
||||
CIBW_BEFORE_BUILD: bash scripts/prepare_for_build.sh
|
||||
```
|
||||
|
||||
|
||||
### `CIBW_REPAIR_WHEEL_COMMAND` {: #repair-wheel-command}
|
||||
> Execute a shell command to repair each (non-pure Python) built wheel
|
||||
|
||||
Default:
|
||||
|
||||
- on Linux: `'auditwheel repair -w {dest_dir} {wheel}'`
|
||||
- on macOS: `'delocate-listdeps {wheel} && delocate-wheel --require-archs x86_64 -w {dest_dir} {wheel}'`
|
||||
- on Windows: `''`
|
||||
|
||||
A shell command to repair a built wheel by copying external library dependencies into the wheel tree and relinking them.
|
||||
The command is run on each built wheel (except for pure Python ones) before testing it.
|
||||
|
||||
The following placeholders must be used inside the command and will be replaced by `cibuildwheel`:
|
||||
|
||||
- `{wheel}` for the absolute path to the built wheel
|
||||
- `{dest_dir}` for the absolute path of the directory where to create the repaired wheel.
|
||||
|
||||
The command is run in a shell, so you can run multiple commands like `cmd1 && cmd2`.
|
||||
|
||||
Platform-specific variants also available:<br/>
|
||||
`CIBW_REPAIR_WHEEL_COMMAND_MACOS` | `CIBW_REPAIR_WHEEL_COMMAND_WINDOWS` | `CIBW_REPAIR_WHEEL_COMMAND_LINUX`
|
||||
|
||||
#### Examples
|
||||
|
||||
```yaml
|
||||
# don't repair macOS wheels
|
||||
CIBW_REPAIR_WHEEL_COMMAND_MACOS: ""
|
||||
|
||||
# pass the `--lib-sdir .` flag to auditwheel on Linux
|
||||
CIBW_REPAIR_WHEEL_COMMAND_LINUX: "auditwheel repair --lib-sdir . -w {dest_dir} {wheel}"
|
||||
```
|
||||
|
||||
|
||||
### `CIBW_MANYLINUX_X86_64_IMAGE`, `CIBW_MANYLINUX_I686_IMAGE`, `CIBW_MANYLINUX_PYPY_X86_64_IMAGE`, `CIBW_MANYLINUX_AARCH64_IMAGE`, `CIBW_MANYLINUX_PPC64LE_IMAGE`, `CIBW_MANYLINUX_S390X_IMAGE` {: #manylinux-image}
|
||||
> Specify alternative manylinux docker images
|
||||
|
||||
An alternative Docker image to be used for building [`manylinux`](https://github.com/pypa/manylinux) wheels. `cibuildwheel` will then pull these instead of the default images, [`quay.io/pypa/manylinux2010_x86_64`](https://quay.io/pypa/manylinux2010_x86_64), [`quay.io/pypa/manylinux2010_i686`](https://quay.io/pypa/manylinux2010_i686), [`pypywheels/manylinux2010-pypy_x86_64`](https://hub.docker.com/r/pypywheels/manylinux2010-pypy_x86_64), [`quay.io/pypa/manylinux2014_aarch64`](https://quay.io/pypa/manylinux2014_aarch64), [`quay.io/pypa/manylinux2014_ppc64le`](https://quay.io/pypa/manylinux2014_ppc64le), and [`quay.io/pypa/manylinux2014_s390x`](https://quay.io/pypa/manylinux2010_s390x).
|
||||
|
||||
The value of this option can either be set to `manylinux1`, `manylinux2010` or `manylinux2014` to use the [official `manylinux` images](https://github.com/pypa/manylinux) and [PyPy `manylinux` images](https://github.com/pypy/manylinux), or any other valid Docker image name. Note that for PyPy, only the official `manylinux2010` image is currently available. For architectures other
|
||||
than x86 (x86\_64 and i686) manylinux2014 must be used because this is the first version of the manylinux specification that supports additional architectures.
|
||||
|
||||
Beware to specify a valid Docker image that can be used in the same way as the official, default Docker images: all necessary Python and pip versions need to be present in `/opt/python/`, and the `auditwheel` tool needs to be present for `cibuildwheel` to work. Apart from that, the architecture and relevant shared system libraries need to be manylinux1-, manylinux2010- or manylinux2014-compatible in order to produce valid `manylinux1`/`manylinux2010`/`manylinux2014` wheels (see [pypa/manylinux on GitHub](https://github.com/pypa/manylinux), [PEP 513](https://www.python.org/dev/peps/pep-0513/), [PEP 571](https://www.python.org/dev/peps/pep-0571/) and [PEP 599](https://www.python.org/dev/peps/pep-0599/) for more details).
|
||||
|
||||
Note that `auditwheel` detects the version of the `manylinux` standard in the Docker image through the `AUDITWHEEL_PLAT` environment variable, as `cibuildwheel` has no way of detecting the correct `--plat` command line argument to pass to `auditwheel` for a custom image. If a Docker image does not correctly set this `AUDITWHEEL_PLAT` environment variable, the `CIBW_ENVIRONMENT` option can be used to do so (e.g., `CIBW_ENVIRONMENT='AUDITWHEEL_PLAT="manylinux2010_$(uname -m)"'`).
|
||||
|
||||
Note that `manylinux2014` doesn't support builds with Python 2.7 - when building with `manylinux2014`, skip Python 2.7 using `CIBW_SKIP` (see example below).
|
||||
|
||||
#### Examples
|
||||
|
||||
```yaml
|
||||
# build using the manylinux1 image to ensure manylinux1 wheels are produced
|
||||
# skip PyPy, since there is no PyPy manylinux1 image
|
||||
CIBW_MANYLINUX_X86_64_IMAGE: manylinux1
|
||||
CIBW_MANYLINUX_I686_IMAGE: manylinux1
|
||||
CIBW_SKIP: pp*
|
||||
|
||||
# build using the manylinux2014 image
|
||||
CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014
|
||||
CIBW_MANYLINUX_I686_IMAGE: manylinux2014
|
||||
CIBW_SKIP: cp27-manylinux*
|
||||
|
||||
# build using a different image from the docker registry
|
||||
CIBW_MANYLINUX_X86_64_IMAGE: dockcross/manylinux-x64
|
||||
CIBW_MANYLINUX_I686_IMAGE: dockcross/manylinux-x86
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
### `CIBW_TEST_COMMAND` {: #test-command}
|
||||
> Execute a shell command to test each built wheel
|
||||
|
||||
Shell command to run tests after the build. The wheel will be installed automatically and available for import from the tests. `{project}` can be used as a placeholder for the absolute path to the project's root and will be replaced by `cibuildwheel`.
|
||||
|
||||
The command is run in a shell, so you can write things like `cmd1 && cmd2`.
|
||||
|
||||
Platform-specific variants also available:<br/>
|
||||
`CIBW_TEST_COMMAND_MACOS` | `CIBW_TEST_COMMAND_WINDOWS` | `CIBW_TEST_COMMAND_LINUX`
|
||||
|
||||
#### Examples
|
||||
|
||||
```yaml
|
||||
# run the project tests against the installed wheel using `nose`
|
||||
CIBW_TEST_COMMAND: nosetests {project}/tests
|
||||
|
||||
# run the project tests using `pytest`
|
||||
CIBW_TEST_COMMAND: nosetests {project}/tests
|
||||
```
|
||||
|
||||
|
||||
### `CIBW_TEST_REQUIRES` {: #test-requires}
|
||||
> Install Python dependencies before running the tests
|
||||
|
||||
Space-separated list of dependencies required for running the tests.
|
||||
|
||||
Platform-specific variants also available:<br/>
|
||||
`CIBW_TEST_REQUIRES_MACOS` | `CIBW_TEST_REQUIRES_WINDOWS` | `CIBW_TEST_REQUIRES_LINUX`
|
||||
|
||||
#### Examples
|
||||
|
||||
```yaml
|
||||
# install pytest before running CIBW_TEST_COMMAND
|
||||
CIBW_TEST_REQUIRES: pytest
|
||||
|
||||
# install specific versions of test dependencies
|
||||
CIBW_TEST_REQUIRES: nose==1.3.7 moto==0.4.31
|
||||
```
|
||||
|
||||
|
||||
### `CIBW_TEST_EXTRAS` {: #test-extras}
|
||||
> Install your wheel for testing using `extras_require`
|
||||
|
||||
Comma-separated list of
|
||||
[extras_require](https://setuptools.readthedocs.io/en/latest/setuptools.html#declaring-extras-optional-features-with-their-own-dependencies)
|
||||
options that should be included when installing the wheel prior to running the
|
||||
tests. This can be used to avoid having to redefine test dependencies in
|
||||
`CIBW_TEST_REQUIRES` if they are already defined in `setup.py` or
|
||||
`setup.cfg`.
|
||||
|
||||
Platform-specific variants also available:<br/>
|
||||
`CIBW_TEST_EXTRAS_MACOS` | `CIBW_TEST_EXTRAS_WINDOWS` | `CIBW_TEST_EXTRAS_LINUX`
|
||||
|
||||
#### Examples
|
||||
|
||||
```yaml
|
||||
# will cause the wheel to be installed with `pip install <wheel_file>[test,qt]`
|
||||
CIBW_TEST_EXTRAS: test,qt
|
||||
```
|
||||
|
||||
### `CIBW_BEFORE_TEST` {: #before-test}
|
||||
> Execute a shell command before testing each wheel
|
||||
|
||||
A shell command to run in **each** test virtual environment, before your wheel is installed and tested. This is useful if you need to install a non pip package, change values of environment variables
|
||||
or perform multi step pip installation (e.g. installing `scikit-build` or `cython` before install test package)
|
||||
|
||||
The active Python binary can be accessed using `python`, and pip with `pip`; `cibuildwheel` makes sure the right version of Python and pip will be executed. `{project}` can be used as a placeholder for the absolute path to the project's root and will be replaced by `cibuildwheel`.
|
||||
|
||||
The command is run in a shell, so you can write things like `cmd1 && cmd2`.
|
||||
|
||||
Platform-specific variants also available:<br/>
|
||||
`CIBW_BEFORE_TEST_MACOS` | `CIBW_BEFORE_TEST_WINDOWS` | `CIBW_BEFORE_TEST_LINUX`
|
||||
|
||||
#### Examples
|
||||
```yaml
|
||||
# install test dependencies with overwritten environment variables.
|
||||
CIBW_BEFORE_TEST: CC=gcc CXX=g++ pip install -r requirements.txt
|
||||
|
||||
# chain commands using &&
|
||||
CIBW_BEFORE_TEST: rm -rf ./data/cache && mkdir -p ./data/cache
|
||||
|
||||
# install non pip python package
|
||||
CIBW_BEFORE_TEST: cd some_dir; ./configure; make; make install
|
||||
|
||||
# install python packages that are required to install test dependencies
|
||||
CIBW_BEFORE_TEST: pip install cmake scikit-build
|
||||
```
|
||||
|
||||
## Other
|
||||
|
||||
### `CIBW_BUILD_VERBOSITY` {: #build-verbosity}
|
||||
> Increase/decrease the output of pip wheel
|
||||
|
||||
An number from 1 to 3 to increase the level of verbosity (corresponding to invoking pip with `-v`, `-vv`, and `-vvv`), between -1 and -3 (`-q`, `-qq`, and `-qqq`), or just 0 (default verbosity). These flags are useful while debugging a build when the output of the actual build invoked by `pip wheel` is required.
|
||||
|
||||
Platform-specific variants also available:<br/>
|
||||
`CIBW_BUILD_VERBOSITY_MACOS` | `CIBW_BUILD_VERBOSITY_WINDOWS` | `CIBW_BUILD_VERBOSITY_LINUX`
|
||||
|
||||
#### Examples
|
||||
|
||||
```yaml
|
||||
# increase pip debugging output
|
||||
CIBW_BUILD_VERBOSITY: 1
|
||||
```
|
||||
|
||||
|
||||
## Command line options
|
||||
|
||||
```text
|
||||
usage: cibuildwheel [-h] [--platform {auto,linux,macos,windows}]
|
||||
[--output-dir OUTPUT_DIR] [--print-build-identifiers]
|
||||
[project_dir]
|
||||
|
||||
Build wheels for all the platforms.
|
||||
|
||||
positional arguments:
|
||||
project_dir Path to the project that you want wheels for.
|
||||
Default: the current directory.
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--platform {auto,linux,macos,windows}
|
||||
Platform to build for. For "linux" you need docker
|
||||
running, on Mac or Linux. For "macos", you need a Mac
|
||||
machine, and note that this script is going to
|
||||
automatically install MacPython on your system, so
|
||||
don't run on your development machine. For "windows",
|
||||
you need to run in Windows, and it will build and test
|
||||
for all versions of Python at C:\PythonXX[-x64].
|
||||
--output-dir OUTPUT_DIR
|
||||
Destination folder for the wheels.
|
||||
--print-build-identifiers
|
||||
Print the build identifiers matched by the current
|
||||
invocation and exit.
|
||||
|
||||
```
|
||||
|
||||
<style>
|
||||
.toctree-l3 {
|
||||
border-left: 10px solid transparent;
|
||||
}
|
||||
|
||||
.options-toc {
|
||||
display: grid;
|
||||
grid-auto-columns: fit-content(20%) 1fr;
|
||||
grid-gap: 16px 32px;
|
||||
gap: 16px 32px;
|
||||
font-size: 90%;
|
||||
margin-bottom: 28px;
|
||||
margin-top: 28px;
|
||||
}
|
||||
.options-toc .header {
|
||||
grid-column: 1 / 3;
|
||||
font-weight: bold;
|
||||
}
|
||||
.options-toc .header:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
.options-toc a.option {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
h3 code {
|
||||
font-size: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// gather the options data
|
||||
var options = {}
|
||||
var headers = []
|
||||
|
||||
$('.rst-content h3')
|
||||
.filter(function (i, el) {
|
||||
return !!$(el).text().match(/(^([A-Z0-9, _]| and )+)¶$/);
|
||||
})
|
||||
.each(function (i, el) {
|
||||
var optionName = $(el).text().replace('¶', '');
|
||||
var description = $(el).next('blockquote').text()
|
||||
var header = $(el).prevAll('h2').first().text().replace('¶', '')
|
||||
var id = el.id;
|
||||
|
||||
if (options[header] === undefined) {
|
||||
options[header] = [];
|
||||
headers.push(header);
|
||||
}
|
||||
console.log(optionName, description, header);
|
||||
|
||||
options[header].push({name: optionName, description, id});
|
||||
});
|
||||
|
||||
// write the table of contents
|
||||
|
||||
var tocTable = $('.options-toc');
|
||||
|
||||
for (var i = 0; i < headers.length; i += 1) {
|
||||
var header = headers[i];
|
||||
var headerOptions = options[header];
|
||||
|
||||
$('<div class="header">').text(header).appendTo(tocTable);
|
||||
|
||||
for (var j = 0; j < headerOptions.length; j += 1) {
|
||||
var option = headerOptions[j];
|
||||
|
||||
var optionNames = option.name.split(', ')
|
||||
|
||||
$('<div class="name">')
|
||||
.append($.map(optionNames, function (name) {
|
||||
return $('<a class="option">')
|
||||
.append(
|
||||
$('<code>').text(name)
|
||||
)
|
||||
.attr('href', '#'+option.id)
|
||||
}
|
||||
))
|
||||
.appendTo(tocTable);
|
||||
$('<div class="description">')
|
||||
.text(option.description)
|
||||
.appendTo(tocTable);
|
||||
}
|
||||
}
|
||||
|
||||
// write the markdown table for the README
|
||||
|
||||
var markdown = ''
|
||||
|
||||
markdown += '| | Option | Description |\n'
|
||||
markdown += '|---|--------|-------------|\n'
|
||||
|
||||
var prevHeader = null
|
||||
|
||||
for (var i = 0; i < headers.length; i += 1) {
|
||||
var header = headers[i];
|
||||
var headerOptions = options[header];
|
||||
for (var j = 0; j < headerOptions.length; j += 1) {
|
||||
var option = headerOptions[j];
|
||||
|
||||
if (j == 0) {
|
||||
markdown += '| **'+header+'** '
|
||||
} else {
|
||||
markdown += '| '
|
||||
}
|
||||
|
||||
var optionNames = option.name.trim().split(', ')
|
||||
var url = 'https://cibuildwheel.readthedocs.io/en/stable/options/#'+option.id;
|
||||
var namesMarkdown = $.map(optionNames, function(n) {
|
||||
return '[`'+n+'`]('+url+') '
|
||||
}).join(' ')
|
||||
|
||||
markdown += '| '+namesMarkdown+' '
|
||||
markdown += '| '+option.description.trim()+' '
|
||||
markdown += '|\n'
|
||||
}
|
||||
}
|
||||
|
||||
console.log('readme options markdown\n', markdown)
|
||||
});
|
||||
</script>
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
---
|
||||
title: 'Setup'
|
||||
---
|
||||
|
||||
# GitHub Actions [linux/mac/windows] {: #github-actions}
|
||||
|
||||
To build Linux, Mac, and Windows wheels using Github Actions, create a `.github/workflows/build.yml` file in your repo.
|
||||
|
||||
> build.yml
|
||||
```yaml
|
||||
{% include "../examples/github-minimal.yml" %}
|
||||
```
|
||||
|
||||
Commit this file, and push to Github - either to your default branch, or to a PR branch. The build should start automatically.
|
||||
|
||||
For more info on this file, check out the [docs](https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions).
|
||||
|
||||
# Azure Pipelines [linux/mac/windows] {: #azure-pipelines}
|
||||
|
||||
To build Linux, Mac, and Windows wheels on Azure Pipelines, create a `azure-pipelines.yml` file in your repo.
|
||||
|
||||
> azure-pipelines.yml
|
||||
```yaml
|
||||
{% include "../examples/azure-pipelines-minimal.yml" %}
|
||||
```
|
||||
|
||||
!!! note
|
||||
To support Python 3.5 on Windows, make sure to specify the use of `{vmImage: 'vs2017-win2016'}` on Windows, to ensure the required toolchain is available.
|
||||
|
||||
Commit this file, enable building of your repo on Azure Pipelines, and push.
|
||||
|
||||
Wheels will be stored for you and available through the Pipelines interface. For more info on this file, check out the [docs](https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema).
|
||||
|
||||
# Travis CI [linux/mac/windows] {: #travis-ci}
|
||||
|
||||
To build Linux, Mac, and Windows wheels on Travis CI, create a `.travis.yml` file in your repo.
|
||||
|
||||
> .travis.yml
|
||||
```yaml
|
||||
{% include "../examples/travis-ci-minimal.yml" %}
|
||||
```
|
||||
|
||||
Note that building Windows Python 2.7 wheels on Travis is unsupported.
|
||||
|
||||
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/).
|
||||
|
||||
# CircleCI [linux/mac] {: #circleci}
|
||||
|
||||
To build Linux and Mac wheels on CircleCI, create a `.circleci/config.yml` file in your repo,
|
||||
|
||||
> .circleci/config.yml
|
||||
```yaml
|
||||
{% include "../examples/circleci-minimal.yml" %}
|
||||
```
|
||||
|
||||
Commit this file, enable building of your repo on CircleCI, and push.
|
||||
|
||||
!!! note
|
||||
CircleCI doesn't enable free macOS containers for open source by default, but you can ask for access. See [here](https://circleci.com/docs/2.0/oss/#overview) for more information.
|
||||
|
||||
CircleCI will store the built wheels for you - you can access them from the project console. Check out the CircleCI [docs](https://circleci.com/docs/2.0/configuration-reference/#section=configuration) for more info on this config file.
|
||||
|
||||
# AppVeyor [linux/mac/windows] {: #appveyor}
|
||||
|
||||
To build Linux, Mac, and Windows wheels on AppVeyor, create an `appveyor.yml` file in your repo.
|
||||
|
||||
> appveyor.yml
|
||||
|
||||
```yaml
|
||||
{% include "../examples/appveyor-minimal.yml" %}
|
||||
```
|
||||
|
||||
Commit this file, enable building of your repo on AppVeyor, and push.
|
||||
|
||||
AppVeyor will store the built wheels for you - you can access them from the project console. Alternatively, you may want to store them in the same place as the Travis CI build. See [AppVeyor deployment docs](https://www.appveyor.com/docs/deployment/) for more info, or see [Delivering to PyPI](deliver-to-pypi.md) below.
|
||||
|
||||
For more info on this config file, check out the [docs](https://www.appveyor.com/docs/).
|
||||
|
||||
> ⚠️ Got an error? Check the [FAQ](faq.md).
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
$('.toctree-l2 a, .rst-content h1').each(function(i, el) {
|
||||
var text = $(el).text()
|
||||
var match = text.match(/(.*) \[([a-z/]+)\]/);
|
||||
|
||||
if (match) {
|
||||
var iconHTML = $.map(match[2].split('/'), function(ident) {
|
||||
switch (ident) {
|
||||
case 'linux':
|
||||
return '<i class="fa fa-linux" aria-hidden="true"></i>'
|
||||
case 'windows':
|
||||
return '<i class="fa fa-windows" aria-hidden="true"></i>'
|
||||
case 'mac':
|
||||
return '<i class="fa fa-apple" aria-hidden="true"></i>'
|
||||
}
|
||||
}).join(' ');
|
||||
|
||||
$(el).append(
|
||||
$('<div>')
|
||||
.append(iconHTML)
|
||||
.css({float: 'right'})
|
||||
)
|
||||
$(el).contents()
|
||||
.filter(function(){ return this.nodeType == 3; }).first()
|
||||
.replaceWith(match[1]);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,21 @@
|
||||
environment:
|
||||
matrix:
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Ubuntu
|
||||
APPVEYOR_JOB_NAME: "python37-x64-ubuntu"
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
APPVEYOR_JOB_NAME: "python37-x64-vs2015"
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: macos-mojave
|
||||
APPVEYOR_JOB_NAME: "python37-x64-macos-mojave"
|
||||
|
||||
stack: python 3.7
|
||||
|
||||
init:
|
||||
- cmd: set PATH=C:\Python37;C:\Python37\Scripts;%PATH%
|
||||
|
||||
install: python -m pip install cibuildwheel==1.3.0
|
||||
|
||||
build_script: python -m cibuildwheel --output-dir wheelhouse
|
||||
|
||||
artifacts:
|
||||
- path: "wheelhouse\\*.whl"
|
||||
name: Wheels
|
||||
@@ -0,0 +1,35 @@
|
||||
jobs:
|
||||
- job: linux
|
||||
pool: {vmImage: 'Ubuntu-16.04'}
|
||||
steps:
|
||||
- task: UsePythonVersion@0
|
||||
- bash: |
|
||||
python3 -m pip install --upgrade pip
|
||||
pip3 install cibuildwheel==1.3.0
|
||||
cibuildwheel --output-dir wheelhouse .
|
||||
- task: PublishBuildArtifacts@1
|
||||
inputs: {pathtoPublish: 'wheelhouse'}
|
||||
|
||||
- job: macos
|
||||
pool: {vmImage: 'macOS-10.15'}
|
||||
steps:
|
||||
- task: UsePythonVersion@0
|
||||
- bash: |
|
||||
python3 -m pip install --upgrade pip
|
||||
pip3 install cibuildwheel==1.3.0
|
||||
cibuildwheel --output-dir wheelhouse .
|
||||
- task: PublishBuildArtifacts@1
|
||||
inputs: {pathtoPublish: 'wheelhouse'}
|
||||
|
||||
- job: windows
|
||||
pool: {vmImage: 'vs2017-win2016'}
|
||||
steps:
|
||||
- task: UsePythonVersion@0
|
||||
- script: choco install vcpython27 -f -y
|
||||
displayName: Install Visual C++ for Python 2.7
|
||||
- bash: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install cibuildwheel==1.3.0
|
||||
cibuildwheel --output-dir wheelhouse .
|
||||
- task: PublishBuildArtifacts@1
|
||||
inputs: {pathtoPublish: 'wheelhouse'}
|
||||
@@ -0,0 +1,38 @@
|
||||
version: 2
|
||||
|
||||
jobs:
|
||||
linux-wheels:
|
||||
working_directory: ~/linux-wheels
|
||||
docker:
|
||||
- image: circleci/python:3.6
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker
|
||||
- run:
|
||||
name: Build the Linux wheels.
|
||||
command: |
|
||||
pip3 install --user cibuildwheel==1.3.0
|
||||
cibuildwheel --output-dir wheelhouse
|
||||
- store_artifacts:
|
||||
path: wheelhouse/
|
||||
|
||||
osx-wheels:
|
||||
working_directory: ~/osx-wheels
|
||||
macos:
|
||||
xcode: "10.0.0"
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Build the OS X wheels.
|
||||
command: |
|
||||
pip3 install --user cibuildwheel==1.3.0
|
||||
cibuildwheel --output-dir wheelhouse
|
||||
- store_artifacts:
|
||||
path: wheelhouse/
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
all-tests:
|
||||
jobs:
|
||||
- linux-wheels
|
||||
- osx-wheels
|
||||
@@ -0,0 +1,37 @@
|
||||
name: Build
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build_wheels:
|
||||
name: Build wheel on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-18.04, windows-latest, macos-latest]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
|
||||
- uses: actions/setup-python@v1
|
||||
name: Install Python
|
||||
with:
|
||||
python-version: '3.7'
|
||||
|
||||
- name: Install cibuildwheel
|
||||
run: |
|
||||
python -m pip install cibuildwheel==1.3.0
|
||||
|
||||
- name: Install Visual C++ for Python 2.7
|
||||
if: startsWith(matrix.os, 'windows')
|
||||
run: |
|
||||
choco install vcpython27 -f -y
|
||||
|
||||
- name: Build wheel
|
||||
run: |
|
||||
python -m cibuildwheel --output-dir wheelhouse
|
||||
|
||||
- uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: wheels
|
||||
path: ./wheelhouse
|
||||
@@ -0,0 +1,40 @@
|
||||
# As written, this configuration will build your wheels on every
|
||||
# commit, but will only push to PyPI on tagged commits.
|
||||
|
||||
language: python
|
||||
|
||||
jobs:
|
||||
include:
|
||||
# perform a linux build
|
||||
- services: docker
|
||||
# and a mac build
|
||||
- os: osx
|
||||
language: shell
|
||||
# and a windows build
|
||||
- os: windows
|
||||
language: shell
|
||||
before_install:
|
||||
- choco install python --version 3.8.0
|
||||
- export PATH="/c/Python38:/c/Python38/Scripts:$PATH"
|
||||
# make sure it's on PATH as 'python3'
|
||||
- ln -s /c/Python38/python.exe /c/Python38/python3.exe
|
||||
|
||||
env:
|
||||
global:
|
||||
- TWINE_USERNAME=__token__
|
||||
# Note: TWINE_PASSWORD is set to a PyPI API token in Travis settings
|
||||
|
||||
install:
|
||||
- python3 -m pip install cibuildwheel==1.3.0
|
||||
|
||||
script:
|
||||
# build the wheels, put them into './wheelhouse'
|
||||
- python3 -m cibuildwheel --output-dir wheelhouse
|
||||
|
||||
after_success:
|
||||
# if the release was tagged, upload them to PyPI
|
||||
- |
|
||||
if [[ $TRAVIS_TAG ]]; then
|
||||
python3 -m pip install twine
|
||||
python3 -m twine upload wheelhouse/*.whl
|
||||
fi
|
||||
@@ -0,0 +1,33 @@
|
||||
language: python
|
||||
|
||||
jobs:
|
||||
include:
|
||||
# perform a linux build
|
||||
- services: docker
|
||||
# perform a linux ARMv8 build
|
||||
- services: docker
|
||||
arch: aarch64
|
||||
# perform a linux PPC64LE build
|
||||
- services: docker
|
||||
arch: ppc64le
|
||||
# perform a linux S390X build
|
||||
- services: docker
|
||||
arch: s390x
|
||||
# and a mac build
|
||||
- os: osx
|
||||
language: shell
|
||||
# and a windows build
|
||||
- os: windows
|
||||
language: shell
|
||||
before_install:
|
||||
- choco install python --version 3.8.0
|
||||
- export PATH="/c/Python38:/c/Python38/Scripts:$PATH"
|
||||
# make sure it's on PATH as 'python3'
|
||||
- ln -s /c/Python38/python.exe /c/Python38/python3.exe
|
||||
|
||||
install:
|
||||
- python3 -m pip install cibuildwheel==1.3.0
|
||||
|
||||
script:
|
||||
# build the wheels, put them into './wheelhouse'
|
||||
- python3 -m cibuildwheel --output-dir wheelhouse
|
||||
@@ -0,0 +1,88 @@
|
||||
# In this configuration, the package is tested against multiple versions of python
|
||||
# on Linux and on one version of Python on Windows. If and only if all tests pass
|
||||
# will the wheels be built and deployed. Further, deployment is only initiated if
|
||||
# the current branch is "master", the current commit is tagged, and the current
|
||||
# repo is yours (e.g. it won't run on a Pull Request). For convenience, a source
|
||||
# distribution is also created.
|
||||
|
||||
language: python
|
||||
python:
|
||||
- 3.5
|
||||
- 3.6
|
||||
- 3.7
|
||||
- 3.8
|
||||
|
||||
before_install:
|
||||
- |
|
||||
if [[ "$TRAVIS_OS_NAME" = windows ]]; then
|
||||
choco install python --version 3.8.0
|
||||
export PATH="/c/Python38:/c/Python38/Scripts:$PATH"
|
||||
# make sure it's on PATH as 'python3'
|
||||
ln -s /c/Python38/python.exe /c/Python38/python3.exe
|
||||
fi
|
||||
|
||||
install:
|
||||
- python3 -m pip install pytest
|
||||
|
||||
script:
|
||||
- python3 setup.py install
|
||||
- pytest
|
||||
|
||||
stages:
|
||||
- test
|
||||
# Only execute deployment stage on tagged commits, and from your repository
|
||||
# (e.g. not PRs). Replace with your repo name.
|
||||
- name: deploy
|
||||
if: tag IS PRESENT AND repo = joerick/cibuildwheel
|
||||
# To only build tags that look like vX.Y.Z:
|
||||
# if: tag =~ ^v\d+\.\d+\.\d+$ AND repo = joerick/cibuildwheel
|
||||
|
||||
jobs:
|
||||
include:
|
||||
# Optional: run a test on Windows
|
||||
- os: windows
|
||||
language: shell
|
||||
name: Test on Windows
|
||||
|
||||
# Deploy source distribution
|
||||
- stage: deploy
|
||||
name: Deploy source distribution
|
||||
install: skip
|
||||
script: python3 setup.py sdist --formats=gztar
|
||||
after_success: |
|
||||
python3 -m pip install twine
|
||||
python3 -m twine upload --skip-existing dist/*.tar.gz
|
||||
# Deploy on linux
|
||||
- stage: deploy
|
||||
name: Build and deploy Linux wheels
|
||||
services: docker
|
||||
install: python3 -m pip install cibuildwheel==1.3.0
|
||||
script: python3 -m cibuildwheel --output-dir wheelhouse
|
||||
after_success: |
|
||||
python3 -m pip install twine
|
||||
python3 -m twine upload --skip-existing wheelhouse/*.whl
|
||||
# Deploy on mac
|
||||
- stage: deploy
|
||||
name: Build and deploy macOS wheels
|
||||
os: osx
|
||||
language: shell
|
||||
install: python3 -m pip install cibuildwheel==1.3.0
|
||||
script: python3 -m cibuildwheel --output-dir wheelhouse
|
||||
after_success: |
|
||||
python3 -m pip install twine
|
||||
python3 -m twine upload --skip-existing wheelhouse/*.whl
|
||||
# Deploy on windows
|
||||
- stage: deploy
|
||||
name: Build and deploy Windows wheels
|
||||
os: windows
|
||||
language: shell
|
||||
install: python3 -m pip install cibuildwheel==1.3.0
|
||||
script: python3 -m cibuildwheel --output-dir wheelhouse
|
||||
after_success: |
|
||||
python3 -m pip install twine
|
||||
python3 -m twine upload --skip-existing wheelhouse/*.whl
|
||||
|
||||
env:
|
||||
global:
|
||||
- TWINE_USERNAME=__token__
|
||||
# Note: TWINE_PASSWORD is set to a PyPI API token in Travis settings
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
site_name: cibuildwheel
|
||||
docs_dir: docs
|
||||
theme:
|
||||
name: readthedocs
|
||||
highlightjs: true
|
||||
hljs_languages:
|
||||
- yaml
|
||||
repo_url: https://github.com/joerick/cibuildwheel
|
||||
|
||||
extra_css:
|
||||
- extra.css
|
||||
|
||||
extra_javascript:
|
||||
- extra.js
|
||||
|
||||
nav:
|
||||
- Home: index.md
|
||||
- setup.md
|
||||
- options.md
|
||||
- deliver-to-pypi.md
|
||||
- cpp_standards.md
|
||||
- faq.md
|
||||
- contributing.md
|
||||
|
||||
markdown_extensions:
|
||||
- fenced_code
|
||||
- toc:
|
||||
permalink: True
|
||||
- attr_list
|
||||
- admonition
|
||||
|
||||
plugins:
|
||||
- importmarkdown
|
||||
@@ -1,2 +1,4 @@
|
||||
-e .
|
||||
-e ./docs/mkdocs_include_markdown_plugin
|
||||
pytest
|
||||
mkdocs==1.0.4
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
[bumpversion]
|
||||
current_version = 0.8.0
|
||||
commit = True
|
||||
tag = True
|
||||
message = Bump version
|
||||
|
||||
[bumpversion:file:setup.py]
|
||||
|
||||
[bumpversion:file:cibuildwheel/__init__.py]
|
||||
|
||||
[bumpversion:file:README.md]
|
||||
search = cibuildwheel=={current_version}
|
||||
replace = cibuildwheel=={new_version}
|
||||
|
||||
[flake8]
|
||||
ignore = E501,W503
|
||||
application-import-names = cibuildwheel
|
||||
exclude =
|
||||
cibuildwheel/resources/,
|
||||
dist/,
|
||||
build/,
|
||||
.git/,
|
||||
env/,
|
||||
env2/,
|
||||
.venv/,
|
||||
site/
|
||||
|
||||
@@ -1,30 +1,40 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
import io
|
||||
import os
|
||||
|
||||
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()
|
||||
|
||||
setup(
|
||||
name='cibuildwheel',
|
||||
version='0.8.0',
|
||||
install_requires=['bashlex'],
|
||||
version='1.3.0',
|
||||
install_requires=['bashlex!=0.13'],
|
||||
description="Build Python wheels on CI with minimal configuration.",
|
||||
long_description='For readme please see http://github.com/joerick/cibuildwheel',
|
||||
long_description=long_description,
|
||||
long_description_content_type='text/markdown',
|
||||
author="Joe Rickerby",
|
||||
author_email='joerick@mac.com',
|
||||
url='https://github.com/joerick/cibuildwheel',
|
||||
packages=['cibuildwheel',],
|
||||
packages=['cibuildwheel', ],
|
||||
license="BSD",
|
||||
zip_safe=False,
|
||||
package_data={
|
||||
'cibuildwheel': ['resources/*'],
|
||||
},
|
||||
# Supported python versions
|
||||
python_requires='>=3.5',
|
||||
keywords='ci wheel packaging pypi travis appveyor macos linux windows',
|
||||
classifiers=[
|
||||
'Intended Audience :: Developers',
|
||||
'Natural Language :: English',
|
||||
'Programming Language :: Python :: 2',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Development Status :: 4 - Beta',
|
||||
'Development Status :: 5 - Production/Stable',
|
||||
'License :: OSI Approved :: BSD License',
|
||||
'Programming Language :: Python :: Implementation :: CPython',
|
||||
'Topic :: Software Development :: Build Tools',
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import os
|
||||
import platform
|
||||
|
||||
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)
|
||||
|
||||
|
||||
def test_build_identifiers():
|
||||
# check that the number of expected wheels matches the number of build
|
||||
# identifiers
|
||||
# after adding CIBW_MANYLINUX_IMAGE to support manylinux2010, there
|
||||
# can be multiple wheels for each wheel, though, so we need to limit
|
||||
# the expected wheels
|
||||
if platform.machine() in ['x86_64', 'i686']:
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if '-manylinux' not in w or '-manylinux1' in w]
|
||||
else:
|
||||
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
||||
build_identifiers = utils.cibuildwheel_get_build_identifiers(project_dir)
|
||||
assert len(expected_wheels) == len(build_identifiers)
|
||||
@@ -1,6 +1,9 @@
|
||||
import os
|
||||
|
||||
from setuptools import setup, Extension
|
||||
from setuptools import (
|
||||
Extension,
|
||||
setup,
|
||||
)
|
||||
|
||||
if os.environ.get('CIBUILDWHEEL', '0') != '1':
|
||||
raise Exception('CIBUILDWHEEL environment variable is not set to 1')
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
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,4 +0,0 @@
|
||||
{
|
||||
"CIBW_TEST_REQUIRES": "nose",
|
||||
"CIBW_TEST_COMMAND": "nosetests {project}/test"
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
from setuptools import setup, Extension
|
||||
from setuptools import (
|
||||
Extension,
|
||||
setup,
|
||||
)
|
||||
|
||||
setup(
|
||||
name="spam",
|
||||
ext_modules=[Extension('spam', sources=['spam.c'])],
|
||||
extras_require={'test': ['nose']},
|
||||
version="0.1.0",
|
||||
)
|
||||
|
||||
@@ -1,8 +1,45 @@
|
||||
from __future__ import print_function
|
||||
import os
|
||||
import sys
|
||||
from unittest import TestCase
|
||||
|
||||
import spam
|
||||
|
||||
|
||||
def path_contains(parent, child):
|
||||
''' returns True if `child` is inside `parent`.
|
||||
|
||||
Works around path-comparison bugs caused by short-paths on Windows e.g.
|
||||
vssadm~1 instead of vssadministrator
|
||||
'''
|
||||
parent = os.path.abspath(parent)
|
||||
child = os.path.abspath(child)
|
||||
|
||||
while child != os.path.dirname(child):
|
||||
child = os.path.dirname(child)
|
||||
if os.stat(parent) == os.stat(child):
|
||||
# parent and child refer to the same directory on the filesystem
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class TestSpam(TestCase):
|
||||
def test_system(self):
|
||||
self.assertEqual(0, spam.system('python -c "exit(0)"'))
|
||||
self.assertNotEqual(0, spam.system('python -c "exit(1)"'))
|
||||
|
||||
def test_virtualenv(self):
|
||||
virtualenv_path = os.environ.get("__CIBW_VIRTUALENV_PATH__")
|
||||
if not virtualenv_path:
|
||||
self.fail("No virtualenv path defined in environment variable __CIBW_VIRTUALENV_PATH__")
|
||||
|
||||
print("=[executable]", sys.executable)
|
||||
print("=[spam location]", spam.__file__)
|
||||
print("=[virtualenv path]", virtualenv_path)
|
||||
print("=[listdir]", os.listdir(virtualenv_path))
|
||||
if os.path.exists(os.path.join(virtualenv_path, 'Scripts')):
|
||||
print("=[listdir]2", os.listdir(os.path.join(virtualenv_path, 'Scripts')))
|
||||
if os.path.exists(os.path.join(virtualenv_path, 'bin')):
|
||||
print("=[listdir]2", os.listdir(os.path.join(virtualenv_path, 'bin')))
|
||||
self.assertTrue(path_contains(virtualenv_path, sys.executable))
|
||||
self.assertTrue(path_contains(virtualenv_path, spam.__file__))
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
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,4 +0,0 @@
|
||||
{
|
||||
"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)\""
|
||||
}
|
||||
@@ -1,24 +1,28 @@
|
||||
from setuptools import setup, Extension
|
||||
import sys, os
|
||||
import os
|
||||
import sys
|
||||
|
||||
if sys.argv[-1] != '--name':
|
||||
# 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
|
||||
from setuptools import (
|
||||
Extension,
|
||||
setup,
|
||||
)
|
||||
|
||||
# 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()
|
||||
# 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",
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
3.6.0 (default, Feb 7 2017, 23:55:32)
|
||||
[GCC 4.8.2 20140120 (Red Hat 4.8.2-15)]
|
||||
@@ -0,0 +1,18 @@
|
||||
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)
|
||||
@@ -0,0 +1,18 @@
|
||||
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,3 +0,0 @@
|
||||
{
|
||||
"CIBW_SKIP": "cp26-* cp33-*"
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
from setuptools import setup, Extension
|
||||
import sys
|
||||
|
||||
if sys.argv[-1] != '--name':
|
||||
# explode if run on Python 2.6 or Python 3.3 (these should be skipped)
|
||||
if sys.version_info[0:2] == (2, 6):
|
||||
raise Exception('Python 2.6 should be skipped')
|
||||
if sys.version_info[0:2] == (3, 3):
|
||||
raise Exception('Python 3.3 should be skipped')
|
||||
|
||||
setup(
|
||||
name="spam",
|
||||
ext_modules=[Extension('spam', sources=['spam.c'])],
|
||||
version="0.1.0",
|
||||
)
|
||||
@@ -1,2 +0,0 @@
|
||||
3.6.0 (default, Feb 7 2017, 23:55:32)
|
||||
[GCC 4.8.2 20140120 (Red Hat 4.8.2-15)]
|
||||
@@ -0,0 +1,32 @@
|
||||
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,4 +0,0 @@
|
||||
{
|
||||
"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\""
|
||||
}
|
||||
@@ -1,21 +1,23 @@
|
||||
from setuptools import setup, Extension
|
||||
import sys, os
|
||||
import os
|
||||
|
||||
if sys.argv[-1] != '--name':
|
||||
# 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')
|
||||
from setuptools import (
|
||||
Extension,
|
||||
setup,
|
||||
)
|
||||
|
||||
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)
|
||||
# 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",
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
3.6.0 (default, Feb 7 2017, 23:55:32)
|
||||
[GCC 4.8.2 20140120 (Red Hat 4.8.2-15)]
|
||||
@@ -0,0 +1,26 @@
|
||||
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,4 +0,0 @@
|
||||
{
|
||||
"CIBW_MANYLINUX1_X86_64_IMAGE": "dockcross/manylinux-x64",
|
||||
"CIBW_MANYLINUX1_I686_IMAGE": "dockcross/manylinux-x86"
|
||||
}
|
||||
@@ -1,12 +1,15 @@
|
||||
import os, sys
|
||||
import os
|
||||
import sys
|
||||
|
||||
from setuptools import setup, Extension
|
||||
from setuptools import (
|
||||
Extension,
|
||||
setup,
|
||||
)
|
||||
|
||||
if sys.argv[-1] != '--name':
|
||||
# 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?')
|
||||
# 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",
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
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)
|
||||
@@ -0,0 +1,24 @@
|
||||
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",
|
||||
)
|
||||
@@ -0,0 +1,48 @@
|
||||
#include <Python.h>
|
||||
|
||||
static PyObject *
|
||||
spam_system(PyObject *self, PyObject *args)
|
||||
{
|
||||
const char *command;
|
||||
int sts;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s", &command))
|
||||
return NULL;
|
||||
sts = system(command);
|
||||
return PyLong_FromLong(sts);
|
||||
}
|
||||
|
||||
/* Module initialization */
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
#define MOD_INIT(name) PyMODINIT_FUNC PyInit_##name(void)
|
||||
#define MOD_DEF(m, name, doc, methods, module_state_size) \
|
||||
static struct PyModuleDef moduledef = { \
|
||||
PyModuleDef_HEAD_INIT, name, doc, module_state_size, methods, }; \
|
||||
m = PyModule_Create(&moduledef);
|
||||
#define MOD_RETURN(m) return m;
|
||||
#else
|
||||
#define MOD_INIT(name) PyMODINIT_FUNC init##name(void)
|
||||
#define MOD_DEF(m, name, doc, methods, module_state_size) \
|
||||
m = Py_InitModule3(name, methods, doc);
|
||||
#define MOD_RETURN(m) return;
|
||||
#endif
|
||||
|
||||
static PyMethodDef module_methods[] = {
|
||||
{"system", (PyCFunction)spam_system, METH_VARARGS,
|
||||
"Execute a shell command."},
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
MOD_INIT(spam)
|
||||
{
|
||||
PyObject* m;
|
||||
|
||||
MOD_DEF(m,
|
||||
"spam",
|
||||
"Example module",
|
||||
module_methods,
|
||||
-1)
|
||||
|
||||
MOD_RETURN(m)
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import os
|
||||
import platform
|
||||
|
||||
import pytest
|
||||
|
||||
import utils
|
||||
|
||||
|
||||
@pytest.mark.parametrize('manylinux_image', ['manylinux1', 'manylinux2010', 'manylinux2014'])
|
||||
def test(manylinux_image):
|
||||
project_dir = os.path.dirname(__file__)
|
||||
|
||||
if utils.platform != 'linux':
|
||||
pytest.skip('the docker test is only relevant to the linux build')
|
||||
elif platform.machine not in ['x86_64', 'i686']:
|
||||
if manylinux_image in ['manylinux1', 'manylinux2010']:
|
||||
pytest.skip("manylinux1 and 2010 doesn't exist for non-x86 architectures")
|
||||
|
||||
# build the wheels
|
||||
# CFLAGS environment variable is necessary to fail on 'malloc_info' (on manylinux1) during compilation/linking,
|
||||
# rather than when dynamically loading the Python
|
||||
add_env = {
|
||||
'CIBW_ENVIRONMENT': 'CFLAGS="$CFLAGS -Werror=implicit-function-declaration"',
|
||||
'CIBW_MANYLINUX_X86_64_IMAGE': manylinux_image,
|
||||
'CIBW_MANYLINUX_I686_IMAGE': manylinux_image,
|
||||
'CIBW_MANYLINUX_PYPY_X86_64_IMAGE': manylinux_image,
|
||||
'CIBW_MANYLINUX_AARCH64_IMAGE': manylinux_image,
|
||||
'CIBW_MANYLINUX_PPC64LE_IMAGE': manylinux_image,
|
||||
'CIBW_MANYLINUX_S390X_IMAGE': manylinux_image,
|
||||
}
|
||||
if manylinux_image == 'manylinux1':
|
||||
# We don't have a manylinux1 image for PyPy
|
||||
add_env['CIBW_SKIP'] = 'pp*'
|
||||
elif manylinux_image == 'manylinux2014':
|
||||
# We don't have a manylinux2014 image for PyPy (yet?)
|
||||
add_env['CIBW_SKIP'] = 'cp27* pp*' # Python 2.7 not available on manylinux2014
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
|
||||
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0', manylinux_versions=[manylinux_image])]
|
||||
if manylinux_image == 'manylinux2014':
|
||||
expected_wheels = [w for w in expected_wheels if '-cp27' not in w]
|
||||
if manylinux_image in ['manylinux1', 'manylinux2014']:
|
||||
expected_wheels = [w for w in expected_wheels if '-pp' not in w]
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
@@ -0,0 +1,10 @@
|
||||
from setuptools import (
|
||||
Extension,
|
||||
setup,
|
||||
)
|
||||
|
||||
setup(
|
||||
name="spam",
|
||||
ext_modules=[Extension('spam', sources=['spam.c'])],
|
||||
version="0.1.0",
|
||||
)
|
||||
@@ -0,0 +1,68 @@
|
||||
#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)
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
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)
|
||||
@@ -0,0 +1,6 @@
|
||||
[metadata]
|
||||
name = spam
|
||||
version = attr: spam.__version__
|
||||
|
||||
[options]
|
||||
packages = find:
|
||||
@@ -0,0 +1,8 @@
|
||||
from setuptools import (
|
||||
Extension,
|
||||
setup,
|
||||
)
|
||||
|
||||
setup(
|
||||
ext_modules=[Extension('spam.spam', sources=['spam/spam.c'])],
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
__version__ = "0.1.0"
|
||||
@@ -0,0 +1,48 @@
|
||||
#include <Python.h>
|
||||
|
||||
static PyObject *
|
||||
spam_system(PyObject *self, PyObject *args)
|
||||
{
|
||||
const char *command;
|
||||
int sts;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s", &command))
|
||||
return NULL;
|
||||
sts = system(command);
|
||||
return PyLong_FromLong(sts);
|
||||
}
|
||||
|
||||
/* Module initialization */
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
#define MOD_INIT(name) PyMODINIT_FUNC PyInit_##name(void)
|
||||
#define MOD_DEF(m, name, doc, methods, module_state_size) \
|
||||
static struct PyModuleDef moduledef = { \
|
||||
PyModuleDef_HEAD_INIT, name, doc, module_state_size, methods, }; \
|
||||
m = PyModule_Create(&moduledef);
|
||||
#define MOD_RETURN(m) return m;
|
||||
#else
|
||||
#define MOD_INIT(name) PyMODINIT_FUNC init##name(void)
|
||||
#define MOD_DEF(m, name, doc, methods, module_state_size) \
|
||||
m = Py_InitModule3(name, methods, doc);
|
||||
#define MOD_RETURN(m) return;
|
||||
#endif
|
||||
|
||||
static PyMethodDef module_methods[] = {
|
||||
{"system", (PyCFunction)spam_system, METH_VARARGS,
|
||||
"Execute a shell command."},
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
MOD_INIT(spam)
|
||||
{
|
||||
PyObject* m;
|
||||
|
||||
MOD_DEF(m,
|
||||
"spam",
|
||||
"Example module",
|
||||
module_methods,
|
||||
-1)
|
||||
|
||||
MOD_RETURN(m)
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import os
|
||||
|
||||
import pytest
|
||||
|
||||
import utils
|
||||
|
||||
project_dir = os.path.dirname(__file__)
|
||||
|
||||
|
||||
def test_cpp11(tmp_path):
|
||||
# This test checks that the C++11 standard is supported
|
||||
|
||||
add_env = {'CIBW_SKIP': 'cp27-win* pp27-win32', 'CIBW_ENVIRONMENT': 'STANDARD=11'}
|
||||
# VC++ for Python 2.7 does not support modern standards
|
||||
if utils.platform == 'macos':
|
||||
add_env['MACOSX_DEPLOYMENT_TARGET'] = '10.9'
|
||||
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
|
||||
expected_wheels = [w for w in utils.expected_wheels(
|
||||
'spam', '0.1.0', macosx_deployment_target='10.9')
|
||||
if 'cp27-cp27m-win' not in w
|
||||
and 'pp27-pypy_73-win32' not in w]
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
|
||||
|
||||
def test_cpp14():
|
||||
# This test checks that the C++14 standard is supported
|
||||
|
||||
add_env = {'CIBW_SKIP': 'cp27-win* pp27-win32 cp35-win*', 'CIBW_ENVIRONMENT': 'STANDARD=14'}
|
||||
# VC++ for Python 2.7 does not support modern standards
|
||||
# The manylinux1 docker image does not have a compiler which supports C++11
|
||||
# Python 3.4 and 3.5 are compiled with MSVC 10, which does not support C++14
|
||||
if utils.platform == 'macos':
|
||||
add_env['MACOSX_DEPLOYMENT_TARGET'] = '10.9'
|
||||
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
|
||||
expected_wheels = [w for w in utils.expected_wheels(
|
||||
'spam', '0.1.0', macosx_deployment_target='10.9')
|
||||
if 'cp27-cp27m-win' not in w
|
||||
and 'pp27-pypy_73-win32' not in w
|
||||
and 'cp35-cp35m-win' not in w]
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
|
||||
|
||||
def test_cpp17():
|
||||
# This test checks that the C++17 standard is supported
|
||||
|
||||
# Python and PyPy 2.7 use the `register` keyword which is forbidden in the C++17 standard
|
||||
# The manylinux1 docker image does not have a compiler which supports C++11
|
||||
# Python 3.5 and PyPy 3.6 are compiled with MSVC 10, which does not support C++17
|
||||
if os.environ.get('APPVEYOR_BUILD_WORKER_IMAGE', '') == 'Visual Studio 2015':
|
||||
pytest.skip('Visual Studio 2015 does not support C++17')
|
||||
|
||||
add_env = {'CIBW_SKIP': 'cp27-win* pp27-win32 cp35-win* pp36-win32', 'CIBW_ENVIRONMENT': 'STANDARD=17'}
|
||||
if utils.platform == 'macos':
|
||||
add_env['MACOSX_DEPLOYMENT_TARGET'] = '10.13'
|
||||
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
|
||||
expected_wheels = [w for w in utils.expected_wheels(
|
||||
'spam', '0.1.0', macosx_deployment_target='10.13')
|
||||
if 'cp27-cp27m-win' not in w
|
||||
and 'pp27-pypy_73-win32' not in w
|
||||
and 'cp35-cp35m-win' not in w
|
||||
and 'pp36-pypy36_pp73-win32' not in w]
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
@@ -0,0 +1,25 @@
|
||||
import os
|
||||
import platform
|
||||
|
||||
from setuptools import (
|
||||
Extension,
|
||||
setup,
|
||||
)
|
||||
|
||||
standard = os.environ["STANDARD"]
|
||||
|
||||
language_standard = "/std:c++" + standard if platform.system() == "Windows" else "-std=c++" + standard
|
||||
|
||||
extra_compile_args = [language_standard, "-DSTANDARD=" + standard]
|
||||
|
||||
if standard == "17":
|
||||
if platform.system() == "Windows":
|
||||
extra_compile_args.append("/wd5033")
|
||||
else:
|
||||
extra_compile_args.append("-Wno-register")
|
||||
|
||||
setup(
|
||||
name="spam",
|
||||
ext_modules=[Extension('spam', sources=['spam.cpp'], language="c++", extra_compile_args=extra_compile_args)],
|
||||
version="0.1.0",
|
||||
)
|
||||
@@ -0,0 +1,62 @@
|
||||
#include <Python.h>
|
||||
#include <string>
|
||||
|
||||
// Depending on the requested standard, use a modern C++ feature
|
||||
// that was introduced in that standard.
|
||||
#if STANDARD == 11
|
||||
#include <array>
|
||||
#elif STANDARD == 14
|
||||
int a = 100'000;
|
||||
#elif STANDARD == 17
|
||||
#include <utility>
|
||||
auto a = std::pair(5.0, false);
|
||||
#else
|
||||
#error Standard needed
|
||||
#endif
|
||||
|
||||
static PyObject *
|
||||
spam_system(PyObject *self, PyObject *args)
|
||||
{
|
||||
const char *command;
|
||||
int sts;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s", &command))
|
||||
return NULL;
|
||||
sts = system(command);
|
||||
return PyLong_FromLong(sts);
|
||||
}
|
||||
|
||||
/* Module initialization */
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
#define MOD_INIT(name) PyMODINIT_FUNC PyInit_##name(void)
|
||||
#define MOD_DEF(m, name, doc, methods, module_state_size) \
|
||||
static struct PyModuleDef moduledef = { \
|
||||
PyModuleDef_HEAD_INIT, name, doc, module_state_size, methods, }; \
|
||||
m = PyModule_Create(&moduledef);
|
||||
#define MOD_RETURN(m) return m;
|
||||
#else
|
||||
#define MOD_INIT(name) PyMODINIT_FUNC init##name(void)
|
||||
#define MOD_DEF(m, name, doc, methods, module_state_size) \
|
||||
m = Py_InitModule3(name, methods, doc);
|
||||
#define MOD_RETURN(m) return;
|
||||
#endif
|
||||
|
||||
static PyMethodDef module_methods[] = {
|
||||
{"system", (PyCFunction)spam_system, METH_VARARGS,
|
||||
"Execute a shell command."},
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
MOD_INIT(spam)
|
||||
{
|
||||
PyObject* m;
|
||||
|
||||
MOD_DEF(m,
|
||||
"spam",
|
||||
"Example module",
|
||||
module_methods,
|
||||
-1)
|
||||
|
||||
MOD_RETURN(m)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import os
|
||||
import utils
|
||||
|
||||
|
||||
def test():
|
||||
project_dir = os.path.dirname(__file__)
|
||||
|
||||
# build the wheels
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
||||
# write python version information to a temporary file, this is
|
||||
# checked in setup.py
|
||||
'CIBW_BEFORE_TEST': '''python -c "import sys; open('/tmp/pythonversion.txt', 'w').write(sys.version)" && python -c "import sys; open('/tmp/pythonprefix.txt', 'w').write(sys.prefix)"''',
|
||||
'CIBW_BEFORE_TEST_WINDOWS': '''python -c "import sys; open('c:\\pythonversion.txt', 'w').write(sys.version)" && python -c "import sys; open('c:\\pythonprefix.txt', 'w').write(sys.prefix)"''',
|
||||
'CIBW_TEST_REQUIRES': 'nose',
|
||||
# the 'false ||' bit is to ensure this command runs in a shell on
|
||||
# mac/linux.
|
||||
'CIBW_TEST_COMMAND': 'false || nosetests {project}/test',
|
||||
'CIBW_TEST_COMMAND_WINDOWS': 'nosetests {project}/test',
|
||||
})
|
||||
|
||||
# also check that we got the right wheels
|
||||
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
@@ -0,0 +1,8 @@
|
||||
from setuptools import setup, Extension
|
||||
|
||||
|
||||
setup(
|
||||
name="spam",
|
||||
ext_modules=[Extension('spam', sources=['spam.c'])],
|
||||
version="0.1.0",
|
||||
)
|
||||
@@ -0,0 +1,48 @@
|
||||
#include <Python.h>
|
||||
|
||||
static PyObject *
|
||||
spam_system(PyObject *self, PyObject *args)
|
||||
{
|
||||
const char *command;
|
||||
int sts;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s", &command))
|
||||
return NULL;
|
||||
sts = system(command);
|
||||
return PyLong_FromLong(sts);
|
||||
}
|
||||
|
||||
/* Module initialization */
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
#define MOD_INIT(name) PyMODINIT_FUNC PyInit_##name(void)
|
||||
#define MOD_DEF(m, name, doc, methods, module_state_size) \
|
||||
static struct PyModuleDef moduledef = { \
|
||||
PyModuleDef_HEAD_INIT, name, doc, module_state_size, methods, }; \
|
||||
m = PyModule_Create(&moduledef);
|
||||
#define MOD_RETURN(m) return m;
|
||||
#else
|
||||
#define MOD_INIT(name) PyMODINIT_FUNC init##name(void)
|
||||
#define MOD_DEF(m, name, doc, methods, module_state_size) \
|
||||
m = Py_InitModule3(name, methods, doc);
|
||||
#define MOD_RETURN(m) return;
|
||||
#endif
|
||||
|
||||
static PyMethodDef module_methods[] = {
|
||||
{"system", (PyCFunction)spam_system, METH_VARARGS,
|
||||
"Execute a shell command."},
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
MOD_INIT(spam)
|
||||
{
|
||||
PyObject* m;
|
||||
|
||||
MOD_DEF(m,
|
||||
"spam",
|
||||
"Example module",
|
||||
module_methods,
|
||||
-1)
|
||||
|
||||
MOD_RETURN(m)
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import sys
|
||||
import os
|
||||
from unittest import TestCase
|
||||
|
||||
|
||||
class TestBeforeTest(TestCase):
|
||||
def test_version(self):
|
||||
# assert that the Python version as written to pythonversion.txt in the CIBW_BEFORE_TEST step
|
||||
# is the same one as is currently running.
|
||||
# because of use symlinks in MacOS run this test is also need
|
||||
version_file = 'c:\\pythonversion.txt' if sys.platform == 'win32' else '/tmp/pythonversion.txt'
|
||||
with open(version_file) as f:
|
||||
stored_version = f.read()
|
||||
print('stored_version', stored_version)
|
||||
print('sys.version', sys.version)
|
||||
assert stored_version == sys.version
|
||||
|
||||
def test_prefix(self):
|
||||
# check that the prefix also was written
|
||||
prefix_file = 'c:\\pythonprefix.txt' if sys.platform == 'win32' else '/tmp/pythonprefix.txt'
|
||||
with open(prefix_file) as f:
|
||||
stored_prefix = f.read()
|
||||
print('stored_prefix', stored_prefix)
|
||||
print('sys.prefix', sys.prefix)
|
||||
# Works around path-comparison bugs caused by short-paths on Windows e.g.
|
||||
# vssadm~1 instead of vssadministrator
|
||||
|
||||
assert os.stat(stored_prefix) == os.stat(sys.prefix)
|
||||
@@ -0,0 +1,4 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), 'shared'))
|
||||
@@ -0,0 +1,151 @@
|
||||
'''
|
||||
Utility functions used by the cibuildwheel tests.
|
||||
|
||||
This file is added to the PYTHONPATH in the test runner at bin/run_test.py.
|
||||
'''
|
||||
|
||||
import os
|
||||
import platform as pm
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from contextlib import contextmanager
|
||||
from tempfile import mkdtemp
|
||||
|
||||
IS_WINDOWS_RUNNING_ON_AZURE = os.path.exists('C:\\hostedtoolcache')
|
||||
IS_WINDOWS_RUNNING_ON_TRAVIS = os.environ.get('TRAVIS_OS_NAME') == 'windows'
|
||||
|
||||
|
||||
# Python 2 does not have a tempfile.TemporaryDirectory context manager
|
||||
@contextmanager
|
||||
def TemporaryDirectoryIfNone(path):
|
||||
_path = path or mkdtemp()
|
||||
try:
|
||||
yield _path
|
||||
finally:
|
||||
if path is None:
|
||||
shutil.rmtree(_path)
|
||||
|
||||
|
||||
def cibuildwheel_get_build_identifiers(project_path, env=None):
|
||||
'''
|
||||
Returns the list of build identifiers that cibuildwheel will try to build
|
||||
for the current platform.
|
||||
'''
|
||||
cmd_output = subprocess.check_output(
|
||||
[sys.executable, '-m', 'cibuildwheel', '--print-build-identifiers', project_path],
|
||||
universal_newlines=True,
|
||||
env=env,
|
||||
)
|
||||
|
||||
return cmd_output.strip().split('\n')
|
||||
|
||||
|
||||
def cibuildwheel_run(project_path, env=None, add_env=None, output_dir=None):
|
||||
'''
|
||||
Runs cibuildwheel as a subprocess, building the project at project_path.
|
||||
|
||||
Uses the current Python interpreter.
|
||||
|
||||
:param project_path: path of the project to be built.
|
||||
:param env: full environment to be used, os.environ if None
|
||||
:param add_env: environment used to update env
|
||||
:param output_dir: directory where wheels are saved. If None, a temporary
|
||||
directory will be used for the duration of the command.
|
||||
:return: list of built wheels (file names).
|
||||
'''
|
||||
if env is None:
|
||||
env = os.environ.copy()
|
||||
# If present in the host environment, remove the MACOSX_DEPLOYMENT_TARGET for consistency
|
||||
env.pop('MACOSX_DEPLOYMENT_TARGET', None)
|
||||
|
||||
if add_env is not None:
|
||||
env.update(add_env)
|
||||
|
||||
with TemporaryDirectoryIfNone(output_dir) as _output_dir:
|
||||
subprocess.check_call(
|
||||
[sys.executable, '-m', 'cibuildwheel', '--output-dir', str(_output_dir), project_path],
|
||||
env=env,
|
||||
)
|
||||
wheels = os.listdir(_output_dir)
|
||||
return wheels
|
||||
|
||||
|
||||
def expected_wheels(package_name, package_version, manylinux_versions=None,
|
||||
macosx_deployment_target=None):
|
||||
'''
|
||||
Returns a list of expected wheels from a run of cibuildwheel.
|
||||
'''
|
||||
# per PEP 425 (https://www.python.org/dev/peps/pep-0425/), wheel files shall have name of the form
|
||||
# {distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-{platform tag}.whl
|
||||
# {python tag} and {abi tag} are closely related to the python interpreter used to build the wheel
|
||||
# so we'll merge them below as python_abi_tag
|
||||
|
||||
python_abi_tags = ['cp35-cp35m', 'cp36-cp36m', 'cp37-cp37m', 'cp38-cp38']
|
||||
extra_x86_python_abi_tags = ['cp27-cp27m', 'pp27-pypy_73', 'pp36-pypy36_pp73']
|
||||
|
||||
if platform == 'linux':
|
||||
if pm.machine() not in ['x86_64', 'i686']:
|
||||
if manylinux_versions is None:
|
||||
manylinux_versions = ['manylinux2014']
|
||||
architectures = {'cp': [pm.machine()]}
|
||||
else:
|
||||
if manylinux_versions is None:
|
||||
manylinux_versions = ['manylinux1', 'manylinux2010']
|
||||
python_abi_tags += extra_x86_python_abi_tags
|
||||
python_abi_tags.append('cp27-cp27mu') # python 2.7 has 2 different ABI on manylinux
|
||||
architectures = {'cp': ['x86_64', 'i686'], 'pp': ['x86_64']}
|
||||
platform_tags = {}
|
||||
for python_implemention in architectures:
|
||||
platform_tags[python_implemention] = [
|
||||
'{manylinux_version}_{architecture}'.format(
|
||||
manylinux_version=manylinux_version, architecture=architecture)
|
||||
for architecture in architectures[python_implemention]
|
||||
for manylinux_version in manylinux_versions
|
||||
]
|
||||
|
||||
def get_platform_tags(python_abi_tag):
|
||||
return platform_tags[python_abi_tag[:2]]
|
||||
elif platform == 'windows':
|
||||
python_abi_tags += extra_x86_python_abi_tags
|
||||
platform_tags = {'cp': ['win32', 'win_amd64'], 'pp': ['win32']}
|
||||
|
||||
def get_platform_tags(python_abi_tag):
|
||||
return platform_tags[python_abi_tag[:2]]
|
||||
|
||||
elif platform == 'macos':
|
||||
python_abi_tags += extra_x86_python_abi_tags
|
||||
|
||||
def get_platform_tags(python_abi_tag):
|
||||
default_version = '10.7' if python_abi_tag.startswith('pp') else '10.9'
|
||||
return ['macosx_{}_x86_64'.format((macosx_deployment_target or default_version).replace('.', '_'))]
|
||||
else:
|
||||
raise Exception('unsupported platform')
|
||||
|
||||
templates = []
|
||||
for python_abi_tag in python_abi_tags:
|
||||
for platform_tag in get_platform_tags(python_abi_tag):
|
||||
templates.append('{package_name}-{package_version}-{python_abi_tag}-{platform_tag}.whl'.format(
|
||||
package_name=package_name, package_version=package_version,
|
||||
python_abi_tag=python_abi_tag, platform_tag=platform_tag
|
||||
))
|
||||
|
||||
if IS_WINDOWS_RUNNING_ON_TRAVIS:
|
||||
# Python 2.7 isn't supported on Travis.
|
||||
templates = [t for t in templates if '-cp27-' not in t and '-pp2' not in t]
|
||||
|
||||
return templates
|
||||
|
||||
|
||||
platform = None
|
||||
|
||||
if 'CIBW_PLATFORM' in os.environ:
|
||||
platform = os.environ['CIBW_PLATFORM']
|
||||
elif sys.platform.startswith('linux'):
|
||||
platform = 'linux'
|
||||
elif sys.platform.startswith('darwin'):
|
||||
platform = 'macos'
|
||||
elif sys.platform in ['win32', 'cygwin']:
|
||||
platform = 'windows'
|
||||
else:
|
||||
raise Exception('Unsupported platform')
|
||||
@@ -0,0 +1,61 @@
|
||||
from cibuildwheel.util import BuildSelector
|
||||
|
||||
|
||||
def test_build():
|
||||
build_selector = BuildSelector(build_config="cp3?-* *-manylinux1*", skip_config="")
|
||||
|
||||
assert build_selector('cp27-manylinux1_x86_64')
|
||||
assert build_selector('cp36-manylinux1_x86_64')
|
||||
assert build_selector('cp37-manylinux1_x86_64')
|
||||
assert build_selector('cp27-manylinux1_i686')
|
||||
assert build_selector('cp36-manylinux1_i686')
|
||||
assert build_selector('cp37-manylinux1_i686')
|
||||
assert not build_selector('cp27-macosx_10_6_intel')
|
||||
assert build_selector('cp36-macosx_10_6_intel')
|
||||
assert build_selector('cp37-macosx_10_6_intel')
|
||||
assert not build_selector('cp27-win32')
|
||||
assert build_selector('cp36-win32')
|
||||
assert build_selector('cp37-win32')
|
||||
assert not build_selector('cp27-win_amd64')
|
||||
assert build_selector('cp36-win_amd64')
|
||||
assert build_selector('cp37-win_amd64')
|
||||
|
||||
|
||||
def test_skip():
|
||||
build_selector = BuildSelector(build_config="*", skip_config="cp27-* cp3?-manylinux1_i686 cp36-win* *-win32")
|
||||
|
||||
assert not build_selector('cp27-manylinux1_x86_64')
|
||||
assert build_selector('cp36-manylinux1_x86_64')
|
||||
assert build_selector('cp37-manylinux1_x86_64')
|
||||
assert not build_selector('cp27-manylinux1_i686')
|
||||
assert not build_selector('cp36-manylinux1_i686')
|
||||
assert not build_selector('cp37-manylinux1_i686')
|
||||
assert not build_selector('cp27-macosx_10_6_intel')
|
||||
assert build_selector('cp36-macosx_10_6_intel')
|
||||
assert build_selector('cp37-macosx_10_6_intel')
|
||||
assert not build_selector('cp27-win32')
|
||||
assert not build_selector('cp36-win32')
|
||||
assert not build_selector('cp37-win32')
|
||||
assert not build_selector('cp27-win_amd64')
|
||||
assert not build_selector('cp36-win_amd64')
|
||||
assert build_selector('cp37-win_amd64')
|
||||
|
||||
|
||||
def test_build_and_skip():
|
||||
build_selector = BuildSelector(build_config="cp36-* cp37-macosx* *-manylinux1*", skip_config="cp27-* cp37-manylinux1_i686")
|
||||
|
||||
assert not build_selector('cp27-manylinux1_x86_64')
|
||||
assert build_selector('cp36-manylinux1_x86_64')
|
||||
assert build_selector('cp37-manylinux1_x86_64')
|
||||
assert not build_selector('cp27-manylinux1_i686')
|
||||
assert build_selector('cp36-manylinux1_i686')
|
||||
assert not build_selector('cp37-manylinux1_i686')
|
||||
assert not build_selector('cp27-macosx_10_6_intel')
|
||||
assert build_selector('cp36-macosx_10_6_intel')
|
||||
assert build_selector('cp37-macosx_10_6_intel')
|
||||
assert not build_selector('cp27-win32')
|
||||
assert build_selector('cp36-win32')
|
||||
assert not build_selector('cp37-win32')
|
||||
assert not build_selector('cp27-win_amd64')
|
||||
assert build_selector('cp36-win_amd64')
|
||||
assert not build_selector('cp37-win_amd64')
|
||||
@@ -1,3 +1,5 @@
|
||||
import os
|
||||
|
||||
from cibuildwheel.environment import parse_environment
|
||||
|
||||
|
||||
@@ -12,6 +14,7 @@ def test_basic_parsing():
|
||||
assert environment_dict == {'VAR': '1', 'VBR': '2'}
|
||||
assert environment_cmds == ['export VAR=1', 'export VBR=2']
|
||||
|
||||
|
||||
def test_quotes():
|
||||
environment_recipe = parse_environment('A=1 VAR="1 NOT_A_VAR=2" VBR=\'vbr\'')
|
||||
|
||||
@@ -23,6 +26,7 @@ def test_quotes():
|
||||
assert environment_dict == {'A': '1', 'VAR': '1 NOT_A_VAR=2', 'VBR': 'vbr'}
|
||||
assert environment_cmds == ['export A=1', 'export VAR="1 NOT_A_VAR=2"', 'export VBR=\'vbr\'']
|
||||
|
||||
|
||||
def test_inheritance():
|
||||
environment_recipe = parse_environment('PATH=$PATH:/usr/local/bin')
|
||||
|
||||
@@ -34,17 +38,22 @@ def test_inheritance():
|
||||
assert environment_dict == {'PATH': '/usr/bin:/usr/local/bin'}
|
||||
assert environment_cmds == ['export PATH=$PATH:/usr/local/bin']
|
||||
|
||||
|
||||
def test_shell_eval():
|
||||
environment_recipe = parse_environment('VAR="$(echo "a test" string)"')
|
||||
|
||||
env_copy = os.environ.copy()
|
||||
env_copy.pop('VAR', None)
|
||||
|
||||
environment_dict = environment_recipe.as_dictionary(
|
||||
prev_environment={}
|
||||
prev_environment=env_copy
|
||||
)
|
||||
environment_cmds = environment_recipe.as_shell_commands()
|
||||
|
||||
assert environment_dict == {'VAR': 'a test string'}
|
||||
assert environment_dict['VAR'] == 'a test string'
|
||||
assert environment_cmds == ['export VAR="$(echo "a test" string)"']
|
||||
|
||||
|
||||
def test_shell_eval_and_env():
|
||||
environment_recipe = parse_environment('VAR="$(echo "$PREV_VAR" string)"')
|
||||
|
||||
@@ -56,6 +65,7 @@ def test_shell_eval_and_env():
|
||||
assert environment_dict == {'PREV_VAR': '1 2 3', 'VAR': '1 2 3 string'}
|
||||
assert environment_cmds == ['export VAR="$(echo "$PREV_VAR" string)"']
|
||||
|
||||
|
||||
def test_empty_var():
|
||||
environment_recipe = parse_environment('CFLAGS=')
|
||||
|
||||
@@ -67,6 +77,7 @@ def test_empty_var():
|
||||
assert environment_dict == {'CFLAGS': ''}
|
||||
assert environment_cmds == ['export CFLAGS=']
|
||||
|
||||
|
||||
def test_no_vars():
|
||||
environment_recipe = parse_environment('')
|
||||
|
||||
@@ -76,6 +87,7 @@ def test_no_vars():
|
||||
assert environment_dict == {}
|
||||
assert environment_cmds == []
|
||||
|
||||
|
||||
def test_no_vars_pass_through():
|
||||
environment_recipe = parse_environment('')
|
||||
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
from cibuildwheel import (
|
||||
linux,
|
||||
macos,
|
||||
util,
|
||||
windows,
|
||||
)
|
||||
|
||||
|
||||
class ArgsInterceptor:
|
||||
def __call__(self, *args, **kwargs):
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
|
||||
|
||||
MOCK_PROJECT_DIR = 'some_project_dir'
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_protection(monkeypatch):
|
||||
'''
|
||||
Ensure that a unit test will never actually run a cibuildwheel 'build'
|
||||
function, which shouldn't be run on a developer's machine
|
||||
'''
|
||||
|
||||
def fail_on_call(*args, **kwargs):
|
||||
raise RuntimeError("This should never be called")
|
||||
|
||||
monkeypatch.setattr(subprocess, 'Popen', fail_on_call)
|
||||
monkeypatch.setattr(util, 'download', fail_on_call)
|
||||
monkeypatch.setattr(windows, 'build', fail_on_call)
|
||||
monkeypatch.setattr(linux, 'build', fail_on_call)
|
||||
monkeypatch.setattr(macos, 'build', fail_on_call)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def fake_project_dir(monkeypatch):
|
||||
'''
|
||||
Monkey-patch enough for the main() function to run
|
||||
'''
|
||||
real_os_path_exists = os.path.exists
|
||||
|
||||
def mock_os_path_exists(path):
|
||||
if path == os.path.join(MOCK_PROJECT_DIR, 'setup.py'):
|
||||
return True
|
||||
else:
|
||||
return real_os_path_exists(path)
|
||||
|
||||
monkeypatch.setattr(os.path, 'exists', mock_os_path_exists)
|
||||
monkeypatch.setattr(sys, 'argv', ['cibuildwheel', MOCK_PROJECT_DIR])
|
||||
|
||||
|
||||
@pytest.fixture(params=['linux', 'macos', 'windows'])
|
||||
def platform(request, monkeypatch):
|
||||
platform_value = request.param
|
||||
monkeypatch.setenv('CIBW_PLATFORM', platform_value)
|
||||
return platform_value
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def intercepted_build_args(platform, monkeypatch):
|
||||
intercepted = ArgsInterceptor()
|
||||
monkeypatch.setattr(globals()[platform], 'build', intercepted)
|
||||
return intercepted
|
||||
@@ -0,0 +1,230 @@
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
from cibuildwheel.__main__ import main
|
||||
from cibuildwheel.environment import ParsedEnvironment
|
||||
from cibuildwheel.util import BuildSelector
|
||||
|
||||
|
||||
# CIBW_PLATFORM is tested in main_platform_test.py
|
||||
|
||||
|
||||
def test_output_dir(platform, intercepted_build_args, monkeypatch):
|
||||
OUTPUT_DIR = 'some_output_dir'
|
||||
|
||||
monkeypatch.setenv('CIBW_OUTPUT_DIR', OUTPUT_DIR)
|
||||
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.kwargs['output_dir'] == OUTPUT_DIR
|
||||
|
||||
|
||||
def test_output_dir_default(platform, intercepted_build_args, monkeypatch):
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.kwargs['output_dir'] == 'wheelhouse'
|
||||
|
||||
|
||||
@pytest.mark.parametrize('also_set_environment', [False, True])
|
||||
def test_output_dir_argument(also_set_environment, platform, intercepted_build_args, monkeypatch):
|
||||
OUTPUT_DIR = 'some_output_dir'
|
||||
|
||||
monkeypatch.setattr(sys, 'argv', sys.argv + ['--output-dir', OUTPUT_DIR])
|
||||
if also_set_environment:
|
||||
monkeypatch.setenv('CIBW_OUTPUT_DIR', 'not_this_output_dir')
|
||||
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.kwargs['output_dir'] == OUTPUT_DIR
|
||||
|
||||
|
||||
def test_build_selector(platform, intercepted_build_args, monkeypatch):
|
||||
BUILD = 'some build* *-selector'
|
||||
SKIP = 'some skip* *-selector'
|
||||
|
||||
monkeypatch.setenv('CIBW_BUILD', BUILD)
|
||||
monkeypatch.setenv('CIBW_SKIP', SKIP)
|
||||
|
||||
main()
|
||||
|
||||
intercepted_build_selector = intercepted_build_args.kwargs['build_selector']
|
||||
assert isinstance(intercepted_build_selector, BuildSelector)
|
||||
assert intercepted_build_selector('build-this')
|
||||
assert not intercepted_build_selector('skip-that')
|
||||
# This unit test is just testing the options of 'main'
|
||||
# Unit tests for BuildSelector are in build_selector_test.py
|
||||
|
||||
|
||||
@pytest.mark.parametrize('architecture, image, full_image', [
|
||||
('x86_64', None, 'quay.io/pypa/manylinux2010_x86_64'),
|
||||
('x86_64', 'manylinux1', 'quay.io/pypa/manylinux1_x86_64'),
|
||||
('x86_64', 'manylinux2010', 'quay.io/pypa/manylinux2010_x86_64'),
|
||||
('x86_64', 'manylinux2014', 'quay.io/pypa/manylinux2014_x86_64'),
|
||||
('x86_64', 'custom_image', 'custom_image'),
|
||||
('i686', None, 'quay.io/pypa/manylinux2010_i686'),
|
||||
('i686', 'manylinux1', 'quay.io/pypa/manylinux1_i686'),
|
||||
('i686', 'manylinux2010', 'quay.io/pypa/manylinux2010_i686'),
|
||||
('i686', 'manylinux2014', 'quay.io/pypa/manylinux2014_i686'),
|
||||
('i686', 'custom_image', 'custom_image'),
|
||||
('pypy_x86_64', None, 'pypywheels/manylinux2010-pypy_x86_64'),
|
||||
('pypy_x86_64', 'manylinux1', 'manylinux1'), # Does not exist
|
||||
('pypy_x86_64', 'manylinux2010', 'pypywheels/manylinux2010-pypy_x86_64'),
|
||||
('pypy_x86_64', 'manylinux2014', 'manylinux2014'), # Does not exist (yet)
|
||||
('pypy_x86_64', 'custom_image', 'custom_image'),
|
||||
])
|
||||
def test_manylinux_images(architecture, image, full_image, platform, intercepted_build_args, monkeypatch):
|
||||
if image is not None:
|
||||
monkeypatch.setenv('CIBW_MANYLINUX_' + architecture.upper() + '_IMAGE', image)
|
||||
|
||||
main()
|
||||
|
||||
if platform == 'linux':
|
||||
assert intercepted_build_args.kwargs['manylinux_images'][architecture] == full_image
|
||||
else:
|
||||
assert 'manylinux_images' not in intercepted_build_args.kwargs
|
||||
|
||||
|
||||
def get_default_repair_command(platform):
|
||||
if platform == 'linux':
|
||||
return 'auditwheel repair -w {dest_dir} {wheel}'
|
||||
elif platform == 'macos':
|
||||
return 'delocate-listdeps {wheel} && delocate-wheel --require-archs x86_64 -w {dest_dir} {wheel}'
|
||||
elif platform == 'windows':
|
||||
return ''
|
||||
else:
|
||||
raise ValueError('Unknown platform', platform)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('repair_command', [None, 'repair', 'repair -w {dest_dir} {wheel}'])
|
||||
@pytest.mark.parametrize('platform_specific', [False, True])
|
||||
def test_repair_command(repair_command, platform_specific, platform, intercepted_build_args, monkeypatch):
|
||||
if repair_command is not None:
|
||||
if platform_specific:
|
||||
monkeypatch.setenv('CIBW_REPAIR_WHEEL_COMMAND_' + platform.upper(), repair_command)
|
||||
monkeypatch.setenv('CIBW_REPAIR_WHEEL_COMMAND', 'overwritten')
|
||||
else:
|
||||
monkeypatch.setenv('CIBW_REPAIR_WHEEL_COMMAND', repair_command)
|
||||
|
||||
main()
|
||||
|
||||
expected_repair = repair_command or get_default_repair_command(platform)
|
||||
assert intercepted_build_args.kwargs['repair_command'] == expected_repair
|
||||
|
||||
|
||||
@pytest.mark.parametrize('environment', [
|
||||
{},
|
||||
{'something': 'value'},
|
||||
{'something': 'value', 'something_else': 'other_value'}
|
||||
])
|
||||
@pytest.mark.parametrize('platform_specific', [False, True])
|
||||
def test_environment(environment, platform_specific, platform, intercepted_build_args, monkeypatch):
|
||||
env_string = ' '.join(['{}={}'.format(k, v) for k, v in environment.items()])
|
||||
if platform_specific:
|
||||
monkeypatch.setenv('CIBW_ENVIRONMENT_' + platform.upper(), env_string)
|
||||
monkeypatch.setenv('CIBW_ENVIRONMENT', 'overwritten')
|
||||
else:
|
||||
monkeypatch.setenv('CIBW_ENVIRONMENT', env_string)
|
||||
|
||||
main()
|
||||
|
||||
intercepted_environment = intercepted_build_args.kwargs['environment']
|
||||
assert isinstance(intercepted_environment, ParsedEnvironment)
|
||||
assert intercepted_environment.as_dictionary(prev_environment={}) == environment
|
||||
|
||||
|
||||
@pytest.mark.parametrize('test_requires', [None, 'requirement other_requirement'])
|
||||
@pytest.mark.parametrize('platform_specific', [False, True])
|
||||
def test_test_requires(test_requires, platform_specific, platform, intercepted_build_args, monkeypatch):
|
||||
if test_requires is not None:
|
||||
if platform_specific:
|
||||
monkeypatch.setenv('CIBW_TEST_REQUIRES_' + platform.upper(), test_requires)
|
||||
monkeypatch.setenv('CIBW_TEST_REQUIRES', 'overwritten')
|
||||
else:
|
||||
monkeypatch.setenv('CIBW_TEST_REQUIRES', test_requires)
|
||||
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.kwargs['test_requires'] == (test_requires or '').split()
|
||||
|
||||
|
||||
@pytest.mark.parametrize('test_extras', [None, 'extras'])
|
||||
@pytest.mark.parametrize('platform_specific', [False, True])
|
||||
def test_test_extras(test_extras, platform_specific, platform, intercepted_build_args, monkeypatch):
|
||||
if test_extras is not None:
|
||||
if platform_specific:
|
||||
monkeypatch.setenv('CIBW_TEST_EXTRAS_' + platform.upper(), test_extras)
|
||||
monkeypatch.setenv('CIBW_TEST_EXTRAS', 'overwritten')
|
||||
else:
|
||||
monkeypatch.setenv('CIBW_TEST_EXTRAS', test_extras)
|
||||
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.kwargs['test_extras'] == ('[' + test_extras + ']' if test_extras else '')
|
||||
|
||||
|
||||
@pytest.mark.parametrize('test_command', [None, 'test --command'])
|
||||
@pytest.mark.parametrize('platform_specific', [False, True])
|
||||
def test_test_command(test_command, platform_specific, platform, intercepted_build_args, monkeypatch):
|
||||
if test_command is not None:
|
||||
if platform_specific:
|
||||
monkeypatch.setenv('CIBW_TEST_COMMAND_' + platform.upper(), test_command)
|
||||
monkeypatch.setenv('CIBW_TEST_COMMAND', 'overwritten')
|
||||
else:
|
||||
monkeypatch.setenv('CIBW_TEST_COMMAND', test_command)
|
||||
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.kwargs['test_command'] == test_command
|
||||
|
||||
|
||||
@pytest.mark.parametrize('before_build', [None, 'before --build'])
|
||||
@pytest.mark.parametrize('platform_specific', [False, True])
|
||||
def test_before_build(before_build, platform_specific, platform, intercepted_build_args, monkeypatch):
|
||||
if before_build is not None:
|
||||
if platform_specific:
|
||||
monkeypatch.setenv('CIBW_BEFORE_BUILD_' + platform.upper(), before_build)
|
||||
monkeypatch.setenv('CIBW_BEFORE_BUILD', 'overwritten')
|
||||
else:
|
||||
monkeypatch.setenv('CIBW_BEFORE_BUILD', before_build)
|
||||
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.kwargs['before_build'] == before_build
|
||||
|
||||
|
||||
@pytest.mark.parametrize('build_verbosity', [None, 0, 2, -2, 4, -4])
|
||||
@pytest.mark.parametrize('platform_specific', [False, True])
|
||||
def test_build_verbosity(build_verbosity, platform_specific, platform, intercepted_build_args, monkeypatch):
|
||||
if build_verbosity is not None:
|
||||
if platform_specific:
|
||||
monkeypatch.setenv('CIBW_BUILD_VERBOSITY_' + platform.upper(), str(build_verbosity))
|
||||
monkeypatch.setenv('CIBW_BUILD_VERBOSITY', 'overwritten')
|
||||
else:
|
||||
monkeypatch.setenv('CIBW_BUILD_VERBOSITY', str(build_verbosity))
|
||||
|
||||
main()
|
||||
|
||||
expected_verbosity = max(-3, min(3, int(build_verbosity or 0)))
|
||||
assert intercepted_build_args.kwargs['build_verbosity'] == expected_verbosity
|
||||
|
||||
|
||||
@pytest.mark.parametrize('option_name', ['CIBW_BUILD', 'CIBW_SKIP'])
|
||||
@pytest.mark.parametrize('option_value, build_selector_patterns', [
|
||||
('*-manylinux1_*', ['*-manylinux_*']),
|
||||
('*-macosx_10_6_intel', ['*-macosx_x86_64']),
|
||||
('*-macosx_10_9_x86_64', ['*-macosx_x86_64']),
|
||||
('cp37-macosx_10_9_x86_64', ['cp37-macosx_x86_64']),
|
||||
])
|
||||
def test_build_selector_migrations(intercepted_build_args, monkeypatch, option_name, option_value, build_selector_patterns):
|
||||
monkeypatch.setenv(option_name, option_value)
|
||||
|
||||
main()
|
||||
|
||||
intercepted_build_selector = intercepted_build_args.kwargs['build_selector']
|
||||
assert isinstance(intercepted_build_selector, BuildSelector)
|
||||
|
||||
if option_name == 'CIBW_BUILD':
|
||||
assert intercepted_build_selector.build_patterns == build_selector_patterns
|
||||
else:
|
||||
assert intercepted_build_selector.skip_patterns == build_selector_patterns
|
||||
@@ -0,0 +1,60 @@
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
from cibuildwheel.__main__ import main
|
||||
|
||||
from conftest import MOCK_PROJECT_DIR # noqa: I100
|
||||
|
||||
|
||||
def test_unknown_platform_non_ci(monkeypatch, capsys):
|
||||
monkeypatch.delenv('CI', raising=False)
|
||||
monkeypatch.delenv('BITRISE_BUILD_NUMBER', raising=False)
|
||||
monkeypatch.delenv('AZURE_HTTP_USER_AGENT', raising=False)
|
||||
monkeypatch.delenv('GITHUB_WORKFLOW', raising=False)
|
||||
|
||||
with pytest.raises(SystemExit) as exit:
|
||||
main()
|
||||
assert exit.value.code == 2
|
||||
_, err = capsys.readouterr()
|
||||
|
||||
assert 'cibuildwheel: Unable to detect platform.' in err
|
||||
assert 'cibuildwheel should run on your CI server' in err
|
||||
|
||||
|
||||
def test_unknown_platform_on_ci(monkeypatch, capsys):
|
||||
monkeypatch.setenv('CI', 'true')
|
||||
monkeypatch.setattr(sys, 'platform', 'nonexistent')
|
||||
|
||||
with pytest.raises(SystemExit) as exit:
|
||||
main()
|
||||
assert exit.value.code == 2
|
||||
_, err = capsys.readouterr()
|
||||
|
||||
assert 'cibuildwheel: Unable to detect platform from "sys.platform"' in err
|
||||
|
||||
|
||||
def test_unknown_platform(monkeypatch, capsys):
|
||||
monkeypatch.setenv('CIBW_PLATFORM', 'nonexistent')
|
||||
|
||||
with pytest.raises(SystemExit) as exit:
|
||||
main()
|
||||
_, err = capsys.readouterr()
|
||||
|
||||
assert exit.value.code == 2
|
||||
assert 'cibuildwheel: Unsupported platform: nonexistent' in err
|
||||
|
||||
|
||||
def test_platform_argument(platform, intercepted_build_args, monkeypatch):
|
||||
monkeypatch.setenv('CIBW_PLATFORM', 'nonexistent')
|
||||
monkeypatch.setattr(sys, 'argv', sys.argv + ['--platform', platform])
|
||||
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.kwargs['project_dir'] == MOCK_PROJECT_DIR
|
||||
|
||||
|
||||
def test_platform_environment(platform, intercepted_build_args, monkeypatch):
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.kwargs['project_dir'] == MOCK_PROJECT_DIR
|
||||
Reference in New Issue
Block a user