Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ddc6d57a2 | ||
|
|
58fa77b73b | ||
|
|
048ddc4af3 | ||
|
|
c521dca40b | ||
|
|
ac2b391790 | ||
|
|
47fe71efb4 | ||
|
|
53ba9bd0d0 | ||
|
|
317397b39a | ||
|
|
8466194576 | ||
|
|
c68be58e78 | ||
|
|
fc24230cb4 | ||
|
|
c36be04ee8 | ||
|
|
7a27ae941c | ||
|
|
d0b6401129 | ||
|
|
141b3e031e | ||
|
|
0beb9518c9 | ||
|
|
ad51e796c0 | ||
|
|
db9d658b51 | ||
|
|
1d88c27530 | ||
|
|
a92959f191 | ||
|
|
321335e756 | ||
|
|
c88a73b40b | ||
|
|
4dd5c0e11c | ||
|
|
a2f4d2aa47 | ||
|
|
41891277d3 | ||
|
|
78ac83ff7e | ||
|
|
50a263df21 | ||
|
|
3ff84c52f9 | ||
|
|
e72e4683db | ||
|
|
dfd8f4809a | ||
|
|
666699a51e | ||
|
|
947059bbf4 | ||
|
|
4302090f08 | ||
|
|
c68d50a073 | ||
|
|
a03a26c90e | ||
|
|
e3b272201e | ||
|
|
43fcb31231 | ||
|
|
b4b46a1f06 | ||
|
|
2057bba07f | ||
|
|
46352c8e30 | ||
|
|
2e15fdddfd | ||
|
|
fc52c5e7b6 | ||
|
|
7b7e24d36d | ||
|
|
9f6c91404d | ||
|
|
219a65d5e3 | ||
|
|
36b8f7fff2 | ||
|
|
4d5cf4629e | ||
|
|
c1f155c0f2 | ||
|
|
3fe560cf14 | ||
|
|
75a0384d02 | ||
|
|
6501470357 | ||
|
|
8bfc313d02 | ||
|
|
e6ac527416 | ||
|
|
c27c5feadb | ||
|
|
2b22ffda9b | ||
|
|
9a1f8a99e0 | ||
|
|
d552d29eae | ||
|
|
aa99405a9c | ||
|
|
39101ae8ea | ||
|
|
1a83967261 | ||
|
|
d43817b49a | ||
|
|
3b1f0dd453 | ||
|
|
a1db0f7ebb | ||
|
|
acf5c24881 | ||
|
|
f012fa1af0 | ||
|
|
b62b17a223 | ||
|
|
265708854a | ||
|
|
e58aed1731 | ||
|
|
b3ffb04a2c | ||
|
|
c53e7824a7 | ||
|
|
46a39b06cb | ||
|
|
a7f73f1593 | ||
|
|
47323761dd | ||
|
|
43fbcfe008 | ||
|
|
5c19ff3f95 | ||
|
|
02e9ee3e96 | ||
|
|
df07aafae1 | ||
|
|
78edb0a06a | ||
|
|
901ac79dbc | ||
|
|
0597088f64 | ||
|
|
f1c4ebbed0 | ||
|
|
fd9e3202a0 | ||
|
|
7afdd40714 | ||
|
|
ed491d0e22 | ||
|
|
e12ca14006 | ||
|
|
58fc418563 | ||
|
|
57e6f0f56d | ||
|
|
cc8dbbe8b3 | ||
|
|
bb5aac3d69 | ||
|
|
374e88382b | ||
|
|
c74acd6363 | ||
|
|
90023a3a70 | ||
|
|
0b82ab6674 | ||
|
|
fb0e192681 | ||
|
|
eedc5c6b79 | ||
|
|
31c9247921 | ||
|
|
4c3cbf702c | ||
|
|
a06998e2f6 | ||
|
|
4e15e202c8 | ||
|
|
35ea7b6307 | ||
|
|
46688ae3f9 | ||
|
|
9c3f44d086 | ||
|
|
896337cbb1 | ||
|
|
a0dab888a7 | ||
|
|
d7cffde00f | ||
|
|
562d81c6ea | ||
|
|
66b392f4ee | ||
|
|
dd3c87ece2 | ||
|
|
63f7a2ddc4 | ||
|
|
ca49d86ed8 | ||
|
|
d9a317c370 | ||
|
|
add792de30 | ||
|
|
004fc03899 | ||
|
|
15f1678439 | ||
|
|
3de38fc679 | ||
|
|
63eda18cf9 | ||
|
|
a15a5f1c0f | ||
|
|
d41cf65173 | ||
|
|
e97b103148 | ||
|
|
4e6246d2a7 | ||
|
|
ab4f5209b5 | ||
|
|
8a25718c71 | ||
|
|
a8bdac07e6 | ||
|
|
2790fd1dd7 | ||
|
|
f9401d94c2 | ||
|
|
4fb7874ca3 | ||
|
|
bb1b050ab5 | ||
|
|
e8d857db21 | ||
|
|
f3a136be78 | ||
|
|
2c564dfb9c | ||
|
|
901f708d4c | ||
|
|
edd461ad42 |
@@ -1,6 +1,8 @@
|
|||||||
|
set -o errexit
|
||||||
|
set -o xtrace
|
||||||
|
|
||||||
$PYTHON --version
|
$PYTHON --version
|
||||||
$PYTHON -m pip --version
|
$PYTHON -m pip --version
|
||||||
$PYTHON -m pip install -q --user --ignore-installed --upgrade virtualenv
|
|
||||||
$PYTHON -m virtualenv -p $PYTHON venv
|
$PYTHON -m virtualenv -p $PYTHON venv
|
||||||
venv/bin/python -m pip install -r requirements-dev.txt
|
venv/bin/python -m pip install -r requirements-dev.txt
|
||||||
venv/bin/python -m pip freeze
|
venv/bin/python -m pip freeze
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# config file for https://github.com/bobvanderlinden/probot-auto-merge
|
||||||
|
|
||||||
|
# no approvals required, just the label is enough
|
||||||
|
minApprovals: {}
|
||||||
|
|
||||||
|
requiredLabels:
|
||||||
|
- merge when green
|
||||||
@@ -18,8 +18,8 @@ jobs:
|
|||||||
os: [ubuntu-18.04, windows-latest, macos-latest]
|
os: [ubuntu-18.04, windows-latest, macos-latest]
|
||||||
python_version: ['3.7']
|
python_version: ['3.7']
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/setup-python@v1
|
- uses: actions/setup-python@v2
|
||||||
name: Install Python ${{ matrix.python_version }}
|
name: Install Python ${{ matrix.python_version }}
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python_version }}
|
python-version: ${{ matrix.python_version }}
|
||||||
@@ -28,7 +28,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
python -m pip install -r requirements-dev.txt
|
python -m pip install -r requirements-dev.txt
|
||||||
- name: Install Visual C++ for Python 2.7
|
- name: Install Visual C++ for Python 2.7
|
||||||
if: startsWith(matrix.os, 'windows')
|
if: runner.os == 'Windows'
|
||||||
run: |
|
run: |
|
||||||
choco install vcpython27 -f -y
|
choco install vcpython27 -f -y
|
||||||
- name: Test cibuildwheel
|
- name: Test cibuildwheel
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
repos:
|
||||||
|
- repo: https://gitlab.com/pycqa/flake8
|
||||||
|
rev: 3.8.1
|
||||||
|
hooks:
|
||||||
|
- id: flake8
|
||||||
|
pass_filenames: true
|
||||||
|
# this seems to need to be here in addition to setup.cfg
|
||||||
|
exclude: cibuildwheel/resources/
|
||||||
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
|
rev: v2.3.0
|
||||||
|
hooks:
|
||||||
|
- id: check-yaml
|
||||||
|
- id: check-merge-conflict
|
||||||
|
- id: end-of-file-fixer
|
||||||
|
exclude: cibuildwheel/resources/pinned_docker_images.cfg
|
||||||
|
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||||
|
rev: '' # Use the sha / tag you want to point at
|
||||||
|
hooks:
|
||||||
|
- id: mypy
|
||||||
|
files: cibuildwheel|test
|
||||||
|
exclude: cibuildwheel/resources
|
||||||
@@ -35,6 +35,8 @@ jobs:
|
|||||||
language: shell
|
language: shell
|
||||||
before_install:
|
before_install:
|
||||||
- choco install python3 --version 3.6.8 --no-progress -y
|
- choco install python3 --version 3.6.8 --no-progress -y
|
||||||
|
# Update root certificates to fix SSL error; see http://www.chawn.com/RootCerts.htm
|
||||||
|
- powershell "md C:\temp\certs; CertUtil -generateSSTFromWU C:\temp\certs\RootStore.sst; Get-ChildItem -Path C:\\temp\certs\Rootstore.sst | Import-Certificate -CertStoreLocation Cert:\\LocalMachine\\Root\\ | out-null"
|
||||||
env:
|
env:
|
||||||
- PYTHON=C:\\Python36\\python
|
- PYTHON=C:\\Python36\\python
|
||||||
|
|
||||||
|
|||||||
@@ -22,11 +22,13 @@ What does it do?
|
|||||||
| CPython 3.6 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅² |
|
| CPython 3.6 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅² |
|
||||||
| CPython 3.7 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅² |
|
| CPython 3.7 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅² |
|
||||||
| CPython 3.8 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅² |
|
| CPython 3.8 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅² |
|
||||||
|
| CPython 3.9³ | 🛠 | 🛠 | 🛠 | 🛠 | 🛠 | 🛠 | 🛠 | |
|
||||||
| PyPy 2.7 v7.3.0 | ✅ | | ✅ | ✅ | | | | |
|
| PyPy 2.7 v7.3.0 | ✅ | | ✅ | ✅ | | | | |
|
||||||
| PyPy 3.6 v7.3.0 | ✅ | | ✅ | ✅ | | | | |
|
| PyPy 3.6 v7.3.0 | ✅ | | ✅ | ✅ | | | | |
|
||||||
|
|
||||||
<sup>¹ Not supported on Travis</sup><br>
|
<sup>¹ Not supported on Travis</sup><br>
|
||||||
<sup>² Beta support until Travis CI fixes <a href="https://travis-ci.community/t/no-space-left-on-device-for-system-z/5954/11">a bug</a></sup>
|
<sup>² Beta support until Travis CI fixes <a href="https://travis-ci.community/t/no-space-left-on-device-for-system-z/5954/11">a bug</a></sup><br>
|
||||||
|
<sup>³ Python 3.9 is not yet ABI stable, so you shouldn't publish wheels with it yet. But if you want to check that your wheels build on Python 3.9, try our [`python3.9` branch](https://github.com/joerick/cibuildwheel/pull/382)!</sup>
|
||||||
|
|
||||||
- Builds manylinux, macOS and Windows wheels for CPython and PyPy using Azure Pipelines, Travis CI, AppVeyor, and CircleCI
|
- Builds manylinux, macOS and Windows wheels for CPython and PyPy using Azure Pipelines, Travis CI, AppVeyor, and CircleCI
|
||||||
- Bundles shared library dependencies on Linux and macOS through [auditwheel](https://github.com/pypa/auditwheel) and [delocate](https://github.com/matthew-brett/delocate)
|
- Bundles shared library dependencies on Linux and macOS through [auditwheel](https://github.com/pypa/auditwheel) and [delocate](https://github.com/matthew-brett/delocate)
|
||||||
@@ -77,7 +79,7 @@ env:
|
|||||||
# Note: TWINE_PASSWORD is set to a PyPI API token in Travis settings
|
# Note: TWINE_PASSWORD is set to a PyPI API token in Travis settings
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- python3 -m pip install cibuildwheel==1.4.2
|
- python3 -m pip install cibuildwheel==1.5.2
|
||||||
|
|
||||||
script:
|
script:
|
||||||
# build the wheels, put them into './wheelhouse'
|
# build the wheels, put them into './wheelhouse'
|
||||||
@@ -92,7 +94,7 @@ after_success:
|
|||||||
fi
|
fi
|
||||||
```
|
```
|
||||||
|
|
||||||
For more information, including how to build on Appveyor, Azure, CircleCI, check out the [documentation](https://cibuildwheel.readthedocs.org) and also check out [the examples](https://github.com/joerick/cibuildwheel/tree/master/examples).
|
For more information, including how to build on GitHub Actions, Appveyor, Azure Pipelines, or CircleCI, check out the [documentation](https://cibuildwheel.readthedocs.org) and the [examples](https://github.com/joerick/cibuildwheel/tree/master/examples).
|
||||||
|
|
||||||
Options
|
Options
|
||||||
-------
|
-------
|
||||||
@@ -100,17 +102,20 @@ Options
|
|||||||
| | Option | Description |
|
| | Option | Description |
|
||||||
|---|--------|-------------|
|
|---|--------|-------------|
|
||||||
| **Build selection** | [`CIBW_PLATFORM`](https://cibuildwheel.readthedocs.io/en/stable/options/#platform) | Override the auto-detected target platform |
|
| **Build selection** | [`CIBW_PLATFORM`](https://cibuildwheel.readthedocs.io/en/stable/options/#platform) | Override the auto-detected target platform |
|
||||||
| | [`CIBW_BUILD`](https://cibuildwheel.readthedocs.io/en/stable/options/#build-skip) [`CIBW_SKIP`](https://cibuildwheel.readthedocs.io/en/stable/options/#build-skip) | Choose the Python versions to build |
|
| | [`CIBW_BUILD`](https://cibuildwheel.readthedocs.io/en/stable/options/#build-skip) <br> [`CIBW_SKIP`](https://cibuildwheel.readthedocs.io/en/stable/options/#build-skip) | Choose the Python versions to build |
|
||||||
| **Build environment** | [`CIBW_ENVIRONMENT`](https://cibuildwheel.readthedocs.io/en/stable/options/#environment) | Set environment variables needed during the build |
|
| **Build customization** | [`CIBW_ENVIRONMENT`](https://cibuildwheel.readthedocs.io/en/stable/options/#environment) | Set environment variables needed during the build |
|
||||||
|
| | [`CIBW_BEFORE_ALL`](https://cibuildwheel.readthedocs.io/en/stable/options/#before-all) | Execute a shell command on the build system before any wheels are built. |
|
||||||
| | [`CIBW_BEFORE_BUILD`](https://cibuildwheel.readthedocs.io/en/stable/options/#before-build) | Execute a shell command preparing each wheel's build |
|
| | [`CIBW_BEFORE_BUILD`](https://cibuildwheel.readthedocs.io/en/stable/options/#before-build) | Execute a shell command preparing each wheel's build |
|
||||||
| | [`CIBW_REPAIR_WHEEL_COMMAND`](https://cibuildwheel.readthedocs.io/en/stable/options/#repair-wheel-command) | Execute a shell command to repair each (non-pure Python) built wheel |
|
| | [`CIBW_REPAIR_WHEEL_COMMAND`](https://cibuildwheel.readthedocs.io/en/stable/options/#repair-wheel-command) | Execute a shell command to repair each (non-pure Python) built wheel |
|
||||||
| | [`CIBW_MANYLINUX_X86_64_IMAGE`](https://cibuildwheel.readthedocs.io/en/stable/options/#manylinux-image) [`CIBW_MANYLINUX_I686_IMAGE`](https://cibuildwheel.readthedocs.io/en/stable/options/#manylinux-image) [`CIBW_MANYLINUX_PYPY_X86_64_IMAGE`](https://cibuildwheel.readthedocs.io/en/stable/options/#manylinux-image) | Specify alternative manylinux docker images |
|
| | [`CIBW_MANYLINUX_X86_64_IMAGE`](https://cibuildwheel.readthedocs.io/en/stable/options/#manylinux-image) <br> [`CIBW_MANYLINUX_I686_IMAGE`](https://cibuildwheel.readthedocs.io/en/stable/options/#manylinux-image) <br> [`CIBW_MANYLINUX_PYPY_X86_64_IMAGE`](https://cibuildwheel.readthedocs.io/en/stable/options/#manylinux-image) <br> [`CIBW_MANYLINUX_AARCH64_IMAGE`](https://cibuildwheel.readthedocs.io/en/stable/options/#manylinux-image) <br> [`CIBW_MANYLINUX_PPC64LE_IMAGE`](https://cibuildwheel.readthedocs.io/en/stable/options/#manylinux-image) <br> [`CIBW_MANYLINUX_S390X_IMAGE`](https://cibuildwheel.readthedocs.io/en/stable/options/#manylinux-image) | Specify alternative manylinux docker images |
|
||||||
|
| | [`CIBW_DEPENDENCY_VERSIONS`](https://cibuildwheel.readthedocs.io/en/stable/options/#dependency-versions) | Specify how cibuildwheel controls the versions of the tools it uses |
|
||||||
| **Testing** | [`CIBW_TEST_COMMAND`](https://cibuildwheel.readthedocs.io/en/stable/options/#test-command) | Execute a shell command to test each built wheel |
|
| **Testing** | [`CIBW_TEST_COMMAND`](https://cibuildwheel.readthedocs.io/en/stable/options/#test-command) | Execute a shell command to test each built wheel |
|
||||||
| | [`CIBW_BEFORE_TEST`](https://cibuildwheel.readthedocs.io/en/stable/options/#before-test) | Execute shell command to prepare test environment |
|
| | [`CIBW_BEFORE_TEST`](https://cibuildwheel.readthedocs.io/en/stable/options/#before-test) | Execute a shell command before testing each wheel |
|
||||||
| | [`CIBW_TEST_REQUIRES`](https://cibuildwheel.readthedocs.io/en/stable/options/#test-requires) | Install Python dependencies before running the tests |
|
| | [`CIBW_TEST_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 |
|
| | [`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 |
|
| **Other** | [`CIBW_BUILD_VERBOSITY`](https://cibuildwheel.readthedocs.io/en/stable/options/#build-verbosity) | Increase/decrease the output of pip wheel |
|
||||||
|
|
||||||
|
|
||||||
Working examples
|
Working examples
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
@@ -147,6 +152,39 @@ This is similar to static linking, so it might have some licence implications. C
|
|||||||
Changelog
|
Changelog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
### 1.5.2
|
||||||
|
|
||||||
|
_8 July 2020_
|
||||||
|
|
||||||
|
- 🐛 Fix an issue on Windows where pyproject.toml would cause an error when
|
||||||
|
some requirements formats were used. (#401)
|
||||||
|
- 🛠 Update CPython 3.7 to 3.7.8 (#394)
|
||||||
|
|
||||||
|
### 1.5.1
|
||||||
|
|
||||||
|
_25 June 2020_
|
||||||
|
|
||||||
|
- 🐛 Fix "OSError: [WinError 17] The system cannot move the file to a different
|
||||||
|
disk drive" on Github Actions (#388, #389)
|
||||||
|
|
||||||
|
### 1.5.0
|
||||||
|
|
||||||
|
_24 June 2020_
|
||||||
|
|
||||||
|
- 🌟 Add [`CIBW_BEFORE_ALL`](https://cibuildwheel.readthedocs.io/en/stable/options/#before-all)
|
||||||
|
option, which lets you run a command on the build machine before any wheels
|
||||||
|
are built. This is especially useful when building on Linux, to `make`
|
||||||
|
something external to Python, or to `yum install` a dependency. (#342)
|
||||||
|
- ✨ Added support for projects using pyproject.toml instead of setup.py
|
||||||
|
(#360, #358)
|
||||||
|
- ✨ Added workaround to allow Python 3.5 on Windows to pull dependencies from
|
||||||
|
pyproject.toml. (#358)
|
||||||
|
- 📚 Improved Github Actions examples and docs (#354, #362)
|
||||||
|
- 🐛 Ensure pip wheel uses the specified package, and doesn't build a wheel
|
||||||
|
from PyPI (#369)
|
||||||
|
- 🛠 Internal changes: using pathlib.Path, precommit hooks, testing
|
||||||
|
improvements.
|
||||||
|
|
||||||
### 1.4.2
|
### 1.4.2
|
||||||
|
|
||||||
_25 May 2020_
|
_25 May 2020_
|
||||||
@@ -169,7 +207,7 @@ _4 May 2020_
|
|||||||
|
|
||||||
_2 May 2020_
|
_2 May 2020_
|
||||||
|
|
||||||
- ✨ Deterministic builds. cibuildwheel now locks the versions of the tools it
|
- 🌟 Deterministic builds. cibuildwheel now locks the versions of the tools it
|
||||||
uses. This means that pinning your version of cibuildwheel pins the versions
|
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
|
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
|
make things more reliable. But note that we don't control the entire build
|
||||||
@@ -199,7 +237,7 @@ _2 May 2020_
|
|||||||
|
|
||||||
_12 March 2020_
|
_12 March 2020_
|
||||||
|
|
||||||
- ✨ Add support for building on Github Actions! Check out the
|
- 🌟 Add support for building on Github Actions! Check out the
|
||||||
[docs](https://cibuildwheel.readthedocs.io/en/stable/setup/#github-actions)
|
[docs](https://cibuildwheel.readthedocs.io/en/stable/setup/#github-actions)
|
||||||
for information on how to set it up. (#194)
|
for information on how to set it up. (#194)
|
||||||
- ✨ Add the `CIBW_BEFORE_TEST` option, which lets you run a command to
|
- ✨ Add the `CIBW_BEFORE_TEST` option, which lets you run a command to
|
||||||
@@ -209,9 +247,9 @@ _12 March 2020_
|
|||||||
|
|
||||||
_8 March 2020_
|
_8 March 2020_
|
||||||
|
|
||||||
- ✨ Add support for building PyPy wheels, across Manylinux, macOS, and
|
- 🌟 Add support for building PyPy wheels, across Manylinux, macOS, and
|
||||||
Windows. (#185)
|
Windows. (#185)
|
||||||
- ✨ Added the ability to build ARM64 (aarch64), ppc64le, and s390x wheels,
|
- 🌟 Added the ability to build ARM64 (aarch64), ppc64le, and s390x wheels,
|
||||||
using manylinux2014 and Travis CI. (#273)
|
using manylinux2014 and Travis CI. (#273)
|
||||||
- ✨ You can now build macOS wheels on Appveyor. (#230)
|
- ✨ You can now build macOS wheels on Appveyor. (#230)
|
||||||
- 🛠 Changed default macOS minimum target to 10.9, from 10.6. This allows the
|
- 🛠 Changed default macOS minimum target to 10.9, from 10.6. This allows the
|
||||||
@@ -232,7 +270,7 @@ _8 March 2020_
|
|||||||
|
|
||||||
_7 December 2019_
|
_7 December 2019_
|
||||||
|
|
||||||
- ✨ Add support for building manylinux2014 wheels. To use, set
|
- 🌟 Add support for building manylinux2014 wheels. To use, set
|
||||||
`CIBW_MANYLINUX_X86_64_IMAGE` and CIBW_MANYLINUX_I686_IMAGE to
|
`CIBW_MANYLINUX_X86_64_IMAGE` and CIBW_MANYLINUX_I686_IMAGE to
|
||||||
`manylinux2014`.
|
`manylinux2014`.
|
||||||
- ✨ Add support for [Linux on Appveyor](https://www.appveyor.com/blog/2018/03/06/appveyor-for-linux/) (#204, #207)
|
- ✨ Add support for [Linux on Appveyor](https://www.appveyor.com/blog/2018/03/06/appveyor-for-linux/) (#204, #207)
|
||||||
@@ -245,8 +283,8 @@ _7 December 2019_
|
|||||||
|
|
||||||
_10 November 2019_
|
_10 November 2019_
|
||||||
|
|
||||||
- ✨ Add support for building Python 3.8 wheels! (#180)
|
- 🌟 Add support for building Python 3.8 wheels! (#180)
|
||||||
- ✨ Add support for building manylinux2010 wheels. cibuildwheel will now
|
- 🌟 Add support for building manylinux2010 wheels. cibuildwheel will now
|
||||||
build using the manylinux2010 images by default. If your project is still
|
build using the manylinux2010 images by default. If your project is still
|
||||||
manylinux1 compatible, you should get both manylinux1 and manylinux2010
|
manylinux1 compatible, you should get both manylinux1 and manylinux2010
|
||||||
wheels - you can upload both to PyPI. If you always require manylinux1 wheels, you can
|
wheels - you can upload both to PyPI. If you always require manylinux1 wheels, you can
|
||||||
@@ -282,7 +320,7 @@ _28 May 2019_
|
|||||||
|
|
||||||
_26 May 2019_
|
_26 May 2019_
|
||||||
|
|
||||||
- ✨ Add support for building on Azure pipelines! This lets you build all
|
- 🌟 Add support for building on Azure pipelines! This lets you build all
|
||||||
Linux, Mac and Windows wheels on one service, so it promises to be the
|
Linux, Mac and Windows wheels on one service, so it promises to be the
|
||||||
easiest to set up! Check out the quickstart in the docs, or
|
easiest to set up! Check out the quickstart in the docs, or
|
||||||
[cibuildwheel-azure-example](https://github.com/joerick/cibuildwheel-azure-example)
|
[cibuildwheel-azure-example](https://github.com/joerick/cibuildwheel-azure-example)
|
||||||
@@ -312,8 +350,8 @@ _3 February 2019_
|
|||||||
|
|
||||||
_23 September 2018_
|
_23 September 2018_
|
||||||
|
|
||||||
- ✨ Add `CIBW_BUILD` option, for specifying which specific builds to perform (#101)
|
- 🌟 Add `CIBW_BUILD` option, for specifying which specific builds to perform (#101)
|
||||||
- ✨ Add support for building Mac and Linux on CircleCI (#91, #97)
|
- 🌟 Add support for building Mac and Linux on CircleCI (#91, #97)
|
||||||
- 🛠 Improved support for building universal wheels (#95)
|
- 🛠 Improved support for building universal wheels (#95)
|
||||||
- 🛠 Ensure log output is unbuffered and therefore in the correct order (#92)
|
- 🛠 Ensure log output is unbuffered and therefore in the correct order (#92)
|
||||||
- 🛠 Improved error reporting for errors that occur inside a package's setup.py (#88)
|
- 🛠 Improved error reporting for errors that occur inside a package's setup.py (#88)
|
||||||
|
|||||||
@@ -10,6 +10,11 @@ environment:
|
|||||||
- APPVEYOR_BUILD_WORKER_IMAGE: macos-mojave
|
- APPVEYOR_BUILD_WORKER_IMAGE: macos-mojave
|
||||||
APPVEYOR_JOB_NAME: "python37-x64-macos-mojave"
|
APPVEYOR_JOB_NAME: "python37-x64-macos-mojave"
|
||||||
|
|
||||||
|
# while pypy libffi dependency is failing the build, allow failures on macos
|
||||||
|
matrix:
|
||||||
|
allow_failures:
|
||||||
|
- APPVEYOR_BUILD_WORKER_IMAGE: macos-mojave
|
||||||
|
|
||||||
stack: python 3.7
|
stack: python 3.7
|
||||||
|
|
||||||
build: off
|
build: off
|
||||||
|
|||||||
@@ -42,4 +42,3 @@ jobs:
|
|||||||
- bash: |
|
- bash: |
|
||||||
python -m pip install -r requirements-dev.txt pytest-azurepipelines
|
python -m pip install -r requirements-dev.txt pytest-azurepipelines
|
||||||
python ./bin/run_tests.py
|
python ./bin/run_tests.py
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -3,10 +3,11 @@
|
|||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# move cwd to the project root
|
# move cwd to the project root
|
||||||
os.chdir(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
os.chdir(Path(__file__).resolve().parents[1])
|
||||||
|
|
||||||
# run the unit tests
|
# run the unit tests
|
||||||
subprocess.check_call([sys.executable, '-m', 'pytest', 'unit_test'])
|
subprocess.check_call([sys.executable, '-m', 'pytest', 'unit_test'])
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
__version__ = '1.4.2'
|
__version__ = '1.5.2'
|
||||||
|
|||||||
+35
-29
@@ -4,6 +4,7 @@ import sys
|
|||||||
import textwrap
|
import textwrap
|
||||||
import traceback
|
import traceback
|
||||||
from configparser import ConfigParser
|
from configparser import ConfigParser
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from typing import Any, Dict, List, Optional, overload
|
from typing import Any, Dict, List, Optional, overload
|
||||||
|
|
||||||
@@ -20,6 +21,7 @@ from cibuildwheel.util import (
|
|||||||
BuildSelector,
|
BuildSelector,
|
||||||
DependencyConstraints,
|
DependencyConstraints,
|
||||||
Unbuffered,
|
Unbuffered,
|
||||||
|
resources_dir,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -114,31 +116,44 @@ def main() -> None:
|
|||||||
file=sys.stderr)
|
file=sys.stderr)
|
||||||
exit(2)
|
exit(2)
|
||||||
|
|
||||||
output_dir = args.output_dir
|
package_dir = Path(args.package_dir)
|
||||||
test_command = get_option_from_environment('CIBW_TEST_COMMAND', platform=platform)
|
output_dir = Path(args.output_dir)
|
||||||
test_requires = get_option_from_environment('CIBW_TEST_REQUIRES', platform=platform, default='').split()
|
|
||||||
test_extras = get_option_from_environment('CIBW_TEST_EXTRAS', platform=platform, default='')
|
|
||||||
package_dir = args.package_dir
|
|
||||||
before_build = get_option_from_environment('CIBW_BEFORE_BUILD', platform=platform)
|
|
||||||
build_verbosity_str = get_option_from_environment('CIBW_BUILD_VERBOSITY', platform=platform, default='')
|
|
||||||
build_config, skip_config = os.environ.get('CIBW_BUILD', '*'), os.environ.get('CIBW_SKIP', '')
|
|
||||||
if platform == 'linux':
|
if platform == 'linux':
|
||||||
repair_command_default = 'auditwheel repair -w {dest_dir} {wheel}'
|
repair_command_default = 'auditwheel repair -w {dest_dir} {wheel}'
|
||||||
elif platform == 'macos':
|
elif platform == 'macos':
|
||||||
repair_command_default = 'delocate-listdeps {wheel} && delocate-wheel --require-archs x86_64 -w {dest_dir} {wheel}'
|
repair_command_default = 'delocate-listdeps {wheel} && delocate-wheel --require-archs x86_64 -w {dest_dir} {wheel}'
|
||||||
else:
|
else:
|
||||||
repair_command_default = ''
|
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='')
|
|
||||||
|
|
||||||
|
build_config, skip_config = os.environ.get('CIBW_BUILD', '*'), os.environ.get('CIBW_SKIP', '')
|
||||||
|
environment_config = get_option_from_environment('CIBW_ENVIRONMENT', platform=platform, default='')
|
||||||
|
before_all = get_option_from_environment('CIBW_BEFORE_ALL', platform=platform, default='')
|
||||||
|
before_build = get_option_from_environment('CIBW_BEFORE_BUILD', platform=platform)
|
||||||
|
repair_command = get_option_from_environment('CIBW_REPAIR_WHEEL_COMMAND', platform=platform, default=repair_command_default)
|
||||||
dependency_versions = get_option_from_environment('CIBW_DEPENDENCY_VERSIONS', platform=platform, default='pinned')
|
dependency_versions = get_option_from_environment('CIBW_DEPENDENCY_VERSIONS', platform=platform, default='pinned')
|
||||||
|
test_command = get_option_from_environment('CIBW_TEST_COMMAND', platform=platform)
|
||||||
|
before_test = get_option_from_environment('CIBW_BEFORE_TEST', platform=platform)
|
||||||
|
test_requires = get_option_from_environment('CIBW_TEST_REQUIRES', platform=platform, default='').split()
|
||||||
|
test_extras = get_option_from_environment('CIBW_TEST_EXTRAS', platform=platform, default='')
|
||||||
|
build_verbosity_str = get_option_from_environment('CIBW_BUILD_VERBOSITY', platform=platform, default='')
|
||||||
|
|
||||||
|
build_selector = BuildSelector(build_config, skip_config)
|
||||||
|
|
||||||
|
try:
|
||||||
|
environment = parse_environment(environment_config)
|
||||||
|
except (EnvironmentParseError, ValueError):
|
||||||
|
print(f'cibuildwheel: Malformed environment option "{environment_config}"', file=sys.stderr)
|
||||||
|
traceback.print_exc(None, sys.stderr)
|
||||||
|
exit(2)
|
||||||
|
|
||||||
if dependency_versions == 'pinned':
|
if dependency_versions == 'pinned':
|
||||||
dependency_constraints: Optional[DependencyConstraints] = DependencyConstraints.with_defaults()
|
dependency_constraints: Optional[DependencyConstraints] = DependencyConstraints.with_defaults()
|
||||||
elif dependency_versions == 'latest':
|
elif dependency_versions == 'latest':
|
||||||
dependency_constraints = None
|
dependency_constraints = None
|
||||||
else:
|
else:
|
||||||
dependency_constraints = DependencyConstraints(dependency_versions)
|
dependency_versions_path = Path(dependency_versions)
|
||||||
|
dependency_constraints = DependencyConstraints(dependency_versions_path)
|
||||||
|
|
||||||
if test_extras:
|
if test_extras:
|
||||||
test_extras = f'[{test_extras}]'
|
test_extras = f'[{test_extras}]'
|
||||||
@@ -148,21 +163,13 @@ def main() -> None:
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
build_verbosity = 0
|
build_verbosity = 0
|
||||||
|
|
||||||
try:
|
|
||||||
environment = parse_environment(environment_config)
|
|
||||||
except (EnvironmentParseError, ValueError):
|
|
||||||
print(f'cibuildwheel: Malformed environment option "{environment_config}"', file=sys.stderr)
|
|
||||||
traceback.print_exc(None, sys.stderr)
|
|
||||||
exit(2)
|
|
||||||
|
|
||||||
build_selector = BuildSelector(build_config, skip_config)
|
|
||||||
|
|
||||||
# Add CIBUILDWHEEL environment variable
|
# Add CIBUILDWHEEL environment variable
|
||||||
# This needs to be passed on to the docker container in linux.py
|
# This needs to be passed on to the docker container in linux.py
|
||||||
os.environ['CIBUILDWHEEL'] = '1'
|
os.environ['CIBUILDWHEEL'] = '1'
|
||||||
|
|
||||||
if not os.path.exists(os.path.join(package_dir, 'setup.py')):
|
if not any((package_dir / name).exists()
|
||||||
print('cibuildwheel: Could not find setup.py at root of package', file=sys.stderr)
|
for name in ["setup.py", "setup.cfg", "pyproject.toml"]):
|
||||||
|
print('cibuildwheel: Could not find setup.py, setup.cfg or pyproject.toml at root of package', file=sys.stderr)
|
||||||
exit(2)
|
exit(2)
|
||||||
|
|
||||||
if args.print_build_identifiers:
|
if args.print_build_identifiers:
|
||||||
@@ -171,9 +178,7 @@ def main() -> None:
|
|||||||
|
|
||||||
manylinux_images: Optional[Dict[str, str]] = None
|
manylinux_images: Optional[Dict[str, str]] = None
|
||||||
if platform == 'linux':
|
if platform == 'linux':
|
||||||
pinned_docker_images_file = os.path.join(
|
pinned_docker_images_file = resources_dir / 'pinned_docker_images.cfg'
|
||||||
os.path.dirname(__file__), 'resources', 'pinned_docker_images.cfg'
|
|
||||||
)
|
|
||||||
all_pinned_docker_images = ConfigParser()
|
all_pinned_docker_images = ConfigParser()
|
||||||
all_pinned_docker_images.read(pinned_docker_images_file)
|
all_pinned_docker_images.read(pinned_docker_images_file)
|
||||||
# all_pinned_docker_images looks like a dict of dicts, e.g.
|
# all_pinned_docker_images looks like a dict of dicts, e.g.
|
||||||
@@ -206,12 +211,13 @@ def main() -> None:
|
|||||||
test_command=test_command,
|
test_command=test_command,
|
||||||
test_requires=test_requires,
|
test_requires=test_requires,
|
||||||
test_extras=test_extras,
|
test_extras=test_extras,
|
||||||
|
before_test=before_test,
|
||||||
before_build=before_build,
|
before_build=before_build,
|
||||||
|
before_all=before_all,
|
||||||
build_verbosity=build_verbosity,
|
build_verbosity=build_verbosity,
|
||||||
build_selector=build_selector,
|
build_selector=build_selector,
|
||||||
repair_command=repair_command,
|
repair_command=repair_command,
|
||||||
environment=environment,
|
environment=environment,
|
||||||
before_test=before_test,
|
|
||||||
dependency_constraints=dependency_constraints,
|
dependency_constraints=dependency_constraints,
|
||||||
manylinux_images=manylinux_images,
|
manylinux_images=manylinux_images,
|
||||||
)
|
)
|
||||||
@@ -221,8 +227,8 @@ def main() -> None:
|
|||||||
|
|
||||||
print_preamble(platform, build_options)
|
print_preamble(platform, build_options)
|
||||||
|
|
||||||
if not os.path.exists(output_dir):
|
if not output_dir.exists():
|
||||||
os.makedirs(output_dir)
|
output_dir.mkdir(parents=True)
|
||||||
|
|
||||||
if platform == 'linux':
|
if platform == 'linux':
|
||||||
cibuildwheel.linux.build(build_options)
|
cibuildwheel.linux.build(build_options)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import bashlex # type: ignore
|
import bashlex # type: ignore
|
||||||
|
|
||||||
from typing import Dict, List
|
from typing import Dict, List, Mapping
|
||||||
|
|
||||||
from . import bashlex_eval
|
from . import bashlex_eval
|
||||||
|
|
||||||
@@ -61,8 +61,8 @@ class ParsedEnvironment:
|
|||||||
def __init__(self, assignments: List[EnvironmentAssignment]):
|
def __init__(self, assignments: List[EnvironmentAssignment]):
|
||||||
self.assignments = assignments
|
self.assignments = assignments
|
||||||
|
|
||||||
def as_dictionary(self, prev_environment: Dict[str, str]) -> Dict[str, str]:
|
def as_dictionary(self, prev_environment: Mapping[str, str]) -> Dict[str, str]:
|
||||||
environment = prev_environment.copy()
|
environment = dict(**prev_environment)
|
||||||
|
|
||||||
for assignment in self.assignments:
|
for assignment in self.assignments:
|
||||||
value = assignment.evaluated_value(environment=environment)
|
value = assignment.evaluated_value(environment=environment)
|
||||||
|
|||||||
+42
-21
@@ -5,6 +5,7 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
import textwrap
|
import textwrap
|
||||||
import uuid
|
import uuid
|
||||||
|
from pathlib import Path, PurePath
|
||||||
|
|
||||||
from typing import List, NamedTuple, Optional, Union
|
from typing import List, NamedTuple, Optional, Union
|
||||||
|
|
||||||
@@ -104,32 +105,58 @@ def build(options: BuildOptions) -> None:
|
|||||||
('pp', 'manylinux_x86_64', options.manylinux_images['pypy_x86_64']),
|
('pp', 'manylinux_x86_64', options.manylinux_images['pypy_x86_64']),
|
||||||
]
|
]
|
||||||
|
|
||||||
if not os.path.realpath(options.package_dir).startswith(os.path.realpath('.')):
|
cwd = Path.cwd()
|
||||||
|
abs_package_dir = options.package_dir.resolve()
|
||||||
|
if cwd != abs_package_dir and cwd not in abs_package_dir.parents:
|
||||||
raise Exception('package_dir must be inside the working directory')
|
raise Exception('package_dir must be inside the working directory')
|
||||||
|
|
||||||
container_package_dir = os.path.join('/project', os.path.relpath(options.package_dir, '.'))
|
container_package_dir = PurePath('/project') / abs_package_dir.relative_to(cwd)
|
||||||
|
|
||||||
for implementation, platform_tag, docker_image in platforms:
|
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)]
|
platform_configs = [c for c in python_configurations if c.identifier.startswith(implementation) and c.identifier.endswith(platform_tag)]
|
||||||
if not platform_configs:
|
if not platform_configs:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
shell_cmd = ['linux32', '/bin/bash'] if platform_tag.endswith("i686") else ['/bin/bash']
|
||||||
|
|
||||||
container_name = f'cibuildwheel-{uuid.uuid4()}'
|
container_name = f'cibuildwheel-{uuid.uuid4()}'
|
||||||
|
call(['docker', 'create',
|
||||||
|
'--env', 'CIBUILDWHEEL',
|
||||||
|
'--name', container_name,
|
||||||
|
'-i',
|
||||||
|
'-v', '/:/host', # ignored on CircleCI
|
||||||
|
docker_image,
|
||||||
|
'/bin/bash'])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
shell_cmd = ['linux32', '/bin/bash'] if platform_tag.endswith("i686") else ['/bin/bash']
|
|
||||||
call(['docker', 'create',
|
|
||||||
'--env', 'CIBUILDWHEEL',
|
|
||||||
'--name', container_name,
|
|
||||||
'-i',
|
|
||||||
'-v', '/:/host', # ignored on CircleCI
|
|
||||||
docker_image,
|
|
||||||
'/bin/bash'])
|
|
||||||
|
|
||||||
call(['docker', 'cp', '.', container_name + ':/project'])
|
call(['docker', 'cp', '.', container_name + ':/project'])
|
||||||
|
|
||||||
call(['docker', 'start', container_name])
|
call(['docker', 'start', container_name])
|
||||||
|
|
||||||
|
if options.before_all:
|
||||||
|
call(
|
||||||
|
['docker', 'exec', '-i', container_name] + shell_cmd,
|
||||||
|
universal_newlines=True,
|
||||||
|
input='''
|
||||||
|
PS4=' + '
|
||||||
|
|
||||||
|
set -o errexit
|
||||||
|
set -o xtrace
|
||||||
|
|
||||||
|
# add a modern Python interpreter to PATH so it can be used by BEFORE_ALL
|
||||||
|
# commands
|
||||||
|
export PATH=/opt/python/cp38-cp38:$PATH
|
||||||
|
|
||||||
|
{environment_exports}
|
||||||
|
|
||||||
|
sh -c {before_all}
|
||||||
|
|
||||||
|
'''.format(
|
||||||
|
environment_exports='\n'.join(options.environment.as_shell_commands()),
|
||||||
|
before_all=shlex.quote(prepare_command(options.before_all, project='/project', package=container_package_dir))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
for config in platform_configs:
|
for config in platform_configs:
|
||||||
if options.dependency_constraints:
|
if options.dependency_constraints:
|
||||||
constraints_file = options.dependency_constraints.get_for_python_version(config.version)
|
constraints_file = options.dependency_constraints.get_for_python_version(config.version)
|
||||||
@@ -200,7 +227,6 @@ def build(options: BuildOptions) -> None:
|
|||||||
pip install {dependency_install_flags} virtualenv
|
pip install {dependency_install_flags} virtualenv
|
||||||
venv_dir=`mktemp -d`/venv
|
venv_dir=`mktemp -d`/venv
|
||||||
python -m virtualenv --no-download "$venv_dir"
|
python -m virtualenv --no-download "$venv_dir"
|
||||||
export __CIBW_VIRTUALENV_PATH__=$venv_dir
|
|
||||||
|
|
||||||
# run the tests in a subshell to keep that `activate`
|
# run the tests in a subshell to keep that `activate`
|
||||||
# script from polluting the env
|
# script from polluting the env
|
||||||
@@ -273,7 +299,7 @@ def build(options: BuildOptions) -> None:
|
|||||||
# copy the output back into the host
|
# copy the output back into the host
|
||||||
call(['docker', 'cp',
|
call(['docker', 'cp',
|
||||||
container_name + ':/output/.',
|
container_name + ':/output/.',
|
||||||
os.path.abspath(options.output_dir)])
|
str(options.output_dir.resolve())])
|
||||||
except subprocess.CalledProcessError as error:
|
except subprocess.CalledProcessError as error:
|
||||||
troubleshoot(options.package_dir, error)
|
troubleshoot(options.package_dir, error)
|
||||||
exit(1)
|
exit(1)
|
||||||
@@ -282,16 +308,11 @@ def build(options: BuildOptions) -> None:
|
|||||||
call(['docker', 'rm', '--force', '-v', container_name])
|
call(['docker', 'rm', '--force', '-v', container_name])
|
||||||
|
|
||||||
|
|
||||||
def troubleshoot(package_dir: str, error: Exception) -> None:
|
def troubleshoot(package_dir: Path, error: Exception) -> None:
|
||||||
if (isinstance(error, subprocess.CalledProcessError) and 'exec' in error.cmd):
|
if (isinstance(error, subprocess.CalledProcessError) and 'exec' in error.cmd):
|
||||||
# the bash script failed
|
# the bash script failed
|
||||||
print('Checking for common errors...')
|
print('Checking for common errors...')
|
||||||
so_files = []
|
so_files = list(package_dir.glob('**/*.so'))
|
||||||
for root, dirs, files in os.walk(package_dir):
|
|
||||||
for name in files:
|
|
||||||
_, ext = os.path.splitext(name)
|
|
||||||
if ext == '.so':
|
|
||||||
so_files.append(os.path.join(root, name))
|
|
||||||
|
|
||||||
if so_files:
|
if so_files:
|
||||||
print(textwrap.dedent('''
|
print(textwrap.dedent('''
|
||||||
@@ -305,5 +326,5 @@ def troubleshoot(package_dir: str, error: Exception) -> None:
|
|||||||
'''))
|
'''))
|
||||||
|
|
||||||
print(' Files detected:')
|
print(' Files detected:')
|
||||||
print('\n'.join([' ' + f for f in so_files]))
|
print('\n'.join([f' {f}' for f in so_files]))
|
||||||
print('')
|
print('')
|
||||||
|
|||||||
+74
-62
@@ -4,27 +4,22 @@ import shutil
|
|||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
from glob import glob
|
from os import PathLike
|
||||||
|
from pathlib import Path
|
||||||
from typing import Dict, List, Optional, NamedTuple, Union
|
from typing import Dict, List, NamedTuple, Optional, Sequence, Union
|
||||||
|
|
||||||
from .environment import ParsedEnvironment
|
from .environment import ParsedEnvironment
|
||||||
from .util import (
|
from .util import (BuildOptions, BuildSelector, download,
|
||||||
BuildOptions,
|
get_build_verbosity_extra_flags, get_pip_script,
|
||||||
BuildSelector,
|
prepare_command)
|
||||||
download,
|
|
||||||
get_build_verbosity_extra_flags,
|
|
||||||
get_pip_script,
|
|
||||||
prepare_command,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def call(args: Union[str, List[str]], env: Optional[Dict[str, str]] = None, cwd: Optional[str] = None, shell: bool = False) -> int:
|
def call(args: Union[str, Sequence[Union[str, PathLike]]], env: Optional[Dict[str, str]] = None, cwd: Optional[str] = None, shell: bool = False) -> int:
|
||||||
# print the command executing for the logs
|
# print the command executing for the logs
|
||||||
if shell:
|
if shell:
|
||||||
print(f'+ {args}')
|
print(f'+ {args}')
|
||||||
else:
|
else:
|
||||||
print('+ ' + ' '.join(shlex.quote(a) for a in args))
|
print('+ ' + ' '.join(shlex.quote(str(a)) for a in args))
|
||||||
|
|
||||||
return subprocess.check_call(args, env=env, cwd=cwd, shell=shell)
|
return subprocess.check_call(args, env=env, cwd=cwd, shell=shell)
|
||||||
|
|
||||||
@@ -41,7 +36,7 @@ def get_python_configurations(build_selector: BuildSelector) -> List[PythonConfi
|
|||||||
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='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.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.6', identifier='cp36-macosx_x86_64', url='https://www.python.org/ftp/python/3.6.8/python-3.6.8-macosx10.9.pkg'),
|
||||||
PythonConfiguration(version='3.7', identifier='cp37-macosx_x86_64', url='https://www.python.org/ftp/python/3.7.7/python-3.7.7-macosx10.9.pkg'),
|
PythonConfiguration(version='3.7', identifier='cp37-macosx_x86_64', url='https://www.python.org/ftp/python/3.7.8/python-3.7.8-macosx10.9.pkg'),
|
||||||
PythonConfiguration(version='3.8', identifier='cp38-macosx_x86_64', url='https://www.python.org/ftp/python/3.8.3/python-3.8.3-macosx10.9.pkg'),
|
PythonConfiguration(version='3.8', identifier='cp38-macosx_x86_64', url='https://www.python.org/ftp/python/3.8.3/python-3.8.3-macosx10.9.pkg'),
|
||||||
# PyPy
|
# 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='2.7', identifier='pp27-macosx_x86_64', url='https://downloads.python.org/pypy/pypy2.7-v7.3.1-osx64.tar.bz2'),
|
||||||
@@ -52,41 +47,41 @@ def get_python_configurations(build_selector: BuildSelector) -> List[PythonConfi
|
|||||||
return [c for c in python_configurations if build_selector(c.identifier)]
|
return [c for c in python_configurations if build_selector(c.identifier)]
|
||||||
|
|
||||||
|
|
||||||
SYMLINKS_DIR = '/tmp/cibw_bin'
|
SYMLINKS_DIR = Path('/tmp/cibw_bin')
|
||||||
|
|
||||||
|
|
||||||
def make_symlinks(installation_bin_path: str, python_executable: str, pip_executable: str) -> None:
|
def make_symlinks(installation_bin_path: Path, python_executable: str, pip_executable: str) -> None:
|
||||||
assert os.path.exists(os.path.join(installation_bin_path, python_executable))
|
assert (installation_bin_path / python_executable).exists()
|
||||||
|
|
||||||
# Python bin folders on Mac don't symlink `python3` to `python`, and neither
|
# Python bin folders on Mac don't symlink `python3` to `python`, and neither
|
||||||
# does PyPy for `pypy` or `pypy3`, so we do that so `python` and `pip` always
|
# does PyPy for `pypy` or `pypy3`, so we do that so `python` and `pip` always
|
||||||
# point to the active configuration.
|
# point to the active configuration.
|
||||||
if os.path.exists(SYMLINKS_DIR):
|
if SYMLINKS_DIR.exists():
|
||||||
shutil.rmtree(SYMLINKS_DIR)
|
shutil.rmtree(SYMLINKS_DIR)
|
||||||
os.makedirs(SYMLINKS_DIR)
|
SYMLINKS_DIR.mkdir(parents=True)
|
||||||
|
|
||||||
os.symlink(os.path.join(installation_bin_path, python_executable), os.path.join(SYMLINKS_DIR, 'python'))
|
(SYMLINKS_DIR / 'python').symlink_to(installation_bin_path / python_executable)
|
||||||
os.symlink(os.path.join(installation_bin_path, python_executable + '-config'), os.path.join(SYMLINKS_DIR, 'python-config'))
|
(SYMLINKS_DIR / 'python-config').symlink_to(installation_bin_path / (python_executable + '-config'))
|
||||||
os.symlink(os.path.join(installation_bin_path, pip_executable), os.path.join(SYMLINKS_DIR, 'pip'))
|
(SYMLINKS_DIR / 'pip').symlink_to(installation_bin_path / pip_executable)
|
||||||
|
|
||||||
|
|
||||||
def install_cpython(version: str, url: str) -> str:
|
def install_cpython(version: str, url: str) -> Path:
|
||||||
installed_system_packages = subprocess.check_output(['pkgutil', '--pkgs'], universal_newlines=True).splitlines()
|
installed_system_packages = subprocess.check_output(['pkgutil', '--pkgs'], universal_newlines=True).splitlines()
|
||||||
|
|
||||||
# if this version of python isn't installed, get it from python.org and install
|
# if this version of python isn't installed, get it from python.org and install
|
||||||
python_package_identifier = f'org.python.Python.PythonFramework-{version}'
|
python_package_identifier = f'org.python.Python.PythonFramework-{version}'
|
||||||
if python_package_identifier not in installed_system_packages:
|
if python_package_identifier not in installed_system_packages:
|
||||||
# download the pkg
|
# download the pkg
|
||||||
download(url, '/tmp/Python.pkg')
|
download(url, Path('/tmp/Python.pkg'))
|
||||||
# install
|
# install
|
||||||
call(['sudo', 'installer', '-pkg', '/tmp/Python.pkg', '-target', '/'])
|
call(['sudo', 'installer', '-pkg', '/tmp/Python.pkg', '-target', '/'])
|
||||||
# patch open ssl
|
# patch open ssl
|
||||||
if version == '3.5':
|
if version == '3.5':
|
||||||
open_ssl_patch_url = f'https://github.com/mayeut/patch-macos-python-openssl/releases/download/v1.0.2u/patch-macos-python-{version}-openssl-v1.0.2u.tar.gz'
|
open_ssl_patch_url = f'https://github.com/mayeut/patch-macos-python-openssl/releases/download/v1.0.2u/patch-macos-python-{version}-openssl-v1.0.2u.tar.gz'
|
||||||
download(open_ssl_patch_url, '/tmp/python-patch.tar.gz')
|
download(open_ssl_patch_url, Path('/tmp/python-patch.tar.gz'))
|
||||||
call(['sudo', 'tar', '-C', f'/Library/Frameworks/Python.framework/Versions/{version}/', '-xmf', '/tmp/python-patch.tar.gz'])
|
call(['sudo', 'tar', '-C', f'/Library/Frameworks/Python.framework/Versions/{version}/', '-xmf', '/tmp/python-patch.tar.gz'])
|
||||||
|
|
||||||
installation_bin_path = f'/Library/Frameworks/Python.framework/Versions/{version}/bin'
|
installation_bin_path = Path(f'/Library/Frameworks/Python.framework/Versions/{version}/bin')
|
||||||
python_executable = 'python3' if version[0] == '3' else 'python'
|
python_executable = 'python3' if version[0] == '3' else 'python'
|
||||||
pip_executable = 'pip3' if version[0] == '3' else 'pip'
|
pip_executable = 'pip3' if version[0] == '3' else 'pip'
|
||||||
make_symlinks(installation_bin_path, python_executable, pip_executable)
|
make_symlinks(installation_bin_path, python_executable, pip_executable)
|
||||||
@@ -94,16 +89,18 @@ def install_cpython(version: str, url: str) -> str:
|
|||||||
return installation_bin_path
|
return installation_bin_path
|
||||||
|
|
||||||
|
|
||||||
def install_pypy(version: str, url: str) -> str:
|
def install_pypy(version: str, url: str) -> Path:
|
||||||
pypy_tar_bz2 = url.rsplit('/', 1)[-1]
|
pypy_tar_bz2 = url.rsplit('/', 1)[-1]
|
||||||
assert pypy_tar_bz2.endswith(".tar.bz2")
|
extension = ".tar.bz2"
|
||||||
pypy_base_filename = os.path.splitext(os.path.splitext(pypy_tar_bz2)[0])[0]
|
assert pypy_tar_bz2.endswith(extension)
|
||||||
installation_path = os.path.join('/tmp', pypy_base_filename)
|
pypy_base_filename = pypy_tar_bz2[:-len(extension)]
|
||||||
if not os.path.exists(installation_path):
|
installation_path = Path('/tmp') / pypy_base_filename
|
||||||
download(url, os.path.join("/tmp", pypy_tar_bz2))
|
if not installation_path.exists():
|
||||||
call(['tar', '-C', '/tmp', '-xf', os.path.join("/tmp", pypy_tar_bz2)])
|
downloaded_tar_bz2 = Path("/tmp") / pypy_tar_bz2
|
||||||
|
download(url, downloaded_tar_bz2)
|
||||||
|
call(['tar', '-C', '/tmp', '-xf', downloaded_tar_bz2])
|
||||||
|
|
||||||
installation_bin_path = os.path.join(installation_path, 'bin')
|
installation_bin_path = installation_path / 'bin'
|
||||||
python_executable = 'pypy3' if version[0] == '3' else 'pypy'
|
python_executable = 'pypy3' if version[0] == '3' else 'pypy'
|
||||||
pip_executable = 'pip3' if version[0] == '3' else 'pip'
|
pip_executable = 'pip3' if version[0] == '3' else 'pip'
|
||||||
make_symlinks(installation_bin_path, python_executable, pip_executable)
|
make_symlinks(installation_bin_path, python_executable, pip_executable)
|
||||||
@@ -111,7 +108,9 @@ def install_pypy(version: str, url: str) -> str:
|
|||||||
return installation_bin_path
|
return installation_bin_path
|
||||||
|
|
||||||
|
|
||||||
def setup_python(python_configuration: PythonConfiguration, dependency_constraint_flags: List[str], environment: ParsedEnvironment) -> Dict[str, str]:
|
def setup_python(python_configuration: PythonConfiguration,
|
||||||
|
dependency_constraint_flags: Sequence[Union[str, PathLike]],
|
||||||
|
environment: ParsedEnvironment) -> Dict[str, str]:
|
||||||
if python_configuration.identifier.startswith('cp'):
|
if python_configuration.identifier.startswith('cp'):
|
||||||
installation_bin_path = install_cpython(python_configuration.version, python_configuration.url)
|
installation_bin_path = install_cpython(python_configuration.version, python_configuration.url)
|
||||||
elif python_configuration.identifier.startswith('pp'):
|
elif python_configuration.identifier.startswith('pp'):
|
||||||
@@ -121,8 +120,8 @@ def setup_python(python_configuration: PythonConfiguration, dependency_constrain
|
|||||||
|
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
env['PATH'] = os.pathsep.join([
|
env['PATH'] = os.pathsep.join([
|
||||||
SYMLINKS_DIR,
|
str(SYMLINKS_DIR),
|
||||||
installation_bin_path,
|
str(installation_bin_path),
|
||||||
env['PATH'],
|
env['PATH'],
|
||||||
])
|
])
|
||||||
|
|
||||||
@@ -144,15 +143,15 @@ def setup_python(python_configuration: PythonConfiguration, dependency_constrain
|
|||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
# install pip & wheel
|
# install pip & wheel
|
||||||
call(['python', get_pip_script] + dependency_constraint_flags, env=env, cwd="/tmp")
|
call(['python', get_pip_script, *dependency_constraint_flags], env=env, cwd="/tmp")
|
||||||
assert os.path.exists(os.path.join(installation_bin_path, 'pip'))
|
assert (installation_bin_path / 'pip').exists()
|
||||||
call(['which', 'pip'], env=env)
|
call(['which', 'pip'], env=env)
|
||||||
call(['pip', '--version'], env=env)
|
call(['pip', '--version'], env=env)
|
||||||
which_pip = subprocess.check_output(['which', 'pip'], env=env, universal_newlines=True).strip()
|
which_pip = subprocess.check_output(['which', 'pip'], env=env, universal_newlines=True).strip()
|
||||||
if which_pip != '/tmp/cibw_bin/pip':
|
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)
|
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)
|
exit(1)
|
||||||
call(['pip', 'install', '--upgrade', 'setuptools', 'wheel', 'delocate'] + dependency_constraint_flags, env=env)
|
call(['pip', 'install', '--upgrade', 'setuptools', 'wheel', 'delocate', *dependency_constraint_flags], env=env)
|
||||||
|
|
||||||
# Set MACOSX_DEPLOYMENT_TARGET to 10.9, if the user didn't set it.
|
# 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
|
# CPython 3.5 defaults to 10.6, and pypy defaults to 10.7, causing
|
||||||
@@ -170,14 +169,19 @@ def setup_python(python_configuration: PythonConfiguration, dependency_constrain
|
|||||||
|
|
||||||
|
|
||||||
def build(options: BuildOptions) -> None:
|
def build(options: BuildOptions) -> None:
|
||||||
temp_dir = tempfile.mkdtemp(prefix='cibuildwheel')
|
temp_dir = Path(tempfile.mkdtemp(prefix='cibuildwheel'))
|
||||||
built_wheel_dir = os.path.join(temp_dir, 'built_wheel')
|
built_wheel_dir = temp_dir / 'built_wheel'
|
||||||
repaired_wheel_dir = os.path.join(temp_dir, 'repaired_wheel')
|
repaired_wheel_dir = temp_dir / 'repaired_wheel'
|
||||||
|
|
||||||
|
if options.before_all:
|
||||||
|
env = options.environment.as_dictionary(prev_environment=os.environ)
|
||||||
|
before_all_prepared = prepare_command(options.before_all, project='.', package=options.package_dir)
|
||||||
|
call([before_all_prepared], shell=True, env=env)
|
||||||
|
|
||||||
python_configurations = get_python_configurations(options.build_selector)
|
python_configurations = get_python_configurations(options.build_selector)
|
||||||
|
|
||||||
for config in python_configurations:
|
for config in python_configurations:
|
||||||
dependency_constraint_flags = []
|
dependency_constraint_flags: Sequence[Union[str, PathLike]] = []
|
||||||
if options.dependency_constraints:
|
if options.dependency_constraints:
|
||||||
dependency_constraint_flags = [
|
dependency_constraint_flags = [
|
||||||
'-c', options.dependency_constraints.get_for_python_version(config.version)
|
'-c', options.dependency_constraints.get_for_python_version(config.version)
|
||||||
@@ -191,29 +195,39 @@ def build(options: BuildOptions) -> None:
|
|||||||
call(before_build_prepared, env=env, shell=True)
|
call(before_build_prepared, env=env, shell=True)
|
||||||
|
|
||||||
# build the wheel
|
# build the wheel
|
||||||
if os.path.exists(built_wheel_dir):
|
if built_wheel_dir.exists():
|
||||||
shutil.rmtree(built_wheel_dir)
|
shutil.rmtree(built_wheel_dir)
|
||||||
os.makedirs(built_wheel_dir)
|
built_wheel_dir.mkdir(parents=True)
|
||||||
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]
|
# Path.resolve() is needed. Without it pip wheel may try to fetch package from pypi.org
|
||||||
|
# see https://github.com/joerick/cibuildwheel/pull/369
|
||||||
|
call([
|
||||||
|
'pip', 'wheel',
|
||||||
|
options.package_dir.resolve(),
|
||||||
|
'-w', built_wheel_dir,
|
||||||
|
'--no-deps',
|
||||||
|
*get_build_verbosity_extra_flags(options.build_verbosity)
|
||||||
|
], env=env)
|
||||||
|
|
||||||
|
built_wheel = next(built_wheel_dir.glob('*.whl'))
|
||||||
|
|
||||||
# repair the wheel
|
# repair the wheel
|
||||||
if os.path.exists(repaired_wheel_dir):
|
if repaired_wheel_dir.exists():
|
||||||
shutil.rmtree(repaired_wheel_dir)
|
shutil.rmtree(repaired_wheel_dir)
|
||||||
os.makedirs(repaired_wheel_dir)
|
repaired_wheel_dir.mkdir(parents=True)
|
||||||
if built_wheel.endswith('none-any.whl') or not options.repair_command:
|
if built_wheel.name.endswith('none-any.whl') or not options.repair_command:
|
||||||
# pure Python wheel or empty repair command
|
# pure Python wheel or empty repair command
|
||||||
shutil.move(built_wheel, repaired_wheel_dir)
|
shutil.move(str(built_wheel), repaired_wheel_dir)
|
||||||
else:
|
else:
|
||||||
repair_command_prepared = prepare_command(options.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)
|
call(repair_command_prepared, env=env, shell=True)
|
||||||
repaired_wheel = glob(os.path.join(repaired_wheel_dir, '*.whl'))[0]
|
repaired_wheel = next(repaired_wheel_dir.glob('*.whl'))
|
||||||
|
|
||||||
if options.test_command:
|
if options.test_command:
|
||||||
# set up a virtual environment to install and test from, to make sure
|
# set up a virtual environment to install and test from, to make sure
|
||||||
# there are no dependencies that were pulled in at build time.
|
# there are no dependencies that were pulled in at build time.
|
||||||
call(['pip', 'install', 'virtualenv'] + dependency_constraint_flags, env=env)
|
call(['pip', 'install', 'virtualenv', *dependency_constraint_flags], env=env)
|
||||||
venv_dir = tempfile.mkdtemp()
|
venv_dir = Path(tempfile.mkdtemp())
|
||||||
|
|
||||||
# Use --no-download to ensure determinism by using seed libraries
|
# Use --no-download to ensure determinism by using seed libraries
|
||||||
# built into virtualenv
|
# built into virtualenv
|
||||||
@@ -221,10 +235,9 @@ def build(options: BuildOptions) -> None:
|
|||||||
|
|
||||||
virtualenv_env = env.copy()
|
virtualenv_env = env.copy()
|
||||||
virtualenv_env['PATH'] = os.pathsep.join([
|
virtualenv_env['PATH'] = os.pathsep.join([
|
||||||
os.path.join(venv_dir, 'bin'),
|
str(venv_dir / 'bin'),
|
||||||
virtualenv_env['PATH'],
|
virtualenv_env['PATH'],
|
||||||
])
|
])
|
||||||
virtualenv_env["__CIBW_VIRTUALENV_PATH__"] = venv_dir
|
|
||||||
|
|
||||||
# check that we are using the Python from the virtual environment
|
# check that we are using the Python from the virtual environment
|
||||||
call(['which', 'python'], env=virtualenv_env)
|
call(['which', 'python'], env=virtualenv_env)
|
||||||
@@ -234,7 +247,7 @@ def build(options: BuildOptions) -> None:
|
|||||||
call(before_test_prepared, env=virtualenv_env, shell=True)
|
call(before_test_prepared, env=virtualenv_env, shell=True)
|
||||||
|
|
||||||
# install the wheel
|
# install the wheel
|
||||||
call(['pip', 'install', repaired_wheel + options.test_extras], env=virtualenv_env)
|
call(['pip', 'install', str(repaired_wheel) + options.test_extras], env=virtualenv_env)
|
||||||
|
|
||||||
# test the wheel
|
# test the wheel
|
||||||
if options.test_requires:
|
if options.test_requires:
|
||||||
@@ -245,8 +258,8 @@ def build(options: BuildOptions) -> None:
|
|||||||
# and not the repo code)
|
# and not the repo code)
|
||||||
test_command_prepared = prepare_command(
|
test_command_prepared = prepare_command(
|
||||||
options.test_command,
|
options.test_command,
|
||||||
project=os.path.abspath('.'),
|
project=Path('.').resolve(),
|
||||||
package=os.path.abspath(options.package_dir)
|
package=options.package_dir.resolve()
|
||||||
)
|
)
|
||||||
call(test_command_prepared, cwd=os.environ['HOME'], env=virtualenv_env, shell=True)
|
call(test_command_prepared, cwd=os.environ['HOME'], env=virtualenv_env, shell=True)
|
||||||
|
|
||||||
@@ -254,5 +267,4 @@ def build(options: BuildOptions) -> None:
|
|||||||
shutil.rmtree(venv_dir)
|
shutil.rmtree(venv_dir)
|
||||||
|
|
||||||
# we're all done here; move it to output (overwrite existing)
|
# we're all done here; move it to output (overwrite existing)
|
||||||
dst = os.path.join(options.output_dir, os.path.basename(repaired_wheel))
|
shutil.move(str(repaired_wheel), options.output_dir)
|
||||||
shutil.move(repaired_wheel, dst)
|
|
||||||
|
|||||||
+30
-29
@@ -1,14 +1,15 @@
|
|||||||
import os
|
import os
|
||||||
import urllib.request
|
import urllib.request
|
||||||
from fnmatch import fnmatch
|
from fnmatch import fnmatch
|
||||||
|
from pathlib import Path
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
from typing import Dict, List, NamedTuple, Optional
|
from typing import Dict, List, NamedTuple, Optional, Union
|
||||||
|
|
||||||
from .environment import ParsedEnvironment
|
from .environment import ParsedEnvironment
|
||||||
|
|
||||||
|
|
||||||
def prepare_command(command: str, **kwargs: str) -> str:
|
def prepare_command(command: str, **kwargs: Union[str, os.PathLike]) -> str:
|
||||||
'''
|
'''
|
||||||
Preprocesses a command by expanding variables like {python}.
|
Preprocesses a command by expanding variables like {python}.
|
||||||
|
|
||||||
@@ -58,11 +59,11 @@ class Unbuffered:
|
|||||||
return getattr(self.stream, attr)
|
return getattr(self.stream, attr)
|
||||||
|
|
||||||
|
|
||||||
def download(url: str, dest: str) -> None:
|
def download(url: str, dest: Path) -> None:
|
||||||
print('+ Download ' + url + ' to ' + dest)
|
print(f'+ Download {url} to {dest}')
|
||||||
dest_dir = os.path.dirname(dest)
|
dest_dir = dest.parent
|
||||||
if not os.path.exists(dest_dir):
|
if not dest_dir.exists():
|
||||||
os.makedirs(dest_dir)
|
dest_dir.mkdir(parents=True)
|
||||||
|
|
||||||
repeat_num = 3
|
repeat_num = 3
|
||||||
for i in range(repeat_num):
|
for i in range(repeat_num):
|
||||||
@@ -76,52 +77,52 @@ def download(url: str, dest: str) -> None:
|
|||||||
break
|
break
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(dest, 'wb') as file:
|
dest.write_bytes(response.read())
|
||||||
file.write(response.read())
|
|
||||||
finally:
|
finally:
|
||||||
response.close()
|
response.close()
|
||||||
|
|
||||||
|
|
||||||
class DependencyConstraints:
|
class DependencyConstraints:
|
||||||
def __init__(self, base_file_path: str):
|
def __init__(self, base_file_path: Path):
|
||||||
assert os.path.exists(base_file_path)
|
assert base_file_path.exists()
|
||||||
self.base_file_path = os.path.abspath(base_file_path)
|
self.base_file_path = base_file_path.resolve()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def with_defaults() -> 'DependencyConstraints':
|
def with_defaults() -> 'DependencyConstraints':
|
||||||
return DependencyConstraints(
|
return DependencyConstraints(
|
||||||
base_file_path=os.path.join(os.path.dirname(__file__), 'resources', 'constraints.txt')
|
base_file_path=resources_dir / 'constraints.txt'
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_for_python_version(self, version: str) -> str:
|
def get_for_python_version(self, version: str) -> Path:
|
||||||
version_parts = version.split('.')
|
version_parts = version.split('.')
|
||||||
|
|
||||||
# try to find a version-specific dependency file e.g. if
|
# try to find a version-specific dependency file e.g. if
|
||||||
# ./constraints.txt is the base, look for ./constraints-python27.txt
|
# ./constraints.txt is the base, look for ./constraints-python27.txt
|
||||||
base, ext = os.path.splitext(self.base_file_path)
|
specific_stem = self.base_file_path.stem + f'-python{version_parts[0]}{version_parts[1]}'
|
||||||
specific = base + f'-python{version_parts[0]}{version_parts[1]}'
|
specific_name = specific_stem + self.base_file_path.suffix
|
||||||
specific_file_path = specific + ext
|
specific_file_path = self.base_file_path.with_name(specific_name)
|
||||||
if os.path.exists(specific_file_path):
|
if specific_file_path.exists():
|
||||||
return specific_file_path
|
return specific_file_path
|
||||||
else:
|
else:
|
||||||
return self.base_file_path
|
return self.base_file_path
|
||||||
|
|
||||||
|
|
||||||
class BuildOptions(NamedTuple):
|
class BuildOptions(NamedTuple):
|
||||||
package_dir: str
|
package_dir: Path
|
||||||
output_dir: str
|
output_dir: Path
|
||||||
|
build_selector: BuildSelector
|
||||||
|
environment: ParsedEnvironment
|
||||||
|
before_all: str
|
||||||
|
before_build: Optional[str]
|
||||||
|
repair_command: str
|
||||||
|
manylinux_images: Optional[Dict[str, str]]
|
||||||
|
dependency_constraints: Optional[DependencyConstraints]
|
||||||
test_command: Optional[str]
|
test_command: Optional[str]
|
||||||
|
before_test: Optional[str]
|
||||||
test_requires: List[str]
|
test_requires: List[str]
|
||||||
test_extras: str
|
test_extras: str
|
||||||
before_build: Optional[str]
|
|
||||||
build_verbosity: int
|
build_verbosity: int
|
||||||
build_selector: BuildSelector
|
|
||||||
repair_command: str
|
|
||||||
environment: ParsedEnvironment
|
|
||||||
before_test: str
|
|
||||||
dependency_constraints: Optional[DependencyConstraints]
|
|
||||||
manylinux_images: Optional[Dict[str, str]]
|
|
||||||
|
|
||||||
|
|
||||||
resources_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), 'resources'))
|
resources_dir = Path(__file__).resolve().parent / 'resources'
|
||||||
get_pip_script = os.path.join(resources_dir, 'get-pip.py')
|
get_pip_script = resources_dir / 'get-pip.py'
|
||||||
|
|||||||
+122
-79
@@ -3,29 +3,32 @@ import shutil
|
|||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
from glob import glob
|
from os import PathLike
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Dict, List, NamedTuple, Optional, Sequence, Union
|
||||||
from zipfile import ZipFile
|
from zipfile import ZipFile
|
||||||
|
import toml
|
||||||
from typing import Dict, List, Optional, NamedTuple
|
|
||||||
|
|
||||||
from .environment import ParsedEnvironment
|
from .environment import ParsedEnvironment
|
||||||
from .util import (
|
from .util import (BuildOptions, BuildSelector, download,
|
||||||
BuildOptions,
|
get_build_verbosity_extra_flags, get_pip_script,
|
||||||
BuildSelector,
|
prepare_command)
|
||||||
download,
|
|
||||||
get_build_verbosity_extra_flags,
|
|
||||||
get_pip_script,
|
|
||||||
prepare_command,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
IS_RUNNING_ON_AZURE = Path('C:\\hostedtoolcache').exists()
|
||||||
IS_RUNNING_ON_AZURE = os.path.exists('C:\\hostedtoolcache')
|
|
||||||
IS_RUNNING_ON_TRAVIS = os.environ.get('TRAVIS_OS_NAME') == 'windows'
|
IS_RUNNING_ON_TRAVIS = os.environ.get('TRAVIS_OS_NAME') == 'windows'
|
||||||
|
|
||||||
|
|
||||||
def shell(args: List[str], env: Optional[Dict[str, str]] = None, cwd: Optional[str] = None) -> int:
|
def call(args: Sequence[Union[str, PathLike]], env: Optional[Dict[str, str]] = None,
|
||||||
print('+ ' + ' '.join(args))
|
cwd: Optional[str] = None) -> None:
|
||||||
return subprocess.check_call(' '.join(args), env=env, cwd=cwd, shell=True)
|
print('+ ' + ' '.join(str(a) for a in args))
|
||||||
|
# we use shell=True here, even though we don't need a shell due to a bug
|
||||||
|
# https://bugs.python.org/issue8557
|
||||||
|
subprocess.check_call([str(a) for a in args], env=env, cwd=cwd, shell=True)
|
||||||
|
|
||||||
|
|
||||||
|
def shell(command: str, env: Optional[Dict[str, str]] = None, cwd: Optional[str] = None) -> None:
|
||||||
|
print(f'+ {command}')
|
||||||
|
subprocess.check_call(command, env=env, cwd=cwd, shell=True)
|
||||||
|
|
||||||
|
|
||||||
def get_nuget_args(version: str, arch: str) -> List[str]:
|
def get_nuget_args(version: str, arch: str) -> List[str]:
|
||||||
@@ -51,8 +54,8 @@ def get_python_configurations(build_selector: BuildSelector) -> List[PythonConfi
|
|||||||
PythonConfiguration(version='3.5.4', arch='64', identifier='cp35-win_amd64', 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='32', identifier='cp36-win32', url=None),
|
||||||
PythonConfiguration(version='3.6.8', arch='64', identifier='cp36-win_amd64', url=None),
|
PythonConfiguration(version='3.6.8', arch='64', identifier='cp36-win_amd64', url=None),
|
||||||
PythonConfiguration(version='3.7.7', arch='32', identifier='cp37-win32', url=None),
|
PythonConfiguration(version='3.7.8', arch='32', identifier='cp37-win32', url=None),
|
||||||
PythonConfiguration(version='3.7.7', arch='64', identifier='cp37-win_amd64', url=None),
|
PythonConfiguration(version='3.7.8', arch='64', identifier='cp37-win_amd64', url=None),
|
||||||
PythonConfiguration(version='3.8.3', arch='32', identifier='cp38-win32', url=None),
|
PythonConfiguration(version='3.8.3', arch='32', identifier='cp38-win32', url=None),
|
||||||
PythonConfiguration(version='3.8.3', arch='64', identifier='cp38-win_amd64', url=None),
|
PythonConfiguration(version='3.8.3', arch='64', identifier='cp38-win_amd64', url=None),
|
||||||
# PyPy
|
# PyPy
|
||||||
@@ -71,36 +74,38 @@ def get_python_configurations(build_selector: BuildSelector) -> List[PythonConfi
|
|||||||
return python_configurations
|
return python_configurations
|
||||||
|
|
||||||
|
|
||||||
def extract_zip(zip_src: str, dest: str) -> None:
|
def extract_zip(zip_src: Path, dest: Path) -> None:
|
||||||
with ZipFile(zip_src) as zip:
|
with ZipFile(zip_src) as zip:
|
||||||
zip.extractall(dest)
|
zip.extractall(dest)
|
||||||
|
|
||||||
|
|
||||||
def install_cpython(version: str, arch: str, nuget: str) -> str:
|
def install_cpython(version: str, arch: str, nuget: Path) -> Path:
|
||||||
nuget_args = get_nuget_args(version, arch)
|
nuget_args = get_nuget_args(version, arch)
|
||||||
installation_path = os.path.join(nuget_args[-1], nuget_args[0] + '.' + version, 'tools')
|
installation_path = Path(nuget_args[-1]) / (nuget_args[0] + '.' + version) / 'tools'
|
||||||
shell([nuget, 'install'] + nuget_args)
|
call([nuget, 'install', *nuget_args])
|
||||||
return installation_path
|
return installation_path
|
||||||
|
|
||||||
|
|
||||||
def install_pypy(version: str, arch: str, url: str) -> str:
|
def install_pypy(version: str, arch: str, url: str) -> Path:
|
||||||
assert arch == '32'
|
assert arch == '32'
|
||||||
# Inside the PyPy zip file is a directory with the same name
|
# Inside the PyPy zip file is a directory with the same name
|
||||||
zip_filename = url.rsplit('/', 1)[-1]
|
zip_filename = url.rsplit('/', 1)[-1]
|
||||||
installation_path = os.path.join('C:\\cibw', os.path.splitext(zip_filename)[0])
|
extension = ".zip"
|
||||||
if not os.path.exists(installation_path):
|
assert zip_filename.endswith(extension)
|
||||||
pypy_zip = os.path.join('C:\\cibw', zip_filename)
|
installation_path = Path('C:\\cibw') / zip_filename[:-len(extension)]
|
||||||
|
if not installation_path.exists():
|
||||||
|
pypy_zip = Path('C:\\cibw') / zip_filename
|
||||||
download(url, pypy_zip)
|
download(url, pypy_zip)
|
||||||
# Extract to the parent directory because the zip file still contains a directory
|
# Extract to the parent directory because the zip file still contains a directory
|
||||||
extract_zip(pypy_zip, os.path.dirname(installation_path))
|
extract_zip(pypy_zip, installation_path.parent)
|
||||||
pypy_exe = 'pypy3.exe' if version[0] == '3' else 'pypy.exe'
|
pypy_exe = 'pypy3.exe' if version[0] == '3' else 'pypy.exe'
|
||||||
shell(['mklink', os.path.join(installation_path, 'python.exe'), os.path.join(installation_path, pypy_exe)])
|
(installation_path / 'python.exe').symlink_to(installation_path / pypy_exe)
|
||||||
return installation_path
|
return installation_path
|
||||||
|
|
||||||
|
|
||||||
def setup_python(python_configuration: PythonConfiguration, dependency_constraint_flags: List[str], environment: ParsedEnvironment) -> Dict[str, str]:
|
def setup_python(python_configuration: PythonConfiguration, dependency_constraint_flags: Sequence[Union[str, PathLike]], environment: ParsedEnvironment) -> Dict[str, str]:
|
||||||
nuget = 'C:\\cibw\\nuget.exe'
|
nuget = Path('C:\\cibw\\nuget.exe')
|
||||||
if not os.path.exists(nuget):
|
if not nuget.exists():
|
||||||
download('https://dist.nuget.org/win-x86-commandline/latest/nuget.exe', nuget)
|
download('https://dist.nuget.org/win-x86-commandline/latest/nuget.exe', nuget)
|
||||||
|
|
||||||
if python_configuration.identifier.startswith('cp'):
|
if python_configuration.identifier.startswith('cp'):
|
||||||
@@ -111,58 +116,88 @@ def setup_python(python_configuration: PythonConfiguration, dependency_constrain
|
|||||||
else:
|
else:
|
||||||
raise ValueError("Unknown Python implementation")
|
raise ValueError("Unknown Python implementation")
|
||||||
|
|
||||||
assert os.path.exists(os.path.join(installation_path, 'python.exe'))
|
assert (installation_path / 'python.exe').exists()
|
||||||
|
|
||||||
# set up PATH and environment variables for run_with_env
|
# set up PATH and environment variables for run_with_env
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
env['PYTHON_VERSION'] = python_configuration.version
|
env['PYTHON_VERSION'] = python_configuration.version
|
||||||
env['PYTHON_ARCH'] = python_configuration.arch
|
env['PYTHON_ARCH'] = python_configuration.arch
|
||||||
env['PATH'] = os.pathsep.join([
|
env['PATH'] = os.pathsep.join([
|
||||||
installation_path,
|
str(installation_path),
|
||||||
os.path.join(installation_path, 'Scripts'),
|
str(installation_path / 'Scripts'),
|
||||||
env['PATH']
|
env['PATH']
|
||||||
])
|
])
|
||||||
# update env with results from CIBW_ENVIRONMENT
|
# update env with results from CIBW_ENVIRONMENT
|
||||||
env = environment.as_dictionary(prev_environment=env)
|
env = environment.as_dictionary(prev_environment=env)
|
||||||
|
|
||||||
# for the logs - check we're running the right version of python
|
# for the logs - check we're running the right version of python
|
||||||
shell(['where', 'python'], env=env)
|
call(['where', 'python'], env=env)
|
||||||
shell(['python', '--version'], env=env)
|
call(['python', '--version'], env=env)
|
||||||
shell(['python', '-c', '"import struct; print(struct.calcsize(\'P\') * 8)"'], env=env)
|
call(['python', '-c', '"import struct; print(struct.calcsize(\'P\') * 8)"'], env=env)
|
||||||
where_python = subprocess.check_output(['where', 'python'], env=env, universal_newlines=True).splitlines()[0].strip()
|
where_python = subprocess.check_output(['where', 'python'], env=env, universal_newlines=True).splitlines()[0].strip()
|
||||||
if where_python != os.path.join(installation_path, 'python.exe'):
|
if where_python != str(installation_path / 'python.exe'):
|
||||||
print("cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it.", file=sys.stderr)
|
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)
|
exit(1)
|
||||||
|
|
||||||
# make sure pip is installed
|
# make sure pip is installed
|
||||||
if not os.path.exists(os.path.join(installation_path, 'Scripts', 'pip.exe')):
|
if not (installation_path / 'Scripts' / 'pip.exe').exists():
|
||||||
shell(['python', get_pip_script] + dependency_constraint_flags, env=env, cwd="C:\\cibw")
|
call(['python', get_pip_script, *dependency_constraint_flags], env=env, cwd="C:\\cibw")
|
||||||
assert os.path.exists(os.path.join(installation_path, 'Scripts', 'pip.exe'))
|
assert (installation_path / 'Scripts' / 'pip.exe').exists()
|
||||||
where_pip = subprocess.check_output(['where', 'pip'], env=env, universal_newlines=True).splitlines()[0].strip()
|
where_pip = subprocess.check_output(['where', 'pip'], env=env, universal_newlines=True).splitlines()[0].strip()
|
||||||
if where_pip.strip() != os.path.join(installation_path, 'Scripts', 'pip.exe'):
|
if where_pip.strip() != str(installation_path / 'Scripts' / 'pip.exe'):
|
||||||
print("cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it.", file=sys.stderr)
|
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)
|
exit(1)
|
||||||
|
|
||||||
# prepare the Python environment
|
# prepare the Python environment
|
||||||
shell(['python', '-m', 'pip', 'install', '--upgrade', 'pip'] + dependency_constraint_flags, env=env)
|
call(['python', '-m', 'pip', 'install', '--upgrade', 'pip', *dependency_constraint_flags], env=env)
|
||||||
shell(['pip', '--version'], env=env)
|
call(['pip', '--version'], env=env)
|
||||||
shell(['pip', 'install', '--upgrade', 'setuptools', 'wheel'] + dependency_constraint_flags, env=env)
|
call(['pip', 'install', '--upgrade', 'setuptools', 'wheel', *dependency_constraint_flags], env=env)
|
||||||
|
|
||||||
return env
|
return env
|
||||||
|
|
||||||
|
|
||||||
|
def pep_518_cp35_workaround(package_dir: Path, env: Dict[str, str]) -> None:
|
||||||
|
"""
|
||||||
|
Python 3.5 PEP 518 hack (see https://github.com/pypa/pip/issues/8392#issuecomment-639563494)
|
||||||
|
Basically, nuget's Python is an embedded Python distribution, which is not supported by pip.
|
||||||
|
Before version 3.6, there was no way to disable the "embedded" behavior, including the ignoring
|
||||||
|
of environment variables, including the ones pip uses to setup PEP 518 builds.
|
||||||
|
|
||||||
|
The fix here is as suggested in that issue; we manually setup the PEP 518 requirements. Since we
|
||||||
|
are in a fresh environment (except for pinned cibuildweel dependencies), the build is already
|
||||||
|
mostly "isolated".
|
||||||
|
"""
|
||||||
|
|
||||||
|
pyproject_path = package_dir / 'pyproject.toml'
|
||||||
|
|
||||||
|
if pyproject_path.exists():
|
||||||
|
data = toml.load(pyproject_path)
|
||||||
|
requirements = (
|
||||||
|
data['build-system'].get('requires', [])
|
||||||
|
if 'build-system' in data
|
||||||
|
else []
|
||||||
|
)
|
||||||
|
if requirements:
|
||||||
|
shell(['pip', 'install'] + requirements, env=env)
|
||||||
|
|
||||||
|
|
||||||
def build(options: BuildOptions) -> None:
|
def build(options: BuildOptions) -> None:
|
||||||
temp_dir = tempfile.mkdtemp(prefix='cibuildwheel')
|
temp_dir = Path(tempfile.mkdtemp(prefix='cibuildwheel'))
|
||||||
built_wheel_dir = os.path.join(temp_dir, 'built_wheel')
|
built_wheel_dir = temp_dir / 'built_wheel'
|
||||||
repaired_wheel_dir = os.path.join(temp_dir, 'repaired_wheel')
|
repaired_wheel_dir = temp_dir / 'repaired_wheel'
|
||||||
|
|
||||||
# install nuget as best way to provide python
|
# install nuget as best way to provide python
|
||||||
nuget = 'C:\\cibw\\nuget.exe'
|
nuget = Path('C:\\cibw\\nuget.exe')
|
||||||
download('https://dist.nuget.org/win-x86-commandline/latest/nuget.exe', nuget)
|
download('https://dist.nuget.org/win-x86-commandline/latest/nuget.exe', nuget)
|
||||||
|
|
||||||
|
if options.before_all:
|
||||||
|
env = options.environment.as_dictionary(prev_environment=os.environ)
|
||||||
|
before_all_prepared = prepare_command(options.before_all, project='.', package=options.package_dir)
|
||||||
|
shell(before_all_prepared, env=env)
|
||||||
|
|
||||||
python_configurations = get_python_configurations(options.build_selector)
|
python_configurations = get_python_configurations(options.build_selector)
|
||||||
for config in python_configurations:
|
for config in python_configurations:
|
||||||
dependency_constraint_flags = []
|
dependency_constraint_flags: Sequence[Union[str, PathLike]] = []
|
||||||
if options.dependency_constraints:
|
if options.dependency_constraints:
|
||||||
dependency_constraint_flags = [
|
dependency_constraint_flags = [
|
||||||
'-c', options.dependency_constraints.get_for_python_version(config.version)
|
'-c', options.dependency_constraints.get_for_python_version(config.version)
|
||||||
@@ -174,48 +209,59 @@ def build(options: BuildOptions) -> None:
|
|||||||
# run the before_build command
|
# run the before_build command
|
||||||
if options.before_build:
|
if options.before_build:
|
||||||
before_build_prepared = prepare_command(options.before_build, project='.', package=options.package_dir)
|
before_build_prepared = prepare_command(options.before_build, project='.', package=options.package_dir)
|
||||||
shell([before_build_prepared], env=env)
|
shell(before_build_prepared, env=env)
|
||||||
|
|
||||||
|
# activate the PEP 518 patch if on Windows Python 3.5
|
||||||
|
# (will only have an effect if PEP 517 builds are used):
|
||||||
|
if config.version.startswith('3.5'):
|
||||||
|
pep_518_cp35_workaround(options.package_dir, env)
|
||||||
|
|
||||||
# build the wheel
|
# build the wheel
|
||||||
if os.path.exists(built_wheel_dir):
|
if built_wheel_dir.exists():
|
||||||
shutil.rmtree(built_wheel_dir)
|
shutil.rmtree(built_wheel_dir)
|
||||||
os.makedirs(built_wheel_dir)
|
built_wheel_dir.mkdir(parents=True)
|
||||||
shell(['pip', 'wheel', options.package_dir, '-w', built_wheel_dir, '--no-deps'] + get_build_verbosity_extra_flags(options.build_verbosity), env=env)
|
# Path.resolve() is needed. Without it pip wheel may try to fetch package from pypi.org
|
||||||
built_wheel = glob(os.path.join(built_wheel_dir, '*.whl'))[0]
|
# see https://github.com/joerick/cibuildwheel/pull/369
|
||||||
|
call([
|
||||||
|
'pip', 'wheel',
|
||||||
|
options.package_dir.resolve(),
|
||||||
|
'-w', built_wheel_dir,
|
||||||
|
'--no-deps',
|
||||||
|
*get_build_verbosity_extra_flags(options.build_verbosity)
|
||||||
|
], env=env)
|
||||||
|
|
||||||
|
built_wheel = next(built_wheel_dir.glob('*.whl'))
|
||||||
|
|
||||||
# repair the wheel
|
# repair the wheel
|
||||||
if os.path.exists(repaired_wheel_dir):
|
if repaired_wheel_dir.exists():
|
||||||
shutil.rmtree(repaired_wheel_dir)
|
shutil.rmtree(repaired_wheel_dir)
|
||||||
os.makedirs(repaired_wheel_dir)
|
repaired_wheel_dir.mkdir(parents=True)
|
||||||
if built_wheel.endswith('none-any.whl') or not options.repair_command:
|
if built_wheel.name.endswith('none-any.whl') or not options.repair_command:
|
||||||
# pure Python wheel or empty repair command
|
# pure Python wheel or empty repair command
|
||||||
shutil.move(built_wheel, repaired_wheel_dir)
|
shutil.move(str(built_wheel), repaired_wheel_dir)
|
||||||
else:
|
else:
|
||||||
repair_command_prepared = prepare_command(options.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)
|
shell(repair_command_prepared, env=env)
|
||||||
repaired_wheel = glob(os.path.join(repaired_wheel_dir, '*.whl'))[0]
|
repaired_wheel = next(repaired_wheel_dir.glob('*.whl'))
|
||||||
|
|
||||||
if options.test_command:
|
if options.test_command:
|
||||||
# set up a virtual environment to install and test from, to make sure
|
# set up a virtual environment to install and test from, to make sure
|
||||||
# there are no dependencies that were pulled in at build time.
|
# there are no dependencies that were pulled in at build time.
|
||||||
shell(['pip', 'install', 'virtualenv'] + dependency_constraint_flags, env=env)
|
call(['pip', 'install', 'virtualenv', *dependency_constraint_flags], env=env)
|
||||||
venv_dir = tempfile.mkdtemp()
|
venv_dir = Path(tempfile.mkdtemp())
|
||||||
|
|
||||||
# Use --no-download to ensure determinism by using seed libraries
|
# Use --no-download to ensure determinism by using seed libraries
|
||||||
# built into virtualenv
|
# built into virtualenv
|
||||||
shell(['python', '-m', 'virtualenv', '--no-download', venv_dir], env=env)
|
call(['python', '-m', 'virtualenv', '--no-download', venv_dir], env=env)
|
||||||
|
|
||||||
virtualenv_env = env.copy()
|
virtualenv_env = env.copy()
|
||||||
|
|
||||||
venv_script_path = os.path.join(venv_dir, 'Scripts')
|
|
||||||
virtualenv_env['PATH'] = os.pathsep.join([
|
virtualenv_env['PATH'] = os.pathsep.join([
|
||||||
venv_script_path,
|
str(venv_dir / 'Scripts'),
|
||||||
virtualenv_env['PATH'],
|
virtualenv_env['PATH'],
|
||||||
])
|
])
|
||||||
virtualenv_env["__CIBW_VIRTUALENV_PATH__"] = venv_dir
|
|
||||||
|
|
||||||
# check that we are using the Python from the virtual environment
|
# check that we are using the Python from the virtual environment
|
||||||
shell(['which', 'python'], env=virtualenv_env)
|
call(['which', 'python'], env=virtualenv_env)
|
||||||
|
|
||||||
if options.before_test:
|
if options.before_test:
|
||||||
before_test_prepared = prepare_command(
|
before_test_prepared = prepare_command(
|
||||||
@@ -223,30 +269,27 @@ def build(options: BuildOptions) -> None:
|
|||||||
project='.',
|
project='.',
|
||||||
package=options.package_dir
|
package=options.package_dir
|
||||||
)
|
)
|
||||||
shell([before_test_prepared], env=virtualenv_env)
|
shell(before_test_prepared, env=virtualenv_env)
|
||||||
|
|
||||||
# install the wheel
|
# install the wheel
|
||||||
shell(['pip', 'install', repaired_wheel + options.test_extras], env=virtualenv_env)
|
call(['pip', 'install', str(repaired_wheel) + options.test_extras], env=virtualenv_env)
|
||||||
|
|
||||||
# test the wheel
|
# test the wheel
|
||||||
if options.test_requires:
|
if options.test_requires:
|
||||||
shell(['pip', 'install'] + options.test_requires, env=virtualenv_env)
|
call(['pip', 'install'] + options.test_requires, env=virtualenv_env)
|
||||||
|
|
||||||
# run the tests from c:\, with an absolute path in the command
|
# run the tests from c:\, with an absolute path in the command
|
||||||
# (this ensures that Python runs the tests against the installed wheel
|
# (this ensures that Python runs the tests against the installed wheel
|
||||||
# and not the repo code)
|
# and not the repo code)
|
||||||
test_command_prepared = prepare_command(
|
test_command_prepared = prepare_command(
|
||||||
options.test_command,
|
options.test_command,
|
||||||
project=os.path.abspath('.'),
|
project=Path('.').resolve(),
|
||||||
package=os.path.abspath(options.package_dir)
|
package=options.package_dir.resolve()
|
||||||
)
|
)
|
||||||
shell([test_command_prepared], cwd='c:\\', env=virtualenv_env)
|
shell(test_command_prepared, cwd='c:\\', env=virtualenv_env)
|
||||||
|
|
||||||
# clean up
|
# clean up
|
||||||
shutil.rmtree(venv_dir)
|
shutil.rmtree(venv_dir)
|
||||||
|
|
||||||
# we're all done here; move it to output (remove if already exists)
|
# we're all done here; move it to output (remove if already exists)
|
||||||
dst = os.path.join(options.output_dir, os.path.basename(repaired_wheel))
|
shutil.move(str(repaired_wheel), options.output_dir)
|
||||||
if os.path.isfile(dst):
|
|
||||||
os.remove(dst)
|
|
||||||
shutil.move(repaired_wheel, dst)
|
|
||||||
|
|||||||
+28
-2
@@ -39,5 +39,31 @@ Forcing `distutils` or `setuptools` to use a more recent version of MSVC that su
|
|||||||
1. Set the environment variables `DISTUTILS_USE_SDK=1` and `MSSdk=1`. These two environment variables will tell `distutils`/`setuptools` to not search and set up a build environment that uses Visual C++ for Python 2.7 (aka. MSVC 9).
|
1. Set the environment variables `DISTUTILS_USE_SDK=1` and `MSSdk=1`. These two environment variables will tell `distutils`/`setuptools` to not search and set up a build environment that uses Visual C++ for Python 2.7 (aka. MSVC 9).
|
||||||
2. Set up the build Visual Studio build environment you want to use, making sure that e.g. `PATH` contains `cl`, `link`, etc.
|
2. Set up the build Visual Studio build environment you want to use, making sure that e.g. `PATH` contains `cl`, `link`, etc.
|
||||||
- Usually, this can be done through `vcvarsall.bat x86` or `vcvarsall.bat x64`. The exact location of this file depends on the installation, but the default path for VS 2019 Community (e.g. used in AppVeyor's `Visual Studio 2019` image) is `C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat`. **Note**: `vcvarsall.bat` changes the environment variables, so this cannot be run in a subprocess/subshell and consequently running `vsvarsall.bat` in `CIBW_BEFORE_BUILD` does not have any effect.
|
- Usually, this can be done through `vcvarsall.bat x86` or `vcvarsall.bat x64`. The exact location of this file depends on the installation, but the default path for VS 2019 Community (e.g. used in AppVeyor's `Visual Studio 2019` image) is `C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat`. **Note**: `vcvarsall.bat` changes the environment variables, so this cannot be run in a subprocess/subshell and consequently running `vsvarsall.bat` in `CIBW_BEFORE_BUILD` does not have any effect.
|
||||||
- In Azure Pipelines, [a `VSBuild` task is available](https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/build/visual-studio-build) and GitHub Actions has [an action `microsoft/setup-msbuild`](https://github.com/microsoft/setup-msbuild) that help setting up the Visual Studio environment.
|
- In Azure Pipelines, [a `VSBuild` task is available](https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/build/visual-studio-build)
|
||||||
3. Next, call `cibuildwheel`. Unfortunately, MSVC has separate toolchains for compiling 32-bit and 64-bit, so you will need to run `cibuildwheel` twice: once with `CIBW_BUILD=*-win32` after setting up the `x86` build environment, and once with `CIBW_BUILD=*-win_amd64` in a `x64` enviroment (see previous step).
|
- In GitHub Actions, the default shell is powershell, so you'll need to use `shell: cmd` to source a `.bat` file, and the directory is `"C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat"`. Or you can use an action, such as the [`ilammy/msvc-dev-cmd@v1`](https://github.com/ilammy/msvc-dev-cmd) action to setup the environment (see example below).
|
||||||
|
3. Next, call `cibuildwheel`. Unfortunately, MSVC has separate toolchains for compiling 32-bit and 64-bit, so you will need to run `cibuildwheel` twice: once with `CIBW_BUILD=*-win32` after setting up the `x86` build environment, and once with `CIBW_BUILD=*-win_amd64` in a `x64` environment (see previous step).
|
||||||
|
|
||||||
|
|
||||||
|
**GitHub Action example with ilammy/msvc-dev-cmd**:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: ilammy/msvc-dev-cmd@v1
|
||||||
|
|
||||||
|
- name: Build 64-bit wheel
|
||||||
|
run: python -m cibuildwheel --output-dir wheelhouse
|
||||||
|
env:
|
||||||
|
CIBW_BUILD: cp27-win_amd64
|
||||||
|
DISTUTILS_USE_SDK: 1
|
||||||
|
MSSdk: 1
|
||||||
|
|
||||||
|
- uses: ilammy/msvc-dev-cmd@v1
|
||||||
|
with:
|
||||||
|
arch: x86
|
||||||
|
|
||||||
|
- name: Build 32-bit wheel
|
||||||
|
run: python -m cibuildwheel --output-dir wheelhouse
|
||||||
|
env:
|
||||||
|
CIBW_BUILD: cp27-win32
|
||||||
|
DISTUTILS_USE_SDK: 1
|
||||||
|
MSSdk: 1
|
||||||
|
```
|
||||||
|
|||||||
@@ -33,4 +33,4 @@ Obviously, manual steps are for chumps, so we can automate this a little by usin
|
|||||||
|
|
||||||
If you don't need much control over the release of a package, you can set up cibuildwheel to deliver the wheels straight to PyPI. This doesn't require anycloud storage to work - you just need to bump the version and tag it.
|
If you don't need much control over the release of a package, you can set up cibuildwheel to deliver the wheels straight to PyPI. This doesn't require anycloud storage to work - you just need to bump the version and tag it.
|
||||||
|
|
||||||
Check out [this example repo](https://github.com/joerick/cibuildwheel-autopypi-example) for instructions on how to set this up.
|
[`examples/travis-ci-deploy.yml`](https://github.com/joerick/cibuildwheel/blob/master/examples/travis-ci-deploy.yml) and [`examples/github-deploy.yml`](https://github.com/joerick/cibuildwheel/blob/master/examples/github-deploy.yml) are example configurations that automatocially upload wheels to PyPI. Also check out [this example repo](https://github.com/joerick/cibuildwheel-autopypi-example) for more detailed instructions on how to set this up.
|
||||||
|
|||||||
@@ -2,4 +2,3 @@
|
|||||||
$('.rst-content pre')
|
$('.rst-content pre')
|
||||||
.prev('blockquote')
|
.prev('blockquote')
|
||||||
.addClass('code-block-filename');
|
.addClass('code-block-filename');
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import cgi
|
import cgi
|
||||||
import io
|
|
||||||
import os
|
|
||||||
import re
|
import re
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
import mkdocs
|
import mkdocs
|
||||||
|
|
||||||
@@ -41,13 +40,12 @@ class ImportMarkdownPlugin(mkdocs.plugins.BasePlugin):
|
|||||||
def found_include_tag(match):
|
def found_include_tag(match):
|
||||||
filename = match.group('filename')
|
filename = match.group('filename')
|
||||||
|
|
||||||
file_path_abs = os.path.join(os.path.dirname(page_src_path), filename)
|
file_path_abs = Path(page_src_path).parent / filename
|
||||||
|
|
||||||
if not os.path.exists(file_path_abs):
|
if not file_path_abs.exists():
|
||||||
raise ValueError('file not found', filename)
|
raise ValueError('file not found', filename)
|
||||||
|
|
||||||
with io.open(file_path_abs, encoding='utf8') as f:
|
text_to_include = file_path_abs.read_text(encoding='utf8')
|
||||||
text_to_include = f.read()
|
|
||||||
|
|
||||||
# Allow good practice of having a final newline in the file
|
# Allow good practice of having a final newline in the file
|
||||||
if text_to_include.endswith('\n'):
|
if text_to_include.endswith('\n'):
|
||||||
@@ -60,13 +58,12 @@ class ImportMarkdownPlugin(mkdocs.plugins.BasePlugin):
|
|||||||
start = match.group('start')
|
start = match.group('start')
|
||||||
end = match.group('end')
|
end = match.group('end')
|
||||||
|
|
||||||
file_path_abs = os.path.join(os.path.dirname(page_src_path), filename)
|
file_path_abs = Path(page_src_path).parent / filename
|
||||||
|
|
||||||
if not os.path.exists(file_path_abs):
|
if not file_path_abs.exists():
|
||||||
raise ValueError('file not found', filename)
|
raise ValueError('file not found', filename)
|
||||||
|
|
||||||
with io.open(file_path_abs, encoding='utf8') as f:
|
text_to_include = file_path_abs.read_text(encoding='utf8')
|
||||||
text_to_include = f.read()
|
|
||||||
|
|
||||||
if start:
|
if start:
|
||||||
_, _, text_to_include = text_to_include.partition(start)
|
_, _, text_to_include = text_to_include.partition(start)
|
||||||
|
|||||||
+54
-30
@@ -52,7 +52,6 @@ env:
|
|||||||
|
|
||||||
## Build selection
|
## Build selection
|
||||||
|
|
||||||
|
|
||||||
### `CIBW_PLATFORM` {: #platform}
|
### `CIBW_PLATFORM` {: #platform}
|
||||||
|
|
||||||
> Override the auto-detected target platform
|
> Override the auto-detected target platform
|
||||||
@@ -67,6 +66,7 @@ For `linux` you need Docker running, on macOS or Linux. For `macos`, you need a
|
|||||||
|
|
||||||
This option can also be set using the command-line option `--platform`.
|
This option can also be set using the command-line option `--platform`.
|
||||||
|
|
||||||
|
|
||||||
### `CIBW_BUILD`, `CIBW_SKIP` {: #build-skip}
|
### `CIBW_BUILD`, `CIBW_SKIP` {: #build-skip}
|
||||||
|
|
||||||
> Choose the Python versions to build
|
> Choose the Python versions to build
|
||||||
@@ -155,7 +155,6 @@ CIBW_SKIP: pp*
|
|||||||
|
|
||||||
## Build customization
|
## Build customization
|
||||||
|
|
||||||
|
|
||||||
### `CIBW_ENVIRONMENT` {: #environment}
|
### `CIBW_ENVIRONMENT` {: #environment}
|
||||||
> Set environment variables needed during the build
|
> Set environment variables needed during the build
|
||||||
|
|
||||||
@@ -191,6 +190,28 @@ CIBW_ENVIRONMENT: "BUILD_TIME=$(date) SAMPLE_TEXT=\"sample text\""
|
|||||||
!!! note
|
!!! note
|
||||||
`cibuildwheel` always defines the environment variable `CIBUILDWHEEL=1`. This can be useful for [building wheels with optional extensions](faq.md#building-packages-with-optional-c-extensions).
|
`cibuildwheel` always defines the environment variable `CIBUILDWHEEL=1`. This can be useful for [building wheels with optional extensions](faq.md#building-packages-with-optional-c-extensions).
|
||||||
|
|
||||||
|
### `CIBW_BEFORE_ALL` {: #before-all}
|
||||||
|
> Execute a shell command on the build system before any wheels are built.
|
||||||
|
|
||||||
|
Shell command to prepare a common part of the project (e.g. build or install libraries which does not depend on the specific version of Python).
|
||||||
|
|
||||||
|
This option is very useful for the Linux build, where builds take place in isolated Docker containers managed by cibuildwheel. This command will run inside the container before the wheel builds start. Note, if you're building both x86_64 and i686 wheels (the default), your build uses two different Docker images. In that case, this command will execute twice - once per build container.
|
||||||
|
|
||||||
|
The placeholder `{package}` can be used here; it will be replaced by the path to the package being built by `cibuildwheel`.
|
||||||
|
|
||||||
|
On Windows and macOS, the version of Python available inside `CIBW_BEFORE_ALL` is whatever is available on the host machine. On Linux, a modern Python version is available on PATH.
|
||||||
|
|
||||||
|
Platform-specific variants also available:<br/>
|
||||||
|
`CIBW_BEFORE_ALL_MACOS` | `CIBW_BEFORE_ALL_WINDOWS` | `CIBW_BEFORE_ALL_LINUX`
|
||||||
|
|
||||||
|
#### Examples
|
||||||
|
```yaml
|
||||||
|
# build third party library
|
||||||
|
CIBW_BEFORE_ALL: make -C third_party_lib
|
||||||
|
|
||||||
|
# install system library
|
||||||
|
CIBW_BEFORE_ALL_LINUX: yum install -y libffi-dev
|
||||||
|
```
|
||||||
|
|
||||||
### `CIBW_BEFORE_BUILD` {: #before-build}
|
### `CIBW_BEFORE_BUILD` {: #before-build}
|
||||||
> Execute a shell command preparing each wheel's build
|
> Execute a shell command preparing each wheel's build
|
||||||
@@ -296,6 +317,7 @@ CIBW_MANYLINUX_X86_64_IMAGE: dockcross/manylinux-x64
|
|||||||
CIBW_MANYLINUX_I686_IMAGE: dockcross/manylinux-x86
|
CIBW_MANYLINUX_I686_IMAGE: dockcross/manylinux-x86
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### `CIBW_DEPENDENCY_VERSIONS` {: #dependency-versions}
|
### `CIBW_DEPENDENCY_VERSIONS` {: #dependency-versions}
|
||||||
> Specify how cibuildwheel controls the versions of the tools it uses
|
> Specify how cibuildwheel controls the versions of the tools it uses
|
||||||
|
|
||||||
@@ -372,6 +394,35 @@ CIBW_TEST_COMMAND: pytest {package}/tests
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### `CIBW_BEFORE_TEST` {: #before-test}
|
||||||
|
> Execute a shell command before testing each wheel
|
||||||
|
|
||||||
|
A shell command to run in **each** test virtual environment, before your wheel is installed and tested. This is useful if you need to install a non pip package, change values of environment variables
|
||||||
|
or perform multi step pip installation (e.g. installing `scikit-build` or `cython` before install test package)
|
||||||
|
|
||||||
|
The active Python binary can be accessed using `python`, and pip with `pip`; `cibuildwheel` makes sure the right version of Python and pip will be executed. The placeholder `{package}` can be used here; it will be replaced by the path to the package being built by `cibuildwheel`.
|
||||||
|
|
||||||
|
The command is run in a shell, so you can write things like `cmd1 && cmd2`.
|
||||||
|
|
||||||
|
Platform-specific variants also available:<br/>
|
||||||
|
`CIBW_BEFORE_TEST_MACOS` | `CIBW_BEFORE_TEST_WINDOWS` | `CIBW_BEFORE_TEST_LINUX`
|
||||||
|
|
||||||
|
#### Examples
|
||||||
|
```yaml
|
||||||
|
# install test dependencies with overwritten environment variables.
|
||||||
|
CIBW_BEFORE_TEST: CC=gcc CXX=g++ pip install -r requirements.txt
|
||||||
|
|
||||||
|
# chain commands using &&
|
||||||
|
CIBW_BEFORE_TEST: rm -rf ./data/cache && mkdir -p ./data/cache
|
||||||
|
|
||||||
|
# install non pip python package
|
||||||
|
CIBW_BEFORE_TEST: cd some_dir; ./configure; make; make install
|
||||||
|
|
||||||
|
# install python packages that are required to install test dependencies
|
||||||
|
CIBW_BEFORE_TEST: pip install cmake scikit-build
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### `CIBW_TEST_REQUIRES` {: #test-requires}
|
### `CIBW_TEST_REQUIRES` {: #test-requires}
|
||||||
> Install Python dependencies before running the tests
|
> Install Python dependencies before running the tests
|
||||||
|
|
||||||
@@ -411,33 +462,6 @@ Platform-specific variants also available:<br/>
|
|||||||
CIBW_TEST_EXTRAS: test,qt
|
CIBW_TEST_EXTRAS: test,qt
|
||||||
```
|
```
|
||||||
|
|
||||||
### `CIBW_BEFORE_TEST` {: #before-test}
|
|
||||||
> Execute a shell command before testing each wheel
|
|
||||||
|
|
||||||
A shell command to run in **each** test virtual environment, before your wheel is installed and tested. This is useful if you need to install a non pip package, change values of environment variables
|
|
||||||
or perform multi step pip installation (e.g. installing `scikit-build` or `cython` before install test package)
|
|
||||||
|
|
||||||
The active Python binary can be accessed using `python`, and pip with `pip`; `cibuildwheel` makes sure the right version of Python and pip will be executed. 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
|
## Other
|
||||||
|
|
||||||
@@ -601,7 +625,7 @@ optional arguments:
|
|||||||
var url = 'https://cibuildwheel.readthedocs.io/en/stable/options/#'+option.id;
|
var url = 'https://cibuildwheel.readthedocs.io/en/stable/options/#'+option.id;
|
||||||
var namesMarkdown = $.map(optionNames, function(n) {
|
var namesMarkdown = $.map(optionNames, function(n) {
|
||||||
return '[`'+n+'`]('+url+') '
|
return '[`'+n+'`]('+url+') '
|
||||||
}).join(' ')
|
}).join(' <br> ')
|
||||||
|
|
||||||
markdown += '| '+namesMarkdown+' '
|
markdown += '| '+namesMarkdown+' '
|
||||||
markdown += '| '+option.description.trim()+' '
|
markdown += '| '+option.description.trim()+' '
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ Commit this file, and push to Github - either to your default branch, or to a PR
|
|||||||
|
|
||||||
For more info on this file, check out the [docs](https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions).
|
For more info on this file, check out the [docs](https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions).
|
||||||
|
|
||||||
|
[`examples/github-deploy.yml`](https://github.com/joerick/cibuildwheel/blob/master/examples/github-deploy.yml) extends this minimal example with a demonstration of how to automatically upload the built wheels to PyPI.
|
||||||
|
|
||||||
# Azure Pipelines [linux/mac/windows] {: #azure-pipelines}
|
# Azure Pipelines [linux/mac/windows] {: #azure-pipelines}
|
||||||
|
|
||||||
To build Linux, Mac, and Windows wheels on Azure Pipelines, create a `azure-pipelines.yml` file in your repo.
|
To build Linux, Mac, and Windows wheels on Azure Pipelines, create a `azure-pipelines.yml` file in your repo.
|
||||||
@@ -46,6 +48,8 @@ Commit this file, enable building of your repo on Travis CI, and push.
|
|||||||
|
|
||||||
Then setup a deployment method by following the [Travis CI deployment docs](https://docs.travis-ci.com/user/deployment/), or see [Delivering to PyPI](deliver-to-pypi.md). For more info on `.travis.yml`, check out the [docs](https://docs.travis-ci.com/).
|
Then setup a deployment method by following the [Travis CI deployment docs](https://docs.travis-ci.com/user/deployment/), or see [Delivering to PyPI](deliver-to-pypi.md). For more info on `.travis.yml`, check out the [docs](https://docs.travis-ci.com/).
|
||||||
|
|
||||||
|
[`examples/travis-deploy.yml`](https://github.com/joerick/cibuildwheel/blob/master/examples/travis-deploy.yml) extends this minimal example with a demonstration of how to automatically upload the built wheels to PyPI.
|
||||||
|
|
||||||
# CircleCI [linux/mac] {: #circleci}
|
# CircleCI [linux/mac] {: #circleci}
|
||||||
|
|
||||||
To build Linux and Mac wheels on CircleCI, create a `.circleci/config.yml` file in your repo,
|
To build Linux and Mac wheels on CircleCI, create a `.circleci/config.yml` file in your repo,
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ stack: python 3.7
|
|||||||
init:
|
init:
|
||||||
- cmd: set PATH=C:\Python37;C:\Python37\Scripts;%PATH%
|
- cmd: set PATH=C:\Python37;C:\Python37\Scripts;%PATH%
|
||||||
|
|
||||||
install: python -m pip install cibuildwheel==1.4.2
|
install: python -m pip install cibuildwheel==1.5.2
|
||||||
|
|
||||||
build_script: python -m cibuildwheel --output-dir wheelhouse
|
build_script: python -m cibuildwheel --output-dir wheelhouse
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ jobs:
|
|||||||
- task: UsePythonVersion@0
|
- task: UsePythonVersion@0
|
||||||
- bash: |
|
- bash: |
|
||||||
python3 -m pip install --upgrade pip
|
python3 -m pip install --upgrade pip
|
||||||
pip3 install cibuildwheel==1.4.2
|
pip3 install cibuildwheel==1.5.2
|
||||||
cibuildwheel --output-dir wheelhouse .
|
cibuildwheel --output-dir wheelhouse .
|
||||||
- task: PublishBuildArtifacts@1
|
- task: PublishBuildArtifacts@1
|
||||||
inputs: {pathtoPublish: 'wheelhouse'}
|
inputs: {pathtoPublish: 'wheelhouse'}
|
||||||
@@ -16,7 +16,7 @@ jobs:
|
|||||||
- task: UsePythonVersion@0
|
- task: UsePythonVersion@0
|
||||||
- bash: |
|
- bash: |
|
||||||
python3 -m pip install --upgrade pip
|
python3 -m pip install --upgrade pip
|
||||||
pip3 install cibuildwheel==1.4.2
|
pip3 install cibuildwheel==1.5.2
|
||||||
cibuildwheel --output-dir wheelhouse .
|
cibuildwheel --output-dir wheelhouse .
|
||||||
- task: PublishBuildArtifacts@1
|
- task: PublishBuildArtifacts@1
|
||||||
inputs: {pathtoPublish: 'wheelhouse'}
|
inputs: {pathtoPublish: 'wheelhouse'}
|
||||||
@@ -29,7 +29,7 @@ jobs:
|
|||||||
displayName: Install Visual C++ for Python 2.7
|
displayName: Install Visual C++ for Python 2.7
|
||||||
- bash: |
|
- bash: |
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install cibuildwheel==1.4.2
|
pip install cibuildwheel==1.5.2
|
||||||
cibuildwheel --output-dir wheelhouse .
|
cibuildwheel --output-dir wheelhouse .
|
||||||
- task: PublishBuildArtifacts@1
|
- task: PublishBuildArtifacts@1
|
||||||
inputs: {pathtoPublish: 'wheelhouse'}
|
inputs: {pathtoPublish: 'wheelhouse'}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ jobs:
|
|||||||
- run:
|
- run:
|
||||||
name: Build the Linux wheels.
|
name: Build the Linux wheels.
|
||||||
command: |
|
command: |
|
||||||
pip3 install --user cibuildwheel==1.4.2
|
pip3 install --user cibuildwheel==1.5.2
|
||||||
cibuildwheel --output-dir wheelhouse
|
cibuildwheel --output-dir wheelhouse
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: wheelhouse/
|
path: wheelhouse/
|
||||||
@@ -25,7 +25,7 @@ jobs:
|
|||||||
- run:
|
- run:
|
||||||
name: Build the OS X wheels.
|
name: Build the OS X wheels.
|
||||||
command: |
|
command: |
|
||||||
pip3 install --user cibuildwheel==1.4.2
|
pip3 install --user cibuildwheel==1.5.2
|
||||||
cibuildwheel --output-dir wheelhouse
|
cibuildwheel --output-dir wheelhouse
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: wheelhouse/
|
path: wheelhouse/
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
name: Build and upload to PyPI
|
||||||
|
|
||||||
|
# Build on every branch push, tag push, and pull request change:
|
||||||
|
on: [push, pull_request]
|
||||||
|
# Alternatively, to publish when a (published) GitHub Release is created, use the following:
|
||||||
|
# on:
|
||||||
|
# push:
|
||||||
|
# pull_request:
|
||||||
|
# release:
|
||||||
|
# types:
|
||||||
|
# - published
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build_wheels:
|
||||||
|
name: Build wheels on ${{ matrix.os }}
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-18.04, windows-latest, macos-latest]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- uses: actions/setup-python@v2
|
||||||
|
name: Install Python
|
||||||
|
with:
|
||||||
|
python-version: '3.7'
|
||||||
|
|
||||||
|
- name: Install cibuildwheel
|
||||||
|
run: |
|
||||||
|
python -m pip install cibuildwheel==1.5.2
|
||||||
|
|
||||||
|
- name: Install Visual C++ for Python 2.7
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
run: |
|
||||||
|
choco install vcpython27 -f -y
|
||||||
|
|
||||||
|
- name: Build wheels
|
||||||
|
run: |
|
||||||
|
python -m cibuildwheel --output-dir wheelhouse
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
path: ./wheelhouse/*.whl
|
||||||
|
|
||||||
|
build_sdist:
|
||||||
|
name: Build source distribution
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- uses: actions/setup-python@v2
|
||||||
|
name: Install Python
|
||||||
|
with:
|
||||||
|
python-version: '3.7'
|
||||||
|
|
||||||
|
- name: Build sdist
|
||||||
|
run: python setup.py sdist
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
path: dist/*.tar.gz
|
||||||
|
|
||||||
|
upload_pypi:
|
||||||
|
needs: [build_wheels, build_sdist]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
# upload to PyPI on every tag starting with 'v'
|
||||||
|
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/v')
|
||||||
|
# alternatively, to publish when a GitHub Release is created, use the following rule:
|
||||||
|
# if: github.event_name == 'release' && github.event.action == 'published'
|
||||||
|
steps:
|
||||||
|
- uses: actions/download-artifact@v2
|
||||||
|
with:
|
||||||
|
name: artifact
|
||||||
|
path: dist
|
||||||
|
|
||||||
|
- uses: pypa/gh-action-pypi-publish@master
|
||||||
|
with:
|
||||||
|
user: __token__
|
||||||
|
password: ${{ secrets.pypi_password }}
|
||||||
|
# To test: repository_url: https://test.pypi.org/legacy/
|
||||||
+19
-20
@@ -4,34 +4,33 @@ on: [push, pull_request]
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build_wheels:
|
build_wheels:
|
||||||
name: Build wheel on ${{ matrix.os }}
|
name: Build wheels on ${{ matrix.os }}
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-18.04, windows-latest, macos-latest]
|
os: [ubuntu-18.04, windows-latest, macos-latest]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- uses: actions/setup-python@v1
|
- uses: actions/setup-python@v2
|
||||||
name: Install Python
|
name: Install Python
|
||||||
with:
|
with:
|
||||||
python-version: '3.7'
|
python-version: '3.7'
|
||||||
|
|
||||||
- name: Install cibuildwheel
|
- name: Install cibuildwheel
|
||||||
run: |
|
run: |
|
||||||
python -m pip install cibuildwheel==1.4.2
|
python -m pip install cibuildwheel==1.5.2
|
||||||
|
|
||||||
- name: Install Visual C++ for Python 2.7
|
- name: Install Visual C++ for Python 2.7
|
||||||
if: startsWith(matrix.os, 'windows')
|
if: runner.os == 'Windows'
|
||||||
run: |
|
run: |
|
||||||
choco install vcpython27 -f -y
|
choco install vcpython27 -f -y
|
||||||
|
|
||||||
- name: Build wheel
|
- name: Build wheels
|
||||||
run: |
|
run: |
|
||||||
python -m cibuildwheel --output-dir wheelhouse
|
python -m cibuildwheel --output-dir wheelhouse
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v1
|
- uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: wheels
|
path: ./wheelhouse/*.whl
|
||||||
path: ./wheelhouse
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ env:
|
|||||||
# Note: TWINE_PASSWORD is set to a PyPI API token in Travis settings
|
# Note: TWINE_PASSWORD is set to a PyPI API token in Travis settings
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- python3 -m pip install cibuildwheel==1.4.2
|
- python3 -m pip install cibuildwheel==1.5.2
|
||||||
|
|
||||||
script:
|
script:
|
||||||
# build the wheels, put them into './wheelhouse'
|
# build the wheels, put them into './wheelhouse'
|
||||||
@@ -26,7 +26,7 @@ jobs:
|
|||||||
- ln -s /c/Python38/python.exe /c/Python38/python3.exe
|
- ln -s /c/Python38/python.exe /c/Python38/python3.exe
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- python3 -m pip install cibuildwheel==1.4.2
|
- python3 -m pip install cibuildwheel==1.5.2
|
||||||
|
|
||||||
script:
|
script:
|
||||||
# build the wheels, put them into './wheelhouse'
|
# build the wheels, put them into './wheelhouse'
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ jobs:
|
|||||||
- stage: deploy
|
- stage: deploy
|
||||||
name: Build and deploy Linux wheels
|
name: Build and deploy Linux wheels
|
||||||
services: docker
|
services: docker
|
||||||
install: python3 -m pip install cibuildwheel==1.4.2
|
install: python3 -m pip install cibuildwheel==1.5.2
|
||||||
script: python3 -m cibuildwheel --output-dir wheelhouse
|
script: python3 -m cibuildwheel --output-dir wheelhouse
|
||||||
after_success: |
|
after_success: |
|
||||||
python3 -m pip install twine
|
python3 -m pip install twine
|
||||||
@@ -65,7 +65,7 @@ jobs:
|
|||||||
name: Build and deploy macOS wheels
|
name: Build and deploy macOS wheels
|
||||||
os: osx
|
os: osx
|
||||||
language: shell
|
language: shell
|
||||||
install: python3 -m pip install cibuildwheel==1.4.2
|
install: python3 -m pip install cibuildwheel==1.5.2
|
||||||
script: python3 -m cibuildwheel --output-dir wheelhouse
|
script: python3 -m cibuildwheel --output-dir wheelhouse
|
||||||
after_success: |
|
after_success: |
|
||||||
python3 -m pip install twine
|
python3 -m pip install twine
|
||||||
@@ -75,7 +75,7 @@ jobs:
|
|||||||
name: Build and deploy Windows wheels
|
name: Build and deploy Windows wheels
|
||||||
os: windows
|
os: windows
|
||||||
language: shell
|
language: shell
|
||||||
install: python3 -m pip install cibuildwheel==1.4.2
|
install: python3 -m pip install cibuildwheel==1.5.2
|
||||||
script: python3 -m cibuildwheel --output-dir wheelhouse
|
script: python3 -m cibuildwheel --output-dir wheelhouse
|
||||||
after_success: |
|
after_success: |
|
||||||
python3 -m pip install twine
|
python3 -m pip install twine
|
||||||
|
|||||||
@@ -1,20 +1,18 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import io
|
from pathlib import Path
|
||||||
import os
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from distutils.core import setup
|
from distutils.core import setup
|
||||||
|
|
||||||
this_directory = os.path.dirname(__file__)
|
this_directory = Path(__file__).parent
|
||||||
with io.open(os.path.join(this_directory, 'README.md'), encoding='utf-8') as f:
|
long_description = (this_directory / 'README.md').read_text(encoding='utf-8')
|
||||||
long_description = f.read()
|
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='cibuildwheel',
|
name='cibuildwheel',
|
||||||
version='1.4.2',
|
version='1.5.2',
|
||||||
install_requires=['bashlex!=0.13'],
|
install_requires=['bashlex!=0.13', 'toml'],
|
||||||
description="Build Python wheels on CI with minimal configuration.",
|
description="Build Python wheels on CI with minimal configuration.",
|
||||||
long_description=long_description,
|
long_description=long_description,
|
||||||
long_description_content_type='text/markdown',
|
long_description_content_type='text/markdown',
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import os
|
||||||
|
import textwrap
|
||||||
|
|
||||||
|
from . import utils
|
||||||
|
from . import test_projects
|
||||||
|
|
||||||
|
project_with_before_build_asserts = test_projects.new_c_project(
|
||||||
|
setup_py_add=textwrap.dedent(r'''
|
||||||
|
# assert that the Python version as written to text_info.txt in the CIBW_BEFORE_ALL step
|
||||||
|
# is the same one as is currently running.
|
||||||
|
with open("text_info.txt") as f:
|
||||||
|
stored_text = f.read()
|
||||||
|
|
||||||
|
print("## stored text: " + stored_text)
|
||||||
|
assert stored_text == "sample text 123"
|
||||||
|
''')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test(tmp_path):
|
||||||
|
project_dir = tmp_path / 'project'
|
||||||
|
project_with_before_build_asserts.generate(project_dir)
|
||||||
|
|
||||||
|
with open(os.path.join(project_dir, "text_info.txt"), mode='w') as ff:
|
||||||
|
print("dummy text", file=ff)
|
||||||
|
|
||||||
|
# build the wheels
|
||||||
|
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
||||||
|
# write python version information to a temporary file, this is
|
||||||
|
# checked in setup.py
|
||||||
|
'CIBW_BEFORE_ALL': '''python -c "import os;open('{project}/text_info.txt', 'w').write('sample text '+os.environ.get('TEST_VAL', ''))"''',
|
||||||
|
'CIBW_ENVIRONMENT': "TEST_VAL='123'"
|
||||||
|
})
|
||||||
|
|
||||||
|
# also check that we got the right wheels
|
||||||
|
os.remove(os.path.join(project_dir, "text_info.txt"))
|
||||||
|
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
||||||
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
import os
|
|
||||||
import re
|
import re
|
||||||
import pytest
|
import pytest
|
||||||
import textwrap
|
import textwrap
|
||||||
@@ -39,11 +38,9 @@ VERSION_REGEX = r'([\w-]+)==([^\s]+)'
|
|||||||
|
|
||||||
|
|
||||||
def get_versions_from_constraint_file(constraint_file):
|
def get_versions_from_constraint_file(constraint_file):
|
||||||
with open(constraint_file, encoding='utf8') as f:
|
constraint_file_text = constraint_file.read_text(encoding='utf8')
|
||||||
constraint_file_text = f.read()
|
|
||||||
|
|
||||||
versions = {}
|
versions = {}
|
||||||
|
|
||||||
for package, version in re.findall(VERSION_REGEX, constraint_file_text):
|
for package, version in re.findall(VERSION_REGEX, constraint_file_text):
|
||||||
versions[package] = version
|
versions[package] = version
|
||||||
|
|
||||||
@@ -73,7 +70,7 @@ def test_pinned_versions(tmp_path, python_version):
|
|||||||
constraint_filename = 'constraints.txt'
|
constraint_filename = 'constraints.txt'
|
||||||
build_pattern = '[cp]p38-*'
|
build_pattern = '[cp]p38-*'
|
||||||
|
|
||||||
constraint_file = os.path.join(cibuildwheel.util.resources_dir, constraint_filename)
|
constraint_file = cibuildwheel.util.resources_dir / constraint_filename
|
||||||
constraint_versions = get_versions_from_constraint_file(constraint_file)
|
constraint_versions = get_versions_from_constraint_file(constraint_file)
|
||||||
|
|
||||||
for package in ['pip', 'setuptools', 'wheel', 'virtualenv']:
|
for package in ['pip', 'setuptools', 'wheel', 'virtualenv']:
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import textwrap
|
||||||
|
from . import test_projects
|
||||||
|
from . import utils
|
||||||
|
|
||||||
|
basic_project = test_projects.new_c_project(
|
||||||
|
setup_py_add=textwrap.dedent(
|
||||||
|
"""
|
||||||
|
# Will fail if PEP 518 does work
|
||||||
|
import sys
|
||||||
|
import requests
|
||||||
|
if sys.version_info < (3, 6, 0):
|
||||||
|
assert requests.__version__ == "2.22.0", "Requests found but wrong version ({0})".format(requests.__version__)
|
||||||
|
else:
|
||||||
|
assert requests.__version__ == "2.23.0", "Requests found but wrong version ({0})".format(requests.__version__)
|
||||||
|
|
||||||
|
# Just making sure environment is still set
|
||||||
|
import os
|
||||||
|
if os.environ.get("CIBUILDWHEEL", "0") != "1":
|
||||||
|
raise Exception("CIBUILDWHEEL environment variable is not set to 1")
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
basic_project.files[
|
||||||
|
"pyproject.toml"
|
||||||
|
] = """
|
||||||
|
[build-system]
|
||||||
|
requires = [
|
||||||
|
"setuptools >= 42",
|
||||||
|
"wheel",
|
||||||
|
"requests==2.22.0; python_version<'3.6'",
|
||||||
|
"requests==2.23.0; python_version>='3.6'"
|
||||||
|
]
|
||||||
|
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def test_pep518(tmp_path):
|
||||||
|
|
||||||
|
project_dir = tmp_path / "project"
|
||||||
|
basic_project.generate(project_dir)
|
||||||
|
|
||||||
|
# build the wheels
|
||||||
|
actual_wheels = utils.cibuildwheel_run(project_dir)
|
||||||
|
|
||||||
|
# check that the expected wheels are produced
|
||||||
|
expected_wheels = utils.expected_wheels("spam", "0.1.0")
|
||||||
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
import os
|
from pathlib import Path
|
||||||
|
|
||||||
import jinja2
|
import jinja2
|
||||||
|
|
||||||
from typing import Union, Dict, Any
|
from typing import Union, Dict, Any
|
||||||
|
|
||||||
|
|
||||||
@@ -14,6 +16,8 @@ class TestProject:
|
|||||||
|
|
||||||
Write out to the filesystem using `generate`.
|
Write out to the filesystem using `generate`.
|
||||||
'''
|
'''
|
||||||
|
__test__ = False # Have pytest ignore this class on `from .test_projects import TestProject`
|
||||||
|
|
||||||
files: FilesDict
|
files: FilesDict
|
||||||
template_context: TemplateContext
|
template_context: TemplateContext
|
||||||
|
|
||||||
@@ -21,12 +25,12 @@ class TestProject:
|
|||||||
self.files = {}
|
self.files = {}
|
||||||
self.template_context = {}
|
self.template_context = {}
|
||||||
|
|
||||||
def generate(self, path: str):
|
def generate(self, path: Path):
|
||||||
for filename, content in self.files.items():
|
for filename, content in self.files.items():
|
||||||
file_path = os.path.join(path, filename)
|
file_path = path / filename
|
||||||
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
with open(file_path, 'w', encoding='utf8') as f:
|
with file_path.open('w', encoding='utf8') as f:
|
||||||
if isinstance(content, jinja2.Template):
|
if isinstance(content, jinja2.Template):
|
||||||
content = content.render(self.template_context)
|
content = content.render(self.template_context)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import os
|
from pathlib import Path
|
||||||
|
|
||||||
import jinja2
|
import jinja2
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ def test(capfd, tmp_path):
|
|||||||
project_dir = tmp_path / 'project'
|
project_dir = tmp_path / 'project'
|
||||||
subdir_package_project.generate(project_dir)
|
subdir_package_project.generate(project_dir)
|
||||||
|
|
||||||
package_dir = os.path.join(project_dir, 'src', 'spam')
|
package_dir = Path('src', 'spam')
|
||||||
# build the wheels
|
# build the wheels
|
||||||
actual_wheels = utils.cibuildwheel_run(project_dir, package_dir=package_dir, add_env={
|
actual_wheels = utils.cibuildwheel_run(project_dir, package_dir=package_dir, add_env={
|
||||||
'CIBW_BEFORE_BUILD': 'python {project}/bin/before_build.py',
|
'CIBW_BEFORE_BUILD': 'python {project}/bin/before_build.py',
|
||||||
|
|||||||
@@ -46,12 +46,15 @@ class TestSpam(TestCase):
|
|||||||
self.assertNotEqual(0, spam.system('python -c "exit(1)"'))
|
self.assertNotEqual(0, spam.system('python -c "exit(1)"'))
|
||||||
|
|
||||||
def test_virtualenv(self):
|
def test_virtualenv(self):
|
||||||
virtualenv_path = os.environ.get("__CIBW_VIRTUALENV_PATH__")
|
# sys.prefix is different from sys.base_prefix when running a virtualenv
|
||||||
if not virtualenv_path:
|
# See https://docs.python.org/3/library/venv.html, which virtualenv seems
|
||||||
self.fail("No virtualenv path defined in environment variable __CIBW_VIRTUALENV_PATH__")
|
# to honor in recent releases
|
||||||
|
# Python 2 doesn't have sys.base_prefix by default
|
||||||
|
if not hasattr(sys, 'base_prefix') or sys.prefix == sys.base_prefix:
|
||||||
|
self.fail("Not running in a virtualenv")
|
||||||
|
|
||||||
self.assertTrue(path_contains(virtualenv_path, sys.executable))
|
self.assertTrue(path_contains(sys.prefix, sys.executable))
|
||||||
self.assertTrue(path_contains(virtualenv_path, spam.__file__))
|
self.assertTrue(path_contains(sys.prefix, spam.__file__))
|
||||||
|
|
||||||
def test_uname(self):
|
def test_uname(self):
|
||||||
if platform.system() == "Windows":
|
if platform.system() == "Windows":
|
||||||
|
|||||||
+3
-2
@@ -10,9 +10,10 @@ import shutil
|
|||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
from pathlib import Path
|
||||||
from tempfile import mkdtemp
|
from tempfile import mkdtemp
|
||||||
|
|
||||||
IS_WINDOWS_RUNNING_ON_AZURE = os.path.exists('C:\\hostedtoolcache')
|
IS_WINDOWS_RUNNING_ON_AZURE = Path('C:\\hostedtoolcache').exists()
|
||||||
IS_WINDOWS_RUNNING_ON_TRAVIS = os.environ.get('TRAVIS_OS_NAME') == 'windows'
|
IS_WINDOWS_RUNNING_ON_TRAVIS = os.environ.get('TRAVIS_OS_NAME') == 'windows'
|
||||||
|
|
||||||
|
|
||||||
@@ -66,7 +67,7 @@ def cibuildwheel_run(project_path, package_dir='.', env=None, add_env=None, outp
|
|||||||
|
|
||||||
with TemporaryDirectoryIfNone(output_dir) as _output_dir:
|
with TemporaryDirectoryIfNone(output_dir) as _output_dir:
|
||||||
subprocess.check_call(
|
subprocess.check_call(
|
||||||
[sys.executable, '-m', 'cibuildwheel', '--output-dir', str(_output_dir), package_dir],
|
[sys.executable, '-m', 'cibuildwheel', '--output-dir', str(_output_dir), str(package_dir)],
|
||||||
env=env,
|
env=env,
|
||||||
cwd=project_path,
|
cwd=project_path,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,30 +1,16 @@
|
|||||||
from cibuildwheel.util import DependencyConstraints
|
from cibuildwheel.util import DependencyConstraints
|
||||||
import os
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
def test_defaults():
|
def test_defaults():
|
||||||
dependency_constraints = DependencyConstraints.with_defaults()
|
dependency_constraints = DependencyConstraints.with_defaults()
|
||||||
|
|
||||||
project_root = os.path.dirname(os.path.dirname(__file__))
|
project_root = Path(__file__).parents[1]
|
||||||
resources_dir = os.path.join(project_root, 'cibuildwheel', 'resources')
|
resources_dir = project_root / 'cibuildwheel' / 'resources'
|
||||||
|
|
||||||
assert os.path.samefile(
|
assert dependency_constraints.base_file_path.samefile(resources_dir / 'constraints.txt')
|
||||||
dependency_constraints.base_file_path,
|
assert dependency_constraints.get_for_python_version('3.8').samefile(resources_dir / 'constraints.txt')
|
||||||
os.path.join(resources_dir, 'constraints.txt')
|
assert dependency_constraints.get_for_python_version('3.6').samefile(resources_dir / 'constraints-python36.txt')
|
||||||
)
|
assert dependency_constraints.get_for_python_version('3.5').samefile(resources_dir / 'constraints-python35.txt')
|
||||||
assert os.path.samefile(
|
assert dependency_constraints.get_for_python_version('2.7').samefile(resources_dir / 'constraints-python27.txt')
|
||||||
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')
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import os
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ class ArgsInterceptor:
|
|||||||
self.kwargs = kwargs
|
self.kwargs = kwargs
|
||||||
|
|
||||||
|
|
||||||
MOCK_PACKAGE_DIR = 'some_package_dir'
|
MOCK_PACKAGE_DIR = Path('some_package_dir')
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
@@ -31,28 +31,33 @@ def mock_protection(monkeypatch):
|
|||||||
def fail_on_call(*args, **kwargs):
|
def fail_on_call(*args, **kwargs):
|
||||||
raise RuntimeError("This should never be called")
|
raise RuntimeError("This should never be called")
|
||||||
|
|
||||||
|
def ignore_call(*args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
monkeypatch.setattr(subprocess, 'Popen', fail_on_call)
|
monkeypatch.setattr(subprocess, 'Popen', fail_on_call)
|
||||||
monkeypatch.setattr(util, 'download', fail_on_call)
|
monkeypatch.setattr(util, 'download', fail_on_call)
|
||||||
monkeypatch.setattr(windows, 'build', fail_on_call)
|
monkeypatch.setattr(windows, 'build', fail_on_call)
|
||||||
monkeypatch.setattr(linux, 'build', fail_on_call)
|
monkeypatch.setattr(linux, 'build', fail_on_call)
|
||||||
monkeypatch.setattr(macos, 'build', fail_on_call)
|
monkeypatch.setattr(macos, 'build', fail_on_call)
|
||||||
|
|
||||||
|
monkeypatch.setattr(Path, 'mkdir', ignore_call)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def fake_package_dir(monkeypatch):
|
def fake_package_dir(monkeypatch):
|
||||||
'''
|
'''
|
||||||
Monkey-patch enough for the main() function to run
|
Monkey-patch enough for the main() function to run
|
||||||
'''
|
'''
|
||||||
real_os_path_exists = os.path.exists
|
real_path_exists = Path.exists
|
||||||
|
|
||||||
def mock_os_path_exists(path):
|
def mock_path_exists(path):
|
||||||
if path == os.path.join(MOCK_PACKAGE_DIR, 'setup.py'):
|
if path == MOCK_PACKAGE_DIR / 'setup.py':
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return real_os_path_exists(path)
|
return real_path_exists(path)
|
||||||
|
|
||||||
monkeypatch.setattr(os.path, 'exists', mock_os_path_exists)
|
monkeypatch.setattr(Path, 'exists', mock_path_exists)
|
||||||
monkeypatch.setattr(sys, 'argv', ['cibuildwheel', MOCK_PACKAGE_DIR])
|
monkeypatch.setattr(sys, 'argv', ['cibuildwheel', str(MOCK_PACKAGE_DIR)])
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(params=['linux', 'macos', 'windows'])
|
@pytest.fixture(params=['linux', 'macos', 'windows'])
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import sys
|
import sys
|
||||||
from fnmatch import fnmatch
|
from fnmatch import fnmatch
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@@ -12,9 +13,9 @@ from cibuildwheel.util import BuildSelector
|
|||||||
|
|
||||||
|
|
||||||
def test_output_dir(platform, intercepted_build_args, monkeypatch):
|
def test_output_dir(platform, intercepted_build_args, monkeypatch):
|
||||||
OUTPUT_DIR = 'some_output_dir'
|
OUTPUT_DIR = Path('some_output_dir')
|
||||||
|
|
||||||
monkeypatch.setenv('CIBW_OUTPUT_DIR', OUTPUT_DIR)
|
monkeypatch.setenv('CIBW_OUTPUT_DIR', str(OUTPUT_DIR))
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
||||||
@@ -24,14 +25,14 @@ def test_output_dir(platform, intercepted_build_args, monkeypatch):
|
|||||||
def test_output_dir_default(platform, intercepted_build_args, monkeypatch):
|
def test_output_dir_default(platform, intercepted_build_args, monkeypatch):
|
||||||
main()
|
main()
|
||||||
|
|
||||||
assert intercepted_build_args.args[0].output_dir == 'wheelhouse'
|
assert intercepted_build_args.args[0].output_dir == Path('wheelhouse')
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('also_set_environment', [False, True])
|
@pytest.mark.parametrize('also_set_environment', [False, True])
|
||||||
def test_output_dir_argument(also_set_environment, platform, intercepted_build_args, monkeypatch):
|
def test_output_dir_argument(also_set_environment, platform, intercepted_build_args, monkeypatch):
|
||||||
OUTPUT_DIR = 'some_output_dir'
|
OUTPUT_DIR = Path('some_output_dir')
|
||||||
|
|
||||||
monkeypatch.setattr(sys, 'argv', sys.argv + ['--output-dir', OUTPUT_DIR])
|
monkeypatch.setattr(sys, 'argv', sys.argv + ['--output-dir', str(OUTPUT_DIR)])
|
||||||
if also_set_environment:
|
if also_set_environment:
|
||||||
monkeypatch.setenv('CIBW_OUTPUT_DIR', 'not_this_output_dir')
|
monkeypatch.setenv('CIBW_OUTPUT_DIR', 'not_this_output_dir')
|
||||||
|
|
||||||
@@ -232,3 +233,21 @@ def test_build_selector_migrations(intercepted_build_args, monkeypatch, option_n
|
|||||||
assert intercepted_build_selector.build_patterns == build_selector_patterns
|
assert intercepted_build_selector.build_patterns == build_selector_patterns
|
||||||
else:
|
else:
|
||||||
assert intercepted_build_selector.skip_patterns == build_selector_patterns
|
assert intercepted_build_selector.skip_patterns == build_selector_patterns
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('before_all', ["", None, 'test text'])
|
||||||
|
@pytest.mark.parametrize('platform_specific', [False, True])
|
||||||
|
def test_before_all(before_all, platform_specific, platform, intercepted_build_args, monkeypatch):
|
||||||
|
if before_all is not None:
|
||||||
|
if platform_specific:
|
||||||
|
monkeypatch.setenv('CIBW_BEFORE_ALL_' + platform.upper(), before_all)
|
||||||
|
monkeypatch.setenv('CIBW_BEFORE_ALL', 'overwritten')
|
||||||
|
else:
|
||||||
|
monkeypatch.setenv('CIBW_BEFORE_ALL', before_all)
|
||||||
|
|
||||||
|
main()
|
||||||
|
|
||||||
|
if before_all is None:
|
||||||
|
before_all = ""
|
||||||
|
|
||||||
|
assert intercepted_build_args.args[0].before_all == before_all
|
||||||
|
|||||||
Reference in New Issue
Block a user