Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ea7fa7628 | ||
|
|
3f3effe25d | ||
|
|
328a6714e9 | ||
|
|
783dedad22 | ||
|
|
dd3bec5ba9 | ||
|
|
c9c1ea6fec | ||
|
|
2e4a29bf1f | ||
|
|
dadf6a7ac9 | ||
|
|
9880c4fbb7 | ||
|
|
2970faf732 | ||
|
|
a57ef07f72 | ||
|
|
0224d72931 | ||
|
|
98c4a395e9 | ||
|
|
95678f3d5d | ||
|
|
f7cda33d96 | ||
|
|
603f77a0c1 | ||
|
|
a290e00282 | ||
|
|
7ebe8fef55 | ||
|
|
21e6b99884 | ||
|
|
f4e911028c | ||
|
|
3e335044c6 | ||
|
|
407a1c0a6e | ||
|
|
fa605d080b | ||
|
|
7b3bcfae34 | ||
|
|
4b5c743a64 | ||
|
|
83b39bcda2 | ||
|
|
a1983b94d8 | ||
|
|
397393ba91 | ||
|
|
e07d8038c6 | ||
|
|
f84388fe26 | ||
|
|
7e59d8c61c | ||
|
|
95bef46832 | ||
|
|
882ea3426a | ||
|
|
a0cc4f6347 | ||
|
|
d5010b8242 | ||
|
|
25f2e8cb46 | ||
|
|
7eb0de0dd2 | ||
|
|
eff997d12e | ||
|
|
b5175e6159 | ||
|
|
b8dbedcdbe | ||
|
|
8be6d47eec | ||
|
|
b0d99df007 | ||
|
|
f38eb2644f | ||
|
|
ef935187d0 | ||
|
|
6113136a1d | ||
|
|
13a8c549bf | ||
|
|
8d7d41cd83 | ||
|
|
6554c3cf86 | ||
|
|
3836bb2919 | ||
|
|
2345cf20da | ||
|
|
3a3d1a4d2b | ||
|
|
49bbbec7f0 | ||
|
|
2b4bd815ed | ||
|
|
e90e94a18e | ||
|
|
bff3e8e37a | ||
|
|
1701bc6bde | ||
|
|
a2eff759e8 | ||
|
|
1c88697e7c | ||
|
|
97628673d2 | ||
|
|
3aa97f09e2 | ||
|
|
5a7dc042b6 | ||
|
|
a4602c32dd | ||
|
|
3018e7a1f8 | ||
|
|
ea6ada0728 | ||
|
|
90634ed872 | ||
|
|
e78a731e2d | ||
|
|
8986b2d468 | ||
|
|
626a045770 | ||
|
|
8b465bc94a | ||
|
|
317c0f4baf | ||
|
|
b52edf89ef | ||
|
|
27b79f8616 | ||
|
|
93df99e43b | ||
|
|
0bdafc5649 | ||
|
|
1eb5fa2311 | ||
|
|
5367e687f9 | ||
|
|
abdd1f71d1 | ||
|
|
ed50cfd20a | ||
|
|
3fa9935d5b | ||
|
|
13554d0490 | ||
|
|
4b6df458fd | ||
|
|
de4bf6dafe | ||
|
|
ffaad9134b | ||
|
|
33da3d65c7 | ||
|
|
36a2f00e68 | ||
|
|
98dbbaecf4 | ||
|
|
7316d69139 | ||
|
|
dc5dbea4c9 | ||
|
|
580b065bb0 | ||
|
|
7090bfa4f1 | ||
|
|
2db9d9a089 | ||
|
|
94824f95af | ||
|
|
4e31c65d09 | ||
|
|
bbb77aa488 | ||
|
|
1e08d29cfc | ||
|
|
40ff745321 | ||
|
|
8f9a68fe94 | ||
|
|
840da16d39 | ||
|
|
e49644edf2 | ||
|
|
c4f54c6aa3 | ||
|
|
7efec9a0fc | ||
|
|
b4ed192e88 | ||
|
|
b55e98fb2c | ||
|
|
cc956ea6cd | ||
|
|
4fb4e44c23 | ||
|
|
8603deac93 | ||
|
|
b797430d21 | ||
|
|
7ab2d599b9 | ||
|
|
c22d50427c | ||
|
|
ce85c8730d | ||
|
|
34f8427aab | ||
|
|
bb12d3dc31 | ||
|
|
77fb97caf3 | ||
|
|
23870b6780 | ||
|
|
4caee57c21 | ||
|
|
7e566e46ee | ||
|
|
02a52791c9 | ||
|
|
9ae2ae4ac6 | ||
|
|
42489436a2 | ||
|
|
dd2c27ab01 | ||
|
|
fd1cd2442c | ||
|
|
ad1b59febf | ||
|
|
2f1ec582e9 | ||
|
|
79cf7e47ae | ||
|
|
7c6e69ea28 | ||
|
|
dbc2f06b9a | ||
|
|
04531d9058 | ||
|
|
e06b7696a0 | ||
|
|
54fda3f965 | ||
|
|
219266258e | ||
|
|
298df76b8b | ||
|
|
7e583dcab7 | ||
|
|
9768897764 | ||
|
|
baba7706e0 | ||
|
|
d613a8c1a6 | ||
|
|
01a7c2d6df | ||
|
|
6461074ec9 | ||
|
|
d9e6bbfa4f | ||
|
|
4e9316036c | ||
|
|
1880b6d14d | ||
|
|
4c7453df6c | ||
|
|
5e16e86355 | ||
|
|
8a450b7655 | ||
|
|
5187633e26 | ||
|
|
fc20273f8b | ||
|
|
f55a5d44a3 | ||
|
|
fd69aed369 | ||
|
|
ddc4fb2ab8 | ||
|
|
3c20f70c5d | ||
|
|
b7fc45afd9 | ||
|
|
c9e4a66915 | ||
|
|
fe3abfba92 | ||
|
|
bf87df5d62 | ||
|
|
4c076b8d41 | ||
|
|
50bd6f41da | ||
|
|
f2c2a06c19 | ||
|
|
103381774c | ||
|
|
ada46aaf43 | ||
|
|
17a019b450 | ||
|
|
bf65679cc3 | ||
|
|
a953c5f4ee | ||
|
|
d8a245dcf6 | ||
|
|
e32d0af532 | ||
|
|
6177408076 | ||
|
|
baa0897f1d | ||
|
|
b9607ffae7 | ||
|
|
076987cb7e | ||
|
|
bad72cbb15 | ||
|
|
8ead985cfd | ||
|
|
1b9b6e9000 | ||
|
|
042efd6090 | ||
|
|
b159617a3e | ||
|
|
c0967ad622 | ||
|
|
1cae983592 | ||
|
|
06696c46a0 |
@@ -13,6 +13,7 @@ jobs:
|
||||
- run:
|
||||
name: Test.
|
||||
command: flake8 .
|
||||
|
||||
osx-python3.6:
|
||||
macos:
|
||||
xcode: "9.4.1"
|
||||
@@ -27,6 +28,7 @@ jobs:
|
||||
- run:
|
||||
name: Test.
|
||||
command: venv/bin/python ./bin/run_tests.py
|
||||
no_output_timeout: 30m
|
||||
|
||||
osx-python3.7:
|
||||
macos:
|
||||
@@ -42,6 +44,7 @@ jobs:
|
||||
- run:
|
||||
name: Test.
|
||||
command: venv/bin/python ./bin/run_tests.py
|
||||
no_output_timeout: 30m
|
||||
|
||||
linux-python3.6:
|
||||
docker:
|
||||
@@ -58,6 +61,7 @@ jobs:
|
||||
- run:
|
||||
name: Test.
|
||||
command: venv/bin/python ./bin/run_tests.py
|
||||
no_output_timeout: 30m
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
name: Test
|
||||
|
||||
on:
|
||||
push: {}
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
# skip branch builds on joerick/cibuildwheel, but always build `master` and pull requests
|
||||
if: github.repository != 'joerick/cibuildwheel' || github.ref == 'refs/heads/master' || github.event_name == 'pull_request'
|
||||
|
||||
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
|
||||
+3
-1
@@ -89,8 +89,10 @@ venv3/
|
||||
venv2/
|
||||
ENV/
|
||||
env/
|
||||
env3/
|
||||
env2/
|
||||
env2?/
|
||||
env3/
|
||||
env3?/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
|
||||
+7
-7
@@ -1,5 +1,9 @@
|
||||
language: minimal
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- name: Linux | x86_64 + i686 | Python 3.5
|
||||
@@ -34,7 +38,8 @@ jobs:
|
||||
env:
|
||||
- PYTHON=C:\\Python35\\python
|
||||
|
||||
- name: Linux | s390x | Python 3.5
|
||||
- &linux_s390x_35
|
||||
name: Linux | s390x | Python 3.5
|
||||
language: python
|
||||
python: 3.5
|
||||
services: docker
|
||||
@@ -43,12 +48,7 @@ jobs:
|
||||
|
||||
allow_failures:
|
||||
# must repeat the s390x job above exactly to match
|
||||
- name: Linux | s390x | Python 3.5
|
||||
language: python
|
||||
python: 3.5
|
||||
services: docker
|
||||
arch: s390x
|
||||
env: PYTHON=python
|
||||
- *linux_s390x_35
|
||||
|
||||
install: $PYTHON -m pip install -r requirements-dev.txt
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
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 | Azure Pipelines |
|
||||
| macOS | Azure Pipelines | CircleCI | Travis CI¹ / CircleCI | Azure Pipelines |
|
||||
| Windows | TravisCI | Azure Pipelines | AppVeyor | Azure Pipelines |
|
||||
| | 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.
|
||||
|
||||
|
||||
@@ -22,3 +22,7 @@ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
Get-pip and Pip (bundled in cibuildwheel/resources) are licensed under the MIT
|
||||
license. See https://github.com/pypa/get-pip/blob/master/LICENSE.txt
|
||||
|
||||
@@ -9,7 +9,7 @@ cibuildwheel
|
||||
|
||||
Python wheels are great. Building them across **Mac, Linux, Windows**, on **multiple versions of Python**, is not.
|
||||
|
||||
`cibuildwheel` is here to help. `cibuildwheel` runs on your CI server - currently it supports Azure Pipelines, Travis CI, AppVeyor, and CircleCI - and it builds and tests your wheels across all of your platforms.
|
||||
`cibuildwheel` is here to help. `cibuildwheel` runs on your CI server - currently it supports Azure Pipelines, Travis CI, AppVeyor, GitHub Actions and CircleCI - and it builds and tests your wheels across all of your platforms.
|
||||
|
||||
|
||||
What does it do?
|
||||
@@ -35,13 +35,14 @@ What does it do?
|
||||
Usage
|
||||
-----
|
||||
|
||||
`cibuildwheel` currently works on **Travis CI**, **Azure Pipelines** and **AppVeyor** to build wheels for all three supported platforms (Linux, macOS, Windows). On **CircleCI** Linux and macOS wheels can be built.
|
||||
`cibuildwheel` currently works on **Travis CI**, **Azure Pipelines**, **AppVeyor** and **GitHub Actions** to build wheels for all three supported platforms (Linux, macOS, Windows). On **CircleCI** Linux and macOS wheels can be built.
|
||||
|
||||
| | Linux | macOS | Windows |
|
||||
|-----------------|-------|-------|---------|
|
||||
| Azure Pipelines | ✅ | ✅ | ✅ |
|
||||
| Travis CI | ✅ | ✅ | ✅ |
|
||||
| AppVeyor | ✅ | ✅ | ✅ |
|
||||
| GitHub Actions | ✅ | ✅ | ✅ |
|
||||
| CircleCI | ✅ | ✅ | |
|
||||
|
||||
`cibuildwheel` is not intended to run on your development machine. Because it uses system Python from Python.org it will try to install packages globally - not what you expect from a build tool! Instead, isolated CI services like Travis CI, CircleCI, Azure Pipelines and AppVeyor are ideal.
|
||||
@@ -76,7 +77,7 @@ env:
|
||||
# Note: TWINE_PASSWORD is set to a PyPI API token in Travis settings
|
||||
|
||||
install:
|
||||
- python3 -m pip install cibuildwheel==1.2.0
|
||||
- python3 -m pip install cibuildwheel==1.4.1
|
||||
|
||||
script:
|
||||
# build the wheels, put them into './wheelhouse'
|
||||
@@ -105,6 +106,7 @@ Options
|
||||
| | [`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 |
|
||||
@@ -124,6 +126,8 @@ Here are some repos that use cibuildwheel.
|
||||
- [apriltags2-ethz](https://github.com/safijari/apriltags2_ethz)
|
||||
- [TgCrypto](https://github.com/pyrogram/tgcrypto)
|
||||
- [Twisted](https://github.com/twisted/twisted)
|
||||
- [gmic-py](https://github.com/dtschump/gmic-py)
|
||||
- [creme](https://github.com/creme-ml/creme)
|
||||
|
||||
> Add your repo here! Send a PR.
|
||||
|
||||
@@ -139,6 +143,54 @@ This is similar to static linking, so it might have some licence implications. C
|
||||
Changelog
|
||||
=========
|
||||
|
||||
### 1.4.1
|
||||
|
||||
_4 May 2020_
|
||||
|
||||
- 🐛 Fix a bug causing programs running inside the i686 manylinux images to
|
||||
think they were running x86_64 and target the wrong architecture. (#336,
|
||||
#338)
|
||||
|
||||
### 1.4.0
|
||||
|
||||
_2 May 2020_
|
||||
|
||||
- ✨ Deterministic builds. cibuildwheel now locks the versions of the tools it
|
||||
uses. This means that pinning your version of cibuildwheel pins the versions
|
||||
of pip, setuptools, manylinux etc. that are used under the hood. This should
|
||||
make things more reliable. But note that we don't control the entire build
|
||||
environment on macOS and Windows, where the version of Xcode and Visual
|
||||
Studio can still effect things.
|
||||
|
||||
This can be controlled using the [CIBW_DEPENDENCY_VERSIONS](https://cibuildwheel.readthedocs.io/en/stable/options/#dependency-versions)
|
||||
and [manylinux image](https://cibuildwheel.readthedocs.io/en/stable/options/#manylinux-image)
|
||||
options - if you always want to use the latest toolchain, you can still do
|
||||
that, or you can specify your own pip constraints file and manylinux image.
|
||||
(#256)
|
||||
- ✨ Added `package_dir` command line option, meaning we now support building
|
||||
a package that lives in a subdirectory and pulls in files from the wider
|
||||
project. See [the `package_dir` option help](https://cibuildwheel.readthedocs.io/en/stable/options/#command-line-options)
|
||||
for more information.
|
||||
|
||||
Note that this change makes the working directory (where you call
|
||||
cibuildwheel from) relevant on Linux, as it's considered the 'project' and
|
||||
will be copied into the Docker container. If your builds are slower on this
|
||||
version, that's likely the reason. `cd` to your project and then call
|
||||
`cibuildwheel` from there. (#319, #295)
|
||||
- 🛠 On macOS, we make `MACOSX_DEPLOYMENT_TARGET` default to `10.9` if it's
|
||||
not set. This should make things more consistent between Python versions.
|
||||
- 🛠 Dependency updates - CPython 3.7.7, CPython 2.7.18, Pypy 7.3.1.
|
||||
|
||||
### 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_
|
||||
|
||||
+17
-1
@@ -1,4 +1,7 @@
|
||||
environment:
|
||||
# workaround an appveyor bug on macos
|
||||
# see https://github.com/appveyor/ci/issues/3376#issuecomment-611314299
|
||||
APPVEYOR_CONSOLE_DISABLE_PTY: true
|
||||
matrix:
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Ubuntu
|
||||
APPVEYOR_JOB_NAME: "python37-x64-ubuntu"
|
||||
@@ -13,9 +16,22 @@ build: off
|
||||
|
||||
init:
|
||||
- cmd: set PATH=C:\Python37;C:\Python37\Scripts;%PATH%
|
||||
- ps: |
|
||||
$BRANCH = if ($env:APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH) { $env:APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH } else { $env:APPVEYOR_REPO_BRANCH }
|
||||
if (-not ($BRANCH -eq 'master' -or $BRANCH.ToLower().StartsWith('appveyor-'))) {
|
||||
$env:PYTEST_ADDOPTS='-k "unit_test or _basic" --suppress-no-test-exit-code'
|
||||
}
|
||||
|
||||
install: python -m pip install -r requirements-dev.txt
|
||||
install: python -m pip install -r requirements-dev.txt pytest-custom-exit-code
|
||||
|
||||
# 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
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
skip_commits:
|
||||
files:
|
||||
- docs/*
|
||||
|
||||
Executable
+75
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import time
|
||||
from pathlib import Path
|
||||
from subprocess import run
|
||||
|
||||
import click
|
||||
import textwrap
|
||||
|
||||
|
||||
def shell(cmd, **kwargs):
|
||||
return run([cmd], shell=True, **kwargs)
|
||||
|
||||
|
||||
def git_repo_has_changes():
|
||||
unstaged_changes = shell('git diff-index --quiet HEAD --').returncode != 0
|
||||
staged_changes = shell('git diff-index --quiet --cached HEAD --').returncode != 0
|
||||
return unstaged_changes or staged_changes
|
||||
|
||||
|
||||
@click.command()
|
||||
def main():
|
||||
project_root = Path(__file__).parent / '..'
|
||||
os.chdir(project_root)
|
||||
|
||||
if git_repo_has_changes():
|
||||
print('Your git repo has uncommitted changes. Commit or stash before continuing.')
|
||||
exit(1)
|
||||
|
||||
previous_branch = shell('git rev-parse --abbrev-ref HEAD',
|
||||
check=True,
|
||||
capture_output=True,
|
||||
encoding='utf8').stdout.strip()
|
||||
|
||||
shell('git fetch origin', check=True)
|
||||
|
||||
timestamp = time.strftime('%Y-%m-%dT%H-%M-%S', time.gmtime())
|
||||
branch_name = f'update-constraints-{timestamp}'
|
||||
|
||||
shell(f'git checkout -b {branch_name} origin/master', check=True)
|
||||
|
||||
try:
|
||||
shell('bin/update_dependencies.py', check=True)
|
||||
|
||||
if not git_repo_has_changes():
|
||||
print('Done: no constraint updates required.')
|
||||
return
|
||||
|
||||
shell('git commit -a -m "Update dependencies"', check=True)
|
||||
run(
|
||||
[
|
||||
'gh', 'pr', 'create',
|
||||
'--repo', 'joerick/cibuildwheel',
|
||||
'--base', 'master',
|
||||
'--title', 'Update dependencies',
|
||||
'--body', textwrap.dedent(f'''
|
||||
Update the versions of our dependencies.
|
||||
|
||||
PR generated by `{os.path.basename(__file__)}`.
|
||||
''')
|
||||
],
|
||||
check=True
|
||||
)
|
||||
|
||||
print('Done.')
|
||||
finally:
|
||||
# remove any local changes
|
||||
shell('git checkout -- .')
|
||||
shell(f'git checkout {previous_branch}', check=True)
|
||||
shell(f'git branch -D --force {branch_name}', check=True)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main.main(standalone_mode=True)
|
||||
Executable
+99
@@ -0,0 +1,99 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import configparser
|
||||
import os
|
||||
import subprocess
|
||||
from collections import namedtuple
|
||||
|
||||
import requests
|
||||
|
||||
os.chdir(os.path.dirname(__file__))
|
||||
os.chdir('..')
|
||||
|
||||
# CUSTOM_COMPILE_COMMAND is a pip-compile option that tells users how to
|
||||
# regenerate the constraints files
|
||||
os.environ['CUSTOM_COMPILE_COMMAND'] = "bin/update_constraints.py"
|
||||
subprocess.check_call([
|
||||
'pip-compile',
|
||||
'--allow-unsafe',
|
||||
'--upgrade',
|
||||
'cibuildwheel/resources/constraints.in',
|
||||
])
|
||||
for python_version in ['27', '35', '36']:
|
||||
subprocess.check_call([
|
||||
f'./env{python_version}/bin/pip-compile',
|
||||
'--allow-unsafe',
|
||||
'--upgrade',
|
||||
'cibuildwheel/resources/constraints.in',
|
||||
'--output-file', f'cibuildwheel/resources/constraints-python{python_version}.txt'
|
||||
])
|
||||
|
||||
Image = namedtuple('Image', [
|
||||
'manylinux_version',
|
||||
'platform',
|
||||
'image_name',
|
||||
])
|
||||
|
||||
images = [
|
||||
Image('manylinux1', 'x86_64', 'quay.io/pypa/manylinux1_x86_64'),
|
||||
Image('manylinux1', 'i686', 'quay.io/pypa/manylinux1_i686'),
|
||||
|
||||
Image('manylinux2010', 'x86_64', 'quay.io/pypa/manylinux2010_x86_64'),
|
||||
Image('manylinux2010', 'i686', 'quay.io/pypa/manylinux2010_i686'),
|
||||
Image('manylinux2010', 'pypy_x86_64', 'pypywheels/manylinux2010-pypy_x86_64'),
|
||||
|
||||
Image('manylinux2014', 'x86_64', 'quay.io/pypa/manylinux2014_x86_64'),
|
||||
Image('manylinux2014', 'i686', 'quay.io/pypa/manylinux2014_i686'),
|
||||
Image('manylinux2014', 'aarch64', 'quay.io/pypa/manylinux2014_aarch64'),
|
||||
Image('manylinux2014', 'ppc64le', 'quay.io/pypa/manylinux2014_ppc64le'),
|
||||
Image('manylinux2014', 's390x', 'quay.io/pypa/manylinux2014_s390x'),
|
||||
]
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
|
||||
for image in images:
|
||||
# get the tag name whose digest matches 'latest'
|
||||
if image.image_name.startswith('quay.io/'):
|
||||
_, _, repository_name = image.image_name.partition('/')
|
||||
response = requests.get(
|
||||
f'https://quay.io/api/v1/repository/{repository_name}?includeTags=true'
|
||||
)
|
||||
response.raise_for_status()
|
||||
repo_info = response.json()
|
||||
tags_dict = repo_info['tags']
|
||||
|
||||
latest_tag = tags_dict.pop('latest')
|
||||
# find the tag whose manifest matches 'latest'
|
||||
tag_name = next(
|
||||
name
|
||||
for (name, info) in tags_dict.items()
|
||||
if info['manifest_digest'] == latest_tag['manifest_digest']
|
||||
)
|
||||
else:
|
||||
response = requests.get(
|
||||
f'https://hub.docker.com/v2/repositories/{image.image_name}/tags'
|
||||
)
|
||||
response.raise_for_status()
|
||||
tags = response.json()['results']
|
||||
|
||||
latest_tag = next(
|
||||
tag for tag in tags if tag['name'] == 'latest'
|
||||
)
|
||||
# i don't know what it would mean to have multiple images per tag
|
||||
assert len(latest_tag['images']) == 1
|
||||
digest = latest_tag['images'][0]['digest']
|
||||
|
||||
pinned_tag = next(
|
||||
tag
|
||||
for tag in tags
|
||||
if tag != latest_tag and tag['images'][0]['digest'] == digest
|
||||
)
|
||||
tag_name = pinned_tag['name']
|
||||
|
||||
if not config.has_section(image.platform):
|
||||
config[image.platform] = {}
|
||||
|
||||
config[image.platform][image.manylinux_version] = f'{image.image_name}:{tag_name}'
|
||||
|
||||
with open('cibuildwheel/resources/pinned_docker_images.cfg', 'w') as f:
|
||||
config.write(f)
|
||||
@@ -1 +1 @@
|
||||
__version__ = '1.2.0'
|
||||
__version__ = '1.4.1'
|
||||
|
||||
+82
-59
@@ -3,6 +3,7 @@ import os
|
||||
import sys
|
||||
import textwrap
|
||||
import traceback
|
||||
from configparser import ConfigParser
|
||||
|
||||
import cibuildwheel
|
||||
import cibuildwheel.linux
|
||||
@@ -14,7 +15,9 @@ from cibuildwheel.environment import (
|
||||
)
|
||||
from cibuildwheel.util import (
|
||||
BuildSelector,
|
||||
Unbuffered
|
||||
DependencyConstraints,
|
||||
Unbuffered,
|
||||
BuildOptions
|
||||
)
|
||||
|
||||
|
||||
@@ -45,26 +48,34 @@ def strtobool(val):
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Build wheels for all the platforms.',
|
||||
epilog=('Most options are supplied via environment variables. '
|
||||
'See https://github.com/joerick/cibuildwheel#options for info.'))
|
||||
epilog='''
|
||||
Most options are supplied via environment variables.
|
||||
See https://github.com/joerick/cibuildwheel#options for info.
|
||||
''')
|
||||
|
||||
parser.add_argument('--platform',
|
||||
choices=['auto', 'linux', 'macos', 'windows'],
|
||||
default=os.environ.get('CIBW_PLATFORM', 'auto'),
|
||||
help=('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. Default: auto.'))
|
||||
help='''
|
||||
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. Default: auto.
|
||||
''')
|
||||
parser.add_argument('--output-dir',
|
||||
default=os.environ.get('CIBW_OUTPUT_DIR', 'wheelhouse'),
|
||||
help='Destination folder for the wheels.')
|
||||
parser.add_argument('project_dir',
|
||||
parser.add_argument('package_dir',
|
||||
default='.',
|
||||
nargs='?',
|
||||
help=('Path to the project that you want wheels for. Default: the current '
|
||||
'directory.'))
|
||||
help='''
|
||||
Path to the package that you want wheels for. Must be a subdirectory of
|
||||
the working directory. When set, the working directory is still
|
||||
considered the 'project' and is copied into the Docker container on
|
||||
Linux. Default: the working directory.
|
||||
''')
|
||||
|
||||
parser.add_argument('--print-build-identifiers',
|
||||
action='store_true',
|
||||
@@ -77,12 +88,12 @@ def main():
|
||||
if args.platform != 'auto':
|
||||
platform = args.platform
|
||||
else:
|
||||
ci = strtobool(os.environ.get('CI', 'false')) or 'BITRISE_BUILD_NUMBER' in os.environ or 'AZURE_HTTP_USER_AGENT' in os.environ
|
||||
ci = strtobool(os.environ.get('CI', 'false')) or 'BITRISE_BUILD_NUMBER' in os.environ or 'AZURE_HTTP_USER_AGENT' in os.environ or 'GITHUB_WORKFLOW' in os.environ
|
||||
if not ci:
|
||||
print('cibuildwheel: Unable to detect platform. cibuildwheel should run on your CI server, '
|
||||
'Travis CI, AppVeyor, Azure Pipelines and CircleCI are supported. You can run on your '
|
||||
'development machine or other CI providers using the --platform argument. Check --help '
|
||||
'output for more information.',
|
||||
'Travis CI, AppVeyor, Azure Pipelines, GitHub Actions and CircleCI are supported. You '
|
||||
'can run on your development machine or other CI providers using the --platform argument. '
|
||||
'Check --help output for more information.',
|
||||
file=sys.stderr)
|
||||
exit(2)
|
||||
if sys.platform.startswith('linux'):
|
||||
@@ -101,7 +112,7 @@ def main():
|
||||
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
|
||||
package_dir = args.package_dir
|
||||
before_build = get_option_from_environment('CIBW_BEFORE_BUILD', platform=platform)
|
||||
build_verbosity = get_option_from_environment('CIBW_BUILD_VERBOSITY', platform=platform, default='')
|
||||
build_config, skip_config = os.environ.get('CIBW_BUILD', '*'), os.environ.get('CIBW_SKIP', '')
|
||||
@@ -113,6 +124,15 @@ def main():
|
||||
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='')
|
||||
|
||||
dependency_versions = get_option_from_environment('CIBW_DEPENDENCY_VERSIONS', platform=platform, default='pinned')
|
||||
if dependency_versions == 'pinned':
|
||||
dependency_constraints = DependencyConstraints.with_defaults()
|
||||
elif dependency_versions == 'latest':
|
||||
dependency_constraints = None
|
||||
else:
|
||||
dependency_constraints = DependencyConstraints(dependency_versions)
|
||||
|
||||
if test_extras:
|
||||
test_extras = '[{0}]'.format(test_extras)
|
||||
@@ -135,16 +155,49 @@ def main():
|
||||
# This needs to be passed on to the docker container in linux.py
|
||||
os.environ['CIBUILDWHEEL'] = '1'
|
||||
|
||||
if not os.path.exists(os.path.join(project_dir, 'setup.py')):
|
||||
print('cibuildwheel: Could not find setup.py at root of project', file=sys.stderr)
|
||||
if not os.path.exists(os.path.join(package_dir, 'setup.py')):
|
||||
print('cibuildwheel: Could not find setup.py at root of package', file=sys.stderr)
|
||||
exit(2)
|
||||
|
||||
if args.print_build_identifiers:
|
||||
print_build_identifiers(platform, build_selector)
|
||||
exit(0)
|
||||
|
||||
build_options = dict(
|
||||
project_dir=project_dir,
|
||||
if platform == 'linux':
|
||||
pinned_docker_images_file = os.path.join(
|
||||
os.path.dirname(__file__), 'resources', 'pinned_docker_images.cfg'
|
||||
)
|
||||
all_pinned_docker_images = ConfigParser()
|
||||
all_pinned_docker_images.read(pinned_docker_images_file)
|
||||
# all_pinned_docker_images looks like a dict of dicts, e.g.
|
||||
# { 'x86_64': {'manylinux1': '...', 'manylinux2010': '...', 'manylinux2014': '...'},
|
||||
# 'i686': {'manylinux1': '...', 'manylinux2010': '...', 'manylinux2014': '...'},
|
||||
# 'pypy_x86_64': {'manylinux2010': '...' }
|
||||
# ... }
|
||||
|
||||
manylinux_images = {}
|
||||
|
||||
for build_platform in ['x86_64', 'i686', 'pypy_x86_64', 'aarch64', 'ppc64le', 's390x']:
|
||||
pinned_images = all_pinned_docker_images[build_platform]
|
||||
|
||||
config_name = 'CIBW_MANYLINUX_{}_IMAGE'.format(build_platform.upper())
|
||||
config_value = os.environ.get(config_name)
|
||||
|
||||
if config_value is None:
|
||||
# default to manylinux2010 if it's available, otherwise manylinux2014
|
||||
image = pinned_images.get('manylinux2010') or pinned_images.get('manylinux2014')
|
||||
elif config_value in pinned_images:
|
||||
image = pinned_images[config_value]
|
||||
else:
|
||||
image = config_value
|
||||
|
||||
manylinux_images[build_platform] = image
|
||||
|
||||
else:
|
||||
manylinux_images = None
|
||||
|
||||
build_options = BuildOptions(
|
||||
package_dir=package_dir,
|
||||
output_dir=output_dir,
|
||||
test_command=test_command,
|
||||
test_requires=test_requires,
|
||||
@@ -154,41 +207,11 @@ def main():
|
||||
build_selector=build_selector,
|
||||
repair_command=repair_command,
|
||||
environment=environment,
|
||||
before_test=before_test,
|
||||
dependency_constraints=dependency_constraints,
|
||||
manylinux_images=manylinux_images,
|
||||
)
|
||||
|
||||
if platform == 'linux':
|
||||
manylinux_x86_64_image = os.environ.get('CIBW_MANYLINUX_X86_64_IMAGE', 'manylinux2010')
|
||||
manylinux_i686_image = os.environ.get('CIBW_MANYLINUX_I686_IMAGE', 'manylinux2010')
|
||||
manylinux_pypy_x86_64_image = os.environ.get('CIBW_MANYLINUX_PYPY_X86_64_IMAGE', 'manylinux2010')
|
||||
manylinux_aarch64_image = os.environ.get('CIBW_MANYLINUX_AARCH64_IMAGE', 'manylinux2014')
|
||||
manylinux_ppc64le_image = os.environ.get('CIBW_MANYLINUX_PPC64LE_IMAGE', 'manylinux2014')
|
||||
manylinux_s390x_image = os.environ.get('CIBW_MANYLINUX_S390X_IMAGE', 'manylinux2014')
|
||||
|
||||
default_manylinux_images_x86_64 = {'manylinux1': 'quay.io/pypa/manylinux1_x86_64',
|
||||
'manylinux2010': 'quay.io/pypa/manylinux2010_x86_64',
|
||||
'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(
|
||||
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)
|
||||
|
||||
@@ -198,11 +221,11 @@ def main():
|
||||
os.makedirs(output_dir)
|
||||
|
||||
if platform == 'linux':
|
||||
cibuildwheel.linux.build(**build_options)
|
||||
cibuildwheel.linux.build(build_options)
|
||||
elif platform == 'windows':
|
||||
cibuildwheel.windows.build(**build_options)
|
||||
cibuildwheel.windows.build(build_options)
|
||||
elif platform == 'macos':
|
||||
cibuildwheel.macos.build(**build_options)
|
||||
cibuildwheel.macos.build(build_options)
|
||||
else:
|
||||
print('cibuildwheel: Unsupported platform: {}'.format(platform), file=sys.stderr)
|
||||
exit(2)
|
||||
@@ -247,7 +270,7 @@ def print_preamble(platform, build_options):
|
||||
|
||||
print('Build options:')
|
||||
print(' platform: %r' % platform)
|
||||
for option, value in sorted(build_options.items()):
|
||||
for option, value in sorted(build_options._asdict().items()):
|
||||
print(' %s: %r' % (option, value))
|
||||
|
||||
warnings = detect_warnings(platform, build_options)
|
||||
@@ -278,7 +301,7 @@ def detect_warnings(platform, build_options):
|
||||
|
||||
# warn about deprecated {python} and {pip}
|
||||
for option_name in ['test_command', 'before_build']:
|
||||
option_value = build_options.get(option_name)
|
||||
option_value = getattr(build_options, option_name)
|
||||
|
||||
if option_value:
|
||||
if '{python}' in option_value or '{pip}' in option_value:
|
||||
|
||||
+199
-148
@@ -10,9 +10,17 @@ from collections import namedtuple
|
||||
from .util import (
|
||||
get_build_verbosity_extra_flags,
|
||||
prepare_command,
|
||||
BuildOptions
|
||||
)
|
||||
|
||||
|
||||
def call(args, input=None, universal_newlines=False):
|
||||
print('+ ' + ' '.join(shlex.quote(a) for a in args))
|
||||
subprocess.run(
|
||||
args, input=input, universal_newlines=universal_newlines, check=True
|
||||
)
|
||||
|
||||
|
||||
def matches_platform(identifier):
|
||||
pm = platform.machine()
|
||||
if pm == "x86_64":
|
||||
@@ -35,40 +43,40 @@ def matches_platform(identifier):
|
||||
|
||||
|
||||
def get_python_configurations(build_selector):
|
||||
PythonConfiguration = namedtuple('PythonConfiguration', ['identifier', 'path'])
|
||||
PythonConfiguration = namedtuple('PythonConfiguration', ['version', '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'),
|
||||
PythonConfiguration(version='2.7', identifier='cp27-manylinux_x86_64', path='/opt/python/cp27-cp27m'),
|
||||
PythonConfiguration(version='2.7', identifier='cp27-manylinux_x86_64', path='/opt/python/cp27-cp27mu'),
|
||||
PythonConfiguration(version='3.5', identifier='cp35-manylinux_x86_64', path='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(version='3.6', identifier='cp36-manylinux_x86_64', path='/opt/python/cp36-cp36m'),
|
||||
PythonConfiguration(version='3.7', identifier='cp37-manylinux_x86_64', path='/opt/python/cp37-cp37m'),
|
||||
PythonConfiguration(version='3.8', identifier='cp38-manylinux_x86_64', path='/opt/python/cp38-cp38'),
|
||||
PythonConfiguration(version='2.7', identifier='cp27-manylinux_i686', path='/opt/python/cp27-cp27m'),
|
||||
PythonConfiguration(version='2.7', identifier='cp27-manylinux_i686', path='/opt/python/cp27-cp27mu'),
|
||||
PythonConfiguration(version='3.5', identifier='cp35-manylinux_i686', path='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(version='3.6', identifier='cp36-manylinux_i686', path='/opt/python/cp36-cp36m'),
|
||||
PythonConfiguration(version='3.7', identifier='cp37-manylinux_i686', path='/opt/python/cp37-cp37m'),
|
||||
PythonConfiguration(version='3.8', identifier='cp38-manylinux_i686', path='/opt/python/cp38-cp38'),
|
||||
PythonConfiguration(version='2.7', identifier='pp27-manylinux_x86_64', path='/opt/python/pp27-pypy_73'),
|
||||
PythonConfiguration(version='3.6', identifier='pp36-manylinux_x86_64', path='/opt/python/pp36-pypy36_pp73'),
|
||||
PythonConfiguration(version='3.5', identifier='cp35-manylinux_aarch64', path='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(version='3.6', identifier='cp36-manylinux_aarch64', path='/opt/python/cp36-cp36m'),
|
||||
PythonConfiguration(version='3.7', identifier='cp37-manylinux_aarch64', path='/opt/python/cp37-cp37m'),
|
||||
PythonConfiguration(version='3.8', identifier='cp38-manylinux_aarch64', path='/opt/python/cp38-cp38'),
|
||||
PythonConfiguration(version='3.5', identifier='cp35-manylinux_ppc64le', path='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(version='3.6', identifier='cp36-manylinux_ppc64le', path='/opt/python/cp36-cp36m'),
|
||||
PythonConfiguration(version='3.7', identifier='cp37-manylinux_ppc64le', path='/opt/python/cp37-cp37m'),
|
||||
PythonConfiguration(version='3.8', identifier='cp38-manylinux_ppc64le', path='/opt/python/cp38-cp38'),
|
||||
PythonConfiguration(version='3.5', identifier='cp35-manylinux_s390x', path='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(version='3.6', identifier='cp36-manylinux_s390x', path='/opt/python/cp36-cp36m'),
|
||||
PythonConfiguration(version='3.7', identifier='cp37-manylinux_s390x', path='/opt/python/cp37-cp37m'),
|
||||
PythonConfiguration(version='3.8', identifier='cp38-manylinux_s390x', path='/opt/python/cp38-cp38'),
|
||||
]
|
||||
# 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, test_requires, test_extras, before_build, build_verbosity, build_selector, repair_command, environment, manylinux_images):
|
||||
def build(options: BuildOptions):
|
||||
try:
|
||||
subprocess.check_call(['docker', '--version'])
|
||||
except Exception:
|
||||
@@ -78,157 +86,200 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef
|
||||
file=sys.stderr)
|
||||
exit(2)
|
||||
|
||||
python_configurations = get_python_configurations(build_selector)
|
||||
python_configurations = get_python_configurations(options.build_selector)
|
||||
platforms = [
|
||||
('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']),
|
||||
('cp', 'manylinux_x86_64', options.manylinux_images['x86_64']),
|
||||
('cp', 'manylinux_i686', options.manylinux_images['i686']),
|
||||
('cp', 'manylinux_aarch64', options.manylinux_images['aarch64']),
|
||||
('cp', 'manylinux_ppc64le', options.manylinux_images['ppc64le']),
|
||||
('cp', 'manylinux_s390x', options.manylinux_images['s390x']),
|
||||
('pp', 'manylinux_x86_64', options.manylinux_images['pypy_x86_64']),
|
||||
]
|
||||
|
||||
if not os.path.realpath(options.package_dir).startswith(os.path.realpath('.')):
|
||||
raise Exception('package_dir must be inside the working directory')
|
||||
|
||||
container_package_dir = os.path.join('/project', os.path.relpath(options.package_dir, '.'))
|
||||
|
||||
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
|
||||
container_name = 'cibuildwheel-{}'.format(uuid.uuid4())
|
||||
|
||||
for PYBIN in {pybin_paths}; do (
|
||||
# Temporary hack/workaround, putting loop body in subshell; fixed in PR #256
|
||||
try:
|
||||
shell_cmd = ['linux32', '/bin/bash'] if platform_tag.endswith("i686") else ['/bin/bash']
|
||||
call(['docker', 'create',
|
||||
'--env', 'CIBUILDWHEEL',
|
||||
'--name', container_name,
|
||||
'-i',
|
||||
'-v', '/:/host', # ignored on CircleCI
|
||||
docker_image,
|
||||
'/bin/bash'])
|
||||
|
||||
export PATH="$PYBIN:$PATH"
|
||||
{environment_exports}
|
||||
call(['docker', 'cp', '.', container_name + ':/project'])
|
||||
|
||||
# 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
|
||||
call(['docker', 'start', container_name])
|
||||
|
||||
if [ ! -z {before_build} ]; then
|
||||
sh -c {before_build}
|
||||
fi
|
||||
for config in platform_configs:
|
||||
if options.dependency_constraints:
|
||||
constraints_file = options.dependency_constraints.get_for_python_version(config.version)
|
||||
|
||||
# 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)
|
||||
# `docker cp` causes 'no space left on device' error when
|
||||
# a container is running and the host filesystem is
|
||||
# mounted. https://github.com/moby/moby/issues/38995
|
||||
# Use `docker exec` instead.
|
||||
with open(constraints_file, 'rb') as f:
|
||||
call(
|
||||
['docker', 'exec', '-i', container_name, 'sh', '-c', 'cat > /constraints.txt'],
|
||||
input=f.read(),
|
||||
)
|
||||
|
||||
# repair the wheel
|
||||
rm -rf /tmp/repaired_wheels
|
||||
mkdir /tmp/repaired_wheels
|
||||
# NOTE: 'built_wheel' here is a bash array of glob matches; "$built_wheel" returns
|
||||
# the first element
|
||||
if [[ "$built_wheel" == *none-any.whl ]] || [ -z {repair_command} ]; then
|
||||
# pure Python wheel or empty repair command
|
||||
mv "$built_wheel" /tmp/repaired_wheels
|
||||
else
|
||||
sh -c {repair_command} repair_command "$built_wheel"
|
||||
fi
|
||||
repaired_wheels=(/tmp/repaired_wheels/*.whl)
|
||||
call(
|
||||
['docker', 'exec', '-i', container_name] + shell_cmd,
|
||||
universal_newlines=True,
|
||||
input='''
|
||||
# give xtrace output an extra level of indent inside docker
|
||||
PS4=' + '
|
||||
|
||||
if [ ! -z {test_command} ]; then
|
||||
# Set up a virtual environment to install and test from, to make sure
|
||||
# there are no dependencies that were pulled in at build time.
|
||||
pip install virtualenv
|
||||
venv_dir=`mktemp -d`/venv
|
||||
python -m virtualenv "$venv_dir"
|
||||
set -o errexit
|
||||
set -o xtrace
|
||||
mkdir -p /output
|
||||
cd /project
|
||||
|
||||
export __CIBW_VIRTUALENV_PATH__=$venv_dir
|
||||
PYBIN="{config_python_bin}"
|
||||
|
||||
# run the tests in a subshell to keep that `activate`
|
||||
# script from polluting the env
|
||||
(
|
||||
source "$venv_dir/bin/activate"
|
||||
export PATH="$PYBIN:$PATH"
|
||||
{environment_exports}
|
||||
|
||||
echo "Running tests using `which python`"
|
||||
|
||||
# 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}
|
||||
# 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
|
||||
|
||||
# Run the tests from a different directory
|
||||
pushd $HOME
|
||||
sh -c {test_command}
|
||||
popd
|
||||
if [ ! -z {before_build} ]; then
|
||||
sh -c {before_build}
|
||||
fi
|
||||
|
||||
# Build the wheel
|
||||
rm -rf /tmp/built_wheel
|
||||
mkdir /tmp/built_wheel
|
||||
pip wheel {package_dir} -w /tmp/built_wheel --no-deps {build_verbosity_flag}
|
||||
built_wheel=(/tmp/built_wheel/*.whl)
|
||||
|
||||
# repair the wheel
|
||||
rm -rf /tmp/repaired_wheels
|
||||
mkdir /tmp/repaired_wheels
|
||||
# NOTE: 'built_wheel' here is a bash array of glob matches; "$built_wheel" returns
|
||||
# the first element
|
||||
if [[ "$built_wheel" == *none-any.whl ]] || [ -z {repair_command} ]; then
|
||||
# pure Python wheel or empty repair command
|
||||
mv "$built_wheel" /tmp/repaired_wheels
|
||||
else
|
||||
sh -c {repair_command} repair_command "$built_wheel"
|
||||
fi
|
||||
repaired_wheels=(/tmp/repaired_wheels/*.whl)
|
||||
|
||||
if [ ! -z {test_command} ]; then
|
||||
# Set up a virtual environment to install and test from, to make sure
|
||||
# there are no dependencies that were pulled in at build time.
|
||||
pip install {dependency_install_flags} virtualenv
|
||||
venv_dir=`mktemp -d`/venv
|
||||
python -m virtualenv --no-download "$venv_dir"
|
||||
export __CIBW_VIRTUALENV_PATH__=$venv_dir
|
||||
|
||||
# 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 "${{repaired_wheels[@]}}" /output
|
||||
for repaired_wheel in "${{repaired_wheels[@]}}"; do
|
||||
chown {uid}:{gid} "/output/$(basename "$repaired_wheel")"
|
||||
done
|
||||
'''.format(
|
||||
config_python_bin=config.path + '/bin',
|
||||
package_dir=container_package_dir,
|
||||
test_requires=' '.join(options.test_requires),
|
||||
test_extras=options.test_extras,
|
||||
test_command=shlex.quote(
|
||||
prepare_command(options.test_command, project='/project', package=container_package_dir) if options.test_command else ''
|
||||
),
|
||||
before_build=shlex.quote(
|
||||
prepare_command(options.before_build, project='/project', package=container_package_dir) if options.before_build else ''
|
||||
),
|
||||
build_verbosity_flag=' '.join(get_build_verbosity_extra_flags(options.build_verbosity)),
|
||||
repair_command=shlex.quote(
|
||||
prepare_command(options.repair_command, wheel='"$1"', dest_dir='/tmp/repaired_wheels') if options.repair_command else ''
|
||||
),
|
||||
environment_exports='\n'.join(options.environment.as_shell_commands()),
|
||||
uid=os.getuid(),
|
||||
gid=os.getgid(),
|
||||
before_test=shlex.quote(
|
||||
prepare_command(options.before_test, project='/project', package=container_package_dir) if options.before_test else ''
|
||||
),
|
||||
dependency_install_flags='-c /constraints.txt' if options.dependency_constraints else '',
|
||||
)
|
||||
# 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 "${{repaired_wheels[@]}}" /output
|
||||
for repaired_wheel in "${{repaired_wheels[@]}}"; do chown {uid}:{gid} "/output/$(basename "$repaired_wheel")"; done
|
||||
) done
|
||||
'''.format(
|
||||
pybin_paths=' '.join(c.path + '/bin' for c in platform_configs),
|
||||
test_requires=' '.join(test_requires),
|
||||
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, 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(),
|
||||
)
|
||||
|
||||
container_name = 'cibuildwheel-{}'.format(uuid.uuid4())
|
||||
try:
|
||||
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)
|
||||
# copy the output back into the host
|
||||
call(['docker', 'cp',
|
||||
container_name + ':/output/.',
|
||||
os.path.abspath(options.output_dir)])
|
||||
except subprocess.CalledProcessError as error:
|
||||
troubleshoot(project_dir, error)
|
||||
troubleshoot(options.package_dir, error)
|
||||
exit(1)
|
||||
finally:
|
||||
# Still gets executed, even when 'exit(1)' gets called
|
||||
subprocess.run(['docker', 'rm', '--force', '-v', container_name], check=True)
|
||||
call(['docker', 'rm', '--force', '-v', container_name])
|
||||
|
||||
|
||||
def troubleshoot(project_dir, error):
|
||||
if (isinstance(error, subprocess.CalledProcessError) and 'start' in error.cmd):
|
||||
def troubleshoot(package_dir, error):
|
||||
if (isinstance(error, subprocess.CalledProcessError) and 'exec' in error.cmd):
|
||||
# the bash script failed
|
||||
print('Checking for common errors...')
|
||||
so_files = []
|
||||
for root, dirs, files in os.walk(project_dir):
|
||||
for root, dirs, files in os.walk(package_dir):
|
||||
for name in files:
|
||||
_, ext = os.path.splitext(name)
|
||||
if ext == '.so':
|
||||
|
||||
+99
-84
@@ -11,6 +11,8 @@ from .util import (
|
||||
download,
|
||||
get_build_verbosity_extra_flags,
|
||||
prepare_command,
|
||||
get_pip_script,
|
||||
BuildOptions
|
||||
)
|
||||
|
||||
|
||||
@@ -27,13 +29,15 @@ def call(args, env=None, cwd=None, shell=False):
|
||||
def get_python_configurations(build_selector):
|
||||
PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'identifier', 'url'])
|
||||
python_configurations = [
|
||||
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'),
|
||||
# CPython
|
||||
PythonConfiguration(version='2.7', identifier='cp27-macosx_x86_64', url='https://www.python.org/ftp/python/2.7.18/python-2.7.18-macosx10.9.pkg'),
|
||||
PythonConfiguration(version='3.5', identifier='cp35-macosx_x86_64', url='https://www.python.org/ftp/python/3.5.4/python-3.5.4-macosx10.6.pkg'),
|
||||
PythonConfiguration(version='3.6', identifier='cp36-macosx_x86_64', url='https://www.python.org/ftp/python/3.6.8/python-3.6.8-macosx10.9.pkg'),
|
||||
PythonConfiguration(version='3.7', identifier='cp37-macosx_x86_64', url='https://www.python.org/ftp/python/3.7.6/python-3.7.6-macosx10.9.pkg'),
|
||||
PythonConfiguration(version='3.7', identifier='cp37-macosx_x86_64', url='https://www.python.org/ftp/python/3.7.7/python-3.7.7-macosx10.9.pkg'),
|
||||
PythonConfiguration(version='3.8', identifier='cp38-macosx_x86_64', url='https://www.python.org/ftp/python/3.8.2/python-3.8.2-macosx10.9.pkg'),
|
||||
PythonConfiguration(version='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'),
|
||||
# PyPy
|
||||
PythonConfiguration(version='2.7', identifier='pp27-macosx_x86_64', url='https://downloads.python.org/pypy/pypy2.7-v7.3.1-osx64.tar.bz2'),
|
||||
PythonConfiguration(version='3.6', identifier='pp36-macosx_x86_64', url='https://downloads.python.org/pypy/pypy3.6-v7.3.1-osx64.tar.bz2'),
|
||||
]
|
||||
|
||||
# skip builds as required
|
||||
@@ -70,7 +74,7 @@ def install_cpython(version, url):
|
||||
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
|
||||
open_ssl_patch_url = 'https://github.com/mayeut/patch-macos-python-openssl/releases/download/v1.0.2u/patch-macos-python-%s-openssl-v1.0.2u.tar.gz' % version
|
||||
download(open_ssl_patch_url, '/tmp/python-patch.tar.gz')
|
||||
call(['sudo', 'tar', '-C', '/Library/Frameworks/Python.framework/Versions/{}/'.format(version), '-xmf', '/tmp/python-patch.tar.gz'])
|
||||
|
||||
@@ -91,12 +95,6 @@ def install_pypy(version, url):
|
||||
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'
|
||||
@@ -105,104 +103,113 @@ def install_pypy(version, url):
|
||||
return installation_bin_path
|
||||
|
||||
|
||||
def build(project_dir, output_dir, test_command, test_requires, test_extras, before_build, build_verbosity, build_selector, repair_command, environment):
|
||||
abs_project_dir = os.path.abspath(project_dir)
|
||||
def setup_python(python_configuration, dependency_constraint_flags, environment):
|
||||
if python_configuration.identifier.startswith('cp'):
|
||||
installation_bin_path = install_cpython(python_configuration.version, python_configuration.url)
|
||||
elif python_configuration.identifier.startswith('pp'):
|
||||
installation_bin_path = install_pypy(python_configuration.version, python_configuration.url)
|
||||
else:
|
||||
raise ValueError("Unknown Python implementation")
|
||||
|
||||
env = os.environ.copy()
|
||||
env['PATH'] = os.pathsep.join([
|
||||
SYMLINKS_DIR,
|
||||
installation_bin_path,
|
||||
env['PATH'],
|
||||
])
|
||||
|
||||
# Fix issue with site.py setting the wrong `sys.prefix`, `sys.exec_prefix`,
|
||||
# `sys.path`, ... for PyPy: https://foss.heptapod.net/pypy/pypy/issues/3175
|
||||
# Also fix an issue with the shebang of installed scripts inside the
|
||||
# testing virtualenv- see https://github.com/theacodes/nox/issues/44 and
|
||||
# https://github.com/pypa/virtualenv/issues/620
|
||||
# Also see https://github.com/python/cpython/pull/9516
|
||||
env.pop('__PYVENV_LAUNCHER__', None)
|
||||
env = environment.as_dictionary(prev_environment=env)
|
||||
|
||||
# check what version we're on
|
||||
call(['which', 'python'], env=env)
|
||||
call(['python', '--version'], env=env)
|
||||
which_python = subprocess.check_output(['which', 'python'], env=env, universal_newlines=True).strip()
|
||||
if which_python != '/tmp/cibw_bin/python':
|
||||
print("cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it.", file=sys.stderr)
|
||||
exit(1)
|
||||
|
||||
# install pip & wheel
|
||||
call(['python', get_pip_script] + dependency_constraint_flags, 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'] + dependency_constraint_flags, env=env)
|
||||
|
||||
# Set MACOSX_DEPLOYMENT_TARGET to 10.9, if the user didn't set it.
|
||||
# CPython 3.5 defaults to 10.6, and pypy defaults to 10.7, causing
|
||||
# inconsistencies if it's left unset.
|
||||
env.setdefault('MACOSX_DEPLOYMENT_TARGET', '10.9')
|
||||
|
||||
if python_configuration.version == '3.5':
|
||||
# Cross-compilation platform override - CPython 3.5 has an
|
||||
# i386/x86_64 version of Python, but we only want a x64_64 build
|
||||
env.setdefault('_PYTHON_HOST_PLATFORM', 'macosx-10.9-x86_64')
|
||||
# https://github.com/python/cpython/blob/a5ed2fe0eedefa1649aa93ee74a0bafc8e628a10/Lib/_osx_support.py#L260
|
||||
env.setdefault('ARCHFLAGS', '-arch x86_64')
|
||||
|
||||
return env
|
||||
|
||||
|
||||
def build(options: BuildOptions):
|
||||
temp_dir = tempfile.mkdtemp(prefix='cibuildwheel')
|
||||
built_wheel_dir = os.path.join(temp_dir, 'built_wheel')
|
||||
repaired_wheel_dir = os.path.join(temp_dir, 'repaired_wheel')
|
||||
|
||||
python_configurations = get_python_configurations(build_selector)
|
||||
|
||||
get_pip_url = 'https://bootstrap.pypa.io/get-pip.py'
|
||||
get_pip_script = '/tmp/get-pip.py'
|
||||
|
||||
# get latest pip once and for all
|
||||
download(get_pip_url, get_pip_script)
|
||||
python_configurations = get_python_configurations(options.build_selector)
|
||||
|
||||
for config in python_configurations:
|
||||
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")
|
||||
dependency_constraint_flags = []
|
||||
if options.dependency_constraints:
|
||||
dependency_constraint_flags = [
|
||||
'-c', options.dependency_constraints.get_for_python_version(config.version)
|
||||
]
|
||||
|
||||
env = os.environ.copy()
|
||||
env['PATH'] = os.pathsep.join([
|
||||
SYMLINKS_DIR,
|
||||
installation_bin_path,
|
||||
env['PATH'],
|
||||
])
|
||||
|
||||
# Fix issue with site.py setting the wrong `sys.prefix`, `sys.exec_prefix`,
|
||||
# `sys.path`, ... for PyPy: https://foss.heptapod.net/pypy/pypy/issues/3175
|
||||
# Also fix an issue with the shebang of installed scripts inside the
|
||||
# testing virtualenv- see https://github.com/theacodes/nox/issues/44 and
|
||||
# https://github.com/pypa/virtualenv/issues/620
|
||||
# Also see https://github.com/python/cpython/pull/9516
|
||||
env.pop('__PYVENV_LAUNCHER__', None)
|
||||
env = environment.as_dictionary(prev_environment=env)
|
||||
|
||||
# check what version we're on
|
||||
call(['which', 'python'], env=env)
|
||||
call(['python', '--version'], env=env)
|
||||
which_python = subprocess.check_output(['which', 'python'], env=env, universal_newlines=True).strip()
|
||||
if which_python != '/tmp/cibw_bin/python':
|
||||
print("cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it.", file=sys.stderr)
|
||||
exit(1)
|
||||
|
||||
# install pip & wheel
|
||||
call(['python', get_pip_script], env=env, cwd="/tmp")
|
||||
assert os.path.exists(os.path.join(installation_bin_path, 'pip'))
|
||||
call(['which', 'pip'], env=env)
|
||||
call(['pip', '--version'], env=env)
|
||||
which_pip = subprocess.check_output(['which', 'pip'], env=env, universal_newlines=True).strip()
|
||||
if which_pip != '/tmp/cibw_bin/pip':
|
||||
print("cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it.", file=sys.stderr)
|
||||
exit(1)
|
||||
call(['pip', 'install', '--upgrade', 'setuptools', 'wheel', 'delocate'], env=env)
|
||||
|
||||
# setup target platform, only required for python 3.5
|
||||
if config.version == '3.5':
|
||||
if '_PYTHON_HOST_PLATFORM' not in env:
|
||||
# cross-compilation platform override
|
||||
env['_PYTHON_HOST_PLATFORM'] = 'macosx-10.9-x86_64'
|
||||
if 'ARCHFLAGS' not in env:
|
||||
# https://github.com/python/cpython/blob/a5ed2fe0eedefa1649aa93ee74a0bafc8e628a10/Lib/_osx_support.py#L260
|
||||
env['ARCHFLAGS'] = '-arch x86_64'
|
||||
if 'MACOSX_DEPLOYMENT_TARGET' not in env:
|
||||
env['MACOSX_DEPLOYMENT_TARGET'] = '10.9'
|
||||
env = setup_python(config, dependency_constraint_flags, options.environment)
|
||||
|
||||
# run the before_build command
|
||||
if before_build:
|
||||
before_build_prepared = prepare_command(before_build, project=abs_project_dir)
|
||||
if options.before_build:
|
||||
before_build_prepared = prepare_command(options.before_build, project='.', package=options.package_dir)
|
||||
call(before_build_prepared, env=env, shell=True)
|
||||
|
||||
# build the wheel
|
||||
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)
|
||||
call(['pip', 'wheel', options.package_dir, '-w', built_wheel_dir, '--no-deps'] + get_build_verbosity_extra_flags(options.build_verbosity), env=env)
|
||||
built_wheel = glob(os.path.join(built_wheel_dir, '*.whl'))[0]
|
||||
|
||||
# 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:
|
||||
if built_wheel.endswith('none-any.whl') or not options.repair_command:
|
||||
# pure Python wheel or empty repair command
|
||||
shutil.move(built_wheel, repaired_wheel_dir)
|
||||
else:
|
||||
repair_command_prepared = prepare_command(repair_command, wheel=built_wheel, dest_dir=repaired_wheel_dir)
|
||||
repair_command_prepared = prepare_command(options.repair_command, wheel=built_wheel, dest_dir=repaired_wheel_dir)
|
||||
call(repair_command_prepared, env=env, shell=True)
|
||||
repaired_wheel = glob(os.path.join(repaired_wheel_dir, '*.whl'))[0]
|
||||
|
||||
if test_command:
|
||||
if options.test_command:
|
||||
# set up a virtual environment to install and test from, to make sure
|
||||
# there are no dependencies that were pulled in at build time.
|
||||
call(['pip', 'install', 'virtualenv'], env=env)
|
||||
call(['pip', 'install', 'virtualenv'] + dependency_constraint_flags, env=env)
|
||||
venv_dir = tempfile.mkdtemp()
|
||||
call(['python', '-m', 'virtualenv', venv_dir], env=env)
|
||||
|
||||
# Use --no-download to ensure determinism by using seed libraries
|
||||
# built into virtualenv
|
||||
call(['python', '-m', 'virtualenv', '--no-download', venv_dir], env=env)
|
||||
|
||||
virtualenv_env = env.copy()
|
||||
virtualenv_env['PATH'] = os.pathsep.join([
|
||||
@@ -214,22 +221,30 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef
|
||||
# check that we are using the Python from the virtual environment
|
||||
call(['which', 'python'], env=virtualenv_env)
|
||||
|
||||
if options.before_test:
|
||||
before_test_prepared = prepare_command(options.before_test, project='.', package=options.package_dir)
|
||||
call(before_test_prepared, env=virtualenv_env, shell=True)
|
||||
|
||||
# install the wheel
|
||||
call(['pip', 'install', repaired_wheel + test_extras], env=virtualenv_env)
|
||||
call(['pip', 'install', repaired_wheel + options.test_extras], env=virtualenv_env)
|
||||
|
||||
# test the wheel
|
||||
if test_requires:
|
||||
call(['pip', 'install'] + test_requires, env=virtualenv_env)
|
||||
if options.test_requires:
|
||||
call(['pip', 'install'] + options.test_requires, env=virtualenv_env)
|
||||
|
||||
# run the tests from $HOME, with an absolute path in the command
|
||||
# (this ensures that Python runs the tests against the installed wheel
|
||||
# and not the repo code)
|
||||
test_command_prepared = prepare_command(test_command, project=abs_project_dir)
|
||||
test_command_prepared = prepare_command(
|
||||
options.test_command,
|
||||
project=os.path.abspath('.'),
|
||||
package=os.path.abspath(options.package_dir)
|
||||
)
|
||||
call(test_command_prepared, cwd=os.environ['HOME'], env=virtualenv_env, shell=True)
|
||||
|
||||
# clean up
|
||||
shutil.rmtree(venv_dir)
|
||||
|
||||
# we're all done here; move it to output (overwrite existing)
|
||||
dst = os.path.join(output_dir, os.path.basename(repaired_wheel))
|
||||
dst = os.path.join(options.output_dir, os.path.basename(repaired_wheel))
|
||||
shutil.move(repaired_wheel, dst)
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
#
|
||||
# This file is autogenerated by pip-compile
|
||||
# To update, run:
|
||||
#
|
||||
# bin/update_constraints.py
|
||||
#
|
||||
appdirs==1.4.3 # via virtualenv
|
||||
configparser==4.0.2 # via importlib-metadata
|
||||
contextlib2==0.6.0.post1 # via importlib-metadata, importlib-resources, virtualenv
|
||||
delocate==0.8.0 # via -r cibuildwheel/resources/constraints.in
|
||||
distlib==0.3.0 # via virtualenv
|
||||
filelock==3.0.12 # via virtualenv
|
||||
importlib-metadata==1.6.0 # via importlib-resources, virtualenv
|
||||
importlib-resources==1.5.0 # via virtualenv
|
||||
pathlib2==2.3.5 # via importlib-metadata, importlib-resources, virtualenv
|
||||
scandir==1.10.0 # via pathlib2
|
||||
singledispatch==3.4.0.3 # via importlib-resources
|
||||
six==1.14.0 # via pathlib2, virtualenv
|
||||
typing==3.7.4.1 # via importlib-resources
|
||||
virtualenv==20.0.18 # via -r cibuildwheel/resources/constraints.in
|
||||
wheel==0.34.2 # via -r cibuildwheel/resources/constraints.in, delocate
|
||||
zipp==1.2.0 # via importlib-metadata, importlib-resources
|
||||
|
||||
# The following packages are considered to be unsafe in a requirements file:
|
||||
pip==20.1 # via -r cibuildwheel/resources/constraints.in
|
||||
setuptools==44.1.0 # via -r cibuildwheel/resources/constraints.in
|
||||
@@ -0,0 +1,20 @@
|
||||
#
|
||||
# This file is autogenerated by pip-compile
|
||||
# To update, run:
|
||||
#
|
||||
# bin/update_constraints.py
|
||||
#
|
||||
appdirs==1.4.3 # via virtualenv
|
||||
delocate==0.8.0 # via -r cibuildwheel/resources/constraints.in
|
||||
distlib==0.3.0 # via virtualenv
|
||||
filelock==3.0.12 # via virtualenv
|
||||
importlib-metadata==1.6.0 # via importlib-resources, virtualenv
|
||||
importlib-resources==1.5.0 # via virtualenv
|
||||
six==1.14.0 # via virtualenv
|
||||
virtualenv==20.0.18 # via -r cibuildwheel/resources/constraints.in
|
||||
wheel==0.34.2 # via -r cibuildwheel/resources/constraints.in, delocate
|
||||
zipp==1.2.0 # via importlib-metadata, importlib-resources
|
||||
|
||||
# The following packages are considered to be unsafe in a requirements file:
|
||||
pip==20.1 # via -r cibuildwheel/resources/constraints.in
|
||||
setuptools==46.1.3 # via -r cibuildwheel/resources/constraints.in
|
||||
@@ -0,0 +1,20 @@
|
||||
#
|
||||
# This file is autogenerated by pip-compile
|
||||
# To update, run:
|
||||
#
|
||||
# bin/update_constraints.py
|
||||
#
|
||||
appdirs==1.4.3 # via virtualenv
|
||||
delocate==0.8.0 # via -r cibuildwheel/resources/constraints.in
|
||||
distlib==0.3.0 # via virtualenv
|
||||
filelock==3.0.12 # via virtualenv
|
||||
importlib-metadata==1.6.0 # via importlib-resources, virtualenv
|
||||
importlib-resources==1.5.0 # via virtualenv
|
||||
six==1.14.0 # via virtualenv
|
||||
virtualenv==20.0.18 # via -r cibuildwheel/resources/constraints.in
|
||||
wheel==0.34.2 # via -r cibuildwheel/resources/constraints.in, delocate
|
||||
zipp==3.1.0 # via importlib-metadata, importlib-resources
|
||||
|
||||
# The following packages are considered to be unsafe in a requirements file:
|
||||
pip==20.1 # via -r cibuildwheel/resources/constraints.in
|
||||
setuptools==46.1.3 # via -r cibuildwheel/resources/constraints.in
|
||||
@@ -0,0 +1,5 @@
|
||||
pip
|
||||
setuptools
|
||||
wheel
|
||||
delocate
|
||||
virtualenv
|
||||
@@ -0,0 +1,19 @@
|
||||
#
|
||||
# This file is autogenerated by pip-compile
|
||||
# To update, run:
|
||||
#
|
||||
# bin/update_constraints.py
|
||||
#
|
||||
appdirs==1.4.3 # via virtualenv
|
||||
delocate==0.8.0 # via -r cibuildwheel/resources/constraints.in
|
||||
distlib==0.3.0 # via virtualenv
|
||||
filelock==3.0.12 # via virtualenv
|
||||
importlib-metadata==1.6.0 # via virtualenv
|
||||
six==1.14.0 # via virtualenv
|
||||
virtualenv==20.0.18 # via -r cibuildwheel/resources/constraints.in
|
||||
wheel==0.34.2 # via -r cibuildwheel/resources/constraints.in, delocate
|
||||
zipp==3.1.0 # via importlib-metadata
|
||||
|
||||
# The following packages are considered to be unsafe in a requirements file:
|
||||
pip==20.1 # via -r cibuildwheel/resources/constraints.in
|
||||
setuptools==46.1.3 # via -r cibuildwheel/resources/constraints.in
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,22 @@
|
||||
[x86_64]
|
||||
manylinux1 = quay.io/pypa/manylinux1_x86_64:2020-04-25-37c204c
|
||||
manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2020-04-29-0e1afc5
|
||||
manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2020-05-01-b37d76b
|
||||
|
||||
[i686]
|
||||
manylinux1 = quay.io/pypa/manylinux1_i686:2020-04-25-37c204c
|
||||
manylinux2010 = quay.io/pypa/manylinux2010_i686:2020-04-29-0e1afc5
|
||||
manylinux2014 = quay.io/pypa/manylinux2014_i686:2020-05-01-b37d76b
|
||||
|
||||
[pypy_x86_64]
|
||||
manylinux2010 = pypywheels/manylinux2010-pypy_x86_64:2020-04-25-eb2cdff
|
||||
|
||||
[aarch64]
|
||||
manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2020-05-01-b37d76b
|
||||
|
||||
[ppc64le]
|
||||
manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2020-05-01-b37d76b
|
||||
|
||||
[s390x]
|
||||
manylinux2014 = quay.io/pypa/manylinux2014_s390x:2020-05-01-b37d76b
|
||||
|
||||
@@ -2,6 +2,9 @@ import os
|
||||
import urllib.request
|
||||
from fnmatch import fnmatch
|
||||
from time import sleep
|
||||
from typing import NamedTuple, List, Optional, Dict
|
||||
|
||||
from .environment import ParsedEnvironment
|
||||
|
||||
|
||||
def prepare_command(command, **kwargs):
|
||||
@@ -76,3 +79,53 @@ def download(url, dest):
|
||||
file.write(response.read())
|
||||
finally:
|
||||
response.close()
|
||||
|
||||
|
||||
class DependencyConstraints:
|
||||
def __init__(self, base_file_path):
|
||||
assert os.path.exists(base_file_path)
|
||||
self.base_file_path = os.path.abspath(base_file_path)
|
||||
|
||||
@classmethod
|
||||
def with_defaults(cls):
|
||||
return cls(
|
||||
base_file_path=os.path.join(os.path.dirname(__file__), 'resources', 'constraints.txt')
|
||||
)
|
||||
|
||||
def get_for_python_version(self, version):
|
||||
version_parts = version.split('.')
|
||||
|
||||
# try to find a version-specific dependency file e.g. if
|
||||
# ./constraints.txt is the base, look for ./constraints-python27.txt
|
||||
base, ext = os.path.splitext(self.base_file_path)
|
||||
specific = base + '-python{}{}'.format(version_parts[0], version_parts[1])
|
||||
specific_file_path = specific + ext
|
||||
if os.path.exists(specific_file_path):
|
||||
return specific_file_path
|
||||
else:
|
||||
return self.base_file_path
|
||||
|
||||
|
||||
BuildOptions = NamedTuple("BuildOptions", [
|
||||
("package_dir", str),
|
||||
("output_dir", str),
|
||||
("test_command", Optional[str]),
|
||||
("test_requires", List[str]),
|
||||
("test_extras", str),
|
||||
("before_build", Optional[str]),
|
||||
("build_verbosity", int),
|
||||
("build_selector", BuildSelector),
|
||||
("repair_command", str),
|
||||
("environment", ParsedEnvironment),
|
||||
("before_test", str),
|
||||
("dependency_constraints", Optional[DependencyConstraints]),
|
||||
("manylinux_images", Optional[Dict[str, str]]),
|
||||
])
|
||||
|
||||
"""
|
||||
Replace this definition with a class-style NamedTuple in the
|
||||
PEP526 style when Python 3.5 host support is dropped
|
||||
"""
|
||||
|
||||
resources_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), 'resources'))
|
||||
get_pip_script = os.path.join(resources_dir, 'get-pip.py')
|
||||
|
||||
+99
-69
@@ -11,6 +11,8 @@ from .util import (
|
||||
download,
|
||||
get_build_verbosity_extra_flags,
|
||||
prepare_command,
|
||||
get_pip_script,
|
||||
BuildOptions
|
||||
)
|
||||
|
||||
|
||||
@@ -33,18 +35,20 @@ def get_nuget_args(version, arch):
|
||||
def get_python_configurations(build_selector):
|
||||
PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'arch', 'identifier', 'url'])
|
||||
python_configurations = [
|
||||
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),
|
||||
# CPython
|
||||
PythonConfiguration(version='2.7.18', arch='32', identifier='cp27-win32', url=None),
|
||||
PythonConfiguration(version='2.7.18', 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.7.7', arch='32', identifier='cp37-win32', url=None),
|
||||
PythonConfiguration(version='3.7.7', arch='64', identifier='cp37-win_amd64', url=None),
|
||||
PythonConfiguration(version='3.8.2', arch='32', identifier='cp38-win32', url=None),
|
||||
PythonConfiguration(version='3.8.2', arch='64', identifier='cp38-win_amd64', url=None),
|
||||
PythonConfiguration(version='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'),
|
||||
# PyPy
|
||||
PythonConfiguration(version='2.7', arch='32', identifier='pp27-win32', url='https://downloads.python.org/pypy/pypy2.7-v7.3.1-win32.zip'),
|
||||
PythonConfiguration(version='3.6', arch='32', identifier='pp36-win32', url='https://downloads.python.org/pypy/pypy3.6-v7.3.1-win32.zip'),
|
||||
]
|
||||
|
||||
if IS_RUNNING_ON_TRAVIS:
|
||||
@@ -82,12 +86,62 @@ def install_pypy(version, arch, url):
|
||||
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, test_requires, test_extras, before_build, build_verbosity, build_selector, repair_command, environment):
|
||||
abs_project_dir = os.path.abspath(project_dir)
|
||||
def setup_python(python_configuration, dependency_constraint_flags, environment):
|
||||
nuget = 'C:\\cibw\\nuget.exe'
|
||||
if not os.path.exists(nuget):
|
||||
download('https://dist.nuget.org/win-x86-commandline/latest/nuget.exe', nuget)
|
||||
|
||||
if python_configuration.identifier.startswith('cp'):
|
||||
installation_path = install_cpython(python_configuration.version, python_configuration.arch, nuget)
|
||||
elif python_configuration.identifier.startswith('pp'):
|
||||
installation_path = install_pypy(python_configuration.version, python_configuration.arch, python_configuration.url)
|
||||
else:
|
||||
raise ValueError("Unknown Python implementation")
|
||||
|
||||
assert os.path.exists(os.path.join(installation_path, 'python.exe'))
|
||||
|
||||
# set up PATH and environment variables for run_with_env
|
||||
env = os.environ.copy()
|
||||
env['PYTHON_VERSION'] = python_configuration.version
|
||||
env['PYTHON_ARCH'] = python_configuration.arch
|
||||
env['PATH'] = os.pathsep.join([
|
||||
installation_path,
|
||||
os.path.join(installation_path, 'Scripts'),
|
||||
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)
|
||||
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] + dependency_constraint_flags, 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'] + dependency_constraint_flags, env=env)
|
||||
shell(['pip', '--version'], env=env)
|
||||
shell(['pip', 'install', '--upgrade', 'setuptools', 'wheel'] + dependency_constraint_flags, env=env)
|
||||
|
||||
return env
|
||||
|
||||
|
||||
def build(options: BuildOptions):
|
||||
temp_dir = tempfile.mkdtemp(prefix='cibuildwheel')
|
||||
built_wheel_dir = os.path.join(temp_dir, 'built_wheel')
|
||||
repaired_wheel_dir = os.path.join(temp_dir, 'repaired_wheel')
|
||||
@@ -95,87 +149,51 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef
|
||||
# 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)
|
||||
python_configurations = get_python_configurations(options.build_selector)
|
||||
for config in python_configurations:
|
||||
dependency_constraint_flags = []
|
||||
if options.dependency_constraints:
|
||||
dependency_constraint_flags = [
|
||||
'-c', options.dependency_constraints.get_for_python_version(config.version)
|
||||
]
|
||||
|
||||
# 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")
|
||||
|
||||
assert os.path.exists(os.path.join(installation_path, 'python.exe'))
|
||||
|
||||
# set up PATH and environment variables for run_with_env
|
||||
env = os.environ.copy()
|
||||
env['PYTHON_VERSION'] = config.version
|
||||
env['PYTHON_ARCH'] = config.arch
|
||||
env['PATH'] = os.pathsep.join([
|
||||
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)
|
||||
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', '--version'], env=env)
|
||||
shell(['pip', 'install', '--upgrade', 'setuptools', 'wheel'], env=env)
|
||||
env = setup_python(config, dependency_constraint_flags, options.environment)
|
||||
|
||||
# run the before_build command
|
||||
if before_build:
|
||||
before_build_prepared = prepare_command(before_build, project=abs_project_dir)
|
||||
if options.before_build:
|
||||
before_build_prepared = prepare_command(options.before_build, project='.', package=options.package_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)
|
||||
shell(['pip', 'wheel', options.package_dir, '-w', built_wheel_dir, '--no-deps'] + get_build_verbosity_extra_flags(options.build_verbosity), env=env)
|
||||
built_wheel = glob(os.path.join(built_wheel_dir, '*.whl'))[0]
|
||||
|
||||
# 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:
|
||||
if built_wheel.endswith('none-any.whl') or not options.repair_command:
|
||||
# pure Python wheel or empty repair command
|
||||
shutil.move(built_wheel, repaired_wheel_dir)
|
||||
else:
|
||||
repair_command_prepared = prepare_command(repair_command, wheel=built_wheel, dest_dir=repaired_wheel_dir)
|
||||
repair_command_prepared = prepare_command(options.repair_command, wheel=built_wheel, dest_dir=repaired_wheel_dir)
|
||||
shell([repair_command_prepared], env=env)
|
||||
repaired_wheel = glob(os.path.join(repaired_wheel_dir, '*.whl'))[0]
|
||||
|
||||
if test_command:
|
||||
if options.test_command:
|
||||
# set up a virtual environment to install and test from, to make sure
|
||||
# there are no dependencies that were pulled in at build time.
|
||||
shell(['pip', 'install', 'virtualenv'], env=env)
|
||||
shell(['pip', 'install', 'virtualenv'] + dependency_constraint_flags, env=env)
|
||||
venv_dir = tempfile.mkdtemp()
|
||||
shell(['python', '-m', 'virtualenv', venv_dir], env=env)
|
||||
|
||||
# Use --no-download to ensure determinism by using seed libraries
|
||||
# built into virtualenv
|
||||
shell(['python', '-m', 'virtualenv', '--no-download', venv_dir], env=env)
|
||||
|
||||
virtualenv_env = env.copy()
|
||||
|
||||
@@ -192,24 +210,36 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef
|
||||
# check that we are using the Python from the virtual environment
|
||||
shell(['which', 'python'], env=virtualenv_env)
|
||||
|
||||
if options.before_test:
|
||||
before_test_prepared = prepare_command(
|
||||
options.before_test,
|
||||
project='.',
|
||||
package=options.package_dir
|
||||
)
|
||||
shell([before_test_prepared], env=virtualenv_env)
|
||||
|
||||
# install the wheel
|
||||
shell(['pip', 'install', repaired_wheel + test_extras], env=virtualenv_env)
|
||||
shell(['pip', 'install', repaired_wheel + options.test_extras], env=virtualenv_env)
|
||||
|
||||
# test the wheel
|
||||
if test_requires:
|
||||
shell(['pip', 'install'] + test_requires, env=virtualenv_env)
|
||||
if options.test_requires:
|
||||
shell(['pip', 'install'] + options.test_requires, env=virtualenv_env)
|
||||
|
||||
# run the tests from c:\, with an absolute path in the command
|
||||
# (this ensures that Python runs the tests against the installed wheel
|
||||
# and not the repo code)
|
||||
test_command_prepared = prepare_command(test_command, project=abs_project_dir)
|
||||
test_command_prepared = prepare_command(
|
||||
options.test_command,
|
||||
project=os.path.abspath('.'),
|
||||
package=os.path.abspath(options.package_dir)
|
||||
)
|
||||
shell([test_command_prepared], cwd='c:\\', env=virtualenv_env)
|
||||
|
||||
# clean up
|
||||
shutil.rmtree(venv_dir)
|
||||
|
||||
# we're all done here; move it to output (remove if already exists)
|
||||
dst = os.path.join(output_dir, os.path.basename(repaired_wheel))
|
||||
dst = os.path.join(options.output_dir, os.path.basename(repaired_wheel))
|
||||
if os.path.isfile(dst):
|
||||
os.remove(dst)
|
||||
shutil.move(repaired_wheel, dst)
|
||||
|
||||
@@ -7,7 +7,7 @@ Building Python wheels with modern C++ standards (C++11 and later) requires a fe
|
||||
|
||||
## 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.
|
||||
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
|
||||
@@ -16,15 +16,17 @@ The default `manylinux1` image (based on CentOS 5) contains a version of GCC and
|
||||
|
||||
## 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.
|
||||
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.
|
||||
|
||||
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).
|
||||
To get C++17 support, Xcode 9.3+ is needed, requiring at least macOS 10.13 on the build machine. To use C++17 library features and link against the C++ runtime library, set `MACOSX_DEPLOYMENT_TARGET` to `"10.13"` or `"10.14"` (or higher) - 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.
|
||||
However, if only C++17 compiler and standard template library (STL) features are used (not needing a C++17 runtime) it might be possible to set `MACOSX_DEPLOYMENT_TARGET` to a lower value, such as `"10.9"`. To find out if this is the case, try compiling and running with a lower `MACOSX_DEPLOYMENT_TARGET`: if C++17 features are used that require a more recent deployment target, building the wheel should fail.
|
||||
|
||||
For more details see https://en.cppreference.com/w/cpp/compiler_support, https://en.wikipedia.org/wiki/Xcode, and https://xcodereleases.com/: Xcode 10 needs macOS 10.13 and Xcode 11 needs macOS 10.14.
|
||||
|
||||
## Windows and Python 2.7
|
||||
|
||||
|
||||
+116
-19
@@ -14,16 +14,16 @@ your CI config file:
|
||||
```yaml
|
||||
env:
|
||||
global:
|
||||
- CIBW_TEST_REQUIRES=nose
|
||||
- CIBW_TEST_COMMAND="nosetests {project}/tests"
|
||||
- CIBW_TEST_REQUIRES=pytest
|
||||
- CIBW_TEST_COMMAND="pytest {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"
|
||||
CIBW_TEST_REQUIRES: pytest
|
||||
CIBW_TEST_COMMAND: "pytest {project}\\tests"
|
||||
```
|
||||
|
||||
> .circleci/config.yml ([docs](https://circleci.com/docs/2.0/configuration-reference/#environment))
|
||||
@@ -31,15 +31,22 @@ environment:
|
||||
jobs:
|
||||
job_name:
|
||||
environment:
|
||||
CIBW_TEST_REQUIRES: nose
|
||||
CIBW_TEST_COMMAND: "nosetests {project}/tests"
|
||||
CIBW_TEST_REQUIRES: pytest
|
||||
CIBW_TEST_COMMAND: "pytest {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"
|
||||
CIBW_TEST_REQUIRES: pytest
|
||||
CIBW_TEST_COMMAND: "pytest {project}/tests"
|
||||
```
|
||||
|
||||
> .github/workflows/*.yml ([docs](https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables)) (can be global, in job, or in step)
|
||||
```yaml
|
||||
env:
|
||||
CIBW_TEST_REQUIRES: pytest
|
||||
CIBW_TEST_COMMAND: "pytest {project}/tests"
|
||||
```
|
||||
|
||||
|
||||
@@ -192,7 +199,7 @@ A shell command to run before building the wheel. This option allows you to run
|
||||
|
||||
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 active Python binary can be accessed using `python`, and pip with `pip`; `cibuildwheel` makes sure the right version of Python and pip will be executed. The placeholder `{package}` can be used here; it will be replaced by the path to the package being built by `cibuildwheel`.
|
||||
|
||||
The command is run in a shell, so you can write things like `cmd1 && cmd2`.
|
||||
|
||||
@@ -210,8 +217,11 @@ 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
|
||||
# run a script that's inside your project
|
||||
CIBW_BEFORE_BUILD: bash scripts/prepare_for_build.sh
|
||||
|
||||
# if cibuildwheel is called with a package_dir argument, it's available as {package}
|
||||
CIBW_BEFORE_BUILD: "{package}/bin/prepare_for_build.sh"
|
||||
```
|
||||
|
||||
|
||||
@@ -253,7 +263,7 @@ CIBW_REPAIR_WHEEL_COMMAND_LINUX: "auditwheel repair --lib-sdir . -w {dest_dir} {
|
||||
|
||||
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
|
||||
The value of this option can either be set to `manylinux1`, `manylinux2010` or `manylinux2014` to use a pinned version of the [official `manylinux` images](https://github.com/pypa/manylinux) and [PyPy `manylinux` images](https://github.com/pypy/manylinux). Alternatively, set these options to 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).
|
||||
@@ -276,17 +286,75 @@ CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014
|
||||
CIBW_MANYLINUX_I686_IMAGE: manylinux2014
|
||||
CIBW_SKIP: cp27-manylinux*
|
||||
|
||||
# build using the latest manylinux2010 release, instead of the cibuildwheel
|
||||
# pinned version
|
||||
CIBW_MANYLINUX_X86_64_IMAGE: quay.io/pypa/manylinux2010_x86_64:latest
|
||||
CIBW_MANYLINUX_I686_IMAGE: quay.io/pypa/manylinux2010_i686:latest
|
||||
|
||||
# build using a different image from the docker registry
|
||||
CIBW_MANYLINUX_X86_64_IMAGE: dockcross/manylinux-x64
|
||||
CIBW_MANYLINUX_I686_IMAGE: dockcross/manylinux-x86
|
||||
```
|
||||
|
||||
### `CIBW_DEPENDENCY_VERSIONS` {: #dependency-versions}
|
||||
> Specify how cibuildwheel controls the versions of the tools it uses
|
||||
|
||||
Options: `pinned` `latest` `<your constraints file>`
|
||||
|
||||
Default: `pinned`
|
||||
|
||||
If `CIBW_DEPENDENCY_VERSIONS` is `pinned`, cibuildwheel uses versions of tools
|
||||
like `pip`, `setuptools`, `virtualenv` that were pinned with that release of
|
||||
cibuildwheel. This represents a known-good set of dependencies, and is
|
||||
recommended for build repeatability.
|
||||
|
||||
If set to `latest`, cibuildwheel will use the latest of these packages that
|
||||
are available on PyPI. This might be preferable if these packages have bug
|
||||
fixes that can't wait for a new cibuildwheel release.
|
||||
|
||||
To control the versions of dependencies yourself, you can supply a [pip
|
||||
constraints](https://pip.pypa.io/en/stable/user_guide/#constraints-files) file
|
||||
here and it will be used instead.
|
||||
|
||||
!!! note
|
||||
If you need different dependencies for each python version, provide them
|
||||
in the same folder with a `-pythonXY` suffix. e.g. if your
|
||||
`CIBW_DEPENDENCY_VERSIONS=./constraints.txt`, cibuildwheel will use
|
||||
`./constraints-python27.txt` on Python 2.7, or fallback to
|
||||
`./constraints.txt` if that's not found.
|
||||
|
||||
Platform-specific variants also available:<br/>
|
||||
`CIBW_DEPENDENCY_VERSIONS_MACOS` | `CIBW_DEPENDENCY_VERSIONS_WINDOWS`
|
||||
|
||||
!!! note
|
||||
This option does not affect the tools used on the Linux build - those versions
|
||||
are bundled with the manylinux image that cibuildwheel uses. To change
|
||||
dependency versions on Linux, use the [CIBW_MANYLINUX_*](#manylinux-image)
|
||||
options.
|
||||
|
||||
#### Examples
|
||||
|
||||
```yaml
|
||||
# use tools versions that are bundled with cibuildwheel (this is the default)
|
||||
CIBW_DEPENDENCY_VERSIONS: pinned
|
||||
|
||||
# use the latest versions available on PyPI
|
||||
CIBW_DEPENDENCY_VERSIONS: latest
|
||||
|
||||
# use your own pip constraints file
|
||||
CIBW_DEPENDENCY_VERSIONS: ./constraints.txt
|
||||
```
|
||||
|
||||
|
||||
## 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`.
|
||||
Shell command to run tests after the build. The wheel will be installed automatically and available for import from the tests. To ensure the wheel is imported by your tests (instead of your source copy), tests are run from a different directory. Use the placeholders `{project}` and `{package}` when specifying paths in your project.
|
||||
|
||||
- `{project}` is an absolute path to the project root - the working directory where cibuildwheel was called.
|
||||
- `{package}` is the path to the package being built - the `package_dir` argument supplied to cibuildwheel on the command line.
|
||||
|
||||
The command is run in a shell, so you can write things like `cmd1 && cmd2`.
|
||||
|
||||
@@ -299,8 +367,8 @@ Platform-specific variants also available:<br/>
|
||||
# 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
|
||||
# run the package tests using `pytest`
|
||||
CIBW_TEST_COMMAND: pytest {package}/tests
|
||||
```
|
||||
|
||||
|
||||
@@ -343,6 +411,33 @@ Platform-specific variants also available:<br/>
|
||||
CIBW_TEST_EXTRAS: test,qt
|
||||
```
|
||||
|
||||
### `CIBW_BEFORE_TEST` {: #before-test}
|
||||
> Execute a shell command before testing each wheel
|
||||
|
||||
A shell command to run in **each** test virtual environment, before your wheel is installed and tested. This is useful if you need to install a non pip package, change values of environment variables
|
||||
or perform multi step pip installation (e.g. installing `scikit-build` or `cython` before install test package)
|
||||
|
||||
The active Python binary can be accessed using `python`, and pip with `pip`; `cibuildwheel` makes sure the right version of Python and pip will be executed. The placeholder `{package}` can be used here; it will be replaced by the path to the package being built by `cibuildwheel`.
|
||||
|
||||
The command is run in a shell, so you can write things like `cmd1 && cmd2`.
|
||||
|
||||
Platform-specific variants also available:<br/>
|
||||
`CIBW_BEFORE_TEST_MACOS` | `CIBW_BEFORE_TEST_WINDOWS` | `CIBW_BEFORE_TEST_LINUX`
|
||||
|
||||
#### Examples
|
||||
```yaml
|
||||
# install test dependencies with overwritten environment variables.
|
||||
CIBW_BEFORE_TEST: CC=gcc CXX=g++ pip install -r requirements.txt
|
||||
|
||||
# chain commands using &&
|
||||
CIBW_BEFORE_TEST: rm -rf ./data/cache && mkdir -p ./data/cache
|
||||
|
||||
# install non pip python package
|
||||
CIBW_BEFORE_TEST: cd some_dir; ./configure; make; make install
|
||||
|
||||
# install python packages that are required to install test dependencies
|
||||
CIBW_BEFORE_TEST: pip install cmake scikit-build
|
||||
```
|
||||
|
||||
## Other
|
||||
|
||||
@@ -367,13 +462,16 @@ CIBW_BUILD_VERBOSITY: 1
|
||||
```text
|
||||
usage: cibuildwheel [-h] [--platform {auto,linux,macos,windows}]
|
||||
[--output-dir OUTPUT_DIR] [--print-build-identifiers]
|
||||
[project_dir]
|
||||
[package_dir]
|
||||
|
||||
Build wheels for all the platforms.
|
||||
|
||||
positional arguments:
|
||||
project_dir Path to the project that you want wheels for.
|
||||
Default: the current directory.
|
||||
package_dir Path to the package that you want wheels for. Must be
|
||||
a subdirectory of the working directory. When set, the
|
||||
working directory is still considered the 'project'
|
||||
and is copied into the Docker container on Linux.
|
||||
Default: the working directory.
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
@@ -384,13 +482,12 @@ optional arguments:
|
||||
automatically install MacPython on your system, so
|
||||
don't run on your development machine. For "windows",
|
||||
you need to run in Windows, and it will build and test
|
||||
for all versions of Python at C:\PythonXX[-x64].
|
||||
for all versions of Python. Default: auto.
|
||||
--output-dir OUTPUT_DIR
|
||||
Destination folder for the wheels.
|
||||
--print-build-identifiers
|
||||
Print the build identifiers matched by the current
|
||||
invocation and exit.
|
||||
|
||||
```
|
||||
|
||||
<style>
|
||||
|
||||
+14
-1
@@ -2,9 +2,22 @@
|
||||
title: 'Setup'
|
||||
---
|
||||
|
||||
# GitHub Actions [linux/mac/windows] {: #github-actions}
|
||||
|
||||
To build Linux, Mac, and Windows wheels using Github Actions, create a `.github/workflows/build.yml` file in your repo.
|
||||
|
||||
> build.yml
|
||||
```yaml
|
||||
{% include "../examples/github-minimal.yml" %}
|
||||
```
|
||||
|
||||
Commit this file, and push to Github - either to your default branch, or to a PR branch. The build should start automatically.
|
||||
|
||||
For more info on this file, check out the [docs](https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions).
|
||||
|
||||
# Azure Pipelines [linux/mac/windows] {: #azure-pipelines}
|
||||
|
||||
Using Azure pipelines, you can build all three platforms on the same service. Create a `azure-pipelines.yml` file in your repo.
|
||||
To build Linux, Mac, and Windows wheels on Azure Pipelines, create a `azure-pipelines.yml` file in your repo.
|
||||
|
||||
> azure-pipelines.yml
|
||||
```yaml
|
||||
|
||||
@@ -12,7 +12,7 @@ stack: python 3.7
|
||||
init:
|
||||
- cmd: set PATH=C:\Python37;C:\Python37\Scripts;%PATH%
|
||||
|
||||
install: python -m pip install cibuildwheel==1.2.0
|
||||
install: python -m pip install cibuildwheel==1.4.1
|
||||
|
||||
build_script: python -m cibuildwheel --output-dir wheelhouse
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ jobs:
|
||||
- task: UsePythonVersion@0
|
||||
- bash: |
|
||||
python3 -m pip install --upgrade pip
|
||||
pip3 install cibuildwheel==1.2.0
|
||||
pip3 install cibuildwheel==1.4.1
|
||||
cibuildwheel --output-dir wheelhouse .
|
||||
- task: PublishBuildArtifacts@1
|
||||
inputs: {pathtoPublish: 'wheelhouse'}
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
- task: UsePythonVersion@0
|
||||
- bash: |
|
||||
python3 -m pip install --upgrade pip
|
||||
pip3 install cibuildwheel==1.2.0
|
||||
pip3 install cibuildwheel==1.4.1
|
||||
cibuildwheel --output-dir wheelhouse .
|
||||
- task: PublishBuildArtifacts@1
|
||||
inputs: {pathtoPublish: 'wheelhouse'}
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
displayName: Install Visual C++ for Python 2.7
|
||||
- bash: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install cibuildwheel==1.2.0
|
||||
pip install cibuildwheel==1.4.1
|
||||
cibuildwheel --output-dir wheelhouse .
|
||||
- task: PublishBuildArtifacts@1
|
||||
inputs: {pathtoPublish: 'wheelhouse'}
|
||||
|
||||
@@ -11,7 +11,7 @@ jobs:
|
||||
- run:
|
||||
name: Build the Linux wheels.
|
||||
command: |
|
||||
pip3 install --user cibuildwheel==1.2.0
|
||||
pip3 install --user cibuildwheel==1.4.1
|
||||
cibuildwheel --output-dir wheelhouse
|
||||
- store_artifacts:
|
||||
path: wheelhouse/
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
- run:
|
||||
name: Build the OS X wheels.
|
||||
command: |
|
||||
pip3 install --user cibuildwheel==1.2.0
|
||||
pip3 install --user cibuildwheel==1.4.1
|
||||
cibuildwheel --output-dir wheelhouse
|
||||
- store_artifacts:
|
||||
path: wheelhouse/
|
||||
|
||||
@@ -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.4.1
|
||||
|
||||
- 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
|
||||
@@ -25,7 +25,7 @@ env:
|
||||
# Note: TWINE_PASSWORD is set to a PyPI API token in Travis settings
|
||||
|
||||
install:
|
||||
- python3 -m pip install cibuildwheel==1.2.0
|
||||
- python3 -m pip install cibuildwheel==1.4.1
|
||||
|
||||
script:
|
||||
# build the wheels, put them into './wheelhouse'
|
||||
|
||||
@@ -6,7 +6,7 @@ jobs:
|
||||
- services: docker
|
||||
# perform a linux ARMv8 build
|
||||
- services: docker
|
||||
arch: aarch64
|
||||
arch: arm64
|
||||
# perform a linux PPC64LE build
|
||||
- services: docker
|
||||
arch: ppc64le
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
- ln -s /c/Python38/python.exe /c/Python38/python3.exe
|
||||
|
||||
install:
|
||||
- python3 -m pip install cibuildwheel==1.2.0
|
||||
- python3 -m pip install cibuildwheel==1.4.1
|
||||
|
||||
script:
|
||||
# build the wheels, put them into './wheelhouse'
|
||||
|
||||
@@ -56,7 +56,7 @@ jobs:
|
||||
- stage: deploy
|
||||
name: Build and deploy Linux wheels
|
||||
services: docker
|
||||
install: python3 -m pip install cibuildwheel==1.2.0
|
||||
install: python3 -m pip install cibuildwheel==1.4.1
|
||||
script: python3 -m cibuildwheel --output-dir wheelhouse
|
||||
after_success: |
|
||||
python3 -m pip install twine
|
||||
@@ -66,7 +66,7 @@ jobs:
|
||||
name: Build and deploy macOS wheels
|
||||
os: osx
|
||||
language: shell
|
||||
install: python3 -m pip install cibuildwheel==1.2.0
|
||||
install: python3 -m pip install cibuildwheel==1.4.1
|
||||
script: python3 -m cibuildwheel --output-dir wheelhouse
|
||||
after_success: |
|
||||
python3 -m pip install twine
|
||||
@@ -76,7 +76,7 @@ jobs:
|
||||
name: Build and deploy Windows wheels
|
||||
os: windows
|
||||
language: shell
|
||||
install: python3 -m pip install cibuildwheel==1.2.0
|
||||
install: python3 -m pip install cibuildwheel==1.4.1
|
||||
script: python3 -m cibuildwheel --output-dir wheelhouse
|
||||
after_success: |
|
||||
python3 -m pip install twine
|
||||
|
||||
@@ -28,6 +28,8 @@ markdown_extensions:
|
||||
permalink: True
|
||||
- attr_list
|
||||
- admonition
|
||||
- pymdownx.magiclink:
|
||||
repo_url_shortener: True
|
||||
|
||||
plugins:
|
||||
- importmarkdown
|
||||
|
||||
@@ -2,3 +2,7 @@
|
||||
-e ./docs/mkdocs_include_markdown_plugin
|
||||
pytest
|
||||
mkdocs==1.0.4
|
||||
pymdown-extensions
|
||||
pip-tools
|
||||
requests
|
||||
click
|
||||
|
||||
@@ -1,24 +1,3 @@
|
||||
[bumpversion]
|
||||
current_version = 1.1.0
|
||||
commit = True
|
||||
tag = True
|
||||
message = Bump version
|
||||
|
||||
[bumpversion:file:setup.py]
|
||||
|
||||
[bumpversion:file:cibuildwheel/__init__.py]
|
||||
|
||||
[bumpversion:file:README.md]
|
||||
search = cibuildwheel=={current_version}
|
||||
replace = cibuildwheel=={new_version}
|
||||
|
||||
[bumpversion:file:docs/setup.md]
|
||||
search = cibuildwheel=={current_version}
|
||||
replace = cibuildwheel=={new_version}
|
||||
|
||||
[bdist_wheel]
|
||||
universal = 1
|
||||
|
||||
[flake8]
|
||||
ignore = E501,W503
|
||||
application-import-names = cibuildwheel
|
||||
@@ -29,5 +8,6 @@ exclude =
|
||||
.git/,
|
||||
env/,
|
||||
env2/,
|
||||
env??/,
|
||||
.venv/,
|
||||
site/
|
||||
|
||||
@@ -13,7 +13,7 @@ with io.open(os.path.join(this_directory, 'README.md'), encoding='utf-8') as f:
|
||||
|
||||
setup(
|
||||
name='cibuildwheel',
|
||||
version='1.2.0',
|
||||
version='1.4.1',
|
||||
install_requires=['bashlex!=0.13'],
|
||||
description="Build Python wheels on CI with minimal configuration.",
|
||||
long_description=long_description,
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
from __future__ import print_function
|
||||
import os
|
||||
import platform
|
||||
import sys
|
||||
import struct
|
||||
from unittest import TestCase
|
||||
|
||||
import spam
|
||||
@@ -43,3 +45,12 @@ class TestSpam(TestCase):
|
||||
print("=[listdir]2", os.listdir(os.path.join(virtualenv_path, 'bin')))
|
||||
self.assertTrue(path_contains(virtualenv_path, sys.executable))
|
||||
self.assertTrue(path_contains(virtualenv_path, spam.__file__))
|
||||
|
||||
def test_uname(self):
|
||||
if platform.system() == "Windows":
|
||||
return
|
||||
# if we're running in 32-bit Python, check that the machine is i686.
|
||||
# See #336 for more info.
|
||||
bits = struct.calcsize("P") * 8
|
||||
if bits == 32:
|
||||
self.assertEqual(platform.machine(), "i686")
|
||||
|
||||
@@ -12,7 +12,7 @@ def test(manylinux_image):
|
||||
|
||||
if utils.platform != 'linux':
|
||||
pytest.skip('the docker test is only relevant to the linux build')
|
||||
elif platform.machine not in ['x86_64', 'i686']:
|
||||
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")
|
||||
|
||||
|
||||
@@ -10,35 +10,30 @@ 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'
|
||||
add_env = {'CIBW_SKIP': 'cp27-win* pp27-win32', 'CIBW_ENVIRONMENT': 'STANDARD=11'}
|
||||
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
|
||||
expected_wheels = [w for w in utils.expected_wheels(
|
||||
'spam', '0.1.0', macosx_deployment_target='10.9')
|
||||
if 'cp27-cp27m-win' not in w
|
||||
and 'pp27-pypy_73-win32' not in w]
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if 'cp27-cp27m-win' not in w and 'pp27-pypy_73-win32' not in w]
|
||||
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
|
||||
|
||||
def test_cpp14():
|
||||
# This test checks that the C++14 standard is supported
|
||||
|
||||
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'
|
||||
add_env = {'CIBW_SKIP': 'cp27-win* pp27-win32 cp35-win*', 'CIBW_ENVIRONMENT': 'STANDARD=14'}
|
||||
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
|
||||
expected_wheels = [w for w in utils.expected_wheels(
|
||||
'spam', '0.1.0', 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]
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if 'cp27-cp27m-win' not in w
|
||||
and 'pp27-pypy_73-win32' not in w
|
||||
and 'cp35-cp35m-win' not in w]
|
||||
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
|
||||
|
||||
@@ -52,14 +47,15 @@ def test_cpp17():
|
||||
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]
|
||||
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,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,125 @@
|
||||
import os
|
||||
import re
|
||||
import pytest
|
||||
import textwrap
|
||||
import cibuildwheel.util
|
||||
|
||||
import utils
|
||||
|
||||
VERSION_REGEX = r'([\w-]+)==([^\s]+)'
|
||||
|
||||
|
||||
def get_versions_from_constraint_file(constraint_file):
|
||||
with open(constraint_file, encoding='utf8') as f:
|
||||
constraint_file_text = f.read()
|
||||
|
||||
versions = {}
|
||||
|
||||
for package, version in re.findall(VERSION_REGEX, constraint_file_text):
|
||||
versions[package] = version
|
||||
|
||||
return versions
|
||||
|
||||
|
||||
@pytest.mark.parametrize('python_version', ['2.7', '3.5', '3.8'])
|
||||
def test_pinned_versions(python_version):
|
||||
if utils.platform == 'linux':
|
||||
pytest.skip('linux doesn\'t pin individual tool versions, it pins manylinux images instead')
|
||||
|
||||
project_dir = os.path.dirname(__file__)
|
||||
|
||||
build_environment = {}
|
||||
|
||||
if python_version == '2.7':
|
||||
constraint_filename = 'constraints-python27.txt'
|
||||
build_pattern = '[cp]p27-*'
|
||||
elif python_version == '3.5':
|
||||
constraint_filename = 'constraints-python35.txt'
|
||||
build_pattern = '[cp]p35-*'
|
||||
else:
|
||||
constraint_filename = 'constraints.txt'
|
||||
build_pattern = '[cp]p38-*'
|
||||
|
||||
constraint_file = os.path.join(cibuildwheel.util.resources_dir, constraint_filename)
|
||||
constraint_versions = get_versions_from_constraint_file(constraint_file)
|
||||
|
||||
for package in ['pip', 'setuptools', 'wheel', 'virtualenv']:
|
||||
env_name = 'EXPECTED_{}_VERSION'.format(package.upper())
|
||||
build_environment[env_name] = constraint_versions[package]
|
||||
|
||||
cibw_environment_option = ' '.join(
|
||||
['{}={}'.format(k, v) for k, v in build_environment.items()]
|
||||
)
|
||||
|
||||
# build and test the wheels
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
||||
'CIBW_BUILD': build_pattern,
|
||||
'CIBW_ENVIRONMENT': cibw_environment_option,
|
||||
})
|
||||
|
||||
# also check that we got the right wheels
|
||||
if python_version == '2.7':
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if '-cp27' in w or '-pp27' in w]
|
||||
elif python_version == '3.5':
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if '-cp35' in w or '-pp35' in w]
|
||||
elif python_version == '3.8':
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if '-cp38' in w or '-pp38' in w]
|
||||
else:
|
||||
raise ValueError('unhandled python version')
|
||||
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('python_version', ['2.7', '3.x'])
|
||||
def test_dependency_constraints_file(tmp_path, python_version):
|
||||
if utils.platform == 'linux':
|
||||
pytest.skip('linux doesn\'t pin individual tool versions, it pins manylinux images instead')
|
||||
|
||||
project_dir = os.path.dirname(__file__)
|
||||
|
||||
tool_versions = {
|
||||
'pip': '20.0.2',
|
||||
'setuptools': '44.0.0' if python_version == '2.7' else '46.0.0',
|
||||
'wheel': '0.34.2',
|
||||
'virtualenv': '20.0.10',
|
||||
}
|
||||
|
||||
constraints_file = tmp_path / 'constraints.txt'
|
||||
constraints_file.write_text(textwrap.dedent(
|
||||
'''
|
||||
pip=={pip}
|
||||
setuptools=={setuptools}
|
||||
wheel=={wheel}
|
||||
virtualenv=={virtualenv}
|
||||
'''.format(**tool_versions)
|
||||
))
|
||||
|
||||
build_environment = {}
|
||||
|
||||
for package_name, version in tool_versions.items():
|
||||
env_name = 'EXPECTED_{}_VERSION'.format(package_name.upper())
|
||||
build_environment[env_name] = version
|
||||
|
||||
cibw_environment_option = ' '.join(
|
||||
['{}={}'.format(k, v) for k, v in build_environment.items()]
|
||||
)
|
||||
|
||||
# build and test the wheels
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
||||
'CIBW_BUILD': '[cp]p27-*' if python_version == '2.7' else '[cp]p3?-*',
|
||||
'CIBW_ENVIRONMENT': cibw_environment_option,
|
||||
'CIBW_DEPENDENCY_VERSIONS': str(constraints_file),
|
||||
})
|
||||
|
||||
# also check that we got the right wheels
|
||||
if python_version == '2.7':
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if '-cp27' in w or '-pp27' in w]
|
||||
else:
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if '-cp27' not in w and '-pp27' not in w]
|
||||
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
@@ -0,0 +1,31 @@
|
||||
import os
|
||||
import subprocess
|
||||
from setuptools import (
|
||||
Extension,
|
||||
setup,
|
||||
)
|
||||
|
||||
versions_output_text = subprocess.check_output(
|
||||
['pip', 'freeze', '--all', '-qq'],
|
||||
universal_newlines=True,
|
||||
)
|
||||
versions = versions_output_text.strip().splitlines()
|
||||
|
||||
# `versions` now looks like:
|
||||
# ['pip==x.x.x', 'setuptools==x.x.x', 'wheel==x.x.x']
|
||||
|
||||
print('Gathered versions', versions)
|
||||
|
||||
for package_name in ['pip', 'setuptools', 'wheel']:
|
||||
env_name = 'EXPECTED_{}_VERSION'.format(package_name.upper())
|
||||
expected_version = os.environ[env_name]
|
||||
|
||||
assert '{}=={}'.format(package_name, expected_version) in versions, (
|
||||
'error: {} version should equal {}'.format(package_name, expected_version)
|
||||
)
|
||||
|
||||
setup(
|
||||
name="spam",
|
||||
ext_modules=[Extension('spam', sources=['spam.c'])],
|
||||
version="0.1.0",
|
||||
)
|
||||
@@ -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)
|
||||
}
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
print('before_build.py executed!')
|
||||
@@ -0,0 +1,25 @@
|
||||
import os
|
||||
import utils
|
||||
|
||||
project_dir = os.path.dirname(__file__)
|
||||
|
||||
|
||||
def test(capfd):
|
||||
package_dir = os.path.join(project_dir, 'src', 'spam')
|
||||
# build the wheels
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, package_dir=package_dir, add_env={
|
||||
'CIBW_BEFORE_BUILD': 'python {project}/bin/before_build.py',
|
||||
'CIBW_TEST_COMMAND': 'python {package}/test/run_tests.py',
|
||||
# this shouldn't depend on the version of python, so build only
|
||||
# CPython 3.6
|
||||
'CIBW_BUILD': 'cp36-*',
|
||||
})
|
||||
|
||||
# check that the expected wheels are produced
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if 'cp36' in w]
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
|
||||
captured = capfd.readouterr()
|
||||
assert "before_build.py executed!" in captured.out
|
||||
assert "run_tests.py executed!" in captured.out
|
||||
@@ -0,0 +1,8 @@
|
||||
from setuptools import Extension, setup
|
||||
|
||||
|
||||
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 @@
|
||||
print('run_tests.py executed!')
|
||||
+45
-42
@@ -41,13 +41,15 @@ def cibuildwheel_get_build_identifiers(project_path, env=None):
|
||||
return cmd_output.strip().split('\n')
|
||||
|
||||
|
||||
def cibuildwheel_run(project_path, env=None, add_env=None, output_dir=None):
|
||||
def cibuildwheel_run(project_path, package_dir='.', 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 package_dir: path of the package to be built. Can be absolute, or
|
||||
relative to project_path.
|
||||
: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
|
||||
@@ -64,15 +66,16 @@ def cibuildwheel_run(project_path, env=None, add_env=None, output_dir=None):
|
||||
|
||||
with TemporaryDirectoryIfNone(output_dir) as _output_dir:
|
||||
subprocess.check_call(
|
||||
[sys.executable, '-m', 'cibuildwheel', '--output-dir', str(_output_dir), project_path],
|
||||
[sys.executable, '-m', 'cibuildwheel', '--output-dir', str(_output_dir), package_dir],
|
||||
env=env,
|
||||
cwd=project_path,
|
||||
)
|
||||
wheels = os.listdir(_output_dir)
|
||||
return wheels
|
||||
|
||||
|
||||
def expected_wheels(package_name, package_version, manylinux_versions=None,
|
||||
macosx_deployment_target=None):
|
||||
macosx_deployment_target='10.9'):
|
||||
'''
|
||||
Returns a list of expected wheels from a run of cibuildwheel.
|
||||
'''
|
||||
@@ -81,60 +84,60 @@ def expected_wheels(package_name, package_version, manylinux_versions=None,
|
||||
# {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()]}
|
||||
if manylinux_versions is None:
|
||||
if pm.machine() == 'x86_64':
|
||||
manylinux_versions = ['manylinux1', 'manylinux2010']
|
||||
else:
|
||||
if manylinux_versions is None:
|
||||
manylinux_versions = ['manylinux1', 'manylinux2010']
|
||||
python_abi_tags += extra_x86_python_abi_tags
|
||||
manylinux_versions = ['manylinux2014']
|
||||
|
||||
python_abi_tags = ['cp35-cp35m', 'cp36-cp36m', 'cp37-cp37m', 'cp38-cp38']
|
||||
|
||||
if pm.machine() in ['x86_64', 'AMD64', 'x86']:
|
||||
python_abi_tags += ['cp27-cp27m', 'pp27-pypy_73', 'pp36-pypy36_pp73']
|
||||
|
||||
if platform == 'linux':
|
||||
python_abi_tags.append('cp27-cp27mu') # python 2.7 has 2 different ABI on manylinux
|
||||
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]
|
||||
|
||||
wheels = []
|
||||
|
||||
for python_abi_tag in python_abi_tags:
|
||||
platform_tags = []
|
||||
|
||||
if platform == 'linux':
|
||||
architectures = [pm.machine()]
|
||||
|
||||
if pm.machine() == 'x86_64' and python_abi_tag.startswith('cp'):
|
||||
architectures.append('i686')
|
||||
|
||||
platform_tags = [
|
||||
'{}_{}'.format(manylinux_version, architecture)
|
||||
for architecture in architectures
|
||||
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']}
|
||||
elif platform == 'windows':
|
||||
if python_abi_tag.startswith('cp'):
|
||||
platform_tags = ['win32', 'win_amd64']
|
||||
else:
|
||||
platform_tags = ['win32']
|
||||
|
||||
def get_platform_tags(python_abi_tag):
|
||||
return platform_tags[python_abi_tag[:2]]
|
||||
elif platform == 'macos':
|
||||
platform_tags = ['macosx_{}_x86_64'.format(macosx_deployment_target.replace('.', '_'))]
|
||||
|
||||
elif platform == 'macos':
|
||||
python_abi_tags += extra_x86_python_abi_tags
|
||||
else:
|
||||
raise Exception('unsupported platform')
|
||||
|
||||
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(
|
||||
for platform_tag in platform_tags:
|
||||
wheels.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]
|
||||
wheels = [w for w in wheels if '-cp27-' not in w and '-pp2' not in w]
|
||||
|
||||
return templates
|
||||
return wheels
|
||||
|
||||
|
||||
platform = None
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
from cibuildwheel.util import DependencyConstraints
|
||||
import os
|
||||
|
||||
|
||||
def test_defaults():
|
||||
dependency_constraints = DependencyConstraints.with_defaults()
|
||||
|
||||
project_root = os.path.dirname(os.path.dirname(__file__))
|
||||
resources_dir = os.path.join(project_root, 'cibuildwheel', 'resources')
|
||||
|
||||
assert os.path.samefile(
|
||||
dependency_constraints.base_file_path,
|
||||
os.path.join(resources_dir, 'constraints.txt')
|
||||
)
|
||||
assert os.path.samefile(
|
||||
dependency_constraints.get_for_python_version('3.8'),
|
||||
os.path.join(resources_dir, 'constraints.txt')
|
||||
)
|
||||
assert os.path.samefile(
|
||||
dependency_constraints.get_for_python_version('3.6'),
|
||||
os.path.join(resources_dir, 'constraints-python36.txt')
|
||||
)
|
||||
assert os.path.samefile(
|
||||
dependency_constraints.get_for_python_version('3.5'),
|
||||
os.path.join(resources_dir, 'constraints-python35.txt')
|
||||
)
|
||||
assert os.path.samefile(
|
||||
dependency_constraints.get_for_python_version('2.7'),
|
||||
os.path.join(resources_dir, 'constraints-python27.txt')
|
||||
)
|
||||
@@ -18,7 +18,7 @@ class ArgsInterceptor:
|
||||
self.kwargs = kwargs
|
||||
|
||||
|
||||
MOCK_PROJECT_DIR = 'some_project_dir'
|
||||
MOCK_PACKAGE_DIR = 'some_package_dir'
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
@@ -39,20 +39,20 @@ def mock_protection(monkeypatch):
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def fake_project_dir(monkeypatch):
|
||||
def fake_package_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'):
|
||||
if path == os.path.join(MOCK_PACKAGE_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])
|
||||
monkeypatch.setattr(sys, 'argv', ['cibuildwheel', MOCK_PACKAGE_DIR])
|
||||
|
||||
|
||||
@pytest.fixture(params=['linux', 'macos', 'windows'])
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import sys
|
||||
from fnmatch import fnmatch
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -17,13 +18,13 @@ def test_output_dir(platform, intercepted_build_args, monkeypatch):
|
||||
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.kwargs['output_dir'] == OUTPUT_DIR
|
||||
assert intercepted_build_args.args[0].output_dir == OUTPUT_DIR
|
||||
|
||||
|
||||
def test_output_dir_default(platform, intercepted_build_args, monkeypatch):
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.kwargs['output_dir'] == 'wheelhouse'
|
||||
assert intercepted_build_args.args[0].output_dir == 'wheelhouse'
|
||||
|
||||
|
||||
@pytest.mark.parametrize('also_set_environment', [False, True])
|
||||
@@ -36,7 +37,7 @@ def test_output_dir_argument(also_set_environment, platform, intercepted_build_a
|
||||
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.kwargs['output_dir'] == OUTPUT_DIR
|
||||
assert intercepted_build_args.args[0].output_dir == OUTPUT_DIR
|
||||
|
||||
|
||||
def test_build_selector(platform, intercepted_build_args, monkeypatch):
|
||||
@@ -48,7 +49,7 @@ def test_build_selector(platform, intercepted_build_args, monkeypatch):
|
||||
|
||||
main()
|
||||
|
||||
intercepted_build_selector = intercepted_build_args.kwargs['build_selector']
|
||||
intercepted_build_selector = intercepted_build_args.args[0].build_selector
|
||||
assert isinstance(intercepted_build_selector, BuildSelector)
|
||||
assert intercepted_build_selector('build-this')
|
||||
assert not intercepted_build_selector('skip-that')
|
||||
@@ -57,19 +58,19 @@ def test_build_selector(platform, intercepted_build_args, monkeypatch):
|
||||
|
||||
|
||||
@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', 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', 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', 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', 'manylinux2010', 'pypywheels/manylinux2010-pypy_x86_64:*'),
|
||||
('pypy_x86_64', 'manylinux2014', 'manylinux2014'), # Does not exist (yet)
|
||||
('pypy_x86_64', 'custom_image', 'custom_image'),
|
||||
])
|
||||
@@ -80,9 +81,12 @@ def test_manylinux_images(architecture, image, full_image, platform, intercepted
|
||||
main()
|
||||
|
||||
if platform == 'linux':
|
||||
assert intercepted_build_args.kwargs['manylinux_images'][architecture] == full_image
|
||||
assert fnmatch(
|
||||
intercepted_build_args.args[0].manylinux_images[architecture],
|
||||
full_image
|
||||
)
|
||||
else:
|
||||
assert 'manylinux_images' not in intercepted_build_args.kwargs
|
||||
assert intercepted_build_args.args[0].manylinux_images is None
|
||||
|
||||
|
||||
def get_default_repair_command(platform):
|
||||
@@ -109,7 +113,7 @@ def test_repair_command(repair_command, platform_specific, platform, intercepted
|
||||
main()
|
||||
|
||||
expected_repair = repair_command or get_default_repair_command(platform)
|
||||
assert intercepted_build_args.kwargs['repair_command'] == expected_repair
|
||||
assert intercepted_build_args.args[0].repair_command == expected_repair
|
||||
|
||||
|
||||
@pytest.mark.parametrize('environment', [
|
||||
@@ -128,7 +132,7 @@ def test_environment(environment, platform_specific, platform, intercepted_build
|
||||
|
||||
main()
|
||||
|
||||
intercepted_environment = intercepted_build_args.kwargs['environment']
|
||||
intercepted_environment = intercepted_build_args.args[0].environment
|
||||
assert isinstance(intercepted_environment, ParsedEnvironment)
|
||||
assert intercepted_environment.as_dictionary(prev_environment={}) == environment
|
||||
|
||||
@@ -145,7 +149,7 @@ def test_test_requires(test_requires, platform_specific, platform, intercepted_b
|
||||
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.kwargs['test_requires'] == (test_requires or '').split()
|
||||
assert intercepted_build_args.args[0].test_requires == (test_requires or '').split()
|
||||
|
||||
|
||||
@pytest.mark.parametrize('test_extras', [None, 'extras'])
|
||||
@@ -160,7 +164,7 @@ def test_test_extras(test_extras, platform_specific, platform, intercepted_build
|
||||
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.kwargs['test_extras'] == ('[' + test_extras + ']' if test_extras else '')
|
||||
assert intercepted_build_args.args[0].test_extras == ('[' + test_extras + ']' if test_extras else '')
|
||||
|
||||
|
||||
@pytest.mark.parametrize('test_command', [None, 'test --command'])
|
||||
@@ -175,7 +179,7 @@ def test_test_command(test_command, platform_specific, platform, intercepted_bui
|
||||
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.kwargs['test_command'] == test_command
|
||||
assert intercepted_build_args.args[0].test_command == test_command
|
||||
|
||||
|
||||
@pytest.mark.parametrize('before_build', [None, 'before --build'])
|
||||
@@ -190,7 +194,7 @@ def test_before_build(before_build, platform_specific, platform, intercepted_bui
|
||||
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.kwargs['before_build'] == before_build
|
||||
assert intercepted_build_args.args[0].before_build == before_build
|
||||
|
||||
|
||||
@pytest.mark.parametrize('build_verbosity', [None, 0, 2, -2, 4, -4])
|
||||
@@ -206,7 +210,7 @@ def test_build_verbosity(build_verbosity, platform_specific, platform, intercept
|
||||
main()
|
||||
|
||||
expected_verbosity = max(-3, min(3, int(build_verbosity or 0)))
|
||||
assert intercepted_build_args.kwargs['build_verbosity'] == expected_verbosity
|
||||
assert intercepted_build_args.args[0].build_verbosity == expected_verbosity
|
||||
|
||||
|
||||
@pytest.mark.parametrize('option_name', ['CIBW_BUILD', 'CIBW_SKIP'])
|
||||
@@ -221,7 +225,7 @@ def test_build_selector_migrations(intercepted_build_args, monkeypatch, option_n
|
||||
|
||||
main()
|
||||
|
||||
intercepted_build_selector = intercepted_build_args.kwargs['build_selector']
|
||||
intercepted_build_selector = intercepted_build_args.args[0].build_selector
|
||||
assert isinstance(intercepted_build_selector, BuildSelector)
|
||||
|
||||
if option_name == 'CIBW_BUILD':
|
||||
|
||||
@@ -4,13 +4,15 @@ import pytest
|
||||
|
||||
from cibuildwheel.__main__ import main
|
||||
|
||||
from conftest import MOCK_PROJECT_DIR # noqa: I100
|
||||
from conftest import MOCK_PACKAGE_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)
|
||||
monkeypatch.delenv('CIBW_PLATFORM', raising=False)
|
||||
|
||||
with pytest.raises(SystemExit) as exit:
|
||||
main()
|
||||
@@ -24,6 +26,7 @@ def test_unknown_platform_non_ci(monkeypatch, capsys):
|
||||
def test_unknown_platform_on_ci(monkeypatch, capsys):
|
||||
monkeypatch.setenv('CI', 'true')
|
||||
monkeypatch.setattr(sys, 'platform', 'nonexistent')
|
||||
monkeypatch.delenv('CIBW_PLATFORM', raising=False)
|
||||
|
||||
with pytest.raises(SystemExit) as exit:
|
||||
main()
|
||||
@@ -50,10 +53,10 @@ def test_platform_argument(platform, intercepted_build_args, monkeypatch):
|
||||
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.kwargs['project_dir'] == MOCK_PROJECT_DIR
|
||||
assert intercepted_build_args.args[0].package_dir == MOCK_PACKAGE_DIR
|
||||
|
||||
|
||||
def test_platform_environment(platform, intercepted_build_args, monkeypatch):
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.kwargs['project_dir'] == MOCK_PROJECT_DIR
|
||||
assert intercepted_build_args.args[0].package_dir == MOCK_PACKAGE_DIR
|
||||
|
||||
Reference in New Issue
Block a user