Merge remote-tracking branch 'origin/master' into deterministic-builds

This commit is contained in:
Joe Rickerby
2020-03-01 12:53:07 +00:00
25 changed files with 539 additions and 206 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
$PYTHON --version $PYTHON --version
$PYTHON -m pip --version $PYTHON -m pip --version
$PYTHON -m pip install -q --user --ignore-installed --upgrade "virtualenv<20" $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
+24
View File
@@ -9,6 +9,30 @@ matrix:
services: docker services: docker
env: PYTHON=python env: PYTHON=python
# Linux Python 3
- sudo: required
language: python
python: 3.5
services: docker
arch: arm64
env: PYTHON=python
# Linux Python 3
- sudo: required
language: python
python: 3.5
services: docker
arch: ppc64le
env: PYTHON=python
# Linux Python 3
- sudo: required
language: python
python: 3.5
services: docker
arch: s390x
env: PYTHON=python
# macOS Python 3 # macOS Python 3
- os: osx - os: osx
env: PYTHON=python3 env: PYTHON=python3
+2
View File
@@ -7,3 +7,5 @@ This is a summary of the Python versions and platforms covered by the different
| Windows | TravisCI | Azure Pipelines | AppVeyor | Azure Pipelines | | Windows | TravisCI | Azure Pipelines | AppVeyor | Azure Pipelines |
> ¹ Python version not really pinned, but dependent on the (default) version of image used. > ¹ Python version not really pinned, but dependent on the (default) version of image used.
Non-x86 architectures are covered on Travis CI using Python 3.5.
+22 -16
View File
@@ -15,17 +15,19 @@ Python wheels are great. Building them across **Mac, Linux, Windows**, on **mult
What does it do? What does it do?
---------------- ----------------
| | macOS 10.9+ x86_64 | manylinux i686 | manylinux x86_64 | Windows 32bit | Windows 64bit | | | macOS x86_64 | Windows 64bit | Windows 32bit | manylinux x86_64 | manylinux i686 | manylinux aarch64 | manylinux ppc64le | manylinux s390x |
|---|---|---|---|---|---| |---|---|---|---|---|---|---|---|---|
| Python 2.7 | ✅ | ✅ | ✅ | ✅¹ | ✅¹ | | CPython 2.7 | ✅ | ✅¹ | ✅¹ | ✅ | ✅ | | | |
| Python 3.5 | ✅ | ✅ | ✅ | ✅ | ✅ | | CPython 3.5 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Python 3.6 | ✅ | ✅ | ✅ | ✅ | ✅ | | CPython 3.6 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Python 3.7 | ✅ | ✅ | ✅ | ✅ | ✅ | | CPython 3.7 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Python 3.8 | ✅ | ✅ | ✅ | ✅ | ✅ | | CPython 3.8 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| PyPy 2.7 v7.3.0 | ✅ | | ✅ | ✅ | | | | |
| PyPy 3.6 v7.3.0 | ✅ | | ✅ | ✅ | | | | |
> ¹ Not supported on Travis <sup>¹ Not supported on Travis</sup>
- Builds manylinux, macOS and Windows (32 and 64bit) wheels 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)
- Runs the library test suite against the wheel-installed version of your library - Runs the library test suite against the wheel-installed version of your library
@@ -48,7 +50,7 @@ Usage
Example setup Example setup
------------- -------------
To build manylinux, macOS, and Windows wheels on Travis CI and upload them to PyPI whenever you tag a version, you could use this `.travis.yml`: To build manylinux, macOS, and Windows wheels on Travis CI and upload them to PyPI whenever you tag a version, you could use this `.travis.yml`:
```yaml ```yaml
language: python language: python
@@ -73,15 +75,19 @@ 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:
- python -m pip install twine cibuildwheel==1.1.0 - python3 -m pip install cibuildwheel==1.1.0
script: script:
# build the wheels, put them into './wheelhouse' # build the wheels, put them into './wheelhouse'
- python -m cibuildwheel --output-dir wheelhouse - python3 -m cibuildwheel --output-dir wheelhouse
after_success: after_success:
# if the release was tagged, upload them to PyPI # if the release was tagged, upload them to PyPI
- if [[ $TRAVIS_TAG ]]; then python -m twine upload wheelhouse/*.whl; fi - |
if [[ $TRAVIS_TAG ]]; then
python3 -m pip install twine
python3 -m twine upload wheelhouse/*.whl
fi
``` ```
For more information, including how to build on Appveyor, Azure, CircleCI, check out the [documentation](https://cibuildwheel.readthedocs.org) and also check out [the examples](https://github.com/joerick/cibuildwheel/tree/master/examples). 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).
@@ -96,7 +102,7 @@ Options
| **Build environment** | [`CIBW_ENVIRONMENT`](https://cibuildwheel.readthedocs.io/en/stable/options/#environment) | Set environment variables needed during the build | | **Build environment** | [`CIBW_ENVIRONMENT`](https://cibuildwheel.readthedocs.io/en/stable/options/#environment) | Set environment variables needed during the build |
| | [`CIBW_BEFORE_BUILD`](https://cibuildwheel.readthedocs.io/en/stable/options/#before-build) | Execute a shell command preparing each wheel's build | | | [`CIBW_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) | Specify alternative manylinux docker images | | | [`CIBW_MANYLINUX_X86_64_IMAGE`](https://cibuildwheel.readthedocs.io/en/stable/options/#manylinux-image) [`CIBW_MANYLINUX_I686_IMAGE`](https://cibuildwheel.readthedocs.io/en/stable/options/#manylinux-image) [`CIBW_MANYLINUX_PYPY_X86_64_IMAGE`](https://cibuildwheel.readthedocs.io/en/stable/options/#manylinux-image) | Specify alternative manylinux docker images |
| **Testing** | [`CIBW_TEST_COMMAND`](https://cibuildwheel.readthedocs.io/en/stable/options/#test-command) | Execute a shell command to test each built wheel | | **Testing** | [`CIBW_TEST_COMMAND`](https://cibuildwheel.readthedocs.io/en/stable/options/#test-command) | Execute a shell command to test each built 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 |
@@ -136,8 +142,8 @@ Changelog
_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)
- ✨ Add `CIBW_REPAIR_WHEEL_COMMAND` env variable, for changing how - ✨ Add `CIBW_REPAIR_WHEEL_COMMAND` env variable, for changing how
+2 -2
View File
@@ -10,7 +10,7 @@ jobs:
python ./bin/run_tests.py python ./bin/run_tests.py
- job: macos_35 - job: macos_35
pool: {vmImage: 'macOS-10.13'} pool: {vmImage: 'macOS-10.15'}
steps: steps:
- task: UsePythonVersion@0 - task: UsePythonVersion@0
inputs: inputs:
@@ -20,7 +20,7 @@ jobs:
python ./bin/run_tests.py python ./bin/run_tests.py
- job: macos_38 - job: macos_38
pool: {vmImage: 'macOS-10.13'} pool: {vmImage: 'macOS-10.15'}
steps: steps:
- task: UsePythonVersion@0 - task: UsePythonVersion@0
inputs: inputs:
+26
View File
@@ -170,6 +170,10 @@ def main():
if platform == 'linux': if platform == 'linux':
manylinux_x86_64_image = os.environ.get('CIBW_MANYLINUX_X86_64_IMAGE', 'manylinux2010') manylinux_x86_64_image = os.environ.get('CIBW_MANYLINUX_X86_64_IMAGE', 'manylinux2010')
manylinux_i686_image = os.environ.get('CIBW_MANYLINUX_I686_IMAGE', 'manylinux2010') manylinux_i686_image = os.environ.get('CIBW_MANYLINUX_I686_IMAGE', 'manylinux2010')
manylinux_pypy_x86_64_image = os.environ.get('CIBW_MANYLINUX_PYPY_X86_64_IMAGE', 'manylinux2010')
manylinux_aarch64_image = os.environ.get('CIBW_MANYLINUX_AARCH64_IMAGE', 'manylinux2014')
manylinux_ppc64le_image = os.environ.get('CIBW_MANYLINUX_PPC64LE_IMAGE', 'manylinux2014')
manylinux_s390x_image = os.environ.get('CIBW_MANYLINUX_S390X_IMAGE', 'manylinux2014')
pinned_docker_images_file = os.path.join( pinned_docker_images_file = os.path.join(
os.path.dirname(__file__), 'resources', 'pinned_docker_images.cfg' os.path.dirname(__file__), 'resources', 'pinned_docker_images.cfg'
@@ -194,6 +198,28 @@ def main():
else: else:
manylinux_images['i686'] = manylinux_i686_image manylinux_images['i686'] = manylinux_i686_image
if manylinux_pypy_x86_64_image in pinned_docker_images:
manylinux_images['pypy_x86_64'] = pinned_docker_images[manylinux_pypy_x86_64_image]['pypy_x86_64']
else:
manylinux_images['pypy_x86_64'] = manylinux_pypy_x86_64_image
if manylinux_aarch64_image in pinned_docker_images:
manylinux_images['aarch64'] = pinned_docker_images[manylinux_aarch64_image]['aarch64']
else:
manylinux_images['aarch64'] = manylinux_aarch64_image
if manylinux_ppc64le_image in pinned_docker_images:
manylinux_images['ppc64le'] = pinned_docker_images[manylinux_ppc64le_image]['ppc64le']
else:
manylinux_images['ppc64le'] = manylinux_ppc64le_image
if manylinux_s390x_image in pinned_docker_images:
manylinux_images['x390x'] = pinned_docker_images[manylinux_s390x_image]['x390x']
else:
manylinux_images['x390x'] = manylinux_s390x_image
manylinux_images['pypy_x86_64'] = ''
build_options.update( build_options.update(
manylinux_images=manylinux_images manylinux_images=manylinux_images
) )
+66 -22
View File
@@ -1,4 +1,5 @@
import os import os
import platform
import shlex import shlex
import subprocess import subprocess
import sys import sys
@@ -12,25 +13,59 @@ from .util import (
) )
def matches_platform(identifier):
pm = platform.machine()
if pm == "x86_64":
# x86_64 machines can run i686 docker containers
if identifier.endswith('x86_64') or identifier.endswith('i686'):
return True
elif pm == "i686":
if identifier.endswith('i686'):
return True
elif pm == "aarch64":
if identifier.endswith('aarch64'):
return True
elif pm == "ppc64le":
if identifier.endswith('ppc64le'):
return True
elif pm == "s390x":
if identifier.endswith('s390x'):
return True
return False
def get_python_configurations(build_selector): def get_python_configurations(build_selector):
PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'identifier', 'path']) PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'identifier', 'path'])
python_configurations = [ python_configurations = [
PythonConfiguration(version='2.7', identifier='cp27-manylinux_x86_64', path='/opt/python/cp27-cp27m'), PythonConfiguration(identifier='cp27-manylinux_x86_64', path='/opt/python/cp27-cp27m'),
PythonConfiguration(version='2.7', identifier='cp27-manylinux_x86_64', path='/opt/python/cp27-cp27mu'), PythonConfiguration(identifier='cp27-manylinux_x86_64', path='/opt/python/cp27-cp27mu'),
PythonConfiguration(version='3.5', identifier='cp35-manylinux_x86_64', path='/opt/python/cp35-cp35m'), PythonConfiguration(identifier='cp35-manylinux_x86_64', path='/opt/python/cp35-cp35m'),
PythonConfiguration(version='3.6', identifier='cp36-manylinux_x86_64', path='/opt/python/cp36-cp36m'), PythonConfiguration(identifier='cp36-manylinux_x86_64', path='/opt/python/cp36-cp36m'),
PythonConfiguration(version='3.7', identifier='cp37-manylinux_x86_64', path='/opt/python/cp37-cp37m'), PythonConfiguration(identifier='cp37-manylinux_x86_64', path='/opt/python/cp37-cp37m'),
PythonConfiguration(version='3.8', identifier='cp38-manylinux_x86_64', path='/opt/python/cp38-cp38'), PythonConfiguration(identifier='cp38-manylinux_x86_64', path='/opt/python/cp38-cp38'),
PythonConfiguration(version='2.7', identifier='cp27-manylinux_i686', path='/opt/python/cp27-cp27m'), PythonConfiguration(identifier='cp27-manylinux_i686', path='/opt/python/cp27-cp27m'),
PythonConfiguration(version='2.7', identifier='cp27-manylinux_i686', path='/opt/python/cp27-cp27mu'), PythonConfiguration(identifier='cp27-manylinux_i686', path='/opt/python/cp27-cp27mu'),
PythonConfiguration(version='3.5', identifier='cp35-manylinux_i686', path='/opt/python/cp35-cp35m'), PythonConfiguration(identifier='cp35-manylinux_i686', path='/opt/python/cp35-cp35m'),
PythonConfiguration(version='3.6', identifier='cp36-manylinux_i686', path='/opt/python/cp36-cp36m'), PythonConfiguration(identifier='cp36-manylinux_i686', path='/opt/python/cp36-cp36m'),
PythonConfiguration(version='3.7', identifier='cp37-manylinux_i686', path='/opt/python/cp37-cp37m'), PythonConfiguration(identifier='cp37-manylinux_i686', path='/opt/python/cp37-cp37m'),
PythonConfiguration(version='3.8', identifier='cp38-manylinux_i686', path='/opt/python/cp38-cp38'), PythonConfiguration(identifier='cp38-manylinux_i686', path='/opt/python/cp38-cp38'),
PythonConfiguration(identifier='pp27-manylinux_x86_64', path='/opt/python/pp27-pypy_73'),
PythonConfiguration(identifier='pp36-manylinux_x86_64', path='/opt/python/pp36-pypy36_pp73'),
PythonConfiguration(identifier='cp35-manylinux_aarch64', path='/opt/python/cp35-cp35m'),
PythonConfiguration(identifier='cp36-manylinux_aarch64', path='/opt/python/cp36-cp36m'),
PythonConfiguration(identifier='cp37-manylinux_aarch64', path='/opt/python/cp37-cp37m'),
PythonConfiguration(identifier='cp38-manylinux_aarch64', path='/opt/python/cp38-cp38'),
PythonConfiguration(identifier='cp35-manylinux_ppc64le', path='/opt/python/cp35-cp35m'),
PythonConfiguration(identifier='cp36-manylinux_ppc64le', path='/opt/python/cp36-cp36m'),
PythonConfiguration(identifier='cp37-manylinux_ppc64le', path='/opt/python/cp37-cp37m'),
PythonConfiguration(identifier='cp38-manylinux_ppc64le', path='/opt/python/cp38-cp38'),
PythonConfiguration(identifier='cp35-manylinux_s390x', path='/opt/python/cp35-cp35m'),
PythonConfiguration(identifier='cp36-manylinux_s390x', path='/opt/python/cp36-cp36m'),
PythonConfiguration(identifier='cp37-manylinux_s390x', path='/opt/python/cp37-cp37m'),
PythonConfiguration(identifier='cp38-manylinux_s390x', path='/opt/python/cp38-cp38'),
] ]
# skip builds as required # skip builds as required
return [c for c in python_configurations if build_selector(c.identifier)] return [c for c in python_configurations if matches_platform(c.identifier) and build_selector(c.identifier)]
def build(project_dir, output_dir, test_command, test_requires, test_extras, before_build, build_verbosity, build_selector, repair_command, environment, manylinux_images, dependency_constraints): def build(project_dir, output_dir, test_command, test_requires, test_extras, before_build, build_verbosity, build_selector, repair_command, environment, manylinux_images, dependency_constraints):
@@ -45,12 +80,16 @@ 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 = [
('manylinux_x86_64', manylinux_images['x86_64']), ('cp', 'manylinux_x86_64', manylinux_images['x86_64']),
('manylinux_i686', manylinux_images['i686']), ('cp', 'manylinux_i686', manylinux_images['i686']),
('cp', 'manylinux_aarch64', manylinux_images['aarch64']),
('cp', 'manylinux_ppc64le', manylinux_images['ppc64le']),
('cp', 'manylinux_s390x', manylinux_images['s390x']),
('pp', 'manylinux_x86_64', manylinux_images['pypy_x86_64']),
] ]
for platform_tag, docker_image in platforms: for implementation, platform_tag, docker_image in platforms:
platform_configs = [c for c in python_configurations if 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
@@ -84,14 +123,19 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef
cd /project cd /project
PYBIN="{config_python_bin}" PYBIN="{config_python_bin}"
export PATH="$PYBIN:$PATH"
export PATH="$PYBIN:$PATH"
{environment_exports} {environment_exports}
# check the active python and pip are in PYBIN # check the active python and pip are in PYBIN
# if `test` returns false, the script will exit due to errexit if [ "$(which pip)" != "$PYBIN/pip" ]; then
test "$(which pip)" = "$PYBIN/pip" echo "cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it."
test "$(which python)" = "$PYBIN/python" exit 1
fi
if [ "$(which python)" != "$PYBIN/python" ]; then
echo "cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it."
exit 1
fi
if [ ! -z {before_build} ]; then if [ ! -z {before_build} ]; then
sh -c {before_build} sh -c {before_build}
+103 -43
View File
@@ -2,6 +2,7 @@ import os
import shlex import shlex
import shutil import shutil
import subprocess import subprocess
import sys
import tempfile import tempfile
from collections import namedtuple from collections import namedtuple
from glob import glob from glob import glob
@@ -14,6 +15,16 @@ from .util import (
) )
def call(args, env=None, cwd=None, shell=False):
# print the command executing for the logs
if shell:
print('+ %s' % args)
else:
print('+ ' + ' '.join(shlex.quote(a) for a in args))
return subprocess.check_call(args, env=env, cwd=cwd, shell=shell)
def get_python_configurations(build_selector): def get_python_configurations(build_selector):
PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'identifier', 'url']) PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'identifier', 'url'])
python_configurations = [ python_configurations = [
@@ -22,12 +33,79 @@ def get_python_configurations(build_selector):
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.6/python-3.7.6-macosx10.9.pkg'), PythonConfiguration(version='3.7', identifier='cp37-macosx_x86_64', url='https://www.python.org/ftp/python/3.7.6/python-3.7.6-macosx10.9.pkg'),
PythonConfiguration(version='3.8', identifier='cp38-macosx_x86_64', url='https://www.python.org/ftp/python/3.8.1/python-3.8.1-macosx10.9.pkg'), PythonConfiguration(version='3.8', identifier='cp38-macosx_x86_64', url='https://www.python.org/ftp/python/3.8.1/python-3.8.1-macosx10.9.pkg'),
PythonConfiguration(version='2.7-v7.3.0', identifier='pp27-macosx_x86_64', url='https://bitbucket.org/pypy/pypy/downloads/pypy2.7-v7.3.0-osx64.tar.bz2'),
PythonConfiguration(version='3.6-v7.3.0', identifier='pp36-macosx_x86_64', url='https://bitbucket.org/pypy/pypy/downloads/pypy3.6-v7.3.0-osx64.tar.bz2'),
] ]
# skip builds as required # 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)]
SYMLINKS_DIR = '/tmp/cibw_bin'
def make_symlinks(installation_bin_path, python_executable, pip_executable):
assert os.path.exists(os.path.join(installation_bin_path, python_executable))
# Python bin folders on Mac don't symlink `python3` to `python`, and neither
# does PyPy for `pypy` or `pypy3`, so we do that so `python` and `pip` always
# point to the active configuration.
if os.path.exists(SYMLINKS_DIR):
shutil.rmtree(SYMLINKS_DIR)
os.makedirs(SYMLINKS_DIR)
os.symlink(os.path.join(installation_bin_path, python_executable), os.path.join(SYMLINKS_DIR, 'python'))
os.symlink(os.path.join(installation_bin_path, python_executable + '-config'), os.path.join(SYMLINKS_DIR, 'python-config'))
os.symlink(os.path.join(installation_bin_path, pip_executable), os.path.join(SYMLINKS_DIR, 'pip'))
def install_cpython(version, url):
installed_system_packages = subprocess.check_output(['pkgutil', '--pkgs'], universal_newlines=True).splitlines()
# if this version of python isn't installed, get it from python.org and install
python_package_identifier = 'org.python.Python.PythonFramework-{}'.format(version)
if python_package_identifier not in installed_system_packages:
# download the pkg
download(url, '/tmp/Python.pkg')
# install
call(['sudo', 'installer', '-pkg', '/tmp/Python.pkg', '-target', '/'])
# patch open ssl
if version == '3.5':
open_ssl_patch_url = 'https://github.com/mayeut/patch-macos-python-openssl/releases/download/v1.0.2t/patch-macos-python-%s-openssl-v1.0.2t.tar.gz' % version
download(open_ssl_patch_url, '/tmp/python-patch.tar.gz')
call(['sudo', 'tar', '-C', '/Library/Frameworks/Python.framework/Versions/{}/'.format(version), '-xmf', '/tmp/python-patch.tar.gz'])
installation_bin_path = '/Library/Frameworks/Python.framework/Versions/{}/bin'.format(version)
python_executable = 'python3' if version[0] == '3' else 'python'
pip_executable = 'pip3' if version[0] == '3' else 'pip'
make_symlinks(installation_bin_path, python_executable, pip_executable)
return installation_bin_path
def install_pypy(version, url):
pypy_tar_bz2 = url.rsplit('/', 1)[-1]
assert pypy_tar_bz2.endswith(".tar.bz2")
pypy_base_filename = os.path.splitext(os.path.splitext(pypy_tar_bz2)[0])[0]
installation_path = os.path.join('/tmp', pypy_base_filename)
if not os.path.exists(installation_path):
download(url, os.path.join("/tmp", pypy_tar_bz2))
call(['tar', '-C', '/tmp', '-xf', os.path.join("/tmp", pypy_tar_bz2)])
# fix PyPy 7.3.0 bug resulting in wrong macOS platform tag
if version.endswith("-v7.3.0") and version[0] == '3':
patch_file = os.path.abspath(os.path.join(os.path.dirname(__file__), 'resources', 'pypy3.6.patch'))
sysconfigdata_file = os.path.join(installation_path, 'lib_pypy', '_sysconfigdata.py')
call(['patch', sysconfigdata_file, patch_file, '-N']) # Always has nonzero return code
installation_bin_path = os.path.join(installation_path, 'bin')
python_executable = 'pypy3' if version[0] == '3' else 'pypy'
pip_executable = 'pip3' if version[0] == '3' else 'pip'
make_symlinks(installation_bin_path, python_executable, pip_executable)
return installation_bin_path
def build(project_dir, output_dir, test_command, test_requires, test_extras, before_build, build_verbosity, build_selector, repair_command, environment, dependency_constraints): def build(project_dir, output_dir, test_command, test_requires, test_extras, before_build, build_verbosity, build_selector, repair_command, environment, dependency_constraints):
abs_project_dir = os.path.abspath(project_dir) abs_project_dir = os.path.abspath(project_dir)
temp_dir = tempfile.mkdtemp(prefix='cibuildwheel') temp_dir = tempfile.mkdtemp(prefix='cibuildwheel')
@@ -36,57 +114,37 @@ 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)
pkgs_output = subprocess.check_output(['pkgutil', '--pkgs'], universal_newlines=True)
installed_system_packages = pkgs_output.splitlines()
def call(args, env=None, cwd=None, shell=False):
# print the command executing for the logs
if shell:
print('+ %s' % args)
else:
print('+ ' + ' '.join(shlex.quote(a) for a in args))
return subprocess.check_call(args, env=env, cwd=cwd, shell=shell)
for config in python_configurations: for config in python_configurations:
# if this version of python isn't installed, get it from python.org and install if config.identifier.startswith('cp'):
python_package_identifier = 'org.python.Python.PythonFramework-%s' % config.version installation_bin_path = install_cpython(config.version, config.url)
if python_package_identifier not in installed_system_packages: elif config.identifier.startswith('pp'):
# download the pkg installation_bin_path = install_pypy(config.version, config.url)
download(config.url, '/tmp/Python.pkg') else:
# install raise ValueError("Unknown Python implementation")
call(['sudo', 'installer', '-pkg', '/tmp/Python.pkg', '-target', '/'])
# patch open ssl
if config.version == '3.5':
open_ssl_patch_url = 'https://github.com/mayeut/patch-macos-python-openssl/releases/download/v1.0.2t/patch-macos-python-%s-openssl-v1.0.2t.tar.gz' % config.version
download(open_ssl_patch_url, '/tmp/python-patch.tar.gz')
call(['sudo', 'tar', '-C', '/Library/Frameworks/Python.framework/Versions/%s/' % config.version, '-xmf', '/tmp/python-patch.tar.gz'])
installation_bin_path = '/Library/Frameworks/Python.framework/Versions/{}/bin'.format(config.version)
assert os.path.exists(os.path.join(installation_bin_path, 'python3' if config.version[0] == '3' else 'python'))
# Python bin folders on Mac don't symlink python3 to python, so we do that
# so `python` and `pip` always point to the active configuration.
if os.path.exists('/tmp/cibw_bin'):
shutil.rmtree('/tmp/cibw_bin')
os.makedirs('/tmp/cibw_bin')
if config.version[0] == '3':
os.symlink(os.path.join(installation_bin_path, 'python3'), '/tmp/cibw_bin/python')
os.symlink(os.path.join(installation_bin_path, 'python3-config'), '/tmp/cibw_bin/python-config')
os.symlink(os.path.join(installation_bin_path, 'pip3'), '/tmp/cibw_bin/pip')
env = os.environ.copy() env = os.environ.copy()
env['PATH'] = os.pathsep.join([ env['PATH'] = os.pathsep.join([
'/tmp/cibw_bin', SYMLINKS_DIR,
installation_bin_path, installation_bin_path,
env['PATH'], env['PATH'],
]) ])
# Fix issue with site.py setting the wrong `sys.prefix`, `sys.exec_prefix`,
# `sys.path`, ... for PyPy: https://foss.heptapod.net/pypy/pypy/issues/3175
# Also fix an issue with the shebang of installed scripts inside the
# testing virtualenv- see https://github.com/theacodes/nox/issues/44 and
# https://github.com/pypa/virtualenv/issues/620
# Also see https://github.com/python/cpython/pull/9516
env.pop('__PYVENV_LAUNCHER__', None)
env = environment.as_dictionary(prev_environment=env) env = environment.as_dictionary(prev_environment=env)
# check what version we're on # check what version we're on
call(['which', 'python'], env=env) call(['which', 'python'], env=env)
call(['python', '--version'], env=env) call(['python', '--version'], env=env)
which_python = subprocess.check_output(['which', 'python'], env=env, universal_newlines=True).strip()
if which_python != '/tmp/cibw_bin/python':
print("cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it.", file=sys.stderr)
exit(1)
dependency_constraint_flags = [] dependency_constraint_flags = []
if dependency_constraints: if dependency_constraints:
@@ -95,9 +153,14 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef
] ]
# install pip & wheel # install pip & wheel
call(['python', get_pip_script, '--no-setuptools', '--no-wheel'] + 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 os.path.exists(os.path.join(installation_bin_path, 'pip'))
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()
if which_pip != '/tmp/cibw_bin/pip':
print("cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it.", file=sys.stderr)
exit(1)
call(['pip', 'install', '--upgrade', 'setuptools', 'wheel', 'delocate'] + dependency_constraint_flags, env=env) call(['pip', 'install', '--upgrade', 'setuptools', 'wheel', 'delocate'] + dependency_constraint_flags, env=env)
# setup target platform, only required for python 3.5 # setup target platform, only required for python 3.5
@@ -150,9 +213,6 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef
os.path.join(venv_dir, 'bin'), os.path.join(venv_dir, 'bin'),
virtualenv_env['PATH'], virtualenv_env['PATH'],
]) ])
# Fix some weird issue with the shebang of installed scripts
# See https://github.com/theacodes/nox/issues/44 and https://github.com/pypa/virtualenv/issues/620
virtualenv_env.pop('__PYVENV_LAUNCHER__', None)
# 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)
+7
View File
@@ -0,0 +1,7 @@
--- a/lib_pypy/_sysconfigdata.py Tue Jan 28 22:54:59 2020 +0200
+++ b/lib_pypy/_sysconfigdata.py Wed Jan 29 19:21:23 2020 +0200
@@ -47,4 +47,5 @@
build_time_vars['CC'] += ' -arch %s' % (arch,)
if "CXX" in build_time_vars:
build_time_vars['CXX'] += ' -arch %s' % (arch,)
+ build_time_vars['MACOSX_DEPLOYMENT_TARGET'] = '10.7'
+89 -47
View File
@@ -1,9 +1,11 @@
import os import os
import shutil import shutil
import subprocess import subprocess
import sys
import tempfile import tempfile
from collections import namedtuple from collections import namedtuple
from glob import glob from glob import glob
from zipfile import ZipFile
from .util import ( from .util import (
download, download,
@@ -17,37 +19,54 @@ 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 get_python_path(config): def simple_shell(args, env=None, cwd=None):
nuget_args = get_nuget_args(config) print('+ ' + ' '.join(args))
return os.path.join(nuget_args[-1], nuget_args[0] + "." + config.version, "tools") args = ['cmd', '/E:ON', '/V:ON', '/C'] + args
return subprocess.check_call(' '.join(args), env=env, cwd=cwd)
def get_nuget_args(configuration): if IS_RUNNING_ON_AZURE or IS_RUNNING_ON_TRAVIS:
python_name = "python" if configuration.version[0] == '3' else "python2" shell = simple_shell
if configuration.arch == "32": else:
python_name = python_name + "x86" run_with_env = os.path.abspath(os.path.join(os.path.dirname(__file__), 'resources', 'appveyor_run_with_env.cmd'))
return [python_name, "-Version", configuration.version, "-OutputDirectory", "C:/cibw/python"]
# run_with_env is a cmd file that sets the right environment variables
# to build on AppVeyor.
def shell(args, env=None, cwd=None):
# print the command executing for the logs
print('+ ' + ' '.join(args))
args = ['cmd', '/E:ON', '/V:ON', '/C', run_with_env] + args
return subprocess.check_call(' '.join(args), env=env, cwd=cwd)
def get_nuget_args(version, arch):
python_name = 'python' if version[0] == '3' else 'python2'
if arch == '32':
python_name = python_name + 'x86'
return [python_name, '-Version', version, '-OutputDirectory', 'C:\\cibw\\python']
def get_python_configurations(build_selector): def get_python_configurations(build_selector):
PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'arch', 'identifier']) PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'arch', 'identifier', 'url'])
python_configurations = [ python_configurations = [
PythonConfiguration(version='2.7.17', arch="32", identifier='cp27-win32'), PythonConfiguration(version='2.7.17', arch='32', identifier='cp27-win32', url=None),
PythonConfiguration(version='2.7.17', arch="64", identifier='cp27-win_amd64'), PythonConfiguration(version='2.7.17', arch='64', identifier='cp27-win_amd64', url=None),
PythonConfiguration(version='3.5.4', arch="32", identifier='cp35-win32'), PythonConfiguration(version='3.5.4', arch='32', identifier='cp35-win32', url=None),
PythonConfiguration(version='3.5.4', arch="64", identifier='cp35-win_amd64'), PythonConfiguration(version='3.5.4', arch='64', identifier='cp35-win_amd64', url=None),
PythonConfiguration(version='3.6.8', arch="32", identifier='cp36-win32'), PythonConfiguration(version='3.6.8', arch='32', identifier='cp36-win32', url=None),
PythonConfiguration(version='3.6.8', arch="64", identifier='cp36-win_amd64'), PythonConfiguration(version='3.6.8', arch='64', identifier='cp36-win_amd64', url=None),
PythonConfiguration(version='3.7.6', arch="32", identifier='cp37-win32'), PythonConfiguration(version='3.7.6', arch='32', identifier='cp37-win32', url=None),
PythonConfiguration(version='3.7.6', arch="64", identifier='cp37-win_amd64'), PythonConfiguration(version='3.7.6', arch='64', identifier='cp37-win_amd64', url=None),
PythonConfiguration(version='3.8.1', arch="32", identifier='cp38-win32'), PythonConfiguration(version='3.8.1', arch='32', identifier='cp38-win32', url=None),
PythonConfiguration(version='3.8.1', arch="64", identifier='cp38-win_amd64'), PythonConfiguration(version='3.8.1', arch='64', identifier='cp38-win_amd64', url=None),
PythonConfiguration(version='2.7-v7.3.0', arch='32', identifier='pp27-win32', url='https://bitbucket.org/pypy/pypy/downloads/pypy2.7-v7.3.0-win32.zip'),
PythonConfiguration(version='3.6-v7.3.0', arch='32', identifier='pp36-win32', url='https://bitbucket.org/pypy/pypy/downloads/pypy3.6-v7.3.0-win32.zip'),
] ]
if IS_RUNNING_ON_TRAVIS: if IS_RUNNING_ON_TRAVIS:
# cannot install VCForPython27.msi which is needed for compiling C software # cannot install VCForPython27.msi which is needed for compiling C software
# try with (and similar): msiexec /i VCForPython27.msi ALLUSERS=1 ACCEPT=YES /passive # try with (and similar): msiexec /i VCForPython27.msi ALLUSERS=1 ACCEPT=YES /passive
python_configurations = [c for c in python_configurations if not c.version.startswith('2.7.')] python_configurations = [c for c in python_configurations if not c.version.startswith('2.7')]
# skip builds as required # skip builds as required
python_configurations = [c for c in python_configurations if build_selector(c.identifier)] python_configurations = [c for c in python_configurations if build_selector(c.identifier)]
@@ -55,25 +74,35 @@ def get_python_configurations(build_selector):
return python_configurations return python_configurations
def extract_zip(zip_src, dest):
with ZipFile(zip_src) as zip:
zip.extractall(dest)
def install_cpython(version, arch, nuget):
nuget_args = get_nuget_args(version, arch)
installation_path = os.path.join(nuget_args[-1], nuget_args[0] + '.' + version, 'tools')
simple_shell([nuget, 'install'] + nuget_args)
return installation_path
def install_pypy(version, arch, url):
assert arch == '32'
# Inside the PyPy zip file is a directory with the same name
zip_filename = url.rsplit('/', 1)[-1]
installation_path = os.path.join('C:\\cibw', os.path.splitext(zip_filename)[0])
if not os.path.exists(installation_path):
pypy_zip = os.path.join('C:\\cibw', zip_filename)
download(url, pypy_zip)
# Extract to the parent directory because the zip file still contains a directory
extract_zip(pypy_zip, os.path.dirname(installation_path))
pypy_exe = 'pypy3.exe' if version[0] == '3' else 'pypy.exe'
simple_shell(['mklink', os.path.join(installation_path, 'python.exe'), os.path.join(installation_path, pypy_exe)])
simple_shell(['mklink', '/d', os.path.join(installation_path, 'Scripts'), os.path.join(installation_path, 'bin')])
return installation_path
def build(project_dir, output_dir, test_command, test_requires, test_extras, before_build, build_verbosity, build_selector, repair_command, environment, dependency_constraints): def build(project_dir, output_dir, test_command, test_requires, test_extras, before_build, build_verbosity, build_selector, repair_command, environment, dependency_constraints):
def simple_shell(args, env=None, cwd=None):
print('+ ' + ' '.join(args))
args = ['cmd', '/E:ON', '/V:ON', '/C'] + args
return subprocess.check_call(' '.join(args), env=env, cwd=cwd)
if IS_RUNNING_ON_AZURE or IS_RUNNING_ON_TRAVIS:
shell = simple_shell
else:
run_with_env = os.path.abspath(os.path.join(os.path.dirname(__file__), 'resources', 'appveyor_run_with_env.cmd'))
# run_with_env is a cmd file that sets the right environment variables
# to build on AppVeyor.
def shell(args, env=None, cwd=None):
# print the command executing for the logs
print('+ ' + ' '.join(args))
args = ['cmd', '/E:ON', '/V:ON', '/C', run_with_env] + args
return subprocess.check_call(' '.join(args), env=env, cwd=cwd)
abs_project_dir = os.path.abspath(project_dir) abs_project_dir = os.path.abspath(project_dir)
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')
@@ -86,17 +115,22 @@ 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)
for config in python_configurations: for config in python_configurations:
# install Python # install Python
config_python_path = get_python_path(config) if config.identifier.startswith('cp'):
simple_shell([nuget, "install"] + get_nuget_args(config)) installation_path = install_cpython(config.version, config.arch, nuget)
assert os.path.exists(os.path.join(config_python_path, 'python.exe')) elif config.identifier.startswith('pp'):
installation_path = install_pypy(config.version, config.arch, config.url)
else:
raise ValueError("Unknown Python implementation")
assert os.path.exists(os.path.join(installation_path, 'python.exe'))
# set up PATH and environment variables for run_with_env # set up PATH and environment variables for run_with_env
env = os.environ.copy() env = os.environ.copy()
env['PYTHON_VERSION'] = config.version env['PYTHON_VERSION'] = config.version
env['PYTHON_ARCH'] = config.arch env['PYTHON_ARCH'] = config.arch
env['PATH'] = os.pathsep.join([ env['PATH'] = os.pathsep.join([
config_python_path, installation_path,
os.path.join(config_python_path, 'Scripts'), os.path.join(installation_path, 'Scripts'),
env['PATH'] env['PATH']
]) ])
# update env with results from CIBW_ENVIRONMENT # update env with results from CIBW_ENVIRONMENT
@@ -105,7 +139,11 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef
# for the logs - check we're running the right version of python # for the logs - check we're running the right version of python
simple_shell(['where', 'python'], env=env) simple_shell(['where', 'python'], env=env)
simple_shell(['python', '--version'], env=env) simple_shell(['python', '--version'], env=env)
simple_shell(['python', '-c', '"import struct; print(struct.calcsize(\'P\') * 8)\"'], env=env) simple_shell(['python', '-c', '"import struct; print(struct.calcsize(\'P\') * 8)"'], env=env)
where_python = subprocess.check_output(['where', 'python'], env=env, universal_newlines=True).splitlines()[0].strip()
if where_python != os.path.join(installation_path, 'python.exe'):
print("cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it.", file=sys.stderr)
exit(1)
dependency_constraint_flags = [] dependency_constraint_flags = []
if dependency_constraints: if dependency_constraints:
@@ -114,9 +152,13 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef
] ]
# make sure pip is installed # make sure pip is installed
if not os.path.exists(os.path.join(config_python_path, 'Scripts', 'pip.exe')): if not os.path.exists(os.path.join(installation_path, 'Scripts', 'pip.exe')):
simple_shell(['python', get_pip_script] + dependency_constraint_flags, env=env, cwd="C:\\cibw") simple_shell(['python', get_pip_script], env=env, cwd="C:\\cibw")
assert os.path.exists(os.path.join(config_python_path, 'Scripts', 'pip.exe')) assert os.path.exists(os.path.join(installation_path, 'Scripts', 'pip.exe'))
where_pip = subprocess.check_output(['where', 'pip'], env=env, universal_newlines=True).splitlines()[0].strip()
if where_pip.strip() != os.path.join(installation_path, 'Scripts', 'pip.exe'):
print("cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it.", file=sys.stderr)
exit(1)
# prepare the Python environment # prepare the Python environment
simple_shell(['python', '-m', 'pip', 'install', '--upgrade', 'pip'] + dependency_constraint_flags, env=env) simple_shell(['python', '-m', 'pip', 'install', '--upgrade', 'pip'] + dependency_constraint_flags, env=env)
+25 -1
View File
@@ -22,7 +22,7 @@ Linux wheels are built in the [`manylinux` docker images](https://github.com/pyp
- 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_MANYLINUX_X86_64_IMAGE` and `CIBW_MANYLINUX_I686_IMAGE` options to allow for a custom, preconfigured build environment for the Linux builds. See [options](options.md#manylinux-image) for more details. - Alternative dockers images can be specified with the `CIBW_MANYLINUX_X86_64_IMAGE`, `CIBW_MANYLINUX_I686_IMAGE`, and `CIBW_MANYLINUX_PYPY_X86_64_IMAGE` options to allow for a custom, preconfigured build environment for the Linux builds. See [options](options.md#manylinux-image) for more details.
### Building packages with optional C extensions ### Building packages with optional C extensions
@@ -37,3 +37,27 @@ myextension = Extension(
optional=os.environ.get('CIBUILDWHEEL', '0') != '1', optional=os.environ.get('CIBUILDWHEEL', '0') != '1',
) )
``` ```
### 'No module named XYZ' errors after running cibuildwheel on macOS
`cibuildwheel` on Mac installs the distributions from Python.org system-wide during its operation. This is necessary, but it can cause some confusing errors after cibuildwheel has finished.
Consider the build script:
```bash
python3 -m pip install twine cibuildwheel
python3 -m cibuildwheel --output-dir wheelhouse
python3 -m twine upload wheelhouse/*.whl
# error: no module named 'twine'
```
This doesn't work because while `cibuildwheel` was running, it installed a few new versions of 'python3', so the `python3` run on line 3 isn't the same as the `python3` that ran on line 1.
Solutions to this vary, but the simplest is to install tools immediately before they're used:
```bash
python3 -m pip install cibuildwheel
python3 -m cibuildwheel --output-dir wheelhouse
python3 -m pip install twine
python3 -m twine upload wheelhouse/*.whl
```
+27 -14
View File
@@ -56,7 +56,7 @@ Default: `auto`
`auto` will auto-detect platform using environment variables, such as `TRAVIS_OS_NAME`/`APPVEYOR`/`CIRCLECI`. `auto` will auto-detect platform using environment variables, such as `TRAVIS_OS_NAME`/`APPVEYOR`/`CIRCLECI`.
For `linux` you need Docker running, on Mac or Linux. For `macos`, you need a Mac machine, and note that this script is going to automatically install MacPython on your system, so don't run on your development machine. For `windows`, you need to run in Windows, and it will build and test for all versions of Python at `C:\PythonXX[-x64]`. For `linux` you need Docker running, on macOS or Linux. For `macos`, you need a Mac machine, and note that this script is going to automatically install MacPython on your system, so don't run on your development machine. For `windows`, you need to run in Windows, and `cibuildwheel` will install required versions of Python to `C:\cibw\python` using NuGet.
This option can also be set using the command-line option `--platform`. This option can also be set using the command-line option `--platform`.
@@ -68,21 +68,25 @@ Space-separated list of builds to build and skip. Each build has an identifier l
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.
When setting the options, you can use shell-style globbing syntax (as per `fnmatch`). All the build identifiers supported by cibuildwheel are shown below: When setting the options, you can use shell-style globbing syntax (as per [`fnmatch`](https://docs.python.org/3/library/fnmatch.html)). All the build identifiers supported by cibuildwheel are shown below:
<div class="build-id-table-marker"></div> <div class="build-id-table-marker"></div>
| | macOS 64bit | Manylinux 64bit | Manylinux 32bit | Windows 64bit | Windows 32bit | | | macOS 64bit | Manylinux x86 64bit | Manylinux x86 32bit | Windows 64bit | Windows 32bit | Manylinux Armv8 64bit | Manylinux PPC64LE | Manylinux s390x |
|------------|--------------------|------------------------|----------------------|-----------------|----------------| |-----------------|---------------------|------------------------|----------------------|-----------------|----------------|------------------------|------------------------|----------------------|
| Python 2.7 | cp27-macosx_x86_64 | cp27-manylinux_x86_64 | cp27-manylinux_i686 | cp27-win_amd64 | cp27-win32 | | Python 2.7 | cp27-macosx_x86_64 | cp27-manylinux_x86_64 | cp27-manylinux_i686 | cp27-win_amd64 | cp27-win32 | | | |
| Python 3.5 | cp35-macosx_x86_64 | cp35-manylinux_x86_64 | cp35-manylinux_i686 | cp35-win_amd64 | cp35-win32 | | Python 3.5 | cp35-macosx_x86_64 | cp35-manylinux_x86_64 | cp35-manylinux_i686 | cp35-win_amd64 | cp35-win32 | cp35-manylinux_aarch64 | cp35-manylinux_ppc64le | cp35-manylinux_s390x |
| Python 3.6 | cp36-macosx_x86_64 | cp36-manylinux_x86_64 | cp36-manylinux_i686 | cp36-win_amd64 | cp36-win32 | | Python 3.6 | cp36-macosx_x86_64 | cp36-manylinux_x86_64 | cp36-manylinux_i686 | cp36-win_amd64 | cp36-win32 | cp36-manylinux_aarch64 | cp36-manylinux_ppc64le | cp36-manylinux_s390x |
| Python 3.7 | cp37-macosx_x86_64 | cp37-manylinux_x86_64 | cp37-manylinux_i686 | cp37-win_amd64 | cp37-win32 | | Python 3.7 | cp37-macosx_x86_64 | cp37-manylinux_x86_64 | cp37-manylinux_i686 | cp37-win_amd64 | cp37-win32 | cp37-manylinux_aarch64 | cp37-manylinux_ppc64le | cp37-manylinux_s390x |
| Python 3.8 | cp38-macosx_x86_64 | cp38-manylinux_x86_64 | cp38-manylinux_i686 | cp38-win_amd64 | cp38-win32 | | Python 3.8 | cp38-macosx_x86_64 | cp38-manylinux_x86_64 | cp38-manylinux_i686 | cp38-win_amd64 | cp38-win32 | cp38-manylinux_aarch64 | cp38-manylinux_ppc64le | cp38-manylinux_s390x |
| PyPy 2.7 v7.3.0 | pp27-macosx_x86_64 | pp27-manylinux_x86_64 | | | pp27-win32 | | | |
| PyPy 3.6 v7.3.0 | pp36-macosx_x86_64 | pp36-manylinux_x86_64 | | | pp36-win32 | | | |
The list of supported and currently selected build identifiers can also be retrieved by passing the `--print-build-identifiers` flag to `cibuildwheel`. The list of supported and currently selected build identifiers can also be retrieved by passing the `--print-build-identifiers` flag to `cibuildwheel`.
The format is `python_tag-platform_tag`, with tags similar to those in [PEP 425](https://www.python.org/dev/peps/pep-0425/#details). The format is `python_tag-platform_tag`, with tags similar to those in [PEP 425](https://www.python.org/dev/peps/pep-0425/#details).
For CPython, the minimally supported macOS version is 10.9; for PyPy 2.7 and PyPy 3.6, respectively macOS 10.7 and 10.13 or higher is required.
#### Examples #### Examples
```yaml ```yaml
@@ -113,6 +117,12 @@ CIBW_SKIP: cp36-manylinux*
# Only build on Python 3 and skip 32-bit builds # Only build on Python 3 and skip 32-bit builds
CIBW_BUILD: cp3?-* CIBW_BUILD: cp3?-*
CIBW_SKIP: "*-win32 *-manylinux_i686" CIBW_SKIP: "*-win32 *-manylinux_i686"
# Only build PyPy and CPython 3
CIBW_BUILD: pp* cp3?-*
# Disable building PyPy wheels on all platforms
CIBW_SKIP: pp*
``` ```
<style> <style>
@@ -235,16 +245,17 @@ CIBW_REPAIR_WHEEL_COMMAND_LINUX: "auditwheel repair --lib-sdir . -w {dest_dir} {
``` ```
### `CIBW_MANYLINUX_X86_64_IMAGE`, `CIBW_MANYLINUX_I686_IMAGE` {: #manylinux-image} ### `CIBW_MANYLINUX_X86_64_IMAGE`, `CIBW_MANYLINUX_I686_IMAGE`, `CIBW_MANYLINUX_PYPY_X86_64_IMAGE`, `CIBW_MANYLINUX_AARCH64_IMAGE`, `CIBW_MANYLINUX_PPC64LE_IMAGE`, `CIBW_MANYLINUX_S390X_IMAGE` {: #manylinux-image}
> Specify alternative manylinux docker images > Specify alternative manylinux docker images
An alternative Docker image to be used for building [`manylinux`](https://github.com/pypa/manylinux) wheels. `cibuildwheel` will then pull these instead of the default images, [`quay.io/pypa/manylinux2010_x86_64`](https://quay.io/pypa/manylinux2010_x86_64) and [`quay.io/pypa/manylinux2010_i686`](https://quay.io/pypa/manylinux2010_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), [`quay.io/pypa/manylinux2010_i686`](https://quay.io/pypa/manylinux2010_i686), [`pypywheels/manylinux2010-pypy_x86_64`](https://hub.docker.com/r/pypywheels/manylinux2010-pypy_x86_64), [`quay.io/pypa/manylinux2014_aarch64`](https://quay.io/pypa/manylinux2014_aarch64), [`quay.io/pypa/manylinux2014_ppc64le`](https://quay.io/pypa/manylinux2014_ppc64le), and [`quay.io/pypa/manylinux2014_s390x`](https://quay.io/pypa/manylinux2010_s390x).
The value of this option can either be set to `manylinux1`, `manylinux2010` or `manylinux2014` to use a pinned version of the [official `manylinux` images](https://github.com/pypa/manylinux). Alternatively, set this options to any other valid Docker image name. The value of this option can either be set to `manylinux1`, `manylinux2010` or `manylinux2014` to use a pinned version of the [official `manylinux` images](https://github.com/pypa/manylinux) and [PyPy `manylinux` images](https://github.com/pypy/manylinux). Alternatively, set these options to any other valid Docker image name. Note that for PyPy, only the official `manylinux2010` image is currently available. For architectures other
than x86 (x86\_64 and i686) manylinux2014 must be used because this is the first version of the manylinux specification that supports additional architectures.
Beware to specify a valid Docker image that can be used in the same way as the official, default Docker images: all necessary Python and pip versions need to be present in `/opt/python/`, and the `auditwheel` tool needs to be present for `cibuildwheel` to work. Apart from that, the architecture and relevant shared system libraries need to be manylinux1-, manylinux2010- or manylinux2014-compatible in order to produce valid `manylinux1`/`manylinux2010`/`manylinux2014` wheels (see https://github.com/pypa/manylinux, [PEP 513](https://www.python.org/dev/peps/pep-0513/), [PEP 571](https://www.python.org/dev/peps/pep-0571/ and [PEP 599](https://www.python.org/dev/peps/pep-0599/) for more details). Beware to specify a valid Docker image that can be used in the same way as the official, default Docker images: all necessary Python and pip versions need to be present in `/opt/python/`, and the `auditwheel` tool needs to be present for `cibuildwheel` to work. Apart from that, the architecture and relevant shared system libraries need to be manylinux1-, manylinux2010- or manylinux2014-compatible in order to produce valid `manylinux1`/`manylinux2010`/`manylinux2014` wheels (see [pypa/manylinux on GitHub](https://github.com/pypa/manylinux), [PEP 513](https://www.python.org/dev/peps/pep-0513/), [PEP 571](https://www.python.org/dev/peps/pep-0571/) and [PEP 599](https://www.python.org/dev/peps/pep-0599/) for more details).
Note that `auditwheel` detects the version of the `manylinux` standard in the Docker image through the `AUDITWHEEL_PLAT` environment variable, as `cibuildwheel` has no way of detecting the correct `--plat` command line argument to pass to `auditwheel` for a custom image. If a Docker image does not correctly set this `AUDITWHEEL_PLAT` environment variable, the `CIBW_ENVIRONMENT` option can be used to do so (e.g., `CIBW_ENVIRONMENT="manylinux2010_$(uname -m)"`). Note that `auditwheel` detects the version of the `manylinux` standard in the Docker image through the `AUDITWHEEL_PLAT` environment variable, as `cibuildwheel` has no way of detecting the correct `--plat` command line argument to pass to `auditwheel` for a custom image. If a Docker image does not correctly set this `AUDITWHEEL_PLAT` environment variable, the `CIBW_ENVIRONMENT` option can be used to do so (e.g., `CIBW_ENVIRONMENT='AUDITWHEEL_PLAT="manylinux2010_$(uname -m)"'`).
Note that `manylinux2014` doesn't support builds with Python 2.7 - when building with `manylinux2014`, skip Python 2.7 using `CIBW_SKIP` (see example below). Note that `manylinux2014` doesn't support builds with Python 2.7 - when building with `manylinux2014`, skip Python 2.7 using `CIBW_SKIP` (see example below).
@@ -252,8 +263,10 @@ Note that `manylinux2014` doesn't support builds with Python 2.7 - when building
```yaml ```yaml
# build using the manylinux1 image to ensure manylinux1 wheels are produced # build using the manylinux1 image to ensure manylinux1 wheels are produced
# skip PyPy, since there is no PyPy manylinux1 image
CIBW_MANYLINUX_X86_64_IMAGE: manylinux1 CIBW_MANYLINUX_X86_64_IMAGE: manylinux1
CIBW_MANYLINUX_I686_IMAGE: manylinux1 CIBW_MANYLINUX_I686_IMAGE: manylinux1
CIBW_SKIP: pp*
# build using the manylinux2014 image # build using the manylinux2014 image
CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014 CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014
+1 -1
View File
@@ -4,7 +4,7 @@ image:
build_script: build_script:
# windows # windows
- cmd: pip install cibuildwheel==1.1.0 - cmd: pip3 install cibuildwheel==1.1.0
- cmd: cibuildwheel --output-dir wheelhouse - cmd: cibuildwheel --output-dir wheelhouse
# linux # linux
- sh: "${HOME}/.localpython3.7.4/bin/python3 -m pip install cibuildwheel==1.1.0" - sh: "${HOME}/.localpython3.7.4/bin/python3 -m pip install cibuildwheel==1.1.0"
+7 -7
View File
@@ -4,19 +4,19 @@ jobs:
steps: steps:
- task: UsePythonVersion@0 - task: UsePythonVersion@0
- bash: | - bash: |
python -m pip install --upgrade pip python3 -m pip install --upgrade pip
pip install cibuildwheel==1.1.0 pip3 install cibuildwheel==1.1.0
cibuildwheel --output-dir wheelhouse . cibuildwheel --output-dir wheelhouse .
- task: PublishBuildArtifacts@1 - task: PublishBuildArtifacts@1
inputs: {pathtoPublish: 'wheelhouse'} inputs: {pathtoPublish: 'wheelhouse'}
- job: macos - job: macos
pool: {vmImage: 'macOS-10.13'} pool: {vmImage: 'macOS-10.15'}
steps: steps:
- task: UsePythonVersion@0 - task: UsePythonVersion@0
- bash: | - bash: |
python -m pip install --upgrade pip python3 -m pip install --upgrade pip
pip install cibuildwheel==1.1.0 pip3 install cibuildwheel==1.1.0
cibuildwheel --output-dir wheelhouse . cibuildwheel --output-dir wheelhouse .
- task: PublishBuildArtifacts@1 - task: PublishBuildArtifacts@1
inputs: {pathtoPublish: 'wheelhouse'} inputs: {pathtoPublish: 'wheelhouse'}
@@ -28,8 +28,8 @@ jobs:
- 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: |
python -m pip install --upgrade pip python3 -m pip install --upgrade pip
pip install cibuildwheel==1.1.0 pip3 install cibuildwheel==1.1.0
cibuildwheel --output-dir wheelhouse . cibuildwheel --output-dir wheelhouse .
- task: PublishBuildArtifacts@1 - task: PublishBuildArtifacts@1
inputs: {pathtoPublish: 'wheelhouse'} inputs: {pathtoPublish: 'wheelhouse'}
+2 -2
View File
@@ -11,7 +11,7 @@ jobs:
- run: - run:
name: Build the Linux wheels. name: Build the Linux wheels.
command: | command: |
pip install --user cibuildwheel==1.1.0 pip3 install --user cibuildwheel==1.1.0
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: |
pip install --user cibuildwheel==1.1.0 pip3 install --user cibuildwheel==1.1.0
cibuildwheel --output-dir wheelhouse cibuildwheel --output-dir wheelhouse
- store_artifacts: - store_artifacts:
path: wheelhouse/ path: wheelhouse/
+7 -3
View File
@@ -23,12 +23,16 @@ 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:
- python -m pip install twine cibuildwheel==1.0.0 - python3 -m pip install cibuildwheel==1.1.0
script: script:
# build the wheels, put them into './wheelhouse' # build the wheels, put them into './wheelhouse'
- python -m cibuildwheel --output-dir wheelhouse - python3 -m cibuildwheel --output-dir wheelhouse
after_success: after_success:
# if the release was tagged, upload them to PyPI # if the release was tagged, upload them to PyPI
- if [[ $TRAVIS_TAG ]]; then python -m twine upload wheelhouse/*.whl; fi - |
if [[ $TRAVIS_TAG ]]; then
python3 -m pip install twine
python3 -m twine upload wheelhouse/*.whl
fi
+11 -2
View File
@@ -4,6 +4,15 @@ jobs:
include: include:
# perform a linux build # perform a linux build
- services: docker - services: docker
# perform a linux ARMv8 build
- services: docker
arch: aarch64
# perform a linux PPC64LE build
- services: docker
arch: ppc64le
# perform a linux S390X build
- services: docker
arch: s390x
# and a mac build # and a mac build
- os: osx - os: osx
language: shell language: shell
@@ -15,8 +24,8 @@ jobs:
- export PATH="/c/Python38:/c/Python38/Scripts:$PATH" - export PATH="/c/Python38:/c/Python38/Scripts:$PATH"
install: install:
- python -m pip install cibuildwheel==1.1.0 - python3 -m pip install cibuildwheel==1.1.0
script: script:
# build the wheels, put them into './wheelhouse' # build the wheels, put them into './wheelhouse'
- python -m cibuildwheel --output-dir wheelhouse - python3 -m cibuildwheel --output-dir wheelhouse
+21 -13
View File
@@ -20,10 +20,10 @@ before_install:
fi fi
install: install:
- python -m pip install pytest - python3 -m pip install pytest
script: script:
- python setup.py install - python3 setup.py install
- pytest - pytest
stages: stages:
@@ -46,31 +46,39 @@ jobs:
- stage: deploy - stage: deploy
name: Deploy source distribution name: Deploy source distribution
install: skip install: skip
script: python setup.py sdist --formats=gztar script: python3 setup.py sdist --formats=gztar
after_success: python -m twine upload --skip-existing dist/*.tar.gz after_success: |
python3 -m pip install twine
python3 -m twine upload --skip-existing dist/*.tar.gz
# Deploy on linux # Deploy on linux
- stage: deploy - stage: deploy
name: Build and deploy Linux wheels name: Build and deploy Linux wheels
services: docker services: docker
install: python -m pip install twine cibuildwheel==1.0.0 install: python3 -m pip install cibuildwheel==1.1.0
script: python -m cibuildwheel --output-dir wheelhouse script: python3 -m cibuildwheel --output-dir wheelhouse
after_success: python -m twine upload --skip-existing wheelhouse/*.whl after_success: |
python3 -m pip install twine
python3 -m twine upload --skip-existing wheelhouse/*.whl
# Deploy on mac # Deploy on mac
- stage: deploy - stage: deploy
name: Build and deploy macOS wheels name: Build and deploy macOS wheels
os: osx os: osx
language: shell language: shell
install: python -m pip install twine cibuildwheel==1.0.0 install: python3 -m pip install cibuildwheel==1.1.0
script: python -m cibuildwheel --output-dir wheelhouse script: python3 -m cibuildwheel --output-dir wheelhouse
after_success: python -m twine upload --skip-existing wheelhouse/*.whl after_success: |
python3 -m pip install twine
python3 -m twine upload --skip-existing wheelhouse/*.whl
# Deploy on windows # Deploy on windows
- stage: deploy - stage: deploy
name: Build and deploy Windows wheels name: Build and deploy Windows wheels
os: windows os: windows
language: shell language: shell
install: python -m pip install twine cibuildwheel==1.0.0 install: python3 -m pip install cibuildwheel==1.1.0
script: python -m cibuildwheel --output-dir wheelhouse script: python3 -m cibuildwheel --output-dir wheelhouse
after_success: python -m twine upload --skip-existing wheelhouse/*.whl after_success: |
python3 -m pip install twine
python3 -m twine upload --skip-existing wheelhouse/*.whl
env: env:
global: global:
+6 -2
View File
@@ -1,4 +1,5 @@
import os import os
import platform
import utils import utils
@@ -20,7 +21,10 @@ def test_build_identifiers():
# after adding CIBW_MANYLINUX_IMAGE to support manylinux2010, there # after adding CIBW_MANYLINUX_IMAGE to support manylinux2010, there
# can be multiple wheels for each wheel, though, so we need to limit # can be multiple wheels for each wheel, though, so we need to limit
# the expected wheels # the expected wheels
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0') if platform.machine() in ['x86_64', 'i686']:
if '-manylinux' not in w or '-manylinux1' in w] expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
if '-manylinux' not in w or '-manylinux1' in w]
else:
expected_wheels = utils.expected_wheels('spam', '0.1.0')
build_identifiers = utils.cibuildwheel_get_build_identifiers(project_dir) build_identifiers = utils.cibuildwheel_get_build_identifiers(project_dir)
assert len(expected_wheels) == len(build_identifiers) assert len(expected_wheels) == len(build_identifiers)
+14 -1
View File
@@ -1,5 +1,6 @@
import os import os
import pytest
import subprocess
import utils import utils
@@ -17,3 +18,15 @@ def test():
# 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 = utils.expected_wheels('spam', '0.1.0')
assert set(actual_wheels) == set(expected_wheels) assert set(actual_wheels) == set(expected_wheels)
def test_overridden_path(tmp_path):
project_dir = os.path.dirname(__file__)
# mess up PATH, somehow
with pytest.raises(subprocess.CalledProcessError):
utils.cibuildwheel_run(project_dir, output_dir=tmp_path, add_env={
'CIBW_ENVIRONMENT': '''SOMETHING="$(mkdir new_path && touch new_path/python)" PATH="$(realpath new_path):$PATH"''',
'CIBW_ENVIRONMENT_WINDOWS': '''SOMETHING="$(mkdir new_path && type nul > new_path/python.exe)" PATH="$CD\\new_path;$PATH"''',
})
assert len(os.listdir(str(tmp_path))) == 0
+6 -1
View File
@@ -1,4 +1,5 @@
import os import os
import platform
import pytest import pytest
@@ -10,12 +11,16 @@ def test():
if utils.platform != 'linux': if utils.platform != 'linux':
pytest.skip('the test is only relevant to the linux build') pytest.skip('the test is only relevant to the linux build')
if platform.machine() not in ['x86_64', 'i686']:
pytest.skip('this test is currently only possible on x86_64/i686 due to availability of alternative images')
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={ actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
'CIBW_MANYLINUX_X86_64_IMAGE': 'dockcross/manylinux2010-x64', 'CIBW_MANYLINUX_X86_64_IMAGE': 'dockcross/manylinux2010-x64',
'CIBW_MANYLINUX_I686_IMAGE': 'dockcross/manylinux2010-x86', 'CIBW_MANYLINUX_I686_IMAGE': 'dockcross/manylinux2010-x86',
'CIBW_SKIP': 'pp*',
}) })
# 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 '-pp' not in w]
assert set(actual_wheels) == set(expected_wheels) assert set(actual_wheels) == set(expected_wheels)
@@ -1,4 +1,5 @@
import os import os
import platform
import pytest import pytest
@@ -11,6 +12,9 @@ def test(manylinux_image):
if utils.platform != 'linux': if utils.platform != 'linux':
pytest.skip('the docker test is only relevant to the linux build') pytest.skip('the docker test is only relevant to the linux build')
elif platform.machine not in ['x86_64', 'i686']:
if manylinux_image in ['manylinux1', 'manylinux2010']:
pytest.skip("manylinux1 and 2010 doesn't exist for non-x86 architectures")
# build the wheels # build the wheels
# CFLAGS environment variable is necessary to fail on 'malloc_info' (on manylinux1) during compilation/linking, # CFLAGS environment variable is necessary to fail on 'malloc_info' (on manylinux1) during compilation/linking,
@@ -19,12 +23,22 @@ def test(manylinux_image):
'CIBW_ENVIRONMENT': 'CFLAGS="$CFLAGS -Werror=implicit-function-declaration"', 'CIBW_ENVIRONMENT': 'CFLAGS="$CFLAGS -Werror=implicit-function-declaration"',
'CIBW_MANYLINUX_X86_64_IMAGE': manylinux_image, 'CIBW_MANYLINUX_X86_64_IMAGE': manylinux_image,
'CIBW_MANYLINUX_I686_IMAGE': manylinux_image, 'CIBW_MANYLINUX_I686_IMAGE': manylinux_image,
'CIBW_MANYLINUX_PYPY_X86_64_IMAGE': manylinux_image,
'CIBW_MANYLINUX_AARCH64_IMAGE': manylinux_image,
'CIBW_MANYLINUX_PPC64LE_IMAGE': manylinux_image,
'CIBW_MANYLINUX_S390X_IMAGE': manylinux_image,
} }
if manylinux_image == 'manylinux2014': if manylinux_image == 'manylinux1':
add_env['CIBW_SKIP'] = 'cp27*' # not available on manylinux2014 # We don't have a manylinux1 image for PyPy
add_env['CIBW_SKIP'] = 'pp*'
elif manylinux_image == 'manylinux2014':
# We don't have a manylinux2014 image for PyPy (yet?)
add_env['CIBW_SKIP'] = 'cp27* pp*' # Python 2.7 not available on manylinux2014
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env) actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0', manylinux_versions=[manylinux_image])] expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0', manylinux_versions=[manylinux_image])]
if manylinux_image == 'manylinux2014': if manylinux_image == 'manylinux2014':
expected_wheels = [w for w in expected_wheels if '-cp27' not in w] expected_wheels = [w for w in expected_wheels if '-cp27' not in w]
if manylinux_image in ['manylinux1', 'manylinux2014']:
expected_wheels = [w for w in expected_wheels if '-pp' not in w]
assert set(actual_wheels) == set(expected_wheels) assert set(actual_wheels) == set(expected_wheels)
+17 -11
View File
@@ -10,22 +10,23 @@ project_dir = os.path.dirname(__file__)
def test_cpp11(tmp_path): def test_cpp11(tmp_path):
# This test checks that the C++11 standard is supported # This test checks that the C++11 standard is supported
add_env = {'CIBW_SKIP': 'cp27-win*', 'CIBW_ENVIRONMENT': 'STANDARD=11'} add_env = {'CIBW_SKIP': 'cp27-win* pp27-win32', 'CIBW_ENVIRONMENT': 'STANDARD=11'}
# VC++ for Python 2.7 does not support modern standards # VC++ for Python 2.7 does not support modern standards
if utils.platform == 'macos': if utils.platform == 'macos':
add_env['MACOSX_DEPLOYMENT_TARGET'] = '10.9' add_env['MACOSX_DEPLOYMENT_TARGET'] = '10.9'
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env) actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
expected_wheels = [x for x in utils.expected_wheels( expected_wheels = [w for w in utils.expected_wheels(
'spam', '0.1.0', macosx_deployment_target='10.9') 'spam', '0.1.0', macosx_deployment_target='10.9')
if 'cp27-cp27m-win' not in x] if 'cp27-cp27m-win' not in w
and 'pp27-pypy_73-win32' not in w]
assert set(actual_wheels) == set(expected_wheels) assert set(actual_wheels) == set(expected_wheels)
def test_cpp14(): def test_cpp14():
# This test checks that the C++14 standard is supported # This test checks that the C++14 standard is supported
add_env = {'CIBW_SKIP': 'cp27-win* cp35-win*', 'CIBW_ENVIRONMENT': 'STANDARD=14'} add_env = {'CIBW_SKIP': 'cp27-win* pp27-win32 cp35-win*', 'CIBW_ENVIRONMENT': 'STANDARD=14'}
# VC++ for Python 2.7 does not support modern standards # VC++ for Python 2.7 does not support modern standards
# The manylinux1 docker image does not have a compiler which supports C++11 # The manylinux1 docker image does not have a compiler which supports C++11
# Python 3.4 and 3.5 are compiled with MSVC 10, which does not support C++14 # Python 3.4 and 3.5 are compiled with MSVC 10, which does not support C++14
@@ -33,27 +34,32 @@ def test_cpp14():
add_env['MACOSX_DEPLOYMENT_TARGET'] = '10.9' add_env['MACOSX_DEPLOYMENT_TARGET'] = '10.9'
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env) actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
expected_wheels = [x for x in utils.expected_wheels( expected_wheels = [w for w in utils.expected_wheels(
'spam', '0.1.0', macosx_deployment_target='10.9') 'spam', '0.1.0', macosx_deployment_target='10.9')
if 'cp27-cp27m-win' not in x and 'cp35-cp35m-win' not in x] if 'cp27-cp27m-win' not in w
and 'pp27-pypy_73-win32' not in w
and 'cp35-cp35m-win' not in w]
assert set(actual_wheels) == set(expected_wheels) assert set(actual_wheels) == set(expected_wheels)
def test_cpp17(): def test_cpp17():
# This test checks that the C++17 standard is supported # This test checks that the C++17 standard is supported
# Python 2.7 uses the `register` keyword which is forbidden in the C++17 standard # Python and PyPy 2.7 use the `register` keyword which is forbidden in the C++17 standard
# The manylinux1 docker image does not have a compiler which supports C++11 # The manylinux1 docker image does not have a compiler which supports C++11
# Python 3.4 and 3.5 are compiled with MSVC 10, which does not support C++17 # Python 3.5 and PyPy 3.6 are compiled with MSVC 10, which does not support C++17
if os.environ.get('APPVEYOR_BUILD_WORKER_IMAGE', '') == 'Visual Studio 2015': if os.environ.get('APPVEYOR_BUILD_WORKER_IMAGE', '') == 'Visual Studio 2015':
pytest.skip('Visual Studio 2015 does not support C++17') pytest.skip('Visual Studio 2015 does not support C++17')
add_env = {'CIBW_SKIP': 'cp27-win* cp35-win*', 'CIBW_ENVIRONMENT': 'STANDARD=17'} add_env = {'CIBW_SKIP': 'cp27-win* pp27-win32 cp35-win* pp36-win32', 'CIBW_ENVIRONMENT': 'STANDARD=17'}
if utils.platform == 'macos': if utils.platform == 'macos':
add_env['MACOSX_DEPLOYMENT_TARGET'] = '10.13' add_env['MACOSX_DEPLOYMENT_TARGET'] = '10.13'
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env) actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
expected_wheels = [x for x in utils.expected_wheels( expected_wheels = [w for w in utils.expected_wheels(
'spam', '0.1.0', macosx_deployment_target='10.13') 'spam', '0.1.0', macosx_deployment_target='10.13')
if 'cp27-cp27m-win' not in x and 'cp35-cp35m-win' not in x] if 'cp27-cp27m-win' not in w
and 'pp27-pypy_73-win32' not in w
and 'cp35-cp35m-win' not in w
and 'pp36-pypy36_pp73-win32' not in w]
assert set(actual_wheels) == set(expected_wheels) assert set(actual_wheels) == set(expected_wheels)
+32 -15
View File
@@ -5,6 +5,7 @@ This file is added to the PYTHONPATH in the test runner at bin/run_test.py.
''' '''
import os import os
import platform as pm
import shutil import shutil
import subprocess import subprocess
import sys import sys
@@ -70,7 +71,7 @@ def cibuildwheel_run(project_path, env=None, add_env=None, output_dir=None):
return wheels return wheels
def expected_wheels(package_name, package_version, manylinux_versions=['manylinux1', 'manylinux2010'], def expected_wheels(package_name, package_version, manylinux_versions=None,
macosx_deployment_target=None): macosx_deployment_target=None):
''' '''
Returns a list of expected wheels from a run of cibuildwheel. Returns a list of expected wheels from a run of cibuildwheel.
@@ -79,29 +80,45 @@ def expected_wheels(package_name, package_version, manylinux_versions=['manylinu
# {distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-{platform tag}.whl # {distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-{platform tag}.whl
# {python tag} and {abi tag} are closely related to the python interpreter used to build the wheel # {python tag} and {abi tag} are closely related to the python interpreter used to build the wheel
# so we'll merge them below as python_abi_tag # so we'll merge them below as python_abi_tag
python_abi_tags = ['cp27-cp27m', 'cp35-cp35m', 'cp36-cp36m', 'cp37-cp37m', 'cp38-cp38']
python_abi_tags = ['cp35-cp35m', 'cp36-cp36m', 'cp37-cp37m', 'cp38-cp38']
extra_x86_python_abi_tags = ['cp27-cp27m', 'pp27-pypy_73', 'pp36-pypy36_pp73']
if platform == 'linux': if platform == 'linux':
python_abi_tags.append('cp27-cp27mu') # python 2.7 has 2 different ABI on manylinux if pm.machine() not in ['x86_64', 'i686']:
platform_tags = [] if manylinux_versions is None:
for architecture in ['x86_64', 'i686']: manylinux_versions = ['manylinux2014']
for manylinux_version in manylinux_versions: architectures = {'cp': [pm.machine()]}
platform_tags.append('{manylinux_version}_{architecture}'.format( else:
manylinux_version=manylinux_version, architecture=architecture if manylinux_versions is None:
)) manylinux_versions = ['manylinux1', 'manylinux2010']
python_abi_tags += extra_x86_python_abi_tags
python_abi_tags.append('cp27-cp27mu') # python 2.7 has 2 different ABI on manylinux
architectures = {'cp': ['x86_64', 'i686'], 'pp': ['x86_64']}
platform_tags = {}
for python_implemention in architectures:
platform_tags[python_implemention] = [
'{manylinux_version}_{architecture}'.format(
manylinux_version=manylinux_version, architecture=architecture)
for architecture in architectures[python_implemention]
for manylinux_version in manylinux_versions
]
def get_platform_tags(python_abi_tag): def get_platform_tags(python_abi_tag):
return platform_tags return platform_tags[python_abi_tag[:2]]
elif platform == 'windows': elif platform == 'windows':
python_abi_tags += extra_x86_python_abi_tags
platform_tags = {'cp': ['win32', 'win_amd64'], 'pp': ['win32']}
def get_platform_tags(python_abi_tag): def get_platform_tags(python_abi_tag):
return ['win32', 'win_amd64'] return platform_tags[python_abi_tag[:2]]
elif platform == 'macos': elif platform == 'macos':
python_abi_tags += extra_x86_python_abi_tags
def get_platform_tags(python_abi_tag): def get_platform_tags(python_abi_tag):
return ['macosx_' + (macosx_deployment_target or "10.9").replace(".", "_") + '_x86_64'] default_version = '10.7' if python_abi_tag.startswith('pp') else '10.9'
return ['macosx_{}_x86_64'.format((macosx_deployment_target or default_version).replace('.', '_'))]
else: else:
raise Exception('unsupported platform') raise Exception('unsupported platform')
@@ -115,7 +132,7 @@ def expected_wheels(package_name, package_version, manylinux_versions=['manylinu
if IS_WINDOWS_RUNNING_ON_TRAVIS: if IS_WINDOWS_RUNNING_ON_TRAVIS:
# Python 2.7 isn't supported on Travis. # Python 2.7 isn't supported on Travis.
templates = [t for t in templates if '-cp27-' not in t] templates = [t for t in templates if '-cp27-' not in t and '-pp2' not in t]
return templates return templates
@@ -68,6 +68,11 @@ def test_build_selector(platform, intercepted_build_args, monkeypatch):
('i686', 'manylinux2010', 'quay.io/pypa/manylinux2010_i686:*'), ('i686', 'manylinux2010', 'quay.io/pypa/manylinux2010_i686:*'),
('i686', 'manylinux2014', 'quay.io/pypa/manylinux2014_i686:*'), ('i686', 'manylinux2014', 'quay.io/pypa/manylinux2014_i686:*'),
('i686', 'custom_image', 'custom_image'), ('i686', 'custom_image', 'custom_image'),
('pypy_x86_64', None, 'pypywheels/manylinux2010-pypy_x86_64'),
('pypy_x86_64', 'manylinux1', 'manylinux1'), # Does not exist
('pypy_x86_64', 'manylinux2010', 'pypywheels/manylinux2010-pypy_x86_64'),
('pypy_x86_64', 'manylinux2014', 'manylinux2014'), # Does not exist (yet)
('pypy_x86_64', 'custom_image', 'custom_image'),
]) ])
def test_manylinux_images(architecture, image, full_image, platform, intercepted_build_args, monkeypatch): def test_manylinux_images(architecture, image, full_image, platform, intercepted_build_args, monkeypatch):
if image is not None: if image is not None: