Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ecbdc2e3d3 | ||
|
|
60a3d51a50 | ||
|
|
bdc86ba539 | ||
|
|
04c1a2aee5 | ||
|
|
e76ebbcdec | ||
|
|
0c323cfb5b | ||
|
|
2e41a4f94a | ||
|
|
81f960b17e | ||
|
|
cf60df5491 | ||
|
|
fd8507212c | ||
|
|
420995f567 | ||
|
|
29084bd5bf | ||
|
|
13907188a1 | ||
|
|
d606aaa85e | ||
|
|
2e8fe841ec | ||
|
|
0411fcc2ae | ||
|
|
6a9ab683a6 | ||
|
|
e9592b2d53 | ||
|
|
db6b1f3541 | ||
|
|
5e767fcc9a | ||
|
|
ac0012e3da | ||
|
|
71150a1cb5 | ||
|
|
0368d58065 | ||
|
|
9daad5c60c | ||
|
|
4683ac3d6c | ||
|
|
7cc85c2e4f | ||
|
|
891e2dd6aa | ||
|
|
575e229514 | ||
|
|
1a765ac431 | ||
|
|
5bdb21b705 | ||
|
|
40967a6572 | ||
|
|
c128018590 | ||
|
|
618f1cefae | ||
|
|
a679a09212 | ||
|
|
cc1ae4a963 | ||
|
|
dbd7006704 | ||
|
|
da4bc0225c | ||
|
|
f145c7c148 | ||
|
|
73425d4bb8 | ||
|
|
7927836489 | ||
|
|
185a1c09ad | ||
|
|
3d4a6b0765 | ||
|
|
7eb82bf90c | ||
|
|
5986c91937 | ||
|
|
63421d40f0 | ||
|
|
e11c0c2dd1 | ||
|
|
362b4314cb | ||
|
|
ba60f1865f | ||
|
|
f0e65d9f5a | ||
|
|
e1881976e8 | ||
|
|
953839be59 | ||
|
|
aaf1bba1cf | ||
|
|
196a0323a4 | ||
|
|
729bab42c8 | ||
|
|
a9c475a910 | ||
|
|
566a880099 | ||
|
|
bf882b065d | ||
|
|
1b38974c60 | ||
|
|
c568f7ba89 | ||
|
|
e9803e8fb3 | ||
|
|
dafe5cd1f7 | ||
|
|
33f210b85c | ||
|
|
e2cafa487b | ||
|
|
ffd5d44cdc | ||
|
|
5d665aac39 | ||
|
|
662612685c | ||
|
|
a204df819b | ||
|
|
9bb075aa0b | ||
|
|
007be7aea7 | ||
|
|
3b35882f87 | ||
|
|
616e9dd6eb | ||
|
|
c38bc9064a | ||
|
|
4f75eaabcd |
+9
-23
@@ -1,18 +1,21 @@
|
|||||||
version: 2
|
version: 2
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
flake8:
|
flake8-mypy:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/python:3.6
|
- image: circleci/python:3.6
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
|
|
||||||
- run:
|
- run:
|
||||||
name: Install flake8
|
name: Install flake8 & mypy
|
||||||
command: sudo python -m pip install flake8
|
command: sudo python -m pip install flake8 mypy
|
||||||
- run:
|
- run:
|
||||||
name: Test.
|
name: flake8
|
||||||
command: flake8 .
|
command: flake8
|
||||||
|
- run:
|
||||||
|
name: mypy
|
||||||
|
command: mypy
|
||||||
|
|
||||||
osx-python3.6:
|
osx-python3.6:
|
||||||
macos:
|
macos:
|
||||||
@@ -30,22 +33,6 @@ jobs:
|
|||||||
command: venv/bin/python ./bin/run_tests.py
|
command: venv/bin/python ./bin/run_tests.py
|
||||||
no_output_timeout: 30m
|
no_output_timeout: 30m
|
||||||
|
|
||||||
osx-python3.7:
|
|
||||||
macos:
|
|
||||||
xcode: "10.0.0"
|
|
||||||
environment:
|
|
||||||
PYTHON: python3
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
|
|
||||||
- run:
|
|
||||||
name: Prepare the environment.
|
|
||||||
command: bash .circleci/prepare.sh
|
|
||||||
- run:
|
|
||||||
name: Test.
|
|
||||||
command: venv/bin/python ./bin/run_tests.py
|
|
||||||
no_output_timeout: 30m
|
|
||||||
|
|
||||||
linux-python3.6:
|
linux-python3.6:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/python:3.6
|
- image: circleci/python:3.6
|
||||||
@@ -67,7 +54,6 @@ workflows:
|
|||||||
version: 2
|
version: 2
|
||||||
all-tests:
|
all-tests:
|
||||||
jobs:
|
jobs:
|
||||||
- flake8
|
- flake8-mypy
|
||||||
- osx-python3.6
|
- osx-python3.6
|
||||||
- osx-python3.7
|
|
||||||
- linux-python3.6
|
- linux-python3.6
|
||||||
|
|||||||
@@ -102,3 +102,6 @@ env3?/
|
|||||||
|
|
||||||
# VSCode project settings
|
# VSCode project settings
|
||||||
/.vscode
|
/.vscode
|
||||||
|
|
||||||
|
# MyPy cache
|
||||||
|
.mypy_cache/
|
||||||
|
|||||||
+13
-13
@@ -6,22 +6,22 @@ branches:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
include:
|
include:
|
||||||
- name: Linux | x86_64 + i686 | Python 3.5
|
- name: Linux | x86_64 + i686 | Python 3.6
|
||||||
language: python
|
language: python
|
||||||
python: 3.5
|
python: 3.6
|
||||||
services: docker
|
services: docker
|
||||||
env: PYTHON=python
|
env: PYTHON=python
|
||||||
|
|
||||||
- name: Linux | arm64 | Python 3.5
|
- name: Linux | arm64 | Python 3.6
|
||||||
language: python
|
language: python
|
||||||
python: 3.5
|
python: 3.6
|
||||||
services: docker
|
services: docker
|
||||||
arch: arm64
|
arch: arm64
|
||||||
env: PYTHON=python
|
env: PYTHON=python
|
||||||
|
|
||||||
- name: Linux | ppc64le | Python 3.5
|
- name: Linux | ppc64le | Python 3.6
|
||||||
language: python
|
language: python
|
||||||
python: 3.5
|
python: 3.6
|
||||||
services: docker
|
services: docker
|
||||||
arch: ppc64le
|
arch: ppc64le
|
||||||
env: PYTHON=python
|
env: PYTHON=python
|
||||||
@@ -30,25 +30,25 @@ jobs:
|
|||||||
os: osx
|
os: osx
|
||||||
env: PYTHON=python3
|
env: PYTHON=python3
|
||||||
|
|
||||||
- name: Windows | x86_64 | Python 3.5
|
- name: Windows | x86_64 | Python 3.6
|
||||||
os: windows
|
os: windows
|
||||||
language: shell
|
language: shell
|
||||||
before_install:
|
before_install:
|
||||||
- choco install python3 --version 3.5.4 --no-progress -y
|
- choco install python3 --version 3.6.8 --no-progress -y
|
||||||
env:
|
env:
|
||||||
- PYTHON=C:\\Python35\\python
|
- PYTHON=C:\\Python36\\python
|
||||||
|
|
||||||
- &linux_s390x_35
|
- &linux_s390x_36
|
||||||
name: Linux | s390x | Python 3.5
|
name: Linux | s390x | Python 3.6
|
||||||
language: python
|
language: python
|
||||||
python: 3.5
|
python: 3.6
|
||||||
services: docker
|
services: docker
|
||||||
arch: s390x
|
arch: s390x
|
||||||
env: PYTHON=python
|
env: PYTHON=python
|
||||||
|
|
||||||
allow_failures:
|
allow_failures:
|
||||||
# must repeat the s390x job above exactly to match
|
# must repeat the s390x job above exactly to match
|
||||||
- *linux_s390x_35
|
- *linux_s390x_36
|
||||||
|
|
||||||
install: $PYTHON -m pip install -r requirements-dev.txt
|
install: $PYTHON -m pip install -r requirements-dev.txt
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
This is a summary of the Python versions and platforms covered by the different CI platforms:
|
This is a summary of the Python versions and platforms covered by the different CI platforms:
|
||||||
|
|
||||||
| | 3.5 | 3.6 | 3.7 | 3.8 |
|
| | 3.6 | 3.7 | 3.8 |
|
||||||
|----------|------------------|------------------|---------------------------------------------------|------------------|
|
|----------|------------------------------|------------------------------------------|------------------|
|
||||||
| Linux | Travis CI | CircleCI | AppVeyor / GitHub Actions | Azure Pipelines |
|
| Linux | Travis CI / CircleCI | AppVeyor² / GitHub Actions | Azure Pipelines |
|
||||||
| macOS | Azure Pipelines | CircleCI | AppVeyor / Travis CI¹ / CircleCI / GitHub Actions | Azure Pipelines |
|
| macOS | CircleCI | AppVeyor² / Travis CI¹ / GitHub Actions | Azure Pipelines |
|
||||||
| Windows | TravisCI | Azure Pipelines | AppVeyor / GitHub Actions | Azure Pipelines |
|
| Windows | Travis CI / Azure Pipelines | AppVeyor² / GitHub Actions | Azure Pipelines |
|
||||||
|
|
||||||
> ¹ Python version not really pinned, but dependent on the (default) version of image used.
|
> ¹ Python version not really pinned, but dependent on the (default) version of image used.
|
||||||
|
> ² AppVeyor only runs the "basic" test to reduce load.
|
||||||
|
|
||||||
Non-x86 architectures are covered on Travis CI using Python 3.5.
|
Non-x86 architectures are covered on Travis CI using Python 3.6.
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ env:
|
|||||||
# Note: TWINE_PASSWORD is set to a PyPI API token in Travis settings
|
# Note: TWINE_PASSWORD is set to a PyPI API token in Travis settings
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- python3 -m pip install cibuildwheel==1.4.1
|
- python3 -m pip install cibuildwheel==1.4.2
|
||||||
|
|
||||||
script:
|
script:
|
||||||
# build the wheels, put them into './wheelhouse'
|
# build the wheels, put them into './wheelhouse'
|
||||||
@@ -128,6 +128,10 @@ Here are some repos that use cibuildwheel.
|
|||||||
- [Twisted](https://github.com/twisted/twisted)
|
- [Twisted](https://github.com/twisted/twisted)
|
||||||
- [gmic-py](https://github.com/dtschump/gmic-py)
|
- [gmic-py](https://github.com/dtschump/gmic-py)
|
||||||
- [creme](https://github.com/creme-ml/creme)
|
- [creme](https://github.com/creme-ml/creme)
|
||||||
|
- [PyAV](https://github.com/PyAV-Org/PyAV)
|
||||||
|
- [aiortc](https://github.com/aiortc/aiortc)
|
||||||
|
- [aioquic](https://github.com/aiortc/aioquic)
|
||||||
|
- [pikepdf](https://github.com/pikepdf/pikepdf)
|
||||||
|
|
||||||
> Add your repo here! Send a PR.
|
> Add your repo here! Send a PR.
|
||||||
|
|
||||||
@@ -143,6 +147,16 @@ This is similar to static linking, so it might have some licence implications. C
|
|||||||
Changelog
|
Changelog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
### 1.4.2
|
||||||
|
|
||||||
|
_25 May 2020_
|
||||||
|
|
||||||
|
- 🛠 Dependency updates, including CPython 3.8.3 & manylinux images.
|
||||||
|
- 🛠 Lots of internal updates - type annotations and checking using mypy, and
|
||||||
|
a new integration testing system.
|
||||||
|
- ⚠️ Removed support for *running* cibuildwheel using Python 3.5. cibuildwheel
|
||||||
|
will continue to build Python 3.5 wheels until EOL.
|
||||||
|
|
||||||
### 1.4.1
|
### 1.4.1
|
||||||
|
|
||||||
_4 May 2020_
|
_4 May 2020_
|
||||||
|
|||||||
+4
-14
@@ -6,17 +6,7 @@ jobs:
|
|||||||
inputs:
|
inputs:
|
||||||
versionSpec: '3.8'
|
versionSpec: '3.8'
|
||||||
- bash: |
|
- bash: |
|
||||||
python -m pip install -r requirements-dev.txt
|
python -m pip install -r requirements-dev.txt pytest-azurepipelines
|
||||||
python ./bin/run_tests.py
|
|
||||||
|
|
||||||
- job: macos_35
|
|
||||||
pool: {vmImage: 'macOS-10.15'}
|
|
||||||
steps:
|
|
||||||
- task: UsePythonVersion@0
|
|
||||||
inputs:
|
|
||||||
versionSpec: '3.5'
|
|
||||||
- bash: |
|
|
||||||
python -m pip install -r requirements-dev.txt
|
|
||||||
python ./bin/run_tests.py
|
python ./bin/run_tests.py
|
||||||
|
|
||||||
- job: macos_38
|
- job: macos_38
|
||||||
@@ -26,7 +16,7 @@ jobs:
|
|||||||
inputs:
|
inputs:
|
||||||
versionSpec: '3.8'
|
versionSpec: '3.8'
|
||||||
- bash: |
|
- bash: |
|
||||||
python -m pip install -r requirements-dev.txt
|
python -m pip install -r requirements-dev.txt pytest-azurepipelines
|
||||||
python ./bin/run_tests.py
|
python ./bin/run_tests.py
|
||||||
|
|
||||||
- job: windows_36
|
- job: windows_36
|
||||||
@@ -38,7 +28,7 @@ jobs:
|
|||||||
- script: choco install vcpython27 -f -y
|
- script: choco install vcpython27 -f -y
|
||||||
displayName: Install Visual C++ for Python 2.7
|
displayName: Install Visual C++ for Python 2.7
|
||||||
- bash: |
|
- bash: |
|
||||||
python -m pip install -r requirements-dev.txt
|
python -m pip install -r requirements-dev.txt pytest-azurepipelines
|
||||||
python ./bin/run_tests.py
|
python ./bin/run_tests.py
|
||||||
|
|
||||||
- job: windows_38
|
- job: windows_38
|
||||||
@@ -50,6 +40,6 @@ jobs:
|
|||||||
- script: choco install vcpython27 -f -y
|
- script: choco install vcpython27 -f -y
|
||||||
displayName: Install Visual C++ for Python 2.7
|
displayName: Install Visual C++ for Python 2.7
|
||||||
- bash: |
|
- bash: |
|
||||||
python -m pip install -r requirements-dev.txt
|
python -m pip install -r requirements-dev.txt pytest-azurepipelines
|
||||||
python ./bin/run_tests.py
|
python ./bin/run_tests.py
|
||||||
|
|
||||||
|
|||||||
+1
-4
@@ -1,6 +1,3 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
cd "$(dirname "$0")"
|
CIBW_PLATFORM=linux pytest "$@"
|
||||||
cd ..
|
|
||||||
|
|
||||||
CIBW_PLATFORM=linux ./bin/run_test.py $1
|
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
|
|
||||||
|
|
||||||
def single_run(test_project):
|
|
||||||
# run the test
|
|
||||||
subprocess.check_call(
|
|
||||||
[sys.executable, '-m', 'pytest', '-vv', os.path.join(test_project, 'cibuildwheel_test.py')],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
parser.add_argument("test_project_dir")
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
project_path = os.path.abspath(args.test_project_dir)
|
|
||||||
|
|
||||||
if not os.path.exists(project_path):
|
|
||||||
print('No test project not found.', file=sys.stderr)
|
|
||||||
exit(2)
|
|
||||||
|
|
||||||
single_run(project_path)
|
|
||||||
+1
-16
@@ -3,28 +3,13 @@
|
|||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from glob import glob
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# move cwd to the project root
|
# move cwd to the project root
|
||||||
os.chdir(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
os.chdir(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
# run the unit tests
|
# run the unit tests
|
||||||
|
|
||||||
subprocess.check_call([sys.executable, '-m', 'pytest', 'unit_test'])
|
subprocess.check_call([sys.executable, '-m', 'pytest', 'unit_test'])
|
||||||
|
|
||||||
# run the integration tests
|
# run the integration tests
|
||||||
|
subprocess.check_call([sys.executable, '-m', 'pytest', '-x', '--durations', '0', 'test'])
|
||||||
test_projects = sorted(glob('test/??_*'))
|
|
||||||
|
|
||||||
if len(test_projects) == 0:
|
|
||||||
print('No test projects found. Aborting.', file=sys.stderr)
|
|
||||||
exit(2)
|
|
||||||
|
|
||||||
print('Testing projects:', test_projects)
|
|
||||||
|
|
||||||
run_test_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'run_test.py')
|
|
||||||
for project_path in test_projects:
|
|
||||||
subprocess.check_call([sys.executable, run_test_path, project_path])
|
|
||||||
|
|
||||||
print('%d projects built successfully.' % len(test_projects))
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
__version__ = '1.4.1'
|
__version__ = '1.4.2'
|
||||||
|
|||||||
+32
-33
@@ -5,6 +5,8 @@ import textwrap
|
|||||||
import traceback
|
import traceback
|
||||||
from configparser import ConfigParser
|
from configparser import ConfigParser
|
||||||
|
|
||||||
|
from typing import Any, Dict, List, Optional, overload
|
||||||
|
|
||||||
import cibuildwheel
|
import cibuildwheel
|
||||||
import cibuildwheel.linux
|
import cibuildwheel.linux
|
||||||
import cibuildwheel.macos
|
import cibuildwheel.macos
|
||||||
@@ -14,14 +16,18 @@ from cibuildwheel.environment import (
|
|||||||
parse_environment,
|
parse_environment,
|
||||||
)
|
)
|
||||||
from cibuildwheel.util import (
|
from cibuildwheel.util import (
|
||||||
|
BuildOptions,
|
||||||
BuildSelector,
|
BuildSelector,
|
||||||
DependencyConstraints,
|
DependencyConstraints,
|
||||||
Unbuffered,
|
Unbuffered,
|
||||||
BuildOptions
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_option_from_environment(option_name, platform=None, default=None):
|
@overload
|
||||||
|
def get_option_from_environment(option_name: str, platform: Optional[str], default: str) -> str: ... # noqa: E704
|
||||||
|
@overload
|
||||||
|
def get_option_from_environment(option_name: str, platform: Optional[str] = None, default: None = None) -> Optional[str]: ... # noqa: E704 E302
|
||||||
|
def get_option_from_environment(option_name: str, platform: Optional[str] = None, default: Optional[str] = None) -> Optional[str]: # noqa: E302
|
||||||
'''
|
'''
|
||||||
Returns an option from the environment, optionally scoped by the platform.
|
Returns an option from the environment, optionally scoped by the platform.
|
||||||
|
|
||||||
@@ -32,20 +38,20 @@ def get_option_from_environment(option_name, platform=None, default=None):
|
|||||||
CIBW_COLOR.
|
CIBW_COLOR.
|
||||||
'''
|
'''
|
||||||
if platform:
|
if platform:
|
||||||
option = os.environ.get('%s_%s' % (option_name, platform.upper()))
|
option = os.environ.get(f'{option_name}_{platform.upper()}')
|
||||||
if option is not None:
|
if option is not None:
|
||||||
return option
|
return option
|
||||||
|
|
||||||
return os.environ.get(option_name, default)
|
return os.environ.get(option_name, default)
|
||||||
|
|
||||||
|
|
||||||
def strtobool(val):
|
def strtobool(val: str) -> bool:
|
||||||
if val.lower() in ('y', 'yes', 't', 'true', 'on', '1'):
|
if val.lower() in ('y', 'yes', 't', 'true', 'on', '1'):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main() -> None:
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description='Build wheels for all the platforms.',
|
description='Build wheels for all the platforms.',
|
||||||
epilog='''
|
epilog='''
|
||||||
@@ -114,7 +120,7 @@ def main():
|
|||||||
test_extras = get_option_from_environment('CIBW_TEST_EXTRAS', platform=platform, default='')
|
test_extras = get_option_from_environment('CIBW_TEST_EXTRAS', platform=platform, default='')
|
||||||
package_dir = args.package_dir
|
package_dir = args.package_dir
|
||||||
before_build = get_option_from_environment('CIBW_BEFORE_BUILD', platform=platform)
|
before_build = get_option_from_environment('CIBW_BEFORE_BUILD', platform=platform)
|
||||||
build_verbosity = get_option_from_environment('CIBW_BUILD_VERBOSITY', platform=platform, default='')
|
build_verbosity_str = get_option_from_environment('CIBW_BUILD_VERBOSITY', platform=platform, default='')
|
||||||
build_config, skip_config = os.environ.get('CIBW_BUILD', '*'), os.environ.get('CIBW_SKIP', '')
|
build_config, skip_config = os.environ.get('CIBW_BUILD', '*'), os.environ.get('CIBW_SKIP', '')
|
||||||
if platform == 'linux':
|
if platform == 'linux':
|
||||||
repair_command_default = 'auditwheel repair -w {dest_dir} {wheel}'
|
repair_command_default = 'auditwheel repair -w {dest_dir} {wheel}'
|
||||||
@@ -128,24 +134,24 @@ def main():
|
|||||||
|
|
||||||
dependency_versions = get_option_from_environment('CIBW_DEPENDENCY_VERSIONS', platform=platform, default='pinned')
|
dependency_versions = get_option_from_environment('CIBW_DEPENDENCY_VERSIONS', platform=platform, default='pinned')
|
||||||
if dependency_versions == 'pinned':
|
if dependency_versions == 'pinned':
|
||||||
dependency_constraints = DependencyConstraints.with_defaults()
|
dependency_constraints: Optional[DependencyConstraints] = DependencyConstraints.with_defaults()
|
||||||
elif dependency_versions == 'latest':
|
elif dependency_versions == 'latest':
|
||||||
dependency_constraints = None
|
dependency_constraints = None
|
||||||
else:
|
else:
|
||||||
dependency_constraints = DependencyConstraints(dependency_versions)
|
dependency_constraints = DependencyConstraints(dependency_versions)
|
||||||
|
|
||||||
if test_extras:
|
if test_extras:
|
||||||
test_extras = '[{0}]'.format(test_extras)
|
test_extras = f'[{test_extras}]'
|
||||||
|
|
||||||
try:
|
try:
|
||||||
build_verbosity = min(3, max(-3, int(build_verbosity)))
|
build_verbosity = min(3, max(-3, int(build_verbosity_str)))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
build_verbosity = 0
|
build_verbosity = 0
|
||||||
|
|
||||||
try:
|
try:
|
||||||
environment = parse_environment(environment_config)
|
environment = parse_environment(environment_config)
|
||||||
except (EnvironmentParseError, ValueError):
|
except (EnvironmentParseError, ValueError):
|
||||||
print('cibuildwheel: Malformed environment option "%s"' % environment_config, file=sys.stderr)
|
print(f'cibuildwheel: Malformed environment option "{environment_config}"', file=sys.stderr)
|
||||||
traceback.print_exc(None, sys.stderr)
|
traceback.print_exc(None, sys.stderr)
|
||||||
exit(2)
|
exit(2)
|
||||||
|
|
||||||
@@ -163,6 +169,7 @@ def main():
|
|||||||
print_build_identifiers(platform, build_selector)
|
print_build_identifiers(platform, build_selector)
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
||||||
|
manylinux_images: Optional[Dict[str, str]] = None
|
||||||
if platform == 'linux':
|
if platform == 'linux':
|
||||||
pinned_docker_images_file = os.path.join(
|
pinned_docker_images_file = os.path.join(
|
||||||
os.path.dirname(__file__), 'resources', 'pinned_docker_images.cfg'
|
os.path.dirname(__file__), 'resources', 'pinned_docker_images.cfg'
|
||||||
@@ -180,7 +187,7 @@ def main():
|
|||||||
for build_platform in ['x86_64', 'i686', 'pypy_x86_64', 'aarch64', 'ppc64le', 's390x']:
|
for build_platform in ['x86_64', 'i686', 'pypy_x86_64', 'aarch64', 'ppc64le', 's390x']:
|
||||||
pinned_images = all_pinned_docker_images[build_platform]
|
pinned_images = all_pinned_docker_images[build_platform]
|
||||||
|
|
||||||
config_name = 'CIBW_MANYLINUX_{}_IMAGE'.format(build_platform.upper())
|
config_name = f'CIBW_MANYLINUX_{build_platform.upper()}_IMAGE'
|
||||||
config_value = os.environ.get(config_name)
|
config_value = os.environ.get(config_name)
|
||||||
|
|
||||||
if config_value is None:
|
if config_value is None:
|
||||||
@@ -193,9 +200,6 @@ def main():
|
|||||||
|
|
||||||
manylinux_images[build_platform] = image
|
manylinux_images[build_platform] = image
|
||||||
|
|
||||||
else:
|
|
||||||
manylinux_images = None
|
|
||||||
|
|
||||||
build_options = BuildOptions(
|
build_options = BuildOptions(
|
||||||
package_dir=package_dir,
|
package_dir=package_dir,
|
||||||
output_dir=output_dir,
|
output_dir=output_dir,
|
||||||
@@ -213,7 +217,7 @@ def main():
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Python is buffering by default when running on the CI platforms, giving problems interleaving subprocess call output with unflushed calls to 'print'
|
# 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)
|
sys.stdout = Unbuffered(sys.stdout) # type: ignore
|
||||||
|
|
||||||
print_preamble(platform, build_options)
|
print_preamble(platform, build_options)
|
||||||
|
|
||||||
@@ -227,21 +231,21 @@ def main():
|
|||||||
elif platform == 'macos':
|
elif platform == 'macos':
|
||||||
cibuildwheel.macos.build(build_options)
|
cibuildwheel.macos.build(build_options)
|
||||||
else:
|
else:
|
||||||
print('cibuildwheel: Unsupported platform: {}'.format(platform), file=sys.stderr)
|
print(f'cibuildwheel: Unsupported platform: {platform}', file=sys.stderr)
|
||||||
exit(2)
|
exit(2)
|
||||||
|
|
||||||
|
|
||||||
def detect_obsolete_options():
|
def detect_obsolete_options() -> None:
|
||||||
# Check the old 'MANYLINUX1_*_IMAGE' options
|
# Check the old 'MANYLINUX1_*_IMAGE' options
|
||||||
for (deprecated, alternative) in [('CIBW_MANYLINUX1_X86_64_IMAGE', 'CIBW_MANYLINUX_X86_64_IMAGE'),
|
for (deprecated, alternative) in [('CIBW_MANYLINUX1_X86_64_IMAGE', 'CIBW_MANYLINUX_X86_64_IMAGE'),
|
||||||
('CIBW_MANYLINUX1_I686_IMAGE', 'CIBW_MANYLINUX_I686_IMAGE')]:
|
('CIBW_MANYLINUX1_I686_IMAGE', 'CIBW_MANYLINUX_I686_IMAGE')]:
|
||||||
if deprecated in os.environ:
|
if deprecated in os.environ:
|
||||||
print("'{}' has been deprecated, and will be removed in a future release. Use the option '{}' instead.".format(deprecated, alternative))
|
print(f"'{deprecated}' has been deprecated, and will be removed in a future release. Use the option '{alternative}' instead.")
|
||||||
if alternative not in os.environ:
|
if alternative not in os.environ:
|
||||||
print("Using value of option '{}' as replacement for '{}'".format(deprecated, alternative))
|
print(f"Using value of option '{deprecated}' as replacement for '{alternative}'")
|
||||||
os.environ[alternative] = os.environ[deprecated]
|
os.environ[alternative] = os.environ[deprecated]
|
||||||
else:
|
else:
|
||||||
print("Option '{}' is not empty. Please unset '{}'".format(alternative, deprecated))
|
print(f"Option '{alternative}' is not empty. Please unset '{deprecated}'")
|
||||||
exit(2)
|
exit(2)
|
||||||
|
|
||||||
# Check for deprecated identifiers in 'CIBW_BUILD' and 'CIBW_SKIP' options
|
# Check for deprecated identifiers in 'CIBW_BUILD' and 'CIBW_SKIP' options
|
||||||
@@ -250,15 +254,11 @@ def detect_obsolete_options():
|
|||||||
('macosx_10_6_intel', 'macosx_x86_64'),
|
('macosx_10_6_intel', 'macosx_x86_64'),
|
||||||
('macosx_10_9_x86_64', 'macosx_x86_64')]:
|
('macosx_10_9_x86_64', 'macosx_x86_64')]:
|
||||||
if option in os.environ and deprecated in os.environ[option]:
|
if option in os.environ and deprecated in os.environ[option]:
|
||||||
print("Build identifiers with '{deprecated}' have been deprecated. Replacing all occurences of '{deprecated}' with '{alternative}' in the option '{option}'".format(
|
print(f"Build identifiers with '{deprecated}' have been deprecated. Replacing all occurences of '{deprecated}' with '{alternative}' in the option '{option}'")
|
||||||
deprecated=deprecated,
|
|
||||||
alternative=alternative,
|
|
||||||
option=option,
|
|
||||||
))
|
|
||||||
os.environ[option] = os.environ[option].replace(deprecated, alternative)
|
os.environ[option] = os.environ[option].replace(deprecated, alternative)
|
||||||
|
|
||||||
|
|
||||||
def print_preamble(platform, build_options):
|
def print_preamble(platform: str, build_options: BuildOptions) -> None:
|
||||||
print(textwrap.dedent('''
|
print(textwrap.dedent('''
|
||||||
_ _ _ _ _ _ _
|
_ _ _ _ _ _ _
|
||||||
___|_| |_ _ _|_| |_| |_ _ _| |_ ___ ___| |
|
___|_| |_ _ _|_| |_| |_ _ _| |_ ___ ___| |
|
||||||
@@ -266,12 +266,12 @@ def print_preamble(platform, build_options):
|
|||||||
|___|_|___|___|_|_|___|_____|_|_|___|___|_|
|
|___|_|___|___|_|_|___|_____|_|_|___|___|_|
|
||||||
'''))
|
'''))
|
||||||
|
|
||||||
print('cibuildwheel version %s\n' % cibuildwheel.__version__)
|
print(f'cibuildwheel version {cibuildwheel.__version__}\n')
|
||||||
|
|
||||||
print('Build options:')
|
print('Build options:')
|
||||||
print(' platform: %r' % platform)
|
print(f' platform: {platform!r}')
|
||||||
for option, value in sorted(build_options._asdict().items()):
|
for option, value in sorted(build_options._asdict().items()):
|
||||||
print(' %s: %r' % (option, value))
|
print(f' {option}: {value!r}')
|
||||||
|
|
||||||
warnings = detect_warnings(platform, build_options)
|
warnings = detect_warnings(platform, build_options)
|
||||||
if warnings:
|
if warnings:
|
||||||
@@ -282,21 +282,20 @@ def print_preamble(platform, build_options):
|
|||||||
print('\nHere we go!\n')
|
print('\nHere we go!\n')
|
||||||
|
|
||||||
|
|
||||||
def print_build_identifiers(platform, build_selector):
|
def print_build_identifiers(platform: str, build_selector: BuildSelector) -> None:
|
||||||
|
python_configurations: List[Any] = []
|
||||||
if platform == 'linux':
|
if platform == 'linux':
|
||||||
python_configurations = cibuildwheel.linux.get_python_configurations(build_selector)
|
python_configurations = cibuildwheel.linux.get_python_configurations(build_selector)
|
||||||
elif platform == 'windows':
|
elif platform == 'windows':
|
||||||
python_configurations = cibuildwheel.windows.get_python_configurations(build_selector)
|
python_configurations = cibuildwheel.windows.get_python_configurations(build_selector)
|
||||||
elif platform == 'macos':
|
elif platform == 'macos':
|
||||||
python_configurations = cibuildwheel.macos.get_python_configurations(build_selector)
|
python_configurations = cibuildwheel.macos.get_python_configurations(build_selector)
|
||||||
else:
|
|
||||||
python_configurations = []
|
|
||||||
|
|
||||||
for config in python_configurations:
|
for config in python_configurations:
|
||||||
print(config.identifier)
|
print(config.identifier)
|
||||||
|
|
||||||
|
|
||||||
def detect_warnings(platform, build_options):
|
def detect_warnings(platform: str, build_options: BuildOptions) -> List[str]:
|
||||||
warnings = []
|
warnings = []
|
||||||
|
|
||||||
# warn about deprecated {python} and {pip}
|
# warn about deprecated {python} and {pip}
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
import shlex
|
import shlex
|
||||||
import subprocess
|
import subprocess
|
||||||
from collections import namedtuple
|
|
||||||
|
|
||||||
import bashlex
|
from typing import Dict, NamedTuple
|
||||||
|
|
||||||
NodeExecutionContext = namedtuple('NodeExecutionContext', ['environment', 'input'])
|
import bashlex # type: ignore
|
||||||
|
|
||||||
|
|
||||||
def evaluate(value, environment):
|
class NodeExecutionContext(NamedTuple):
|
||||||
|
environment: Dict[str, str]
|
||||||
|
input: str
|
||||||
|
|
||||||
|
|
||||||
|
def evaluate(value: str, environment: Dict[str, str]) -> str:
|
||||||
if not value:
|
if not value:
|
||||||
# empty string evaluates to empty string
|
# empty string evaluates to empty string
|
||||||
# (but trips up bashlex)
|
# (but trips up bashlex)
|
||||||
@@ -16,7 +20,7 @@ def evaluate(value, environment):
|
|||||||
command_node = bashlex.parsesingle(value)
|
command_node = bashlex.parsesingle(value)
|
||||||
|
|
||||||
if len(command_node.parts) != 1:
|
if len(command_node.parts) != 1:
|
||||||
raise ValueError('"%s" has too many parts' % value)
|
raise ValueError(f'"{value}" has too many parts')
|
||||||
|
|
||||||
value_word_node = command_node.parts[0]
|
value_word_node = command_node.parts[0]
|
||||||
|
|
||||||
@@ -26,7 +30,7 @@ def evaluate(value, environment):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def evaluate_node(node, context):
|
def evaluate_node(node: bashlex.ast.node, context: NodeExecutionContext) -> str:
|
||||||
if node.kind == 'word':
|
if node.kind == 'word':
|
||||||
return evaluate_word_node(node, context=context)
|
return evaluate_word_node(node, context=context)
|
||||||
elif node.kind == 'commandsubstitution':
|
elif node.kind == 'commandsubstitution':
|
||||||
@@ -34,10 +38,10 @@ def evaluate_node(node, context):
|
|||||||
elif node.kind == 'parameter':
|
elif node.kind == 'parameter':
|
||||||
return evaluate_parameter_node(node, context=context)
|
return evaluate_parameter_node(node, context=context)
|
||||||
else:
|
else:
|
||||||
raise ValueError('Unsupported bash construct: "%s"' % node.word)
|
raise ValueError(f'Unsupported bash construct: "{node.word}"')
|
||||||
|
|
||||||
|
|
||||||
def evaluate_word_node(node, context):
|
def evaluate_word_node(node: bashlex.ast.node, context: NodeExecutionContext) -> str:
|
||||||
word_start = node.pos[0]
|
word_start = node.pos[0]
|
||||||
word_end = node.pos[1]
|
word_end = node.pos[1]
|
||||||
word_string = context.input[word_start:word_end]
|
word_string = context.input[word_start:word_end]
|
||||||
@@ -49,22 +53,22 @@ def evaluate_word_node(node, context):
|
|||||||
|
|
||||||
# Set all the characters in the part to None
|
# Set all the characters in the part to None
|
||||||
for i in range(part_start, part_end):
|
for i in range(part_start, part_end):
|
||||||
letters[i] = None
|
letters[i] = ''
|
||||||
|
|
||||||
letters[part_start] = evaluate_node(part, context=context)
|
letters[part_start] = evaluate_node(part, context=context)
|
||||||
|
|
||||||
# remove the None letters and concat
|
# remove the None letters and concat
|
||||||
value = ''.join(l for l in letters if l is not None)
|
value = ''.join(letters)
|
||||||
|
|
||||||
# apply bash-like quotes/whitespace treatment
|
# apply bash-like quotes/whitespace treatment
|
||||||
return ' '.join(word.strip() for word in shlex.split(value))
|
return ' '.join(word.strip() for word in shlex.split(value))
|
||||||
|
|
||||||
|
|
||||||
def evaluate_command_node(node, context):
|
def evaluate_command_node(node: bashlex.ast.node, context: NodeExecutionContext) -> str:
|
||||||
words = [evaluate_node(part, context=context) for part in node.parts]
|
words = [evaluate_node(part, context=context) for part in node.parts]
|
||||||
command = ' '.join(words)
|
command = ' '.join(words)
|
||||||
return subprocess.check_output(shlex.split(command), env=context.environment, universal_newlines=True)
|
return subprocess.check_output(shlex.split(command), env=context.environment, universal_newlines=True)
|
||||||
|
|
||||||
|
|
||||||
def evaluate_parameter_node(node, context):
|
def evaluate_parameter_node(node: bashlex.ast.node, context: NodeExecutionContext) -> str:
|
||||||
return context.environment.get(node.value, '')
|
return context.environment.get(node.value, '')
|
||||||
|
|||||||
+21
-19
@@ -1,4 +1,6 @@
|
|||||||
import bashlex
|
import bashlex # type: ignore
|
||||||
|
|
||||||
|
from typing import Dict, List
|
||||||
|
|
||||||
from . import bashlex_eval
|
from . import bashlex_eval
|
||||||
|
|
||||||
@@ -7,13 +9,7 @@ class EnvironmentParseError(Exception):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def parse_environment(env_string):
|
def split_env_items(env_string: str) -> List[str]:
|
||||||
env_items = split_env_items(env_string)
|
|
||||||
assignments = [EnvironmentAssignment(item) for item in env_items]
|
|
||||||
return ParsedEnvironment(assignments=assignments)
|
|
||||||
|
|
||||||
|
|
||||||
def split_env_items(env_string):
|
|
||||||
'''Splits space-separated variable assignments into a list of individual assignments.
|
'''Splits space-separated variable assignments into a list of individual assignments.
|
||||||
|
|
||||||
>>> split_env_items('VAR=abc')
|
>>> split_env_items('VAR=abc')
|
||||||
@@ -43,29 +39,29 @@ def split_env_items(env_string):
|
|||||||
|
|
||||||
|
|
||||||
class EnvironmentAssignment:
|
class EnvironmentAssignment:
|
||||||
def __init__(self, assignment):
|
def __init__(self, assignment: str):
|
||||||
name, equals, value = assignment.partition('=')
|
name, equals, value = assignment.partition('=')
|
||||||
if not equals:
|
if not equals:
|
||||||
raise EnvironmentParseError(assignment)
|
raise EnvironmentParseError(assignment)
|
||||||
self.name = name
|
self.name = name
|
||||||
self.value = value
|
self.value = value
|
||||||
|
|
||||||
def evaluated_value(self, environment):
|
def evaluated_value(self, environment: Dict[str, str]) -> str:
|
||||||
'''Returns the value of this assignment, as evaluated in the environment'''
|
'''Returns the value of this assignment, as evaluated in the environment'''
|
||||||
return bashlex_eval.evaluate(self.value, environment=environment)
|
return bashlex_eval.evaluate(self.value, environment=environment)
|
||||||
|
|
||||||
def as_shell_assignment(self):
|
def as_shell_assignment(self) -> str:
|
||||||
return 'export %s=%s' % (self.name, self.value)
|
return f'export {self.name}={self.value}'
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self) -> str:
|
||||||
return '%s=%s' % (self.name, self.value)
|
return f'{self.name}={self.value}'
|
||||||
|
|
||||||
|
|
||||||
class ParsedEnvironment:
|
class ParsedEnvironment:
|
||||||
def __init__(self, assignments):
|
def __init__(self, assignments: List[EnvironmentAssignment]):
|
||||||
self.assignments = assignments
|
self.assignments = assignments
|
||||||
|
|
||||||
def as_dictionary(self, prev_environment):
|
def as_dictionary(self, prev_environment: Dict[str, str]) -> Dict[str, str]:
|
||||||
environment = prev_environment.copy()
|
environment = prev_environment.copy()
|
||||||
|
|
||||||
for assignment in self.assignments:
|
for assignment in self.assignments:
|
||||||
@@ -74,8 +70,14 @@ class ParsedEnvironment:
|
|||||||
|
|
||||||
return environment
|
return environment
|
||||||
|
|
||||||
def as_shell_commands(self):
|
def as_shell_commands(self) -> List[str]:
|
||||||
return [a.as_shell_assignment() for a in self.assignments]
|
return [a.as_shell_assignment() for a in self.assignments]
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self) -> str:
|
||||||
return 'ParsedEnvironment(%r)' % [repr(a) for a in self.assignments]
|
return f'ParsedEnvironment({[repr(a) for a in self.assignments]!r})'
|
||||||
|
|
||||||
|
|
||||||
|
def parse_environment(env_string: str) -> ParsedEnvironment:
|
||||||
|
env_items = split_env_items(env_string)
|
||||||
|
assignments = [EnvironmentAssignment(item) for item in env_items]
|
||||||
|
return ParsedEnvironment(assignments=assignments)
|
||||||
|
|||||||
+17
-9
@@ -5,23 +5,25 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
import textwrap
|
import textwrap
|
||||||
import uuid
|
import uuid
|
||||||
from collections import namedtuple
|
|
||||||
|
from typing import List, NamedTuple, Optional, Union
|
||||||
|
|
||||||
from .util import (
|
from .util import (
|
||||||
|
BuildOptions,
|
||||||
|
BuildSelector,
|
||||||
get_build_verbosity_extra_flags,
|
get_build_verbosity_extra_flags,
|
||||||
prepare_command,
|
prepare_command,
|
||||||
BuildOptions
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def call(args, input=None, universal_newlines=False):
|
def call(args: List[str], input: Optional[Union[str, bytes]] = None, universal_newlines: bool = False) -> None:
|
||||||
print('+ ' + ' '.join(shlex.quote(a) for a in args))
|
print('+ ' + ' '.join(shlex.quote(a) for a in args))
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
args, input=input, universal_newlines=universal_newlines, check=True
|
args, input=input, universal_newlines=universal_newlines, check=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def matches_platform(identifier):
|
def matches_platform(identifier: str) -> bool:
|
||||||
pm = platform.machine()
|
pm = platform.machine()
|
||||||
if pm == "x86_64":
|
if pm == "x86_64":
|
||||||
# x86_64 machines can run i686 docker containers
|
# x86_64 machines can run i686 docker containers
|
||||||
@@ -42,8 +44,13 @@ def matches_platform(identifier):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def get_python_configurations(build_selector):
|
class PythonConfiguration(NamedTuple):
|
||||||
PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'identifier', 'path'])
|
version: str
|
||||||
|
identifier: str
|
||||||
|
path: str
|
||||||
|
|
||||||
|
|
||||||
|
def get_python_configurations(build_selector: BuildSelector) -> List[PythonConfiguration]:
|
||||||
python_configurations = [
|
python_configurations = [
|
||||||
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-cp27m'),
|
||||||
PythonConfiguration(version='2.7', identifier='cp27-manylinux_x86_64', path='/opt/python/cp27-cp27mu'),
|
PythonConfiguration(version='2.7', identifier='cp27-manylinux_x86_64', path='/opt/python/cp27-cp27mu'),
|
||||||
@@ -76,7 +83,7 @@ def get_python_configurations(build_selector):
|
|||||||
return [c for c in python_configurations if matches_platform(c.identifier) and build_selector(c.identifier)]
|
return [c for c in python_configurations if matches_platform(c.identifier) and build_selector(c.identifier)]
|
||||||
|
|
||||||
|
|
||||||
def build(options: BuildOptions):
|
def build(options: BuildOptions) -> None:
|
||||||
try:
|
try:
|
||||||
subprocess.check_call(['docker', '--version'])
|
subprocess.check_call(['docker', '--version'])
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -86,6 +93,7 @@ def build(options: BuildOptions):
|
|||||||
file=sys.stderr)
|
file=sys.stderr)
|
||||||
exit(2)
|
exit(2)
|
||||||
|
|
||||||
|
assert options.manylinux_images is not None
|
||||||
python_configurations = get_python_configurations(options.build_selector)
|
python_configurations = get_python_configurations(options.build_selector)
|
||||||
platforms = [
|
platforms = [
|
||||||
('cp', 'manylinux_x86_64', options.manylinux_images['x86_64']),
|
('cp', 'manylinux_x86_64', options.manylinux_images['x86_64']),
|
||||||
@@ -106,7 +114,7 @@ def build(options: BuildOptions):
|
|||||||
if not platform_configs:
|
if not platform_configs:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
container_name = 'cibuildwheel-{}'.format(uuid.uuid4())
|
container_name = f'cibuildwheel-{uuid.uuid4()}'
|
||||||
|
|
||||||
try:
|
try:
|
||||||
shell_cmd = ['linux32', '/bin/bash'] if platform_tag.endswith("i686") else ['/bin/bash']
|
shell_cmd = ['linux32', '/bin/bash'] if platform_tag.endswith("i686") else ['/bin/bash']
|
||||||
@@ -274,7 +282,7 @@ def build(options: BuildOptions):
|
|||||||
call(['docker', 'rm', '--force', '-v', container_name])
|
call(['docker', 'rm', '--force', '-v', container_name])
|
||||||
|
|
||||||
|
|
||||||
def troubleshoot(package_dir, error):
|
def troubleshoot(package_dir: str, error: Exception) -> None:
|
||||||
if (isinstance(error, subprocess.CalledProcessError) and 'exec' in error.cmd):
|
if (isinstance(error, subprocess.CalledProcessError) and 'exec' in error.cmd):
|
||||||
# the bash script failed
|
# the bash script failed
|
||||||
print('Checking for common errors...')
|
print('Checking for common errors...')
|
||||||
|
|||||||
+25
-17
@@ -4,37 +4,45 @@ import shutil
|
|||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
from collections import namedtuple
|
|
||||||
from glob import glob
|
from glob import glob
|
||||||
|
|
||||||
|
from typing import Dict, List, Optional, NamedTuple, Union
|
||||||
|
|
||||||
|
from .environment import ParsedEnvironment
|
||||||
from .util import (
|
from .util import (
|
||||||
|
BuildOptions,
|
||||||
|
BuildSelector,
|
||||||
download,
|
download,
|
||||||
get_build_verbosity_extra_flags,
|
get_build_verbosity_extra_flags,
|
||||||
prepare_command,
|
|
||||||
get_pip_script,
|
get_pip_script,
|
||||||
BuildOptions
|
prepare_command,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def call(args, env=None, cwd=None, shell=False):
|
def call(args: Union[str, List[str]], env: Optional[Dict[str, str]] = None, cwd: Optional[str] = None, shell: bool = False) -> int:
|
||||||
# print the command executing for the logs
|
# print the command executing for the logs
|
||||||
if shell:
|
if shell:
|
||||||
print('+ %s' % args)
|
print(f'+ {args}')
|
||||||
else:
|
else:
|
||||||
print('+ ' + ' '.join(shlex.quote(a) for a in args))
|
print('+ ' + ' '.join(shlex.quote(a) for a in args))
|
||||||
|
|
||||||
return subprocess.check_call(args, env=env, cwd=cwd, shell=shell)
|
return subprocess.check_call(args, env=env, cwd=cwd, shell=shell)
|
||||||
|
|
||||||
|
|
||||||
def get_python_configurations(build_selector):
|
class PythonConfiguration(NamedTuple):
|
||||||
PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'identifier', 'url'])
|
version: str
|
||||||
|
identifier: str
|
||||||
|
url: str
|
||||||
|
|
||||||
|
|
||||||
|
def get_python_configurations(build_selector: BuildSelector) -> List[PythonConfiguration]:
|
||||||
python_configurations = [
|
python_configurations = [
|
||||||
# CPython
|
# CPython
|
||||||
PythonConfiguration(version='2.7', identifier='cp27-macosx_x86_64', url='https://www.python.org/ftp/python/2.7.18/python-2.7.18-macosx10.9.pkg'),
|
PythonConfiguration(version='2.7', identifier='cp27-macosx_x86_64', url='https://www.python.org/ftp/python/2.7.18/python-2.7.18-macosx10.9.pkg'),
|
||||||
PythonConfiguration(version='3.5', identifier='cp35-macosx_x86_64', url='https://www.python.org/ftp/python/3.5.4/python-3.5.4-macosx10.6.pkg'),
|
PythonConfiguration(version='3.5', identifier='cp35-macosx_x86_64', url='https://www.python.org/ftp/python/3.5.4/python-3.5.4-macosx10.6.pkg'),
|
||||||
PythonConfiguration(version='3.6', identifier='cp36-macosx_x86_64', url='https://www.python.org/ftp/python/3.6.8/python-3.6.8-macosx10.9.pkg'),
|
PythonConfiguration(version='3.6', identifier='cp36-macosx_x86_64', url='https://www.python.org/ftp/python/3.6.8/python-3.6.8-macosx10.9.pkg'),
|
||||||
PythonConfiguration(version='3.7', identifier='cp37-macosx_x86_64', url='https://www.python.org/ftp/python/3.7.7/python-3.7.7-macosx10.9.pkg'),
|
PythonConfiguration(version='3.7', identifier='cp37-macosx_x86_64', url='https://www.python.org/ftp/python/3.7.7/python-3.7.7-macosx10.9.pkg'),
|
||||||
PythonConfiguration(version='3.8', identifier='cp38-macosx_x86_64', url='https://www.python.org/ftp/python/3.8.2/python-3.8.2-macosx10.9.pkg'),
|
PythonConfiguration(version='3.8', identifier='cp38-macosx_x86_64', url='https://www.python.org/ftp/python/3.8.3/python-3.8.3-macosx10.9.pkg'),
|
||||||
# PyPy
|
# PyPy
|
||||||
PythonConfiguration(version='2.7', identifier='pp27-macosx_x86_64', url='https://downloads.python.org/pypy/pypy2.7-v7.3.1-osx64.tar.bz2'),
|
PythonConfiguration(version='2.7', identifier='pp27-macosx_x86_64', url='https://downloads.python.org/pypy/pypy2.7-v7.3.1-osx64.tar.bz2'),
|
||||||
PythonConfiguration(version='3.6', identifier='pp36-macosx_x86_64', url='https://downloads.python.org/pypy/pypy3.6-v7.3.1-osx64.tar.bz2'),
|
PythonConfiguration(version='3.6', identifier='pp36-macosx_x86_64', url='https://downloads.python.org/pypy/pypy3.6-v7.3.1-osx64.tar.bz2'),
|
||||||
@@ -47,7 +55,7 @@ def get_python_configurations(build_selector):
|
|||||||
SYMLINKS_DIR = '/tmp/cibw_bin'
|
SYMLINKS_DIR = '/tmp/cibw_bin'
|
||||||
|
|
||||||
|
|
||||||
def make_symlinks(installation_bin_path, python_executable, pip_executable):
|
def make_symlinks(installation_bin_path: str, python_executable: str, pip_executable: str) -> None:
|
||||||
assert os.path.exists(os.path.join(installation_bin_path, python_executable))
|
assert os.path.exists(os.path.join(installation_bin_path, python_executable))
|
||||||
|
|
||||||
# Python bin folders on Mac don't symlink `python3` to `python`, and neither
|
# Python bin folders on Mac don't symlink `python3` to `python`, and neither
|
||||||
@@ -62,11 +70,11 @@ def make_symlinks(installation_bin_path, python_executable, pip_executable):
|
|||||||
os.symlink(os.path.join(installation_bin_path, pip_executable), os.path.join(SYMLINKS_DIR, 'pip'))
|
os.symlink(os.path.join(installation_bin_path, pip_executable), os.path.join(SYMLINKS_DIR, 'pip'))
|
||||||
|
|
||||||
|
|
||||||
def install_cpython(version, url):
|
def install_cpython(version: str, url: str) -> str:
|
||||||
installed_system_packages = subprocess.check_output(['pkgutil', '--pkgs'], universal_newlines=True).splitlines()
|
installed_system_packages = subprocess.check_output(['pkgutil', '--pkgs'], universal_newlines=True).splitlines()
|
||||||
|
|
||||||
# if this version of python isn't installed, get it from python.org and install
|
# if this version of python isn't installed, get it from python.org and install
|
||||||
python_package_identifier = 'org.python.Python.PythonFramework-{}'.format(version)
|
python_package_identifier = f'org.python.Python.PythonFramework-{version}'
|
||||||
if python_package_identifier not in installed_system_packages:
|
if python_package_identifier not in installed_system_packages:
|
||||||
# download the pkg
|
# download the pkg
|
||||||
download(url, '/tmp/Python.pkg')
|
download(url, '/tmp/Python.pkg')
|
||||||
@@ -74,11 +82,11 @@ def install_cpython(version, url):
|
|||||||
call(['sudo', 'installer', '-pkg', '/tmp/Python.pkg', '-target', '/'])
|
call(['sudo', 'installer', '-pkg', '/tmp/Python.pkg', '-target', '/'])
|
||||||
# patch open ssl
|
# patch open ssl
|
||||||
if version == '3.5':
|
if version == '3.5':
|
||||||
open_ssl_patch_url = 'https://github.com/mayeut/patch-macos-python-openssl/releases/download/v1.0.2u/patch-macos-python-%s-openssl-v1.0.2u.tar.gz' % version
|
open_ssl_patch_url = f'https://github.com/mayeut/patch-macos-python-openssl/releases/download/v1.0.2u/patch-macos-python-{version}-openssl-v1.0.2u.tar.gz'
|
||||||
download(open_ssl_patch_url, '/tmp/python-patch.tar.gz')
|
download(open_ssl_patch_url, '/tmp/python-patch.tar.gz')
|
||||||
call(['sudo', 'tar', '-C', '/Library/Frameworks/Python.framework/Versions/{}/'.format(version), '-xmf', '/tmp/python-patch.tar.gz'])
|
call(['sudo', 'tar', '-C', f'/Library/Frameworks/Python.framework/Versions/{version}/', '-xmf', '/tmp/python-patch.tar.gz'])
|
||||||
|
|
||||||
installation_bin_path = '/Library/Frameworks/Python.framework/Versions/{}/bin'.format(version)
|
installation_bin_path = f'/Library/Frameworks/Python.framework/Versions/{version}/bin'
|
||||||
python_executable = 'python3' if version[0] == '3' else 'python'
|
python_executable = 'python3' if version[0] == '3' else 'python'
|
||||||
pip_executable = 'pip3' if version[0] == '3' else 'pip'
|
pip_executable = 'pip3' if version[0] == '3' else 'pip'
|
||||||
make_symlinks(installation_bin_path, python_executable, pip_executable)
|
make_symlinks(installation_bin_path, python_executable, pip_executable)
|
||||||
@@ -86,7 +94,7 @@ def install_cpython(version, url):
|
|||||||
return installation_bin_path
|
return installation_bin_path
|
||||||
|
|
||||||
|
|
||||||
def install_pypy(version, url):
|
def install_pypy(version: str, url: str) -> str:
|
||||||
pypy_tar_bz2 = url.rsplit('/', 1)[-1]
|
pypy_tar_bz2 = url.rsplit('/', 1)[-1]
|
||||||
assert pypy_tar_bz2.endswith(".tar.bz2")
|
assert pypy_tar_bz2.endswith(".tar.bz2")
|
||||||
pypy_base_filename = os.path.splitext(os.path.splitext(pypy_tar_bz2)[0])[0]
|
pypy_base_filename = os.path.splitext(os.path.splitext(pypy_tar_bz2)[0])[0]
|
||||||
@@ -103,7 +111,7 @@ def install_pypy(version, url):
|
|||||||
return installation_bin_path
|
return installation_bin_path
|
||||||
|
|
||||||
|
|
||||||
def setup_python(python_configuration, dependency_constraint_flags, environment):
|
def setup_python(python_configuration: PythonConfiguration, dependency_constraint_flags: List[str], environment: ParsedEnvironment) -> Dict[str, str]:
|
||||||
if python_configuration.identifier.startswith('cp'):
|
if python_configuration.identifier.startswith('cp'):
|
||||||
installation_bin_path = install_cpython(python_configuration.version, python_configuration.url)
|
installation_bin_path = install_cpython(python_configuration.version, python_configuration.url)
|
||||||
elif python_configuration.identifier.startswith('pp'):
|
elif python_configuration.identifier.startswith('pp'):
|
||||||
@@ -161,7 +169,7 @@ def setup_python(python_configuration, dependency_constraint_flags, environment)
|
|||||||
return env
|
return env
|
||||||
|
|
||||||
|
|
||||||
def build(options: BuildOptions):
|
def build(options: BuildOptions) -> None:
|
||||||
temp_dir = tempfile.mkdtemp(prefix='cibuildwheel')
|
temp_dir = tempfile.mkdtemp(prefix='cibuildwheel')
|
||||||
built_wheel_dir = os.path.join(temp_dir, 'built_wheel')
|
built_wheel_dir = os.path.join(temp_dir, 'built_wheel')
|
||||||
repaired_wheel_dir = os.path.join(temp_dir, 'repaired_wheel')
|
repaired_wheel_dir = os.path.join(temp_dir, 'repaired_wheel')
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#
|
#
|
||||||
# bin/update_constraints.py
|
# bin/update_constraints.py
|
||||||
#
|
#
|
||||||
appdirs==1.4.3 # via virtualenv
|
appdirs==1.4.4 # via virtualenv
|
||||||
configparser==4.0.2 # via importlib-metadata
|
configparser==4.0.2 # via importlib-metadata
|
||||||
contextlib2==0.6.0.post1 # via importlib-metadata, importlib-resources, virtualenv
|
contextlib2==0.6.0.post1 # via importlib-metadata, importlib-resources, virtualenv
|
||||||
delocate==0.8.0 # via -r cibuildwheel/resources/constraints.in
|
delocate==0.8.0 # via -r cibuildwheel/resources/constraints.in
|
||||||
@@ -15,12 +15,12 @@ importlib-resources==1.5.0 # via virtualenv
|
|||||||
pathlib2==2.3.5 # via importlib-metadata, importlib-resources, virtualenv
|
pathlib2==2.3.5 # via importlib-metadata, importlib-resources, virtualenv
|
||||||
scandir==1.10.0 # via pathlib2
|
scandir==1.10.0 # via pathlib2
|
||||||
singledispatch==3.4.0.3 # via importlib-resources
|
singledispatch==3.4.0.3 # via importlib-resources
|
||||||
six==1.14.0 # via pathlib2, virtualenv
|
six==1.15.0 # via pathlib2, virtualenv
|
||||||
typing==3.7.4.1 # via importlib-resources
|
typing==3.7.4.1 # via importlib-resources
|
||||||
virtualenv==20.0.18 # via -r cibuildwheel/resources/constraints.in
|
virtualenv==20.0.21 # via -r cibuildwheel/resources/constraints.in
|
||||||
wheel==0.34.2 # via -r cibuildwheel/resources/constraints.in, delocate
|
wheel==0.34.2 # via -r cibuildwheel/resources/constraints.in, delocate
|
||||||
zipp==1.2.0 # via importlib-metadata, importlib-resources
|
zipp==1.2.0 # via importlib-metadata, importlib-resources
|
||||||
|
|
||||||
# The following packages are considered to be unsafe in a requirements file:
|
# The following packages are considered to be unsafe in a requirements file:
|
||||||
pip==20.1 # via -r cibuildwheel/resources/constraints.in
|
pip==20.1.1 # via -r cibuildwheel/resources/constraints.in
|
||||||
setuptools==44.1.0 # via -r cibuildwheel/resources/constraints.in
|
setuptools==44.1.0 # via -r cibuildwheel/resources/constraints.in
|
||||||
|
|||||||
@@ -4,17 +4,17 @@
|
|||||||
#
|
#
|
||||||
# bin/update_constraints.py
|
# bin/update_constraints.py
|
||||||
#
|
#
|
||||||
appdirs==1.4.3 # via virtualenv
|
appdirs==1.4.4 # via virtualenv
|
||||||
delocate==0.8.0 # via -r cibuildwheel/resources/constraints.in
|
delocate==0.8.0 # via -r cibuildwheel/resources/constraints.in
|
||||||
distlib==0.3.0 # via virtualenv
|
distlib==0.3.0 # via virtualenv
|
||||||
filelock==3.0.12 # via virtualenv
|
filelock==3.0.12 # via virtualenv
|
||||||
importlib-metadata==1.6.0 # via importlib-resources, virtualenv
|
importlib-metadata==1.6.0 # via importlib-resources, virtualenv
|
||||||
importlib-resources==1.5.0 # via virtualenv
|
importlib-resources==1.5.0 # via virtualenv
|
||||||
six==1.14.0 # via virtualenv
|
six==1.15.0 # via virtualenv
|
||||||
virtualenv==20.0.18 # via -r cibuildwheel/resources/constraints.in
|
virtualenv==20.0.21 # via -r cibuildwheel/resources/constraints.in
|
||||||
wheel==0.34.2 # via -r cibuildwheel/resources/constraints.in, delocate
|
wheel==0.34.2 # via -r cibuildwheel/resources/constraints.in, delocate
|
||||||
zipp==1.2.0 # via importlib-metadata, importlib-resources
|
zipp==1.2.0 # via importlib-metadata, importlib-resources
|
||||||
|
|
||||||
# The following packages are considered to be unsafe in a requirements file:
|
# The following packages are considered to be unsafe in a requirements file:
|
||||||
pip==20.1 # via -r cibuildwheel/resources/constraints.in
|
pip==20.1.1 # via -r cibuildwheel/resources/constraints.in
|
||||||
setuptools==46.1.3 # via -r cibuildwheel/resources/constraints.in
|
setuptools==46.4.0 # via -r cibuildwheel/resources/constraints.in
|
||||||
|
|||||||
@@ -4,17 +4,17 @@
|
|||||||
#
|
#
|
||||||
# bin/update_constraints.py
|
# bin/update_constraints.py
|
||||||
#
|
#
|
||||||
appdirs==1.4.3 # via virtualenv
|
appdirs==1.4.4 # via virtualenv
|
||||||
delocate==0.8.0 # via -r cibuildwheel/resources/constraints.in
|
delocate==0.8.0 # via -r cibuildwheel/resources/constraints.in
|
||||||
distlib==0.3.0 # via virtualenv
|
distlib==0.3.0 # via virtualenv
|
||||||
filelock==3.0.12 # via virtualenv
|
filelock==3.0.12 # via virtualenv
|
||||||
importlib-metadata==1.6.0 # via importlib-resources, virtualenv
|
importlib-metadata==1.6.0 # via importlib-resources, virtualenv
|
||||||
importlib-resources==1.5.0 # via virtualenv
|
importlib-resources==1.5.0 # via virtualenv
|
||||||
six==1.14.0 # via virtualenv
|
six==1.15.0 # via virtualenv
|
||||||
virtualenv==20.0.18 # via -r cibuildwheel/resources/constraints.in
|
virtualenv==20.0.21 # via -r cibuildwheel/resources/constraints.in
|
||||||
wheel==0.34.2 # via -r cibuildwheel/resources/constraints.in, delocate
|
wheel==0.34.2 # via -r cibuildwheel/resources/constraints.in, delocate
|
||||||
zipp==3.1.0 # via importlib-metadata, importlib-resources
|
zipp==3.1.0 # via importlib-metadata, importlib-resources
|
||||||
|
|
||||||
# The following packages are considered to be unsafe in a requirements file:
|
# The following packages are considered to be unsafe in a requirements file:
|
||||||
pip==20.1 # via -r cibuildwheel/resources/constraints.in
|
pip==20.1.1 # via -r cibuildwheel/resources/constraints.in
|
||||||
setuptools==46.1.3 # via -r cibuildwheel/resources/constraints.in
|
setuptools==46.4.0 # via -r cibuildwheel/resources/constraints.in
|
||||||
|
|||||||
@@ -4,16 +4,16 @@
|
|||||||
#
|
#
|
||||||
# bin/update_constraints.py
|
# bin/update_constraints.py
|
||||||
#
|
#
|
||||||
appdirs==1.4.3 # via virtualenv
|
appdirs==1.4.4 # via virtualenv
|
||||||
delocate==0.8.0 # via -r cibuildwheel/resources/constraints.in
|
delocate==0.8.0 # via -r cibuildwheel/resources/constraints.in
|
||||||
distlib==0.3.0 # via virtualenv
|
distlib==0.3.0 # via virtualenv
|
||||||
filelock==3.0.12 # via virtualenv
|
filelock==3.0.12 # via virtualenv
|
||||||
importlib-metadata==1.6.0 # via virtualenv
|
importlib-metadata==1.6.0 # via virtualenv
|
||||||
six==1.14.0 # via virtualenv
|
six==1.15.0 # via virtualenv
|
||||||
virtualenv==20.0.18 # via -r cibuildwheel/resources/constraints.in
|
virtualenv==20.0.21 # via -r cibuildwheel/resources/constraints.in
|
||||||
wheel==0.34.2 # via -r cibuildwheel/resources/constraints.in, delocate
|
wheel==0.34.2 # via -r cibuildwheel/resources/constraints.in, delocate
|
||||||
zipp==3.1.0 # via importlib-metadata
|
zipp==3.1.0 # via importlib-metadata
|
||||||
|
|
||||||
# The following packages are considered to be unsafe in a requirements file:
|
# The following packages are considered to be unsafe in a requirements file:
|
||||||
pip==20.1 # via -r cibuildwheel/resources/constraints.in
|
pip==20.1.1 # via -r cibuildwheel/resources/constraints.in
|
||||||
setuptools==46.1.3 # via -r cibuildwheel/resources/constraints.in
|
setuptools==46.4.0 # via -r cibuildwheel/resources/constraints.in
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
[x86_64]
|
[x86_64]
|
||||||
manylinux1 = quay.io/pypa/manylinux1_x86_64:2020-04-25-37c204c
|
manylinux1 = quay.io/pypa/manylinux1_x86_64:2020-05-25-b56702d
|
||||||
manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2020-04-29-0e1afc5
|
manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2020-05-20-c7bbd11
|
||||||
manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2020-05-01-b37d76b
|
manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2020-05-17-2f8ac3b
|
||||||
|
|
||||||
[i686]
|
[i686]
|
||||||
manylinux1 = quay.io/pypa/manylinux1_i686:2020-04-25-37c204c
|
manylinux1 = quay.io/pypa/manylinux1_i686:2020-05-25-b56702d
|
||||||
manylinux2010 = quay.io/pypa/manylinux2010_i686:2020-04-29-0e1afc5
|
manylinux2010 = quay.io/pypa/manylinux2010_i686:2020-05-20-c7bbd11
|
||||||
manylinux2014 = quay.io/pypa/manylinux2014_i686:2020-05-01-b37d76b
|
manylinux2014 = quay.io/pypa/manylinux2014_i686:2020-05-17-2f8ac3b
|
||||||
|
|
||||||
[pypy_x86_64]
|
[pypy_x86_64]
|
||||||
manylinux2010 = pypywheels/manylinux2010-pypy_x86_64:2020-04-25-eb2cdff
|
manylinux2010 = pypywheels/manylinux2010-pypy_x86_64:2020-04-25-eb2cdff
|
||||||
|
|
||||||
[aarch64]
|
[aarch64]
|
||||||
manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2020-05-01-b37d76b
|
manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2020-05-17-2f8ac3b
|
||||||
|
|
||||||
[ppc64le]
|
[ppc64le]
|
||||||
manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2020-05-01-b37d76b
|
manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2020-05-17-2f8ac3b
|
||||||
|
|
||||||
[s390x]
|
[s390x]
|
||||||
manylinux2014 = quay.io/pypa/manylinux2014_s390x:2020-05-01-b37d76b
|
manylinux2014 = quay.io/pypa/manylinux2014_s390x:2020-05-17-2f8ac3b
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
--- a/lib_pypy/_sysconfigdata.py Tue Jan 28 22:54:59 2020 +0200
|
|
||||||
+++ b/lib_pypy/_sysconfigdata.py Wed Jan 29 19:21:23 2020 +0200
|
|
||||||
@@ -47,4 +47,5 @@
|
|
||||||
build_time_vars['CC'] += ' -arch %s' % (arch,)
|
|
||||||
if "CXX" in build_time_vars:
|
|
||||||
build_time_vars['CXX'] += ' -arch %s' % (arch,)
|
|
||||||
+ build_time_vars['MACOSX_DEPLOYMENT_TARGET'] = '10.7'
|
|
||||||
+34
-38
@@ -2,12 +2,13 @@ import os
|
|||||||
import urllib.request
|
import urllib.request
|
||||||
from fnmatch import fnmatch
|
from fnmatch import fnmatch
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from typing import NamedTuple, List, Optional, Dict
|
|
||||||
|
from typing import Dict, List, NamedTuple, Optional
|
||||||
|
|
||||||
from .environment import ParsedEnvironment
|
from .environment import ParsedEnvironment
|
||||||
|
|
||||||
|
|
||||||
def prepare_command(command, **kwargs):
|
def prepare_command(command: str, **kwargs: str) -> str:
|
||||||
'''
|
'''
|
||||||
Preprocesses a command by expanding variables like {python}.
|
Preprocesses a command by expanding variables like {python}.
|
||||||
|
|
||||||
@@ -17,7 +18,7 @@ def prepare_command(command, **kwargs):
|
|||||||
return command.format(python='python', pip='pip', **kwargs)
|
return command.format(python='python', pip='pip', **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def get_build_verbosity_extra_flags(level):
|
def get_build_verbosity_extra_flags(level: int) -> List[str]:
|
||||||
if level > 0:
|
if level > 0:
|
||||||
return ['-' + level * 'v']
|
return ['-' + level * 'v']
|
||||||
elif level < 0:
|
elif level < 0:
|
||||||
@@ -27,37 +28,37 @@ def get_build_verbosity_extra_flags(level):
|
|||||||
|
|
||||||
|
|
||||||
class BuildSelector:
|
class BuildSelector:
|
||||||
def __init__(self, build_config, skip_config):
|
def __init__(self, build_config: str, skip_config: str):
|
||||||
self.build_patterns = build_config.split()
|
self.build_patterns = build_config.split()
|
||||||
self.skip_patterns = skip_config.split()
|
self.skip_patterns = skip_config.split()
|
||||||
|
|
||||||
def __call__(self, build_id):
|
def __call__(self, build_id: str) -> bool:
|
||||||
def match_any(patterns):
|
def match_any(patterns: List[str]) -> bool:
|
||||||
return any(fnmatch(build_id, pattern) for pattern in patterns)
|
return any(fnmatch(build_id, pattern) for pattern in patterns)
|
||||||
return match_any(self.build_patterns) and not match_any(self.skip_patterns)
|
return match_any(self.build_patterns) and not match_any(self.skip_patterns)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self) -> str:
|
||||||
return 'BuildSelector({!r} - {!r})'.format(' '.join(self.build_patterns), ' '.join(self.skip_patterns))
|
return f'BuildSelector({" ".join(self.build_patterns)!r} - {" ".join(self.skip_patterns)!r})'
|
||||||
|
|
||||||
|
|
||||||
# Taken from https://stackoverflow.com/a/107717
|
# Taken from https://stackoverflow.com/a/107717
|
||||||
class Unbuffered:
|
class Unbuffered:
|
||||||
def __init__(self, stream):
|
def __init__(self, stream): # type: ignore
|
||||||
self.stream = stream
|
self.stream = stream
|
||||||
|
|
||||||
def write(self, data):
|
def write(self, data): # type: ignore
|
||||||
self.stream.write(data)
|
self.stream.write(data)
|
||||||
self.stream.flush()
|
self.stream.flush()
|
||||||
|
|
||||||
def writelines(self, datas):
|
def writelines(self, datas): # type: ignore
|
||||||
self.stream.writelines(datas)
|
self.stream.writelines(datas)
|
||||||
self.stream.flush()
|
self.stream.flush()
|
||||||
|
|
||||||
def __getattr__(self, attr):
|
def __getattr__(self, attr): # type: ignore
|
||||||
return getattr(self.stream, attr)
|
return getattr(self.stream, attr)
|
||||||
|
|
||||||
|
|
||||||
def download(url, dest):
|
def download(url: str, dest: str) -> None:
|
||||||
print('+ Download ' + url + ' to ' + dest)
|
print('+ Download ' + url + ' to ' + dest)
|
||||||
dest_dir = os.path.dirname(dest)
|
dest_dir = os.path.dirname(dest)
|
||||||
if not os.path.exists(dest_dir):
|
if not os.path.exists(dest_dir):
|
||||||
@@ -82,23 +83,23 @@ def download(url, dest):
|
|||||||
|
|
||||||
|
|
||||||
class DependencyConstraints:
|
class DependencyConstraints:
|
||||||
def __init__(self, base_file_path):
|
def __init__(self, base_file_path: str):
|
||||||
assert os.path.exists(base_file_path)
|
assert os.path.exists(base_file_path)
|
||||||
self.base_file_path = os.path.abspath(base_file_path)
|
self.base_file_path = os.path.abspath(base_file_path)
|
||||||
|
|
||||||
@classmethod
|
@staticmethod
|
||||||
def with_defaults(cls):
|
def with_defaults() -> 'DependencyConstraints':
|
||||||
return cls(
|
return DependencyConstraints(
|
||||||
base_file_path=os.path.join(os.path.dirname(__file__), 'resources', 'constraints.txt')
|
base_file_path=os.path.join(os.path.dirname(__file__), 'resources', 'constraints.txt')
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_for_python_version(self, version):
|
def get_for_python_version(self, version: str) -> str:
|
||||||
version_parts = version.split('.')
|
version_parts = version.split('.')
|
||||||
|
|
||||||
# try to find a version-specific dependency file e.g. if
|
# try to find a version-specific dependency file e.g. if
|
||||||
# ./constraints.txt is the base, look for ./constraints-python27.txt
|
# ./constraints.txt is the base, look for ./constraints-python27.txt
|
||||||
base, ext = os.path.splitext(self.base_file_path)
|
base, ext = os.path.splitext(self.base_file_path)
|
||||||
specific = base + '-python{}{}'.format(version_parts[0], version_parts[1])
|
specific = base + f'-python{version_parts[0]}{version_parts[1]}'
|
||||||
specific_file_path = specific + ext
|
specific_file_path = specific + ext
|
||||||
if os.path.exists(specific_file_path):
|
if os.path.exists(specific_file_path):
|
||||||
return specific_file_path
|
return specific_file_path
|
||||||
@@ -106,26 +107,21 @@ class DependencyConstraints:
|
|||||||
return self.base_file_path
|
return self.base_file_path
|
||||||
|
|
||||||
|
|
||||||
BuildOptions = NamedTuple("BuildOptions", [
|
class BuildOptions(NamedTuple):
|
||||||
("package_dir", str),
|
package_dir: str
|
||||||
("output_dir", str),
|
output_dir: str
|
||||||
("test_command", Optional[str]),
|
test_command: Optional[str]
|
||||||
("test_requires", List[str]),
|
test_requires: List[str]
|
||||||
("test_extras", str),
|
test_extras: str
|
||||||
("before_build", Optional[str]),
|
before_build: Optional[str]
|
||||||
("build_verbosity", int),
|
build_verbosity: int
|
||||||
("build_selector", BuildSelector),
|
build_selector: BuildSelector
|
||||||
("repair_command", str),
|
repair_command: str
|
||||||
("environment", ParsedEnvironment),
|
environment: ParsedEnvironment
|
||||||
("before_test", str),
|
before_test: str
|
||||||
("dependency_constraints", Optional[DependencyConstraints]),
|
dependency_constraints: Optional[DependencyConstraints]
|
||||||
("manylinux_images", Optional[Dict[str, str]]),
|
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'))
|
resources_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), 'resources'))
|
||||||
get_pip_script = os.path.join(resources_dir, 'get-pip.py')
|
get_pip_script = os.path.join(resources_dir, 'get-pip.py')
|
||||||
|
|||||||
+24
-17
@@ -3,16 +3,19 @@ import shutil
|
|||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
from collections import namedtuple
|
|
||||||
from glob import glob
|
from glob import glob
|
||||||
from zipfile import ZipFile
|
from zipfile import ZipFile
|
||||||
|
|
||||||
|
from typing import Dict, List, Optional, NamedTuple
|
||||||
|
|
||||||
|
from .environment import ParsedEnvironment
|
||||||
from .util import (
|
from .util import (
|
||||||
|
BuildOptions,
|
||||||
|
BuildSelector,
|
||||||
download,
|
download,
|
||||||
get_build_verbosity_extra_flags,
|
get_build_verbosity_extra_flags,
|
||||||
prepare_command,
|
|
||||||
get_pip_script,
|
get_pip_script,
|
||||||
BuildOptions
|
prepare_command,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -20,20 +23,26 @@ IS_RUNNING_ON_AZURE = os.path.exists('C:\\hostedtoolcache')
|
|||||||
IS_RUNNING_ON_TRAVIS = os.environ.get('TRAVIS_OS_NAME') == 'windows'
|
IS_RUNNING_ON_TRAVIS = os.environ.get('TRAVIS_OS_NAME') == 'windows'
|
||||||
|
|
||||||
|
|
||||||
def shell(args, env=None, cwd=None):
|
def shell(args: List[str], env: Optional[Dict[str, str]] = None, cwd: Optional[str] = None) -> int:
|
||||||
print('+ ' + ' '.join(args))
|
print('+ ' + ' '.join(args))
|
||||||
return subprocess.check_call(' '.join(args), env=env, cwd=cwd, shell=True)
|
return subprocess.check_call(' '.join(args), env=env, cwd=cwd, shell=True)
|
||||||
|
|
||||||
|
|
||||||
def get_nuget_args(version, arch):
|
def get_nuget_args(version: str, arch: str) -> List[str]:
|
||||||
python_name = 'python' if version[0] == '3' else 'python2'
|
python_name = 'python' if version[0] == '3' else 'python2'
|
||||||
if arch == '32':
|
if arch == '32':
|
||||||
python_name = python_name + 'x86'
|
python_name = python_name + 'x86'
|
||||||
return [python_name, '-Version', version, '-OutputDirectory', 'C:\\cibw\\python']
|
return [python_name, '-Version', version, '-OutputDirectory', 'C:\\cibw\\python']
|
||||||
|
|
||||||
|
|
||||||
def get_python_configurations(build_selector):
|
class PythonConfiguration(NamedTuple):
|
||||||
PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'arch', 'identifier', 'url'])
|
version: str
|
||||||
|
arch: str
|
||||||
|
identifier: str
|
||||||
|
url: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
|
def get_python_configurations(build_selector: BuildSelector) -> List[PythonConfiguration]:
|
||||||
python_configurations = [
|
python_configurations = [
|
||||||
# CPython
|
# CPython
|
||||||
PythonConfiguration(version='2.7.18', arch='32', identifier='cp27-win32', url=None),
|
PythonConfiguration(version='2.7.18', arch='32', identifier='cp27-win32', url=None),
|
||||||
@@ -44,8 +53,8 @@ def get_python_configurations(build_selector):
|
|||||||
PythonConfiguration(version='3.6.8', arch='64', identifier='cp36-win_amd64', url=None),
|
PythonConfiguration(version='3.6.8', arch='64', identifier='cp36-win_amd64', url=None),
|
||||||
PythonConfiguration(version='3.7.7', arch='32', identifier='cp37-win32', url=None),
|
PythonConfiguration(version='3.7.7', arch='32', identifier='cp37-win32', url=None),
|
||||||
PythonConfiguration(version='3.7.7', arch='64', identifier='cp37-win_amd64', url=None),
|
PythonConfiguration(version='3.7.7', arch='64', identifier='cp37-win_amd64', url=None),
|
||||||
PythonConfiguration(version='3.8.2', arch='32', identifier='cp38-win32', url=None),
|
PythonConfiguration(version='3.8.3', arch='32', identifier='cp38-win32', url=None),
|
||||||
PythonConfiguration(version='3.8.2', arch='64', identifier='cp38-win_amd64', url=None),
|
PythonConfiguration(version='3.8.3', arch='64', identifier='cp38-win_amd64', url=None),
|
||||||
# PyPy
|
# PyPy
|
||||||
PythonConfiguration(version='2.7', arch='32', identifier='pp27-win32', url='https://downloads.python.org/pypy/pypy2.7-v7.3.1-win32.zip'),
|
PythonConfiguration(version='2.7', arch='32', identifier='pp27-win32', url='https://downloads.python.org/pypy/pypy2.7-v7.3.1-win32.zip'),
|
||||||
PythonConfiguration(version='3.6', arch='32', identifier='pp36-win32', url='https://downloads.python.org/pypy/pypy3.6-v7.3.1-win32.zip'),
|
PythonConfiguration(version='3.6', arch='32', identifier='pp36-win32', url='https://downloads.python.org/pypy/pypy3.6-v7.3.1-win32.zip'),
|
||||||
@@ -62,19 +71,19 @@ def get_python_configurations(build_selector):
|
|||||||
return python_configurations
|
return python_configurations
|
||||||
|
|
||||||
|
|
||||||
def extract_zip(zip_src, dest):
|
def extract_zip(zip_src: str, dest: str) -> None:
|
||||||
with ZipFile(zip_src) as zip:
|
with ZipFile(zip_src) as zip:
|
||||||
zip.extractall(dest)
|
zip.extractall(dest)
|
||||||
|
|
||||||
|
|
||||||
def install_cpython(version, arch, nuget):
|
def install_cpython(version: str, arch: str, nuget: str) -> str:
|
||||||
nuget_args = get_nuget_args(version, arch)
|
nuget_args = get_nuget_args(version, arch)
|
||||||
installation_path = os.path.join(nuget_args[-1], nuget_args[0] + '.' + version, 'tools')
|
installation_path = os.path.join(nuget_args[-1], nuget_args[0] + '.' + version, 'tools')
|
||||||
shell([nuget, 'install'] + nuget_args)
|
shell([nuget, 'install'] + nuget_args)
|
||||||
return installation_path
|
return installation_path
|
||||||
|
|
||||||
|
|
||||||
def install_pypy(version, arch, url):
|
def install_pypy(version: str, arch: str, url: str) -> str:
|
||||||
assert arch == '32'
|
assert arch == '32'
|
||||||
# Inside the PyPy zip file is a directory with the same name
|
# Inside the PyPy zip file is a directory with the same name
|
||||||
zip_filename = url.rsplit('/', 1)[-1]
|
zip_filename = url.rsplit('/', 1)[-1]
|
||||||
@@ -89,7 +98,7 @@ def install_pypy(version, arch, url):
|
|||||||
return installation_path
|
return installation_path
|
||||||
|
|
||||||
|
|
||||||
def setup_python(python_configuration, dependency_constraint_flags, environment):
|
def setup_python(python_configuration: PythonConfiguration, dependency_constraint_flags: List[str], environment: ParsedEnvironment) -> Dict[str, str]:
|
||||||
nuget = 'C:\\cibw\\nuget.exe'
|
nuget = 'C:\\cibw\\nuget.exe'
|
||||||
if not os.path.exists(nuget):
|
if not os.path.exists(nuget):
|
||||||
download('https://dist.nuget.org/win-x86-commandline/latest/nuget.exe', nuget)
|
download('https://dist.nuget.org/win-x86-commandline/latest/nuget.exe', nuget)
|
||||||
@@ -97,6 +106,7 @@ def setup_python(python_configuration, dependency_constraint_flags, environment)
|
|||||||
if python_configuration.identifier.startswith('cp'):
|
if python_configuration.identifier.startswith('cp'):
|
||||||
installation_path = install_cpython(python_configuration.version, python_configuration.arch, nuget)
|
installation_path = install_cpython(python_configuration.version, python_configuration.arch, nuget)
|
||||||
elif python_configuration.identifier.startswith('pp'):
|
elif python_configuration.identifier.startswith('pp'):
|
||||||
|
assert python_configuration.url is not None
|
||||||
installation_path = install_pypy(python_configuration.version, python_configuration.arch, python_configuration.url)
|
installation_path = install_pypy(python_configuration.version, python_configuration.arch, python_configuration.url)
|
||||||
else:
|
else:
|
||||||
raise ValueError("Unknown Python implementation")
|
raise ValueError("Unknown Python implementation")
|
||||||
@@ -141,7 +151,7 @@ def setup_python(python_configuration, dependency_constraint_flags, environment)
|
|||||||
return env
|
return env
|
||||||
|
|
||||||
|
|
||||||
def build(options: BuildOptions):
|
def build(options: BuildOptions) -> None:
|
||||||
temp_dir = tempfile.mkdtemp(prefix='cibuildwheel')
|
temp_dir = tempfile.mkdtemp(prefix='cibuildwheel')
|
||||||
built_wheel_dir = os.path.join(temp_dir, 'built_wheel')
|
built_wheel_dir = os.path.join(temp_dir, 'built_wheel')
|
||||||
repaired_wheel_dir = os.path.join(temp_dir, 'repaired_wheel')
|
repaired_wheel_dir = os.path.join(temp_dir, 'repaired_wheel')
|
||||||
@@ -198,9 +208,6 @@ def build(options: BuildOptions):
|
|||||||
virtualenv_env = env.copy()
|
virtualenv_env = env.copy()
|
||||||
|
|
||||||
venv_script_path = os.path.join(venv_dir, 'Scripts')
|
venv_script_path = os.path.join(venv_dir, 'Scripts')
|
||||||
if os.path.exists(os.path.join(venv_dir, 'bin')):
|
|
||||||
# pypy2.7 bugfix
|
|
||||||
venv_script_path = os.pathsep.join([venv_script_path, os.path.join(venv_dir, 'bin')])
|
|
||||||
virtualenv_env['PATH'] = os.pathsep.join([
|
virtualenv_env['PATH'] = os.pathsep.join([
|
||||||
venv_script_path,
|
venv_script_path,
|
||||||
virtualenv_env['PATH'],
|
virtualenv_env['PATH'],
|
||||||
|
|||||||
+10
-2
@@ -30,6 +30,14 @@ For more details see https://en.cppreference.com/w/cpp/compiler_support, https:/
|
|||||||
|
|
||||||
## Windows and Python 2.7
|
## Windows and Python 2.7
|
||||||
|
|
||||||
Visual C++ for Python 2.7 does not support modern standards of C++. When building on Appveyor, you will need to either use the "Visual Studio 2017" or "Visual Studio 2019" image, but Python 2.7 is not supported on these images - skip it by setting `CIBW_SKIP=cp27-win*`.
|
Visual C++ for Python 2.7 does not support modern C++ standards (i.e., C++11 and later). When building on Appveyor, you will need to either use the "Visual Studio 2017" or "Visual Studio 2019" image, but Python 2.7 is not supported on these images - skip it by setting `CIBW_SKIP=cp27-win*`.
|
||||||
|
|
||||||
There is an optional workaround for this, though: the pybind11 project argues and shows that it is [possible to compile Python 2.7 extension with a newer compiler](https://pybind11.readthedocs.io/en/stable/faq.html#working-with-ancient-visual-studio-2008-builds-on-windows) and has an example project showing how to do this: https://github.com/pybind/python_example. The main catch is that a user might need to install a newer "Microsoft Visual C++ Redistributable", since the newer C++ standard libraries are not included by default with the Python 2.7 installation.
|
There is an optional workaround for this, though: the pybind11 project argues and shows that it is [possible to compile Python 2.7 extension with a newer compiler](https://pybind11.readthedocs.io/en/stable/faq.html#working-with-ancient-visual-studio-2008-builds-on-windows) and has an example project showing how to do this: https://github.com/pybind/python_example. The main catch is that a user might need to install [a newer "Microsoft Visual C++ Redistributable"](https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads), since the newer C++ standard library's binaries are not included by default with the Python 2.7 installation.
|
||||||
|
|
||||||
|
Forcing `distutils` or `setuptools` to use a more recent version of MSVC that supports modern C++ can be done in the following way:
|
||||||
|
|
||||||
|
1. Set the environment variables `DISTUTILS_USE_SDK=1` and `MSSdk=1`. These two environment variables will tell `distutils`/`setuptools` to not search and set up a build environment that uses Visual C++ for Python 2.7 (aka. MSVC 9).
|
||||||
|
2. Set up the build Visual Studio build environment you want to use, making sure that e.g. `PATH` contains `cl`, `link`, etc.
|
||||||
|
- Usually, this can be done through `vcvarsall.bat x86` or `vcvarsall.bat x64`. The exact location of this file depends on the installation, but the default path for VS 2019 Community (e.g. used in AppVeyor's `Visual Studio 2019` image) is `C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat`. **Note**: `vcvarsall.bat` changes the environment variables, so this cannot be run in a subprocess/subshell and consequently running `vsvarsall.bat` in `CIBW_BEFORE_BUILD` does not have any effect.
|
||||||
|
- In Azure Pipelines, [a `VSBuild` task is available](https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/build/visual-studio-build) and GitHub Actions has [an action `microsoft/setup-msbuild`](https://github.com/microsoft/setup-msbuild) that help setting up the Visual Studio environment.
|
||||||
|
3. Next, call `cibuildwheel`. Unfortunately, MSVC has separate toolchains for compiling 32-bit and 64-bit, so you will need to run `cibuildwheel` twice: once with `CIBW_BUILD=*-win32` after setting up the `x86` build environment, and once with `CIBW_BUILD=*-win_amd64` in a `x64` enviroment (see previous step).
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ stack: python 3.7
|
|||||||
init:
|
init:
|
||||||
- cmd: set PATH=C:\Python37;C:\Python37\Scripts;%PATH%
|
- cmd: set PATH=C:\Python37;C:\Python37\Scripts;%PATH%
|
||||||
|
|
||||||
install: python -m pip install cibuildwheel==1.4.1
|
install: python -m pip install cibuildwheel==1.4.2
|
||||||
|
|
||||||
build_script: python -m cibuildwheel --output-dir wheelhouse
|
build_script: python -m cibuildwheel --output-dir wheelhouse
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ jobs:
|
|||||||
- task: UsePythonVersion@0
|
- task: UsePythonVersion@0
|
||||||
- bash: |
|
- bash: |
|
||||||
python3 -m pip install --upgrade pip
|
python3 -m pip install --upgrade pip
|
||||||
pip3 install cibuildwheel==1.4.1
|
pip3 install cibuildwheel==1.4.2
|
||||||
cibuildwheel --output-dir wheelhouse .
|
cibuildwheel --output-dir wheelhouse .
|
||||||
- task: PublishBuildArtifacts@1
|
- task: PublishBuildArtifacts@1
|
||||||
inputs: {pathtoPublish: 'wheelhouse'}
|
inputs: {pathtoPublish: 'wheelhouse'}
|
||||||
@@ -16,7 +16,7 @@ jobs:
|
|||||||
- task: UsePythonVersion@0
|
- task: UsePythonVersion@0
|
||||||
- bash: |
|
- bash: |
|
||||||
python3 -m pip install --upgrade pip
|
python3 -m pip install --upgrade pip
|
||||||
pip3 install cibuildwheel==1.4.1
|
pip3 install cibuildwheel==1.4.2
|
||||||
cibuildwheel --output-dir wheelhouse .
|
cibuildwheel --output-dir wheelhouse .
|
||||||
- task: PublishBuildArtifacts@1
|
- task: PublishBuildArtifacts@1
|
||||||
inputs: {pathtoPublish: 'wheelhouse'}
|
inputs: {pathtoPublish: 'wheelhouse'}
|
||||||
@@ -29,7 +29,7 @@ jobs:
|
|||||||
displayName: Install Visual C++ for Python 2.7
|
displayName: Install Visual C++ for Python 2.7
|
||||||
- bash: |
|
- bash: |
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install cibuildwheel==1.4.1
|
pip install cibuildwheel==1.4.2
|
||||||
cibuildwheel --output-dir wheelhouse .
|
cibuildwheel --output-dir wheelhouse .
|
||||||
- task: PublishBuildArtifacts@1
|
- task: PublishBuildArtifacts@1
|
||||||
inputs: {pathtoPublish: 'wheelhouse'}
|
inputs: {pathtoPublish: 'wheelhouse'}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ jobs:
|
|||||||
- run:
|
- run:
|
||||||
name: Build the Linux wheels.
|
name: Build the Linux wheels.
|
||||||
command: |
|
command: |
|
||||||
pip3 install --user cibuildwheel==1.4.1
|
pip3 install --user cibuildwheel==1.4.2
|
||||||
cibuildwheel --output-dir wheelhouse
|
cibuildwheel --output-dir wheelhouse
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: wheelhouse/
|
path: wheelhouse/
|
||||||
@@ -25,7 +25,7 @@ jobs:
|
|||||||
- run:
|
- run:
|
||||||
name: Build the OS X wheels.
|
name: Build the OS X wheels.
|
||||||
command: |
|
command: |
|
||||||
pip3 install --user cibuildwheel==1.4.1
|
pip3 install --user cibuildwheel==1.4.2
|
||||||
cibuildwheel --output-dir wheelhouse
|
cibuildwheel --output-dir wheelhouse
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: wheelhouse/
|
path: wheelhouse/
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Install cibuildwheel
|
- name: Install cibuildwheel
|
||||||
run: |
|
run: |
|
||||||
python -m pip install cibuildwheel==1.4.1
|
python -m pip install cibuildwheel==1.4.2
|
||||||
|
|
||||||
- name: Install Visual C++ for Python 2.7
|
- name: Install Visual C++ for Python 2.7
|
||||||
if: startsWith(matrix.os, 'windows')
|
if: startsWith(matrix.os, 'windows')
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ env:
|
|||||||
# Note: TWINE_PASSWORD is set to a PyPI API token in Travis settings
|
# Note: TWINE_PASSWORD is set to a PyPI API token in Travis settings
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- python3 -m pip install cibuildwheel==1.4.1
|
- python3 -m pip install cibuildwheel==1.4.2
|
||||||
|
|
||||||
script:
|
script:
|
||||||
# build the wheels, put them into './wheelhouse'
|
# build the wheels, put them into './wheelhouse'
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ jobs:
|
|||||||
- ln -s /c/Python38/python.exe /c/Python38/python3.exe
|
- ln -s /c/Python38/python.exe /c/Python38/python3.exe
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- python3 -m pip install cibuildwheel==1.4.1
|
- python3 -m pip install cibuildwheel==1.4.2
|
||||||
|
|
||||||
script:
|
script:
|
||||||
# build the wheels, put them into './wheelhouse'
|
# build the wheels, put them into './wheelhouse'
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
language: python
|
language: python
|
||||||
python:
|
python:
|
||||||
- 3.5
|
|
||||||
- 3.6
|
- 3.6
|
||||||
- 3.7
|
- 3.7
|
||||||
- 3.8
|
- 3.8
|
||||||
@@ -56,7 +55,7 @@ jobs:
|
|||||||
- stage: deploy
|
- stage: deploy
|
||||||
name: Build and deploy Linux wheels
|
name: Build and deploy Linux wheels
|
||||||
services: docker
|
services: docker
|
||||||
install: python3 -m pip install cibuildwheel==1.4.1
|
install: python3 -m pip install cibuildwheel==1.4.2
|
||||||
script: python3 -m cibuildwheel --output-dir wheelhouse
|
script: python3 -m cibuildwheel --output-dir wheelhouse
|
||||||
after_success: |
|
after_success: |
|
||||||
python3 -m pip install twine
|
python3 -m pip install twine
|
||||||
@@ -66,7 +65,7 @@ jobs:
|
|||||||
name: Build and deploy macOS wheels
|
name: Build and deploy macOS wheels
|
||||||
os: osx
|
os: osx
|
||||||
language: shell
|
language: shell
|
||||||
install: python3 -m pip install cibuildwheel==1.4.1
|
install: python3 -m pip install cibuildwheel==1.4.2
|
||||||
script: python3 -m cibuildwheel --output-dir wheelhouse
|
script: python3 -m cibuildwheel --output-dir wheelhouse
|
||||||
after_success: |
|
after_success: |
|
||||||
python3 -m pip install twine
|
python3 -m pip install twine
|
||||||
@@ -76,7 +75,7 @@ jobs:
|
|||||||
name: Build and deploy Windows wheels
|
name: Build and deploy Windows wheels
|
||||||
os: windows
|
os: windows
|
||||||
language: shell
|
language: shell
|
||||||
install: python3 -m pip install cibuildwheel==1.4.1
|
install: python3 -m pip install cibuildwheel==1.4.2
|
||||||
script: python3 -m cibuildwheel --output-dir wheelhouse
|
script: python3 -m cibuildwheel --output-dir wheelhouse
|
||||||
after_success: |
|
after_success: |
|
||||||
python3 -m pip install twine
|
python3 -m pip install twine
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[flake8]
|
[flake8]
|
||||||
ignore = E501,W503
|
ignore = E501,W503,E741
|
||||||
application-import-names = cibuildwheel
|
application-import-names = cibuildwheel
|
||||||
exclude =
|
exclude =
|
||||||
cibuildwheel/resources/,
|
cibuildwheel/resources/,
|
||||||
@@ -11,3 +11,30 @@ exclude =
|
|||||||
env??/,
|
env??/,
|
||||||
.venv/,
|
.venv/,
|
||||||
site/
|
site/
|
||||||
|
|
||||||
|
[tool:pytest]
|
||||||
|
junit_family=xunit2
|
||||||
|
|
||||||
|
[mypy]
|
||||||
|
python_version = 3.6
|
||||||
|
files = cibuildwheel,test
|
||||||
|
|
||||||
|
warn_unused_configs = True
|
||||||
|
warn_redundant_casts = True
|
||||||
|
|
||||||
|
[mypy-cibuildwheel]
|
||||||
|
disallow_any_generics = True
|
||||||
|
disallow_subclassing_any = True
|
||||||
|
disallow_untyped_calls = True
|
||||||
|
disallow_untyped_defs = True
|
||||||
|
disallow_incomplete_defs = True
|
||||||
|
check_untyped_defs = True
|
||||||
|
disallow_untyped_decorators = True
|
||||||
|
no_implicit_optional = True
|
||||||
|
warn_unused_ignores = True
|
||||||
|
warn_return_any = True
|
||||||
|
no_implicit_reexport = True
|
||||||
|
strict_equality = True
|
||||||
|
|
||||||
|
[mypy-pytest,setuptools]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ with io.open(os.path.join(this_directory, 'README.md'), encoding='utf-8') as f:
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='cibuildwheel',
|
name='cibuildwheel',
|
||||||
version='1.4.1',
|
version='1.4.2',
|
||||||
install_requires=['bashlex!=0.13'],
|
install_requires=['bashlex!=0.13'],
|
||||||
description="Build Python wheels on CI with minimal configuration.",
|
description="Build Python wheels on CI with minimal configuration.",
|
||||||
long_description=long_description,
|
long_description=long_description,
|
||||||
@@ -28,7 +28,7 @@ setup(
|
|||||||
'cibuildwheel': ['resources/*'],
|
'cibuildwheel': ['resources/*'],
|
||||||
},
|
},
|
||||||
# Supported python versions
|
# Supported python versions
|
||||||
python_requires='>=3.5',
|
python_requires='>=3.6',
|
||||||
keywords='ci wheel packaging pypi travis appveyor macos linux windows',
|
keywords='ci wheel packaging pypi travis appveyor macos linux windows',
|
||||||
classifiers=[
|
classifiers=[
|
||||||
'Intended Audience :: Developers',
|
'Intended Audience :: Developers',
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
import os
|
|
||||||
|
|
||||||
from setuptools import (
|
|
||||||
Extension,
|
|
||||||
setup,
|
|
||||||
)
|
|
||||||
|
|
||||||
if os.environ.get('CIBUILDWHEEL', '0') != '1':
|
|
||||||
raise Exception('CIBUILDWHEEL environment variable is not set to 1')
|
|
||||||
|
|
||||||
setup(
|
|
||||||
name="spam",
|
|
||||||
ext_modules=[Extension('spam', sources=['spam.c'])],
|
|
||||||
version="0.1.0",
|
|
||||||
)
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
#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)
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
import os
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
import utils
|
|
||||||
|
|
||||||
|
|
||||||
def test():
|
|
||||||
project_dir = os.path.dirname(__file__)
|
|
||||||
|
|
||||||
# build and test the wheels
|
|
||||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
|
||||||
'CIBW_TEST_REQUIRES': 'nose',
|
|
||||||
# the 'false ||' bit is to ensure this command runs in a shell on
|
|
||||||
# mac/linux.
|
|
||||||
'CIBW_TEST_COMMAND': 'false || nosetests {project}/test',
|
|
||||||
'CIBW_TEST_COMMAND_WINDOWS': 'COLOR 00 || nosetests {project}/test',
|
|
||||||
})
|
|
||||||
|
|
||||||
# also check that we got the right wheels
|
|
||||||
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
|
||||||
|
|
||||||
|
|
||||||
def test_extras_require():
|
|
||||||
project_dir = os.path.dirname(__file__)
|
|
||||||
|
|
||||||
# build and test the wheels
|
|
||||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
|
||||||
'CIBW_TEST_EXTRAS': 'test',
|
|
||||||
# the 'false ||' bit is to ensure this command runs in a shell on
|
|
||||||
# mac/linux.
|
|
||||||
'CIBW_TEST_COMMAND': 'false || nosetests {project}/test',
|
|
||||||
'CIBW_TEST_COMMAND_WINDOWS': 'COLOR 00 || nosetests {project}/test',
|
|
||||||
})
|
|
||||||
|
|
||||||
# also check that we got the right wheels
|
|
||||||
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
|
||||||
|
|
||||||
|
|
||||||
def test_failing_test(tmp_path):
|
|
||||||
'''Ensure a failing test causes cibuildwheel to error out and exit'''
|
|
||||||
project_dir = os.path.dirname(__file__)
|
|
||||||
|
|
||||||
with pytest.raises(subprocess.CalledProcessError):
|
|
||||||
utils.cibuildwheel_run(project_dir, output_dir=tmp_path, add_env={
|
|
||||||
'CIBW_TEST_COMMAND': 'false',
|
|
||||||
# manylinux1 has a version of bash that's been shown to have
|
|
||||||
# problems with this, so let's check that.
|
|
||||||
'CIBW_MANYLINUX_I686_IMAGE': 'manylinux1',
|
|
||||||
'CIBW_MANYLINUX_X86_64_IMAGE': 'manylinux1',
|
|
||||||
})
|
|
||||||
assert len(os.listdir(str(tmp_path))) == 0
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
from setuptools import (
|
|
||||||
Extension,
|
|
||||||
setup,
|
|
||||||
)
|
|
||||||
|
|
||||||
setup(
|
|
||||||
name="spam",
|
|
||||||
ext_modules=[Extension('spam', sources=['spam.c'])],
|
|
||||||
extras_require={'test': ['nose']},
|
|
||||||
version="0.1.0",
|
|
||||||
)
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
#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)
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
from __future__ import print_function
|
|
||||||
import os
|
|
||||||
import platform
|
|
||||||
import sys
|
|
||||||
import struct
|
|
||||||
from unittest import TestCase
|
|
||||||
|
|
||||||
import spam
|
|
||||||
|
|
||||||
|
|
||||||
def path_contains(parent, child):
|
|
||||||
''' returns True if `child` is inside `parent`.
|
|
||||||
|
|
||||||
Works around path-comparison bugs caused by short-paths on Windows e.g.
|
|
||||||
vssadm~1 instead of vssadministrator
|
|
||||||
'''
|
|
||||||
parent = os.path.abspath(parent)
|
|
||||||
child = os.path.abspath(child)
|
|
||||||
|
|
||||||
while child != os.path.dirname(child):
|
|
||||||
child = os.path.dirname(child)
|
|
||||||
if os.stat(parent) == os.stat(child):
|
|
||||||
# parent and child refer to the same directory on the filesystem
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class TestSpam(TestCase):
|
|
||||||
def test_system(self):
|
|
||||||
self.assertEqual(0, spam.system('python -c "exit(0)"'))
|
|
||||||
self.assertNotEqual(0, spam.system('python -c "exit(1)"'))
|
|
||||||
|
|
||||||
def test_virtualenv(self):
|
|
||||||
virtualenv_path = os.environ.get("__CIBW_VIRTUALENV_PATH__")
|
|
||||||
if not virtualenv_path:
|
|
||||||
self.fail("No virtualenv path defined in environment variable __CIBW_VIRTUALENV_PATH__")
|
|
||||||
|
|
||||||
print("=[executable]", sys.executable)
|
|
||||||
print("=[spam location]", spam.__file__)
|
|
||||||
print("=[virtualenv path]", virtualenv_path)
|
|
||||||
print("=[listdir]", os.listdir(virtualenv_path))
|
|
||||||
if os.path.exists(os.path.join(virtualenv_path, 'Scripts')):
|
|
||||||
print("=[listdir]2", os.listdir(os.path.join(virtualenv_path, 'Scripts')))
|
|
||||||
if os.path.exists(os.path.join(virtualenv_path, 'bin')):
|
|
||||||
print("=[listdir]2", os.listdir(os.path.join(virtualenv_path, 'bin')))
|
|
||||||
self.assertTrue(path_contains(virtualenv_path, sys.executable))
|
|
||||||
self.assertTrue(path_contains(virtualenv_path, spam.__file__))
|
|
||||||
|
|
||||||
def test_uname(self):
|
|
||||||
if platform.system() == "Windows":
|
|
||||||
return
|
|
||||||
# if we're running in 32-bit Python, check that the machine is i686.
|
|
||||||
# See #336 for more info.
|
|
||||||
bits = struct.calcsize("P") * 8
|
|
||||||
if bits == 32:
|
|
||||||
self.assertEqual(platform.machine(), "i686")
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import os
|
|
||||||
|
|
||||||
import utils
|
|
||||||
|
|
||||||
|
|
||||||
def test():
|
|
||||||
project_dir = os.path.dirname(__file__)
|
|
||||||
|
|
||||||
# build the wheels
|
|
||||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
|
||||||
# write python version information to a temporary file, this is
|
|
||||||
# checked in setup.py
|
|
||||||
'CIBW_BEFORE_BUILD': '''python -c "import sys; open('/tmp/pythonversion.txt', 'w').write(sys.version)" && python -c "import sys; open('/tmp/pythonexecutable.txt', 'w').write(sys.executable)"''',
|
|
||||||
'CIBW_BEFORE_BUILD_WINDOWS': '''python -c "import sys; open('c:\\pythonversion.txt', 'w').write(sys.version)" && python -c "import sys; open('c:\\pythonexecutable.txt', 'w').write(sys.executable)"''',
|
|
||||||
})
|
|
||||||
|
|
||||||
# also check that we got the right wheels
|
|
||||||
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from setuptools import (
|
|
||||||
Extension,
|
|
||||||
setup,
|
|
||||||
)
|
|
||||||
|
|
||||||
# assert that the Python version as written to pythonversion.txt in the CIBW_BEFORE_BUILD step
|
|
||||||
# is the same one as is currently running.
|
|
||||||
version_file = 'c:\\pythonversion.txt' if sys.platform == 'win32' else '/tmp/pythonversion.txt'
|
|
||||||
with open(version_file) as f:
|
|
||||||
stored_version = f.read()
|
|
||||||
print('stored_version', stored_version)
|
|
||||||
print('sys.version', sys.version)
|
|
||||||
assert stored_version == sys.version
|
|
||||||
|
|
||||||
# check that the executable also was written
|
|
||||||
executable_file = 'c:\\pythonexecutable.txt' if sys.platform == 'win32' else '/tmp/pythonexecutable.txt'
|
|
||||||
with open(executable_file) as f:
|
|
||||||
stored_executable = f.read()
|
|
||||||
print('stored_executable', stored_executable)
|
|
||||||
print('sys.executable', sys.executable)
|
|
||||||
# windows/mac are case insensitive
|
|
||||||
assert os.path.realpath(stored_executable).lower() == os.path.realpath(sys.executable).lower()
|
|
||||||
|
|
||||||
setup(
|
|
||||||
name="spam",
|
|
||||||
ext_modules=[Extension('spam', sources=['spam.c'])],
|
|
||||||
version="0.1.0",
|
|
||||||
)
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
#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)
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import os
|
|
||||||
|
|
||||||
import utils
|
|
||||||
|
|
||||||
|
|
||||||
def test():
|
|
||||||
project_dir = os.path.dirname(__file__)
|
|
||||||
|
|
||||||
# build the wheels
|
|
||||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
|
||||||
'CIBW_BUILD': 'cp3?-*',
|
|
||||||
'CIBW_SKIP': 'cp37-*',
|
|
||||||
})
|
|
||||||
|
|
||||||
# check that we got the right wheels. There should be no 2.7 or 3.7.
|
|
||||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
|
||||||
if ('-cp3' in w) and ('-cp37' not in w)]
|
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import sys
|
|
||||||
|
|
||||||
from setuptools import (
|
|
||||||
Extension,
|
|
||||||
setup,
|
|
||||||
)
|
|
||||||
|
|
||||||
# explode if run on Python 2.7 or Python 3.4 (these should be skipped)
|
|
||||||
if sys.version_info[0:2] == (2, 7):
|
|
||||||
raise Exception('Python 2.7 should not be built')
|
|
||||||
if sys.version_info[0:2] == (3, 4):
|
|
||||||
raise Exception('Python 3.4 should be skipped')
|
|
||||||
|
|
||||||
setup(
|
|
||||||
name="spam",
|
|
||||||
ext_modules=[Extension('spam', sources=['spam.c'])],
|
|
||||||
version="0.1.0",
|
|
||||||
)
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
#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)
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
import os
|
|
||||||
import pytest
|
|
||||||
import subprocess
|
|
||||||
import utils
|
|
||||||
|
|
||||||
|
|
||||||
def test():
|
|
||||||
project_dir = os.path.dirname(__file__)
|
|
||||||
|
|
||||||
# write some information into the CIBW_ENVIRONMENT, for expansion and
|
|
||||||
# insertion into the environment by cibuildwheel. This is checked
|
|
||||||
# in setup.py
|
|
||||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
|
||||||
'CIBW_ENVIRONMENT': '''CIBW_TEST_VAR="a b c" CIBW_TEST_VAR_2=1 CIBW_TEST_VAR_3="$(echo 'test string 3')" PATH=$PATH:/opt/cibw_test_path''',
|
|
||||||
'CIBW_ENVIRONMENT_WINDOWS': '''CIBW_TEST_VAR="a b c" CIBW_TEST_VAR_2=1 CIBW_TEST_VAR_3="$(echo 'test string 3')" PATH="$PATH;/opt/cibw_test_path"''',
|
|
||||||
})
|
|
||||||
|
|
||||||
# also check that we got the right wheels built
|
|
||||||
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
|
||||||
|
|
||||||
|
|
||||||
def test_overridden_path(tmp_path):
|
|
||||||
project_dir = os.path.dirname(__file__)
|
|
||||||
|
|
||||||
# mess up PATH, somehow
|
|
||||||
with pytest.raises(subprocess.CalledProcessError):
|
|
||||||
utils.cibuildwheel_run(project_dir, output_dir=tmp_path, add_env={
|
|
||||||
'CIBW_ENVIRONMENT': '''SOMETHING="$(mkdir new_path && touch new_path/python)" PATH="$(realpath new_path):$PATH"''',
|
|
||||||
'CIBW_ENVIRONMENT_WINDOWS': '''SOMETHING="$(mkdir new_path && type nul > new_path/python.exe)" PATH="$CD\\new_path;$PATH"''',
|
|
||||||
})
|
|
||||||
assert len(os.listdir(str(tmp_path))) == 0
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import os
|
|
||||||
|
|
||||||
from setuptools import (
|
|
||||||
Extension,
|
|
||||||
setup,
|
|
||||||
)
|
|
||||||
|
|
||||||
# explode if environment isn't correct, as set in CIBW_ENVIRONMENT
|
|
||||||
CIBW_TEST_VAR = os.environ.get('CIBW_TEST_VAR')
|
|
||||||
CIBW_TEST_VAR_2 = os.environ.get('CIBW_TEST_VAR_2')
|
|
||||||
PATH = os.environ.get('PATH')
|
|
||||||
|
|
||||||
if CIBW_TEST_VAR != 'a b c':
|
|
||||||
raise Exception('CIBW_TEST_VAR should equal "a b c". It was "%s"' % CIBW_TEST_VAR)
|
|
||||||
if CIBW_TEST_VAR_2 != '1':
|
|
||||||
raise Exception('CIBW_TEST_VAR_2 should equal "1". It was "%s"' % CIBW_TEST_VAR_2)
|
|
||||||
if '/opt/cibw_test_path' not in PATH:
|
|
||||||
raise Exception('PATH should contain "/opt/cibw_test_path". It was "%s"' % PATH)
|
|
||||||
if '$PATH' in PATH:
|
|
||||||
raise Exception('$PATH should be expanded in PATH. It was "%s"' % PATH)
|
|
||||||
|
|
||||||
setup(
|
|
||||||
name="spam",
|
|
||||||
ext_modules=[Extension('spam', sources=['spam.c'])],
|
|
||||||
version="0.1.0",
|
|
||||||
)
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
#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)
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from setuptools import (
|
|
||||||
Extension,
|
|
||||||
setup,
|
|
||||||
)
|
|
||||||
|
|
||||||
# check that we're running in the correct docker image as specified in the
|
|
||||||
# environment options CIBW_MANYLINUX1_*_IMAGE
|
|
||||||
if 'linux' in sys.platform and not os.path.exists('/dockcross'):
|
|
||||||
raise Exception('/dockcross directory not found. Is this test running in the correct docker image?')
|
|
||||||
|
|
||||||
setup(
|
|
||||||
name="spam",
|
|
||||||
ext_modules=[Extension('spam', sources=['spam.c'])],
|
|
||||||
version="0.1.0",
|
|
||||||
)
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
#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)
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import os
|
|
||||||
|
|
||||||
import utils
|
|
||||||
|
|
||||||
|
|
||||||
def test():
|
|
||||||
project_dir = os.path.dirname(__file__)
|
|
||||||
# this test checks that SSL is working in the build environment using
|
|
||||||
# some checks in setup.py.
|
|
||||||
|
|
||||||
utils.cibuildwheel_run(project_dir)
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
import ssl
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from setuptools import (
|
|
||||||
Extension,
|
|
||||||
setup,
|
|
||||||
)
|
|
||||||
|
|
||||||
if sys.version_info[0] == 2:
|
|
||||||
from urllib2 import urlopen
|
|
||||||
else:
|
|
||||||
from urllib.request import urlopen
|
|
||||||
|
|
||||||
if sys.version_info[0:2] == (3, 3):
|
|
||||||
data = urlopen('https://www.nist.gov')
|
|
||||||
else:
|
|
||||||
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
|
|
||||||
data = urlopen('https://www.nist.gov', context=context)
|
|
||||||
|
|
||||||
setup(
|
|
||||||
name="spam",
|
|
||||||
ext_modules=[Extension('spam', sources=['spam.c'])],
|
|
||||||
version="0.1.0",
|
|
||||||
)
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
#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)
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
from setuptools import (
|
|
||||||
Extension,
|
|
||||||
setup,
|
|
||||||
)
|
|
||||||
|
|
||||||
setup(
|
|
||||||
name="spam",
|
|
||||||
ext_modules=[Extension('spam', sources=['spam.c'])],
|
|
||||||
version="0.1.0",
|
|
||||||
)
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
#include <Python.h>
|
|
||||||
#include <malloc.h>
|
|
||||||
|
|
||||||
#if !defined(__GLIBC_PREREQ)
|
|
||||||
#error "Must run on a glibc linux environment"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !__GLIBC_PREREQ(2, 5) /* manylinux1 is glibc 2.5 */
|
|
||||||
#error "Must run on a glibc >= 2.5 linux environment"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static PyObject *
|
|
||||||
spam_system(PyObject *self, PyObject *args)
|
|
||||||
{
|
|
||||||
const char *command;
|
|
||||||
int sts = 0;
|
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "s", &command))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
#if defined(__GLIBC_PREREQ) && __GLIBC_PREREQ(2, 17) /* manylinux2014 is glibc 2.17 */
|
|
||||||
// secure_getenv is only available in manylinux2014, ensuring
|
|
||||||
// that only a manylinux2014 wheel is produced
|
|
||||||
sts = (int)secure_getenv("NON_EXISTING_ENV_VARIABLE");
|
|
||||||
#elif defined(__GLIBC_PREREQ) && __GLIBC_PREREQ(2, 10) /* manylinux2010 is glibc 2.12 */
|
|
||||||
// malloc_info is only available on manylinux2010+
|
|
||||||
sts = malloc_info(0, stdout);
|
|
||||||
#endif
|
|
||||||
if (sts == 0) {
|
|
||||||
sts = system(command);
|
|
||||||
}
|
|
||||||
return PyLong_FromLong(sts);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Module initialization */
|
|
||||||
|
|
||||||
#if PY_MAJOR_VERSION >= 3
|
|
||||||
#define MOD_INIT(name) PyMODINIT_FUNC PyInit_##name(void)
|
|
||||||
#define MOD_DEF(m, name, doc, methods, module_state_size) \
|
|
||||||
static struct PyModuleDef moduledef = { \
|
|
||||||
PyModuleDef_HEAD_INIT, name, doc, module_state_size, methods, }; \
|
|
||||||
m = PyModule_Create(&moduledef);
|
|
||||||
#define MOD_RETURN(m) return m;
|
|
||||||
#else
|
|
||||||
#define MOD_INIT(name) PyMODINIT_FUNC init##name(void)
|
|
||||||
#define MOD_DEF(m, name, doc, methods, module_state_size) \
|
|
||||||
m = Py_InitModule3(name, methods, doc);
|
|
||||||
#define MOD_RETURN(m) return;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static PyMethodDef module_methods[] = {
|
|
||||||
{"system", (PyCFunction)spam_system, METH_VARARGS,
|
|
||||||
"Execute a shell command."},
|
|
||||||
{NULL} /* Sentinel */
|
|
||||||
};
|
|
||||||
|
|
||||||
MOD_INIT(spam)
|
|
||||||
{
|
|
||||||
PyObject* m;
|
|
||||||
|
|
||||||
MOD_DEF(m,
|
|
||||||
"spam",
|
|
||||||
"Example module",
|
|
||||||
module_methods,
|
|
||||||
-1)
|
|
||||||
|
|
||||||
MOD_RETURN(m)
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import os
|
|
||||||
|
|
||||||
import utils
|
|
||||||
|
|
||||||
project_dir = os.path.dirname(__file__)
|
|
||||||
|
|
||||||
|
|
||||||
def test():
|
|
||||||
# build the wheels
|
|
||||||
actual_wheels = utils.cibuildwheel_run(project_dir)
|
|
||||||
|
|
||||||
# check that the expected wheels are produced
|
|
||||||
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
[metadata]
|
|
||||||
name = spam
|
|
||||||
version = attr: spam.__version__
|
|
||||||
|
|
||||||
[options]
|
|
||||||
packages = find:
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
from setuptools import (
|
|
||||||
Extension,
|
|
||||||
setup,
|
|
||||||
)
|
|
||||||
|
|
||||||
setup(
|
|
||||||
ext_modules=[Extension('spam.spam', sources=['spam/spam.c'])],
|
|
||||||
)
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
__version__ = "0.1.0"
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
#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)
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
import os
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
import utils
|
|
||||||
|
|
||||||
project_dir = os.path.dirname(__file__)
|
|
||||||
|
|
||||||
|
|
||||||
def test_cpp11(tmp_path):
|
|
||||||
# This test checks that the C++11 standard is supported
|
|
||||||
|
|
||||||
# VC++ for Python 2.7 does not support modern standards
|
|
||||||
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')
|
|
||||||
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
|
|
||||||
|
|
||||||
# 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
|
|
||||||
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')
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
def test_cpp17():
|
|
||||||
# This test checks that the C++17 standard is supported
|
|
||||||
|
|
||||||
# Python and PyPy 2.7 use the `register` keyword which is forbidden in the C++17 standard
|
|
||||||
# The manylinux1 docker image does not have a compiler which supports C++11
|
|
||||||
# Python 3.5 and PyPy 3.6 are compiled with MSVC 10, which does not support C++17
|
|
||||||
if os.environ.get('APPVEYOR_BUILD_WORKER_IMAGE', '') == 'Visual Studio 2015':
|
|
||||||
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]
|
|
||||||
|
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import os
|
|
||||||
import platform
|
|
||||||
|
|
||||||
from setuptools import (
|
|
||||||
Extension,
|
|
||||||
setup,
|
|
||||||
)
|
|
||||||
|
|
||||||
standard = os.environ["STANDARD"]
|
|
||||||
|
|
||||||
language_standard = "/std:c++" + standard if platform.system() == "Windows" else "-std=c++" + standard
|
|
||||||
|
|
||||||
extra_compile_args = [language_standard, "-DSTANDARD=" + standard]
|
|
||||||
|
|
||||||
if standard == "17":
|
|
||||||
if platform.system() == "Windows":
|
|
||||||
extra_compile_args.append("/wd5033")
|
|
||||||
else:
|
|
||||||
extra_compile_args.append("-Wno-register")
|
|
||||||
|
|
||||||
setup(
|
|
||||||
name="spam",
|
|
||||||
ext_modules=[Extension('spam', sources=['spam.cpp'], language="c++", extra_compile_args=extra_compile_args)],
|
|
||||||
version="0.1.0",
|
|
||||||
)
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
#include <Python.h>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
// Depending on the requested standard, use a modern C++ feature
|
|
||||||
// that was introduced in that standard.
|
|
||||||
#if STANDARD == 11
|
|
||||||
#include <array>
|
|
||||||
#elif STANDARD == 14
|
|
||||||
int a = 100'000;
|
|
||||||
#elif STANDARD == 17
|
|
||||||
#include <utility>
|
|
||||||
auto a = std::pair(5.0, false);
|
|
||||||
#else
|
|
||||||
#error Standard needed
|
|
||||||
#endif
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import os
|
|
||||||
import utils
|
|
||||||
|
|
||||||
|
|
||||||
def test():
|
|
||||||
project_dir = os.path.dirname(__file__)
|
|
||||||
|
|
||||||
# build the wheels
|
|
||||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
|
||||||
# write python version information to a temporary file, this is
|
|
||||||
# checked in setup.py
|
|
||||||
'CIBW_BEFORE_TEST': '''python -c "import sys; open('/tmp/pythonversion.txt', 'w').write(sys.version)" && python -c "import sys; open('/tmp/pythonprefix.txt', 'w').write(sys.prefix)"''',
|
|
||||||
'CIBW_BEFORE_TEST_WINDOWS': '''python -c "import sys; open('c:\\pythonversion.txt', 'w').write(sys.version)" && python -c "import sys; open('c:\\pythonprefix.txt', 'w').write(sys.prefix)"''',
|
|
||||||
'CIBW_TEST_REQUIRES': 'nose',
|
|
||||||
# the 'false ||' bit is to ensure this command runs in a shell on
|
|
||||||
# mac/linux.
|
|
||||||
'CIBW_TEST_COMMAND': 'false || nosetests {project}/test',
|
|
||||||
'CIBW_TEST_COMMAND_WINDOWS': 'nosetests {project}/test',
|
|
||||||
})
|
|
||||||
|
|
||||||
# also check that we got the right wheels
|
|
||||||
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
from setuptools import setup, Extension
|
|
||||||
|
|
||||||
|
|
||||||
setup(
|
|
||||||
name="spam",
|
|
||||||
ext_modules=[Extension('spam', sources=['spam.c'])],
|
|
||||||
version="0.1.0",
|
|
||||||
)
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
#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)
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
import sys
|
|
||||||
import os
|
|
||||||
from unittest import TestCase
|
|
||||||
|
|
||||||
|
|
||||||
class TestBeforeTest(TestCase):
|
|
||||||
def test_version(self):
|
|
||||||
# assert that the Python version as written to pythonversion.txt in the CIBW_BEFORE_TEST step
|
|
||||||
# is the same one as is currently running.
|
|
||||||
# because of use symlinks in MacOS run this test is also need
|
|
||||||
version_file = 'c:\\pythonversion.txt' if sys.platform == 'win32' else '/tmp/pythonversion.txt'
|
|
||||||
with open(version_file) as f:
|
|
||||||
stored_version = f.read()
|
|
||||||
print('stored_version', stored_version)
|
|
||||||
print('sys.version', sys.version)
|
|
||||||
assert stored_version == sys.version
|
|
||||||
|
|
||||||
def test_prefix(self):
|
|
||||||
# check that the prefix also was written
|
|
||||||
prefix_file = 'c:\\pythonprefix.txt' if sys.platform == 'win32' else '/tmp/pythonprefix.txt'
|
|
||||||
with open(prefix_file) as f:
|
|
||||||
stored_prefix = f.read()
|
|
||||||
print('stored_prefix', stored_prefix)
|
|
||||||
print('sys.prefix', sys.prefix)
|
|
||||||
# Works around path-comparison bugs caused by short-paths on Windows e.g.
|
|
||||||
# vssadm~1 instead of vssadministrator
|
|
||||||
|
|
||||||
assert os.stat(stored_prefix) == os.stat(sys.prefix)
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
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",
|
|
||||||
)
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
#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)
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
print('before_build.py executed!')
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import os
|
|
||||||
import utils
|
|
||||||
|
|
||||||
project_dir = os.path.dirname(__file__)
|
|
||||||
|
|
||||||
|
|
||||||
def test(capfd):
|
|
||||||
package_dir = os.path.join(project_dir, 'src', 'spam')
|
|
||||||
# build the wheels
|
|
||||||
actual_wheels = utils.cibuildwheel_run(project_dir, package_dir=package_dir, add_env={
|
|
||||||
'CIBW_BEFORE_BUILD': 'python {project}/bin/before_build.py',
|
|
||||||
'CIBW_TEST_COMMAND': 'python {package}/test/run_tests.py',
|
|
||||||
# this shouldn't depend on the version of python, so build only
|
|
||||||
# CPython 3.6
|
|
||||||
'CIBW_BUILD': 'cp36-*',
|
|
||||||
})
|
|
||||||
|
|
||||||
# check that the expected wheels are produced
|
|
||||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
|
||||||
if 'cp36' in w]
|
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
|
||||||
|
|
||||||
captured = capfd.readouterr()
|
|
||||||
assert "before_build.py executed!" in captured.out
|
|
||||||
assert "run_tests.py executed!" in captured.out
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
from setuptools import Extension, setup
|
|
||||||
|
|
||||||
|
|
||||||
setup(
|
|
||||||
name="spam",
|
|
||||||
ext_modules=[Extension('spam', sources=['spam.c'])],
|
|
||||||
version="0.1.0",
|
|
||||||
)
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
#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)
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
print('run_tests.py executed!')
|
|
||||||
+1
-3
@@ -1,3 +1 @@
|
|||||||
This folder contains repos that are built by cibuildwheel to test cibuildwheel. Confusing, I know.
|
This folder contains integration tests for cibuildwheel.
|
||||||
|
|
||||||
Basically, if cibuildwheel can build these projects, we're good.
|
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
sys.path.append(os.path.join(os.path.dirname(__file__), 'shared'))
|
|
||||||
@@ -1,12 +1,22 @@
|
|||||||
import os
|
import textwrap
|
||||||
import platform
|
import platform
|
||||||
|
from . import test_projects
|
||||||
|
from . import utils
|
||||||
|
|
||||||
import utils
|
basic_project = test_projects.new_c_project(
|
||||||
|
setup_py_add=textwrap.dedent('''
|
||||||
|
import os
|
||||||
|
|
||||||
project_dir = os.path.dirname(__file__)
|
if os.environ.get("CIBUILDWHEEL", "0") != "1":
|
||||||
|
raise Exception("CIBUILDWHEEL environment variable is not set to 1")
|
||||||
|
''')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test():
|
def test(tmp_path):
|
||||||
|
project_dir = tmp_path / 'project'
|
||||||
|
basic_project.generate(project_dir)
|
||||||
|
|
||||||
# build the wheels
|
# build the wheels
|
||||||
actual_wheels = utils.cibuildwheel_run(project_dir)
|
actual_wheels = utils.cibuildwheel_run(project_dir)
|
||||||
|
|
||||||
@@ -15,7 +25,10 @@ def test():
|
|||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
|
|
||||||
def test_build_identifiers():
|
def test_build_identifiers(tmp_path):
|
||||||
|
project_dir = tmp_path / 'project'
|
||||||
|
basic_project.generate(project_dir)
|
||||||
|
|
||||||
# check that the number of expected wheels matches the number of build
|
# check that the number of expected wheels matches the number of build
|
||||||
# identifiers
|
# identifiers
|
||||||
# after adding CIBW_MANYLINUX_IMAGE to support manylinux2010, there
|
# after adding CIBW_MANYLINUX_IMAGE to support manylinux2010, there
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import textwrap
|
||||||
|
|
||||||
|
from . import utils
|
||||||
|
from . import test_projects
|
||||||
|
|
||||||
|
project_with_before_build_asserts = test_projects.new_c_project(
|
||||||
|
setup_py_add=textwrap.dedent(r'''
|
||||||
|
import sys, os
|
||||||
|
|
||||||
|
# assert that the Python version as written to pythonversion.txt in the CIBW_BEFORE_BUILD step
|
||||||
|
# is the same one as is currently running.
|
||||||
|
version_file = 'c:\\pythonversion.txt' if sys.platform == 'win32' else '/tmp/pythonversion.txt'
|
||||||
|
with open(version_file) as f:
|
||||||
|
stored_version = f.read()
|
||||||
|
print('stored_version', stored_version)
|
||||||
|
print('sys.version', sys.version)
|
||||||
|
assert stored_version == sys.version
|
||||||
|
|
||||||
|
# check that the executable also was written
|
||||||
|
executable_file = 'c:\\pythonexecutable.txt' if sys.platform == 'win32' else '/tmp/pythonexecutable.txt'
|
||||||
|
with open(executable_file) as f:
|
||||||
|
stored_executable = f.read()
|
||||||
|
print('stored_executable', stored_executable)
|
||||||
|
print('sys.executable', sys.executable)
|
||||||
|
# windows/mac are case insensitive
|
||||||
|
assert os.path.realpath(stored_executable).lower() == os.path.realpath(sys.executable).lower()
|
||||||
|
''')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test(tmp_path):
|
||||||
|
project_dir = tmp_path / 'project'
|
||||||
|
project_with_before_build_asserts.generate(project_dir)
|
||||||
|
|
||||||
|
# build the wheels
|
||||||
|
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
||||||
|
# write python version information to a temporary file, this is
|
||||||
|
# checked in setup.py
|
||||||
|
'CIBW_BEFORE_BUILD': '''python -c "import sys; open('/tmp/pythonversion.txt', 'w').write(sys.version)" && python -c "import sys; open('/tmp/pythonexecutable.txt', 'w').write(sys.executable)"''',
|
||||||
|
'CIBW_BEFORE_BUILD_WINDOWS': '''python -c "import sys; open('c:\\pythonversion.txt', 'w').write(sys.version)" && python -c "import sys; open('c:\\pythonexecutable.txt', 'w').write(sys.executable)"''',
|
||||||
|
})
|
||||||
|
|
||||||
|
# also check that we got the right wheels
|
||||||
|
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
||||||
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
from . import test_projects
|
||||||
|
from . import utils
|
||||||
|
|
||||||
|
before_test_project = test_projects.new_c_project()
|
||||||
|
before_test_project.files['test/spam_test.py'] = r'''
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
from unittest import TestCase
|
||||||
|
|
||||||
|
|
||||||
|
class TestBeforeTest(TestCase):
|
||||||
|
def test_version(self):
|
||||||
|
# assert that the Python version as written to pythonversion.txt in the CIBW_BEFORE_TEST step
|
||||||
|
# is the same one as is currently running.
|
||||||
|
# because of use symlinks in MacOS run this test is also need
|
||||||
|
version_file = 'c:\\pythonversion.txt' if sys.platform == 'win32' else '/tmp/pythonversion.txt'
|
||||||
|
with open(version_file) as f:
|
||||||
|
stored_version = f.read()
|
||||||
|
print('stored_version', stored_version)
|
||||||
|
print('sys.version', sys.version)
|
||||||
|
assert stored_version == sys.version
|
||||||
|
|
||||||
|
def test_prefix(self):
|
||||||
|
# check that the prefix also was written
|
||||||
|
prefix_file = 'c:\\pythonprefix.txt' if sys.platform == 'win32' else '/tmp/pythonprefix.txt'
|
||||||
|
with open(prefix_file) as f:
|
||||||
|
stored_prefix = f.read()
|
||||||
|
print('stored_prefix', stored_prefix)
|
||||||
|
print('sys.prefix', sys.prefix)
|
||||||
|
# Works around path-comparison bugs caused by short-paths on Windows e.g.
|
||||||
|
# vssadm~1 instead of vssadministrator
|
||||||
|
|
||||||
|
assert os.stat(stored_prefix) == os.stat(sys.prefix)
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
def test(tmp_path):
|
||||||
|
project_dir = tmp_path / 'project'
|
||||||
|
before_test_project.generate(project_dir)
|
||||||
|
|
||||||
|
# build the wheels
|
||||||
|
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
||||||
|
# write python version information to a temporary file, this is
|
||||||
|
# checked in setup.py
|
||||||
|
'CIBW_BEFORE_TEST': '''python -c "import sys; open('/tmp/pythonversion.txt', 'w').write(sys.version)" && python -c "import sys; open('/tmp/pythonprefix.txt', 'w').write(sys.prefix)"''',
|
||||||
|
'CIBW_BEFORE_TEST_WINDOWS': '''python -c "import sys; open('c:\\pythonversion.txt', 'w').write(sys.version)" && python -c "import sys; open('c:\\pythonprefix.txt', 'w').write(sys.prefix)"''',
|
||||||
|
'CIBW_TEST_REQUIRES': 'nose',
|
||||||
|
# the 'false ||' bit is to ensure this command runs in a shell on
|
||||||
|
# mac/linux.
|
||||||
|
'CIBW_TEST_COMMAND': 'false || nosetests {project}/test',
|
||||||
|
'CIBW_TEST_COMMAND_WINDOWS': 'nosetests {project}/test',
|
||||||
|
})
|
||||||
|
|
||||||
|
# also check that we got the right wheels
|
||||||
|
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
||||||
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import textwrap
|
||||||
|
|
||||||
|
from . import utils
|
||||||
|
from . import test_projects
|
||||||
|
|
||||||
|
project_with_skip_asserts = test_projects.new_c_project(
|
||||||
|
setup_py_add=textwrap.dedent(r'''
|
||||||
|
# explode if run on Python 2.7 or Python 3.7 (these should be skipped)
|
||||||
|
if sys.version_info[0:2] == (2, 7):
|
||||||
|
raise Exception("Python 2.7 should not be built")
|
||||||
|
if sys.version_info[0:2] == (3, 7):
|
||||||
|
raise Exception("Python 3.7 should be skipped")
|
||||||
|
''')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test(tmp_path):
|
||||||
|
project_dir = tmp_path / 'project'
|
||||||
|
project_with_skip_asserts.generate(project_dir)
|
||||||
|
|
||||||
|
# build the wheels
|
||||||
|
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
||||||
|
'CIBW_BUILD': 'cp3?-*',
|
||||||
|
'CIBW_SKIP': 'cp37-*',
|
||||||
|
})
|
||||||
|
|
||||||
|
# check that we got the right wheels. There should be no 2.7 or 3.7.
|
||||||
|
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||||
|
if ('-cp3' in w) and ('-cp37' not in w)]
|
||||||
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
@@ -0,0 +1,209 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
import jinja2
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from . import utils
|
||||||
|
from .test_projects import TestProject
|
||||||
|
|
||||||
|
cpp_test_project = TestProject()
|
||||||
|
|
||||||
|
cpp_test_project.files['setup.py'] = jinja2.Template(r'''
|
||||||
|
from setuptools import Extension, setup
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name="spam",
|
||||||
|
ext_modules=[Extension('spam', sources=['spam.cpp'], language="c++", extra_compile_args={{ extra_compile_args }})],
|
||||||
|
version="0.1.0",
|
||||||
|
)
|
||||||
|
''')
|
||||||
|
|
||||||
|
cpp_test_project.files['spam.cpp'] = jinja2.Template(r'''
|
||||||
|
#include <Python.h>
|
||||||
|
|
||||||
|
{{ spam_cpp_top_level_add }}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
''')
|
||||||
|
|
||||||
|
|
||||||
|
cpp11_project = cpp_test_project.copy()
|
||||||
|
cpp11_project.template_context['extra_compile_args'] = (
|
||||||
|
['/std:c++11'] if utils.platform == 'windows' else ['-std=c++11']
|
||||||
|
)
|
||||||
|
cpp11_project.template_context['spam_cpp_top_level_add'] = '#include <array>'
|
||||||
|
|
||||||
|
|
||||||
|
def test_cpp11(tmp_path):
|
||||||
|
# This test checks that the C++11 standard is supported
|
||||||
|
project_dir = tmp_path / 'project'
|
||||||
|
|
||||||
|
cpp11_project.generate(project_dir)
|
||||||
|
|
||||||
|
# VC++ for Python 2.7 does not support modern standards
|
||||||
|
add_env = {'CIBW_SKIP': 'cp27-win* pp27-win32'}
|
||||||
|
|
||||||
|
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
cpp14_project = cpp_test_project.copy()
|
||||||
|
cpp14_project.template_context['extra_compile_args'] = (
|
||||||
|
['/std:c++14'] if utils.platform == 'windows' else ['-std=c++14']
|
||||||
|
)
|
||||||
|
cpp14_project.template_context['spam_cpp_top_level_add'] = "int a = 100'000;"
|
||||||
|
|
||||||
|
|
||||||
|
def test_cpp14(tmp_path):
|
||||||
|
# This test checks that the C++14 standard is supported
|
||||||
|
project_dir = tmp_path / 'project'
|
||||||
|
|
||||||
|
cpp14_project.generate(project_dir)
|
||||||
|
|
||||||
|
# VC++ for Python 2.7 does not support modern standards
|
||||||
|
# The manylinux1 docker image does not have a compiler which supports C++11
|
||||||
|
add_env = {'CIBW_SKIP': 'cp27-win* pp27-win32'}
|
||||||
|
|
||||||
|
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
cpp17_project = cpp_test_project.copy()
|
||||||
|
|
||||||
|
# Python and PyPy 2.7 headers use the `register` keyword, which is forbidden in
|
||||||
|
# the C++17 standard, so we need the -Wno-register or /wd5033 options
|
||||||
|
cpp17_project.template_context['extra_compile_args'] = (
|
||||||
|
['/std:c++17', '/wd5033'] if utils.platform == 'windows' else ['-std=c++17', '-Wno-register']
|
||||||
|
)
|
||||||
|
cpp17_project.template_context['spam_cpp_top_level_add'] = r'''
|
||||||
|
#include <utility>
|
||||||
|
auto a = std::pair(5.0, false);
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
def test_cpp17(tmp_path):
|
||||||
|
# This test checks that the C++17 standard is supported
|
||||||
|
project_dir = tmp_path / 'project'
|
||||||
|
|
||||||
|
cpp17_project.generate(project_dir)
|
||||||
|
|
||||||
|
if os.environ.get('APPVEYOR_BUILD_WORKER_IMAGE', '') == 'Visual Studio 2015':
|
||||||
|
pytest.skip('Visual Studio 2015 does not support C++17')
|
||||||
|
|
||||||
|
# Pypy's distutils sets the default compiler to 'msvc9compiler', which
|
||||||
|
# is too old to support cpp17.
|
||||||
|
add_env = {'CIBW_SKIP': 'cp27-win* pp??-*'}
|
||||||
|
|
||||||
|
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 '-pp' not in w]
|
||||||
|
|
||||||
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
|
|
||||||
|
def test_cpp17_py27_modern_msvc_workaround(tmp_path):
|
||||||
|
# This test checks the workaround for building Python 2.7 wheel with MSVC 14
|
||||||
|
|
||||||
|
if utils.platform != 'windows':
|
||||||
|
pytest.skip('the test is only relevant to the Windows build')
|
||||||
|
|
||||||
|
if os.environ.get('APPVEYOR_BUILD_WORKER_IMAGE', '') == 'Visual Studio 2015':
|
||||||
|
pytest.skip('Visual Studio 2015 does not support C++17')
|
||||||
|
|
||||||
|
project_dir = tmp_path / 'project'
|
||||||
|
cpp17_project.generate(project_dir)
|
||||||
|
|
||||||
|
# VC++ for Python 2.7 (i.e., MSVC 9) does not support modern standards
|
||||||
|
# This is a workaround which forces distutils/setupstools to a newer version
|
||||||
|
# Wheels compiled need a more modern C++ redistributable installed, which is not
|
||||||
|
# included with Python: see documentation for more info
|
||||||
|
# DISTUTILS_USE_SDK and MSSdk=1 tell distutils/setuptools that we are adding
|
||||||
|
# MSVC's compiler, tools, and libraries to PATH ourselves
|
||||||
|
add_env = {'DISTUTILS_USE_SDK': '1', 'MSSdk': '1'}
|
||||||
|
|
||||||
|
# Use existing setuptools code to run Visual Studio's vcvarsall.bat and get the
|
||||||
|
# necessary environment variables, since running vcvarsall.bat in a subprocess
|
||||||
|
# does not keep the relevant environment variables
|
||||||
|
# There are different environment variables for 32-bit/64-bit targets, so we
|
||||||
|
# need to run cibuildwheel twice, once for 32-bit with `vcvarsall.bat x86, and
|
||||||
|
# once for 64-bit with `vcvarsall.bat x64`
|
||||||
|
# In a normal CI setup, just run vcvarsall.bat before running cibuildwheel and set
|
||||||
|
# DISTUTILS_USE_SDK and MSSdk
|
||||||
|
import setuptools
|
||||||
|
|
||||||
|
def add_vcvars(prev_env, platform):
|
||||||
|
vcvarsall_env = setuptools.msvc.msvc14_get_vc_env(platform)
|
||||||
|
env = prev_env.copy()
|
||||||
|
for vcvar in ['path', 'include', 'lib']:
|
||||||
|
env[vcvar] = vcvarsall_env[vcvar]
|
||||||
|
return env
|
||||||
|
|
||||||
|
add_env_x86 = add_vcvars(add_env, 'x86')
|
||||||
|
add_env_x86['CIBW_BUILD'] = '?p27-win32'
|
||||||
|
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env_x86)
|
||||||
|
|
||||||
|
add_env_x64 = add_vcvars(add_env, 'x64')
|
||||||
|
add_env_x64['CIBW_BUILD'] = 'cp27-win_amd64'
|
||||||
|
actual_wheels += utils.cibuildwheel_run(project_dir, add_env=add_env_x64)
|
||||||
|
|
||||||
|
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||||
|
if 'cp27-cp27m-win' in w
|
||||||
|
or 'pp27-pypy_73-win32' in w]
|
||||||
|
|
||||||
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
+46
-9
@@ -4,7 +4,36 @@ import pytest
|
|||||||
import textwrap
|
import textwrap
|
||||||
import cibuildwheel.util
|
import cibuildwheel.util
|
||||||
|
|
||||||
import utils
|
from . import utils
|
||||||
|
from . import test_projects
|
||||||
|
|
||||||
|
|
||||||
|
project_with_expected_version_checks = test_projects.new_c_project(
|
||||||
|
setup_py_add=textwrap.dedent(r'''
|
||||||
|
import subprocess
|
||||||
|
import os
|
||||||
|
|
||||||
|
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)
|
||||||
|
)
|
||||||
|
''')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
VERSION_REGEX = r'([\w-]+)==([^\s]+)'
|
VERSION_REGEX = r'([\w-]+)==([^\s]+)'
|
||||||
|
|
||||||
@@ -21,12 +50,13 @@ def get_versions_from_constraint_file(constraint_file):
|
|||||||
return versions
|
return versions
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('python_version', ['2.7', '3.5', '3.8'])
|
@pytest.mark.parametrize('python_version', ['2.7', '3.5', '3.6', '3.8'])
|
||||||
def test_pinned_versions(python_version):
|
def test_pinned_versions(tmp_path, python_version):
|
||||||
if utils.platform == 'linux':
|
if utils.platform == 'linux':
|
||||||
pytest.skip('linux doesn\'t pin individual tool versions, it pins manylinux images instead')
|
pytest.skip('linux doesn\'t pin individual tool versions, it pins manylinux images instead')
|
||||||
|
|
||||||
project_dir = os.path.dirname(__file__)
|
project_dir = tmp_path / 'project'
|
||||||
|
project_with_expected_version_checks.generate(project_dir)
|
||||||
|
|
||||||
build_environment = {}
|
build_environment = {}
|
||||||
|
|
||||||
@@ -36,6 +66,9 @@ def test_pinned_versions(python_version):
|
|||||||
elif python_version == '3.5':
|
elif python_version == '3.5':
|
||||||
constraint_filename = 'constraints-python35.txt'
|
constraint_filename = 'constraints-python35.txt'
|
||||||
build_pattern = '[cp]p35-*'
|
build_pattern = '[cp]p35-*'
|
||||||
|
elif python_version == '3.6':
|
||||||
|
constraint_filename = 'constraints-python36.txt'
|
||||||
|
build_pattern = '[cp]p36-*'
|
||||||
else:
|
else:
|
||||||
constraint_filename = 'constraints.txt'
|
constraint_filename = 'constraints.txt'
|
||||||
build_pattern = '[cp]p38-*'
|
build_pattern = '[cp]p38-*'
|
||||||
@@ -44,11 +77,11 @@ def test_pinned_versions(python_version):
|
|||||||
constraint_versions = get_versions_from_constraint_file(constraint_file)
|
constraint_versions = get_versions_from_constraint_file(constraint_file)
|
||||||
|
|
||||||
for package in ['pip', 'setuptools', 'wheel', 'virtualenv']:
|
for package in ['pip', 'setuptools', 'wheel', 'virtualenv']:
|
||||||
env_name = 'EXPECTED_{}_VERSION'.format(package.upper())
|
env_name = f'EXPECTED_{package.upper()}_VERSION'
|
||||||
build_environment[env_name] = constraint_versions[package]
|
build_environment[env_name] = constraint_versions[package]
|
||||||
|
|
||||||
cibw_environment_option = ' '.join(
|
cibw_environment_option = ' '.join(
|
||||||
['{}={}'.format(k, v) for k, v in build_environment.items()]
|
[f'{k}={v}' for k, v in build_environment.items()]
|
||||||
)
|
)
|
||||||
|
|
||||||
# build and test the wheels
|
# build and test the wheels
|
||||||
@@ -64,6 +97,9 @@ def test_pinned_versions(python_version):
|
|||||||
elif python_version == '3.5':
|
elif python_version == '3.5':
|
||||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||||
if '-cp35' in w or '-pp35' in w]
|
if '-cp35' in w or '-pp35' in w]
|
||||||
|
elif python_version == '3.6':
|
||||||
|
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||||
|
if '-cp36' in w or '-pp36' in w]
|
||||||
elif python_version == '3.8':
|
elif python_version == '3.8':
|
||||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||||
if '-cp38' in w or '-pp38' in w]
|
if '-cp38' in w or '-pp38' in w]
|
||||||
@@ -78,7 +114,8 @@ def test_dependency_constraints_file(tmp_path, python_version):
|
|||||||
if utils.platform == 'linux':
|
if utils.platform == 'linux':
|
||||||
pytest.skip('linux doesn\'t pin individual tool versions, it pins manylinux images instead')
|
pytest.skip('linux doesn\'t pin individual tool versions, it pins manylinux images instead')
|
||||||
|
|
||||||
project_dir = os.path.dirname(__file__)
|
project_dir = tmp_path / 'project'
|
||||||
|
project_with_expected_version_checks.generate(project_dir)
|
||||||
|
|
||||||
tool_versions = {
|
tool_versions = {
|
||||||
'pip': '20.0.2',
|
'pip': '20.0.2',
|
||||||
@@ -100,11 +137,11 @@ def test_dependency_constraints_file(tmp_path, python_version):
|
|||||||
build_environment = {}
|
build_environment = {}
|
||||||
|
|
||||||
for package_name, version in tool_versions.items():
|
for package_name, version in tool_versions.items():
|
||||||
env_name = 'EXPECTED_{}_VERSION'.format(package_name.upper())
|
env_name = f'EXPECTED_{package_name.upper()}_VERSION'
|
||||||
build_environment[env_name] = version
|
build_environment[env_name] = version
|
||||||
|
|
||||||
cibw_environment_option = ' '.join(
|
cibw_environment_option = ' '.join(
|
||||||
['{}={}'.format(k, v) for k, v in build_environment.items()]
|
[f'{k}={v}' for k, v in build_environment.items()]
|
||||||
)
|
)
|
||||||
|
|
||||||
# build and test the wheels
|
# build and test the wheels
|
||||||
@@ -1,19 +1,34 @@
|
|||||||
import os
|
|
||||||
import platform
|
import platform
|
||||||
|
import textwrap
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import utils
|
from . import utils
|
||||||
|
from . import test_projects
|
||||||
|
|
||||||
|
dockcross_only_project = test_projects.new_c_project(
|
||||||
|
setup_py_add=textwrap.dedent(r'''
|
||||||
|
import os, sys
|
||||||
|
|
||||||
|
# check that we're running in the correct docker image as specified in the
|
||||||
|
# environment options CIBW_MANYLINUX1_*_IMAGE
|
||||||
|
if "linux" in sys.platform and not os.path.exists("/dockcross"):
|
||||||
|
raise Exception(
|
||||||
|
"/dockcross directory not found. Is this test running in the correct docker image?"
|
||||||
|
)
|
||||||
|
''')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test():
|
def test(tmp_path):
|
||||||
project_dir = os.path.dirname(__file__)
|
|
||||||
|
|
||||||
if utils.platform != 'linux':
|
if utils.platform != 'linux':
|
||||||
pytest.skip('the test is only relevant to the linux build')
|
pytest.skip('the test is only relevant to the linux build')
|
||||||
if platform.machine() not in ['x86_64', 'i686']:
|
if platform.machine() not in ['x86_64', 'i686']:
|
||||||
pytest.skip('this test is currently only possible on x86_64/i686 due to availability of alternative images')
|
pytest.skip('this test is currently only possible on x86_64/i686 due to availability of alternative images')
|
||||||
|
|
||||||
|
project_dir = tmp_path / 'project'
|
||||||
|
dockcross_only_project.generate(project_dir)
|
||||||
|
|
||||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
||||||
'CIBW_MANYLINUX_X86_64_IMAGE': 'dockcross/manylinux2010-x64',
|
'CIBW_MANYLINUX_X86_64_IMAGE': 'dockcross/manylinux2010-x64',
|
||||||
'CIBW_MANYLINUX_I686_IMAGE': 'dockcross/manylinux2010-x86',
|
'CIBW_MANYLINUX_I686_IMAGE': 'dockcross/manylinux2010-x86',
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import os
|
||||||
|
import pytest
|
||||||
|
import subprocess
|
||||||
|
import textwrap
|
||||||
|
from . import utils
|
||||||
|
from . import test_projects
|
||||||
|
|
||||||
|
|
||||||
|
project_with_environment_asserts = test_projects.new_c_project(
|
||||||
|
setup_py_add=textwrap.dedent(r'''
|
||||||
|
import os
|
||||||
|
|
||||||
|
# explode if environment isn't correct, as set in CIBW_ENVIRONMENT
|
||||||
|
CIBW_TEST_VAR = os.environ.get("CIBW_TEST_VAR")
|
||||||
|
CIBW_TEST_VAR_2 = os.environ.get("CIBW_TEST_VAR_2")
|
||||||
|
PATH = os.environ.get("PATH")
|
||||||
|
|
||||||
|
if CIBW_TEST_VAR != "a b c":
|
||||||
|
raise Exception('CIBW_TEST_VAR should equal "a b c". It was "%s"' % CIBW_TEST_VAR)
|
||||||
|
if CIBW_TEST_VAR_2 != "1":
|
||||||
|
raise Exception('CIBW_TEST_VAR_2 should equal "1". It was "%s"' % CIBW_TEST_VAR_2)
|
||||||
|
if "/opt/cibw_test_path" not in PATH:
|
||||||
|
raise Exception('PATH should contain "/opt/cibw_test_path". It was "%s"' % PATH)
|
||||||
|
if "$PATH" in PATH:
|
||||||
|
raise Exception('$PATH should be expanded in PATH. It was "%s"' % PATH)
|
||||||
|
''')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test(tmp_path):
|
||||||
|
project_dir = tmp_path / 'project'
|
||||||
|
project_with_environment_asserts.generate(project_dir)
|
||||||
|
|
||||||
|
# write some information into the CIBW_ENVIRONMENT, for expansion and
|
||||||
|
# insertion into the environment by cibuildwheel. This is checked
|
||||||
|
# in setup.py
|
||||||
|
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
||||||
|
'CIBW_ENVIRONMENT': '''CIBW_TEST_VAR="a b c" CIBW_TEST_VAR_2=1 CIBW_TEST_VAR_3="$(echo 'test string 3')" PATH=$PATH:/opt/cibw_test_path''',
|
||||||
|
'CIBW_ENVIRONMENT_WINDOWS': '''CIBW_TEST_VAR="a b c" CIBW_TEST_VAR_2=1 CIBW_TEST_VAR_3="$(echo 'test string 3')" PATH="$PATH;/opt/cibw_test_path"''',
|
||||||
|
})
|
||||||
|
|
||||||
|
# also check that we got the right wheels built
|
||||||
|
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
||||||
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
|
|
||||||
|
def test_overridden_path(tmp_path):
|
||||||
|
project_dir = tmp_path / 'project'
|
||||||
|
output_dir = tmp_path / 'output'
|
||||||
|
|
||||||
|
project = test_projects.new_c_project()
|
||||||
|
project.generate(project_dir)
|
||||||
|
output_dir.mkdir()
|
||||||
|
|
||||||
|
# mess up PATH, somehow
|
||||||
|
with pytest.raises(subprocess.CalledProcessError):
|
||||||
|
utils.cibuildwheel_run(project_dir, output_dir=output_dir, add_env={
|
||||||
|
'CIBW_ENVIRONMENT': '''SOMETHING="$(mkdir new_path && touch new_path/python)" PATH="$(realpath new_path):$PATH"''',
|
||||||
|
'CIBW_ENVIRONMENT_WINDOWS': '''SOMETHING="$(mkdir new_path && type nul > new_path/python.exe)" PATH="$CD\\new_path;$PATH"''',
|
||||||
|
})
|
||||||
|
assert len(os.listdir(output_dir)) == 0
|
||||||
@@ -1,21 +1,48 @@
|
|||||||
import os
|
|
||||||
import platform
|
import platform
|
||||||
|
import textwrap
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import utils
|
from . import utils
|
||||||
|
from . import test_projects
|
||||||
|
|
||||||
|
# TODO: specify these at runtime according to manylinux_image
|
||||||
|
project_with_manylinux_symbols = test_projects.new_c_project(
|
||||||
|
spam_c_top_level_add=textwrap.dedent(r'''
|
||||||
|
#include <malloc.h>
|
||||||
|
|
||||||
|
#if !defined(__GLIBC_PREREQ)
|
||||||
|
#error "Must run on a glibc linux environment"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !__GLIBC_PREREQ(2, 5) /* manylinux1 is glibc 2.5 */
|
||||||
|
#error "Must run on a glibc >= 2.5 linux environment"
|
||||||
|
#endif
|
||||||
|
'''),
|
||||||
|
spam_c_function_add=textwrap.dedent(r'''
|
||||||
|
#if defined(__GLIBC_PREREQ) && __GLIBC_PREREQ(2, 17) /* manylinux2014 is glibc 2.17 */
|
||||||
|
// secure_getenv is only available in manylinux2014, ensuring
|
||||||
|
// that only a manylinux2014 wheel is produced
|
||||||
|
secure_getenv("NON_EXISTING_ENV_VARIABLE");
|
||||||
|
#elif defined(__GLIBC_PREREQ) && __GLIBC_PREREQ(2, 10) /* manylinux2010 is glibc 2.12 */
|
||||||
|
// malloc_info is only available on manylinux2010+
|
||||||
|
malloc_info(0, stdout);
|
||||||
|
#endif
|
||||||
|
'''),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('manylinux_image', ['manylinux1', 'manylinux2010', 'manylinux2014'])
|
@pytest.mark.parametrize('manylinux_image', ['manylinux1', 'manylinux2010', 'manylinux2014'])
|
||||||
def test(manylinux_image):
|
def test(manylinux_image, tmp_path):
|
||||||
project_dir = os.path.dirname(__file__)
|
|
||||||
|
|
||||||
if utils.platform != 'linux':
|
if utils.platform != 'linux':
|
||||||
pytest.skip('the docker test is only relevant to the linux build')
|
pytest.skip('the 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']:
|
if manylinux_image in ['manylinux1', 'manylinux2010']:
|
||||||
pytest.skip("manylinux1 and 2010 doesn't exist for non-x86 architectures")
|
pytest.skip("manylinux1 and 2010 doesn't exist for non-x86 architectures")
|
||||||
|
|
||||||
|
project_dir = tmp_path / 'project'
|
||||||
|
project_with_manylinux_symbols.generate(project_dir)
|
||||||
|
|
||||||
# build the wheels
|
# build the wheels
|
||||||
# CFLAGS environment variable is necessary to fail on 'malloc_info' (on manylinux1) during compilation/linking,
|
# CFLAGS environment variable is necessary to fail on 'malloc_info' (on manylinux1) during compilation/linking,
|
||||||
# rather than when dynamically loading the Python
|
# rather than when dynamically loading the Python
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
from .base import TestProject # noqa
|
||||||
|
from .c import new_c_project # noqa
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
from argparse import ArgumentParser
|
||||||
|
import importlib
|
||||||
|
import tempfile
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = ArgumentParser(
|
||||||
|
prog="python -m test.test_projects",
|
||||||
|
description='Generate a test project to check it out'
|
||||||
|
)
|
||||||
|
parser.add_argument('PROJECT', help='''
|
||||||
|
Python path to a project object. E.g. test.test_0_basic.basic_project
|
||||||
|
''')
|
||||||
|
options = parser.parse_args()
|
||||||
|
|
||||||
|
module, _, name = options.PROJECT.rpartition('.')
|
||||||
|
|
||||||
|
project = getattr(importlib.import_module(module), name)
|
||||||
|
|
||||||
|
project_dir = tempfile.mkdtemp()
|
||||||
|
project.generate(project_dir)
|
||||||
|
|
||||||
|
print('Project generated at', project_dir)
|
||||||
|
print()
|
||||||
|
|
||||||
|
if sys.platform == 'darwin':
|
||||||
|
subprocess.check_call(['open', '--', project_dir])
|
||||||
|
elif sys.platform == 'linux2':
|
||||||
|
subprocess.check_call(['xdg-open', '--', project_dir])
|
||||||
|
elif sys.platform == 'win32':
|
||||||
|
subprocess.check_call(['explorer', project_dir])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import os
|
||||||
|
import jinja2
|
||||||
|
from typing import Union, Dict, Any
|
||||||
|
|
||||||
|
|
||||||
|
FilesDict = Dict[str, Union[str, jinja2.Template]]
|
||||||
|
TemplateContext = Dict[str, Any]
|
||||||
|
|
||||||
|
|
||||||
|
class TestProject:
|
||||||
|
'''
|
||||||
|
An object that represents a project that can be built by cibuildwheel.
|
||||||
|
Can be manipulated in tests by changing `files` and `template_context`.
|
||||||
|
|
||||||
|
Write out to the filesystem using `generate`.
|
||||||
|
'''
|
||||||
|
files: FilesDict
|
||||||
|
template_context: TemplateContext
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.files = {}
|
||||||
|
self.template_context = {}
|
||||||
|
|
||||||
|
def generate(self, path: str):
|
||||||
|
for filename, content in self.files.items():
|
||||||
|
file_path = os.path.join(path, filename)
|
||||||
|
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
||||||
|
|
||||||
|
with open(file_path, 'w', encoding='utf8') as f:
|
||||||
|
if isinstance(content, jinja2.Template):
|
||||||
|
content = content.render(self.template_context)
|
||||||
|
|
||||||
|
f.write(content)
|
||||||
|
|
||||||
|
def copy(self):
|
||||||
|
other = TestProject()
|
||||||
|
other.files = self.files.copy()
|
||||||
|
other.template_context = self.template_context.copy()
|
||||||
|
return other
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
import jinja2
|
||||||
|
from .base import TestProject
|
||||||
|
|
||||||
|
|
||||||
|
SPAM_C_TEMPLATE = r'''
|
||||||
|
#include <Python.h>
|
||||||
|
|
||||||
|
{{ spam_c_top_level_add }}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
spam_system(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
const char *command;
|
||||||
|
int sts;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple(args, "s", &command))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
sts = system(command);
|
||||||
|
|
||||||
|
{{ spam_c_function_add | indent(4) }}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
|
||||||
|
SETUP_PY_TEMPLATE = r'''
|
||||||
|
from setuptools import setup, Extension
|
||||||
|
|
||||||
|
{{ setup_py_add }}
|
||||||
|
|
||||||
|
setup(
|
||||||
|
ext_modules=[Extension('spam', sources=['spam.c'])],
|
||||||
|
{{ setup_py_setup_args_add | indent(4) }}
|
||||||
|
)
|
||||||
|
'''
|
||||||
|
|
||||||
|
SETUP_CFG_TEMPLATE = r'''
|
||||||
|
[metadata]
|
||||||
|
name = spam
|
||||||
|
version = 0.1.0
|
||||||
|
|
||||||
|
{{ setup_cfg_add }}
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
def new_c_project(*, spam_c_top_level_add='', spam_c_function_add='', setup_py_add='',
|
||||||
|
setup_py_setup_args_add='', setup_cfg_add=''):
|
||||||
|
project = TestProject()
|
||||||
|
|
||||||
|
project.files.update({
|
||||||
|
'spam.c': jinja2.Template(SPAM_C_TEMPLATE),
|
||||||
|
'setup.py': jinja2.Template(SETUP_PY_TEMPLATE),
|
||||||
|
'setup.cfg': jinja2.Template(SETUP_CFG_TEMPLATE),
|
||||||
|
})
|
||||||
|
|
||||||
|
project.template_context.update({
|
||||||
|
'spam_c_top_level_add': spam_c_top_level_add,
|
||||||
|
'spam_c_function_add': spam_c_function_add,
|
||||||
|
'setup_py_add': setup_py_add,
|
||||||
|
'setup_py_setup_args_add': setup_py_setup_args_add,
|
||||||
|
'setup_cfg_add': setup_cfg_add,
|
||||||
|
})
|
||||||
|
|
||||||
|
return project
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import textwrap
|
||||||
|
|
||||||
|
from . import utils
|
||||||
|
from . import test_projects
|
||||||
|
|
||||||
|
project_with_ssl_tests = test_projects.new_c_project(
|
||||||
|
setup_py_add=textwrap.dedent(r'''
|
||||||
|
import ssl
|
||||||
|
import sys
|
||||||
|
|
||||||
|
if sys.version_info[0] == 2:
|
||||||
|
from urllib2 import urlopen
|
||||||
|
else:
|
||||||
|
from urllib.request import urlopen
|
||||||
|
|
||||||
|
if sys.version_info[0:2] == (3, 3):
|
||||||
|
data = urlopen("https://www.nist.gov")
|
||||||
|
else:
|
||||||
|
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
|
||||||
|
data = urlopen("https://www.nist.gov", context=context)
|
||||||
|
''')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test(tmp_path):
|
||||||
|
# this test checks that SSL is working in the build environment using
|
||||||
|
# some checks in setup.py.
|
||||||
|
project_dir = tmp_path / 'project'
|
||||||
|
project_with_ssl_tests.generate(project_dir)
|
||||||
|
|
||||||
|
actual_wheels = utils.cibuildwheel_run(project_dir)
|
||||||
|
|
||||||
|
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
||||||
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
import jinja2
|
||||||
|
|
||||||
|
from . import utils
|
||||||
|
from .test_projects import TestProject
|
||||||
|
from .test_projects.c import SPAM_C_TEMPLATE
|
||||||
|
|
||||||
|
subdir_package_project = TestProject()
|
||||||
|
|
||||||
|
subdir_package_project.files['src/spam/spam.c'] = jinja2.Template(SPAM_C_TEMPLATE)
|
||||||
|
subdir_package_project.template_context['spam_c_top_level_add'] = ''
|
||||||
|
subdir_package_project.template_context['spam_c_function_add'] = ''
|
||||||
|
|
||||||
|
subdir_package_project.files['src/spam/setup.py'] = r'''
|
||||||
|
from setuptools import Extension, setup
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name="spam",
|
||||||
|
ext_modules=[Extension('spam', sources=['spam.c'])],
|
||||||
|
version="0.1.0",
|
||||||
|
)
|
||||||
|
'''
|
||||||
|
|
||||||
|
subdir_package_project.files['src/spam/test/run_tests.py'] = r'''
|
||||||
|
print('run_tests.py executed!')
|
||||||
|
'''
|
||||||
|
|
||||||
|
subdir_package_project.files['bin/before_build.py'] = r'''
|
||||||
|
print('before_build.py executed!')
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
def test(capfd, tmp_path):
|
||||||
|
project_dir = tmp_path / 'project'
|
||||||
|
subdir_package_project.generate(project_dir)
|
||||||
|
|
||||||
|
package_dir = os.path.join(project_dir, 'src', 'spam')
|
||||||
|
# build the wheels
|
||||||
|
actual_wheels = utils.cibuildwheel_run(project_dir, package_dir=package_dir, add_env={
|
||||||
|
'CIBW_BEFORE_BUILD': 'python {project}/bin/before_build.py',
|
||||||
|
'CIBW_TEST_COMMAND': 'python {package}/test/run_tests.py',
|
||||||
|
# this shouldn't depend on the version of python, so build only CPython 3.6
|
||||||
|
'CIBW_BUILD': 'cp36-*',
|
||||||
|
})
|
||||||
|
|
||||||
|
# check that the expected wheels are produced
|
||||||
|
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||||
|
if 'cp36' in w]
|
||||||
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
|
captured = capfd.readouterr()
|
||||||
|
assert "before_build.py executed!" in captured.out
|
||||||
|
assert "run_tests.py executed!" in captured.out
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import textwrap
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from . import utils
|
||||||
|
from . import test_projects
|
||||||
|
|
||||||
|
project_with_a_test = test_projects.new_c_project(
|
||||||
|
setup_cfg_add=textwrap.dedent(r'''
|
||||||
|
[options.extras_require]
|
||||||
|
test = nose
|
||||||
|
''')
|
||||||
|
)
|
||||||
|
|
||||||
|
project_with_a_test.files['test/spam_test.py'] = r'''
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
import sys
|
||||||
|
import struct
|
||||||
|
from unittest import TestCase
|
||||||
|
|
||||||
|
import spam
|
||||||
|
|
||||||
|
|
||||||
|
def path_contains(parent, child):
|
||||||
|
""" returns True if `child` is inside `parent`.
|
||||||
|
Works around path-comparison bugs caused by short-paths on Windows e.g.
|
||||||
|
vssadm~1 instead of vssadministrator
|
||||||
|
"""
|
||||||
|
parent = os.path.abspath(parent)
|
||||||
|
child = os.path.abspath(child)
|
||||||
|
|
||||||
|
while child != os.path.dirname(child):
|
||||||
|
child = os.path.dirname(child)
|
||||||
|
if os.stat(parent) == os.stat(child):
|
||||||
|
# parent and child refer to the same directory on the filesystem
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class TestSpam(TestCase):
|
||||||
|
def test_system(self):
|
||||||
|
self.assertEqual(0, spam.system('python -c "exit(0)"'))
|
||||||
|
self.assertNotEqual(0, spam.system('python -c "exit(1)"'))
|
||||||
|
|
||||||
|
def test_virtualenv(self):
|
||||||
|
virtualenv_path = os.environ.get("__CIBW_VIRTUALENV_PATH__")
|
||||||
|
if not virtualenv_path:
|
||||||
|
self.fail("No virtualenv path defined in environment variable __CIBW_VIRTUALENV_PATH__")
|
||||||
|
|
||||||
|
self.assertTrue(path_contains(virtualenv_path, sys.executable))
|
||||||
|
self.assertTrue(path_contains(virtualenv_path, spam.__file__))
|
||||||
|
|
||||||
|
def test_uname(self):
|
||||||
|
if platform.system() == "Windows":
|
||||||
|
return
|
||||||
|
# if we're running in 32-bit Python, check that the machine is i686.
|
||||||
|
# See #336 for more info.
|
||||||
|
bits = struct.calcsize("P") * 8
|
||||||
|
if bits == 32:
|
||||||
|
self.assertEqual(platform.machine(), "i686")
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
def test(tmp_path):
|
||||||
|
project_dir = tmp_path / 'project'
|
||||||
|
project_with_a_test.generate(project_dir)
|
||||||
|
|
||||||
|
# build and test the wheels
|
||||||
|
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
||||||
|
'CIBW_TEST_REQUIRES': 'nose',
|
||||||
|
# the 'false ||' bit is to ensure this command runs in a shell on
|
||||||
|
# mac/linux.
|
||||||
|
'CIBW_TEST_COMMAND': 'false || nosetests {project}/test',
|
||||||
|
'CIBW_TEST_COMMAND_WINDOWS': 'COLOR 00 || nosetests {project}/test',
|
||||||
|
})
|
||||||
|
|
||||||
|
# also check that we got the right wheels
|
||||||
|
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
||||||
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
|
|
||||||
|
def test_extras_require(tmp_path):
|
||||||
|
project_dir = tmp_path / 'project'
|
||||||
|
project_with_a_test.generate(project_dir)
|
||||||
|
|
||||||
|
# build and test the wheels
|
||||||
|
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
||||||
|
'CIBW_TEST_EXTRAS': 'test',
|
||||||
|
# the 'false ||' bit is to ensure this command runs in a shell on
|
||||||
|
# mac/linux.
|
||||||
|
'CIBW_TEST_COMMAND': 'false || nosetests {project}/test',
|
||||||
|
'CIBW_TEST_COMMAND_WINDOWS': 'COLOR 00 || nosetests {project}/test',
|
||||||
|
})
|
||||||
|
|
||||||
|
# also check that we got the right wheels
|
||||||
|
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
||||||
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
|
|
||||||
|
def test_failing_test(tmp_path):
|
||||||
|
"""Ensure a failing test causes cibuildwheel to error out and exit"""
|
||||||
|
project_dir = tmp_path / 'project'
|
||||||
|
output_dir = tmp_path / 'output'
|
||||||
|
project_with_a_test.generate(project_dir)
|
||||||
|
|
||||||
|
with pytest.raises(subprocess.CalledProcessError):
|
||||||
|
utils.cibuildwheel_run(project_dir, output_dir=output_dir, add_env={
|
||||||
|
'CIBW_TEST_COMMAND': 'false',
|
||||||
|
# manylinux1 has a version of bash that's been shown to have
|
||||||
|
# problems with this, so let's check that.
|
||||||
|
'CIBW_MANYLINUX_I686_IMAGE': 'manylinux1',
|
||||||
|
'CIBW_MANYLINUX_X86_64_IMAGE': 'manylinux1',
|
||||||
|
})
|
||||||
|
|
||||||
|
assert len(os.listdir(output_dir)) == 0
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import textwrap
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from . import utils
|
||||||
|
from . import test_projects
|
||||||
|
|
||||||
|
project_with_unicode = test_projects.new_c_project(
|
||||||
|
spam_c_function_add=textwrap.dedent(r'''
|
||||||
|
{
|
||||||
|
Py_XDECREF(PyUnicode_FromStringAndSize("foo", 4));
|
||||||
|
}
|
||||||
|
'''),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test(tmp_path):
|
||||||
|
if utils.platform != 'linux':
|
||||||
|
pytest.skip('the docker test is only relevant to the linux build')
|
||||||
|
|
||||||
|
project_dir = tmp_path / 'project'
|
||||||
|
project_with_unicode.generate(project_dir)
|
||||||
|
|
||||||
|
# build the wheels
|
||||||
|
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
||||||
|
'CIBW_TEST_COMMAND': 'python -c "import spam"'
|
||||||
|
})
|
||||||
|
|
||||||
|
# check that the expected wheels are produced
|
||||||
|
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
||||||
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
@@ -33,7 +33,7 @@ def cibuildwheel_get_build_identifiers(project_path, env=None):
|
|||||||
for the current platform.
|
for the current platform.
|
||||||
'''
|
'''
|
||||||
cmd_output = subprocess.check_output(
|
cmd_output = subprocess.check_output(
|
||||||
[sys.executable, '-m', 'cibuildwheel', '--print-build-identifiers', project_path],
|
[sys.executable, '-m', 'cibuildwheel', '--print-build-identifiers', str(project_path)],
|
||||||
universal_newlines=True,
|
universal_newlines=True,
|
||||||
env=env,
|
env=env,
|
||||||
)
|
)
|
||||||
@@ -110,7 +110,7 @@ def expected_wheels(package_name, package_version, manylinux_versions=None,
|
|||||||
architectures.append('i686')
|
architectures.append('i686')
|
||||||
|
|
||||||
platform_tags = [
|
platform_tags = [
|
||||||
'{}_{}'.format(manylinux_version, architecture)
|
f'{manylinux_version}_{architecture}'
|
||||||
for architecture in architectures
|
for architecture in architectures
|
||||||
for manylinux_version in manylinux_versions
|
for manylinux_version in manylinux_versions
|
||||||
]
|
]
|
||||||
@@ -122,16 +122,13 @@ def expected_wheels(package_name, package_version, manylinux_versions=None,
|
|||||||
platform_tags = ['win32']
|
platform_tags = ['win32']
|
||||||
|
|
||||||
elif platform == 'macos':
|
elif platform == 'macos':
|
||||||
platform_tags = ['macosx_{}_x86_64'.format(macosx_deployment_target.replace('.', '_'))]
|
platform_tags = [f'macosx_{macosx_deployment_target.replace(".", "_")}_x86_64']
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise Exception('unsupported platform')
|
raise Exception('unsupported platform')
|
||||||
|
|
||||||
for platform_tag in platform_tags:
|
for platform_tag in platform_tags:
|
||||||
wheels.append('{package_name}-{package_version}-{python_abi_tag}-{platform_tag}.whl'.format(
|
wheels.append(f'{package_name}-{package_version}-{python_abi_tag}-{platform_tag}.whl')
|
||||||
package_name=package_name, package_version=package_version,
|
|
||||||
python_abi_tag=python_abi_tag, platform_tag=platform_tag
|
|
||||||
))
|
|
||||||
|
|
||||||
if IS_WINDOWS_RUNNING_ON_TRAVIS:
|
if IS_WINDOWS_RUNNING_ON_TRAVIS:
|
||||||
# Python 2.7 isn't supported on Travis.
|
# Python 2.7 isn't supported on Travis.
|
||||||
@@ -123,7 +123,7 @@ def test_repair_command(repair_command, platform_specific, platform, intercepted
|
|||||||
])
|
])
|
||||||
@pytest.mark.parametrize('platform_specific', [False, True])
|
@pytest.mark.parametrize('platform_specific', [False, True])
|
||||||
def test_environment(environment, platform_specific, platform, intercepted_build_args, monkeypatch):
|
def test_environment(environment, platform_specific, platform, intercepted_build_args, monkeypatch):
|
||||||
env_string = ' '.join(['{}={}'.format(k, v) for k, v in environment.items()])
|
env_string = ' '.join([f'{k}={v}' for k, v in environment.items()])
|
||||||
if platform_specific:
|
if platform_specific:
|
||||||
monkeypatch.setenv('CIBW_ENVIRONMENT_' + platform.upper(), env_string)
|
monkeypatch.setenv('CIBW_ENVIRONMENT_' + platform.upper(), env_string)
|
||||||
monkeypatch.setenv('CIBW_ENVIRONMENT', 'overwritten')
|
monkeypatch.setenv('CIBW_ENVIRONMENT', 'overwritten')
|
||||||
|
|||||||
Reference in New Issue
Block a user