Merge branch 'master' into returncode

This commit is contained in:
Gertjan van den Burg
2019-11-05 17:09:17 +00:00
15 changed files with 361 additions and 175 deletions
+9 -3
View File
@@ -12,7 +12,7 @@ matrix:
# Linux Python 3 # Linux Python 3
- sudo: required - sudo: required
language: python language: python
python: 3.4 python: 3.5
services: docker services: docker
env: PYTHON=python env: PYTHON=python
@@ -23,9 +23,15 @@ matrix:
# macOS Python 3 # macOS Python 3
- os: osx - os: osx
env: PYTHON=python3 env: PYTHON=python3
- os: windows
language: shell
before_install: before_install:
- brew update - choco install python3 --version 3.6.8 --no-progress -y
- brew outdated python || brew upgrade python install:
- C:\\Python36\\python -m pip install -r requirements-dev.txt
script:
- C:\\Python36\\python ./bin/run_tests.py
install: $PYTHON -m pip install -r requirements-dev.txt install: $PYTHON -m pip install -r requirements-dev.txt
+73 -59
View File
@@ -12,15 +12,15 @@ Python wheels are great. Building them across **Mac, Linux, Windows**, on **mult
What does it do? What does it do?
---------------- ----------------
| | macOS 10.6+ | manylinux i686 | manylinux x86_64 | Windows 32bit | Windows 64bit | | | macOS 10.6+ intel | macOS 10.9+ x86_64 | manylinux i686 | manylinux x86_64 | Windows 32bit | Windows 64bit |
|---|---|---|---|---|---| |---|---|---|---|---|---|---|
| Python 2.7 | ✅ | ✅ | ✅ | ✅ | ✅ | | Python 2.7 | ✅ | | ✅ | ✅ | ✅¹ | ✅¹ |
| Python 3.4 | ✅ | ✅ | ✅ | ✅* | ✅* | | Python 3.5 | ✅ | | ✅ | ✅ | ✅ | ✅ |
| Python 3.5 | ✅ | ✅ | ✅ | ✅ | ✅ | | Python 3.6 | ✅ | | ✅ | ✅ | ✅ | ✅ |
| Python 3.6 | ✅ | ✅ | ✅ | ✅ | ✅ | | Python 3.7 | ✅ | | ✅ | ✅ | ✅ | ✅ |
| Python 3.7 | ✅ | ✅ | ✅ | ✅ | ✅ | | Python 3.8 | | ✅ | ✅ | ✅ | ✅ | ✅ |
> \* Not supported on Azure Pipelines > ¹ Not supported on Travis
- Builds manylinux, macOS and Windows (32 and 64bit) wheels using Azure Pipelines, Travis CI, AppVeyor, and CircleCI - Builds manylinux, macOS and Windows (32 and 64bit) wheels 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)
@@ -29,12 +29,12 @@ What does it do?
Usage Usage
----- -----
`cibuildwheel` currently works **Travis CI** and **CircleCI** to build Linux and Mac wheels, and **AppVeyor** to build Windows wheels. **Azure Pipelines** supports all three. `cibuildwheel` currently works on **Travis CI** and **Azure Pipelines** to build wheels for all three supported platforms (Linux, macOS, Windows). On **CircleCI** Linux and macOS wheels can be built, and on **AppVeyor** Windows is supported.
| | Linux | macOS | Windows | | | Linux | macOS | Windows |
|-----------------|-------|-------|---------| |-----------------|-------|-------|---------|
| Azure Pipelines | ✅ | ✅ | ✅ | | Azure Pipelines | ✅ | ✅ | ✅ |
| Travis CI | ✅ | ✅ | | | Travis CI | ✅ | ✅ | ✅ |
| AppVeyor | | | ✅ | | AppVeyor | | | ✅ |
| CircleCI | ✅ | ✅ | | | CircleCI | ✅ | ✅ | |
@@ -56,7 +56,7 @@ Usage
jobs: jobs:
- job: linux - job: linux
pool: {vmImage: 'Ubuntu-16.04'} pool: {vmImage: 'Ubuntu-16.04'}
steps: steps:
- task: UsePythonVersion@0 - task: UsePythonVersion@0
- bash: | - bash: |
python -m pip install --upgrade pip python -m pip install --upgrade pip
@@ -66,7 +66,7 @@ jobs:
inputs: {pathtoPublish: 'wheelhouse'} inputs: {pathtoPublish: 'wheelhouse'}
- job: macos - job: macos
pool: {vmImage: 'macOS-10.13'} pool: {vmImage: 'macOS-10.13'}
steps: steps:
- task: UsePythonVersion@0 - task: UsePythonVersion@0
- bash: | - bash: |
python -m pip install --upgrade pip python -m pip install --upgrade pip
@@ -76,15 +76,8 @@ jobs:
inputs: {pathtoPublish: 'wheelhouse'} inputs: {pathtoPublish: 'wheelhouse'}
- job: windows - job: windows
pool: {vmImage: 'vs2017-win2016'} pool: {vmImage: 'vs2017-win2016'}
steps: steps:
- {task: UsePythonVersion@0, inputs: {versionSpec: '2.7', architecture: x86}} - task: UsePythonVersion@0
- {task: UsePythonVersion@0, inputs: {versionSpec: '2.7', architecture: x64}}
- {task: UsePythonVersion@0, inputs: {versionSpec: '3.5', architecture: x86}}
- {task: UsePythonVersion@0, inputs: {versionSpec: '3.5', architecture: x64}}
- {task: UsePythonVersion@0, inputs: {versionSpec: '3.6', architecture: x86}}
- {task: UsePythonVersion@0, inputs: {versionSpec: '3.6', architecture: x64}}
- {task: UsePythonVersion@0, inputs: {versionSpec: '3.7', architecture: x86}}
- {task: UsePythonVersion@0, inputs: {versionSpec: '3.7', architecture: x64}}
- script: choco install vcpython27 -f -y - script: choco install vcpython27 -f -y
displayName: Install Visual C++ for Python 2.7 displayName: Install Visual C++ for Python 2.7
- bash: | - bash: |
@@ -101,13 +94,14 @@ jobs:
<summary><b>Travis CI</b> <summary><b>Travis CI</b>
<img width="16" src="https://unpkg.com/simple-icons@latest/icons/apple.svg" /> <img width="16" src="https://unpkg.com/simple-icons@latest/icons/apple.svg" />
<img width="16" src="https://unpkg.com/simple-icons@latest/icons/linux.svg" /> <img width="16" src="https://unpkg.com/simple-icons@latest/icons/linux.svg" />
<img width="16" src="https://unpkg.com/simple-icons@latest/icons/windows.svg" />
</summary> </summary>
- To build Linux and Mac wheels on Travis CI, create a `.travis.yml` file in your repo. - To build Linux and Mac wheels on Travis CI, create a `.travis.yml` file in your repo.
``` ```yaml
language: python language: python
matrix: matrix:
include: include:
- sudo: required - sudo: required
@@ -123,6 +117,18 @@ jobs:
- cibuildwheel --output-dir wheelhouse - cibuildwheel --output-dir wheelhouse
``` ```
To build on Windows too, add this matrix entry:
```yaml
- os: windows
language: shell
before_install:
- choco install python3 --version 3.6.8 --no-progress -y
env:
- PATH=/c/Python36:/c/Python36/Scripts:$PATH
```
Note that building Windows Python 2.7 wheels on Travis is unsupported.
Then setup a deployment method by following the [Travis CI deployment docs](https://docs.travis-ci.com/user/deployment/), or see [Delivering to PyPI](#delivering-to-pypi) below. Then setup a deployment method by following the [Travis CI deployment docs](https://docs.travis-ci.com/user/deployment/), or see [Delivering to PyPI](#delivering-to-pypi) below.
</details> </details>
@@ -132,7 +138,7 @@ jobs:
<img width="16" src="https://unpkg.com/simple-icons@latest/icons/apple.svg" /> <img width="16" src="https://unpkg.com/simple-icons@latest/icons/apple.svg" />
<img width="16" src="https://unpkg.com/simple-icons@latest/icons/linux.svg" /> <img width="16" src="https://unpkg.com/simple-icons@latest/icons/linux.svg" />
</summary> </summary>
- 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,
``` ```
@@ -198,14 +204,14 @@ jobs:
- path: "wheelhouse\\*.whl" - path: "wheelhouse\\*.whl"
name: Wheels name: Wheels
``` ```
AppVeyor will store the built wheels for you - you can access them from the project console. Alternatively, you may want to store them in the same place as the Travis CI build. See [AppVeyor deployment docs](https://www.appveyor.com/docs/deployment/) for more info, or see [Delivering to PyPI](#delivering-to-pypi) below. AppVeyor will store the built wheels for you - you can access them from the project console. Alternatively, you may want to store them in the same place as the Travis CI build. See [AppVeyor deployment docs](https://www.appveyor.com/docs/deployment/) for more info, or see [Delivering to PyPI](#delivering-to-pypi) below.
</details> </details>
- Commit those files, enable building of your repo on Travis CI and AppVeyor, and push. - Commit those files, enable building of your repo on Travis CI and AppVeyor, and push.
All being well, you should get wheels delivered to you in a few minutes. All being well, you should get wheels delivered to you in a few minutes.
> ⚠️ Got an error? Check the [checklist](#it-didnt-work) below. > ⚠️ Got an error? Check the [checklist](#it-didnt-work) below.
@@ -221,8 +227,8 @@ All being well, you should get wheels delivered to you in a few minutes.
| **Build parameters** | `CIBW_BUILD_VERBOSITY` | Increase or decrease the output of `pip wheel` | | **Build parameters** | `CIBW_BUILD_VERBOSITY` | Increase or decrease the output of `pip wheel` |
| **Build environment** | `CIBW_ENVIRONMENT` | Set environment variables needed during the build | | **Build environment** | `CIBW_ENVIRONMENT` | Set environment variables needed during the build |
| | `CIBW_BEFORE_BUILD` | Execute a shell command preparing each wheel's build | | | `CIBW_BEFORE_BUILD` | Execute a shell command preparing each wheel's build |
| | `CIBW_MANYLINUX1_X86_64_IMAGE` | Specify an alternative manylinx1 x86_64 docker image | | | `CIBW_MANYLINUX_X86_64_IMAGE` | Specify an alternative manylinux x86_64 docker image |
| | `CIBW_MANYLINUX1_I686_IMAGE` | Specify an alternative manylinux1 i686 docker image | | | `CIBW_MANYLINUX_I686_IMAGE` | Specify an alternative manylinux i686 docker image |
| **Tests** | `CIBW_TEST_COMMAND` | Execute a shell command to test all built wheels | | **Tests** | `CIBW_TEST_COMMAND` | Execute a shell command to test all built wheels |
| | `CIBW_TEST_REQUIRES` | Install Python dependencies before running the tests | | | `CIBW_TEST_REQUIRES` | Install Python dependencies before running the tests |
| | `CIBW_TEST_EXTRAS` | Install Python dependencies before running the tests using ``extras_require``| | | `CIBW_TEST_EXTRAS` | Install Python dependencies before running the tests using ``extras_require``|
@@ -231,10 +237,10 @@ A more detailed description of the options, the allowed values, and some example
### Linux builds on Docker ### Linux builds on Docker
Linux wheels are built in the [`manylinux1` docker images](https://github.com/pypa/manylinux) to provide binary compatible wheels on Linux, according to [PEP 513](https://www.python.org/dev/peps/pep-0513/). Because of this, when building with `cibuildwheel` on Linux, a few things should be taken into account: Linux wheels are built in the [`manylinux` docker images](https://github.com/pypa/manylinux) to provide binary compatible wheels on Linux, according to [PEP 571](https://www.python.org/dev/peps/pep-0571/). Because of this, when building with `cibuildwheel` on Linux, a few things should be taken into account:
- Programs and libraries cannot be installed on the Travis CI Ubuntu host with `apt-get`, but can be installed inside of the Docker image using `yum` or manually. The same goes for environment variables that are potentially needed to customize the wheel building. `cibuildwheel` supports this by providing the `CIBW_ENVIRONMENT` and `CIBW_BEFORE_BUILD` options to setup the build environment inside the running Docker image. See [below](#options) for details on these options. - Programs and libraries cannot be installed on the Travis CI Ubuntu host with `apt-get`, but can be installed inside of the Docker image using `yum` or manually. The same goes for environment variables that are potentially needed to customize the wheel building. `cibuildwheel` supports this by providing the `CIBW_ENVIRONMENT` and `CIBW_BEFORE_BUILD` options to setup the build environment inside the running Docker image. See [below](#options) for details on these options.
- The project directory is mounted in the running Docker instance as `/project`, the output directory for the wheels as `/output`. In general, this is handled transparently by `cibuildwheel`. For a more finegrained level of control however, the root of the host file system is mounted as `/host`, allowing for example to access shared files, caches, etc. on the host file system. Note that this is not available on CircleCI due to their Docker policies. - The project directory is mounted in the running Docker instance as `/project`, the output directory for the wheels as `/output`. In general, this is handled transparently by `cibuildwheel`. For a more finegrained level of control however, the root of the host file system is mounted as `/host`, allowing for example to access shared files, caches, etc. on the host file system. Note that this is not available on CircleCI due to their Docker policies.
- Alternative dockers images can be specified with the `CIBW_MANYLINUX1_X86_64_IMAGE` and `CIBW_MANYLINUX1_I686_IMAGE` options to allow for a custom, preconfigured build environment for the Linux builds. See [below](#options) for more details. - Alternative dockers images can be specified with the `CIBW_MANYLINUX_X86_64_IMAGE` and `CIBW_MANYLINUX_I686_IMAGE` options to allow for a custom, preconfigured build environment for the Linux builds. See [below](#options) for more details.
Options Options
@@ -244,7 +250,7 @@ Options
usage: cibuildwheel [-h] [--platform {auto,linux,macos,windows}] usage: cibuildwheel [-h] [--platform {auto,linux,macos,windows}]
[--output-dir OUTPUT_DIR] [--print-build-identifiers] [--output-dir OUTPUT_DIR] [--print-build-identifiers]
[project_dir] [project_dir]
Build wheels for all the platforms. Build wheels for all the platforms.
positional arguments: positional arguments:
@@ -291,27 +297,30 @@ For `linux` you need Docker running, on Mac or Linux. For `macos`, you need a Ma
Optional. Optional.
Space-separated list of builds to build and skip. Each build has an identifier like `cp27-manylinux1_x86_64` or `cp34-macosx_10_6_intel` - you can list specific ones to build and `cibuildwheel` will only build those, and/or list ones to skip and `cibuildwheel` won't try to build them. Space-separated list of builds to build and skip. Each build has an identifier like `cp27-manylinux_x86_64` or `cp35-macosx_10_6_intel` - you can list specific ones to build and `cibuildwheel` will only build those, and/or list ones to skip and `cibuildwheel` won't try to build them.
When both options are specified, both conditions are applied and only builds with a tag that matches `CIBW_BUILD` and does not match `CIBW_SKIP` will be built. When both options are specified, both conditions are applied and only builds with a tag that matches `CIBW_BUILD` and does not match `CIBW_SKIP` will be built.
The format is `python_tag-platform_tag`. The tags are as defined in [PEP 0425](https://www.python.org/dev/peps/pep-0425/#details). The format is `python_tag-platform_tag`. The tags are similar but not identical to the ones defined in [PEP 425](https://www.python.org/dev/peps/pep-0425/#details).
Python tags look like `cp27` `cp34` `cp35` `cp36` `cp37` Python tags look like `cp27` `cp35` `cp36` `cp37` `cp38`
Platform tags look like `macosx_10_6_intel` `manylinux1_x86_64` `manylinux1_i686` `win32` `win_amd64` Platform tags look like `macosx_10_6_intel` `macosx_10_9_x86_64` `manylinux_x86_64` `manylinux_i686` `win32` `win_amd64`
You can also use shell-style globbing syntax (as per `fnmatch`) You can also use shell-style globbing syntax (as per `fnmatch`).
The list of supported and currently selected build identifiers can be retrieved by passing the `--print-build-identifiers` flag to `cibuildwheel`.
Examples: Examples:
- Only build on Python 3.6: `CIBW_BUILD`:`cp36-*` - Only build on Python 3.6: `CIBW_BUILD`:`cp36-*`
- Skip building on Python 2.7 on the Mac: `CIBW_SKIP`:`cp27-macosx_10_6_intel` - Skip building on Python 2.7 on the Mac: `CIBW_SKIP`:`cp27-macosx_10_6_intel`
- Skip building on Python 3.8 on the Mac: `CIBW_SKIP`:`cp38-macosx_10_9_x86_64`
- Skip building on Python 2.7 on all platforms: `CIBW_SKIP`:`cp27-*` - Skip building on Python 2.7 on all platforms: `CIBW_SKIP`:`cp27-*`
- Skip Python 2.7 on Windows: `CIBW_SKIP`:`cp27-win*` - Skip Python 2.7 on Windows: `CIBW_SKIP`:`cp27-win*`
- Skip Python 2.7 on 32-bit Windows: `CIBW_SKIP`:`cp27-win32` - Skip Python 2.7 on 32-bit Windows: `CIBW_SKIP`:`cp27-win32`
- Skip Python 3.4 and Python 3.5: `CIBW_SKIP`:`cp34-* cp35-*` - Skip Python 2.7 and Python 3.5: `CIBW_SKIP`:`cp27-* cp35-*`
- Skip Python 3.6 on Linux: `CIBW_SKIP`:`cp36-manylinux*` - Skip Python 3.6 on Linux: `CIBW_SKIP`:`cp36-manylinux*`
- Only build on Python 3 and skip 32-bit builds: `CIBW_BUILD`:`cp3?-*` and `CIBW_SKIP`:`*-win32 *-manylinux1_i686` - Only build on Python 3 and skip 32-bit builds: `CIBW_BUILD`:`cp3?-*` and `CIBW_SKIP`:`*-win32 *-manylinux_i686`
*** ***
@@ -338,10 +347,10 @@ You must set this variable to pass variables to Linux builds (since they execute
You can use `$PATH` syntax to insert other variables, or the `$(pwd)` syntax to insert the output of other shell commands. You can use `$PATH` syntax to insert other variables, or the `$(pwd)` syntax to insert the output of other shell commands.
Example: `CFLAGS="-g -Wall" CXXFLAGS="-Wall"` Example: `CFLAGS="-g -Wall" CXXFLAGS="-Wall"`\
Example: `PATH=$PATH:/usr/local/bin` Example: `PATH=$PATH:/usr/local/bin`\
Example: `BUILD_TIME="$(date)"` Example: `BUILD_TIME="$(date)"`\
Example: `PIP_EXTRA_INDEX_URL="https://pypi.myorg.com/simple"` Example: `PIP_EXTRA_INDEX_URL="https://pypi.myorg.com/simple"`\
Platform-specific variants also available: Platform-specific variants also available:
`CIBW_ENVIRONMENT_MACOS` | `CIBW_ENVIRONMENT_WINDOWS` | `CIBW_ENVIRONMENT_LINUX` `CIBW_ENVIRONMENT_MACOS` | `CIBW_ENVIRONMENT_WINDOWS` | `CIBW_ENVIRONMENT_LINUX`
@@ -361,25 +370,30 @@ If dependencies are required to build your wheel (for example if you include a h
The active Python binary can be accessed using `python`, and pip with `pip`; `cibuildwheel` makes sure the right version of Python and pip will be executed. `{project}` can be used as a placeholder for the absolute path to the project's root. The active Python binary can be accessed using `python`, and pip with `pip`; `cibuildwheel` makes sure the right version of Python and pip will be executed. `{project}` can be used as a placeholder for the absolute path to the project's root.
Example: `pip install .` Example: `pip install .`\
Example: `pip install pybind11` Example: `pip install pybind11`\
Example: `yum install -y libffi-dev && pip install .` Example: `yum install -y libffi-dev && pip install .`
Platform-specific variants also available: Platform-specific variants also available:\
`CIBW_BEFORE_BUILD_MACOS` | `CIBW_BEFORE_BUILD_WINDOWS` | `CIBW_BEFORE_BUILD_LINUX` `CIBW_BEFORE_BUILD_MACOS` | `CIBW_BEFORE_BUILD_WINDOWS` | `CIBW_BEFORE_BUILD_LINUX`
*** ***
| Environment variables: `CIBW_MANYLINUX1_X86_64_IMAGE` and `CIBW_MANYLINUX1_I686_IMAGE` | Environment variables: `CIBW_MANYLINUX_X86_64_IMAGE` and `CIBW_MANYLINUX_I686_IMAGE`
| --- | ---
Optional. Optional.
An alternative docker image to be used for building [`manylinux1`](https://github.com/pypa/manylinux) wheels. `cibuildwheel` will then pull these instead of the official images, [`quay.io/pypa/manylinux1_x86_64`](https://quay.io/pypa/manylinux1_i686) and [`quay.io/pypa/manylinux1_i686`](https://quay.io/pypa/manylinux1_i686). An alternative Docker image to be used for building [`manylinux`](https://github.com/pypa/manylinux) wheels. `cibuildwheel` will then pull these instead of the default images, [`quay.io/pypa/manylinux2010_x86_64`](https://quay.io/pypa/manylinux2010_x86_64) and [`quay.io/pypa/manylinux2010_i686`](https://quay.io/pypa/manylinux2010_i686).
Beware to specify a valid docker image that can be used the same as the official, default docker images: all necessary Python and pip versions need to be present in `/opt/python/`, and the `auditwheel` tool needs to be present for `cibuildwheel` to work. Apart from that, the architecture and relevant shared system libraries need to be manylinux1-compatible in order to produce valid `manylinux1` wheels (see https://github.com/pypa/manylinux and [PEP 513](https://www.python.org/dev/peps/pep-0513/) for more details). The value of this option can either be set to `manylinux1` or `manylinux2010` to use the [official `manylinux` images](https://github.com/pypa/manylinux), or any other valid Docker image name.
Example: `dockcross/manylinux-x64` Beware to specify a valid Docker image that can be used in the same way as the official, default Docker images: all necessary Python and pip versions need to be present in `/opt/python/`, and the `auditwheel` tool needs to be present for `cibuildwheel` to work. Apart from that, the architecture and relevant shared system libraries need to be manylinux1- or manylinux2010-compatible in order to produce valid `manylinux1`/`manylinux2010` wheels (see https://github.com/pypa/manylinux, [PEP 513](https://www.python.org/dev/peps/pep-0513/), and [PEP 571](https://www.python.org/dev/peps/pep-0571/) for more details).
Note that `auditwheel` detects the version of the `manylinux` standard in the Docker image through the `AUDITWHEEL_PLAT` environment variable, as `cibuildwheel` has no way of detecting the correct `--plat` command line argument to pass to `auditwheel` for a custom image. If a Docker image does not correctly set this `AUDITWHEEL_PLAT` environment variable, the `CIBW_ENVIRONMENT` option can be used to do so (e.g., `CIBW_ENVIRONMENT="manylinux2010_$(uname -m)"`).
Example: `manylinux1`\
Example: `dockcross/manylinux-x64`\
Example: `dockcross/manylinux-x86` Example: `dockcross/manylinux-x86`
*** ***
@@ -391,7 +405,7 @@ Optional.
Shell command to run tests after the build. The wheel will be installed automatically and available for import from the tests. `{project}` can be used as a placeholder for the absolute path to the project's root and will be replaced by `cibuildwheel`. Shell command to run tests after the build. The wheel will be installed automatically and available for import from the tests. `{project}` can be used as a placeholder for the absolute path to the project's root and will be replaced by `cibuildwheel`.
On Linux and Mac, the command runs in a shell, so you can write things like `cmd1 && cmd2`. On Linux and Mac, the command runs in a shell, so you can write things like `cmd1 && cmd2`.
Example: `nosetests {project}/tests` Example: `nosetests {project}/tests`
@@ -407,7 +421,7 @@ Optional.
Space-separated list of dependencies required for running the tests. Space-separated list of dependencies required for running the tests.
Example: `pytest` Example: `pytest`\
Example: `nose==1.3.7 moto==0.4.31` Example: `nose==1.3.7 moto==0.4.31`
Platform-specific variants also available: Platform-specific variants also available:
@@ -458,13 +472,13 @@ After you've built your wheels, you'll probably want to deliver them to PyPI.
On your development machine, do the following... On your development machine, do the following...
```bash ```bash
# Clear out your 'dist' folder. # Clear out your 'dist' folder.
rm -rf dist rm -rf dist
# Make a source distribution # Make a source distribution
python setup.py sdist python setup.py sdist
# 🏃🏻 # 🏃🏻
# Go and download your wheel files from wherever you put them. Put # Go and download your wheel files from wherever you put them. Put
# them all into the 'dist' folder. # them all into the 'dist' folder.
# Upload using 'twine' (you may need to 'pip install twine') # Upload using 'twine' (you may need to 'pip install twine')
@@ -496,7 +510,7 @@ If your wheel didn't compile, check the list below for some debugging tips.
Working examples Working examples
---------------- ----------------
Here are some repos that use cibuildwheel. Here are some repos that use cibuildwheel.
- [pyinstrument_cext](https://github.com/joerick/pyinstrument_cext) - [pyinstrument_cext](https://github.com/joerick/pyinstrument_cext)
- [websockets](https://github.com/aaugustin/websockets) - [websockets](https://github.com/aaugustin/websockets)
@@ -514,7 +528,7 @@ Here are some repos that use cibuildwheel.
Legal note Legal note
---------- ----------
Since `cibuildwheel` runs the wheel through delocate or auditwheel, it might automatically bundle dynamically linked libraries from the build machine. Since `cibuildwheel` runs the wheel through delocate or auditwheel, it might automatically bundle dynamically linked libraries from the build machine.
It helps ensure that the library can run without any dependencies outside of the pip toolchain. It helps ensure that the library can run without any dependencies outside of the pip toolchain.
@@ -546,7 +560,7 @@ _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)
for an example project. (#126, #132) for an example project. (#126, #132)
- 🛠 Internal change - the end-to-end test projects format was updated, so we - 🛠 Internal change - the end-to-end test projects format was updated, so we
@@ -686,7 +700,7 @@ _11 June 2017_
_13 April 2017_ _13 April 2017_
- ✨ Added `CIBW_SKIP` option, letting users explicitly skip a build - ✨ Added `CIBW_SKIP` option, letting users explicitly skip a build
- ✨ Added `CIBW_BEFORE_BUILD` option, letting users run a shell command before the build starts - ✨ Added `CIBW_BEFORE_BUILD` option, letting users run a shell command before the build starts
### 0.1.3 ### 0.1.3
@@ -728,7 +742,7 @@ Maintainers
Credits Credits
------- -------
`cibuildwheel` stands on the shoulders of giants. `cibuildwheel` stands on the shoulders of giants.
- ⭐️ @matthew-brett for [matthew-brett/multibuild](http://github.com/matthew-brett/multibuild) and [matthew-brett/delocate](http://github.com/matthew-brett/delocate) - ⭐️ @matthew-brett for [matthew-brett/multibuild](http://github.com/matthew-brett/multibuild) and [matthew-brett/delocate](http://github.com/matthew-brett/delocate)
- @PyPA for the manylinux Docker images [pypa/manylinux](https://github.com/pypa/manylinux) - @PyPA for the manylinux Docker images [pypa/manylinux](https://github.com/pypa/manylinux)
+5 -12
View File
@@ -1,7 +1,7 @@
jobs: jobs:
- job: linux - job: linux
pool: {vmImage: 'Ubuntu-16.04'} pool: {vmImage: 'Ubuntu-18.04'}
steps: steps:
- task: UsePythonVersion@0 - task: UsePythonVersion@0
- bash: | - bash: |
python -m pip install -r requirements-dev.txt python -m pip install -r requirements-dev.txt
@@ -9,7 +9,7 @@ jobs:
- job: macos - job: macos
pool: {vmImage: 'macOS-10.13'} pool: {vmImage: 'macOS-10.13'}
steps: steps:
- task: UsePythonVersion@0 - task: UsePythonVersion@0
- bash: | - bash: |
python -m pip install -r requirements-dev.txt python -m pip install -r requirements-dev.txt
@@ -17,15 +17,8 @@ jobs:
- job: windows - job: windows
pool: {vmImage: 'vs2017-win2016'} pool: {vmImage: 'vs2017-win2016'}
steps: steps:
- {task: UsePythonVersion@0, inputs: {versionSpec: '2.7', architecture: x86}} - task: UsePythonVersion@0
- {task: UsePythonVersion@0, inputs: {versionSpec: '2.7', architecture: x64}}
- {task: UsePythonVersion@0, inputs: {versionSpec: '3.5', architecture: x86}}
- {task: UsePythonVersion@0, inputs: {versionSpec: '3.5', architecture: x64}}
- {task: UsePythonVersion@0, inputs: {versionSpec: '3.6', architecture: x86}}
- {task: UsePythonVersion@0, inputs: {versionSpec: '3.6', architecture: x64}}
- {task: UsePythonVersion@0, inputs: {versionSpec: '3.7', architecture: x86}}
- {task: UsePythonVersion@0, inputs: {versionSpec: '3.7', architecture: x64}}
- script: choco install vcpython27 -f -y - script: choco install vcpython27 -f -y
displayName: Install Visual C++ for Python 2.7 displayName: Install Visual C++ for Python 2.7
- bash: | - bash: |
+33 -3
View File
@@ -54,6 +54,8 @@ def main():
args = parser.parse_args() args = parser.parse_args()
detect_obsolete_options()
if args.platform != 'auto': if args.platform != 'auto':
platform = args.platform platform = args.platform
else: else:
@@ -63,6 +65,8 @@ def main():
platform = 'linux' platform = 'linux'
elif os.environ.get('TRAVIS_OS_NAME') == 'osx': elif os.environ.get('TRAVIS_OS_NAME') == 'osx':
platform = 'macos' platform = 'macos'
elif os.environ.get('TRAVIS_OS_NAME') == 'windows':
platform = 'windows'
elif 'APPVEYOR' in os.environ: elif 'APPVEYOR' in os.environ:
platform = 'windows' platform = 'windows'
elif 'BITRISE_BUILD_NUMBER' in os.environ: elif 'BITRISE_BUILD_NUMBER' in os.environ:
@@ -141,11 +145,17 @@ def main():
) )
if platform == 'linux': if platform == 'linux':
manylinux1_x86_64_image = os.environ.get('CIBW_MANYLINUX1_X86_64_IMAGE', None) manylinux_x86_64_image = os.environ.get('CIBW_MANYLINUX_X86_64_IMAGE', 'manylinux2010')
manylinux1_i686_image = os.environ.get('CIBW_MANYLINUX1_I686_IMAGE', None) manylinux_i686_image = os.environ.get('CIBW_MANYLINUX_I686_IMAGE', 'manylinux2010')
default_manylinux_images_x86_64 = {'manylinux1': 'quay.io/pypa/manylinux1_x86_64',
'manylinux2010': 'quay.io/pypa/manylinux2010_x86_64'}
default_manylinux_images_i686 = {'manylinux1': 'quay.io/pypa/manylinux1_i686',
'manylinux2010': 'quay.io/pypa/manylinux2010_i686'}
build_options.update( build_options.update(
manylinux1_images={'x86_64': manylinux1_x86_64_image, 'i686': manylinux1_i686_image}, manylinux_images={'x86_64': default_manylinux_images_x86_64.get(manylinux_x86_64_image) or manylinux_x86_64_image,
'i686': default_manylinux_images_i686.get(manylinux_i686_image) or manylinux_i686_image},
) )
elif platform == 'macos': elif platform == 'macos':
pass pass
@@ -170,6 +180,26 @@ def main():
raise Exception('Unsupported platform') raise Exception('Unsupported platform')
def detect_obsolete_options():
# Check the old 'MANYLINUX1_*_IMAGE' options
for (deprecated, alternative) in [('CIBW_MANYLINUX1_X86_64_IMAGE', 'CIBW_MANYLINUX_X86_64_IMAGE'),
('CIBW_MANYLINUX1_I686_IMAGE', 'CIBW_MANYLINUX_I686_IMAGE')]:
if deprecated in os.environ:
print("'{}' has been deprecated, and will be removed in a future release. Use the option '{}' instead.".format(deprecated, alternative))
if alternative not in os.environ:
print("Using value of option '{}' as replacement for '{}'".format(deprecated, alternative))
os.environ[alternative] = os.environ[deprecated]
else:
print("Option '{}' is not empty. Please unset '{}'".format(alternative, deprecated))
exit(2)
# Check for 'manylinux1' in the 'CIBW_BUILD' and 'CIBW_SKIP' options
for deprecated in ['CIBW_BUILD', 'CIBW_SKIP']:
if deprecated in os.environ and 'manylinux1' in os.environ[deprecated]:
print("Build identifiers with 'manylinux1' been deprecated. Replacing all occurences of 'manylinux1' by 'manylinux' in the option '{}'".format(deprecated))
os.environ[deprecated] = os.environ[deprecated].replace('manylinux1', 'manylinux')
def print_preamble(platform, build_options): def print_preamble(platform, build_options):
print(textwrap.dedent(''' print(textwrap.dedent('''
_ _ _ _ _ _ _ _ _ _ _ _ _ _
+28 -23
View File
@@ -12,25 +12,25 @@ except ImportError:
def get_python_configurations(build_selector): def get_python_configurations(build_selector):
PythonConfiguration = namedtuple('PythonConfiguration', ['identifier', 'path']) PythonConfiguration = namedtuple('PythonConfiguration', ['identifier', 'path'])
python_configurations = [ python_configurations = [
PythonConfiguration(identifier='cp27-manylinux1_x86_64', path='/opt/python/cp27-cp27m'), PythonConfiguration(identifier='cp27-manylinux_x86_64', path='/opt/python/cp27-cp27m'),
PythonConfiguration(identifier='cp27-manylinux1_x86_64', path='/opt/python/cp27-cp27mu'), PythonConfiguration(identifier='cp27-manylinux_x86_64', path='/opt/python/cp27-cp27mu'),
PythonConfiguration(identifier='cp34-manylinux1_x86_64', path='/opt/python/cp34-cp34m'), PythonConfiguration(identifier='cp35-manylinux_x86_64', path='/opt/python/cp35-cp35m'),
PythonConfiguration(identifier='cp35-manylinux1_x86_64', path='/opt/python/cp35-cp35m'), PythonConfiguration(identifier='cp36-manylinux_x86_64', path='/opt/python/cp36-cp36m'),
PythonConfiguration(identifier='cp36-manylinux1_x86_64', path='/opt/python/cp36-cp36m'), PythonConfiguration(identifier='cp37-manylinux_x86_64', path='/opt/python/cp37-cp37m'),
PythonConfiguration(identifier='cp37-manylinux1_x86_64', path='/opt/python/cp37-cp37m'), PythonConfiguration(identifier='cp38-manylinux_x86_64', path='/opt/python/cp38-cp38'),
PythonConfiguration(identifier='cp27-manylinux1_i686', path='/opt/python/cp27-cp27m'), PythonConfiguration(identifier='cp27-manylinux_i686', path='/opt/python/cp27-cp27m'),
PythonConfiguration(identifier='cp27-manylinux1_i686', path='/opt/python/cp27-cp27mu'), PythonConfiguration(identifier='cp27-manylinux_i686', path='/opt/python/cp27-cp27mu'),
PythonConfiguration(identifier='cp34-manylinux1_i686', path='/opt/python/cp34-cp34m'), PythonConfiguration(identifier='cp35-manylinux_i686', path='/opt/python/cp35-cp35m'),
PythonConfiguration(identifier='cp35-manylinux1_i686', path='/opt/python/cp35-cp35m'), PythonConfiguration(identifier='cp36-manylinux_i686', path='/opt/python/cp36-cp36m'),
PythonConfiguration(identifier='cp36-manylinux1_i686', path='/opt/python/cp36-cp36m'), PythonConfiguration(identifier='cp37-manylinux_i686', path='/opt/python/cp37-cp37m'),
PythonConfiguration(identifier='cp37-manylinux1_i686', path='/opt/python/cp37-cp37m'), PythonConfiguration(identifier='cp38-manylinux_i686', path='/opt/python/cp38-cp38'),
] ]
# skip builds as required # skip builds as required
return [c for c in python_configurations if build_selector(c.identifier)] return [c for c in python_configurations if build_selector(c.identifier)]
def build(project_dir, output_dir, test_command, test_requires, test_extras, before_build, build_verbosity, build_selector, environment, manylinux1_images): def build(project_dir, output_dir, test_command, test_requires, test_extras, before_build, build_verbosity, build_selector, environment, manylinux_images):
try: try:
subprocess.check_call(['docker', '--version']) subprocess.check_call(['docker', '--version'])
except: except:
@@ -42,8 +42,8 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef
python_configurations = get_python_configurations(build_selector) python_configurations = get_python_configurations(build_selector)
platforms = [ platforms = [
('manylinux1_x86_64', manylinux1_images.get('x86_64') or 'quay.io/pypa/manylinux1_x86_64'), ('manylinux_x86_64', manylinux_images['x86_64']),
('manylinux1_i686', manylinux1_images.get('i686') or 'quay.io/pypa/manylinux1_i686'), ('manylinux_i686', manylinux_images['i686']),
] ]
for platform_tag, docker_image in platforms: for platform_tag, docker_image in platforms:
@@ -62,9 +62,9 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef
for PYBIN in {pybin_paths}; do for PYBIN in {pybin_paths}; do
# Setup # Setup
rm -rf /tmp/built_wheel rm -rf /tmp/built_wheel
rm -rf /tmp/delocated_wheel rm -rf /tmp/delocated_wheels
mkdir /tmp/built_wheel mkdir /tmp/built_wheel
mkdir /tmp/delocated_wheel mkdir /tmp/delocated_wheels
if [ ! -z {before_build} ]; then if [ ! -z {before_build} ]; then
PATH="$PYBIN:$PATH" sh -c {before_build} PATH="$PYBIN:$PATH" sh -c {before_build}
@@ -79,11 +79,11 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef
# the first element # the first element
if [[ "$built_wheel" == *none-any.whl ]]; then if [[ "$built_wheel" == *none-any.whl ]]; then
# pure python wheel - just copy # pure python wheel - just copy
mv "$built_wheel" /tmp/delocated_wheel mv "$built_wheel" /tmp/delocated_wheels
else else
auditwheel repair "$built_wheel" -w /tmp/delocated_wheel auditwheel repair "$built_wheel" -w /tmp/delocated_wheels
fi fi
delocated_wheel=(/tmp/delocated_wheel/*.whl) delocated_wheels=(/tmp/delocated_wheels/*.whl)
if [ ! -z {test_command} ]; then if [ ! -z {test_command} ]; then
# 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
@@ -100,7 +100,12 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef
echo "Running tests using `which python`" echo "Running tests using `which python`"
# Install the wheel we just built # Install the wheel we just built
pip install "$delocated_wheel"{test_extras} # Note: If auditwheel produced two wheels, it's because the earlier produced wheel
# conforms to multiple manylinux standards. These multiple versions of the wheel are
# functionally the same, differing only in name, wheel metadata, and possibly include
# different external shared libraries. so it doesn't matter which one we run the tests on.
# Let's just pick the first one.
pip install "${{delocated_wheels[0]}}"{test_extras}
# Install any requirements to run the tests # Install any requirements to run the tests
if [ ! -z "{test_requires}" ]; then if [ ! -z "{test_requires}" ]; then
@@ -122,8 +127,8 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef
fi fi
# we're all done here; move it to output # we're all done here; move it to output
mv "$delocated_wheel" /output mv "${{delocated_wheels[@]}}" /output
chown {uid}:{gid} "/output/$(basename "$delocated_wheel")" for delocated_wheel in "${{delocated_wheels[@]}}"; do chown {uid}:{gid} "/output/$(basename "$delocated_wheel")"; done
done done
'''.format( '''.format(
pybin_paths=' '.join(c.path+'/bin' for c in platform_configs), pybin_paths=' '.join(c.path+'/bin' for c in platform_configs),
+5 -5
View File
@@ -14,11 +14,11 @@ from .util import prepare_command, get_build_verbosity_extra_flags
def get_python_configurations(build_selector): def get_python_configurations(build_selector):
PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'identifier', 'url']) PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'identifier', 'url'])
python_configurations = [ python_configurations = [
PythonConfiguration(version='2.7', identifier='cp27-macosx_10_6_intel', url='https://www.python.org/ftp/python/2.7.16/python-2.7.16-macosx10.6.pkg'), PythonConfiguration(version='2.7', identifier='cp27-macosx_10_6_intel', url='https://www.python.org/ftp/python/2.7.17/python-2.7.17-macosx10.6.pkg'),
PythonConfiguration(version='3.4', identifier='cp34-macosx_10_6_intel', url='https://www.python.org/ftp/python/3.4.4/python-3.4.4-macosx10.6.pkg'),
PythonConfiguration(version='3.5', identifier='cp35-macosx_10_6_intel', url='https://www.python.org/ftp/python/3.5.4/python-3.5.4-macosx10.6.pkg'), PythonConfiguration(version='3.5', identifier='cp35-macosx_10_6_intel', url='https://www.python.org/ftp/python/3.5.4/python-3.5.4-macosx10.6.pkg'),
PythonConfiguration(version='3.6', identifier='cp36-macosx_10_6_intel', url='https://www.python.org/ftp/python/3.6.8/python-3.6.8-macosx10.6.pkg'), PythonConfiguration(version='3.6', identifier='cp36-macosx_10_6_intel', url='https://www.python.org/ftp/python/3.6.8/python-3.6.8-macosx10.6.pkg'),
PythonConfiguration(version='3.7', identifier='cp37-macosx_10_6_intel', url='https://www.python.org/ftp/python/3.7.4/python-3.7.4-macosx10.6.pkg'), PythonConfiguration(version='3.7', identifier='cp37-macosx_10_6_intel', url='https://www.python.org/ftp/python/3.7.5/python-3.7.5-macosx10.6.pkg'),
PythonConfiguration(version='3.8', identifier='cp38-macosx_10_9_x86_64', url='https://www.python.org/ftp/python/3.8.0/python-3.8.0-macosx10.9.pkg'),
] ]
# skip builds as required # skip builds as required
@@ -58,7 +58,7 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef
# 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 config.version in ('3.4', '3.5'): if config.version == '3.5':
call(['curl', '-fsSLo', '/tmp/python-patch.tar.gz', 'https://github.com/mayeut/patch-macos-python-openssl/releases/download/v1.0.2t/patch-macos-python-%s-openssl-v1.0.2t.tar.gz' % config.version]) call(['curl', '-fsSLo', '/tmp/python-patch.tar.gz', 'https://github.com/mayeut/patch-macos-python-openssl/releases/download/v1.0.2t/patch-macos-python-%s-openssl-v1.0.2t.tar.gz' % config.version])
call(['sudo', 'tar', '-C', '/Library/Frameworks/Python.framework/Versions/%s/' % config.version, '-xmf', '/tmp/python-patch.tar.gz']) call(['sudo', 'tar', '-C', '/Library/Frameworks/Python.framework/Versions/%s/' % config.version, '-xmf', '/tmp/python-patch.tar.gz'])
@@ -152,7 +152,7 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef
# and not the repo code) # and not the repo code)
test_command_prepared = prepare_command(test_command, project=abs_project_dir) test_command_prepared = prepare_command(test_command, project=abs_project_dir)
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)
# clean up # clean up
shutil.rmtree(venv_dir) shutil.rmtree(venv_dir)
+66 -47
View File
@@ -1,67 +1,78 @@
from __future__ import print_function from __future__ import print_function
import os, tempfile, subprocess, shutil import os, tempfile, subprocess, shutil, sys
from collections import namedtuple from collections import namedtuple
from glob import glob from glob import glob
try:
from shlex import quote as shlex_quote
except ImportError:
from pipes import quote as shlex_quote
try:
from urllib.request import urlopen
except ImportError:
from urllib2 import urlopen
from .util import prepare_command, get_build_verbosity_extra_flags from .util import prepare_command, get_build_verbosity_extra_flags
IS_RUNNING_ON_AZURE = os.path.exists('C:\\hostedtoolcache') IS_RUNNING_ON_AZURE = os.path.exists('C:\\hostedtoolcache')
IS_RUNNING_ON_TRAVIS = os.environ.get('TRAVIS_OS_NAME') == 'windows'
def get_python_path(config): def get_python_path(config):
if IS_RUNNING_ON_AZURE: nuget_args = get_nuget_args(config)
# We can't hard-code the paths because on Azure, we don't know which return os.path.join(nuget_args[-1], nuget_args[0] + "." + config.version, "tools")
# bugfix release of Python we are getting so we need to check which
# ones exist. We just use the first one that is found since there should
# only be one.
path_pattern = 'C:\\hostedtoolcache\\windows\\Python\\{version}\\{arch}'.format(
version=config.version.replace('x', '*'),
arch='x86' if config.arch == '32' else 'x64'
)
try:
return glob(path_pattern)[0]
except IndexError:
raise Exception('Could not find a Python install at ' + path_pattern)
else:
# Assume we're running on AppVeyor
major, minor = config.version.split('.')[:2]
return 'C:\\Python{major}{minor}{arch}'.format(
major=major,
minor=minor,
arch = '-x64' if config.arch == '64' else ''
)
def get_nuget_args(configuration):
python_name = "python" if configuration.version[0] == '3' else "python2"
if configuration.arch == "32":
python_name = python_name + "x86"
return [python_name, "-Version", configuration.version, "-OutputDirectory", "C:/python"]
def get_python_configurations(build_selector): def get_python_configurations(build_selector):
PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'arch', 'identifier', 'path']) PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'arch', 'identifier'])
python_configurations = [ python_configurations = [
PythonConfiguration(version='2.7.x', arch="32", identifier='cp27-win32', path='C:\Python27'), PythonConfiguration(version='2.7.17', arch="32", identifier='cp27-win32'),
PythonConfiguration(version='2.7.x', arch="64", identifier='cp27-win_amd64', path='C:\Python27-x64'), PythonConfiguration(version='2.7.17', arch="64", identifier='cp27-win_amd64'),
PythonConfiguration(version='3.4.x', arch="32", identifier='cp34-win32', path='C:\Python34'), PythonConfiguration(version='3.5.4', arch="32", identifier='cp35-win32'),
PythonConfiguration(version='3.4.x', arch="64", identifier='cp34-win_amd64', path='C:\Python34-x64'), PythonConfiguration(version='3.5.4', arch="64", identifier='cp35-win_amd64'),
PythonConfiguration(version='3.5.x', arch="32", identifier='cp35-win32', path='C:\Python35'), PythonConfiguration(version='3.6.8', arch="32", identifier='cp36-win32'),
PythonConfiguration(version='3.5.x', arch="64", identifier='cp35-win_amd64', path='C:\Python35-x64'), PythonConfiguration(version='3.6.8', arch="64", identifier='cp36-win_amd64'),
PythonConfiguration(version='3.6.x', arch="32", identifier='cp36-win32', path='C:\Python36'), PythonConfiguration(version='3.7.5', arch="32", identifier='cp37-win32'),
PythonConfiguration(version='3.6.x', arch="64", identifier='cp36-win_amd64', path='C:\Python36-x64'), PythonConfiguration(version='3.7.5', arch="64", identifier='cp37-win_amd64'),
PythonConfiguration(version='3.7.x', arch="32", identifier='cp37-win32', path='C:\Python37'), PythonConfiguration(version='3.8.0', arch="32", identifier='cp38-win32'),
PythonConfiguration(version='3.7.x', arch="64", identifier='cp37-win_amd64', path='C:\Python37-x64'), PythonConfiguration(version='3.8.0', arch="64", identifier='cp38-win_amd64'),
] ]
if IS_RUNNING_ON_AZURE: if IS_RUNNING_ON_TRAVIS:
# Python 3.4 isn't supported on Azure. # cannot install VCForPython27.msi which is needed for compiling C software
# See https://github.com/Microsoft/azure-pipelines-tasks/issues/9674 # try with (and similar): msiexec /i VCForPython27.msi ALLUSERS=1 ACCEPT=YES /passive
python_configurations = [c for c in python_configurations if c.version != '3.4.x'] python_configurations = [c for c in python_configurations if not c.version.startswith('2.7.')]
# skip builds as required
python_configurations = [c for c in python_configurations if build_selector(c.identifier)]
return python_configurations
# skip builds as required
return [c for c in python_configurations if build_selector(c.identifier)]
def build(project_dir, output_dir, test_command, test_requires, test_extras, before_build, build_verbosity, build_selector, environment): def build(project_dir, output_dir, test_command, test_requires, test_extras, before_build, build_verbosity, build_selector, environment):
if IS_RUNNING_ON_AZURE: def simple_shell(args, env=None, cwd=None):
def shell(args, env=None, cwd=None): print('+ ' + ' '.join(args))
print('+ ' + ' '.join(args)) args = ['cmd', '/E:ON', '/V:ON', '/C'] + args
args = ['cmd', '/E:ON', '/V:ON', '/C'] + args return subprocess.check_call(' '.join(args), env=env, cwd=cwd)
return subprocess.check_call(' '.join(args), env=env, cwd=cwd) def download(url, dest):
print('+ Download ' + url + ' to ' + dest)
response = urlopen(url)
try:
with open(dest, 'wb') as file:
file.write(response.read())
finally:
response.close()
if IS_RUNNING_ON_AZURE or IS_RUNNING_ON_TRAVIS:
shell = simple_shell
else: else:
run_with_env = os.path.abspath(os.path.join(os.path.dirname(__file__), 'resources', 'appveyor_run_with_env.cmd')) run_with_env = os.path.abspath(os.path.join(os.path.dirname(__file__), 'resources', 'appveyor_run_with_env.cmd'))
@@ -77,10 +88,19 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef
temp_dir = tempfile.mkdtemp(prefix='cibuildwheel') temp_dir = tempfile.mkdtemp(prefix='cibuildwheel')
built_wheel_dir = os.path.join(temp_dir, 'built_wheel') built_wheel_dir = os.path.join(temp_dir, 'built_wheel')
python_configurations = get_python_configurations(build_selector) # install nuget as best way to provide python
nuget = 'C:\\nuget.exe'
download('https://dist.nuget.org/win-x86-commandline/latest/nuget.exe', nuget)
# get pip fo this installation which not have.
get_pip_script = 'C:\\get-pip.py'
download('https://bootstrap.pypa.io/get-pip.py', get_pip_script)
python_configurations = get_python_configurations(build_selector)
for config in python_configurations: for config in python_configurations:
config_python_path = get_python_path(config) config_python_path = get_python_path(config)
simple_shell([nuget, "install"] + get_nuget_args(config))
if not os.path.exists(os.path.join(config_python_path, 'Scripts', 'pip.exe')):
simple_shell([os.path.join(config_python_path, 'python.exe'), get_pip_script ])
# check python & pip exist for this configuration # check python & pip exist for this configuration
assert os.path.exists(os.path.join(config_python_path, 'python.exe')) assert os.path.exists(os.path.join(config_python_path, 'python.exe'))
@@ -107,8 +127,7 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef
shell(['python', '-c', '"import struct; print(struct.calcsize(\'P\') * 8)\"'], env=env) shell(['python', '-c', '"import struct; print(struct.calcsize(\'P\') * 8)\"'], env=env)
# prepare the Python environment # prepare the Python environment
shell(['python', '-m', 'pip', 'install', '--upgrade', 'pip'], shell(['python', '-m', 'pip', 'install', '--upgrade', 'pip'], env=env)
env=env)
shell(['pip', 'install', '--upgrade', 'setuptools'], env=env) shell(['pip', 'install', '--upgrade', 'setuptools'], env=env)
shell(['pip', 'install', 'wheel'], env=env) shell(['pip', 'install', 'wheel'], env=env)
+5 -1
View File
@@ -17,6 +17,10 @@ def test():
def test_build_identifiers(): def test_build_identifiers():
# check that the number of expected wheels matches the number of build # check that the number of expected wheels matches the number of build
# identifiers # identifiers
expected_wheels = utils.expected_wheels('spam', '0.1.0') # after adding CIBW_MANYLINUX_IMAGE to support manylinux2010, there
# can be multiple wheels for each wheel, though, so we need to limit
# the expected wheels
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
if not '-manylinux' in w or '-manylinux1' in w]
build_identifiers = utils.cibuildwheel_get_build_identifiers(project_dir) build_identifiers = utils.cibuildwheel_get_build_identifiers(project_dir)
assert len(expected_wheels) == len(build_identifiers) assert len(expected_wheels) == len(build_identifiers)
+15 -1
View File
@@ -1,4 +1,5 @@
import os import os, subprocess
import pytest
import utils import utils
def test(): def test():
@@ -35,3 +36,16 @@ def test_extras_require():
expected_wheels = utils.expected_wheels('spam', '0.1.0') expected_wheels = utils.expected_wheels('spam', '0.1.0')
actual_wheels = os.listdir('wheelhouse') actual_wheels = os.listdir('wheelhouse')
assert set(actual_wheels) == set(expected_wheels) assert set(actual_wheels) == set(expected_wheels)
def test_failing_test():
'''Ensure a failing test causes cibuildwheel to error out and exit'''
project_dir = os.path.dirname(__file__)
with pytest.raises(subprocess.CalledProcessError):
utils.cibuildwheel_run(project_dir, add_env={
'CIBW_TEST_COMMAND': 'false',
})
assert len(os.listdir('wheelhouse'))
+3 -3
View File
@@ -7,11 +7,11 @@ def test():
# build the wheels # build the wheels
utils.cibuildwheel_run(project_dir, add_env={ utils.cibuildwheel_run(project_dir, add_env={
'CIBW_BUILD': 'cp3?-*', 'CIBW_BUILD': 'cp3?-*',
'CIBW_SKIP': 'cp34-*', 'CIBW_SKIP': 'cp37-*',
}) })
# check that we got the right wheels. There should be no 2.7 or 3.4. # check that we got the right wheels. There should be no 2.7 or 3.7.
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0') expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
if ('-cp3' in w) and ('-cp34' not in w)] if ('-cp3' in w) and ('-cp37' not in w)]
actual_wheels = os.listdir('wheelhouse') actual_wheels = os.listdir('wheelhouse')
assert set(actual_wheels) == set(expected_wheels) assert set(actual_wheels) == set(expected_wheels)
+7 -4
View File
@@ -5,14 +5,17 @@ def test():
project_dir = os.path.dirname(__file__) project_dir = os.path.dirname(__file__)
if utils.platform != 'linux': if utils.platform != 'linux':
pytest.skip('the docker test is only relevant to the linux build') pytest.skip('the test is only relevant to the linux build')
utils.cibuildwheel_run(project_dir, add_env={ utils.cibuildwheel_run(project_dir, add_env={
'CIBW_MANYLINUX1_X86_64_IMAGE': 'dockcross/manylinux-x64', 'CIBW_MANYLINUX_X86_64_IMAGE': 'dockcross/manylinux2010-x64',
'CIBW_MANYLINUX1_I686_IMAGE': 'dockcross/manylinux-x86', 'CIBW_MANYLINUX_I686_IMAGE': 'dockcross/manylinux1-x86',
'CIBW_BEFORE_BUILD': '/opt/python/cp36-cp36m/bin/pip install -U auditwheel', # Currently necessary on dockcross images to get auditwheel 2.1 supporting AUDITWHEEL_PLAT
'CIBW_ENVIRONMENT': 'AUDITWHEEL_PLAT=`if [ $(uname -i) == "x86_64" ]; then echo "manylinux2010_x86_64"; else echo "manylinux1_i686"; fi`',
}) })
# also check that we got the right wheels built # also check that we got the right wheels built
expected_wheels = utils.expected_wheels('spam', '0.1.0') expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
if '-manylinux2010_i686' not in w]
actual_wheels = os.listdir('wheelhouse') actual_wheels = os.listdir('wheelhouse')
assert set(actual_wheels) == set(expected_wheels) assert set(actual_wheels) == set(expected_wheels)
@@ -0,0 +1,21 @@
import os, pytest
import utils
def test():
project_dir = os.path.dirname(__file__)
if utils.platform != 'linux':
pytest.skip('the docker test is only relevant to the linux build')
# build the wheels
# CFLAGS environment veriable is ecessary to fail on 'malloc_info' (on manylinux1) during compilation/linking,
# rather than when dynamically loading the Python
utils.cibuildwheel_run(project_dir, add_env={
'CIBW_ENVIRONMENT': 'CFLAGS="$CFLAGS -Werror=implicit-function-declaration"',
})
# also check that we got the right wheels
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
if not '-manylinux' in w or '-manylinux2010' in w]
actual_wheels = os.listdir('wheelhouse')
assert set(actual_wheels) == set(expected_wheels)
+7
View File
@@ -0,0 +1,7 @@
from setuptools import setup, Extension
setup(
name="spam",
ext_modules=[Extension('spam', sources=['spam.c'])],
version="0.1.0",
)
+57
View File
@@ -0,0 +1,57 @@
#include <Python.h>
#if defined(__linux__)
#include <malloc.h>
#endif
static PyObject *
spam_system(PyObject *self, PyObject *args)
{
const char *command;
int sts = 0;
if (!PyArg_ParseTuple(args, "s", &command))
return NULL;
#if defined(__linux__)
sts = malloc_info(0, stdout);
#endif
if (sts == 0) {
sts = system(command);
}
return PyLong_FromLong(sts);
}
/* Module initialization */
#if PY_MAJOR_VERSION >= 3
#define MOD_INIT(name) PyMODINIT_FUNC PyInit_##name(void)
#define MOD_DEF(m, name, doc, methods, module_state_size) \
static struct PyModuleDef moduledef = { \
PyModuleDef_HEAD_INIT, name, doc, module_state_size, methods, }; \
m = PyModule_Create(&moduledef);
#define MOD_RETURN(m) return m;
#else
#define MOD_INIT(name) PyMODINIT_FUNC init##name(void)
#define MOD_DEF(m, name, doc, methods, module_state_size) \
m = Py_InitModule3(name, methods, doc);
#define MOD_RETURN(m) return;
#endif
static PyMethodDef module_methods[] = {
{"system", (PyCFunction)spam_system, METH_VARARGS,
"Execute a shell command."},
{NULL} /* Sentinel */
};
MOD_INIT(spam)
{
PyObject* m;
MOD_DEF(m,
"spam",
"Example module",
module_methods,
-1)
MOD_RETURN(m)
}
+27 -14
View File
@@ -6,7 +6,8 @@ This file is added to the PYTHONPATH in the test runner at bin/run_test.py.
import subprocess, sys, os import subprocess, sys, os
IS_RUNNING_ON_AZURE = os.path.exists('C:\\hostedtoolcache') IS_WINDOWS_RUNNING_ON_AZURE = os.path.exists('C:\\hostedtoolcache')
IS_WINDOWS_RUNNING_ON_TRAVIS = os.environ.get('TRAVIS_OS_NAME') == 'windows'
def cibuildwheel_get_build_identifiers(project_path, env=None): def cibuildwheel_get_build_identifiers(project_path, env=None):
@@ -25,14 +26,14 @@ def cibuildwheel_get_build_identifiers(project_path, env=None):
def cibuildwheel_run(project_path, env=None, add_env=None): def cibuildwheel_run(project_path, env=None, add_env=None):
''' '''
Runs cibuildwheel as a subprocess, building the project at project_path. Runs cibuildwheel as a subprocess, building the project at project_path.
Uses the current Python interpreter. Uses the current Python interpreter.
Configure settings using env. Configure settings using env.
''' '''
if env is None: if env is None:
env = os.environ.copy() env = os.environ.copy()
if add_env is not None: if add_env is not None:
env.update(add_env) env.update(add_env)
@@ -50,45 +51,57 @@ def expected_wheels(package_name, package_version):
templates = [ templates = [
'{package_name}-{package_version}-cp27-cp27m-manylinux1_x86_64.whl', '{package_name}-{package_version}-cp27-cp27m-manylinux1_x86_64.whl',
'{package_name}-{package_version}-cp27-cp27mu-manylinux1_x86_64.whl', '{package_name}-{package_version}-cp27-cp27mu-manylinux1_x86_64.whl',
'{package_name}-{package_version}-cp34-cp34m-manylinux1_x86_64.whl',
'{package_name}-{package_version}-cp35-cp35m-manylinux1_x86_64.whl', '{package_name}-{package_version}-cp35-cp35m-manylinux1_x86_64.whl',
'{package_name}-{package_version}-cp36-cp36m-manylinux1_x86_64.whl', '{package_name}-{package_version}-cp36-cp36m-manylinux1_x86_64.whl',
'{package_name}-{package_version}-cp37-cp37m-manylinux1_x86_64.whl', '{package_name}-{package_version}-cp37-cp37m-manylinux1_x86_64.whl',
'{package_name}-{package_version}-cp38-cp38-manylinux1_x86_64.whl',
'{package_name}-{package_version}-cp27-cp27m-manylinux2010_x86_64.whl',
'{package_name}-{package_version}-cp27-cp27mu-manylinux2010_x86_64.whl',
'{package_name}-{package_version}-cp35-cp35m-manylinux2010_x86_64.whl',
'{package_name}-{package_version}-cp36-cp36m-manylinux2010_x86_64.whl',
'{package_name}-{package_version}-cp37-cp37m-manylinux2010_x86_64.whl',
'{package_name}-{package_version}-cp38-cp38-manylinux2010_x86_64.whl',
'{package_name}-{package_version}-cp27-cp27m-manylinux1_i686.whl', '{package_name}-{package_version}-cp27-cp27m-manylinux1_i686.whl',
'{package_name}-{package_version}-cp27-cp27mu-manylinux1_i686.whl', '{package_name}-{package_version}-cp27-cp27mu-manylinux1_i686.whl',
'{package_name}-{package_version}-cp34-cp34m-manylinux1_i686.whl',
'{package_name}-{package_version}-cp35-cp35m-manylinux1_i686.whl', '{package_name}-{package_version}-cp35-cp35m-manylinux1_i686.whl',
'{package_name}-{package_version}-cp36-cp36m-manylinux1_i686.whl', '{package_name}-{package_version}-cp36-cp36m-manylinux1_i686.whl',
'{package_name}-{package_version}-cp37-cp37m-manylinux1_i686.whl', '{package_name}-{package_version}-cp37-cp37m-manylinux1_i686.whl',
'{package_name}-{package_version}-cp38-cp38-manylinux1_i686.whl',
'{package_name}-{package_version}-cp27-cp27m-manylinux2010_i686.whl',
'{package_name}-{package_version}-cp27-cp27mu-manylinux2010_i686.whl',
'{package_name}-{package_version}-cp35-cp35m-manylinux2010_i686.whl',
'{package_name}-{package_version}-cp36-cp36m-manylinux2010_i686.whl',
'{package_name}-{package_version}-cp37-cp37m-manylinux2010_i686.whl',
'{package_name}-{package_version}-cp38-cp38-manylinux2010_i686.whl',
] ]
elif platform == 'windows': elif platform == 'windows':
templates = [ templates = [
'{package_name}-{package_version}-cp27-cp27m-win32.whl', '{package_name}-{package_version}-cp27-cp27m-win32.whl',
'{package_name}-{package_version}-cp34-cp34m-win32.whl',
'{package_name}-{package_version}-cp35-cp35m-win32.whl', '{package_name}-{package_version}-cp35-cp35m-win32.whl',
'{package_name}-{package_version}-cp36-cp36m-win32.whl', '{package_name}-{package_version}-cp36-cp36m-win32.whl',
'{package_name}-{package_version}-cp37-cp37m-win32.whl', '{package_name}-{package_version}-cp37-cp37m-win32.whl',
'{package_name}-{package_version}-cp38-cp38-win32.whl',
'{package_name}-{package_version}-cp27-cp27m-win_amd64.whl', '{package_name}-{package_version}-cp27-cp27m-win_amd64.whl',
'{package_name}-{package_version}-cp34-cp34m-win_amd64.whl',
'{package_name}-{package_version}-cp35-cp35m-win_amd64.whl', '{package_name}-{package_version}-cp35-cp35m-win_amd64.whl',
'{package_name}-{package_version}-cp36-cp36m-win_amd64.whl', '{package_name}-{package_version}-cp36-cp36m-win_amd64.whl',
'{package_name}-{package_version}-cp37-cp37m-win_amd64.whl', '{package_name}-{package_version}-cp37-cp37m-win_amd64.whl',
'{package_name}-{package_version}-cp38-cp38-win_amd64.whl',
] ]
elif platform == 'macos': elif platform == 'macos':
templates = [ templates = [
'{package_name}-{package_version}-cp27-cp27m-macosx_10_6_intel.whl', '{package_name}-{package_version}-cp27-cp27m-macosx_10_6_intel.whl',
'{package_name}-{package_version}-cp34-cp34m-macosx_10_6_intel.whl',
'{package_name}-{package_version}-cp35-cp35m-macosx_10_6_intel.whl', '{package_name}-{package_version}-cp35-cp35m-macosx_10_6_intel.whl',
'{package_name}-{package_version}-cp36-cp36m-macosx_10_6_intel.whl', '{package_name}-{package_version}-cp36-cp36m-macosx_10_6_intel.whl',
'{package_name}-{package_version}-cp37-cp37m-macosx_10_6_intel.whl', '{package_name}-{package_version}-cp37-cp37m-macosx_10_6_intel.whl',
'{package_name}-{package_version}-cp38-cp38-macosx_10_9_x86_64.whl',
] ]
else: else:
raise Exception('unsupported platform') raise Exception('unsupported platform')
if IS_RUNNING_ON_AZURE: if IS_WINDOWS_RUNNING_ON_TRAVIS:
# Python 3.4 isn't supported on Azure. # Python 2.7 isn't supported on Travis.
templates = [t for t in templates if '-cp34-' not in t] templates = [t for t in templates if '-cp27-' not in t]
return [filename.format(package_name=package_name, package_version=package_version) return [filename.format(package_name=package_name, package_version=package_version)
for filename in templates] for filename in templates]