Merge remote-tracking branch 'origin/master' into support-subdirectory
This commit is contained in:
@@ -13,6 +13,7 @@ jobs:
|
||||
- run:
|
||||
name: Test.
|
||||
command: flake8 .
|
||||
|
||||
osx-python3.6:
|
||||
macos:
|
||||
xcode: "9.4.1"
|
||||
@@ -27,6 +28,7 @@ jobs:
|
||||
- run:
|
||||
name: Test.
|
||||
command: venv/bin/python ./bin/run_tests.py
|
||||
no_output_timeout: 30m
|
||||
|
||||
osx-python3.7:
|
||||
macos:
|
||||
@@ -42,6 +44,7 @@ jobs:
|
||||
- run:
|
||||
name: Test.
|
||||
command: venv/bin/python ./bin/run_tests.py
|
||||
no_output_timeout: 30m
|
||||
|
||||
linux-python3.6:
|
||||
docker:
|
||||
@@ -58,6 +61,7 @@ jobs:
|
||||
- run:
|
||||
name: Test.
|
||||
command: venv/bin/python ./bin/run_tests.py
|
||||
no_output_timeout: 30m
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
name: Test
|
||||
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
push: {}
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
# skip branch builds on joerick/cibuildwheel, but always build `master` and pull requests
|
||||
if: github.repository != 'joerick/cibuildwheel' || github.ref == 'refs/heads/master' || github.event_name == 'pull_request'
|
||||
|
||||
name: Test cibuildwheel on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
|
||||
+3
-1
@@ -89,8 +89,10 @@ venv3/
|
||||
venv2/
|
||||
ENV/
|
||||
env/
|
||||
env3/
|
||||
env2/
|
||||
env2?/
|
||||
env3/
|
||||
env3?/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
language: minimal
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- name: Linux | x86_64 + i686 | Python 3.5
|
||||
|
||||
@@ -22,3 +22,7 @@ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
Get-pip and Pip (bundled in cibuildwheel/resources) are licensed under the MIT
|
||||
license. See https://github.com/pypa/get-pip/blob/master/LICENSE.txt
|
||||
|
||||
+18
-1
@@ -7,15 +7,32 @@ environment:
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: macos-mojave
|
||||
APPVEYOR_JOB_NAME: "python37-x64-macos-mojave"
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: macos-mojave
|
||||
|
||||
stack: python 3.7
|
||||
|
||||
build: off
|
||||
|
||||
init:
|
||||
- cmd: set PATH=C:\Python37;C:\Python37\Scripts;%PATH%
|
||||
- ps: |
|
||||
$BRANCH = if ($env:APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH) { $env:APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH } else { $env:APPVEYOR_REPO_BRANCH }
|
||||
if (-not ($BRANCH -eq 'master' -or $BRANCH.ToLower().StartsWith('appveyor-'))) {
|
||||
$env:PYTEST_ADDOPTS='-k "unit_test or _basic" --suppress-no-test-exit-code'
|
||||
}
|
||||
|
||||
install: python -m pip install -r requirements-dev.txt
|
||||
install: python -m pip install -r requirements-dev.txt pytest-custom-exit-code
|
||||
|
||||
# the '-u' flag is required so the output is in the correct order.
|
||||
# See https://github.com/joerick/cibuildwheel/pull/24 for more info.
|
||||
test_script: python -u ./bin/run_tests.py
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
skip_commits:
|
||||
files:
|
||||
- docs/*
|
||||
|
||||
Executable
+75
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import time
|
||||
from pathlib import Path
|
||||
from subprocess import run
|
||||
|
||||
import click
|
||||
import textwrap
|
||||
|
||||
|
||||
def shell(cmd, **kwargs):
|
||||
return run([cmd], shell=True, **kwargs)
|
||||
|
||||
|
||||
def git_repo_has_changes():
|
||||
unstaged_changes = shell('git diff-index --quiet HEAD --').returncode != 0
|
||||
staged_changes = shell('git diff-index --quiet --cached HEAD --').returncode != 0
|
||||
return unstaged_changes or staged_changes
|
||||
|
||||
|
||||
@click.command()
|
||||
def main():
|
||||
project_root = Path(__file__).parent / '..'
|
||||
os.chdir(project_root)
|
||||
|
||||
if git_repo_has_changes():
|
||||
print('Your git repo has uncommitted changes. Commit or stash before continuing.')
|
||||
exit(1)
|
||||
|
||||
previous_branch = shell('git rev-parse --abbrev-ref HEAD',
|
||||
check=True,
|
||||
capture_output=True,
|
||||
encoding='utf8').stdout.strip()
|
||||
|
||||
shell('git fetch origin', check=True)
|
||||
|
||||
timestamp = time.strftime('%Y-%m-%dT%H-%M-%S', time.gmtime())
|
||||
branch_name = f'update-constraints-{timestamp}'
|
||||
|
||||
shell(f'git checkout -b {branch_name} origin/master', check=True)
|
||||
|
||||
try:
|
||||
shell('bin/update_dependencies.py', check=True)
|
||||
|
||||
if not git_repo_has_changes():
|
||||
print('Done: no constraint updates required.')
|
||||
return
|
||||
|
||||
shell('git commit -a -m "Update dependencies"', check=True)
|
||||
run(
|
||||
[
|
||||
'gh', 'pr', 'create',
|
||||
'--repo', 'joerick/cibuildwheel',
|
||||
'--base', 'master',
|
||||
'--title', 'Update dependencies',
|
||||
'--body', textwrap.dedent(f'''
|
||||
Update the versions of our dependencies.
|
||||
|
||||
PR generated by `{os.path.basename(__file__)}`.
|
||||
''')
|
||||
],
|
||||
check=True
|
||||
)
|
||||
|
||||
print('Done.')
|
||||
finally:
|
||||
# remove any local changes
|
||||
shell('git checkout -- .')
|
||||
shell(f'git checkout {previous_branch}', check=True)
|
||||
shell(f'git branch -D --force {branch_name}', check=True)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main.main(standalone_mode=True)
|
||||
Executable
+99
@@ -0,0 +1,99 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import configparser
|
||||
import os
|
||||
import subprocess
|
||||
from collections import namedtuple
|
||||
|
||||
import requests
|
||||
|
||||
os.chdir(os.path.dirname(__file__))
|
||||
os.chdir('..')
|
||||
|
||||
# CUSTOM_COMPILE_COMMAND is a pip-compile option that tells users how to
|
||||
# regenerate the constraints files
|
||||
os.environ['CUSTOM_COMPILE_COMMAND'] = "bin/update_constraints.py"
|
||||
subprocess.check_call([
|
||||
'pip-compile',
|
||||
'--allow-unsafe',
|
||||
'--upgrade',
|
||||
'cibuildwheel/resources/constraints.in',
|
||||
])
|
||||
for python_version in ['27', '35', '36']:
|
||||
subprocess.check_call([
|
||||
f'./env{python_version}/bin/pip-compile',
|
||||
'--allow-unsafe',
|
||||
'--upgrade',
|
||||
'cibuildwheel/resources/constraints.in',
|
||||
'--output-file', f'cibuildwheel/resources/constraints-python{python_version}.txt'
|
||||
])
|
||||
|
||||
Image = namedtuple('Image', [
|
||||
'manylinux_version',
|
||||
'platform',
|
||||
'image_name',
|
||||
])
|
||||
|
||||
images = [
|
||||
Image('manylinux1', 'x86_64', 'quay.io/pypa/manylinux1_x86_64'),
|
||||
Image('manylinux1', 'i686', 'quay.io/pypa/manylinux1_i686'),
|
||||
|
||||
Image('manylinux2010', 'x86_64', 'quay.io/pypa/manylinux2010_x86_64'),
|
||||
Image('manylinux2010', 'i686', 'quay.io/pypa/manylinux2010_i686'),
|
||||
Image('manylinux2010', 'pypy_x86_64', 'pypywheels/manylinux2010-pypy_x86_64'),
|
||||
|
||||
Image('manylinux2014', 'x86_64', 'quay.io/pypa/manylinux2014_x86_64'),
|
||||
Image('manylinux2014', 'i686', 'quay.io/pypa/manylinux2014_i686'),
|
||||
Image('manylinux2014', 'aarch64', 'quay.io/pypa/manylinux2014_aarch64'),
|
||||
Image('manylinux2014', 'ppc64le', 'quay.io/pypa/manylinux2014_ppc64le'),
|
||||
Image('manylinux2014', 's390x', 'quay.io/pypa/manylinux2014_s390x'),
|
||||
]
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
|
||||
for image in images:
|
||||
# get the tag name whose digest matches 'latest'
|
||||
if image.image_name.startswith('quay.io/'):
|
||||
_, _, repository_name = image.image_name.partition('/')
|
||||
response = requests.get(
|
||||
f'https://quay.io/api/v1/repository/{repository_name}?includeTags=true'
|
||||
)
|
||||
response.raise_for_status()
|
||||
repo_info = response.json()
|
||||
tags_dict = repo_info['tags']
|
||||
|
||||
latest_tag = tags_dict.pop('latest')
|
||||
# find the tag whose manifest matches 'latest'
|
||||
tag_name = next(
|
||||
name
|
||||
for (name, info) in tags_dict.items()
|
||||
if info['manifest_digest'] == latest_tag['manifest_digest']
|
||||
)
|
||||
else:
|
||||
response = requests.get(
|
||||
f'https://hub.docker.com/v2/repositories/{image.image_name}/tags'
|
||||
)
|
||||
response.raise_for_status()
|
||||
tags = response.json()['results']
|
||||
|
||||
latest_tag = next(
|
||||
tag for tag in tags if tag['name'] == 'latest'
|
||||
)
|
||||
# i don't know what it would mean to have multiple images per tag
|
||||
assert len(latest_tag['images']) == 1
|
||||
digest = latest_tag['images'][0]['digest']
|
||||
|
||||
pinned_tag = next(
|
||||
tag
|
||||
for tag in tags
|
||||
if tag != latest_tag and tag['images'][0]['digest'] == digest
|
||||
)
|
||||
tag_name = pinned_tag['name']
|
||||
|
||||
if not config.has_section(image.platform):
|
||||
config[image.platform] = {}
|
||||
|
||||
config[image.platform][image.manylinux_version] = f'{image.image_name}:{tag_name}'
|
||||
|
||||
with open('cibuildwheel/resources/pinned_docker_images.cfg', 'w') as f:
|
||||
config.write(f)
|
||||
+54
-41
@@ -3,6 +3,7 @@ import os
|
||||
import sys
|
||||
import textwrap
|
||||
import traceback
|
||||
from configparser import ConfigParser
|
||||
|
||||
import cibuildwheel
|
||||
import cibuildwheel.linux
|
||||
@@ -14,7 +15,9 @@ from cibuildwheel.environment import (
|
||||
)
|
||||
from cibuildwheel.util import (
|
||||
BuildSelector,
|
||||
Unbuffered
|
||||
DependencyConstraints,
|
||||
Unbuffered,
|
||||
BuildOptions
|
||||
)
|
||||
|
||||
|
||||
@@ -115,6 +118,14 @@ def main():
|
||||
environment_config = get_option_from_environment('CIBW_ENVIRONMENT', platform=platform, default='')
|
||||
before_test = get_option_from_environment('CIBW_BEFORE_TEST', platform=platform, default='')
|
||||
|
||||
dependency_versions = get_option_from_environment('CIBW_DEPENDENCY_VERSIONS', platform=platform, default='pinned')
|
||||
if dependency_versions == 'pinned':
|
||||
dependency_constraints = DependencyConstraints.with_defaults()
|
||||
elif dependency_versions == 'latest':
|
||||
dependency_constraints = None
|
||||
else:
|
||||
dependency_constraints = DependencyConstraints(dependency_versions)
|
||||
|
||||
if test_extras:
|
||||
test_extras = '[{0}]'.format(test_extras)
|
||||
|
||||
@@ -144,7 +155,40 @@ def main():
|
||||
print_build_identifiers(platform, build_selector)
|
||||
exit(0)
|
||||
|
||||
build_options = dict(
|
||||
if platform == 'linux':
|
||||
pinned_docker_images_file = os.path.join(
|
||||
os.path.dirname(__file__), 'resources', 'pinned_docker_images.cfg'
|
||||
)
|
||||
all_pinned_docker_images = ConfigParser()
|
||||
all_pinned_docker_images.read(pinned_docker_images_file)
|
||||
# all_pinned_docker_images looks like a dict of dicts, e.g.
|
||||
# { 'x86_64': {'manylinux1': '...', 'manylinux2010': '...', 'manylinux2014': '...'},
|
||||
# 'i686': {'manylinux1': '...', 'manylinux2010': '...', 'manylinux2014': '...'},
|
||||
# 'pypy_x86_64': {'manylinux2010': '...' }
|
||||
# ... }
|
||||
|
||||
manylinux_images = {}
|
||||
|
||||
for build_platform in ['x86_64', 'i686', 'pypy_x86_64', 'aarch64', 'ppc64le', 's390x']:
|
||||
pinned_images = all_pinned_docker_images[build_platform]
|
||||
|
||||
config_name = 'CIBW_MANYLINUX_{}_IMAGE'.format(build_platform.upper())
|
||||
config_value = os.environ.get(config_name)
|
||||
|
||||
if config_value is None:
|
||||
# default to manylinux2010 if it's available, otherwise manylinux2014
|
||||
image = pinned_images.get('manylinux2010') or pinned_images.get('manylinux2014')
|
||||
elif config_value in pinned_images:
|
||||
image = pinned_images[config_value]
|
||||
else:
|
||||
image = config_value
|
||||
|
||||
manylinux_images[build_platform] = image
|
||||
|
||||
else:
|
||||
manylinux_images = None
|
||||
|
||||
build_options = BuildOptions(
|
||||
project_dir='.',
|
||||
package_dir=package_dir,
|
||||
output_dir=output_dir,
|
||||
@@ -156,42 +200,11 @@ def main():
|
||||
build_selector=build_selector,
|
||||
repair_command=repair_command,
|
||||
environment=environment,
|
||||
before_test=before_test
|
||||
before_test=before_test,
|
||||
dependency_constraints=dependency_constraints,
|
||||
manylinux_images=manylinux_images,
|
||||
)
|
||||
|
||||
if platform == 'linux':
|
||||
manylinux_x86_64_image = os.environ.get('CIBW_MANYLINUX_X86_64_IMAGE', 'manylinux2010')
|
||||
manylinux_i686_image = os.environ.get('CIBW_MANYLINUX_I686_IMAGE', 'manylinux2010')
|
||||
manylinux_pypy_x86_64_image = os.environ.get('CIBW_MANYLINUX_PYPY_X86_64_IMAGE', 'manylinux2010')
|
||||
manylinux_aarch64_image = os.environ.get('CIBW_MANYLINUX_AARCH64_IMAGE', 'manylinux2014')
|
||||
manylinux_ppc64le_image = os.environ.get('CIBW_MANYLINUX_PPC64LE_IMAGE', 'manylinux2014')
|
||||
manylinux_s390x_image = os.environ.get('CIBW_MANYLINUX_S390X_IMAGE', 'manylinux2014')
|
||||
|
||||
default_manylinux_images_x86_64 = {'manylinux1': 'quay.io/pypa/manylinux1_x86_64',
|
||||
'manylinux2010': 'quay.io/pypa/manylinux2010_x86_64',
|
||||
'manylinux2014': 'quay.io/pypa/manylinux2014_x86_64'}
|
||||
default_manylinux_images_i686 = {'manylinux1': 'quay.io/pypa/manylinux1_i686',
|
||||
'manylinux2010': 'quay.io/pypa/manylinux2010_i686',
|
||||
'manylinux2014': 'quay.io/pypa/manylinux2014_i686'}
|
||||
default_manylinux_images_pypy_x86_64 = {'manylinux2010': 'pypywheels/manylinux2010-pypy_x86_64'}
|
||||
default_manylinux_images_aarch64 = {'manylinux2014': 'quay.io/pypa/manylinux2014_aarch64'}
|
||||
default_manylinux_images_ppc64le = {'manylinux2014': 'quay.io/pypa/manylinux2014_ppc64le'}
|
||||
default_manylinux_images_s390x = {'manylinux2014': 'quay.io/pypa/manylinux2014_s390x'}
|
||||
|
||||
build_options.update(
|
||||
manylinux_images={'x86_64': default_manylinux_images_x86_64.get(manylinux_x86_64_image) or manylinux_x86_64_image,
|
||||
'i686': default_manylinux_images_i686.get(manylinux_i686_image) or manylinux_i686_image,
|
||||
'pypy_x86_64': default_manylinux_images_pypy_x86_64.get(manylinux_pypy_x86_64_image) or manylinux_pypy_x86_64_image,
|
||||
'aarch64': default_manylinux_images_aarch64.get(manylinux_aarch64_image) or manylinux_aarch64_image,
|
||||
'ppc64le': default_manylinux_images_ppc64le.get(manylinux_ppc64le_image) or manylinux_ppc64le_image,
|
||||
's390x': default_manylinux_images_s390x.get(manylinux_s390x_image) or manylinux_s390x_image,
|
||||
},
|
||||
)
|
||||
elif platform == 'macos':
|
||||
pass
|
||||
elif platform == 'windows':
|
||||
pass
|
||||
|
||||
# Python is buffering by default when running on the CI platforms, giving problems interleaving subprocess call output with unflushed calls to 'print'
|
||||
sys.stdout = Unbuffered(sys.stdout)
|
||||
|
||||
@@ -201,11 +214,11 @@ def main():
|
||||
os.makedirs(output_dir)
|
||||
|
||||
if platform == 'linux':
|
||||
cibuildwheel.linux.build(**build_options)
|
||||
cibuildwheel.linux.build(build_options)
|
||||
elif platform == 'windows':
|
||||
cibuildwheel.windows.build(**build_options)
|
||||
cibuildwheel.windows.build(build_options)
|
||||
elif platform == 'macos':
|
||||
cibuildwheel.macos.build(**build_options)
|
||||
cibuildwheel.macos.build(build_options)
|
||||
else:
|
||||
print('cibuildwheel: Unsupported platform: {}'.format(platform), file=sys.stderr)
|
||||
exit(2)
|
||||
@@ -250,7 +263,7 @@ def print_preamble(platform, build_options):
|
||||
|
||||
print('Build options:')
|
||||
print(' platform: %r' % platform)
|
||||
for option, value in sorted(build_options.items()):
|
||||
for option, value in sorted(build_options._asdict().items()):
|
||||
print(' %s: %r' % (option, value))
|
||||
|
||||
warnings = detect_warnings(platform, build_options)
|
||||
@@ -281,7 +294,7 @@ def detect_warnings(platform, build_options):
|
||||
|
||||
# warn about deprecated {python} and {pip}
|
||||
for option_name in ['test_command', 'before_build']:
|
||||
option_value = build_options.get(option_name)
|
||||
option_value = getattr(build_options, option_name)
|
||||
|
||||
if option_value:
|
||||
if '{python}' in option_value or '{pip}' in option_value:
|
||||
|
||||
+206
-169
@@ -10,9 +10,17 @@ from collections import namedtuple
|
||||
from .util import (
|
||||
get_build_verbosity_extra_flags,
|
||||
prepare_command,
|
||||
BuildOptions
|
||||
)
|
||||
|
||||
|
||||
def call(args, input=None, universal_newlines=False):
|
||||
print('+ ' + ' '.join(shlex.quote(a) for a in args))
|
||||
subprocess.run(
|
||||
args, input=input, universal_newlines=universal_newlines, check=True
|
||||
)
|
||||
|
||||
|
||||
def matches_platform(identifier):
|
||||
pm = platform.machine()
|
||||
if pm == "x86_64":
|
||||
@@ -35,40 +43,40 @@ def matches_platform(identifier):
|
||||
|
||||
|
||||
def get_python_configurations(build_selector):
|
||||
PythonConfiguration = namedtuple('PythonConfiguration', ['identifier', 'path'])
|
||||
PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'identifier', 'path'])
|
||||
python_configurations = [
|
||||
PythonConfiguration(identifier='cp27-manylinux_x86_64', path='/opt/python/cp27-cp27m'),
|
||||
PythonConfiguration(identifier='cp27-manylinux_x86_64', path='/opt/python/cp27-cp27mu'),
|
||||
PythonConfiguration(identifier='cp35-manylinux_x86_64', path='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(identifier='cp36-manylinux_x86_64', path='/opt/python/cp36-cp36m'),
|
||||
PythonConfiguration(identifier='cp37-manylinux_x86_64', path='/opt/python/cp37-cp37m'),
|
||||
PythonConfiguration(identifier='cp38-manylinux_x86_64', path='/opt/python/cp38-cp38'),
|
||||
PythonConfiguration(identifier='cp27-manylinux_i686', path='/opt/python/cp27-cp27m'),
|
||||
PythonConfiguration(identifier='cp27-manylinux_i686', path='/opt/python/cp27-cp27mu'),
|
||||
PythonConfiguration(identifier='cp35-manylinux_i686', path='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(identifier='cp36-manylinux_i686', path='/opt/python/cp36-cp36m'),
|
||||
PythonConfiguration(identifier='cp37-manylinux_i686', path='/opt/python/cp37-cp37m'),
|
||||
PythonConfiguration(identifier='cp38-manylinux_i686', path='/opt/python/cp38-cp38'),
|
||||
PythonConfiguration(identifier='pp27-manylinux_x86_64', path='/opt/python/pp27-pypy_73'),
|
||||
PythonConfiguration(identifier='pp36-manylinux_x86_64', path='/opt/python/pp36-pypy36_pp73'),
|
||||
PythonConfiguration(identifier='cp35-manylinux_aarch64', path='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(identifier='cp36-manylinux_aarch64', path='/opt/python/cp36-cp36m'),
|
||||
PythonConfiguration(identifier='cp37-manylinux_aarch64', path='/opt/python/cp37-cp37m'),
|
||||
PythonConfiguration(identifier='cp38-manylinux_aarch64', path='/opt/python/cp38-cp38'),
|
||||
PythonConfiguration(identifier='cp35-manylinux_ppc64le', path='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(identifier='cp36-manylinux_ppc64le', path='/opt/python/cp36-cp36m'),
|
||||
PythonConfiguration(identifier='cp37-manylinux_ppc64le', path='/opt/python/cp37-cp37m'),
|
||||
PythonConfiguration(identifier='cp38-manylinux_ppc64le', path='/opt/python/cp38-cp38'),
|
||||
PythonConfiguration(identifier='cp35-manylinux_s390x', path='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(identifier='cp36-manylinux_s390x', path='/opt/python/cp36-cp36m'),
|
||||
PythonConfiguration(identifier='cp37-manylinux_s390x', path='/opt/python/cp37-cp37m'),
|
||||
PythonConfiguration(identifier='cp38-manylinux_s390x', path='/opt/python/cp38-cp38'),
|
||||
PythonConfiguration(version='2.7', identifier='cp27-manylinux_x86_64', path='/opt/python/cp27-cp27m'),
|
||||
PythonConfiguration(version='2.7', identifier='cp27-manylinux_x86_64', path='/opt/python/cp27-cp27mu'),
|
||||
PythonConfiguration(version='3.5', identifier='cp35-manylinux_x86_64', path='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(version='3.6', identifier='cp36-manylinux_x86_64', path='/opt/python/cp36-cp36m'),
|
||||
PythonConfiguration(version='3.7', identifier='cp37-manylinux_x86_64', path='/opt/python/cp37-cp37m'),
|
||||
PythonConfiguration(version='3.8', identifier='cp38-manylinux_x86_64', path='/opt/python/cp38-cp38'),
|
||||
PythonConfiguration(version='2.7', identifier='cp27-manylinux_i686', path='/opt/python/cp27-cp27m'),
|
||||
PythonConfiguration(version='2.7', identifier='cp27-manylinux_i686', path='/opt/python/cp27-cp27mu'),
|
||||
PythonConfiguration(version='3.5', identifier='cp35-manylinux_i686', path='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(version='3.6', identifier='cp36-manylinux_i686', path='/opt/python/cp36-cp36m'),
|
||||
PythonConfiguration(version='3.7', identifier='cp37-manylinux_i686', path='/opt/python/cp37-cp37m'),
|
||||
PythonConfiguration(version='3.8', identifier='cp38-manylinux_i686', path='/opt/python/cp38-cp38'),
|
||||
PythonConfiguration(version='2.7', identifier='pp27-manylinux_x86_64', path='/opt/python/pp27-pypy_73'),
|
||||
PythonConfiguration(version='3.6', identifier='pp36-manylinux_x86_64', path='/opt/python/pp36-pypy36_pp73'),
|
||||
PythonConfiguration(version='3.5', identifier='cp35-manylinux_aarch64', path='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(version='3.6', identifier='cp36-manylinux_aarch64', path='/opt/python/cp36-cp36m'),
|
||||
PythonConfiguration(version='3.7', identifier='cp37-manylinux_aarch64', path='/opt/python/cp37-cp37m'),
|
||||
PythonConfiguration(version='3.8', identifier='cp38-manylinux_aarch64', path='/opt/python/cp38-cp38'),
|
||||
PythonConfiguration(version='3.5', identifier='cp35-manylinux_ppc64le', path='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(version='3.6', identifier='cp36-manylinux_ppc64le', path='/opt/python/cp36-cp36m'),
|
||||
PythonConfiguration(version='3.7', identifier='cp37-manylinux_ppc64le', path='/opt/python/cp37-cp37m'),
|
||||
PythonConfiguration(version='3.8', identifier='cp38-manylinux_ppc64le', path='/opt/python/cp38-cp38'),
|
||||
PythonConfiguration(version='3.5', identifier='cp35-manylinux_s390x', path='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(version='3.6', identifier='cp36-manylinux_s390x', path='/opt/python/cp36-cp36m'),
|
||||
PythonConfiguration(version='3.7', identifier='cp37-manylinux_s390x', path='/opt/python/cp37-cp37m'),
|
||||
PythonConfiguration(version='3.8', identifier='cp38-manylinux_s390x', path='/opt/python/cp38-cp38'),
|
||||
]
|
||||
# skip builds as required
|
||||
return [c for c in python_configurations if matches_platform(c.identifier) and build_selector(c.identifier)]
|
||||
|
||||
|
||||
def build(project_dir, output_dir, test_command, before_test, test_requires, test_extras, before_build, build_verbosity, build_selector, repair_command, environment, manylinux_images):
|
||||
def build(options: BuildOptions):
|
||||
try:
|
||||
subprocess.check_call(['docker', '--version'])
|
||||
except Exception:
|
||||
@@ -78,18 +86,18 @@ def build(project_dir, output_dir, test_command, before_test, test_requires, tes
|
||||
file=sys.stderr)
|
||||
exit(2)
|
||||
|
||||
python_configurations = get_python_configurations(build_selector)
|
||||
python_configurations = get_python_configurations(options.build_selector)
|
||||
platforms = [
|
||||
('cp', 'manylinux_x86_64', manylinux_images['x86_64']),
|
||||
('cp', 'manylinux_i686', manylinux_images['i686']),
|
||||
('cp', 'manylinux_aarch64', manylinux_images['aarch64']),
|
||||
('cp', 'manylinux_ppc64le', manylinux_images['ppc64le']),
|
||||
('cp', 'manylinux_s390x', manylinux_images['s390x']),
|
||||
('pp', 'manylinux_x86_64', manylinux_images['pypy_x86_64']),
|
||||
('cp', 'manylinux_x86_64', options.manylinux_images['x86_64']),
|
||||
('cp', 'manylinux_i686', options.manylinux_images['i686']),
|
||||
('cp', 'manylinux_aarch64', options.manylinux_images['aarch64']),
|
||||
('cp', 'manylinux_ppc64le', options.manylinux_images['ppc64le']),
|
||||
('cp', 'manylinux_s390x', options.manylinux_images['s390x']),
|
||||
('pp', 'manylinux_x86_64', options.manylinux_images['pypy_x86_64']),
|
||||
]
|
||||
|
||||
abs_project_dir = os.path.abspath(project_dir)
|
||||
abs_package_dir = os.path.abspath(package_dir)
|
||||
abs_project_dir = os.path.abspath(options.project_dir)
|
||||
abs_package_dir = os.path.abspath(options.package_dir)
|
||||
|
||||
container_project_dir = '/project'
|
||||
container_package_dir = os.path.join(container_project_dir, os.path.relpath(abs_package_dir, os.path.commonprefix([abs_project_dir, abs_package_dir]))),
|
||||
@@ -99,146 +107,175 @@ def build(project_dir, output_dir, test_command, before_test, test_requires, tes
|
||||
if not platform_configs:
|
||||
continue
|
||||
|
||||
bash_script = '''
|
||||
set -o errexit
|
||||
set -o xtrace
|
||||
mkdir /output
|
||||
cd /project
|
||||
|
||||
for PYBIN in {pybin_paths}; do (
|
||||
# Temporary hack/workaround, putting loop body in subshell; fixed in PR #256
|
||||
|
||||
export PATH="$PYBIN:$PATH"
|
||||
{environment_exports}
|
||||
|
||||
# check the active python and pip are in PYBIN
|
||||
if [ "$(which pip)" != "$PYBIN/pip" ]; then
|
||||
echo "cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it."
|
||||
exit 1
|
||||
fi
|
||||
if [ "$(which python)" != "$PYBIN/python" ]; then
|
||||
echo "cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -z {before_build} ]; then
|
||||
sh -c {before_build}
|
||||
fi
|
||||
|
||||
# Build the wheel
|
||||
rm -rf /tmp/built_wheel
|
||||
mkdir /tmp/built_wheel
|
||||
pip wheel {package_dir} -w /tmp/built_wheel --no-deps {build_verbosity_flag}
|
||||
built_wheel=(/tmp/built_wheel/*.whl)
|
||||
|
||||
# repair the wheel
|
||||
rm -rf /tmp/repaired_wheels
|
||||
mkdir /tmp/repaired_wheels
|
||||
# NOTE: 'built_wheel' here is a bash array of glob matches; "$built_wheel" returns
|
||||
# the first element
|
||||
if [[ "$built_wheel" == *none-any.whl ]] || [ -z {repair_command} ]; then
|
||||
# pure Python wheel or empty repair command
|
||||
mv "$built_wheel" /tmp/repaired_wheels
|
||||
else
|
||||
sh -c {repair_command} repair_command "$built_wheel"
|
||||
fi
|
||||
repaired_wheels=(/tmp/repaired_wheels/*.whl)
|
||||
|
||||
if [ ! -z {test_command} ]; then
|
||||
# Set up a virtual environment to install and test from, to make sure
|
||||
# there are no dependencies that were pulled in at build time.
|
||||
pip install virtualenv
|
||||
venv_dir=`mktemp -d`/venv
|
||||
python -m virtualenv "$venv_dir"
|
||||
|
||||
export __CIBW_VIRTUALENV_PATH__=$venv_dir
|
||||
|
||||
# run the tests in a subshell to keep that `activate`
|
||||
# script from polluting the env
|
||||
(
|
||||
source "$venv_dir/bin/activate"
|
||||
|
||||
echo "Running tests using `which python`"
|
||||
|
||||
if [ ! -z {before_test} ]; then
|
||||
sh -c {before_test}
|
||||
fi
|
||||
|
||||
# Install the wheel we just built
|
||||
# Note: If auditwheel produced two wheels, it's because the earlier produced wheel
|
||||
# conforms to multiple manylinux standards. These multiple versions of the wheel are
|
||||
# functionally the same, differing only in name, wheel metadata, and possibly include
|
||||
# different external shared libraries. so it doesn't matter which one we run the tests on.
|
||||
# Let's just pick the first one.
|
||||
pip install "${{repaired_wheels[0]}}"{test_extras}
|
||||
|
||||
# Install any requirements to run the tests
|
||||
if [ ! -z "{test_requires}" ]; then
|
||||
pip install {test_requires}
|
||||
fi
|
||||
|
||||
# Run the tests from a different directory
|
||||
pushd $HOME
|
||||
sh -c {test_command}
|
||||
popd
|
||||
)
|
||||
# exit if tests failed (needed for older bash versions)
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
# clean up
|
||||
rm -rf "$venv_dir"
|
||||
fi
|
||||
|
||||
# we're all done here; move it to output
|
||||
mv "${{repaired_wheels[@]}}" /output
|
||||
for repaired_wheel in "${{repaired_wheels[@]}}"; do chown {uid}:{gid} "/output/$(basename "$repaired_wheel")"; done
|
||||
) done
|
||||
'''.format(
|
||||
pybin_paths=' '.join(c.path + '/bin' for c in platform_configs),
|
||||
package_dir=container_package_dir,
|
||||
test_requires=' '.join(test_requires),
|
||||
test_extras=test_extras,
|
||||
test_command=shlex.quote(
|
||||
prepare_command(test_command, project=container_project_dir, package=container_package_dir) if test_command else ''
|
||||
),
|
||||
before_build=shlex.quote(
|
||||
prepare_command(before_build, project=container_project_dir, package=container_package_dir) if before_build else ''
|
||||
),
|
||||
build_verbosity_flag=' '.join(get_build_verbosity_extra_flags(build_verbosity)),
|
||||
repair_command=shlex.quote(
|
||||
prepare_command(repair_command, wheel='"$1"', dest_dir='/tmp/repaired_wheels') if repair_command else ''
|
||||
),
|
||||
environment_exports='\n'.join(environment.as_shell_commands()),
|
||||
uid=os.getuid(),
|
||||
gid=os.getgid(),
|
||||
before_test=shlex.quote(
|
||||
prepare_command(before_test, project=container_project_dir, package=container_package_dir) if before_test else ''
|
||||
),
|
||||
)
|
||||
|
||||
container_name = 'cibuildwheel-{}'.format(uuid.uuid4())
|
||||
try:
|
||||
subprocess.run(['docker', 'create',
|
||||
'--env', 'CIBUILDWHEEL',
|
||||
'--name', container_name,
|
||||
'-i',
|
||||
'-v', '/:/host', # ignored on CircleCI
|
||||
docker_image, '/bin/bash'], check=True)
|
||||
subprocess.run(['docker', 'cp', os.path.abspath(project_dir) + '/.', container_name + ':/project'], check=True)
|
||||
subprocess.run(['docker', 'start', '-i', '-a', container_name], input=bash_script, universal_newlines=True, check=True)
|
||||
subprocess.run(['docker', 'cp', container_name + ':/output/.', os.path.abspath(output_dir)], check=True)
|
||||
except subprocess.CalledProcessError as error:
|
||||
troubleshoot(project_dir, error)
|
||||
call(['docker', 'create',
|
||||
'--env', 'CIBUILDWHEEL',
|
||||
'--name', container_name,
|
||||
'-i',
|
||||
'-v', '/:/host', # ignored on CircleCI
|
||||
docker_image,
|
||||
'/bin/bash'])
|
||||
|
||||
call(['docker', 'cp',
|
||||
os.path.abspath(options.project_dir) + '/.',
|
||||
container_name + ':/project'])
|
||||
|
||||
call(['docker', 'start', container_name])
|
||||
|
||||
for config in platform_configs:
|
||||
if options.dependency_constraints:
|
||||
constraints_file = options.dependency_constraints.get_for_python_version(config.version)
|
||||
|
||||
# `docker cp` causes 'no space left on device' error when
|
||||
# a container is running and the host filesystem is
|
||||
# mounted. https://github.com/moby/moby/issues/38995
|
||||
# Use `docker exec` instead.
|
||||
with open(constraints_file, 'rb') as f:
|
||||
call(
|
||||
['docker', 'exec', '-i', container_name, 'sh', '-c', 'cat > /constraints.txt'],
|
||||
input=f.read(),
|
||||
)
|
||||
|
||||
call(
|
||||
['docker', 'exec', '-i', container_name, '/bin/bash'],
|
||||
universal_newlines=True,
|
||||
input='''
|
||||
# give xtrace output an extra level of indent inside docker
|
||||
PS4=' + '
|
||||
|
||||
set -o errexit
|
||||
set -o xtrace
|
||||
mkdir -p /output
|
||||
cd /project
|
||||
|
||||
PYBIN="{config_python_bin}"
|
||||
|
||||
export PATH="$PYBIN:$PATH"
|
||||
{environment_exports}
|
||||
|
||||
# check the active python and pip are in PYBIN
|
||||
if [ "$(which pip)" != "$PYBIN/pip" ]; then
|
||||
echo "cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it."
|
||||
exit 1
|
||||
fi
|
||||
if [ "$(which python)" != "$PYBIN/python" ]; then
|
||||
echo "cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -z {before_build} ]; then
|
||||
sh -c {before_build}
|
||||
fi
|
||||
|
||||
# Build the wheel
|
||||
rm -rf /tmp/built_wheel
|
||||
mkdir /tmp/built_wheel
|
||||
pip wheel {package_dir} -w /tmp/built_wheel --no-deps {build_verbosity_flag}
|
||||
built_wheel=(/tmp/built_wheel/*.whl)
|
||||
|
||||
# repair the wheel
|
||||
rm -rf /tmp/repaired_wheels
|
||||
mkdir /tmp/repaired_wheels
|
||||
# NOTE: 'built_wheel' here is a bash array of glob matches; "$built_wheel" returns
|
||||
# the first element
|
||||
if [[ "$built_wheel" == *none-any.whl ]] || [ -z {repair_command} ]; then
|
||||
# pure Python wheel or empty repair command
|
||||
mv "$built_wheel" /tmp/repaired_wheels
|
||||
else
|
||||
sh -c {repair_command} repair_command "$built_wheel"
|
||||
fi
|
||||
repaired_wheels=(/tmp/repaired_wheels/*.whl)
|
||||
|
||||
if [ ! -z {test_command} ]; then
|
||||
# Set up a virtual environment to install and test from, to make sure
|
||||
# there are no dependencies that were pulled in at build time.
|
||||
pip install {dependency_install_flags} virtualenv
|
||||
venv_dir=`mktemp -d`/venv
|
||||
python -m virtualenv --no-download "$venv_dir"
|
||||
export __CIBW_VIRTUALENV_PATH__=$venv_dir
|
||||
|
||||
# run the tests in a subshell to keep that `activate`
|
||||
# script from polluting the env
|
||||
(
|
||||
source "$venv_dir/bin/activate"
|
||||
|
||||
echo "Running tests using `which python`"
|
||||
|
||||
if [ ! -z {before_test} ]; then
|
||||
sh -c {before_test}
|
||||
fi
|
||||
|
||||
# Install the wheel we just built
|
||||
# Note: If auditwheel produced two wheels, it's because the earlier produced wheel
|
||||
# conforms to multiple manylinux standards. These multiple versions of the wheel are
|
||||
# functionally the same, differing only in name, wheel metadata, and possibly include
|
||||
# different external shared libraries. so it doesn't matter which one we run the tests on.
|
||||
# Let's just pick the first one.
|
||||
pip install "${{repaired_wheels[0]}}"{test_extras}
|
||||
|
||||
# Install any requirements to run the tests
|
||||
if [ ! -z "{test_requires}" ]; then
|
||||
pip install {test_requires}
|
||||
fi
|
||||
|
||||
# Run the tests from a different directory
|
||||
pushd $HOME
|
||||
sh -c {test_command}
|
||||
popd
|
||||
)
|
||||
# exit if tests failed (needed for older bash versions)
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
# clean up
|
||||
rm -rf "$venv_dir"
|
||||
fi
|
||||
|
||||
# we're all done here; move it to output
|
||||
mv "${{repaired_wheels[@]}}" /output
|
||||
for repaired_wheel in "${{repaired_wheels[@]}}"; do
|
||||
chown {uid}:{gid} "/output/$(basename "$repaired_wheel")"
|
||||
done
|
||||
'''.format(
|
||||
config_python_bin=config.path + '/bin',
|
||||
package_dir=container_package_dir,
|
||||
test_requires=' '.join(options.test_requires),
|
||||
test_extras=options.test_extras,
|
||||
test_command=shlex.quote(
|
||||
prepare_command(options.test_command, project='/project') if options.test_command else ''
|
||||
),
|
||||
before_build=shlex.quote(
|
||||
prepare_command(options.before_build, project='/project') if options.before_build else ''
|
||||
),
|
||||
build_verbosity_flag=' '.join(get_build_verbosity_extra_flags(options.build_verbosity)),
|
||||
repair_command=shlex.quote(
|
||||
prepare_command(options.repair_command, wheel='"$1"', dest_dir='/tmp/repaired_wheels') if options.repair_command else ''
|
||||
),
|
||||
environment_exports='\n'.join(options.environment.as_shell_commands()),
|
||||
uid=os.getuid(),
|
||||
gid=os.getgid(),
|
||||
before_test=shlex.quote(
|
||||
prepare_command(options.before_test, project='/project') if options.before_test else ''
|
||||
),
|
||||
dependency_install_flags='-c /constraints.txt' if options.dependency_constraints else '',
|
||||
)
|
||||
)
|
||||
|
||||
# copy the output back into the host
|
||||
call(['docker', 'cp',
|
||||
container_name + ':/output/.',
|
||||
os.path.abspath(options.output_dir)])
|
||||
except subprocess.CalledProcessError:
|
||||
exit(1)
|
||||
finally:
|
||||
# Still gets executed, even when 'exit(1)' gets called
|
||||
subprocess.run(['docker', 'rm', '--force', '-v', container_name], check=True)
|
||||
call(['docker', 'rm', '--force', '-v', container_name])
|
||||
|
||||
|
||||
def troubleshoot(project_dir, error):
|
||||
if (isinstance(error, subprocess.CalledProcessError) and 'start' in error.cmd):
|
||||
if (isinstance(error, subprocess.CalledProcessError) and 'exec' in error.cmd):
|
||||
# the bash script failed
|
||||
print('Checking for common errors...')
|
||||
so_files = []
|
||||
|
||||
+93
-79
@@ -11,6 +11,8 @@ from .util import (
|
||||
download,
|
||||
get_build_verbosity_extra_flags,
|
||||
prepare_command,
|
||||
get_pip_script,
|
||||
BuildOptions
|
||||
)
|
||||
|
||||
|
||||
@@ -27,13 +29,15 @@ def call(args, env=None, cwd=None, shell=False):
|
||||
def get_python_configurations(build_selector):
|
||||
PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'identifier', 'url'])
|
||||
python_configurations = [
|
||||
# CPython
|
||||
PythonConfiguration(version='2.7', identifier='cp27-macosx_x86_64', url='https://www.python.org/ftp/python/2.7.17/python-2.7.17-macosx10.9.pkg'),
|
||||
PythonConfiguration(version='3.5', identifier='cp35-macosx_x86_64', url='https://www.python.org/ftp/python/3.5.4/python-3.5.4-macosx10.6.pkg'),
|
||||
PythonConfiguration(version='3.6', identifier='cp36-macosx_x86_64', url='https://www.python.org/ftp/python/3.6.8/python-3.6.8-macosx10.9.pkg'),
|
||||
PythonConfiguration(version='3.7', identifier='cp37-macosx_x86_64', url='https://www.python.org/ftp/python/3.7.6/python-3.7.6-macosx10.9.pkg'),
|
||||
PythonConfiguration(version='3.8', identifier='cp38-macosx_x86_64', url='https://www.python.org/ftp/python/3.8.2/python-3.8.2-macosx10.9.pkg'),
|
||||
PythonConfiguration(version='2.7-v7.3.0', identifier='pp27-macosx_x86_64', url='https://bitbucket.org/pypy/pypy/downloads/pypy2.7-v7.3.0-osx64.tar.bz2'),
|
||||
PythonConfiguration(version='3.6-v7.3.0', identifier='pp36-macosx_x86_64', url='https://bitbucket.org/pypy/pypy/downloads/pypy3.6-v7.3.0-osx64.tar.bz2'),
|
||||
# PyPy
|
||||
PythonConfiguration(version='2.7', identifier='pp27-macosx_x86_64', url='https://bitbucket.org/pypy/pypy/downloads/pypy2.7-v7.3.0-osx64.tar.bz2'),
|
||||
PythonConfiguration(version='3.6', identifier='pp36-macosx_x86_64', url='https://bitbucket.org/pypy/pypy/downloads/pypy3.6-v7.3.0-osx64.tar.bz2'),
|
||||
]
|
||||
|
||||
# skip builds as required
|
||||
@@ -92,7 +96,7 @@ def install_pypy(version, url):
|
||||
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':
|
||||
if "-v7.3.0-" in url 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
|
||||
@@ -105,105 +109,115 @@ def install_pypy(version, url):
|
||||
return installation_bin_path
|
||||
|
||||
|
||||
def build(project_dir, package_dir, output_dir, test_command, before_test, test_requires, test_extras, before_build, build_verbosity, build_selector, repair_command, environment):
|
||||
abs_project_dir = os.path.abspath(project_dir)
|
||||
abs_package_dir = os.path.abspath(package_dir)
|
||||
def setup_python(python_configuration, dependency_constraint_flags, environment):
|
||||
if python_configuration.identifier.startswith('cp'):
|
||||
installation_bin_path = install_cpython(python_configuration.version, python_configuration.url)
|
||||
elif python_configuration.identifier.startswith('pp'):
|
||||
installation_bin_path = install_pypy(python_configuration.version, python_configuration.url)
|
||||
else:
|
||||
raise ValueError("Unknown Python implementation")
|
||||
|
||||
env = os.environ.copy()
|
||||
env['PATH'] = os.pathsep.join([
|
||||
SYMLINKS_DIR,
|
||||
installation_bin_path,
|
||||
env['PATH'],
|
||||
])
|
||||
|
||||
# Fix issue with site.py setting the wrong `sys.prefix`, `sys.exec_prefix`,
|
||||
# `sys.path`, ... for PyPy: https://foss.heptapod.net/pypy/pypy/issues/3175
|
||||
# Also fix an issue with the shebang of installed scripts inside the
|
||||
# testing virtualenv- see https://github.com/theacodes/nox/issues/44 and
|
||||
# https://github.com/pypa/virtualenv/issues/620
|
||||
# Also see https://github.com/python/cpython/pull/9516
|
||||
env.pop('__PYVENV_LAUNCHER__', None)
|
||||
env = environment.as_dictionary(prev_environment=env)
|
||||
|
||||
# check what version we're on
|
||||
call(['which', 'python'], env=env)
|
||||
call(['python', '--version'], env=env)
|
||||
which_python = subprocess.check_output(['which', 'python'], env=env, universal_newlines=True).strip()
|
||||
if which_python != '/tmp/cibw_bin/python':
|
||||
print("cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it.", file=sys.stderr)
|
||||
exit(1)
|
||||
|
||||
# install pip & wheel
|
||||
call(['python', get_pip_script] + dependency_constraint_flags, env=env, cwd="/tmp")
|
||||
assert os.path.exists(os.path.join(installation_bin_path, 'pip'))
|
||||
call(['which', 'pip'], env=env)
|
||||
call(['pip', '--version'], env=env)
|
||||
which_pip = subprocess.check_output(['which', 'pip'], env=env, universal_newlines=True).strip()
|
||||
if which_pip != '/tmp/cibw_bin/pip':
|
||||
print("cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it.", file=sys.stderr)
|
||||
exit(1)
|
||||
call(['pip', 'install', '--upgrade', 'setuptools', 'wheel', 'delocate'] + dependency_constraint_flags, env=env)
|
||||
|
||||
# Set MACOSX_DEPLOYMENT_TARGET to 10.9, if the user didn't set it.
|
||||
# CPython 3.5 defaults to 10.6, and pypy defaults to 10.7, causing
|
||||
# inconsistencies if it's left unset.
|
||||
env.setdefault('MACOSX_DEPLOYMENT_TARGET', '10.9')
|
||||
|
||||
if python_configuration.version == '3.5':
|
||||
# Cross-compilation platform override - CPython 3.5 has an
|
||||
# i386/x86_64 version of Python, but we only want a x64_64 build
|
||||
env.setdefault('_PYTHON_HOST_PLATFORM', 'macosx-10.9-x86_64')
|
||||
# https://github.com/python/cpython/blob/a5ed2fe0eedefa1649aa93ee74a0bafc8e628a10/Lib/_osx_support.py#L260
|
||||
env.setdefault('ARCHFLAGS', '-arch x86_64')
|
||||
|
||||
return env
|
||||
|
||||
|
||||
def build(options: BuildOptions):
|
||||
abs_project_dir = os.path.abspath(options.project_dir)
|
||||
abs_package_dir = os.path.abspath(options.package_dir)
|
||||
temp_dir = tempfile.mkdtemp(prefix='cibuildwheel')
|
||||
built_wheel_dir = os.path.join(temp_dir, 'built_wheel')
|
||||
repaired_wheel_dir = os.path.join(temp_dir, 'repaired_wheel')
|
||||
|
||||
python_configurations = get_python_configurations(build_selector)
|
||||
|
||||
get_pip_url = 'https://bootstrap.pypa.io/get-pip.py'
|
||||
get_pip_script = '/tmp/get-pip.py'
|
||||
|
||||
# get latest pip once and for all
|
||||
download(get_pip_url, get_pip_script)
|
||||
python_configurations = get_python_configurations(options.build_selector)
|
||||
|
||||
for config in python_configurations:
|
||||
if config.identifier.startswith('cp'):
|
||||
installation_bin_path = install_cpython(config.version, config.url)
|
||||
elif config.identifier.startswith('pp'):
|
||||
installation_bin_path = install_pypy(config.version, config.url)
|
||||
else:
|
||||
raise ValueError("Unknown Python implementation")
|
||||
dependency_constraint_flags = []
|
||||
if options.dependency_constraints:
|
||||
dependency_constraint_flags = [
|
||||
'-c', options.dependency_constraints.get_for_python_version(config.version)
|
||||
]
|
||||
|
||||
env = os.environ.copy()
|
||||
env['PATH'] = os.pathsep.join([
|
||||
SYMLINKS_DIR,
|
||||
installation_bin_path,
|
||||
env['PATH'],
|
||||
])
|
||||
|
||||
# Fix issue with site.py setting the wrong `sys.prefix`, `sys.exec_prefix`,
|
||||
# `sys.path`, ... for PyPy: https://foss.heptapod.net/pypy/pypy/issues/3175
|
||||
# Also fix an issue with the shebang of installed scripts inside the
|
||||
# testing virtualenv- see https://github.com/theacodes/nox/issues/44 and
|
||||
# https://github.com/pypa/virtualenv/issues/620
|
||||
# Also see https://github.com/python/cpython/pull/9516
|
||||
env.pop('__PYVENV_LAUNCHER__', None)
|
||||
env = environment.as_dictionary(prev_environment=env)
|
||||
|
||||
# check what version we're on
|
||||
call(['which', 'python'], env=env)
|
||||
call(['python', '--version'], env=env)
|
||||
which_python = subprocess.check_output(['which', 'python'], env=env, universal_newlines=True).strip()
|
||||
if which_python != '/tmp/cibw_bin/python':
|
||||
print("cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it.", file=sys.stderr)
|
||||
exit(1)
|
||||
|
||||
# install pip & wheel
|
||||
call(['python', get_pip_script], env=env, cwd="/tmp")
|
||||
assert os.path.exists(os.path.join(installation_bin_path, 'pip'))
|
||||
call(['which', 'pip'], env=env)
|
||||
call(['pip', '--version'], env=env)
|
||||
which_pip = subprocess.check_output(['which', 'pip'], env=env, universal_newlines=True).strip()
|
||||
if which_pip != '/tmp/cibw_bin/pip':
|
||||
print("cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it.", file=sys.stderr)
|
||||
exit(1)
|
||||
call(['pip', 'install', '--upgrade', 'setuptools', 'wheel', 'delocate'], env=env)
|
||||
|
||||
# setup target platform, only required for python 3.5
|
||||
if config.version == '3.5':
|
||||
if '_PYTHON_HOST_PLATFORM' not in env:
|
||||
# cross-compilation platform override
|
||||
env['_PYTHON_HOST_PLATFORM'] = 'macosx-10.9-x86_64'
|
||||
if 'ARCHFLAGS' not in env:
|
||||
# https://github.com/python/cpython/blob/a5ed2fe0eedefa1649aa93ee74a0bafc8e628a10/Lib/_osx_support.py#L260
|
||||
env['ARCHFLAGS'] = '-arch x86_64'
|
||||
if 'MACOSX_DEPLOYMENT_TARGET' not in env:
|
||||
env['MACOSX_DEPLOYMENT_TARGET'] = '10.9'
|
||||
env = setup_python(config, dependency_constraint_flags, options.environment)
|
||||
|
||||
# run the before_build command
|
||||
if before_build:
|
||||
before_build_prepared = prepare_command(before_build, project=abs_project_dir, package=abs_package_dir)
|
||||
if options.before_build:
|
||||
before_build_prepared = prepare_command(options.before_build, project=abs_project_dir, package=abs_package_dir)
|
||||
call(before_build_prepared, env=env, shell=True)
|
||||
|
||||
# build the wheel
|
||||
if os.path.exists(built_wheel_dir):
|
||||
shutil.rmtree(built_wheel_dir)
|
||||
os.makedirs(built_wheel_dir)
|
||||
call(['pip', 'wheel', abs_package_dir, '-w', built_wheel_dir, '--no-deps'] + get_build_verbosity_extra_flags(build_verbosity), env=env)
|
||||
call(['pip', 'wheel', abs_package_dir, '-w', built_wheel_dir, '--no-deps'] + get_build_verbosity_extra_flags(options.build_verbosity), env=env)
|
||||
built_wheel = glob(os.path.join(built_wheel_dir, '*.whl'))[0]
|
||||
|
||||
# repair the wheel
|
||||
if os.path.exists(repaired_wheel_dir):
|
||||
shutil.rmtree(repaired_wheel_dir)
|
||||
os.makedirs(repaired_wheel_dir)
|
||||
if built_wheel.endswith('none-any.whl') or not repair_command:
|
||||
if built_wheel.endswith('none-any.whl') or not options.repair_command:
|
||||
# pure Python wheel or empty repair command
|
||||
shutil.move(built_wheel, repaired_wheel_dir)
|
||||
else:
|
||||
repair_command_prepared = prepare_command(repair_command, wheel=built_wheel, dest_dir=repaired_wheel_dir)
|
||||
repair_command_prepared = prepare_command(options.repair_command, wheel=built_wheel, dest_dir=repaired_wheel_dir)
|
||||
call(repair_command_prepared, env=env, shell=True)
|
||||
repaired_wheel = glob(os.path.join(repaired_wheel_dir, '*.whl'))[0]
|
||||
|
||||
if test_command:
|
||||
if options.test_command:
|
||||
# set up a virtual environment to install and test from, to make sure
|
||||
# there are no dependencies that were pulled in at build time.
|
||||
call(['pip', 'install', 'virtualenv'], env=env)
|
||||
call(['pip', 'install', 'virtualenv'] + dependency_constraint_flags, env=env)
|
||||
venv_dir = tempfile.mkdtemp()
|
||||
call(['python', '-m', 'virtualenv', venv_dir], env=env)
|
||||
|
||||
# Use --no-download to ensure determinism by using seed libraries
|
||||
# built into virtualenv
|
||||
call(['python', '-m', 'virtualenv', '--no-download', venv_dir], env=env)
|
||||
|
||||
virtualenv_env = env.copy()
|
||||
virtualenv_env['PATH'] = os.pathsep.join([
|
||||
@@ -215,26 +229,26 @@ def build(project_dir, package_dir, output_dir, test_command, before_test, test_
|
||||
# check that we are using the Python from the virtual environment
|
||||
call(['which', 'python'], env=virtualenv_env)
|
||||
|
||||
if before_test:
|
||||
before_test_prepared = prepare_command(before_test, project=abs_project_dir, package=abs_package_dir)
|
||||
if options.before_test:
|
||||
before_test_prepared = prepare_command(options.before_test, project=abs_project_dir, package=abs_package_dir)
|
||||
call(before_test_prepared, env=virtualenv_env, shell=True)
|
||||
|
||||
# install the wheel
|
||||
call(['pip', 'install', repaired_wheel + test_extras], env=virtualenv_env)
|
||||
call(['pip', 'install', repaired_wheel + options.test_extras], env=virtualenv_env)
|
||||
|
||||
# test the wheel
|
||||
if test_requires:
|
||||
call(['pip', 'install'] + test_requires, env=virtualenv_env)
|
||||
if options.test_requires:
|
||||
call(['pip', 'install'] + options.test_requires, env=virtualenv_env)
|
||||
|
||||
# run the tests from $HOME, with an absolute path in the command
|
||||
# (this ensures that Python runs the tests against the installed wheel
|
||||
# and not the repo code)
|
||||
test_command_prepared = prepare_command(test_command, project=abs_project_dir, package=abs_package_dir)
|
||||
test_command_prepared = prepare_command(options.test_command, project=abs_project_dir, package=abs_package_dir)
|
||||
call(test_command_prepared, cwd=os.environ['HOME'], env=virtualenv_env, shell=True)
|
||||
|
||||
# clean up
|
||||
shutil.rmtree(venv_dir)
|
||||
|
||||
# we're all done here; move it to output (overwrite existing)
|
||||
dst = os.path.join(output_dir, os.path.basename(repaired_wheel))
|
||||
dst = os.path.join(options.output_dir, os.path.basename(repaired_wheel))
|
||||
shutil.move(repaired_wheel, dst)
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
#
|
||||
# This file is autogenerated by pip-compile
|
||||
# To update, run:
|
||||
#
|
||||
# bin/update_constraints
|
||||
#
|
||||
appdirs==1.4.3 # via virtualenv
|
||||
configparser==4.0.2 # via importlib-metadata
|
||||
contextlib2==0.6.0.post1 # via importlib-metadata, importlib-resources, virtualenv
|
||||
delocate==0.8.0 # via -r cibuildwheel/resources/constraints.in
|
||||
distlib==0.3.0 # via virtualenv
|
||||
filelock==3.0.12 # via virtualenv
|
||||
importlib-metadata==1.5.0 # via importlib-resources, virtualenv
|
||||
importlib-resources==1.3.1 # via virtualenv
|
||||
pathlib2==2.3.5 # via importlib-metadata, importlib-resources, virtualenv
|
||||
scandir==1.10.0 # via pathlib2
|
||||
singledispatch==3.4.0.3 # via importlib-resources
|
||||
six==1.14.0 # via pathlib2, virtualenv
|
||||
typing==3.7.4.1 # via importlib-resources
|
||||
virtualenv==20.0.10 # via -r cibuildwheel/resources/constraints.in
|
||||
wheel==0.34.2 # via -r cibuildwheel/resources/constraints.in, delocate
|
||||
zipp==1.2.0 # via importlib-metadata, importlib-resources
|
||||
|
||||
# The following packages are considered to be unsafe in a requirements file:
|
||||
pip==20.0.2 # via -r cibuildwheel/resources/constraints.in
|
||||
setuptools==44.0.0 # via -r cibuildwheel/resources/constraints.in
|
||||
@@ -0,0 +1,20 @@
|
||||
#
|
||||
# This file is autogenerated by pip-compile
|
||||
# To update, run:
|
||||
#
|
||||
# bin/update_constraints
|
||||
#
|
||||
appdirs==1.4.3 # via virtualenv
|
||||
delocate==0.8.0 # via -r cibuildwheel/resources/constraints.in
|
||||
distlib==0.3.0 # via virtualenv
|
||||
filelock==3.0.12 # via virtualenv
|
||||
importlib-metadata==1.5.0 # via importlib-resources, virtualenv
|
||||
importlib-resources==1.3.1 # via virtualenv
|
||||
six==1.14.0 # via virtualenv
|
||||
virtualenv==20.0.10 # via -r cibuildwheel/resources/constraints.in
|
||||
wheel==0.34.2 # via -r cibuildwheel/resources/constraints.in, delocate
|
||||
zipp==1.2.0 # via importlib-metadata, importlib-resources
|
||||
|
||||
# The following packages are considered to be unsafe in a requirements file:
|
||||
pip==20.0.2 # via -r cibuildwheel/resources/constraints.in
|
||||
setuptools==46.0.0 # via -r cibuildwheel/resources/constraints.in
|
||||
@@ -0,0 +1,20 @@
|
||||
#
|
||||
# This file is autogenerated by pip-compile
|
||||
# To update, run:
|
||||
#
|
||||
# bin/update_constraints
|
||||
#
|
||||
appdirs==1.4.3 # via virtualenv
|
||||
delocate==0.8.0 # via -r cibuildwheel/resources/constraints.in
|
||||
distlib==0.3.0 # via virtualenv
|
||||
filelock==3.0.12 # via virtualenv
|
||||
importlib-metadata==1.5.0 # via importlib-resources, virtualenv
|
||||
importlib-resources==1.3.1 # via virtualenv
|
||||
six==1.14.0 # via virtualenv
|
||||
virtualenv==20.0.10 # via -r cibuildwheel/resources/constraints.in
|
||||
wheel==0.34.2 # via -r cibuildwheel/resources/constraints.in, delocate
|
||||
zipp==3.1.0 # via importlib-metadata, importlib-resources
|
||||
|
||||
# The following packages are considered to be unsafe in a requirements file:
|
||||
pip==20.0.2 # via -r cibuildwheel/resources/constraints.in
|
||||
setuptools==46.0.0 # via -r cibuildwheel/resources/constraints.in
|
||||
@@ -0,0 +1,5 @@
|
||||
pip
|
||||
setuptools
|
||||
wheel
|
||||
delocate
|
||||
virtualenv
|
||||
@@ -0,0 +1,19 @@
|
||||
#
|
||||
# This file is autogenerated by pip-compile
|
||||
# To update, run:
|
||||
#
|
||||
# bin/update_constraints
|
||||
#
|
||||
appdirs==1.4.3 # via virtualenv
|
||||
delocate==0.8.0 # via -r cibuildwheel/resources/constraints.in
|
||||
distlib==0.3.0 # via virtualenv
|
||||
filelock==3.0.12 # via virtualenv
|
||||
importlib-metadata==1.5.0 # via virtualenv
|
||||
six==1.14.0 # via virtualenv
|
||||
virtualenv==20.0.10 # via -r cibuildwheel/resources/constraints.in
|
||||
wheel==0.34.2 # via -r cibuildwheel/resources/constraints.in, delocate
|
||||
zipp==3.1.0 # via importlib-metadata
|
||||
|
||||
# The following packages are considered to be unsafe in a requirements file:
|
||||
pip==20.0.2 # via -r cibuildwheel/resources/constraints.in
|
||||
setuptools==46.0.0 # via -r cibuildwheel/resources/constraints.in
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,22 @@
|
||||
[x86_64]
|
||||
manylinux1 = quay.io/pypa/manylinux1_x86_64:2020-03-07-9c5ba95
|
||||
manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2020-03-07-1825e8f
|
||||
manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2020-03-07-06139da
|
||||
|
||||
[i686]
|
||||
manylinux1 = quay.io/pypa/manylinux1_i686:2020-03-07-9c5ba95
|
||||
manylinux2010 = quay.io/pypa/manylinux2010_i686:2020-03-07-1825e8f
|
||||
manylinux2014 = quay.io/pypa/manylinux2014_i686:2020-03-07-06139da
|
||||
|
||||
[pypy_x86_64]
|
||||
manylinux2010 = pypywheels/manylinux2010-pypy_x86_64:latest
|
||||
|
||||
[aarch64]
|
||||
manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2020-03-07-06139da
|
||||
|
||||
[ppc64le]
|
||||
manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2020-03-07-06139da
|
||||
|
||||
[s390x]
|
||||
manylinux2014 = quay.io/pypa/manylinux2014_s390x:2020-03-07-06139da
|
||||
|
||||
@@ -2,6 +2,9 @@ import os
|
||||
import urllib.request
|
||||
from fnmatch import fnmatch
|
||||
from time import sleep
|
||||
from typing import NamedTuple, List, Optional, Dict
|
||||
|
||||
from .environment import ParsedEnvironment
|
||||
|
||||
|
||||
def prepare_command(command, **kwargs):
|
||||
@@ -76,3 +79,54 @@ def download(url, dest):
|
||||
file.write(response.read())
|
||||
finally:
|
||||
response.close()
|
||||
|
||||
|
||||
class DependencyConstraints:
|
||||
def __init__(self, base_file_path):
|
||||
assert os.path.exists(base_file_path)
|
||||
self.base_file_path = os.path.abspath(base_file_path)
|
||||
|
||||
@classmethod
|
||||
def with_defaults(cls):
|
||||
return cls(
|
||||
base_file_path=os.path.join(os.path.dirname(__file__), 'resources', 'constraints.txt')
|
||||
)
|
||||
|
||||
def get_for_python_version(self, version):
|
||||
version_parts = version.split('.')
|
||||
|
||||
# try to find a version-specific dependency file e.g. if
|
||||
# ./constraints.txt is the base, look for ./constraints-python27.txt
|
||||
base, ext = os.path.splitext(self.base_file_path)
|
||||
specific = base + '-python{}{}'.format(version_parts[0], version_parts[1])
|
||||
specific_file_path = specific + ext
|
||||
if os.path.exists(specific_file_path):
|
||||
return specific_file_path
|
||||
else:
|
||||
return self.base_file_path
|
||||
|
||||
|
||||
BuildOptions = NamedTuple("BuildOptions", [
|
||||
("project_dir", str),
|
||||
("package_dir", str),
|
||||
("output_dir", str),
|
||||
("test_command", Optional[str]),
|
||||
("test_requires", List[str]),
|
||||
("test_extras", str),
|
||||
("before_build", Optional[str]),
|
||||
("build_verbosity", int),
|
||||
("build_selector", BuildSelector),
|
||||
("repair_command", str),
|
||||
("environment", ParsedEnvironment),
|
||||
("before_test", str),
|
||||
("dependency_constraints", Optional[DependencyConstraints]),
|
||||
("manylinux_images", Optional[Dict[str, str]]),
|
||||
])
|
||||
|
||||
"""
|
||||
Replace this definition with a class-style NamedTuple in the
|
||||
PEP526 style when Python 3.5 host support is dropped
|
||||
"""
|
||||
|
||||
resources_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), 'resources'))
|
||||
get_pip_script = os.path.join(resources_dir, 'get-pip.py')
|
||||
|
||||
+87
-67
@@ -11,6 +11,8 @@ from .util import (
|
||||
download,
|
||||
get_build_verbosity_extra_flags,
|
||||
prepare_command,
|
||||
get_pip_script,
|
||||
BuildOptions
|
||||
)
|
||||
|
||||
|
||||
@@ -33,6 +35,7 @@ def get_nuget_args(version, arch):
|
||||
def get_python_configurations(build_selector):
|
||||
PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'arch', 'identifier', 'url'])
|
||||
python_configurations = [
|
||||
# CPython
|
||||
PythonConfiguration(version='2.7.17', arch='32', identifier='cp27-win32', url=None),
|
||||
PythonConfiguration(version='2.7.17', arch='64', identifier='cp27-win_amd64', url=None),
|
||||
PythonConfiguration(version='3.5.4', arch='32', identifier='cp35-win32', url=None),
|
||||
@@ -43,8 +46,9 @@ def get_python_configurations(build_selector):
|
||||
PythonConfiguration(version='3.7.6', arch='64', identifier='cp37-win_amd64', url=None),
|
||||
PythonConfiguration(version='3.8.2', arch='32', identifier='cp38-win32', url=None),
|
||||
PythonConfiguration(version='3.8.2', arch='64', identifier='cp38-win_amd64', url=None),
|
||||
PythonConfiguration(version='2.7-v7.3.0', arch='32', identifier='pp27-win32', url='https://bitbucket.org/pypy/pypy/downloads/pypy2.7-v7.3.0-win32.zip'),
|
||||
PythonConfiguration(version='3.6-v7.3.0', arch='32', identifier='pp36-win32', url='https://bitbucket.org/pypy/pypy/downloads/pypy3.6-v7.3.0-win32.zip'),
|
||||
# PyPy
|
||||
PythonConfiguration(version='2.7', arch='32', identifier='pp27-win32', url='https://bitbucket.org/pypy/pypy/downloads/pypy2.7-v7.3.0-win32.zip'),
|
||||
PythonConfiguration(version='3.6', arch='32', identifier='pp36-win32', url='https://bitbucket.org/pypy/pypy/downloads/pypy3.6-v7.3.0-win32.zip'),
|
||||
]
|
||||
|
||||
if IS_RUNNING_ON_TRAVIS:
|
||||
@@ -86,9 +90,61 @@ def install_pypy(version, arch, url):
|
||||
return installation_path
|
||||
|
||||
|
||||
def build(project_dir, package_dir, output_dir, test_command, before_test, test_requires, test_extras, before_build, build_verbosity, build_selector, repair_command, environment):
|
||||
abs_project_dir = os.path.abspath(project_dir)
|
||||
abs_package_dir = os.path.abspath(package_dir)
|
||||
def setup_python(python_configuration, dependency_constraint_flags, environment):
|
||||
nuget = 'C:\\cibw\\nuget.exe'
|
||||
if not os.path.exists(nuget):
|
||||
download('https://dist.nuget.org/win-x86-commandline/latest/nuget.exe', nuget)
|
||||
|
||||
if python_configuration.identifier.startswith('cp'):
|
||||
installation_path = install_cpython(python_configuration.version, python_configuration.arch, nuget)
|
||||
elif python_configuration.identifier.startswith('pp'):
|
||||
installation_path = install_pypy(python_configuration.version, python_configuration.arch, python_configuration.url)
|
||||
else:
|
||||
raise ValueError("Unknown Python implementation")
|
||||
|
||||
assert os.path.exists(os.path.join(installation_path, 'python.exe'))
|
||||
|
||||
# set up PATH and environment variables for run_with_env
|
||||
env = os.environ.copy()
|
||||
env['PYTHON_VERSION'] = python_configuration.version
|
||||
env['PYTHON_ARCH'] = python_configuration.arch
|
||||
env['PATH'] = os.pathsep.join([
|
||||
installation_path,
|
||||
os.path.join(installation_path, 'Scripts'),
|
||||
env['PATH']
|
||||
])
|
||||
# update env with results from CIBW_ENVIRONMENT
|
||||
env = environment.as_dictionary(prev_environment=env)
|
||||
|
||||
# for the logs - check we're running the right version of python
|
||||
shell(['where', 'python'], env=env)
|
||||
shell(['python', '--version'], env=env)
|
||||
shell(['python', '-c', '"import struct; print(struct.calcsize(\'P\') * 8)"'], env=env)
|
||||
where_python = subprocess.check_output(['where', 'python'], env=env, universal_newlines=True).splitlines()[0].strip()
|
||||
if where_python != os.path.join(installation_path, 'python.exe'):
|
||||
print("cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it.", file=sys.stderr)
|
||||
exit(1)
|
||||
|
||||
# make sure pip is installed
|
||||
if not os.path.exists(os.path.join(installation_path, 'Scripts', 'pip.exe')):
|
||||
shell(['python', get_pip_script] + dependency_constraint_flags, env=env, cwd="C:\\cibw")
|
||||
assert os.path.exists(os.path.join(installation_path, 'Scripts', 'pip.exe'))
|
||||
where_pip = subprocess.check_output(['where', 'pip'], env=env, universal_newlines=True).splitlines()[0].strip()
|
||||
if where_pip.strip() != os.path.join(installation_path, 'Scripts', 'pip.exe'):
|
||||
print("cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it.", file=sys.stderr)
|
||||
exit(1)
|
||||
|
||||
# prepare the Python environment
|
||||
shell(['python', '-m', 'pip', 'install', '--upgrade', 'pip'] + dependency_constraint_flags, env=env)
|
||||
shell(['pip', '--version'], env=env)
|
||||
shell(['pip', 'install', '--upgrade', 'setuptools', 'wheel'] + dependency_constraint_flags, env=env)
|
||||
|
||||
return env
|
||||
|
||||
|
||||
def build(options: BuildOptions):
|
||||
abs_project_dir = os.path.abspath(options.project_dir)
|
||||
abs_package_dir = os.path.abspath(options.package_dir)
|
||||
temp_dir = tempfile.mkdtemp(prefix='cibuildwheel')
|
||||
built_wheel_dir = os.path.join(temp_dir, 'built_wheel')
|
||||
repaired_wheel_dir = os.path.join(temp_dir, 'repaired_wheel')
|
||||
@@ -96,87 +152,51 @@ def build(project_dir, package_dir, output_dir, test_command, before_test, test_
|
||||
# install nuget as best way to provide python
|
||||
nuget = 'C:\\cibw\\nuget.exe'
|
||||
download('https://dist.nuget.org/win-x86-commandline/latest/nuget.exe', nuget)
|
||||
# get pip fo this installation which not have.
|
||||
get_pip_script = 'C:\\cibw\\get-pip.py'
|
||||
download('https://bootstrap.pypa.io/get-pip.py', get_pip_script)
|
||||
|
||||
python_configurations = get_python_configurations(build_selector)
|
||||
python_configurations = get_python_configurations(options.build_selector)
|
||||
for config in python_configurations:
|
||||
dependency_constraint_flags = []
|
||||
if options.dependency_constraints:
|
||||
dependency_constraint_flags = [
|
||||
'-c', options.dependency_constraints.get_for_python_version(config.version)
|
||||
]
|
||||
|
||||
# install Python
|
||||
if config.identifier.startswith('cp'):
|
||||
installation_path = install_cpython(config.version, config.arch, nuget)
|
||||
elif config.identifier.startswith('pp'):
|
||||
installation_path = install_pypy(config.version, config.arch, config.url)
|
||||
else:
|
||||
raise ValueError("Unknown Python implementation")
|
||||
|
||||
assert os.path.exists(os.path.join(installation_path, 'python.exe'))
|
||||
|
||||
# set up PATH and environment variables for run_with_env
|
||||
env = os.environ.copy()
|
||||
env['PYTHON_VERSION'] = config.version
|
||||
env['PYTHON_ARCH'] = config.arch
|
||||
env['PATH'] = os.pathsep.join([
|
||||
installation_path,
|
||||
os.path.join(installation_path, 'Scripts'),
|
||||
env['PATH']
|
||||
])
|
||||
# update env with results from CIBW_ENVIRONMENT
|
||||
env = environment.as_dictionary(prev_environment=env)
|
||||
|
||||
# for the logs - check we're running the right version of python
|
||||
shell(['where', 'python'], env=env)
|
||||
shell(['python', '--version'], env=env)
|
||||
shell(['python', '-c', '"import struct; print(struct.calcsize(\'P\') * 8)"'], env=env)
|
||||
where_python = subprocess.check_output(['where', 'python'], env=env, universal_newlines=True).splitlines()[0].strip()
|
||||
if where_python != os.path.join(installation_path, 'python.exe'):
|
||||
print("cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it.", file=sys.stderr)
|
||||
exit(1)
|
||||
|
||||
# make sure pip is installed
|
||||
if not os.path.exists(os.path.join(installation_path, 'Scripts', 'pip.exe')):
|
||||
shell(['python', get_pip_script], env=env, cwd="C:\\cibw")
|
||||
assert os.path.exists(os.path.join(installation_path, 'Scripts', 'pip.exe'))
|
||||
where_pip = subprocess.check_output(['where', 'pip'], env=env, universal_newlines=True).splitlines()[0].strip()
|
||||
if where_pip.strip() != os.path.join(installation_path, 'Scripts', 'pip.exe'):
|
||||
print("cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it.", file=sys.stderr)
|
||||
exit(1)
|
||||
|
||||
# prepare the Python environment
|
||||
shell(['python', '-m', 'pip', 'install', '--upgrade', 'pip'], env=env)
|
||||
shell(['pip', '--version'], env=env)
|
||||
shell(['pip', 'install', '--upgrade', 'setuptools', 'wheel'], env=env)
|
||||
env = setup_python(config, dependency_constraint_flags, options.environment)
|
||||
|
||||
# run the before_build command
|
||||
if before_build:
|
||||
before_build_prepared = prepare_command(before_build, project=abs_project_dir, package=abs_package_dir)
|
||||
if options.before_build:
|
||||
before_build_prepared = prepare_command(options.before_build, project=abs_project_dir, package=abs_package_dir)
|
||||
shell([before_build_prepared], env=env)
|
||||
|
||||
# build the wheel
|
||||
if os.path.exists(built_wheel_dir):
|
||||
shutil.rmtree(built_wheel_dir)
|
||||
os.makedirs(built_wheel_dir)
|
||||
shell(['pip', 'wheel', abs_package_dir, '-w', built_wheel_dir, '--no-deps'] + get_build_verbosity_extra_flags(build_verbosity), env=env)
|
||||
shell(['pip', 'wheel', abs_package_dir, '-w', built_wheel_dir, '--no-deps'] + get_build_verbosity_extra_flags(options.build_verbosity), env=env)
|
||||
built_wheel = glob(os.path.join(built_wheel_dir, '*.whl'))[0]
|
||||
|
||||
# repair the wheel
|
||||
if os.path.exists(repaired_wheel_dir):
|
||||
shutil.rmtree(repaired_wheel_dir)
|
||||
os.makedirs(repaired_wheel_dir)
|
||||
if built_wheel.endswith('none-any.whl') or not repair_command:
|
||||
if built_wheel.endswith('none-any.whl') or not options.repair_command:
|
||||
# pure Python wheel or empty repair command
|
||||
shutil.move(built_wheel, repaired_wheel_dir)
|
||||
else:
|
||||
repair_command_prepared = prepare_command(repair_command, wheel=built_wheel, dest_dir=repaired_wheel_dir)
|
||||
repair_command_prepared = prepare_command(options.repair_command, wheel=built_wheel, dest_dir=repaired_wheel_dir)
|
||||
shell([repair_command_prepared], env=env)
|
||||
repaired_wheel = glob(os.path.join(repaired_wheel_dir, '*.whl'))[0]
|
||||
|
||||
if test_command:
|
||||
if options.test_command:
|
||||
# set up a virtual environment to install and test from, to make sure
|
||||
# there are no dependencies that were pulled in at build time.
|
||||
shell(['pip', 'install', 'virtualenv'], env=env)
|
||||
shell(['pip', 'install', 'virtualenv'] + dependency_constraint_flags, env=env)
|
||||
venv_dir = tempfile.mkdtemp()
|
||||
shell(['python', '-m', 'virtualenv', venv_dir], env=env)
|
||||
|
||||
# Use --no-download to ensure determinism by using seed libraries
|
||||
# built into virtualenv
|
||||
shell(['python', '-m', 'virtualenv', '--no-download', venv_dir], env=env)
|
||||
|
||||
virtualenv_env = env.copy()
|
||||
|
||||
@@ -193,28 +213,28 @@ def build(project_dir, package_dir, output_dir, test_command, before_test, test_
|
||||
# check that we are using the Python from the virtual environment
|
||||
shell(['which', 'python'], env=virtualenv_env)
|
||||
|
||||
if before_test:
|
||||
before_test_prepared = prepare_command(before_test, project=abs_project_dir, package=abs_package_dir)
|
||||
if options.before_test:
|
||||
before_test_prepared = prepare_command(options.before_test, project=abs_project_dir, package=abs_package_dir)
|
||||
shell([before_test_prepared], env=virtualenv_env)
|
||||
|
||||
# install the wheel
|
||||
shell(['pip', 'install', repaired_wheel + test_extras], env=virtualenv_env)
|
||||
shell(['pip', 'install', repaired_wheel + options.test_extras], env=virtualenv_env)
|
||||
|
||||
# test the wheel
|
||||
if test_requires:
|
||||
shell(['pip', 'install'] + test_requires, env=virtualenv_env)
|
||||
if options.test_requires:
|
||||
shell(['pip', 'install'] + options.test_requires, env=virtualenv_env)
|
||||
|
||||
# run the tests from c:\, with an absolute path in the command
|
||||
# (this ensures that Python runs the tests against the installed wheel
|
||||
# and not the repo code)
|
||||
test_command_prepared = prepare_command(test_command, project=abs_project_dir, package=abs_package_dir)
|
||||
test_command_prepared = prepare_command(options.test_command, project=abs_project_dir, package=abs_package_dir)
|
||||
shell([test_command_prepared], cwd='c:\\', env=virtualenv_env)
|
||||
|
||||
# clean up
|
||||
shutil.rmtree(venv_dir)
|
||||
|
||||
# we're all done here; move it to output (remove if already exists)
|
||||
dst = os.path.join(output_dir, os.path.basename(repaired_wheel))
|
||||
dst = os.path.join(options.output_dir, os.path.basename(repaired_wheel))
|
||||
if os.path.isfile(dst):
|
||||
os.remove(dst)
|
||||
shutil.move(repaired_wheel, dst)
|
||||
|
||||
@@ -7,7 +7,7 @@ Building Python wheels with modern C++ standards (C++11 and later) requires a fe
|
||||
|
||||
## Python 2.7 and C++17
|
||||
|
||||
The Python 2.7 header files use the `register` keyword, which is [reserved and unused from C+17 onwards](https://en.cppreference.com/w/cpp/keyword/register). Compiling a wheel for Python 2.7 with the C++17 standard is still possible to allow usage of `register` using proper flag `-Wno-register` for gcc/clang and `/wd5033` for MSVC.
|
||||
The Python 2.7 header files use the `register` keyword, which is [reserved and unused from C+17 onwards](https://en.cppreference.com/w/cpp/keyword/register). Compiling a wheel for Python 2.7 with the C++17 standard is still possible to allow usage of `register` using proper flag `-Wno-register` for gcc/clang and `/wd5033` for MSVC.
|
||||
|
||||
## manylinux1 and C++14
|
||||
The default `manylinux1` image (based on CentOS 5) contains a version of GCC and libstdc++ that only supports C++11 and earlier standards. There are however ways to compile wheels with the C++14 standard (and later): https://github.com/pypa/manylinux/issues/118
|
||||
@@ -16,7 +16,7 @@ The default `manylinux1` image (based on CentOS 5) contains a version of GCC and
|
||||
|
||||
## macOS and deployment target versions
|
||||
|
||||
OS X/macOS allows you to specify a so-called "deployment target" version that will ensure backwards compatibility with older versions of macOS. One way to do this is by setting the `MACOSX_DEPLOYMENT_TARGET` environment variable. If not set, Python will set this variable to the version the Python distribution itself was compiled on (10.6 or 10.9, for the python.org packages), when creating the wheel.
|
||||
OS X/macOS allows you to specify a so-called "deployment target" version that will ensure backwards compatibility with older versions of macOS. One way to do this is by setting the `MACOSX_DEPLOYMENT_TARGET` environment variable.
|
||||
|
||||
However, to enable modern C++ standards, the deploment target needs to be set high enough (since older OS X/macOS versions did not have the necessary modern C++ standard library).
|
||||
|
||||
|
||||
+72
-10
@@ -14,16 +14,16 @@ your CI config file:
|
||||
```yaml
|
||||
env:
|
||||
global:
|
||||
- CIBW_TEST_REQUIRES=nose
|
||||
- CIBW_TEST_COMMAND="nosetests {project}/tests"
|
||||
- CIBW_TEST_REQUIRES=pytest
|
||||
- CIBW_TEST_COMMAND="pytest {project}/tests"
|
||||
```
|
||||
|
||||
> appveyor.yml ([docs](https://www.appveyor.com/docs/build-configuration/#environment-variables))
|
||||
```yaml
|
||||
environment:
|
||||
global:
|
||||
CIBW_TEST_REQUIRES: nose
|
||||
CIBW_TEST_COMMAND: "nosetests {project}\\tests"
|
||||
CIBW_TEST_REQUIRES: pytest
|
||||
CIBW_TEST_COMMAND: "pytest {project}\\tests"
|
||||
```
|
||||
|
||||
> .circleci/config.yml ([docs](https://circleci.com/docs/2.0/configuration-reference/#environment))
|
||||
@@ -31,15 +31,22 @@ environment:
|
||||
jobs:
|
||||
job_name:
|
||||
environment:
|
||||
CIBW_TEST_REQUIRES: nose
|
||||
CIBW_TEST_COMMAND: "nosetests {project}/tests"
|
||||
CIBW_TEST_REQUIRES: pytest
|
||||
CIBW_TEST_COMMAND: "pytest {project}/tests"
|
||||
```
|
||||
|
||||
> azure-pipelines.yml ([docs](https://docs.microsoft.com/en-us/azure/devops/pipelines/process/variables))
|
||||
```yaml
|
||||
variables:
|
||||
CIBW_TEST_REQUIRES: nose
|
||||
CIBW_TEST_COMMAND: "nosetests {project}/tests"
|
||||
CIBW_TEST_REQUIRES: pytest
|
||||
CIBW_TEST_COMMAND: "pytest {project}/tests"
|
||||
```
|
||||
|
||||
> .github/workflows/*.yml ([docs](https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables)) (can be global, in job, or in step)
|
||||
```yaml
|
||||
env:
|
||||
CIBW_TEST_REQUIRES: pytest
|
||||
CIBW_TEST_COMMAND: "pytest {project}/tests"
|
||||
```
|
||||
|
||||
|
||||
@@ -253,7 +260,7 @@ CIBW_REPAIR_WHEEL_COMMAND_LINUX: "auditwheel repair --lib-sdir . -w {dest_dir} {
|
||||
|
||||
An alternative Docker image to be used for building [`manylinux`](https://github.com/pypa/manylinux) wheels. `cibuildwheel` will then pull these instead of the default images, [`quay.io/pypa/manylinux2010_x86_64`](https://quay.io/pypa/manylinux2010_x86_64), [`quay.io/pypa/manylinux2010_i686`](https://quay.io/pypa/manylinux2010_i686), [`pypywheels/manylinux2010-pypy_x86_64`](https://hub.docker.com/r/pypywheels/manylinux2010-pypy_x86_64), [`quay.io/pypa/manylinux2014_aarch64`](https://quay.io/pypa/manylinux2014_aarch64), [`quay.io/pypa/manylinux2014_ppc64le`](https://quay.io/pypa/manylinux2014_ppc64le), and [`quay.io/pypa/manylinux2014_s390x`](https://quay.io/pypa/manylinux2010_s390x).
|
||||
|
||||
The value of this option can either be set to `manylinux1`, `manylinux2010` or `manylinux2014` to use the [official `manylinux` images](https://github.com/pypa/manylinux) and [PyPy `manylinux` images](https://github.com/pypy/manylinux), or any other valid Docker image name. Note that for PyPy, only the official `manylinux2010` image is currently available. For architectures other
|
||||
The value of this option can either be set to `manylinux1`, `manylinux2010` or `manylinux2014` to use a pinned version of the [official `manylinux` images](https://github.com/pypa/manylinux) and [PyPy `manylinux` images](https://github.com/pypy/manylinux). Alternatively, set these options to any other valid Docker image name. Note that for PyPy, only the official `manylinux2010` image is currently available. For architectures other
|
||||
than x86 (x86\_64 and i686) manylinux2014 must be used because this is the first version of the manylinux specification that supports additional architectures.
|
||||
|
||||
Beware to specify a valid Docker image that can be used in the same way as the official, default Docker images: all necessary Python and pip versions need to be present in `/opt/python/`, and the `auditwheel` tool needs to be present for `cibuildwheel` to work. Apart from that, the architecture and relevant shared system libraries need to be manylinux1-, manylinux2010- or manylinux2014-compatible in order to produce valid `manylinux1`/`manylinux2010`/`manylinux2014` wheels (see [pypa/manylinux on GitHub](https://github.com/pypa/manylinux), [PEP 513](https://www.python.org/dev/peps/pep-0513/), [PEP 571](https://www.python.org/dev/peps/pep-0571/) and [PEP 599](https://www.python.org/dev/peps/pep-0599/) for more details).
|
||||
@@ -276,11 +283,66 @@ CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014
|
||||
CIBW_MANYLINUX_I686_IMAGE: manylinux2014
|
||||
CIBW_SKIP: cp27-manylinux*
|
||||
|
||||
# build using the latest manylinux2010 release, instead of the cibuildwheel
|
||||
# pinned version
|
||||
CIBW_MANYLINUX_X86_64_IMAGE: quay.io/pypa/manylinux2010_x86_64:latest
|
||||
CIBW_MANYLINUX_I686_IMAGE: quay.io/pypa/manylinux2010_i686:latest
|
||||
|
||||
# build using a different image from the docker registry
|
||||
CIBW_MANYLINUX_X86_64_IMAGE: dockcross/manylinux-x64
|
||||
CIBW_MANYLINUX_I686_IMAGE: dockcross/manylinux-x86
|
||||
```
|
||||
|
||||
### `CIBW_DEPENDENCY_VERSIONS` {: #dependency-versions}
|
||||
> Specify how cibuildwheel controls the versions of the tools it uses
|
||||
|
||||
Options: `pinned` `latest` `<your constraints file>`
|
||||
|
||||
Default: `pinned`
|
||||
|
||||
If `CIBW_DEPENDENCY_VERSIONS` is `pinned`, cibuildwheel uses versions of tools
|
||||
like `pip`, `setuptools`, `virtualenv` that were pinned with that release of
|
||||
cibuildwheel. This represents a known-good set of dependencies, and is
|
||||
recommended for build repeatability.
|
||||
|
||||
If set to `latest`, cibuildwheel will use the latest of these packages that
|
||||
are available on PyPI. This might be preferable if these packages have bug
|
||||
fixes that can't wait for a new cibuildwheel release.
|
||||
|
||||
To control the versions of dependencies yourself, you can supply a [pip
|
||||
constraints](https://pip.pypa.io/en/stable/user_guide/#constraints-files) file
|
||||
here and it will be used instead.
|
||||
|
||||
!!! note
|
||||
If you need different dependencies for each python version, provide them
|
||||
in the same folder with a `-pythonXY` suffix. e.g. if your
|
||||
`CIBW_DEPENDENCY_VERSIONS=./constraints.txt`, cibuildwheel will use
|
||||
`./constraints-python27.txt` on Python 2.7, or fallback to
|
||||
`./constraints.txt` if that's not found.
|
||||
|
||||
Platform-specific variants also available:<br/>
|
||||
`CIBW_DEPENDENCY_VERSIONS_MACOS` | `CIBW_DEPENDENCY_VERSIONS_WINDOWS`
|
||||
|
||||
!!! note
|
||||
This option does not affect the tools used on the Linux build - those versions
|
||||
are bundled with the manylinux image that cibuildwheel uses. To change
|
||||
dependency versions on Linux, use the [CIBW_MANYLINUX_*](#manylinux-image)
|
||||
options.
|
||||
|
||||
#### Examples
|
||||
|
||||
```yaml
|
||||
# use tools versions that are bundled with cibuildwheel (this is the default)
|
||||
CIBW_DEPENDENCY_VERSIONS: pinned
|
||||
|
||||
# use the latest versions available on PyPI
|
||||
CIBW_DEPENDENCY_VERSIONS: latest
|
||||
|
||||
# use your own pip constraints file
|
||||
CIBW_DEPENDENCY_VERSIONS: ./constraints.txt
|
||||
```
|
||||
|
||||
|
||||
## Testing
|
||||
|
||||
### `CIBW_TEST_COMMAND` {: #test-command}
|
||||
@@ -300,7 +362,7 @@ Platform-specific variants also available:<br/>
|
||||
CIBW_TEST_COMMAND: nosetests {project}/tests
|
||||
|
||||
# run the project tests using `pytest`
|
||||
CIBW_TEST_COMMAND: nosetests {project}/tests
|
||||
CIBW_TEST_COMMAND: pytest {project}/tests
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -2,3 +2,6 @@
|
||||
-e ./docs/mkdocs_include_markdown_plugin
|
||||
pytest
|
||||
mkdocs==1.0.4
|
||||
pip-tools
|
||||
requests
|
||||
click
|
||||
|
||||
@@ -12,7 +12,7 @@ def test(manylinux_image):
|
||||
|
||||
if utils.platform != 'linux':
|
||||
pytest.skip('the docker test is only relevant to the linux build')
|
||||
elif platform.machine not in ['x86_64', 'i686']:
|
||||
elif platform.machine() not in ['x86_64', 'i686']:
|
||||
if manylinux_image in ['manylinux1', 'manylinux2010']:
|
||||
pytest.skip("manylinux1 and 2010 doesn't exist for non-x86 architectures")
|
||||
|
||||
|
||||
@@ -10,35 +10,30 @@ project_dir = os.path.dirname(__file__)
|
||||
def test_cpp11(tmp_path):
|
||||
# This test checks that the C++11 standard is supported
|
||||
|
||||
add_env = {'CIBW_SKIP': 'cp27-win* pp27-win32', 'CIBW_ENVIRONMENT': 'STANDARD=11'}
|
||||
# VC++ for Python 2.7 does not support modern standards
|
||||
if utils.platform == 'macos':
|
||||
add_env['MACOSX_DEPLOYMENT_TARGET'] = '10.9'
|
||||
add_env = {'CIBW_SKIP': 'cp27-win* pp27-win32', 'CIBW_ENVIRONMENT': 'STANDARD=11'}
|
||||
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
|
||||
expected_wheels = [w for w in utils.expected_wheels(
|
||||
'spam', '0.1.0', macosx_deployment_target='10.9')
|
||||
if 'cp27-cp27m-win' not in w
|
||||
and 'pp27-pypy_73-win32' not in w]
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if 'cp27-cp27m-win' not in w and 'pp27-pypy_73-win32' not in w]
|
||||
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
|
||||
|
||||
def test_cpp14():
|
||||
# This test checks that the C++14 standard is supported
|
||||
|
||||
add_env = {'CIBW_SKIP': 'cp27-win* pp27-win32 cp35-win*', 'CIBW_ENVIRONMENT': 'STANDARD=14'}
|
||||
# VC++ for Python 2.7 does not support modern standards
|
||||
# The manylinux1 docker image does not have a compiler which supports C++11
|
||||
# Python 3.4 and 3.5 are compiled with MSVC 10, which does not support C++14
|
||||
if utils.platform == 'macos':
|
||||
add_env['MACOSX_DEPLOYMENT_TARGET'] = '10.9'
|
||||
add_env = {'CIBW_SKIP': 'cp27-win* pp27-win32 cp35-win*', 'CIBW_ENVIRONMENT': 'STANDARD=14'}
|
||||
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
|
||||
expected_wheels = [w for w in utils.expected_wheels(
|
||||
'spam', '0.1.0', macosx_deployment_target='10.9')
|
||||
if 'cp27-cp27m-win' not in w
|
||||
and 'pp27-pypy_73-win32' not in w
|
||||
and 'cp35-cp35m-win' not in w]
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if 'cp27-cp27m-win' not in w
|
||||
and 'pp27-pypy_73-win32' not in w
|
||||
and 'cp35-cp35m-win' not in w]
|
||||
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
|
||||
|
||||
@@ -52,14 +47,15 @@ def test_cpp17():
|
||||
pytest.skip('Visual Studio 2015 does not support C++17')
|
||||
|
||||
add_env = {'CIBW_SKIP': 'cp27-win* pp27-win32 cp35-win* pp36-win32', 'CIBW_ENVIRONMENT': 'STANDARD=17'}
|
||||
|
||||
if utils.platform == 'macos':
|
||||
add_env['MACOSX_DEPLOYMENT_TARGET'] = '10.13'
|
||||
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
|
||||
expected_wheels = [w for w in utils.expected_wheels(
|
||||
'spam', '0.1.0', macosx_deployment_target='10.13')
|
||||
if 'cp27-cp27m-win' not in w
|
||||
and 'pp27-pypy_73-win32' not in w
|
||||
and 'cp35-cp35m-win' not in w
|
||||
and 'pp36-pypy36_pp73-win32' not in w]
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0', macosx_deployment_target='10.13')
|
||||
if 'cp27-cp27m-win' not in w
|
||||
and 'pp27-pypy_73-win32' not in w
|
||||
and 'cp35-cp35m-win' not in w
|
||||
and 'pp36-pypy36_pp73-win32' not in w]
|
||||
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
import os
|
||||
import re
|
||||
import pytest
|
||||
import textwrap
|
||||
import cibuildwheel.util
|
||||
|
||||
import utils
|
||||
|
||||
VERSION_REGEX = r'([\w-]+)==([^\s]+)'
|
||||
|
||||
|
||||
def get_versions_from_constraint_file(constraint_file):
|
||||
with open(constraint_file, encoding='utf8') as f:
|
||||
constraint_file_text = f.read()
|
||||
|
||||
versions = {}
|
||||
|
||||
for package, version in re.findall(VERSION_REGEX, constraint_file_text):
|
||||
versions[package] = version
|
||||
|
||||
return versions
|
||||
|
||||
|
||||
@pytest.mark.parametrize('python_version', ['2.7', '3.5', '3.8'])
|
||||
def test_pinned_versions(python_version):
|
||||
if utils.platform == 'linux':
|
||||
pytest.skip('linux doesn\'t pin individual tool versions, it pins manylinux images instead')
|
||||
|
||||
project_dir = os.path.dirname(__file__)
|
||||
|
||||
build_environment = {}
|
||||
|
||||
if python_version == '2.7':
|
||||
constraint_filename = 'constraints-python27.txt'
|
||||
build_pattern = '[cp]p27-*'
|
||||
elif python_version == '3.5':
|
||||
constraint_filename = 'constraints-python35.txt'
|
||||
build_pattern = '[cp]p35-*'
|
||||
else:
|
||||
constraint_filename = 'constraints.txt'
|
||||
build_pattern = '[cp]p38-*'
|
||||
|
||||
constraint_file = os.path.join(cibuildwheel.util.resources_dir, constraint_filename)
|
||||
constraint_versions = get_versions_from_constraint_file(constraint_file)
|
||||
|
||||
for package in ['pip', 'setuptools', 'wheel', 'virtualenv']:
|
||||
env_name = 'EXPECTED_{}_VERSION'.format(package.upper())
|
||||
build_environment[env_name] = constraint_versions[package]
|
||||
|
||||
cibw_environment_option = ' '.join(
|
||||
['{}={}'.format(k, v) for k, v in build_environment.items()]
|
||||
)
|
||||
|
||||
# build and test the wheels
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
||||
'CIBW_BUILD': build_pattern,
|
||||
'CIBW_ENVIRONMENT': cibw_environment_option,
|
||||
})
|
||||
|
||||
# also check that we got the right wheels
|
||||
if python_version == '2.7':
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if '-cp27' in w or '-pp27' in w]
|
||||
elif python_version == '3.5':
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if '-cp35' in w or '-pp35' in w]
|
||||
elif python_version == '3.8':
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if '-cp38' in w or '-pp38' in w]
|
||||
else:
|
||||
raise ValueError('unhandled python version')
|
||||
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('python_version', ['2.7', '3.x'])
|
||||
def test_dependency_constraints_file(tmp_path, python_version):
|
||||
if utils.platform == 'linux':
|
||||
pytest.skip('linux doesn\'t pin individual tool versions, it pins manylinux images instead')
|
||||
|
||||
project_dir = os.path.dirname(__file__)
|
||||
|
||||
tool_versions = {
|
||||
'pip': '20.0.2',
|
||||
'setuptools': '44.0.0' if python_version == '2.7' else '46.0.0',
|
||||
'wheel': '0.34.2',
|
||||
'virtualenv': '20.0.10',
|
||||
}
|
||||
|
||||
constraints_file = tmp_path / 'constraints.txt'
|
||||
constraints_file.write_text(textwrap.dedent(
|
||||
'''
|
||||
pip=={pip}
|
||||
setuptools=={setuptools}
|
||||
wheel=={wheel}
|
||||
virtualenv=={virtualenv}
|
||||
'''.format(**tool_versions)
|
||||
))
|
||||
|
||||
build_environment = {}
|
||||
|
||||
for package_name, version in tool_versions.items():
|
||||
env_name = 'EXPECTED_{}_VERSION'.format(package_name.upper())
|
||||
build_environment[env_name] = version
|
||||
|
||||
cibw_environment_option = ' '.join(
|
||||
['{}={}'.format(k, v) for k, v in build_environment.items()]
|
||||
)
|
||||
|
||||
# build and test the wheels
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
||||
'CIBW_BUILD': '[cp]p27-*' if python_version == '2.7' else '[cp]p3?-*',
|
||||
'CIBW_ENVIRONMENT': cibw_environment_option,
|
||||
'CIBW_DEPENDENCY_VERSIONS': str(constraints_file),
|
||||
})
|
||||
|
||||
# also check that we got the right wheels
|
||||
if python_version == '2.7':
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if '-cp27' in w or '-pp27' in w]
|
||||
else:
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if '-cp27' not in w and '-pp27' not in w]
|
||||
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
@@ -0,0 +1,31 @@
|
||||
import os
|
||||
import subprocess
|
||||
from setuptools import (
|
||||
Extension,
|
||||
setup,
|
||||
)
|
||||
|
||||
versions_output_text = subprocess.check_output(
|
||||
['pip', 'freeze', '--all', '-qq'],
|
||||
universal_newlines=True,
|
||||
)
|
||||
versions = versions_output_text.strip().splitlines()
|
||||
|
||||
# `versions` now looks like:
|
||||
# ['pip==x.x.x', 'setuptools==x.x.x', 'wheel==x.x.x']
|
||||
|
||||
print('Gathered versions', versions)
|
||||
|
||||
for package_name in ['pip', 'setuptools', 'wheel']:
|
||||
env_name = 'EXPECTED_{}_VERSION'.format(package_name.upper())
|
||||
expected_version = os.environ[env_name]
|
||||
|
||||
assert '{}=={}'.format(package_name, expected_version) in versions, (
|
||||
'error: {} version should equal {}'.format(package_name, expected_version)
|
||||
)
|
||||
|
||||
setup(
|
||||
name="spam",
|
||||
ext_modules=[Extension('spam', sources=['spam.c'])],
|
||||
version="0.1.0",
|
||||
)
|
||||
@@ -0,0 +1,48 @@
|
||||
#include <Python.h>
|
||||
|
||||
static PyObject *
|
||||
spam_system(PyObject *self, PyObject *args)
|
||||
{
|
||||
const char *command;
|
||||
int sts;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s", &command))
|
||||
return NULL;
|
||||
sts = system(command);
|
||||
return PyLong_FromLong(sts);
|
||||
}
|
||||
|
||||
/* Module initialization */
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
#define MOD_INIT(name) PyMODINIT_FUNC PyInit_##name(void)
|
||||
#define MOD_DEF(m, name, doc, methods, module_state_size) \
|
||||
static struct PyModuleDef moduledef = { \
|
||||
PyModuleDef_HEAD_INIT, name, doc, module_state_size, methods, }; \
|
||||
m = PyModule_Create(&moduledef);
|
||||
#define MOD_RETURN(m) return m;
|
||||
#else
|
||||
#define MOD_INIT(name) PyMODINIT_FUNC init##name(void)
|
||||
#define MOD_DEF(m, name, doc, methods, module_state_size) \
|
||||
m = Py_InitModule3(name, methods, doc);
|
||||
#define MOD_RETURN(m) return;
|
||||
#endif
|
||||
|
||||
static PyMethodDef module_methods[] = {
|
||||
{"system", (PyCFunction)spam_system, METH_VARARGS,
|
||||
"Execute a shell command."},
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
MOD_INIT(spam)
|
||||
{
|
||||
PyObject* m;
|
||||
|
||||
MOD_DEF(m,
|
||||
"spam",
|
||||
"Example module",
|
||||
module_methods,
|
||||
-1)
|
||||
|
||||
MOD_RETURN(m)
|
||||
}
|
||||
+40
-40
@@ -72,7 +72,7 @@ def cibuildwheel_run(project_path, env=None, add_env=None, output_dir=None):
|
||||
|
||||
|
||||
def expected_wheels(package_name, package_version, manylinux_versions=None,
|
||||
macosx_deployment_target=None):
|
||||
macosx_deployment_target='10.9'):
|
||||
'''
|
||||
Returns a list of expected wheels from a run of cibuildwheel.
|
||||
'''
|
||||
@@ -81,60 +81,60 @@ def expected_wheels(package_name, package_version, manylinux_versions=None,
|
||||
# {python tag} and {abi tag} are closely related to the python interpreter used to build the wheel
|
||||
# so we'll merge them below as python_abi_tag
|
||||
|
||||
python_abi_tags = ['cp35-cp35m', 'cp36-cp36m', 'cp37-cp37m', 'cp38-cp38']
|
||||
extra_x86_python_abi_tags = ['cp27-cp27m', 'pp27-pypy_73', 'pp36-pypy36_pp73']
|
||||
|
||||
if platform == 'linux':
|
||||
if pm.machine() not in ['x86_64', 'i686']:
|
||||
if manylinux_versions is None:
|
||||
manylinux_versions = ['manylinux2014']
|
||||
architectures = {'cp': [pm.machine()]}
|
||||
if manylinux_versions is None:
|
||||
if pm.machine() == 'x86_64':
|
||||
manylinux_versions = ['manylinux1', 'manylinux2010']
|
||||
else:
|
||||
if manylinux_versions is None:
|
||||
manylinux_versions = ['manylinux1', 'manylinux2010']
|
||||
python_abi_tags += extra_x86_python_abi_tags
|
||||
manylinux_versions = ['manylinux2014']
|
||||
|
||||
python_abi_tags = ['cp35-cp35m', 'cp36-cp36m', 'cp37-cp37m', 'cp38-cp38']
|
||||
|
||||
if pm.machine() in ['x86_64', 'AMD64', 'x86']:
|
||||
python_abi_tags += ['cp27-cp27m', 'pp27-pypy_73', 'pp36-pypy36_pp73']
|
||||
|
||||
if platform == 'linux':
|
||||
python_abi_tags.append('cp27-cp27mu') # python 2.7 has 2 different ABI on manylinux
|
||||
architectures = {'cp': ['x86_64', 'i686'], 'pp': ['x86_64']}
|
||||
platform_tags = {}
|
||||
for python_implemention in architectures:
|
||||
platform_tags[python_implemention] = [
|
||||
'{manylinux_version}_{architecture}'.format(
|
||||
manylinux_version=manylinux_version, architecture=architecture)
|
||||
for architecture in architectures[python_implemention]
|
||||
|
||||
wheels = []
|
||||
|
||||
for python_abi_tag in python_abi_tags:
|
||||
platform_tags = []
|
||||
|
||||
if platform == 'linux':
|
||||
architectures = [pm.machine()]
|
||||
|
||||
if pm.machine() == 'x86_64' and python_abi_tag.startswith('cp'):
|
||||
architectures.append('i686')
|
||||
|
||||
platform_tags = [
|
||||
'{}_{}'.format(manylinux_version, architecture)
|
||||
for architecture in architectures
|
||||
for manylinux_version in manylinux_versions
|
||||
]
|
||||
|
||||
def get_platform_tags(python_abi_tag):
|
||||
return platform_tags[python_abi_tag[:2]]
|
||||
elif platform == 'windows':
|
||||
python_abi_tags += extra_x86_python_abi_tags
|
||||
platform_tags = {'cp': ['win32', 'win_amd64'], 'pp': ['win32']}
|
||||
elif platform == 'windows':
|
||||
if python_abi_tag.startswith('cp'):
|
||||
platform_tags = ['win32', 'win_amd64']
|
||||
else:
|
||||
platform_tags = ['win32']
|
||||
|
||||
def get_platform_tags(python_abi_tag):
|
||||
return platform_tags[python_abi_tag[:2]]
|
||||
elif platform == 'macos':
|
||||
platform_tags = ['macosx_{}_x86_64'.format(macosx_deployment_target.replace('.', '_'))]
|
||||
|
||||
elif platform == 'macos':
|
||||
python_abi_tags += extra_x86_python_abi_tags
|
||||
else:
|
||||
raise Exception('unsupported platform')
|
||||
|
||||
def get_platform_tags(python_abi_tag):
|
||||
default_version = '10.7' if python_abi_tag.startswith('pp') else '10.9'
|
||||
return ['macosx_{}_x86_64'.format((macosx_deployment_target or default_version).replace('.', '_'))]
|
||||
else:
|
||||
raise Exception('unsupported platform')
|
||||
|
||||
templates = []
|
||||
for python_abi_tag in python_abi_tags:
|
||||
for platform_tag in get_platform_tags(python_abi_tag):
|
||||
templates.append('{package_name}-{package_version}-{python_abi_tag}-{platform_tag}.whl'.format(
|
||||
for platform_tag in platform_tags:
|
||||
wheels.append('{package_name}-{package_version}-{python_abi_tag}-{platform_tag}.whl'.format(
|
||||
package_name=package_name, package_version=package_version,
|
||||
python_abi_tag=python_abi_tag, platform_tag=platform_tag
|
||||
))
|
||||
|
||||
if IS_WINDOWS_RUNNING_ON_TRAVIS:
|
||||
# Python 2.7 isn't supported on Travis.
|
||||
templates = [t for t in templates if '-cp27-' not in t and '-pp2' not in t]
|
||||
wheels = [w for w in wheels if '-cp27-' not in w and '-pp2' not in w]
|
||||
|
||||
return templates
|
||||
return wheels
|
||||
|
||||
|
||||
platform = None
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
from cibuildwheel.util import DependencyConstraints
|
||||
import os
|
||||
|
||||
|
||||
def test_defaults():
|
||||
dependency_constraints = DependencyConstraints.with_defaults()
|
||||
|
||||
project_root = os.path.dirname(os.path.dirname(__file__))
|
||||
resources_dir = os.path.join(project_root, 'cibuildwheel', 'resources')
|
||||
|
||||
assert os.path.samefile(
|
||||
dependency_constraints.base_file_path,
|
||||
os.path.join(resources_dir, 'constraints.txt')
|
||||
)
|
||||
assert os.path.samefile(
|
||||
dependency_constraints.get_for_python_version('3.8'),
|
||||
os.path.join(resources_dir, 'constraints.txt')
|
||||
)
|
||||
assert os.path.samefile(
|
||||
dependency_constraints.get_for_python_version('3.6'),
|
||||
os.path.join(resources_dir, 'constraints-python36.txt')
|
||||
)
|
||||
assert os.path.samefile(
|
||||
dependency_constraints.get_for_python_version('3.5'),
|
||||
os.path.join(resources_dir, 'constraints-python35.txt')
|
||||
)
|
||||
assert os.path.samefile(
|
||||
dependency_constraints.get_for_python_version('2.7'),
|
||||
os.path.join(resources_dir, 'constraints-python27.txt')
|
||||
)
|
||||
@@ -1,4 +1,5 @@
|
||||
import sys
|
||||
from fnmatch import fnmatch
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -17,13 +18,13 @@ def test_output_dir(platform, intercepted_build_args, monkeypatch):
|
||||
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.kwargs['output_dir'] == OUTPUT_DIR
|
||||
assert intercepted_build_args.args[0].output_dir == OUTPUT_DIR
|
||||
|
||||
|
||||
def test_output_dir_default(platform, intercepted_build_args, monkeypatch):
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.kwargs['output_dir'] == 'wheelhouse'
|
||||
assert intercepted_build_args.args[0].output_dir == 'wheelhouse'
|
||||
|
||||
|
||||
@pytest.mark.parametrize('also_set_environment', [False, True])
|
||||
@@ -36,7 +37,7 @@ def test_output_dir_argument(also_set_environment, platform, intercepted_build_a
|
||||
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.kwargs['output_dir'] == OUTPUT_DIR
|
||||
assert intercepted_build_args.args[0].output_dir == OUTPUT_DIR
|
||||
|
||||
|
||||
def test_build_selector(platform, intercepted_build_args, monkeypatch):
|
||||
@@ -48,7 +49,7 @@ def test_build_selector(platform, intercepted_build_args, monkeypatch):
|
||||
|
||||
main()
|
||||
|
||||
intercepted_build_selector = intercepted_build_args.kwargs['build_selector']
|
||||
intercepted_build_selector = intercepted_build_args.args[0].build_selector
|
||||
assert isinstance(intercepted_build_selector, BuildSelector)
|
||||
assert intercepted_build_selector('build-this')
|
||||
assert not intercepted_build_selector('skip-that')
|
||||
@@ -57,19 +58,19 @@ def test_build_selector(platform, intercepted_build_args, monkeypatch):
|
||||
|
||||
|
||||
@pytest.mark.parametrize('architecture, image, full_image', [
|
||||
('x86_64', None, 'quay.io/pypa/manylinux2010_x86_64'),
|
||||
('x86_64', 'manylinux1', 'quay.io/pypa/manylinux1_x86_64'),
|
||||
('x86_64', 'manylinux2010', 'quay.io/pypa/manylinux2010_x86_64'),
|
||||
('x86_64', 'manylinux2014', 'quay.io/pypa/manylinux2014_x86_64'),
|
||||
('x86_64', None, 'quay.io/pypa/manylinux2010_x86_64:*'),
|
||||
('x86_64', 'manylinux1', 'quay.io/pypa/manylinux1_x86_64:*'),
|
||||
('x86_64', 'manylinux2010', 'quay.io/pypa/manylinux2010_x86_64:*'),
|
||||
('x86_64', 'manylinux2014', 'quay.io/pypa/manylinux2014_x86_64:*'),
|
||||
('x86_64', 'custom_image', 'custom_image'),
|
||||
('i686', None, 'quay.io/pypa/manylinux2010_i686'),
|
||||
('i686', 'manylinux1', 'quay.io/pypa/manylinux1_i686'),
|
||||
('i686', 'manylinux2010', 'quay.io/pypa/manylinux2010_i686'),
|
||||
('i686', 'manylinux2014', 'quay.io/pypa/manylinux2014_i686'),
|
||||
('i686', None, 'quay.io/pypa/manylinux2010_i686:*'),
|
||||
('i686', 'manylinux1', 'quay.io/pypa/manylinux1_i686:*'),
|
||||
('i686', 'manylinux2010', 'quay.io/pypa/manylinux2010_i686:*'),
|
||||
('i686', 'manylinux2014', 'quay.io/pypa/manylinux2014_i686:*'),
|
||||
('i686', 'custom_image', 'custom_image'),
|
||||
('pypy_x86_64', None, 'pypywheels/manylinux2010-pypy_x86_64'),
|
||||
('pypy_x86_64', None, 'pypywheels/manylinux2010-pypy_x86_64:*'),
|
||||
('pypy_x86_64', 'manylinux1', 'manylinux1'), # Does not exist
|
||||
('pypy_x86_64', 'manylinux2010', 'pypywheels/manylinux2010-pypy_x86_64'),
|
||||
('pypy_x86_64', 'manylinux2010', 'pypywheels/manylinux2010-pypy_x86_64:*'),
|
||||
('pypy_x86_64', 'manylinux2014', 'manylinux2014'), # Does not exist (yet)
|
||||
('pypy_x86_64', 'custom_image', 'custom_image'),
|
||||
])
|
||||
@@ -80,9 +81,12 @@ def test_manylinux_images(architecture, image, full_image, platform, intercepted
|
||||
main()
|
||||
|
||||
if platform == 'linux':
|
||||
assert intercepted_build_args.kwargs['manylinux_images'][architecture] == full_image
|
||||
assert fnmatch(
|
||||
intercepted_build_args.args[0].manylinux_images[architecture],
|
||||
full_image
|
||||
)
|
||||
else:
|
||||
assert 'manylinux_images' not in intercepted_build_args.kwargs
|
||||
assert intercepted_build_args.args[0].manylinux_images is None
|
||||
|
||||
|
||||
def get_default_repair_command(platform):
|
||||
@@ -109,7 +113,7 @@ def test_repair_command(repair_command, platform_specific, platform, intercepted
|
||||
main()
|
||||
|
||||
expected_repair = repair_command or get_default_repair_command(platform)
|
||||
assert intercepted_build_args.kwargs['repair_command'] == expected_repair
|
||||
assert intercepted_build_args.args[0].repair_command == expected_repair
|
||||
|
||||
|
||||
@pytest.mark.parametrize('environment', [
|
||||
@@ -128,7 +132,7 @@ def test_environment(environment, platform_specific, platform, intercepted_build
|
||||
|
||||
main()
|
||||
|
||||
intercepted_environment = intercepted_build_args.kwargs['environment']
|
||||
intercepted_environment = intercepted_build_args.args[0].environment
|
||||
assert isinstance(intercepted_environment, ParsedEnvironment)
|
||||
assert intercepted_environment.as_dictionary(prev_environment={}) == environment
|
||||
|
||||
@@ -145,7 +149,7 @@ def test_test_requires(test_requires, platform_specific, platform, intercepted_b
|
||||
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.kwargs['test_requires'] == (test_requires or '').split()
|
||||
assert intercepted_build_args.args[0].test_requires == (test_requires or '').split()
|
||||
|
||||
|
||||
@pytest.mark.parametrize('test_extras', [None, 'extras'])
|
||||
@@ -160,7 +164,7 @@ def test_test_extras(test_extras, platform_specific, platform, intercepted_build
|
||||
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.kwargs['test_extras'] == ('[' + test_extras + ']' if test_extras else '')
|
||||
assert intercepted_build_args.args[0],test_extras == ('[' + test_extras + ']' if test_extras else '')
|
||||
|
||||
|
||||
@pytest.mark.parametrize('test_command', [None, 'test --command'])
|
||||
@@ -175,7 +179,7 @@ def test_test_command(test_command, platform_specific, platform, intercepted_bui
|
||||
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.kwargs['test_command'] == test_command
|
||||
assert intercepted_build_args.args[0].test_command == test_command
|
||||
|
||||
|
||||
@pytest.mark.parametrize('before_build', [None, 'before --build'])
|
||||
@@ -190,7 +194,7 @@ def test_before_build(before_build, platform_specific, platform, intercepted_bui
|
||||
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.kwargs['before_build'] == before_build
|
||||
assert intercepted_build_args.args[0].before_build == before_build
|
||||
|
||||
|
||||
@pytest.mark.parametrize('build_verbosity', [None, 0, 2, -2, 4, -4])
|
||||
@@ -206,7 +210,7 @@ def test_build_verbosity(build_verbosity, platform_specific, platform, intercept
|
||||
main()
|
||||
|
||||
expected_verbosity = max(-3, min(3, int(build_verbosity or 0)))
|
||||
assert intercepted_build_args.kwargs['build_verbosity'] == expected_verbosity
|
||||
assert intercepted_build_args.args[0].build_verbosity == expected_verbosity
|
||||
|
||||
|
||||
@pytest.mark.parametrize('option_name', ['CIBW_BUILD', 'CIBW_SKIP'])
|
||||
@@ -221,7 +225,7 @@ def test_build_selector_migrations(intercepted_build_args, monkeypatch, option_n
|
||||
|
||||
main()
|
||||
|
||||
intercepted_build_selector = intercepted_build_args.kwargs['build_selector']
|
||||
intercepted_build_selector = intercepted_build_args.args[0].build_selector
|
||||
assert isinstance(intercepted_build_selector, BuildSelector)
|
||||
|
||||
if option_name == 'CIBW_BUILD':
|
||||
|
||||
@@ -51,10 +51,10 @@ def test_platform_argument(platform, intercepted_build_args, monkeypatch):
|
||||
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.kwargs['package_dir'] == MOCK_PACKAGE_DIR
|
||||
assert intercepted_build_args.args[0].package_dir == MOCK_PACKAGE_DIR
|
||||
|
||||
|
||||
def test_platform_environment(platform, intercepted_build_args, monkeypatch):
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.kwargs['package_dir'] == MOCK_PACKAGE_DIR
|
||||
assert intercepted_build_args.args[0].package_dir == MOCK_PACKAGE_DIR
|
||||
|
||||
Reference in New Issue
Block a user