style: apply black via pre-commit run -a
This commit is contained in:
committed by
Henry Schreiner
parent
9cbed6a9be
commit
178aaea6c7
+36
-17
@@ -37,11 +37,18 @@ def bump_version() -> None:
|
||||
current_version = cibuildwheel.__version__
|
||||
|
||||
try:
|
||||
commit_date_str = subprocess.run([
|
||||
'git',
|
||||
'show', '--no-patch', '--pretty=format:%ci',
|
||||
f'v{current_version}^{{commit}}',
|
||||
], check=True, capture_output=True, encoding='utf8').stdout
|
||||
commit_date_str = subprocess.run(
|
||||
[
|
||||
'git',
|
||||
'show',
|
||||
'--no-patch',
|
||||
'--pretty=format:%ci',
|
||||
f'v{current_version}^{{commit}}',
|
||||
],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
encoding='utf8',
|
||||
).stdout
|
||||
cd_date, cd_time, cd_tz = commit_date_str.split(' ')
|
||||
|
||||
url_opts = urllib.parse.urlencode({'q': f'is:pr merged:>{cd_date}T{cd_time}{cd_tz}'})
|
||||
@@ -90,7 +97,11 @@ def bump_version() -> None:
|
||||
if find_pattern in contents:
|
||||
found_at_least_one_file_needing_update = True
|
||||
actions.append(
|
||||
(path, find_pattern, replace_pattern,)
|
||||
(
|
||||
path,
|
||||
find_pattern,
|
||||
replace_pattern,
|
||||
)
|
||||
)
|
||||
|
||||
if not found_at_least_one_file_needing_update:
|
||||
@@ -125,18 +136,26 @@ def bump_version() -> None:
|
||||
while input('Type "done" to continue: ').strip().lower() != 'done':
|
||||
pass
|
||||
|
||||
subprocess.run([
|
||||
'git', 'commit',
|
||||
'--all',
|
||||
f"--message=Bump version: v{new_version}",
|
||||
], check=True)
|
||||
subprocess.run(
|
||||
[
|
||||
'git',
|
||||
'commit',
|
||||
'--all',
|
||||
f"--message=Bump version: v{new_version}",
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
|
||||
subprocess.run([
|
||||
'git', 'tag',
|
||||
'--annotate',
|
||||
f"--message=v{new_version}",
|
||||
f'v{new_version}',
|
||||
], check=True)
|
||||
subprocess.run(
|
||||
[
|
||||
'git',
|
||||
'tag',
|
||||
'--annotate',
|
||||
f"--message=v{new_version}",
|
||||
f'v{new_version}',
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
|
||||
print('Done.')
|
||||
|
||||
|
||||
@@ -31,10 +31,9 @@ def main():
|
||||
print('Your git repo has uncommitted changes. Commit or stash before continuing.')
|
||||
sys.exit(1)
|
||||
|
||||
previous_branch = shell('git rev-parse --abbrev-ref HEAD',
|
||||
check=True,
|
||||
capture_output=True,
|
||||
encoding='utf8').stdout.strip()
|
||||
previous_branch = shell(
|
||||
'git rev-parse --abbrev-ref HEAD', check=True, capture_output=True, encoding='utf8'
|
||||
).stdout.strip()
|
||||
|
||||
shell('git fetch origin', check=True)
|
||||
|
||||
@@ -51,20 +50,24 @@ def main():
|
||||
return
|
||||
|
||||
shell('git commit -a -m "Update dependencies"', check=True)
|
||||
body = textwrap.dedent(f'''
|
||||
body = textwrap.dedent(
|
||||
f'''
|
||||
Update the versions of our dependencies.
|
||||
|
||||
PR generated by `{os.path.basename(__file__)}`.
|
||||
''')
|
||||
'''
|
||||
)
|
||||
run(
|
||||
[
|
||||
'gh', 'pr', 'create',
|
||||
'gh',
|
||||
'pr',
|
||||
'create',
|
||||
'--repo=joerick/cibuildwheel',
|
||||
'--base=master',
|
||||
"--title=Update dependencies",
|
||||
f"--body='{body}'",
|
||||
],
|
||||
check=True
|
||||
check=True,
|
||||
)
|
||||
|
||||
print('Done.')
|
||||
|
||||
+12
-4
@@ -83,9 +83,11 @@ class Project:
|
||||
|
||||
@classmethod
|
||||
def header(cls) -> str:
|
||||
return textwrap.dedent(f"""\
|
||||
return textwrap.dedent(
|
||||
f"""\
|
||||
| {'Name':{cls.NAME}} | CI | OS | Notes |
|
||||
|{'':-^{cls.NAME+2 }}|----|----|:------|""")
|
||||
|{'':-^{cls.NAME+2 }}|----|----|:------|"""
|
||||
)
|
||||
|
||||
@property
|
||||
def namelink(self) -> str:
|
||||
@@ -142,7 +144,10 @@ def path_for_icon(icon_name: str) -> Path:
|
||||
|
||||
|
||||
def str_projects(
|
||||
config: list[dict[str, Any]], *, online: bool = True, auth: str | None = None,
|
||||
config: list[dict[str, Any]],
|
||||
*,
|
||||
online: bool = True,
|
||||
auth: str | None = None,
|
||||
) -> str:
|
||||
io = StringIO()
|
||||
print = functools.partial(builtins.print, file=io)
|
||||
@@ -180,7 +185,10 @@ def str_projects(
|
||||
@click.option("--auth", help="GitHub authentication token")
|
||||
@click.option("--readme", type=click.File("r+"), help="Modify a readme file if given")
|
||||
def projects(
|
||||
input: TextIO, online: bool, auth: str | None, readme: TextIO | None,
|
||||
input: TextIO,
|
||||
online: bool,
|
||||
auth: str | None,
|
||||
readme: TextIO | None,
|
||||
) -> None:
|
||||
config = yaml.safe_load(input)
|
||||
output = str_projects(config, online=online, auth=auth)
|
||||
|
||||
@@ -64,7 +64,7 @@ services = [
|
||||
CIService(
|
||||
name='gitlab',
|
||||
dst_config_path='.gitlab-ci.yml',
|
||||
badge_md='[](https://gitlab.com/joerick/cibuildwheel/-/commits/{branch})'
|
||||
badge_md='[](https://gitlab.com/joerick/cibuildwheel/-/commits/{branch})',
|
||||
),
|
||||
]
|
||||
|
||||
@@ -83,8 +83,8 @@ def ci_service_for_config_file(config_file):
|
||||
@click.argument('config_files', nargs=-1, type=click.Path())
|
||||
def run_example_ci_configs(config_files=None):
|
||||
'''
|
||||
Test the example configs. If no files are specified, will test
|
||||
examples/*-minimal.yml
|
||||
Test the example configs. If no files are specified, will test
|
||||
examples/*-minimal.yml
|
||||
'''
|
||||
|
||||
if len(config_files) == 0:
|
||||
@@ -102,10 +102,9 @@ def run_example_ci_configs(config_files=None):
|
||||
print('Your git repo has uncommitted changes. Commit or stash before continuing.')
|
||||
sys.exit(1)
|
||||
|
||||
previous_branch = shell('git rev-parse --abbrev-ref HEAD',
|
||||
check=True,
|
||||
capture_output=True,
|
||||
encoding='utf8').stdout.strip()
|
||||
previous_branch = shell(
|
||||
'git rev-parse --abbrev-ref HEAD', check=True, capture_output=True, encoding='utf8'
|
||||
).stdout.strip()
|
||||
|
||||
timestamp = time.strftime('%Y-%m-%dT%H-%M-%S', time.gmtime())
|
||||
branch_name = f'example-config-test---{previous_branch}-{timestamp}'
|
||||
@@ -125,13 +124,15 @@ def run_example_ci_configs(config_files=None):
|
||||
shutil.copyfile(src_config_file, dst_config_file)
|
||||
|
||||
run(['git', 'add', example_project], check=True)
|
||||
message = textwrap.dedent(f'''\
|
||||
message = textwrap.dedent(
|
||||
f'''\
|
||||
Test example minimal configs
|
||||
|
||||
Testing files: {config_files}
|
||||
Generated from branch: {previous_branch}
|
||||
Time: {timestamp}
|
||||
''')
|
||||
'''
|
||||
)
|
||||
run(['git', 'commit', '--no-verify', '--message', message], check=True)
|
||||
shell(f'git subtree --prefix={example_project} push origin {branch_name}', check=True)
|
||||
|
||||
@@ -139,13 +140,17 @@ def run_example_ci_configs(config_files=None):
|
||||
print()
|
||||
print('> **Examples test run**')
|
||||
print('> ')
|
||||
print(f'> Branch: [{branch_name}](https://github.com/joerick/cibuildwheel/tree/{branch_name})')
|
||||
print(
|
||||
f'> Branch: [{branch_name}](https://github.com/joerick/cibuildwheel/tree/{branch_name})'
|
||||
)
|
||||
print('> ')
|
||||
print('> | Service | Config | Status |')
|
||||
print('> |---|---|---|')
|
||||
for config_file in config_files:
|
||||
service = ci_service_for_config_file(config_file)
|
||||
badge = service.badge_md.format(branch=branch_name, branch_escaped=quote(branch_name, safe=''))
|
||||
badge = service.badge_md.format(
|
||||
branch=branch_name, branch_escaped=quote(branch_name, safe='')
|
||||
)
|
||||
print(f'> | {service.name} | `{config_file}` | {badge} |')
|
||||
print('> ')
|
||||
print('> Generated by `bin/run_example_ci_config.py`')
|
||||
|
||||
+4
-1
@@ -17,4 +17,7 @@ if __name__ == '__main__':
|
||||
subprocess.run(unit_test_args, check=True)
|
||||
|
||||
# run the integration tests
|
||||
subprocess.run([sys.executable, '-m', 'pytest', '-x', '--durations', '0', '--timeout=2400', 'test'], check=True)
|
||||
subprocess.run(
|
||||
[sys.executable, '-m', 'pytest', '-x', '--durations', '0', '--timeout=2400', 'test'],
|
||||
check=True,
|
||||
)
|
||||
|
||||
+4
-4
@@ -19,9 +19,9 @@ if __name__ == '__main__':
|
||||
options = parser.parse_args()
|
||||
|
||||
project_dir = tempfile.mkdtemp()
|
||||
subprocess.run([
|
||||
sys.executable, '-m', 'test.test_projects',
|
||||
options.project_python_path, project_dir
|
||||
], check=True,)
|
||||
subprocess.run(
|
||||
[sys.executable, '-m', 'test.test_projects', options.project_python_path, project_dir],
|
||||
check=True,
|
||||
)
|
||||
|
||||
sys.exit(subprocess.run([sys.executable, '-m', 'cibuildwheel'], cwd=project_dir).returncode)
|
||||
|
||||
+32
-31
@@ -21,13 +21,16 @@ PYTHON_VERSIONS = ['27', '35', '36', '37', '38', '39']
|
||||
|
||||
if '--no-docker' in sys.argv:
|
||||
for python_version in PYTHON_VERSIONS:
|
||||
subprocess.run([
|
||||
f'./env{python_version}/bin/pip-compile',
|
||||
'--allow-unsafe',
|
||||
'--upgrade',
|
||||
'cibuildwheel/resources/constraints.in',
|
||||
'--output-file=cibuildwheel/resources/constraints-python{python_version}.txt',
|
||||
], check=True)
|
||||
subprocess.run(
|
||||
[
|
||||
f'./env{python_version}/bin/pip-compile',
|
||||
'--allow-unsafe',
|
||||
'--upgrade',
|
||||
'cibuildwheel/resources/constraints.in',
|
||||
'--output-file=cibuildwheel/resources/constraints-python{python_version}.txt',
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
else:
|
||||
# latest manylinux2010 image with cpython 2.7 support
|
||||
image_runner = 'quay.io/pypa/manylinux2010_x86_64:2021-02-06-3d322a5'
|
||||
@@ -41,19 +44,27 @@ else:
|
||||
'cibuildwheel/resources/constraints.in '
|
||||
f'--output-file cibuildwheel/resources/constraints-python{python_version}.txt'
|
||||
)
|
||||
subprocess.run([
|
||||
'docker', 'run',
|
||||
'--rm',
|
||||
'--env=CUSTOM_COMPILE_COMMAND',
|
||||
"--volume={os.getcwd()}:/volume",
|
||||
'--workdir=/volume',
|
||||
image_runner,
|
||||
'bash', '-c',
|
||||
command,
|
||||
], check=True)
|
||||
subprocess.run(
|
||||
[
|
||||
'docker',
|
||||
'run',
|
||||
'--rm',
|
||||
'--env=CUSTOM_COMPILE_COMMAND',
|
||||
"--volume={os.getcwd()}:/volume",
|
||||
'--workdir=/volume',
|
||||
image_runner,
|
||||
'bash',
|
||||
'-c',
|
||||
command,
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
|
||||
# default constraints.txt
|
||||
shutil.copyfile(f'cibuildwheel/resources/constraints-python{PYTHON_VERSIONS[-1]}.txt', 'cibuildwheel/resources/constraints.txt',)
|
||||
shutil.copyfile(
|
||||
f'cibuildwheel/resources/constraints-python{PYTHON_VERSIONS[-1]}.txt',
|
||||
'cibuildwheel/resources/constraints.txt',
|
||||
)
|
||||
|
||||
|
||||
class Image(NamedTuple):
|
||||
@@ -66,20 +77,16 @@ class Image(NamedTuple):
|
||||
images = [
|
||||
Image('manylinux1', 'x86_64', 'quay.io/pypa/manylinux1_x86_64', None),
|
||||
Image('manylinux1', 'i686', 'quay.io/pypa/manylinux1_i686', None),
|
||||
|
||||
# Images for manylinux2010 are pinned to the latest tag supporting cp27
|
||||
Image('manylinux2010', 'x86_64', 'quay.io/pypa/manylinux2010_x86_64', '2021-02-06-3d322a5'),
|
||||
Image('manylinux2010', 'i686', 'quay.io/pypa/manylinux2010_i686', '2021-02-06-3d322a5'),
|
||||
|
||||
Image('manylinux2010', 'pypy_x86_64', 'pypywheels/manylinux2010-pypy_x86_64', None),
|
||||
|
||||
# 2014 images
|
||||
Image('manylinux2014', 'x86_64', 'quay.io/pypa/manylinux2014_x86_64', None),
|
||||
Image('manylinux2014', 'i686', 'quay.io/pypa/manylinux2014_i686', None),
|
||||
Image('manylinux2014', 'aarch64', 'quay.io/pypa/manylinux2014_aarch64', None),
|
||||
Image('manylinux2014', 'ppc64le', 'quay.io/pypa/manylinux2014_ppc64le', None),
|
||||
Image('manylinux2014', 's390x', 'quay.io/pypa/manylinux2014_s390x', None),
|
||||
|
||||
# 2_24 images
|
||||
Image('manylinux_2_24', 'x86_64', 'quay.io/pypa/manylinux_2_24_x86_64', None),
|
||||
Image('manylinux_2_24', 'i686', 'quay.io/pypa/manylinux_2_24_i686', None),
|
||||
@@ -112,23 +119,17 @@ for image in images:
|
||||
if info['manifest_digest'] == latest_tag['manifest_digest']
|
||||
)
|
||||
else:
|
||||
response = requests.get(
|
||||
f'https://hub.docker.com/v2/repositories/{image.image_name}/tags'
|
||||
)
|
||||
response = requests.get(f'https://hub.docker.com/v2/repositories/{image.image_name}/tags')
|
||||
response.raise_for_status()
|
||||
tags = response.json()['results']
|
||||
|
||||
latest_tag = next(
|
||||
tag for tag in tags if tag['name'] == 'latest'
|
||||
)
|
||||
latest_tag = next(tag for tag in tags if tag['name'] == 'latest')
|
||||
# i don't know what it would mean to have multiple images per tag
|
||||
assert len(latest_tag['images']) == 1
|
||||
digest = latest_tag['images'][0]['digest']
|
||||
|
||||
pinned_tag = next(
|
||||
tag
|
||||
for tag in tags
|
||||
if tag != latest_tag and tag['images'][0]['digest'] == digest
|
||||
tag for tag in tags if tag != latest_tag and tag['images'][0]['digest'] == digest
|
||||
)
|
||||
tag_name = pinned_tag['name']
|
||||
|
||||
|
||||
+15
-5
@@ -112,7 +112,9 @@ class PyPyVersions:
|
||||
release["python_version"] = Version(release["python_version"])
|
||||
|
||||
self.releases = [
|
||||
r for r in releases if not r["pypy_version"].is_prerelease and not r["pypy_version"].is_devrelease
|
||||
r
|
||||
for r in releases
|
||||
if not r["pypy_version"].is_prerelease and not r["pypy_version"].is_devrelease
|
||||
]
|
||||
self.arch = arch_str
|
||||
|
||||
@@ -154,7 +156,9 @@ class PyPyVersions:
|
||||
identifier = f"pp{version.major}{version.minor}-macosx_x86_64"
|
||||
|
||||
(url,) = [
|
||||
rf["download_url"] for rf in release["files"] if "" in rf["platform"] == "darwin" and rf["arch"] == "x64"
|
||||
rf["download_url"]
|
||||
for rf in release["files"]
|
||||
if "" in rf["platform"] == "darwin" and rf["arch"] == "x64"
|
||||
]
|
||||
|
||||
return ConfigMacOS(
|
||||
@@ -167,7 +171,9 @@ class PyPyVersions:
|
||||
class CPythonVersions:
|
||||
def __init__(self) -> None:
|
||||
|
||||
response = requests.get("https://www.python.org/api/v2/downloads/release/?is_published=true")
|
||||
response = requests.get(
|
||||
"https://www.python.org/api/v2/downloads/release/?is_published=true"
|
||||
)
|
||||
response.raise_for_status()
|
||||
|
||||
releases_info = response.json()
|
||||
@@ -188,7 +194,9 @@ class CPythonVersions:
|
||||
for version in reversed(sorted_versions):
|
||||
# Find the first patch version that contains the requested file
|
||||
uri = self.versions_dict[version]
|
||||
response = requests.get(f"https://www.python.org/api/v2/downloads/release_file/?release={uri}")
|
||||
response = requests.get(
|
||||
f"https://www.python.org/api/v2/downloads/release_file/?release={uri}"
|
||||
)
|
||||
response.raise_for_status()
|
||||
file_info = response.json()
|
||||
|
||||
@@ -252,7 +260,9 @@ class AllVersions:
|
||||
|
||||
@click.command()
|
||||
@click.option("--force", is_flag=True)
|
||||
@click.option("--level", default="INFO", type=click.Choice(["INFO", "DEBUG", "TRACE"], case_sensitive=False))
|
||||
@click.option(
|
||||
"--level", default="INFO", type=click.Choice(["INFO", "DEBUG", "TRACE"], case_sensitive=False)
|
||||
)
|
||||
def update_pythons(force: bool, level: str) -> None:
|
||||
|
||||
logging.basicConfig(
|
||||
|
||||
+132
-58
@@ -30,10 +30,22 @@ from cibuildwheel.util import (
|
||||
|
||||
|
||||
@overload
|
||||
def get_option_from_environment(option_name: str, *, platform: Optional[str] = None, default: str) -> str: ... # noqa: E704
|
||||
def get_option_from_environment(
|
||||
option_name: str, *, platform: Optional[str] = None, 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
|
||||
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.
|
||||
|
||||
@@ -59,53 +71,66 @@ def main() -> None:
|
||||
epilog='''
|
||||
Most options are supplied via environment variables.
|
||||
See https://github.com/joerick/cibuildwheel#options for info.
|
||||
''')
|
||||
''',
|
||||
)
|
||||
|
||||
parser.add_argument('--platform',
|
||||
choices=['auto', 'linux', 'macos', 'windows'],
|
||||
default=os.environ.get('CIBW_PLATFORM', 'auto'),
|
||||
help='''
|
||||
parser.add_argument(
|
||||
'--platform',
|
||||
choices=['auto', 'linux', 'macos', 'windows'],
|
||||
default=os.environ.get('CIBW_PLATFORM', 'auto'),
|
||||
help='''
|
||||
Platform to build for. For "linux" you need docker running, on Mac
|
||||
or Linux. For "macos", you need a Mac machine, and note that this
|
||||
script is going to automatically install MacPython on your system,
|
||||
so don't run on your development machine. For "windows", you need to
|
||||
run in Windows, and it will build and test for all versions of
|
||||
Python. Default: auto.
|
||||
''')
|
||||
''',
|
||||
)
|
||||
|
||||
arch_list_str = ", ".join(a.name for a in Architecture)
|
||||
parser.add_argument('--archs',
|
||||
default=None,
|
||||
help=f'''
|
||||
parser.add_argument(
|
||||
'--archs',
|
||||
default=None,
|
||||
help=f'''
|
||||
Comma-separated list of CPU architectures to build for.
|
||||
When set to 'auto', builds the architectures natively supported
|
||||
on this machine. Set this option to build an architecture
|
||||
via emulation, for example, using binfmt_misc and QEMU.
|
||||
Default: auto.
|
||||
Choices: auto, auto64, auto32, native, all, {arch_list_str}
|
||||
''')
|
||||
''',
|
||||
)
|
||||
|
||||
parser.add_argument('--output-dir',
|
||||
default=os.environ.get('CIBW_OUTPUT_DIR', 'wheelhouse'),
|
||||
help='Destination folder for the wheels.')
|
||||
parser.add_argument(
|
||||
'--output-dir',
|
||||
default=os.environ.get('CIBW_OUTPUT_DIR', 'wheelhouse'),
|
||||
help='Destination folder for the wheels.',
|
||||
)
|
||||
|
||||
parser.add_argument('package_dir',
|
||||
default='.',
|
||||
nargs='?',
|
||||
help='''
|
||||
parser.add_argument(
|
||||
'package_dir',
|
||||
default='.',
|
||||
nargs='?',
|
||||
help='''
|
||||
Path to the package that you want wheels for. Must be a subdirectory of
|
||||
the working directory. When set, the working directory is still
|
||||
considered the 'project' and is copied into the Docker container on
|
||||
Linux. Default: the working directory.
|
||||
''')
|
||||
''',
|
||||
)
|
||||
|
||||
parser.add_argument('--print-build-identifiers',
|
||||
action='store_true',
|
||||
help='Print the build identifiers matched by the current invocation and exit.')
|
||||
parser.add_argument(
|
||||
'--print-build-identifiers',
|
||||
action='store_true',
|
||||
help='Print the build identifiers matched by the current invocation and exit.',
|
||||
)
|
||||
|
||||
parser.add_argument('--allow-empty',
|
||||
action='store_true',
|
||||
help='Do not report an error code if the build does not match any wheels.',)
|
||||
parser.add_argument(
|
||||
'--allow-empty',
|
||||
action='store_true',
|
||||
help='Do not report an error code if the build does not match any wheels.',
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
@@ -116,12 +141,17 @@ def main() -> None:
|
||||
else:
|
||||
ci_provider = detect_ci_provider()
|
||||
if ci_provider is None:
|
||||
print(textwrap.dedent('''
|
||||
print(
|
||||
textwrap.dedent(
|
||||
'''
|
||||
cibuildwheel: Unable to detect platform. cibuildwheel should run on your CI server;
|
||||
Travis CI, AppVeyor, Azure Pipelines, GitHub Actions, CircleCI, and Gitlab are
|
||||
supported. You can run on your development machine or other CI providers using the
|
||||
--platform argument. Check --help output for more information.
|
||||
'''), file=sys.stderr)
|
||||
'''
|
||||
),
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(2)
|
||||
if sys.platform.startswith('linux'):
|
||||
platform = 'linux'
|
||||
@@ -130,9 +160,11 @@ def main() -> None:
|
||||
elif sys.platform == 'win32':
|
||||
platform = 'windows'
|
||||
else:
|
||||
print('cibuildwheel: Unable to detect platform from "sys.platform" in a CI environment. You can run '
|
||||
'cibuildwheel using the --platform argument. Check --help output for more information.',
|
||||
file=sys.stderr)
|
||||
print(
|
||||
'cibuildwheel: Unable to detect platform from "sys.platform" in a CI environment. You can run '
|
||||
'cibuildwheel using the --platform argument. Check --help output for more information.',
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(2)
|
||||
|
||||
if platform not in PLATFORMS:
|
||||
@@ -154,29 +186,45 @@ def main() -> None:
|
||||
build_config = os.environ.get('CIBW_BUILD') or '*'
|
||||
skip_config = os.environ.get('CIBW_SKIP', '')
|
||||
test_skip = os.environ.get('CIBW_TEST_SKIP', '')
|
||||
environment_config = get_option_from_environment('CIBW_ENVIRONMENT', platform=platform, default='')
|
||||
environment_config = get_option_from_environment(
|
||||
'CIBW_ENVIRONMENT', platform=platform, default=''
|
||||
)
|
||||
before_all = get_option_from_environment('CIBW_BEFORE_ALL', platform=platform, default='')
|
||||
before_build = get_option_from_environment('CIBW_BEFORE_BUILD', platform=platform)
|
||||
repair_command = get_option_from_environment('CIBW_REPAIR_WHEEL_COMMAND', platform=platform, default=repair_command_default)
|
||||
dependency_versions = get_option_from_environment('CIBW_DEPENDENCY_VERSIONS', platform=platform, default='pinned')
|
||||
repair_command = get_option_from_environment(
|
||||
'CIBW_REPAIR_WHEEL_COMMAND', platform=platform, default=repair_command_default
|
||||
)
|
||||
dependency_versions = get_option_from_environment(
|
||||
'CIBW_DEPENDENCY_VERSIONS', platform=platform, default='pinned'
|
||||
)
|
||||
test_command = get_option_from_environment('CIBW_TEST_COMMAND', platform=platform)
|
||||
before_test = get_option_from_environment('CIBW_BEFORE_TEST', platform=platform)
|
||||
test_requires = get_option_from_environment('CIBW_TEST_REQUIRES', platform=platform, default='').split()
|
||||
test_requires = get_option_from_environment(
|
||||
'CIBW_TEST_REQUIRES', platform=platform, default=''
|
||||
).split()
|
||||
test_extras = get_option_from_environment('CIBW_TEST_EXTRAS', platform=platform, default='')
|
||||
build_verbosity_str = get_option_from_environment('CIBW_BUILD_VERBOSITY', platform=platform, default='')
|
||||
build_verbosity_str = get_option_from_environment(
|
||||
'CIBW_BUILD_VERBOSITY', platform=platform, default=''
|
||||
)
|
||||
|
||||
package_files = {'setup.py', 'setup.cfg', 'pyproject.toml'}
|
||||
|
||||
if not any(package_dir.joinpath(name).exists() for name in package_files):
|
||||
names = ', '.join(sorted(package_files, reverse=True))
|
||||
print(f'cibuildwheel: Could not find any of {{{names}}} at root of package', file=sys.stderr)
|
||||
print(
|
||||
f'cibuildwheel: Could not find any of {{{names}}} at root of package', file=sys.stderr
|
||||
)
|
||||
sys.exit(2)
|
||||
|
||||
# Passing this in as an environment variable will override pyproject.toml, setup.cfg, or setup.py
|
||||
requires_python_str: Optional[str] = os.environ.get('CIBW_PROJECT_REQUIRES_PYTHON') or get_requires_python_str(package_dir)
|
||||
requires_python_str: Optional[str] = os.environ.get(
|
||||
'CIBW_PROJECT_REQUIRES_PYTHON'
|
||||
) or get_requires_python_str(package_dir)
|
||||
requires_python = None if requires_python_str is None else SpecifierSet(requires_python_str)
|
||||
|
||||
build_selector = BuildSelector(build_config=build_config, skip_config=skip_config, requires_python=requires_python)
|
||||
build_selector = BuildSelector(
|
||||
build_config=build_config, skip_config=skip_config, requires_python=requires_python
|
||||
)
|
||||
test_selector = TestSelector(skip_config=test_skip)
|
||||
|
||||
try:
|
||||
@@ -187,7 +235,9 @@ def main() -> None:
|
||||
sys.exit(2)
|
||||
|
||||
if dependency_versions == 'pinned':
|
||||
dependency_constraints: Optional[DependencyConstraints] = DependencyConstraints.with_defaults()
|
||||
dependency_constraints: Optional[
|
||||
DependencyConstraints
|
||||
] = DependencyConstraints.with_defaults()
|
||||
elif dependency_versions == 'latest':
|
||||
dependency_constraints = None
|
||||
else:
|
||||
@@ -209,7 +259,9 @@ def main() -> None:
|
||||
if args.archs is not None:
|
||||
archs_config_str = args.archs
|
||||
else:
|
||||
archs_config_str = get_option_from_environment('CIBW_ARCHS', platform=platform, default='auto')
|
||||
archs_config_str = get_option_from_environment(
|
||||
'CIBW_ARCHS', platform=platform, default='auto'
|
||||
)
|
||||
|
||||
archs = Architecture.parse_config(archs_config_str, platform=platform)
|
||||
|
||||
@@ -287,7 +339,9 @@ def main() -> None:
|
||||
if not output_dir.exists():
|
||||
output_dir.mkdir(parents=True)
|
||||
|
||||
with cibuildwheel.util.print_new_wheels("\n{n} wheels produced in {m:.0f} minutes:", output_dir):
|
||||
with cibuildwheel.util.print_new_wheels(
|
||||
"\n{n} wheels produced in {m:.0f} minutes:", output_dir
|
||||
):
|
||||
if platform == 'linux':
|
||||
cibuildwheel.linux.build(build_options)
|
||||
elif platform == 'windows':
|
||||
@@ -300,10 +354,14 @@ def main() -> None:
|
||||
|
||||
def detect_obsolete_options() -> None:
|
||||
# Check the old 'MANYLINUX1_*_IMAGE' options
|
||||
for (deprecated, alternative) in [('CIBW_MANYLINUX1_X86_64_IMAGE', 'CIBW_MANYLINUX_X86_64_IMAGE'),
|
||||
('CIBW_MANYLINUX1_I686_IMAGE', 'CIBW_MANYLINUX_I686_IMAGE')]:
|
||||
for (deprecated, alternative) in [
|
||||
('CIBW_MANYLINUX1_X86_64_IMAGE', 'CIBW_MANYLINUX_X86_64_IMAGE'),
|
||||
('CIBW_MANYLINUX1_I686_IMAGE', 'CIBW_MANYLINUX_I686_IMAGE'),
|
||||
]:
|
||||
if deprecated in os.environ:
|
||||
print(f"'{deprecated}' has been deprecated, and will be removed in a future release. Use the option '{alternative}' instead.")
|
||||
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:
|
||||
print(f"Using value of option '{deprecated}' as replacement for '{alternative}'")
|
||||
os.environ[alternative] = os.environ[deprecated]
|
||||
@@ -313,21 +371,29 @@ def detect_obsolete_options() -> None:
|
||||
|
||||
# Check for deprecated identifiers in 'CIBW_BUILD' and 'CIBW_SKIP' options
|
||||
for option in ['CIBW_BUILD', 'CIBW_SKIP']:
|
||||
for deprecated, alternative in [('manylinux1', 'manylinux'),
|
||||
('macosx_10_6_intel', 'macosx_x86_64'),
|
||||
('macosx_10_9_x86_64', 'macosx_x86_64')]:
|
||||
for deprecated, alternative in [
|
||||
('manylinux1', 'manylinux'),
|
||||
('macosx_10_6_intel', 'macosx_x86_64'),
|
||||
('macosx_10_9_x86_64', 'macosx_x86_64'),
|
||||
]:
|
||||
if option in os.environ and deprecated in os.environ[option]:
|
||||
print(f"Build identifiers with '{deprecated}' have been deprecated. Replacing all occurences of '{deprecated}' with '{alternative}' in the option '{option}'")
|
||||
print(
|
||||
f"Build identifiers with '{deprecated}' have been deprecated. Replacing all occurences of '{deprecated}' with '{alternative}' in the option '{option}'"
|
||||
)
|
||||
os.environ[option] = os.environ[option].replace(deprecated, alternative)
|
||||
|
||||
|
||||
def print_preamble(platform: str, build_options: BuildOptions) -> None:
|
||||
print(textwrap.dedent('''
|
||||
print(
|
||||
textwrap.dedent(
|
||||
'''
|
||||
_ _ _ _ _ _ _
|
||||
___|_| |_ _ _|_| |_| |_ _ _| |_ ___ ___| |
|
||||
| _| | . | | | | | . | | | | | -_| -_| |
|
||||
|___|_|___|___|_|_|___|_____|_|_|___|___|_|
|
||||
'''))
|
||||
'''
|
||||
)
|
||||
)
|
||||
|
||||
print(f'cibuildwheel version {cibuildwheel.__version__}\n')
|
||||
|
||||
@@ -348,16 +414,24 @@ def print_preamble(platform: str, build_options: BuildOptions) -> None:
|
||||
def get_build_identifiers(
|
||||
platform: PlatformName, build_selector: BuildSelector, architectures: Set[Architecture]
|
||||
) -> List[str]:
|
||||
python_configurations: Union[List[cibuildwheel.linux.PythonConfiguration],
|
||||
List[cibuildwheel.windows.PythonConfiguration],
|
||||
List[cibuildwheel.macos.PythonConfiguration]]
|
||||
python_configurations: Union[
|
||||
List[cibuildwheel.linux.PythonConfiguration],
|
||||
List[cibuildwheel.windows.PythonConfiguration],
|
||||
List[cibuildwheel.macos.PythonConfiguration],
|
||||
]
|
||||
|
||||
if platform == 'linux':
|
||||
python_configurations = cibuildwheel.linux.get_python_configurations(build_selector, architectures)
|
||||
python_configurations = cibuildwheel.linux.get_python_configurations(
|
||||
build_selector, architectures
|
||||
)
|
||||
elif platform == 'windows':
|
||||
python_configurations = cibuildwheel.windows.get_python_configurations(build_selector, architectures)
|
||||
python_configurations = cibuildwheel.windows.get_python_configurations(
|
||||
build_selector, architectures
|
||||
)
|
||||
elif platform == 'macos':
|
||||
python_configurations = cibuildwheel.macos.get_python_configurations(build_selector, architectures)
|
||||
python_configurations = cibuildwheel.macos.get_python_configurations(
|
||||
build_selector, architectures
|
||||
)
|
||||
else:
|
||||
assert_never(platform)
|
||||
|
||||
|
||||
@@ -73,7 +73,13 @@ class Architecture(Enum):
|
||||
@staticmethod
|
||||
def all_archs(platform: PlatformName) -> 'Set[Architecture]':
|
||||
if platform == 'linux':
|
||||
return {Architecture.x86_64, Architecture.i686, Architecture.aarch64, Architecture.ppc64le, Architecture.s390x}
|
||||
return {
|
||||
Architecture.x86_64,
|
||||
Architecture.i686,
|
||||
Architecture.aarch64,
|
||||
Architecture.ppc64le,
|
||||
Architecture.s390x,
|
||||
}
|
||||
elif platform == 'macos':
|
||||
return {Architecture.x86_64, Architecture.arm64, Architecture.universal2}
|
||||
elif platform == 'windows':
|
||||
|
||||
@@ -8,7 +8,9 @@ EnvironmentExecutor = Callable[[List[str], Dict[str, str]], str]
|
||||
|
||||
|
||||
def local_environment_executor(command: List[str], env: Dict[str, str]) -> str:
|
||||
return subprocess.run(command, env=env, universal_newlines=True, stdout=subprocess.PIPE, check=True).stdout
|
||||
return subprocess.run(
|
||||
command, env=env, universal_newlines=True, stdout=subprocess.PIPE, check=True
|
||||
).stdout
|
||||
|
||||
|
||||
class NodeExecutionContext(NamedTuple):
|
||||
@@ -17,7 +19,9 @@ class NodeExecutionContext(NamedTuple):
|
||||
executor: EnvironmentExecutor
|
||||
|
||||
|
||||
def evaluate(value: str, environment: Dict[str, str], executor: Optional[EnvironmentExecutor] = None) -> str:
|
||||
def evaluate(
|
||||
value: str, environment: Dict[str, str], executor: Optional[EnvironmentExecutor] = None
|
||||
) -> str:
|
||||
if not value:
|
||||
# empty string evaluates to empty string
|
||||
# (but trips up bashlex)
|
||||
@@ -32,7 +36,9 @@ def evaluate(value: str, environment: Dict[str, str], executor: Optional[Environ
|
||||
|
||||
return evaluate_node(
|
||||
value_word_node,
|
||||
context=NodeExecutionContext(environment=environment, input=value, executor=executor or local_environment_executor)
|
||||
context=NodeExecutionContext(
|
||||
environment=environment, input=value, executor=executor or local_environment_executor
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -53,7 +59,7 @@ def evaluate_word_node(node: bashlex.ast.node, context: NodeExecutionContext) ->
|
||||
value: str = node.word
|
||||
|
||||
for part in node.parts:
|
||||
part_string = context.input[part.pos[0]:part.pos[1]]
|
||||
part_string = context.input[part.pos[0] : part.pos[1]]
|
||||
part_value = evaluate_node(part, context=context)
|
||||
|
||||
if part_string not in value:
|
||||
@@ -74,7 +80,9 @@ def evaluate_command_node(node: bashlex.ast.node, context: NodeExecutionContext)
|
||||
return evaluate_nodes_as_simple_command(node.parts, context=context)
|
||||
|
||||
|
||||
def evaluate_nodes_as_compound_command(nodes: Sequence[bashlex.ast.node], context: NodeExecutionContext) -> str:
|
||||
def evaluate_nodes_as_compound_command(
|
||||
nodes: Sequence[bashlex.ast.node], context: NodeExecutionContext
|
||||
) -> str:
|
||||
# bashlex doesn't support any operators besides ';' inside command
|
||||
# substitutions, so we only need to handle that case. We do so assuming
|
||||
# that `set -o errexit` is on, because it's easier to code!
|
||||
@@ -92,7 +100,9 @@ def evaluate_nodes_as_compound_command(nodes: Sequence[bashlex.ast.node], contex
|
||||
return result
|
||||
|
||||
|
||||
def evaluate_nodes_as_simple_command(nodes: List[bashlex.ast.node], context: NodeExecutionContext) -> str:
|
||||
def evaluate_nodes_as_simple_command(
|
||||
nodes: List[bashlex.ast.node], context: NodeExecutionContext
|
||||
) -> str:
|
||||
command = [evaluate_node(part, context=context) for part in nodes]
|
||||
return context.executor(command, context.environment)
|
||||
|
||||
|
||||
@@ -23,13 +23,16 @@ class DockerContainer:
|
||||
the command is relayed to the remote shell, and the results are streamed
|
||||
back to cibuildwheel.
|
||||
'''
|
||||
|
||||
UTILITY_PYTHON = '/opt/python/cp38-cp38/bin/python'
|
||||
|
||||
process: PopenBytes
|
||||
bash_stdin: IO[bytes]
|
||||
bash_stdout: IO[bytes]
|
||||
|
||||
def __init__(self, docker_image: str, simulate_32_bit: bool = False, cwd: Optional[PathOrStr] = None):
|
||||
def __init__(
|
||||
self, docker_image: str, simulate_32_bit: bool = False, cwd: Optional[PathOrStr] = None
|
||||
):
|
||||
if not docker_image:
|
||||
raise ValueError("Must have a non-empty docker image to run.")
|
||||
|
||||
@@ -44,21 +47,24 @@ class DockerContainer:
|
||||
shell_args = ['linux32', '/bin/bash'] if self.simulate_32_bit else ['/bin/bash']
|
||||
subprocess.run(
|
||||
[
|
||||
'docker', 'create',
|
||||
'docker',
|
||||
'create',
|
||||
'--env=CIBUILDWHEEL',
|
||||
f'--name={self.name}',
|
||||
'--interactive',
|
||||
'--volume=/:/host', # ignored on CircleCI
|
||||
*cwd_args,
|
||||
self.docker_image,
|
||||
*shell_args
|
||||
*shell_args,
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
self.process = subprocess.Popen(
|
||||
[
|
||||
'docker', 'start',
|
||||
'--attach', '--interactive',
|
||||
'docker',
|
||||
'start',
|
||||
'--attach',
|
||||
'--interactive',
|
||||
self.name,
|
||||
],
|
||||
stdin=subprocess.PIPE,
|
||||
@@ -75,10 +81,11 @@ class DockerContainer:
|
||||
return self
|
||||
|
||||
def __exit__(
|
||||
self,
|
||||
exc_type: Optional[Type[BaseException]],
|
||||
exc_val: Optional[BaseException],
|
||||
exc_tb: Optional[TracebackType]) -> None:
|
||||
self,
|
||||
exc_type: Optional[Type[BaseException]],
|
||||
exc_val: Optional[BaseException],
|
||||
exc_tb: Optional[TracebackType],
|
||||
) -> None:
|
||||
|
||||
self.bash_stdin.close()
|
||||
self.process.terminate()
|
||||
@@ -101,12 +108,14 @@ class DockerContainer:
|
||||
f'tar cf - . | docker exec -i {self.name} tar -xC {shell_quote(to_path)} -f -',
|
||||
shell=True,
|
||||
check=True,
|
||||
cwd=from_path)
|
||||
cwd=from_path,
|
||||
)
|
||||
else:
|
||||
subprocess.run(
|
||||
f'cat {shell_quote(from_path)} | docker exec -i {self.name} sh -c "cat > {shell_quote(to_path)}"',
|
||||
shell=True,
|
||||
check=True)
|
||||
check=True,
|
||||
)
|
||||
|
||||
def copy_out(self, from_path: PurePath, to_path: Path) -> None:
|
||||
# note: we assume from_path is a dir
|
||||
@@ -116,30 +125,39 @@ class DockerContainer:
|
||||
f'docker exec -i {self.name} tar -cC {shell_quote(from_path)} -f - . | tar -xf -',
|
||||
shell=True,
|
||||
check=True,
|
||||
cwd=to_path
|
||||
cwd=to_path,
|
||||
)
|
||||
|
||||
def glob(self, path: PurePath, pattern: str) -> List[PurePath]:
|
||||
glob_pattern = os.path.join(str(path), pattern)
|
||||
|
||||
path_strs = json.loads(self.call([
|
||||
self.UTILITY_PYTHON,
|
||||
'-c',
|
||||
f'import sys, json, glob; json.dump(glob.glob({glob_pattern!r}), sys.stdout)'
|
||||
], capture_output=True))
|
||||
path_strs = json.loads(
|
||||
self.call(
|
||||
[
|
||||
self.UTILITY_PYTHON,
|
||||
'-c',
|
||||
f'import sys, json, glob; json.dump(glob.glob({glob_pattern!r}), sys.stdout)',
|
||||
],
|
||||
capture_output=True,
|
||||
)
|
||||
)
|
||||
|
||||
return [PurePath(p) for p in path_strs]
|
||||
|
||||
def call(
|
||||
self,
|
||||
args: Sequence[PathOrStr],
|
||||
env: Optional[Dict[str, str]] = None,
|
||||
capture_output: bool = False,
|
||||
cwd: Optional[PathOrStr] = None) -> str:
|
||||
self,
|
||||
args: Sequence[PathOrStr],
|
||||
env: Optional[Dict[str, str]] = None,
|
||||
capture_output: bool = False,
|
||||
cwd: Optional[PathOrStr] = None,
|
||||
) -> str:
|
||||
|
||||
chdir = f'cd {cwd}' if cwd else ''
|
||||
env_assignments = ' '.join(f'{shlex.quote(k)}={shlex.quote(v)}'
|
||||
for k, v in env.items()) if env is not None else ''
|
||||
env_assignments = (
|
||||
' '.join(f'{shlex.quote(k)}={shlex.quote(v)}' for k, v in env.items())
|
||||
if env is not None
|
||||
else ''
|
||||
)
|
||||
command = ' '.join(shlex.quote(str(a)) for a in args)
|
||||
end_of_message = str(uuid.uuid4())
|
||||
|
||||
@@ -153,12 +171,18 @@ class DockerContainer:
|
||||
# Finally, the remote shell is told to write a footer - this will show
|
||||
# up in the output so we know when to stop reading, and will include
|
||||
# the returncode of `command`.
|
||||
self.bash_stdin.write(bytes(f'''(
|
||||
self.bash_stdin.write(
|
||||
bytes(
|
||||
f'''(
|
||||
{chdir}
|
||||
env {env_assignments} {command}
|
||||
printf "%04d%s\n" $? {end_of_message}
|
||||
)
|
||||
''', encoding='utf8', errors='surrogateescape'))
|
||||
''',
|
||||
encoding='utf8',
|
||||
errors='surrogateescape',
|
||||
)
|
||||
)
|
||||
self.bash_stdin.flush()
|
||||
|
||||
if capture_output:
|
||||
@@ -178,7 +202,7 @@ class DockerContainer:
|
||||
- 4 # 4 returncode decimals
|
||||
)
|
||||
# fmt: on
|
||||
returncode_str = line[footer_offset:footer_offset+4]
|
||||
returncode_str = line[footer_offset : footer_offset + 4]
|
||||
returncode = int(returncode_str)
|
||||
# add the last line to output, without the footer
|
||||
output_io.write(line[0:footer_offset])
|
||||
@@ -197,11 +221,16 @@ class DockerContainer:
|
||||
return output
|
||||
|
||||
def get_environment(self) -> Dict[str, str]:
|
||||
env = json.loads(self.call([
|
||||
self.UTILITY_PYTHON,
|
||||
'-c',
|
||||
'import sys, json, os; json.dump(os.environ.copy(), sys.stdout)'
|
||||
], capture_output=True))
|
||||
env = json.loads(
|
||||
self.call(
|
||||
[
|
||||
self.UTILITY_PYTHON,
|
||||
'-c',
|
||||
'import sys, json, os; json.dump(os.environ.copy(), sys.stdout)',
|
||||
],
|
||||
capture_output=True,
|
||||
)
|
||||
)
|
||||
return cast(Dict[str, str], env)
|
||||
|
||||
def environment_executor(self, command: List[str], environment: Dict[str, str]) -> str:
|
||||
|
||||
@@ -32,7 +32,7 @@ def split_env_items(env_string: str) -> List[str]:
|
||||
result = []
|
||||
|
||||
for word_node in command_node.parts:
|
||||
part_string = env_string[word_node.pos[0]:word_node.pos[1]]
|
||||
part_string = env_string[word_node.pos[0] : word_node.pos[1]]
|
||||
result.append(part_string)
|
||||
|
||||
return result
|
||||
@@ -46,7 +46,11 @@ class EnvironmentAssignment:
|
||||
self.name = name
|
||||
self.value = value
|
||||
|
||||
def evaluated_value(self, environment: Dict[str, str], executor: Optional[bashlex_eval.EnvironmentExecutor] = None) -> str:
|
||||
def evaluated_value(
|
||||
self,
|
||||
environment: Dict[str, str],
|
||||
executor: Optional[bashlex_eval.EnvironmentExecutor] = None,
|
||||
) -> str:
|
||||
'''Returns the value of this assignment, as evaluated in the environment'''
|
||||
return bashlex_eval.evaluate(self.value, environment=environment, executor=executor)
|
||||
|
||||
@@ -61,9 +65,11 @@ class ParsedEnvironment:
|
||||
def __init__(self, assignments: List[EnvironmentAssignment]):
|
||||
self.assignments = assignments
|
||||
|
||||
def as_dictionary(self,
|
||||
prev_environment: Mapping[str, str],
|
||||
executor: Optional[bashlex_eval.EnvironmentExecutor] = None) -> Dict[str, str]:
|
||||
def as_dictionary(
|
||||
self,
|
||||
prev_environment: Mapping[str, str],
|
||||
executor: Optional[bashlex_eval.EnvironmentExecutor] = None,
|
||||
) -> Dict[str, str]:
|
||||
environment = dict(**prev_environment)
|
||||
|
||||
for assignment in self.assignments:
|
||||
|
||||
+115
-36
@@ -41,7 +41,8 @@ def get_python_configurations(
|
||||
# return all configurations whose arch is in our `architectures` set,
|
||||
# and match the build/skip rules
|
||||
return [
|
||||
c for c in python_configurations
|
||||
c
|
||||
for c in python_configurations
|
||||
if any(c.identifier.endswith(arch.value) for arch in architectures)
|
||||
and build_selector(c.identifier)
|
||||
]
|
||||
@@ -52,10 +53,12 @@ def build(options: BuildOptions) -> None:
|
||||
# check docker is installed
|
||||
subprocess.run(['docker', '--version'], check=True, stdout=subprocess.DEVNULL)
|
||||
except Exception:
|
||||
print('cibuildwheel: Docker not found. Docker is required to run Linux builds. '
|
||||
'If you\'re building on Travis CI, add `services: [docker]` to your .travis.yml.'
|
||||
'If you\'re building on Circle CI in Linux, add a `setup_remote_docker` step to your .circleci/config.yml',
|
||||
file=sys.stderr)
|
||||
print(
|
||||
'cibuildwheel: Docker not found. Docker is required to run Linux builds. '
|
||||
'If you\'re building on Travis CI, add `services: [docker]` to your .travis.yml.'
|
||||
'If you\'re building on Circle CI in Linux, add a `setup_remote_docker` step to your .circleci/config.yml',
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(2)
|
||||
|
||||
assert options.manylinux_images is not None
|
||||
@@ -79,13 +82,21 @@ def build(options: BuildOptions) -> None:
|
||||
container_output_dir = PurePath('/output')
|
||||
|
||||
for implementation, platform_tag, docker_image in platforms:
|
||||
platform_configs = [c for c in python_configurations if c.identifier.startswith(implementation) and c.identifier.endswith(platform_tag)]
|
||||
platform_configs = [
|
||||
c
|
||||
for c in python_configurations
|
||||
if c.identifier.startswith(implementation) and c.identifier.endswith(platform_tag)
|
||||
]
|
||||
if not platform_configs:
|
||||
continue
|
||||
|
||||
try:
|
||||
log.step(f'Starting Docker image {docker_image}...')
|
||||
with DockerContainer(docker_image, simulate_32_bit=platform_tag.endswith('i686'), cwd=container_project_path) as docker:
|
||||
with DockerContainer(
|
||||
docker_image,
|
||||
simulate_32_bit=platform_tag.endswith('i686'),
|
||||
cwd=container_project_path,
|
||||
) as docker:
|
||||
|
||||
log.step('Copying project into Docker...')
|
||||
docker.copy_into(Path.cwd(), container_project_path)
|
||||
@@ -96,9 +107,15 @@ def build(options: BuildOptions) -> None:
|
||||
env = docker.get_environment()
|
||||
env['PATH'] = f'/opt/python/cp38-cp38/bin:{env["PATH"]}'
|
||||
env['PIP_DISABLE_PIP_VERSION_CHECK'] = '1'
|
||||
env = options.environment.as_dictionary(env, executor=docker.environment_executor)
|
||||
env = options.environment.as_dictionary(
|
||||
env, executor=docker.environment_executor
|
||||
)
|
||||
|
||||
before_all_prepared = prepare_command(options.before_all, project=container_project_path, package=container_package_dir)
|
||||
before_all_prepared = prepare_command(
|
||||
options.before_all,
|
||||
project=container_project_path,
|
||||
package=container_package_dir,
|
||||
)
|
||||
docker.call(['sh', '-c', before_all_prepared], env=env)
|
||||
|
||||
for config in platform_configs:
|
||||
@@ -112,12 +129,24 @@ def build(options: BuildOptions) -> None:
|
||||
patch_docker_path = PurePath('/pypy_venv.patch')
|
||||
docker.copy_into(patch_path, patch_docker_path)
|
||||
try:
|
||||
docker.call(['patch', '--force', '-p1', '-d', config.path, '-i', patch_docker_path])
|
||||
docker.call(
|
||||
[
|
||||
'patch',
|
||||
'--force',
|
||||
'-p1',
|
||||
'-d',
|
||||
config.path,
|
||||
'-i',
|
||||
patch_docker_path,
|
||||
]
|
||||
)
|
||||
except subprocess.CalledProcessError:
|
||||
print("PyPy patch not applied", file=sys.stderr)
|
||||
|
||||
if options.dependency_constraints:
|
||||
constraints_file = options.dependency_constraints.get_for_python_version(config.version)
|
||||
constraints_file = options.dependency_constraints.get_for_python_version(
|
||||
config.version
|
||||
)
|
||||
container_constraints_file = PurePath('/constraints.txt')
|
||||
|
||||
docker.copy_into(constraints_file, container_constraints_file)
|
||||
@@ -131,22 +160,36 @@ def build(options: BuildOptions) -> None:
|
||||
python_bin = config.path / 'bin'
|
||||
env['PATH'] = f'{python_bin}:{env["PATH"]}'
|
||||
|
||||
env = options.environment.as_dictionary(env, executor=docker.environment_executor)
|
||||
env = options.environment.as_dictionary(
|
||||
env, executor=docker.environment_executor
|
||||
)
|
||||
|
||||
# check config python and pip are still on PATH
|
||||
which_python = docker.call(['which', 'python'], env=env, capture_output=True).strip()
|
||||
which_python = docker.call(
|
||||
['which', 'python'], env=env, capture_output=True
|
||||
).strip()
|
||||
if PurePath(which_python) != python_bin / 'python':
|
||||
print("cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it.", file=sys.stderr)
|
||||
print(
|
||||
"cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
which_pip = docker.call(['which', 'pip'], env=env, capture_output=True).strip()
|
||||
if PurePath(which_pip) != python_bin / 'pip':
|
||||
print("cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it.", file=sys.stderr)
|
||||
print(
|
||||
"cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
if options.before_build:
|
||||
log.step('Running before_build...')
|
||||
before_build_prepared = prepare_command(options.before_build, project=container_project_path, package=container_package_dir)
|
||||
before_build_prepared = prepare_command(
|
||||
options.before_build,
|
||||
project=container_project_path,
|
||||
package=container_package_dir,
|
||||
)
|
||||
docker.call(['sh', '-c', before_build_prepared], env=env)
|
||||
|
||||
log.step('Building wheel...')
|
||||
@@ -156,13 +199,18 @@ def build(options: BuildOptions) -> None:
|
||||
docker.call(['rm', '-rf', built_wheel_dir])
|
||||
docker.call(['mkdir', '-p', built_wheel_dir])
|
||||
|
||||
docker.call([
|
||||
'pip', 'wheel',
|
||||
container_package_dir,
|
||||
'--wheel-dir', built_wheel_dir,
|
||||
'--no-deps',
|
||||
*get_build_verbosity_extra_flags(options.build_verbosity)
|
||||
], env=env)
|
||||
docker.call(
|
||||
[
|
||||
'pip',
|
||||
'wheel',
|
||||
container_package_dir,
|
||||
'--wheel-dir',
|
||||
built_wheel_dir,
|
||||
'--no-deps',
|
||||
*get_build_verbosity_extra_flags(options.build_verbosity),
|
||||
],
|
||||
env=env,
|
||||
)
|
||||
|
||||
built_wheel = docker.glob(built_wheel_dir, '*.whl')[0]
|
||||
|
||||
@@ -175,7 +223,9 @@ def build(options: BuildOptions) -> None:
|
||||
|
||||
if options.repair_command:
|
||||
log.step('Repairing wheel...')
|
||||
repair_command_prepared = prepare_command(options.repair_command, wheel=built_wheel, dest_dir=repaired_wheel_dir)
|
||||
repair_command_prepared = prepare_command(
|
||||
options.repair_command, wheel=built_wheel, dest_dir=repaired_wheel_dir
|
||||
)
|
||||
docker.call(['sh', '-c', repair_command_prepared], env=env)
|
||||
else:
|
||||
docker.call(['mv', built_wheel, repaired_wheel_dir])
|
||||
@@ -187,16 +237,27 @@ def build(options: BuildOptions) -> None:
|
||||
|
||||
# set up a virtual environment to install and test from, to make sure
|
||||
# there are no dependencies that were pulled in at build time.
|
||||
docker.call(['pip', 'install', 'virtualenv', *dependency_constraint_flags], env=env)
|
||||
venv_dir = PurePath(docker.call(['mktemp', '-d'], capture_output=True).strip()) / 'venv'
|
||||
docker.call(
|
||||
['pip', 'install', 'virtualenv', *dependency_constraint_flags], env=env
|
||||
)
|
||||
venv_dir = (
|
||||
PurePath(docker.call(['mktemp', '-d'], capture_output=True).strip())
|
||||
/ 'venv'
|
||||
)
|
||||
|
||||
docker.call(['python', '-m', 'virtualenv', '--no-download', venv_dir], env=env)
|
||||
docker.call(
|
||||
['python', '-m', 'virtualenv', '--no-download', venv_dir], env=env
|
||||
)
|
||||
|
||||
virtualenv_env = env.copy()
|
||||
virtualenv_env['PATH'] = f"{venv_dir / 'bin'}:{virtualenv_env['PATH']}"
|
||||
|
||||
if options.before_test:
|
||||
before_test_prepared = prepare_command(options.before_test, project=container_project_path, package=container_package_dir)
|
||||
before_test_prepared = prepare_command(
|
||||
options.before_test,
|
||||
project=container_project_path,
|
||||
package=container_package_dir,
|
||||
)
|
||||
docker.call(['sh', '-c', before_test_prepared], env=virtualenv_env)
|
||||
|
||||
# Install the wheel we just built
|
||||
@@ -206,15 +267,26 @@ def build(options: BuildOptions) -> None:
|
||||
# different external shared libraries. so it doesn't matter which one we run the tests on.
|
||||
# Let's just pick the first one.
|
||||
wheel_to_test = repaired_wheels[0]
|
||||
docker.call(['pip', 'install', str(wheel_to_test) + options.test_extras], env=virtualenv_env)
|
||||
docker.call(
|
||||
['pip', 'install', str(wheel_to_test) + options.test_extras],
|
||||
env=virtualenv_env,
|
||||
)
|
||||
|
||||
# Install any requirements to run the tests
|
||||
if options.test_requires:
|
||||
docker.call(['pip', 'install', *options.test_requires], env=virtualenv_env)
|
||||
docker.call(
|
||||
['pip', 'install', *options.test_requires], env=virtualenv_env
|
||||
)
|
||||
|
||||
# Run the tests from a different directory
|
||||
test_command_prepared = prepare_command(options.test_command, project=container_project_path, package=container_package_dir)
|
||||
docker.call(['sh', '-c', test_command_prepared], cwd='/root', env=virtualenv_env)
|
||||
test_command_prepared = prepare_command(
|
||||
options.test_command,
|
||||
project=container_project_path,
|
||||
package=container_package_dir,
|
||||
)
|
||||
docker.call(
|
||||
['sh', '-c', test_command_prepared], cwd='/root', env=virtualenv_env
|
||||
)
|
||||
|
||||
# clean up test environment
|
||||
docker.call(['rm', '-rf', venv_dir])
|
||||
@@ -230,19 +302,23 @@ def build(options: BuildOptions) -> None:
|
||||
docker.copy_out(container_output_dir, options.output_dir)
|
||||
log.step_end()
|
||||
except subprocess.CalledProcessError as error:
|
||||
log.step_end_with_error(f'Command {error.cmd} failed with code {error.returncode}. {error.stdout}')
|
||||
log.step_end_with_error(
|
||||
f'Command {error.cmd} failed with code {error.returncode}. {error.stdout}'
|
||||
)
|
||||
troubleshoot(options.package_dir, error)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def troubleshoot(package_dir: Path, error: Exception) -> None:
|
||||
if (isinstance(error, subprocess.CalledProcessError) and error.cmd[0:2] == ['pip', 'wheel']):
|
||||
if isinstance(error, subprocess.CalledProcessError) and error.cmd[0:2] == ['pip', 'wheel']:
|
||||
# the 'pip wheel' step failed.
|
||||
print('Checking for common errors...')
|
||||
so_files = list(package_dir.glob('**/*.so'))
|
||||
|
||||
if so_files:
|
||||
print(textwrap.dedent('''
|
||||
print(
|
||||
textwrap.dedent(
|
||||
'''
|
||||
NOTE: Shared object (.so) files found in this project.
|
||||
|
||||
These files might be built against the wrong OS, causing problems with
|
||||
@@ -250,7 +326,10 @@ def troubleshoot(package_dir: Path, error: Exception) -> None:
|
||||
|
||||
If you're using Cython and have previously done an in-place build,
|
||||
remove those build files (*.so and *.c) before starting cibuildwheel.
|
||||
'''), file=sys.stderr)
|
||||
'''
|
||||
),
|
||||
file=sys.stderr,
|
||||
)
|
||||
|
||||
print(' Files detected:')
|
||||
print('\n'.join(f' {f}' for f in so_files))
|
||||
|
||||
@@ -89,7 +89,9 @@ class Logger:
|
||||
duration = time.time() - self.build_start_time
|
||||
|
||||
print()
|
||||
print(f'{c.green}{s.done} {c.end}{self.active_build_identifier} finished in {duration:.2f}s')
|
||||
print(
|
||||
f'{c.green}{s.done} {c.end}{self.active_build_identifier} finished in {duration:.2f}s'
|
||||
)
|
||||
self.build_start_time = None
|
||||
self.active_build_identifier = None
|
||||
|
||||
@@ -143,7 +145,9 @@ class Logger:
|
||||
if self.active_fold_group_name:
|
||||
fold_start_pattern = FOLD_PATTERNS.get(self.fold_mode, DEFAULT_FOLD_PATTERN)[1]
|
||||
identifier = self._fold_group_identifier(self.active_fold_group_name)
|
||||
print(fold_start_pattern.format(name=self.active_fold_group_name, identifier=identifier))
|
||||
print(
|
||||
fold_start_pattern.format(name=self.active_fold_group_name, identifier=identifier)
|
||||
)
|
||||
sys.stdout.flush()
|
||||
self.active_fold_group_name = None
|
||||
|
||||
@@ -231,11 +235,11 @@ def file_supports_color(file_obj: IO[AnyStr]) -> bool:
|
||||
Returns True if the running system's terminal supports color.
|
||||
"""
|
||||
plat = sys.platform
|
||||
supported_platform = (plat != 'win32' or 'ANSICON' in os.environ)
|
||||
supported_platform = plat != 'win32' or 'ANSICON' in os.environ
|
||||
|
||||
is_a_tty = file_is_a_tty(file_obj)
|
||||
|
||||
return (supported_platform and is_a_tty)
|
||||
return supported_platform and is_a_tty
|
||||
|
||||
|
||||
def file_is_a_tty(file_obj: IO[AnyStr]) -> bool:
|
||||
@@ -249,7 +253,7 @@ def file_supports_unicode(file_obj: IO[AnyStr]) -> bool:
|
||||
|
||||
codec_info = codecs.lookup(encoding)
|
||||
|
||||
return ('utf' in codec_info.name)
|
||||
return 'utf' in codec_info.name
|
||||
|
||||
|
||||
'''
|
||||
|
||||
+169
-60
@@ -28,7 +28,12 @@ from .util import (
|
||||
)
|
||||
|
||||
|
||||
def call(args: Sequence[PathOrStr], env: Optional[Dict[str, str]] = None, cwd: Optional[str] = None, shell: bool = False) -> None:
|
||||
def call(
|
||||
args: Sequence[PathOrStr],
|
||||
env: Optional[Dict[str, str]] = None,
|
||||
cwd: Optional[str] = None,
|
||||
shell: bool = False,
|
||||
) -> None:
|
||||
# print the command executing for the logs
|
||||
if shell:
|
||||
print(f'+ {args}')
|
||||
@@ -69,16 +74,20 @@ class PythonConfiguration(NamedTuple):
|
||||
url: str
|
||||
|
||||
|
||||
def get_python_configurations(build_selector: BuildSelector,
|
||||
architectures: Set[Architecture]) -> List[PythonConfiguration]:
|
||||
def get_python_configurations(
|
||||
build_selector: BuildSelector, architectures: Set[Architecture]
|
||||
) -> List[PythonConfiguration]:
|
||||
|
||||
full_python_configs = read_python_configs('macos')
|
||||
|
||||
python_configurations = [PythonConfiguration(**item) for item in full_python_configs]
|
||||
|
||||
# filter out configs that don't match any of the selected architectures
|
||||
python_configurations = [c for c in python_configurations
|
||||
if any(c.identifier.endswith(a.value) for a in architectures)]
|
||||
python_configurations = [
|
||||
c
|
||||
for c in python_configurations
|
||||
if any(c.identifier.endswith(a.value) for a in architectures)
|
||||
]
|
||||
|
||||
# skip builds as required by BUILD/SKIP
|
||||
python_configurations = [c for c in python_configurations if build_selector(c.identifier)]
|
||||
@@ -89,21 +98,33 @@ def get_python_configurations(build_selector: BuildSelector,
|
||||
if any(c.identifier.startswith('pp') for c in python_configurations):
|
||||
# pypy doesn't work on macOS 11 yet
|
||||
# See https://foss.heptapod.net/pypy/pypy/-/issues/3314
|
||||
log.warning(unwrap('''
|
||||
log.warning(
|
||||
unwrap(
|
||||
'''
|
||||
PyPy is currently unsupported when building on macOS 11. To build macOS PyPy wheels,
|
||||
build on an older OS, such as macOS 10.15. To silence this warning, deselect PyPy by
|
||||
adding "pp*-macosx*" to your CIBW_SKIP option.
|
||||
'''))
|
||||
python_configurations = [c for c in python_configurations if not c.identifier.startswith('pp')]
|
||||
'''
|
||||
)
|
||||
)
|
||||
python_configurations = [
|
||||
c for c in python_configurations if not c.identifier.startswith('pp')
|
||||
]
|
||||
|
||||
if any(c.identifier.startswith('cp35') for c in python_configurations):
|
||||
# CPython 3.5 doesn't work on macOS 11
|
||||
log.warning(unwrap('''
|
||||
log.warning(
|
||||
unwrap(
|
||||
'''
|
||||
CPython 3.5 is unsupported when building on macOS 11. To build CPython 3.5 wheels,
|
||||
build on an older OS, such as macOS 10.15. To silence this warning, deselect CPython
|
||||
3.5 by adding "cp35-macosx_x86_64" to your CIBW_SKIP option.
|
||||
'''))
|
||||
python_configurations = [c for c in python_configurations if not c.identifier.startswith('cp35')]
|
||||
'''
|
||||
)
|
||||
)
|
||||
python_configurations = [
|
||||
c for c in python_configurations if not c.identifier.startswith('cp35')
|
||||
]
|
||||
|
||||
return python_configurations
|
||||
|
||||
@@ -122,12 +143,16 @@ def make_symlinks(installation_bin_path: Path, python_executable: str, pip_execu
|
||||
SYMLINKS_DIR.mkdir(parents=True)
|
||||
|
||||
(SYMLINKS_DIR / 'python').symlink_to(installation_bin_path / python_executable)
|
||||
(SYMLINKS_DIR / 'python-config').symlink_to(installation_bin_path / (python_executable + '-config'))
|
||||
(SYMLINKS_DIR / 'python-config').symlink_to(
|
||||
installation_bin_path / (python_executable + '-config')
|
||||
)
|
||||
(SYMLINKS_DIR / 'pip').symlink_to(installation_bin_path / pip_executable)
|
||||
|
||||
|
||||
def install_cpython(version: str, url: str) -> Path:
|
||||
installed_system_packages = subprocess.run(['pkgutil', '--pkgs'], universal_newlines=True, check=True, stdout=subprocess.PIPE).stdout.splitlines()
|
||||
installed_system_packages = subprocess.run(
|
||||
['pkgutil', '--pkgs'], universal_newlines=True, check=True, stdout=subprocess.PIPE
|
||||
).stdout.splitlines()
|
||||
|
||||
# if this version of python isn't installed, get it from python.org and install
|
||||
python_package_identifier = f'org.python.Python.PythonFramework-{version}'
|
||||
@@ -143,9 +168,18 @@ def install_cpython(version: str, url: str) -> Path:
|
||||
if version == '3.5':
|
||||
open_ssl_patch_url = f'https://github.com/mayeut/patch-macos-python-openssl/releases/download/v1.1.1h/patch-macos-python-{version}-openssl-v1.1.1h.tar.gz'
|
||||
download(open_ssl_patch_url, Path('/tmp/python-patch.tar.gz'))
|
||||
call(['sudo', 'tar', '-C', f'/Library/Frameworks/Python.framework/Versions/{version}/', '-xmf', '/tmp/python-patch.tar.gz'])
|
||||
call(
|
||||
[
|
||||
'sudo',
|
||||
'tar',
|
||||
'-C',
|
||||
f'/Library/Frameworks/Python.framework/Versions/{version}/',
|
||||
'-xmf',
|
||||
'/tmp/python-patch.tar.gz',
|
||||
]
|
||||
)
|
||||
|
||||
call(["sudo", str(installation_bin_path/python_executable), str(install_certifi_script)])
|
||||
call(["sudo", str(installation_bin_path / python_executable), str(install_certifi_script)])
|
||||
|
||||
pip_executable = 'pip3' if version[0] == '3' else 'pip'
|
||||
make_symlinks(installation_bin_path, python_executable, pip_executable)
|
||||
@@ -157,7 +191,7 @@ def install_pypy(version: str, url: str) -> Path:
|
||||
pypy_tar_bz2 = url.rsplit('/', 1)[-1]
|
||||
extension = ".tar.bz2"
|
||||
assert pypy_tar_bz2.endswith(extension)
|
||||
pypy_base_filename = pypy_tar_bz2[:-len(extension)]
|
||||
pypy_base_filename = pypy_tar_bz2[: -len(extension)]
|
||||
installation_path = Path('/tmp') / pypy_base_filename
|
||||
if not installation_path.exists():
|
||||
downloaded_tar_bz2 = Path("/tmp") / pypy_tar_bz2
|
||||
@@ -176,14 +210,18 @@ def install_pypy(version: str, url: str) -> Path:
|
||||
return installation_bin_path
|
||||
|
||||
|
||||
def setup_python(python_configuration: PythonConfiguration,
|
||||
dependency_constraint_flags: Sequence[PathOrStr],
|
||||
environment: ParsedEnvironment) -> Dict[str, str]:
|
||||
def setup_python(
|
||||
python_configuration: PythonConfiguration,
|
||||
dependency_constraint_flags: Sequence[PathOrStr],
|
||||
environment: ParsedEnvironment,
|
||||
) -> Dict[str, str]:
|
||||
implementation_id = python_configuration.identifier.split("-")[0]
|
||||
log.step(f'Installing Python {implementation_id}...')
|
||||
|
||||
if implementation_id.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 implementation_id.startswith('pp'):
|
||||
installation_bin_path = install_pypy(python_configuration.version, python_configuration.url)
|
||||
else:
|
||||
@@ -192,11 +230,13 @@ def setup_python(python_configuration: PythonConfiguration,
|
||||
log.step('Setting up build environment...')
|
||||
|
||||
env = os.environ.copy()
|
||||
env['PATH'] = os.pathsep.join([
|
||||
str(SYMLINKS_DIR),
|
||||
str(installation_bin_path),
|
||||
env['PATH'],
|
||||
])
|
||||
env['PATH'] = os.pathsep.join(
|
||||
[
|
||||
str(SYMLINKS_DIR),
|
||||
str(installation_bin_path),
|
||||
env['PATH'],
|
||||
]
|
||||
)
|
||||
|
||||
# Fix issue with site.py setting the wrong `sys.prefix`, `sys.exec_prefix`,
|
||||
# `sys.path`, ... for PyPy: https://foss.heptapod.net/pypy/pypy/issues/3175
|
||||
@@ -213,9 +253,14 @@ def setup_python(python_configuration: PythonConfiguration,
|
||||
# check what version we're on
|
||||
call(['which', 'python'], env=env)
|
||||
call(['python', '--version'], env=env)
|
||||
which_python = subprocess.run(['which', 'python'], env=env, universal_newlines=True, check=True, stdout=subprocess.PIPE).stdout.strip()
|
||||
which_python = subprocess.run(
|
||||
['which', 'python'], env=env, universal_newlines=True, check=True, stdout=subprocess.PIPE
|
||||
).stdout.strip()
|
||||
if which_python != '/tmp/cibw_bin/python':
|
||||
print("cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it.", file=sys.stderr)
|
||||
print(
|
||||
"cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
# install pip & wheel
|
||||
@@ -223,9 +268,14 @@ def setup_python(python_configuration: PythonConfiguration,
|
||||
assert (installation_bin_path / 'pip').exists()
|
||||
call(['which', 'pip'], env=env)
|
||||
call(['pip', '--version'], env=env)
|
||||
which_pip = subprocess.run(['which', 'pip'], env=env, universal_newlines=True, check=True, stdout=subprocess.PIPE).stdout.strip()
|
||||
which_pip = subprocess.run(
|
||||
['which', 'pip'], env=env, universal_newlines=True, check=True, stdout=subprocess.PIPE
|
||||
).stdout.strip()
|
||||
if which_pip != '/tmp/cibw_bin/pip':
|
||||
print("cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it.", file=sys.stderr)
|
||||
print(
|
||||
"cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
# Set MACOSX_DEPLOYMENT_TARGET to 10.9, if the user didn't set it.
|
||||
@@ -271,15 +321,30 @@ def setup_python(python_configuration: PythonConfiguration,
|
||||
arm64_compatible_sdks = [s for s in sdks if not s.startswith('macosx10.')]
|
||||
|
||||
if not arm64_compatible_sdks:
|
||||
log.warning(unwrap('''
|
||||
log.warning(
|
||||
unwrap(
|
||||
'''
|
||||
SDK for building arm64-compatible wheels not found. You need Xcode 12.2 or later
|
||||
to build universal2 or arm64 wheels.
|
||||
'''))
|
||||
'''
|
||||
)
|
||||
)
|
||||
else:
|
||||
env.setdefault('SDKROOT', arm64_compatible_sdks[0])
|
||||
|
||||
log.step('Installing build tools...')
|
||||
call(['pip', 'install', '--upgrade', 'setuptools', 'wheel', 'delocate', *dependency_constraint_flags], env=env)
|
||||
call(
|
||||
[
|
||||
'pip',
|
||||
'install',
|
||||
'--upgrade',
|
||||
'setuptools',
|
||||
'wheel',
|
||||
'delocate',
|
||||
*dependency_constraint_flags,
|
||||
],
|
||||
env=env,
|
||||
)
|
||||
|
||||
return env
|
||||
|
||||
@@ -294,10 +359,14 @@ def build(options: BuildOptions) -> None:
|
||||
log.step('Running before_all...')
|
||||
env = options.environment.as_dictionary(prev_environment=os.environ)
|
||||
env.setdefault('MACOSX_DEPLOYMENT_TARGET', '10.9')
|
||||
before_all_prepared = prepare_command(options.before_all, project='.', package=options.package_dir)
|
||||
before_all_prepared = prepare_command(
|
||||
options.before_all, project='.', package=options.package_dir
|
||||
)
|
||||
call([before_all_prepared], shell=True, env=env)
|
||||
|
||||
python_configurations = get_python_configurations(options.build_selector, options.architectures)
|
||||
python_configurations = get_python_configurations(
|
||||
options.build_selector, options.architectures
|
||||
)
|
||||
|
||||
for config in python_configurations:
|
||||
log.build_start(config.identifier)
|
||||
@@ -308,14 +377,17 @@ def build(options: BuildOptions) -> None:
|
||||
dependency_constraint_flags: Sequence[PathOrStr] = []
|
||||
if options.dependency_constraints:
|
||||
dependency_constraint_flags = [
|
||||
'-c', options.dependency_constraints.get_for_python_version(config.version)
|
||||
'-c',
|
||||
options.dependency_constraints.get_for_python_version(config.version),
|
||||
]
|
||||
|
||||
env = setup_python(config, dependency_constraint_flags, options.environment)
|
||||
|
||||
if options.before_build:
|
||||
log.step('Running before_build...')
|
||||
before_build_prepared = prepare_command(options.before_build, project='.', package=options.package_dir)
|
||||
before_build_prepared = prepare_command(
|
||||
options.before_build, project='.', package=options.package_dir
|
||||
)
|
||||
call(before_build_prepared, env=env, shell=True)
|
||||
|
||||
log.step('Building wheel...')
|
||||
@@ -325,13 +397,18 @@ def build(options: BuildOptions) -> None:
|
||||
|
||||
# Path.resolve() is needed. Without it pip wheel may try to fetch package from pypi.org
|
||||
# see https://github.com/joerick/cibuildwheel/pull/369
|
||||
call([
|
||||
'pip', 'wheel',
|
||||
options.package_dir.resolve(),
|
||||
'--wheel-dir', built_wheel_dir,
|
||||
'--no-deps',
|
||||
*get_build_verbosity_extra_flags(options.build_verbosity)
|
||||
], env=env)
|
||||
call(
|
||||
[
|
||||
'pip',
|
||||
'wheel',
|
||||
options.package_dir.resolve(),
|
||||
'--wheel-dir',
|
||||
built_wheel_dir,
|
||||
'--no-deps',
|
||||
*get_build_verbosity_extra_flags(options.build_verbosity),
|
||||
],
|
||||
env=env,
|
||||
)
|
||||
|
||||
built_wheel = next(built_wheel_dir.glob('*.whl'))
|
||||
|
||||
@@ -385,27 +462,39 @@ def build(options: BuildOptions) -> None:
|
||||
|
||||
if machine_arch == 'x86_64' and testing_arch == 'arm64':
|
||||
if config_is_arm64:
|
||||
log.warning(unwrap('''
|
||||
log.warning(
|
||||
unwrap(
|
||||
'''
|
||||
While arm64 wheels can be built on x86_64, they cannot be tested. The
|
||||
ability to test the arm64 wheels will be added in a future release of
|
||||
cibuildwheel, once Apple Silicon CI runners are widely available. To
|
||||
silence this warning, set `CIBW_TEST_SKIP: *-macosx_arm64`.
|
||||
'''))
|
||||
'''
|
||||
)
|
||||
)
|
||||
elif config_is_universal2:
|
||||
log.warning(unwrap('''
|
||||
log.warning(
|
||||
unwrap(
|
||||
'''
|
||||
While universal2 wheels can be built on x86_64, the arm64 part of them
|
||||
cannot currently be tested. The ability to test the arm64 part of a
|
||||
universal2 wheel will be added in a future release of cibuildwheel, once
|
||||
Apple Silicon CI runners are widely available. To silence this warning,
|
||||
set `CIBW_TEST_SKIP: *-macosx_universal2:arm64`.
|
||||
'''))
|
||||
'''
|
||||
)
|
||||
)
|
||||
else:
|
||||
raise RuntimeError('unreachable')
|
||||
|
||||
# skip this test
|
||||
continue
|
||||
|
||||
log.step('Testing wheel...' if testing_arch == machine_arch else f'Testing wheel on {testing_arch}...')
|
||||
log.step(
|
||||
'Testing wheel...'
|
||||
if testing_arch == machine_arch
|
||||
else f'Testing wheel on {testing_arch}...'
|
||||
)
|
||||
|
||||
# set up a virtual environment to install and test from, to make sure
|
||||
# there are no dependencies that were pulled in at build time.
|
||||
@@ -418,7 +507,9 @@ def build(options: BuildOptions) -> None:
|
||||
# rosetta2 will provide the emulation with just the arch prefix.
|
||||
arch_prefix = ['arch', '-x86_64']
|
||||
else:
|
||||
raise RuntimeError("don't know how to emulate {testing_arch} on {machine_arch}")
|
||||
raise RuntimeError(
|
||||
"don't know how to emulate {testing_arch} on {machine_arch}"
|
||||
)
|
||||
|
||||
# define a custom 'call' function that adds the arch prefix each time
|
||||
def call_with_arch(args: Sequence[PathOrStr], **kwargs: Any) -> None:
|
||||
@@ -430,27 +521,38 @@ def build(options: BuildOptions) -> None:
|
||||
|
||||
# Use --no-download to ensure determinism by using seed libraries
|
||||
# built into virtualenv
|
||||
call_with_arch(['python', '-m', 'virtualenv', '--no-download', venv_dir], env=env)
|
||||
call_with_arch(
|
||||
['python', '-m', 'virtualenv', '--no-download', venv_dir], env=env
|
||||
)
|
||||
|
||||
virtualenv_env = env.copy()
|
||||
virtualenv_env['PATH'] = os.pathsep.join([
|
||||
str(venv_dir / 'bin'),
|
||||
virtualenv_env['PATH'],
|
||||
])
|
||||
virtualenv_env['PATH'] = os.pathsep.join(
|
||||
[
|
||||
str(venv_dir / 'bin'),
|
||||
virtualenv_env['PATH'],
|
||||
]
|
||||
)
|
||||
|
||||
# check that we are using the Python from the virtual environment
|
||||
call_with_arch(['which', 'python'], env=virtualenv_env)
|
||||
|
||||
if options.before_test:
|
||||
before_test_prepared = prepare_command(options.before_test, project='.', package=options.package_dir)
|
||||
before_test_prepared = prepare_command(
|
||||
options.before_test, project='.', package=options.package_dir
|
||||
)
|
||||
call_with_arch(before_test_prepared, env=virtualenv_env, shell=True)
|
||||
|
||||
# install the wheel
|
||||
call_with_arch(['pip', 'install', f"{repaired_wheel}{options.test_extras}"], env=virtualenv_env)
|
||||
call_with_arch(
|
||||
['pip', 'install', f"{repaired_wheel}{options.test_extras}"],
|
||||
env=virtualenv_env,
|
||||
)
|
||||
|
||||
# test the wheel
|
||||
if options.test_requires:
|
||||
call_with_arch(['pip', 'install'] + options.test_requires, env=virtualenv_env)
|
||||
call_with_arch(
|
||||
['pip', 'install'] + options.test_requires, env=virtualenv_env
|
||||
)
|
||||
|
||||
# run the tests from $HOME, with an absolute path in the command
|
||||
# (this ensures that Python runs the tests against the installed wheel
|
||||
@@ -458,9 +560,14 @@ def build(options: BuildOptions) -> None:
|
||||
test_command_prepared = prepare_command(
|
||||
options.test_command,
|
||||
project=Path('.').resolve(),
|
||||
package=options.package_dir.resolve()
|
||||
package=options.package_dir.resolve(),
|
||||
)
|
||||
call_with_arch(
|
||||
test_command_prepared,
|
||||
cwd=os.environ['HOME'],
|
||||
env=virtualenv_env,
|
||||
shell=True,
|
||||
)
|
||||
call_with_arch(test_command_prepared, cwd=os.environ['HOME'], env=virtualenv_env, shell=True)
|
||||
|
||||
# clean up
|
||||
shutil.rmtree(venv_dir)
|
||||
@@ -469,5 +576,7 @@ def build(options: BuildOptions) -> None:
|
||||
shutil.move(str(repaired_wheel), options.output_dir)
|
||||
log.build_end()
|
||||
except subprocess.CalledProcessError as error:
|
||||
log.step_end_with_error(f'Command {error.cmd} failed with code {error.returncode}. {error.stdout}')
|
||||
log.step_end_with_error(
|
||||
f'Command {error.cmd} failed with code {error.returncode}. {error.stdout}'
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
@@ -35,9 +35,8 @@ class Analyzer(ast.NodeVisitor):
|
||||
if node.arg == "python_requires":
|
||||
# Must not be nested in an if or other structure
|
||||
# This will be Module -> Expr -> Call -> keyword
|
||||
if (
|
||||
not hasattr(node.parent.parent.parent, "parent") # type: ignore
|
||||
and isinstance(node.value, Constant)
|
||||
if not hasattr(node.parent.parent.parent, "parent") and isinstance( # type: ignore
|
||||
node.value, Constant
|
||||
):
|
||||
self.requires_python = get_constant(node.value)
|
||||
|
||||
|
||||
@@ -34,14 +34,17 @@ PY3 = sys.version_info[0] == 3
|
||||
if PY3:
|
||||
iterbytes = iter
|
||||
else:
|
||||
|
||||
def iterbytes(buf):
|
||||
return (ord(byte) for byte in buf)
|
||||
|
||||
|
||||
try:
|
||||
from base64 import b85decode
|
||||
except ImportError:
|
||||
_b85alphabet = (b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
b"abcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~")
|
||||
_b85alphabet = (
|
||||
b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" b"abcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~"
|
||||
)
|
||||
|
||||
def b85decode(b):
|
||||
_b85dec = [None] * 256
|
||||
@@ -53,7 +56,7 @@ except ImportError:
|
||||
out = []
|
||||
packI = struct.Struct('!I').pack
|
||||
for i in range(0, len(b), 5):
|
||||
chunk = b[i:i + 5]
|
||||
chunk = b[i : i + 5]
|
||||
acc = 0
|
||||
try:
|
||||
for c in iterbytes(chunk):
|
||||
@@ -61,15 +64,12 @@ except ImportError:
|
||||
except TypeError:
|
||||
for j, c in enumerate(iterbytes(chunk)):
|
||||
if _b85dec[c] is None:
|
||||
raise ValueError(
|
||||
'bad base85 character at position %d' % (i + j)
|
||||
)
|
||||
raise ValueError('bad base85 character at position %d' % (i + j))
|
||||
raise
|
||||
try:
|
||||
out.append(packI(acc))
|
||||
except struct.error:
|
||||
raise ValueError('base85 overflow in hunk starting at byte %d'
|
||||
% i)
|
||||
raise ValueError('base85 overflow in hunk starting at byte %d' % i)
|
||||
|
||||
result = b''.join(out)
|
||||
if padding:
|
||||
@@ -87,6 +87,7 @@ def bootstrap(tmpdir=None):
|
||||
# Due to pip._internal.commands.commands_dict structure, a monkeypatch
|
||||
# seems the simplest workaround.
|
||||
install_parse_args = InstallCommand.parse_args
|
||||
|
||||
def cert_parse_args(self, args):
|
||||
# If cert isn't specified in config or environment, we provide our
|
||||
# own certificate through defaults.
|
||||
@@ -95,6 +96,7 @@ def bootstrap(tmpdir=None):
|
||||
if not self.parser.get_default_values().cert:
|
||||
self.parser.defaults["cert"] = cert_path # calculated below
|
||||
return install_parse_args(self, args)
|
||||
|
||||
InstallCommand.parse_args = cert_parse_args
|
||||
|
||||
implicit_pip = True
|
||||
@@ -118,12 +120,14 @@ def bootstrap(tmpdir=None):
|
||||
if implicit_setuptools:
|
||||
try:
|
||||
import setuptools # noqa
|
||||
|
||||
implicit_setuptools = False
|
||||
except ImportError:
|
||||
pass
|
||||
if implicit_wheel:
|
||||
try:
|
||||
import wheel # noqa
|
||||
|
||||
implicit_wheel = False
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
@@ -13,20 +13,27 @@ import stat
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
STAT_0o775 = (stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
|
||||
| stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP
|
||||
| stat.S_IROTH | stat.S_IXOTH)
|
||||
STAT_0o775 = (
|
||||
stat.S_IRUSR
|
||||
| stat.S_IWUSR
|
||||
| stat.S_IXUSR
|
||||
| stat.S_IRGRP
|
||||
| stat.S_IWGRP
|
||||
| stat.S_IXGRP
|
||||
| stat.S_IROTH
|
||||
| stat.S_IXOTH
|
||||
)
|
||||
|
||||
if sys.version_info[0] == 2:
|
||||
FileNotFoundError = OSError
|
||||
|
||||
|
||||
def main():
|
||||
openssl_dir, openssl_cafile = os.path.split(
|
||||
ssl.get_default_verify_paths().openssl_cafile)
|
||||
openssl_dir, openssl_cafile = os.path.split(ssl.get_default_verify_paths().openssl_cafile)
|
||||
print(" -- pip install --upgrade certifi")
|
||||
subprocess.check_call([sys.executable,
|
||||
"-E", "-s", "-m", "pip", "install", "--upgrade", "certifi"])
|
||||
subprocess.check_call(
|
||||
[sys.executable, "-E", "-s", "-m", "pip", "install", "--upgrade", "certifi"]
|
||||
)
|
||||
|
||||
import certifi
|
||||
|
||||
|
||||
+12
-1
@@ -9,7 +9,18 @@ else:
|
||||
from typing import Final, Literal, TypedDict
|
||||
|
||||
|
||||
__all__ = ("Final", "Literal", "TypedDict", "Set", "Union", "PopenBytes", "PathOrStr", "PlatformName", "PLATFORMS", "assert_never")
|
||||
__all__ = (
|
||||
"Final",
|
||||
"Literal",
|
||||
"TypedDict",
|
||||
"Set",
|
||||
"Union",
|
||||
"PopenBytes",
|
||||
"PathOrStr",
|
||||
"PlatformName",
|
||||
"PLATFORMS",
|
||||
"assert_never",
|
||||
)
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
||||
+11
-7
@@ -62,7 +62,9 @@ class IdentifierSelector:
|
||||
numeric digits before the first dash.
|
||||
"""
|
||||
|
||||
def __init__(self, *, build_config: str, skip_config: str, requires_python: Optional[SpecifierSet] = None):
|
||||
def __init__(
|
||||
self, *, build_config: str, skip_config: str, requires_python: Optional[SpecifierSet] = None
|
||||
):
|
||||
self.build_patterns = build_config.split()
|
||||
self.skip_patterns = skip_config.split()
|
||||
self.requires_python = requires_python
|
||||
@@ -77,7 +79,9 @@ class IdentifierSelector:
|
||||
if not self.requires_python.contains(version):
|
||||
return False
|
||||
|
||||
build_patterns = itertools.chain.from_iterable(bracex.expand(p) for p in self.build_patterns)
|
||||
build_patterns = itertools.chain.from_iterable(
|
||||
bracex.expand(p) for p in self.build_patterns
|
||||
)
|
||||
skip_patterns = itertools.chain.from_iterable(bracex.expand(p) for p in self.skip_patterns)
|
||||
|
||||
build: bool = any(fnmatch.fnmatch(build_id, pat) for pat in build_patterns)
|
||||
@@ -153,9 +157,7 @@ class DependencyConstraints:
|
||||
|
||||
@staticmethod
|
||||
def with_defaults() -> 'DependencyConstraints':
|
||||
return DependencyConstraints(
|
||||
base_file_path=resources_dir / 'constraints.txt'
|
||||
)
|
||||
return DependencyConstraints(base_file_path=resources_dir / 'constraints.txt')
|
||||
|
||||
def get_for_python_version(self, version: str) -> Path:
|
||||
version_parts = version.split('.')
|
||||
@@ -195,7 +197,8 @@ class BuildOptions(NamedTuple):
|
||||
|
||||
class NonPlatformWheelError(Exception):
|
||||
def __init__(self) -> None:
|
||||
message = textwrap.dedent('''
|
||||
message = textwrap.dedent(
|
||||
'''
|
||||
cibuildwheel: Build failed because a pure Python wheel was generated.
|
||||
|
||||
If you intend to build a pure-Python wheel, you don't need cibuildwheel - use
|
||||
@@ -203,7 +206,8 @@ class NonPlatformWheelError(Exception):
|
||||
|
||||
If you expected a platform wheel, check your project configuration, or run
|
||||
cibuildwheel with CIBW_BUILD_VERBOSITY=1 to view build logs.
|
||||
''')
|
||||
'''
|
||||
)
|
||||
|
||||
super().__init__(message)
|
||||
|
||||
|
||||
+105
-47
@@ -27,8 +27,9 @@ from .util import (
|
||||
IS_RUNNING_ON_AZURE = Path('C:\\hostedtoolcache').exists()
|
||||
|
||||
|
||||
def call(args: Sequence[PathOrStr], env: Optional[Dict[str, str]] = None,
|
||||
cwd: Optional[str] = None) -> None:
|
||||
def call(
|
||||
args: Sequence[PathOrStr], env: Optional[Dict[str, str]] = None, cwd: Optional[str] = None
|
||||
) -> None:
|
||||
print('+ ' + ' '.join(str(a) for a in args))
|
||||
# we use shell=True here, even though we don't need a shell due to a bug
|
||||
# https://bugs.python.org/issue8557
|
||||
@@ -46,9 +47,12 @@ def get_nuget_args(version: str, arch: str) -> List[str]:
|
||||
python_name += 'x86'
|
||||
return [
|
||||
python_name,
|
||||
'-Version', version,
|
||||
'-FallbackSource', 'https://api.nuget.org/v3/index.json',
|
||||
'-OutputDirectory', 'C:\\cibw\\python',
|
||||
'-Version',
|
||||
version,
|
||||
'-FallbackSource',
|
||||
'https://api.nuget.org/v3/index.json',
|
||||
'-OutputDirectory',
|
||||
'C:\\cibw\\python',
|
||||
]
|
||||
|
||||
|
||||
@@ -60,8 +64,8 @@ class PythonConfiguration(NamedTuple):
|
||||
|
||||
|
||||
def get_python_configurations(
|
||||
build_selector: BuildSelector,
|
||||
architectures: Set[Architecture],
|
||||
build_selector: BuildSelector,
|
||||
architectures: Set[Architecture],
|
||||
) -> List[PythonConfiguration]:
|
||||
|
||||
full_python_configs = read_python_configs('windows')
|
||||
@@ -76,11 +80,14 @@ def get_python_configurations(
|
||||
# Only supported with custom compiler, since MS removed the 2008 compiler download
|
||||
custom_compiler = os.environ.get('DISTUTILS_USE_SDK') and os.environ.get('MSSdk')
|
||||
if not custom_compiler:
|
||||
python_configurations = [c for c in python_configurations if not c.version.startswith('2.7')]
|
||||
python_configurations = [
|
||||
c for c in python_configurations if not c.version.startswith('2.7')
|
||||
]
|
||||
|
||||
# skip builds as required
|
||||
python_configurations = [
|
||||
c for c in python_configurations
|
||||
c
|
||||
for c in python_configurations
|
||||
if build_selector(c.identifier) and map_arch[c.arch] in architectures
|
||||
]
|
||||
|
||||
@@ -105,7 +112,7 @@ def install_pypy(version: str, arch: str, url: str) -> Path:
|
||||
zip_filename = url.rsplit('/', 1)[-1]
|
||||
extension = ".zip"
|
||||
assert zip_filename.endswith(extension)
|
||||
installation_path = Path('C:\\cibw') / zip_filename[:-len(extension)]
|
||||
installation_path = Path('C:\\cibw') / zip_filename[: -len(extension)]
|
||||
if not installation_path.exists():
|
||||
pypy_zip = Path('C:\\cibw') / zip_filename
|
||||
download(url, pypy_zip)
|
||||
@@ -116,7 +123,11 @@ def install_pypy(version: str, arch: str, url: str) -> Path:
|
||||
return installation_path
|
||||
|
||||
|
||||
def setup_python(python_configuration: PythonConfiguration, dependency_constraint_flags: Sequence[PathOrStr], environment: ParsedEnvironment) -> Dict[str, str]:
|
||||
def setup_python(
|
||||
python_configuration: PythonConfiguration,
|
||||
dependency_constraint_flags: Sequence[PathOrStr],
|
||||
environment: ParsedEnvironment,
|
||||
) -> Dict[str, str]:
|
||||
nuget = Path('C:\\cibw\\nuget.exe')
|
||||
if not nuget.exists():
|
||||
log.step('Downloading nuget...')
|
||||
@@ -126,10 +137,14 @@ def setup_python(python_configuration: PythonConfiguration, dependency_constrain
|
||||
log.step(f'Installing Python {implementation_id}...')
|
||||
|
||||
if implementation_id.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 implementation_id.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:
|
||||
raise ValueError("Unknown Python implementation")
|
||||
|
||||
@@ -141,11 +156,9 @@ def setup_python(python_configuration: PythonConfiguration, dependency_constrain
|
||||
env = os.environ.copy()
|
||||
env['PYTHON_VERSION'] = python_configuration.version
|
||||
env['PYTHON_ARCH'] = python_configuration.arch
|
||||
env['PATH'] = os.pathsep.join([
|
||||
str(installation_path),
|
||||
str(installation_path / 'Scripts'),
|
||||
env['PATH']
|
||||
])
|
||||
env['PATH'] = os.pathsep.join(
|
||||
[str(installation_path), str(installation_path / 'Scripts'), env['PATH']]
|
||||
)
|
||||
env['PIP_DISABLE_PIP_VERSION_CHECK'] = '1'
|
||||
|
||||
# update env with results from CIBW_ENVIRONMENT
|
||||
@@ -155,25 +168,53 @@ def setup_python(python_configuration: PythonConfiguration, dependency_constrain
|
||||
call(['where', 'python'], env=env)
|
||||
call(['python', '--version'], env=env)
|
||||
call(['python', '-c', '"import struct; print(struct.calcsize(\'P\') * 8)"'], env=env)
|
||||
where_python = subprocess.run(['where', 'python'], env=env, universal_newlines=True, check=True, stdout=subprocess.PIPE).stdout.splitlines()[0].strip()
|
||||
where_python = (
|
||||
subprocess.run(
|
||||
['where', 'python'],
|
||||
env=env,
|
||||
universal_newlines=True,
|
||||
check=True,
|
||||
stdout=subprocess.PIPE,
|
||||
)
|
||||
.stdout.splitlines()[0]
|
||||
.strip()
|
||||
)
|
||||
if where_python != str(installation_path / 'python.exe'):
|
||||
print("cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it.", file=sys.stderr)
|
||||
print(
|
||||
"cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
# make sure pip is installed
|
||||
if not (installation_path / 'Scripts' / 'pip.exe').exists():
|
||||
call(['python', get_pip_script, *dependency_constraint_flags], env=env, cwd="C:\\cibw")
|
||||
assert (installation_path / 'Scripts' / 'pip.exe').exists()
|
||||
where_pip = subprocess.run(['where', 'pip'], env=env, universal_newlines=True, check=True, stdout=subprocess.PIPE).stdout.splitlines()[0].strip()
|
||||
where_pip = (
|
||||
subprocess.run(
|
||||
['where', 'pip'], env=env, universal_newlines=True, check=True, stdout=subprocess.PIPE
|
||||
)
|
||||
.stdout.splitlines()[0]
|
||||
.strip()
|
||||
)
|
||||
if where_pip.strip() != str(installation_path / 'Scripts' / 'pip.exe'):
|
||||
print("cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it.", file=sys.stderr)
|
||||
print(
|
||||
"cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
log.step('Installing build tools...')
|
||||
|
||||
call(['python', '-m', 'pip', 'install', '--upgrade', 'pip', *dependency_constraint_flags], env=env)
|
||||
call(
|
||||
['python', '-m', 'pip', 'install', '--upgrade', 'pip', *dependency_constraint_flags],
|
||||
env=env,
|
||||
)
|
||||
call(['pip', '--version'], env=env)
|
||||
call(['pip', 'install', '--upgrade', 'setuptools', 'wheel', *dependency_constraint_flags], env=env)
|
||||
call(
|
||||
['pip', 'install', '--upgrade', 'setuptools', 'wheel', *dependency_constraint_flags],
|
||||
env=env,
|
||||
)
|
||||
|
||||
return env
|
||||
|
||||
@@ -194,11 +235,7 @@ def pep_518_cp35_workaround(package_dir: Path, env: Dict[str, str]) -> None:
|
||||
|
||||
if pyproject_path.exists():
|
||||
data = toml.load(pyproject_path)
|
||||
requirements = (
|
||||
data['build-system'].get('requires', [])
|
||||
if 'build-system' in data
|
||||
else []
|
||||
)
|
||||
requirements = data['build-system'].get('requires', []) if 'build-system' in data else []
|
||||
|
||||
if requirements:
|
||||
log.step('Performing PEP518 workaround...')
|
||||
@@ -219,10 +256,14 @@ def build(options: BuildOptions) -> None:
|
||||
if options.before_all:
|
||||
log.step('Running before_all...')
|
||||
env = options.environment.as_dictionary(prev_environment=os.environ)
|
||||
before_all_prepared = prepare_command(options.before_all, project='.', package=options.package_dir)
|
||||
before_all_prepared = prepare_command(
|
||||
options.before_all, project='.', package=options.package_dir
|
||||
)
|
||||
shell(before_all_prepared, env=env)
|
||||
|
||||
python_configurations = get_python_configurations(options.build_selector, options.architectures)
|
||||
python_configurations = get_python_configurations(
|
||||
options.build_selector, options.architectures
|
||||
)
|
||||
|
||||
for config in python_configurations:
|
||||
log.build_start(config.identifier)
|
||||
@@ -230,7 +271,8 @@ def build(options: BuildOptions) -> None:
|
||||
dependency_constraint_flags: Sequence[PathOrStr] = []
|
||||
if options.dependency_constraints:
|
||||
dependency_constraint_flags = [
|
||||
'-c', options.dependency_constraints.get_for_python_version(config.version)
|
||||
'-c',
|
||||
options.dependency_constraints.get_for_python_version(config.version),
|
||||
]
|
||||
|
||||
# install Python
|
||||
@@ -239,7 +281,9 @@ def build(options: BuildOptions) -> None:
|
||||
# run the before_build command
|
||||
if options.before_build:
|
||||
log.step('Running before_build...')
|
||||
before_build_prepared = prepare_command(options.before_build, project='.', package=options.package_dir)
|
||||
before_build_prepared = prepare_command(
|
||||
options.before_build, project='.', package=options.package_dir
|
||||
)
|
||||
shell(before_build_prepared, env=env)
|
||||
|
||||
# activate the PEP 518 patch if on Windows Python 3.5
|
||||
@@ -253,13 +297,18 @@ def build(options: BuildOptions) -> None:
|
||||
built_wheel_dir.mkdir(parents=True)
|
||||
# Path.resolve() is needed. Without it pip wheel may try to fetch package from pypi.org
|
||||
# see https://github.com/joerick/cibuildwheel/pull/369
|
||||
call([
|
||||
'pip', 'wheel',
|
||||
options.package_dir.resolve(),
|
||||
'-w', built_wheel_dir,
|
||||
'--no-deps',
|
||||
*get_build_verbosity_extra_flags(options.build_verbosity)
|
||||
], env=env)
|
||||
call(
|
||||
[
|
||||
'pip',
|
||||
'wheel',
|
||||
options.package_dir.resolve(),
|
||||
'-w',
|
||||
built_wheel_dir,
|
||||
'--no-deps',
|
||||
*get_build_verbosity_extra_flags(options.build_verbosity),
|
||||
],
|
||||
env=env,
|
||||
)
|
||||
|
||||
built_wheel = next(built_wheel_dir.glob('*.whl'))
|
||||
|
||||
@@ -273,7 +322,9 @@ def build(options: BuildOptions) -> None:
|
||||
|
||||
if options.repair_command:
|
||||
log.step('Repairing wheel...')
|
||||
repair_command_prepared = prepare_command(options.repair_command, wheel=built_wheel, dest_dir=repaired_wheel_dir)
|
||||
repair_command_prepared = prepare_command(
|
||||
options.repair_command, wheel=built_wheel, dest_dir=repaired_wheel_dir
|
||||
)
|
||||
shell(repair_command_prepared, env=env)
|
||||
else:
|
||||
shutil.move(str(built_wheel), repaired_wheel_dir)
|
||||
@@ -292,10 +343,12 @@ def build(options: BuildOptions) -> None:
|
||||
call(['python', '-m', 'virtualenv', '--no-download', venv_dir], env=env)
|
||||
|
||||
virtualenv_env = env.copy()
|
||||
virtualenv_env['PATH'] = os.pathsep.join([
|
||||
str(venv_dir / 'Scripts'),
|
||||
virtualenv_env['PATH'],
|
||||
])
|
||||
virtualenv_env['PATH'] = os.pathsep.join(
|
||||
[
|
||||
str(venv_dir / 'Scripts'),
|
||||
virtualenv_env['PATH'],
|
||||
]
|
||||
)
|
||||
|
||||
# check that we are using the Python from the virtual environment
|
||||
call(['where', 'python'], env=virtualenv_env)
|
||||
@@ -309,7 +362,10 @@ def build(options: BuildOptions) -> None:
|
||||
shell(before_test_prepared, env=virtualenv_env)
|
||||
|
||||
# install the wheel
|
||||
call(['pip', 'install', str(repaired_wheel) + options.test_extras], env=virtualenv_env)
|
||||
call(
|
||||
['pip', 'install', str(repaired_wheel) + options.test_extras],
|
||||
env=virtualenv_env,
|
||||
)
|
||||
|
||||
# test the wheel
|
||||
if options.test_requires:
|
||||
@@ -332,5 +388,7 @@ def build(options: BuildOptions) -> None:
|
||||
shutil.move(str(repaired_wheel), options.output_dir)
|
||||
log.build_end()
|
||||
except subprocess.CalledProcessError as error:
|
||||
log.step_end_with_error(f'Command {error.cmd} failed with code {error.returncode}. {error.stdout}')
|
||||
log.step_end_with_error(
|
||||
f'Command {error.cmd} failed with code {error.returncode}. {error.stdout}'
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
@@ -8,12 +8,14 @@ from cibuildwheel.logger import Logger
|
||||
from . import test_projects, utils
|
||||
|
||||
basic_project = test_projects.new_c_project(
|
||||
setup_py_add=textwrap.dedent('''
|
||||
setup_py_add=textwrap.dedent(
|
||||
'''
|
||||
import os
|
||||
|
||||
if os.environ.get("CIBUILDWHEEL", "0") != "1":
|
||||
raise Exception("CIBUILDWHEEL environment variable is not set to 1")
|
||||
''')
|
||||
'''
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -55,8 +57,11 @@ def test_build_identifiers(tmp_path):
|
||||
# can be multiple wheels for each wheel, though, so we need to limit
|
||||
# the expected wheels
|
||||
if platform.machine() in ['x86_64', 'i686']:
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if '-manylinux' not in w or '-manylinux1' in w]
|
||||
expected_wheels = [
|
||||
w
|
||||
for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if '-manylinux' not in w or '-manylinux1' in w
|
||||
]
|
||||
else:
|
||||
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
||||
build_identifiers = utils.cibuildwheel_get_build_identifiers(project_dir)
|
||||
|
||||
+28
-17
@@ -6,7 +6,8 @@ import pytest
|
||||
from . import test_projects, utils
|
||||
|
||||
project_with_before_build_asserts = test_projects.new_c_project(
|
||||
setup_py_add=textwrap.dedent(r'''
|
||||
setup_py_add=textwrap.dedent(
|
||||
r'''
|
||||
# assert that the Python version as written to text_info.txt in the CIBW_BEFORE_ALL step
|
||||
# is the same one as is currently running.
|
||||
with open("text_info.txt") as f:
|
||||
@@ -14,7 +15,8 @@ project_with_before_build_asserts = test_projects.new_c_project(
|
||||
|
||||
print("## stored text: " + stored_text)
|
||||
assert stored_text == "sample text 123"
|
||||
''')
|
||||
'''
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -27,13 +29,16 @@ def test(tmp_path):
|
||||
|
||||
# build the wheels
|
||||
before_all_command = '''python -c "import os;open('{project}/text_info.txt', 'w').write('sample text '+os.environ.get('TEST_VAL', ''))"'''
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
||||
# write python version information to a temporary file, this is
|
||||
# checked in setup.py
|
||||
'CIBW_BEFORE_ALL': before_all_command,
|
||||
'CIBW_BEFORE_ALL_LINUX': f'{before_all_command} && python -c "import sys; assert sys.version_info >= (3, 6)"',
|
||||
'CIBW_ENVIRONMENT': "TEST_VAL='123'"
|
||||
})
|
||||
actual_wheels = utils.cibuildwheel_run(
|
||||
project_dir,
|
||||
add_env={
|
||||
# write python version information to a temporary file, this is
|
||||
# checked in setup.py
|
||||
'CIBW_BEFORE_ALL': before_all_command,
|
||||
'CIBW_BEFORE_ALL_LINUX': f'{before_all_command} && python -c "import sys; assert sys.version_info >= (3, 6)"',
|
||||
'CIBW_ENVIRONMENT': "TEST_VAL='123'",
|
||||
},
|
||||
)
|
||||
|
||||
# also check that we got the right wheels
|
||||
(project_dir / 'text_info.txt').unlink()
|
||||
@@ -46,20 +51,26 @@ def test_failing_command(tmp_path):
|
||||
test_projects.new_c_project().generate(project_dir)
|
||||
|
||||
with pytest.raises(subprocess.CalledProcessError):
|
||||
utils.cibuildwheel_run(project_dir, add_env={
|
||||
'CIBW_BEFORE_ALL': 'false',
|
||||
'CIBW_BEFORE_ALL_WINDOWS': 'exit /b 1',
|
||||
})
|
||||
utils.cibuildwheel_run(
|
||||
project_dir,
|
||||
add_env={
|
||||
'CIBW_BEFORE_ALL': 'false',
|
||||
'CIBW_BEFORE_ALL_WINDOWS': 'exit /b 1',
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def test_cwd(tmp_path):
|
||||
project_dir = tmp_path / 'project'
|
||||
test_projects.new_c_project().generate(project_dir)
|
||||
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
||||
'CIBW_BEFORE_ALL': f'''python -c "import os; assert os.getcwd() == {str(project_dir)!r}"''',
|
||||
'CIBW_BEFORE_ALL_LINUX': '''python -c "import os; assert os.getcwd() == '/project'"''',
|
||||
})
|
||||
actual_wheels = utils.cibuildwheel_run(
|
||||
project_dir,
|
||||
add_env={
|
||||
'CIBW_BEFORE_ALL': f'''python -c "import os; assert os.getcwd() == {str(project_dir)!r}"''',
|
||||
'CIBW_BEFORE_ALL_LINUX': '''python -c "import os; assert os.getcwd() == '/project'"''',
|
||||
},
|
||||
)
|
||||
|
||||
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
|
||||
+31
-18
@@ -6,7 +6,8 @@ import pytest
|
||||
from . import test_projects, utils
|
||||
|
||||
project_with_before_build_asserts = test_projects.new_c_project(
|
||||
setup_py_add=textwrap.dedent(r'''
|
||||
setup_py_add=textwrap.dedent(
|
||||
r'''
|
||||
import os
|
||||
|
||||
# assert that the Python version as written to pythonversion.txt in the CIBW_BEFORE_BUILD step
|
||||
@@ -26,7 +27,8 @@ project_with_before_build_asserts = test_projects.new_c_project(
|
||||
print('sys.executable', sys.executable)
|
||||
# windows/mac are case insensitive
|
||||
assert os.path.realpath(stored_executable).lower() == os.path.realpath(sys.executable).lower()
|
||||
''')
|
||||
'''
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -34,16 +36,21 @@ def test(tmp_path):
|
||||
project_dir = tmp_path / 'project'
|
||||
project_with_before_build_asserts.generate(project_dir)
|
||||
|
||||
before_build = ('''python -c "import sys; open('{output_dir}pythonversion.txt', 'w').write(sys.version)" && '''
|
||||
'''python -c "import sys; open('{output_dir}pythonexecutable.txt', 'w').write(sys.executable)"''')
|
||||
before_build = (
|
||||
'''python -c "import sys; open('{output_dir}pythonversion.txt', 'w').write(sys.version)" && '''
|
||||
'''python -c "import sys; open('{output_dir}pythonexecutable.txt', 'w').write(sys.executable)"'''
|
||||
)
|
||||
|
||||
# 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': before_build.format(output_dir='/tmp/'),
|
||||
'CIBW_BEFORE_BUILD_WINDOWS': before_build.format(output_dir=r'c:\\'),
|
||||
})
|
||||
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': before_build.format(output_dir='/tmp/'),
|
||||
'CIBW_BEFORE_BUILD_WINDOWS': before_build.format(output_dir=r'c:\\'),
|
||||
},
|
||||
)
|
||||
|
||||
# also check that we got the right wheels
|
||||
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
||||
@@ -55,20 +62,26 @@ def test_failing_command(tmp_path):
|
||||
test_projects.new_c_project().generate(project_dir)
|
||||
|
||||
with pytest.raises(subprocess.CalledProcessError):
|
||||
utils.cibuildwheel_run(project_dir, add_env={
|
||||
'CIBW_BEFORE_BUILD': 'false',
|
||||
'CIBW_BEFORE_BUILD_WINDOWS': 'exit /b 1',
|
||||
})
|
||||
utils.cibuildwheel_run(
|
||||
project_dir,
|
||||
add_env={
|
||||
'CIBW_BEFORE_BUILD': 'false',
|
||||
'CIBW_BEFORE_BUILD_WINDOWS': 'exit /b 1',
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def test_cwd(tmp_path):
|
||||
project_dir = tmp_path / 'project'
|
||||
test_projects.new_c_project().generate(project_dir)
|
||||
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
||||
'CIBW_BEFORE_BUILD': f'''python -c "import os; assert os.getcwd() == {str(project_dir)!r}"''',
|
||||
'CIBW_BEFORE_BUILD_LINUX': '''python -c "import os; assert os.getcwd() == '/project'"''',
|
||||
})
|
||||
actual_wheels = utils.cibuildwheel_run(
|
||||
project_dir,
|
||||
add_env={
|
||||
'CIBW_BEFORE_BUILD': f'''python -c "import os; assert os.getcwd() == {str(project_dir)!r}"''',
|
||||
'CIBW_BEFORE_BUILD_LINUX': '''python -c "import os; assert os.getcwd() == '/project'"''',
|
||||
},
|
||||
)
|
||||
|
||||
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
|
||||
+17
-12
@@ -1,7 +1,9 @@
|
||||
from . import test_projects, utils
|
||||
|
||||
before_test_project = test_projects.new_c_project()
|
||||
before_test_project.files['test/spam_test.py'] = r'''
|
||||
before_test_project.files[
|
||||
'test/spam_test.py'
|
||||
] = r'''
|
||||
import sys
|
||||
import os
|
||||
from unittest import TestCase
|
||||
@@ -40,17 +42,20 @@ def test(tmp_path):
|
||||
test_projects.new_c_project().generate(test_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)" && python -m pip install {project}/dependency''',
|
||||
'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)" && python -m pip install {project}/dependency''',
|
||||
'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',
|
||||
})
|
||||
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)" && python -m pip install {project}/dependency''',
|
||||
'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)" && python -m pip install {project}/dependency''',
|
||||
'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')
|
||||
|
||||
+14
-8
@@ -3,13 +3,15 @@ import textwrap
|
||||
from . import test_projects, utils
|
||||
|
||||
project_with_skip_asserts = test_projects.new_c_project(
|
||||
setup_py_add=textwrap.dedent(r'''
|
||||
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")
|
||||
''')
|
||||
'''
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -18,12 +20,16 @@ def test(tmp_path):
|
||||
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-*',
|
||||
})
|
||||
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)]
|
||||
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)
|
||||
|
||||
+23
-12
@@ -91,8 +91,11 @@ def test_cpp11(tmp_path):
|
||||
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]
|
||||
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)
|
||||
|
||||
@@ -115,9 +118,11 @@ def test_cpp14(tmp_path):
|
||||
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]
|
||||
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)
|
||||
|
||||
@@ -129,7 +134,9 @@ cpp17_project = cpp_test_project.copy()
|
||||
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'''
|
||||
cpp17_project.template_context[
|
||||
'spam_cpp_top_level_add'
|
||||
] = r'''
|
||||
#include <utility>
|
||||
auto a = std::pair(5.0, false);
|
||||
'''
|
||||
@@ -152,9 +159,11 @@ def test_cpp17(tmp_path):
|
||||
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]
|
||||
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)
|
||||
|
||||
@@ -204,8 +213,10 @@ def test_cpp17_py27_modern_msvc_workaround(tmp_path):
|
||||
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', exclude_27=False)
|
||||
if 'cp27-cp27m-win' in w
|
||||
or 'pp27-pypy_73-win32' in w]
|
||||
expected_wheels = [
|
||||
w
|
||||
for w in utils.expected_wheels('spam', '0.1.0', exclude_27=False)
|
||||
if 'cp27-cp27m-win' in w or 'pp27-pypy_73-win32' in w
|
||||
]
|
||||
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
|
||||
@@ -8,7 +8,8 @@ import cibuildwheel.util
|
||||
from . import test_projects, utils
|
||||
|
||||
project_with_expected_version_checks = test_projects.new_c_project(
|
||||
setup_py_add=textwrap.dedent(r'''
|
||||
setup_py_add=textwrap.dedent(
|
||||
r'''
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
@@ -30,7 +31,8 @@ project_with_expected_version_checks = test_projects.new_c_project(
|
||||
assert '{}=={}'.format(package_name, expected_version) in versions, (
|
||||
'error: {} version should equal {}'.format(package_name, expected_version)
|
||||
)
|
||||
''')
|
||||
'''
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -41,8 +43,7 @@ def get_versions_from_constraint_file(constraint_file):
|
||||
constraint_file_text = constraint_file.read_text(encoding='utf8')
|
||||
|
||||
return {
|
||||
package: version
|
||||
for package, version in re.findall(VERSION_REGEX, constraint_file_text)
|
||||
package: version for package, version in re.findall(VERSION_REGEX, constraint_file_text)
|
||||
}
|
||||
|
||||
|
||||
@@ -54,9 +55,7 @@ def test_pinned_versions(tmp_path, python_version):
|
||||
if utils.platform == 'windows' and python_version == '2.7':
|
||||
pytest.skip('Windows requires a workaround')
|
||||
|
||||
is_macos_11_or_later = (
|
||||
utils.platform == 'macos' and utils.get_macos_version() >= (10, 16)
|
||||
)
|
||||
is_macos_11_or_later = utils.platform == 'macos' and utils.get_macos_version() >= (10, 16)
|
||||
|
||||
if is_macos_11_or_later and python_version == '3.5':
|
||||
pytest.skip('CPython 3.5 doesn\'t work on macOS Big Sur+')
|
||||
@@ -89,29 +88,34 @@ def test_pinned_versions(tmp_path, python_version):
|
||||
env_name = f'EXPECTED_{package.upper()}_VERSION'
|
||||
build_environment[env_name] = constraint_versions[package]
|
||||
|
||||
cibw_environment_option = ' '.join(
|
||||
f'{k}={v}' for k, v in build_environment.items()
|
||||
)
|
||||
cibw_environment_option = ' '.join(f'{k}={v}' for k, v in build_environment.items())
|
||||
|
||||
# build and test the wheels
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
||||
'CIBW_BUILD': build_pattern,
|
||||
'CIBW_ENVIRONMENT': cibw_environment_option,
|
||||
})
|
||||
actual_wheels = utils.cibuildwheel_run(
|
||||
project_dir,
|
||||
add_env={
|
||||
'CIBW_BUILD': build_pattern,
|
||||
'CIBW_ENVIRONMENT': cibw_environment_option,
|
||||
},
|
||||
)
|
||||
|
||||
# also check that we got the right wheels
|
||||
if python_version == '2.7':
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if '-cp27' in w or '-pp27' in w]
|
||||
expected_wheels = [
|
||||
w for w in utils.expected_wheels('spam', '0.1.0') if '-cp27' in w or '-pp27' in w
|
||||
]
|
||||
elif python_version == '3.5':
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if '-cp35' in w or '-pp35' in w]
|
||||
expected_wheels = [
|
||||
w for w in utils.expected_wheels('spam', '0.1.0') if '-cp35' in w or '-pp35' in w
|
||||
]
|
||||
elif python_version == '3.6':
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if '-cp36' in w or '-pp36' in w]
|
||||
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':
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if '-cp38' in w or '-pp38' in w]
|
||||
expected_wheels = [
|
||||
w for w in utils.expected_wheels('spam', '0.1.0') if '-cp38' in w or '-pp38' in w
|
||||
]
|
||||
else:
|
||||
raise ValueError('unhandled python version')
|
||||
|
||||
@@ -137,14 +141,18 @@ def test_dependency_constraints_file(tmp_path, python_version):
|
||||
}
|
||||
|
||||
constraints_file = tmp_path / 'constraints.txt'
|
||||
constraints_file.write_text(textwrap.dedent(
|
||||
'''
|
||||
constraints_file.write_text(
|
||||
textwrap.dedent(
|
||||
'''
|
||||
pip=={pip}
|
||||
setuptools=={setuptools}
|
||||
wheel=={wheel}
|
||||
virtualenv=={virtualenv}
|
||||
'''.format(**tool_versions)
|
||||
))
|
||||
'''.format(
|
||||
**tool_versions
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
build_environment = {}
|
||||
|
||||
@@ -152,23 +160,28 @@ def test_dependency_constraints_file(tmp_path, python_version):
|
||||
env_name = f'EXPECTED_{package_name.upper()}_VERSION'
|
||||
build_environment[env_name] = version
|
||||
|
||||
cibw_environment_option = ' '.join(
|
||||
f'{k}={v}' for k, v in build_environment.items()
|
||||
)
|
||||
cibw_environment_option = ' '.join(f'{k}={v}' for k, v in build_environment.items())
|
||||
|
||||
# build and test the wheels
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
||||
'CIBW_BUILD': '[cp]p27-*' if python_version == '2.7' else '[cp]p3?-*',
|
||||
'CIBW_ENVIRONMENT': cibw_environment_option,
|
||||
'CIBW_DEPENDENCY_VERSIONS': str(constraints_file),
|
||||
})
|
||||
actual_wheels = utils.cibuildwheel_run(
|
||||
project_dir,
|
||||
add_env={
|
||||
'CIBW_BUILD': '[cp]p27-*' if python_version == '2.7' else '[cp]p3?-*',
|
||||
'CIBW_ENVIRONMENT': cibw_environment_option,
|
||||
'CIBW_DEPENDENCY_VERSIONS': str(constraints_file),
|
||||
},
|
||||
)
|
||||
|
||||
# also check that we got the right wheels
|
||||
if python_version == '2.7':
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if '-cp27' in w or '-pp27' in w]
|
||||
expected_wheels = [
|
||||
w for w in utils.expected_wheels('spam', '0.1.0') if '-cp27' in w or '-pp27' in w
|
||||
]
|
||||
else:
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if '-cp27' not in w and '-pp27' not in w]
|
||||
expected_wheels = [
|
||||
w
|
||||
for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if '-cp27' not in w and '-pp27' not in w
|
||||
]
|
||||
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
|
||||
+20
-10
@@ -6,7 +6,8 @@ import pytest
|
||||
from . import test_projects, utils
|
||||
|
||||
dockcross_only_project = test_projects.new_c_project(
|
||||
setup_py_add=textwrap.dedent(r'''
|
||||
setup_py_add=textwrap.dedent(
|
||||
r'''
|
||||
import os
|
||||
|
||||
# check that we're running in the correct docker image as specified in the
|
||||
@@ -15,7 +16,8 @@ dockcross_only_project = test_projects.new_c_project(
|
||||
raise Exception(
|
||||
"/dockcross directory not found. Is this test running in the correct docker image?"
|
||||
)
|
||||
''')
|
||||
'''
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -23,18 +25,26 @@ def test(tmp_path):
|
||||
if utils.platform != 'linux':
|
||||
pytest.skip('the test is only relevant to the linux build')
|
||||
if platform.machine() not in ['x86_64', 'i686']:
|
||||
pytest.skip('this test is currently only possible on x86_64/i686 due to availability of alternative images')
|
||||
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={
|
||||
'CIBW_MANYLINUX_X86_64_IMAGE': 'dockcross/manylinux2010-x64',
|
||||
'CIBW_MANYLINUX_I686_IMAGE': 'dockcross/manylinux2010-x86',
|
||||
'CIBW_SKIP': 'pp* cp27-* cp39-*',
|
||||
})
|
||||
actual_wheels = utils.cibuildwheel_run(
|
||||
project_dir,
|
||||
add_env={
|
||||
'CIBW_MANYLINUX_X86_64_IMAGE': 'dockcross/manylinux2010-x64',
|
||||
'CIBW_MANYLINUX_I686_IMAGE': 'dockcross/manylinux2010-x86',
|
||||
'CIBW_SKIP': 'pp* cp27-* cp39-*',
|
||||
},
|
||||
)
|
||||
|
||||
# also check that we got the right wheels built
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if '-pp' not in w and '-cp39-' not in w and '-cp27-' not in w]
|
||||
expected_wheels = [
|
||||
w
|
||||
for w in utils.expected_wheels('spam', '0.1.0')
|
||||
if '-pp' not in w and '-cp39-' not in w and '-cp27-' not in w
|
||||
]
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
|
||||
+17
-9
@@ -6,7 +6,9 @@ from . import test_projects, utils
|
||||
|
||||
project_with_a_test = test_projects.new_c_project()
|
||||
|
||||
project_with_a_test.files['test/spam_test.py'] = r'''
|
||||
project_with_a_test.files[
|
||||
'test/spam_test.py'
|
||||
] = r'''
|
||||
import spam
|
||||
|
||||
def test_spam():
|
||||
@@ -21,11 +23,14 @@ def test(tmp_path):
|
||||
project_with_a_test.generate(project_dir)
|
||||
|
||||
# build and test the wheels
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
||||
'CIBW_TEST_REQUIRES': 'pytest',
|
||||
'CIBW_TEST_COMMAND': 'pytest {project}/test',
|
||||
'CIBW_ARCHS': 'aarch64 ppc64le s390x',
|
||||
})
|
||||
actual_wheels = utils.cibuildwheel_run(
|
||||
project_dir,
|
||||
add_env={
|
||||
'CIBW_TEST_REQUIRES': 'pytest',
|
||||
'CIBW_TEST_COMMAND': 'pytest {project}/test',
|
||||
'CIBW_ARCHS': 'aarch64 ppc64le s390x',
|
||||
},
|
||||
)
|
||||
|
||||
# also check that we got the right wheels
|
||||
expected_wheels = (
|
||||
@@ -45,9 +50,12 @@ def test_setting_arch_on_other_platforms(tmp_path, capfd):
|
||||
|
||||
# build and test the wheels
|
||||
with pytest.raises(subprocess.CalledProcessError):
|
||||
utils.cibuildwheel_run(project_dir, add_env={
|
||||
'CIBW_ARCHS': 'aarch64',
|
||||
})
|
||||
utils.cibuildwheel_run(
|
||||
project_dir,
|
||||
add_env={
|
||||
'CIBW_ARCHS': 'aarch64',
|
||||
},
|
||||
)
|
||||
|
||||
captured = capfd.readouterr()
|
||||
assert "Invalid archs option" in captured.err
|
||||
|
||||
+27
-14
@@ -7,7 +7,8 @@ import pytest
|
||||
from . import test_projects, utils
|
||||
|
||||
project_with_environment_asserts = test_projects.new_c_project(
|
||||
setup_py_add=textwrap.dedent(r'''
|
||||
setup_py_add=textwrap.dedent(
|
||||
r'''
|
||||
import os
|
||||
|
||||
# explode if environment isn't correct, as set in CIBW_ENVIRONMENT
|
||||
@@ -26,7 +27,8 @@ project_with_environment_asserts = test_projects.new_c_project(
|
||||
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)
|
||||
''')
|
||||
'''
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -37,10 +39,13 @@ def test(tmp_path):
|
||||
# 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"''',
|
||||
})
|
||||
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')
|
||||
@@ -58,19 +63,27 @@ def test_overridden_path(tmp_path, capfd):
|
||||
# mess up PATH, somehow
|
||||
with pytest.raises(subprocess.CalledProcessError):
|
||||
if utils.platform == 'linux':
|
||||
utils.cibuildwheel_run(project_dir, output_dir=output_dir, add_env={
|
||||
'CIBW_BEFORE_ALL': 'mkdir new_path && touch new_path/python && chmod +x new_path/python',
|
||||
'CIBW_ENVIRONMENT': '''PATH="$(pwd)/new_path:$PATH"''',
|
||||
})
|
||||
utils.cibuildwheel_run(
|
||||
project_dir,
|
||||
output_dir=output_dir,
|
||||
add_env={
|
||||
'CIBW_BEFORE_ALL': 'mkdir new_path && touch new_path/python && chmod +x new_path/python',
|
||||
'CIBW_ENVIRONMENT': '''PATH="$(pwd)/new_path:$PATH"''',
|
||||
},
|
||||
)
|
||||
else:
|
||||
new_path = tmp_path / 'another_bin'
|
||||
new_path.mkdir()
|
||||
(new_path / 'python').touch(mode=0o777)
|
||||
|
||||
utils.cibuildwheel_run(project_dir, output_dir=output_dir, add_env={
|
||||
'NEW_PATH': str(new_path),
|
||||
'CIBW_ENVIRONMENT': f'''PATH="$NEW_PATH{os.pathsep}$PATH"''',
|
||||
})
|
||||
utils.cibuildwheel_run(
|
||||
project_dir,
|
||||
output_dir=output_dir,
|
||||
add_env={
|
||||
'NEW_PATH': str(new_path),
|
||||
'CIBW_ENVIRONMENT': f'''PATH="$NEW_PATH{os.pathsep}$PATH"''',
|
||||
},
|
||||
)
|
||||
|
||||
assert len(os.listdir(output_dir)) == 0
|
||||
captured = capfd.readouterr()
|
||||
|
||||
+32
-18
@@ -10,7 +10,7 @@ basic_project = test_projects.new_c_project()
|
||||
|
||||
ALL_MACOS_WHEELS = {
|
||||
*utils.expected_wheels('spam', '0.1.0', machine_arch='x86_64'),
|
||||
*utils.expected_wheels('spam', '0.1.0', machine_arch='arm64')
|
||||
*utils.expected_wheels('spam', '0.1.0', machine_arch='arm64'),
|
||||
}
|
||||
|
||||
|
||||
@@ -37,10 +37,13 @@ def test_cross_compiled_build(tmp_path):
|
||||
project_dir = tmp_path / 'project'
|
||||
basic_project.generate(project_dir)
|
||||
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
||||
'CIBW_BUILD': 'cp39-*',
|
||||
'CIBW_ARCHS': 'x86_64, universal2, arm64',
|
||||
})
|
||||
actual_wheels = utils.cibuildwheel_run(
|
||||
project_dir,
|
||||
add_env={
|
||||
'CIBW_BUILD': 'cp39-*',
|
||||
'CIBW_ARCHS': 'x86_64, universal2, arm64',
|
||||
},
|
||||
)
|
||||
|
||||
expected_wheels = [w for w in ALL_MACOS_WHEELS if 'cp39' in w]
|
||||
assert set(actual_wheels) == set(expected_wheels)
|
||||
@@ -56,11 +59,14 @@ def test_cross_compiled_test(tmp_path, capfd, build_universal2):
|
||||
project_dir = tmp_path / 'project'
|
||||
basic_project.generate(project_dir)
|
||||
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
||||
'CIBW_BUILD': 'cp39-*',
|
||||
'CIBW_TEST_COMMAND': '''python -c "import platform; print('running tests on ' + platform.machine())"''',
|
||||
'CIBW_ARCHS': 'universal2' if build_universal2 else 'x86_64 arm64',
|
||||
})
|
||||
actual_wheels = utils.cibuildwheel_run(
|
||||
project_dir,
|
||||
add_env={
|
||||
'CIBW_BUILD': 'cp39-*',
|
||||
'CIBW_TEST_COMMAND': '''python -c "import platform; print('running tests on ' + platform.machine())"''',
|
||||
'CIBW_ARCHS': 'universal2' if build_universal2 else 'x86_64 arm64',
|
||||
},
|
||||
)
|
||||
|
||||
captured = capfd.readouterr()
|
||||
|
||||
@@ -69,9 +75,14 @@ def test_cross_compiled_test(tmp_path, capfd, build_universal2):
|
||||
assert 'running tests on x86_64' in captured.out
|
||||
assert 'running tests on arm64' not in captured.out
|
||||
if build_universal2:
|
||||
assert 'While universal2 wheels can be built on x86_64, the arm64 part of them cannot currently be tested' in captured.err
|
||||
assert (
|
||||
'While universal2 wheels can be built on x86_64, the arm64 part of them cannot currently be tested'
|
||||
in captured.err
|
||||
)
|
||||
else:
|
||||
assert 'While arm64 wheels can be built on x86_64, they cannot be tested' in captured.err
|
||||
assert (
|
||||
'While arm64 wheels can be built on x86_64, they cannot be tested' in captured.err
|
||||
)
|
||||
elif platform.machine() == 'arm64':
|
||||
# ensure that tests were run on both x86_64 and arm64
|
||||
assert 'running tests on x86_64' in captured.out
|
||||
@@ -97,12 +108,15 @@ def test_universal2_testing(tmp_path, capfd, skip_arm64_test):
|
||||
project_dir = tmp_path / 'project'
|
||||
basic_project.generate(project_dir)
|
||||
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env={
|
||||
'CIBW_BUILD': 'cp39-*',
|
||||
'CIBW_TEST_COMMAND': '''python -c "import platform; print('running tests on ' + platform.machine())"''',
|
||||
'CIBW_ARCHS': 'universal2',
|
||||
'CIBW_TEST_SKIP': '*_universal2:arm64' if skip_arm64_test else '',
|
||||
})
|
||||
actual_wheels = utils.cibuildwheel_run(
|
||||
project_dir,
|
||||
add_env={
|
||||
'CIBW_BUILD': 'cp39-*',
|
||||
'CIBW_TEST_COMMAND': '''python -c "import platform; print('running tests on ' + platform.machine())"''',
|
||||
'CIBW_ARCHS': 'universal2',
|
||||
'CIBW_TEST_SKIP': '*_universal2:arm64' if skip_arm64_test else '',
|
||||
},
|
||||
)
|
||||
|
||||
captured = capfd.readouterr()
|
||||
|
||||
|
||||
@@ -7,7 +7,8 @@ from . import test_projects, utils
|
||||
|
||||
# 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'''
|
||||
spam_c_top_level_add=textwrap.dedent(
|
||||
r'''
|
||||
#include <malloc.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
@@ -20,8 +21,10 @@ project_with_manylinux_symbols = test_projects.new_c_project(
|
||||
#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'''
|
||||
'''
|
||||
),
|
||||
spam_c_function_add=textwrap.dedent(
|
||||
r'''
|
||||
#if defined(__GLIBC_PREREQ) && __GLIBC_PREREQ(2, 24)
|
||||
// nextupf is only available in manylinux_2_24+
|
||||
sts = (int)nextupf(0.0F);
|
||||
@@ -32,11 +35,14 @@ project_with_manylinux_symbols = test_projects.new_c_project(
|
||||
// malloc_info is only available on manylinux2010+
|
||||
sts = malloc_info(0, stdout);
|
||||
#endif
|
||||
'''),
|
||||
'''
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('manylinux_image', ['manylinux1', 'manylinux2010', 'manylinux2014', 'manylinux_2_24'])
|
||||
@pytest.mark.parametrize(
|
||||
'manylinux_image', ['manylinux1', 'manylinux2010', 'manylinux2014', 'manylinux_2_24']
|
||||
)
|
||||
def test(manylinux_image, tmp_path):
|
||||
if utils.platform != 'linux':
|
||||
pytest.skip('the docker test is only relevant to the linux build')
|
||||
@@ -68,7 +74,9 @@ def test(manylinux_image, tmp_path):
|
||||
add_env['CIBW_SKIP'] = 'cp27* pp*'
|
||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
|
||||
|
||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0', manylinux_versions=[manylinux_image])]
|
||||
expected_wheels = [
|
||||
w for w in utils.expected_wheels('spam', '0.1.0', manylinux_versions=[manylinux_image])
|
||||
]
|
||||
if manylinux_image in {'manylinux2014', 'manylinux_2_24'}:
|
||||
expected_wheels = [w for w in expected_wheels if '-cp27' not in w]
|
||||
if manylinux_image in {'manylinux1', 'manylinux2014', 'manylinux_2_24'}:
|
||||
|
||||
@@ -8,18 +8,28 @@ from pathlib import Path
|
||||
|
||||
def main():
|
||||
parser = ArgumentParser(
|
||||
prog="python -m test.test_projects",
|
||||
description='Generate a test project to check it out'
|
||||
prog="python -m test.test_projects", description='Generate a test project to check it out'
|
||||
)
|
||||
parser.add_argument('--open', action='store_true', help='''
|
||||
parser.add_argument(
|
||||
'--open',
|
||||
action='store_true',
|
||||
help='''
|
||||
Open the generated project in a file explorer
|
||||
''')
|
||||
parser.add_argument('PROJECT', help='''
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'PROJECT',
|
||||
help='''
|
||||
Python path to a project object. E.g. test.test_0_basic.basic_project
|
||||
''')
|
||||
parser.add_argument('OUTPUT', nargs='?', help='''
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'OUTPUT',
|
||||
nargs='?',
|
||||
help='''
|
||||
Path to output dir. If no dir is passed, a tempdir will be generated.
|
||||
''')
|
||||
''',
|
||||
)
|
||||
options = parser.parse_args()
|
||||
|
||||
module, _, name = options.PROJECT.rpartition('.')
|
||||
|
||||
@@ -14,6 +14,7 @@ class TestProject:
|
||||
|
||||
Write out to the filesystem using `generate`.
|
||||
'''
|
||||
|
||||
__test__ = False # Have pytest ignore this class on `from .test_projects import TestProject`
|
||||
|
||||
files: FilesDict
|
||||
|
||||
+24
-14
@@ -88,22 +88,32 @@ version = 0.1.0
|
||||
'''
|
||||
|
||||
|
||||
def new_c_project(*, spam_c_top_level_add='', spam_c_function_add='', setup_py_add='',
|
||||
setup_py_setup_args_add='', 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.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,
|
||||
})
|
||||
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
|
||||
|
||||
@@ -6,7 +6,9 @@ import pytest
|
||||
from . import utils
|
||||
|
||||
pure_python_project = test_projects.TestProject()
|
||||
pure_python_project.files['setup.py'] = '''
|
||||
pure_python_project.files[
|
||||
'setup.py'
|
||||
] = '''
|
||||
from setuptools import Extension, setup
|
||||
|
||||
setup(
|
||||
@@ -16,7 +18,9 @@ setup(
|
||||
)
|
||||
'''
|
||||
|
||||
pure_python_project.files['spam.py'] = '''
|
||||
pure_python_project.files[
|
||||
'spam.py'
|
||||
] = '''
|
||||
def a_function():
|
||||
pass
|
||||
'''
|
||||
|
||||
+4
-2
@@ -3,7 +3,8 @@ import textwrap
|
||||
from . import test_projects, utils
|
||||
|
||||
project_with_ssl_tests = test_projects.new_c_project(
|
||||
setup_py_add=textwrap.dedent(r'''
|
||||
setup_py_add=textwrap.dedent(
|
||||
r'''
|
||||
import ssl
|
||||
|
||||
if sys.version_info[0] == 2:
|
||||
@@ -15,7 +16,8 @@ project_with_ssl_tests = test_projects.new_c_project(
|
||||
data = urlopen("https://www.nist.gov", context=context)
|
||||
data = urlopen("https://raw.githubusercontent.com/joerick/cibuildwheel/master/CI.md", context=context)
|
||||
data = urlopen("https://raw.githubusercontent.com/joerick/cibuildwheel/master/CI.md")
|
||||
''')
|
||||
'''
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
||||
+20
-11
@@ -12,7 +12,9 @@ subdir_package_project.files['src/spam/spam.c'] = jinja2.Template(SPAM_C_TEMPLAT
|
||||
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'''
|
||||
subdir_package_project.files[
|
||||
'src/spam/setup.py'
|
||||
] = r'''
|
||||
from setuptools import Extension, setup
|
||||
|
||||
setup(
|
||||
@@ -22,11 +24,15 @@ setup(
|
||||
)
|
||||
'''
|
||||
|
||||
subdir_package_project.files['src/spam/test/run_tests.py'] = r'''
|
||||
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'''
|
||||
subdir_package_project.files[
|
||||
'bin/before_build.py'
|
||||
] = r'''
|
||||
print('before_build.py executed!')
|
||||
'''
|
||||
|
||||
@@ -37,16 +43,19 @@ def test(capfd, tmp_path):
|
||||
|
||||
package_dir = Path('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-*',
|
||||
})
|
||||
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]
|
||||
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()
|
||||
|
||||
+42
-26
@@ -7,13 +7,17 @@ import pytest
|
||||
from . import test_projects, utils
|
||||
|
||||
project_with_a_test = test_projects.new_c_project(
|
||||
setup_cfg_add=textwrap.dedent(r'''
|
||||
setup_cfg_add=textwrap.dedent(
|
||||
r'''
|
||||
[options.extras_require]
|
||||
test = nose
|
||||
''')
|
||||
'''
|
||||
)
|
||||
)
|
||||
|
||||
project_with_a_test.files['test/spam_test.py'] = r'''
|
||||
project_with_a_test.files[
|
||||
'test/spam_test.py'
|
||||
] = r'''
|
||||
import os
|
||||
import platform
|
||||
import sys
|
||||
@@ -75,13 +79,16 @@ def test(tmp_path):
|
||||
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',
|
||||
})
|
||||
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')
|
||||
@@ -93,13 +100,16 @@ def test_extras_require(tmp_path):
|
||||
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',
|
||||
})
|
||||
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')
|
||||
@@ -107,7 +117,9 @@ def test_extras_require(tmp_path):
|
||||
|
||||
|
||||
project_with_a_failing_test = test_projects.new_c_project()
|
||||
project_with_a_failing_test.files['test/spam_test.py'] = r'''
|
||||
project_with_a_failing_test.files[
|
||||
'test/spam_test.py'
|
||||
] = r'''
|
||||
from unittest import TestCase
|
||||
|
||||
class TestSpam(TestCase):
|
||||
@@ -123,13 +135,17 @@ def test_failing_test(tmp_path):
|
||||
project_with_a_failing_test.generate(project_dir)
|
||||
|
||||
with pytest.raises(subprocess.CalledProcessError):
|
||||
utils.cibuildwheel_run(project_dir, output_dir=output_dir, add_env={
|
||||
'CIBW_TEST_REQUIRES': 'nose',
|
||||
'CIBW_TEST_COMMAND': 'nosetests {project}/test',
|
||||
# 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',
|
||||
})
|
||||
utils.cibuildwheel_run(
|
||||
project_dir,
|
||||
output_dir=output_dir,
|
||||
add_env={
|
||||
'CIBW_TEST_REQUIRES': 'nose',
|
||||
'CIBW_TEST_COMMAND': 'nosetests {project}/test',
|
||||
# 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
|
||||
|
||||
@@ -9,7 +9,9 @@ so_file_project = TestProject()
|
||||
|
||||
so_file_project.files['libnothing.so'] = ''
|
||||
|
||||
so_file_project.files['setup.py'] = '''
|
||||
so_file_project.files[
|
||||
'setup.py'
|
||||
] = '''
|
||||
raise Exception('this build will fail')
|
||||
'''
|
||||
|
||||
|
||||
@@ -5,11 +5,13 @@ import pytest
|
||||
from . import test_projects, utils
|
||||
|
||||
project_with_unicode = test_projects.new_c_project(
|
||||
spam_c_function_add=textwrap.dedent(r'''
|
||||
spam_c_function_add=textwrap.dedent(
|
||||
r'''
|
||||
{
|
||||
Py_XDECREF(PyUnicode_FromStringAndSize("foo", 4));
|
||||
}
|
||||
'''),
|
||||
'''
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -21,9 +23,9 @@ def test(tmp_path):
|
||||
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"'
|
||||
})
|
||||
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')
|
||||
|
||||
+20
-5
@@ -78,7 +78,14 @@ def cibuildwheel_run(project_path, package_dir='.', env=None, add_env=None, outp
|
||||
|
||||
with TemporaryDirectoryIfNone(output_dir) as _output_dir:
|
||||
subprocess.run(
|
||||
[sys.executable, '-m', 'cibuildwheel', '--output-dir', str(_output_dir), str(package_dir)],
|
||||
[
|
||||
sys.executable,
|
||||
'-m',
|
||||
'cibuildwheel',
|
||||
'--output-dir',
|
||||
str(_output_dir),
|
||||
str(package_dir),
|
||||
],
|
||||
env=env,
|
||||
cwd=project_path,
|
||||
check=True,
|
||||
@@ -100,9 +107,15 @@ def _get_arm64_macosx_deployment_target(macosx_deployment_target: str) -> str:
|
||||
return macosx_deployment_target
|
||||
|
||||
|
||||
def expected_wheels(package_name, package_version, manylinux_versions=None,
|
||||
macosx_deployment_target='10.9', machine_arch=None, *,
|
||||
exclude_27=platform == 'windows'):
|
||||
def expected_wheels(
|
||||
package_name,
|
||||
package_version,
|
||||
manylinux_versions=None,
|
||||
macosx_deployment_target='10.9',
|
||||
machine_arch=None,
|
||||
*,
|
||||
exclude_27=platform == 'windows',
|
||||
):
|
||||
'''
|
||||
Returns a list of expected wheels from a run of cibuildwheel.
|
||||
'''
|
||||
@@ -164,7 +177,9 @@ def expected_wheels(package_name, package_version, manylinux_versions=None,
|
||||
|
||||
elif platform == 'macos':
|
||||
if python_abi_tag == 'cp39-cp39' and machine_arch == 'arm64':
|
||||
arm64_macosx_deployment_target = _get_arm64_macosx_deployment_target(macosx_deployment_target)
|
||||
arm64_macosx_deployment_target = _get_arm64_macosx_deployment_target(
|
||||
macosx_deployment_target
|
||||
)
|
||||
platform_tags = [
|
||||
f'macosx_{macosx_deployment_target.replace(".", "_")}_universal2',
|
||||
f'macosx_{arm64_macosx_deployment_target.replace(".", "_")}_arm64',
|
||||
|
||||
@@ -24,7 +24,9 @@ def test_build():
|
||||
|
||||
|
||||
def test_skip():
|
||||
build_selector = BuildSelector(build_config="*", skip_config="cp27-* cp3?-manylinux1_i686 cp36-win* *-win32")
|
||||
build_selector = BuildSelector(
|
||||
build_config="*", skip_config="cp27-* cp3?-manylinux1_i686 cp36-win* *-win32"
|
||||
)
|
||||
|
||||
assert not build_selector('cp27-manylinux1_x86_64')
|
||||
assert build_selector('cp36-manylinux1_x86_64')
|
||||
@@ -44,7 +46,9 @@ def test_skip():
|
||||
|
||||
|
||||
def test_build_and_skip():
|
||||
build_selector = BuildSelector(build_config="cp36-* cp37-macosx* *-manylinux1*", skip_config="cp27-* cp37-manylinux1_i686")
|
||||
build_selector = BuildSelector(
|
||||
build_config="cp36-* cp37-macosx* *-manylinux1*", skip_config="cp27-* cp37-manylinux1_i686"
|
||||
)
|
||||
|
||||
assert not build_selector('cp27-manylinux1_x86_64')
|
||||
assert build_selector('cp36-manylinux1_x86_64')
|
||||
@@ -74,7 +78,9 @@ def test_build_braces():
|
||||
|
||||
|
||||
def test_build_limited_python():
|
||||
build_selector = BuildSelector(build_config="*", skip_config="", requires_python=SpecifierSet(">=3.6"))
|
||||
build_selector = BuildSelector(
|
||||
build_config="*", skip_config="", requires_python=SpecifierSet(">=3.6")
|
||||
)
|
||||
|
||||
assert not build_selector('cp27-manylinux1_x86_64')
|
||||
assert build_selector('cp36-manylinux1_x86_64')
|
||||
@@ -91,7 +97,11 @@ def test_build_limited_python():
|
||||
|
||||
|
||||
def test_build_limited_python_partial():
|
||||
build_selector = BuildSelector(build_config="*", skip_config="", requires_python=SpecifierSet(">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"))
|
||||
build_selector = BuildSelector(
|
||||
build_config="*",
|
||||
skip_config="",
|
||||
requires_python=SpecifierSet(">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"),
|
||||
)
|
||||
|
||||
assert build_selector('cp27-manylinux1_x86_64')
|
||||
assert not build_selector('cp35-manylinux1_x86_64')
|
||||
@@ -99,7 +109,9 @@ def test_build_limited_python_partial():
|
||||
|
||||
|
||||
def test_build_limited_python_patch():
|
||||
build_selector = BuildSelector(build_config="*", skip_config="", requires_python=SpecifierSet(">=2.7.9"))
|
||||
build_selector = BuildSelector(
|
||||
build_config="*", skip_config="", requires_python=SpecifierSet(">=2.7.9")
|
||||
)
|
||||
|
||||
assert build_selector('cp27-manylinux1_x86_64')
|
||||
assert build_selector('cp36-manylinux1_x86_64')
|
||||
|
||||
@@ -2,9 +2,7 @@ import pytest
|
||||
|
||||
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption(
|
||||
"--run-docker", action="store_true", default=False, help="run docker tests"
|
||||
)
|
||||
parser.addoption("--run-docker", action="store_true", default=False, help="run docker tests")
|
||||
|
||||
|
||||
def pytest_configure(config):
|
||||
|
||||
@@ -10,8 +10,18 @@ def test_defaults():
|
||||
resources_dir = project_root / 'cibuildwheel' / 'resources'
|
||||
|
||||
assert dependency_constraints.base_file_path.samefile(resources_dir / 'constraints.txt')
|
||||
assert dependency_constraints.get_for_python_version('3.99').samefile(resources_dir / 'constraints.txt')
|
||||
assert dependency_constraints.get_for_python_version('3.9').samefile(resources_dir / 'constraints-python39.txt')
|
||||
assert dependency_constraints.get_for_python_version('3.6').samefile(resources_dir / 'constraints-python36.txt')
|
||||
assert dependency_constraints.get_for_python_version('3.5').samefile(resources_dir / 'constraints-python35.txt')
|
||||
assert dependency_constraints.get_for_python_version('2.7').samefile(resources_dir / 'constraints-python27.txt')
|
||||
assert dependency_constraints.get_for_python_version('3.99').samefile(
|
||||
resources_dir / 'constraints.txt'
|
||||
)
|
||||
assert dependency_constraints.get_for_python_version('3.9').samefile(
|
||||
resources_dir / 'constraints-python39.txt'
|
||||
)
|
||||
assert dependency_constraints.get_for_python_version('3.6').samefile(
|
||||
resources_dir / 'constraints-python36.txt'
|
||||
)
|
||||
assert dependency_constraints.get_for_python_version('3.5').samefile(
|
||||
resources_dir / 'constraints-python35.txt'
|
||||
)
|
||||
assert dependency_constraints.get_for_python_version('2.7').samefile(
|
||||
resources_dir / 'constraints-python27.txt'
|
||||
)
|
||||
|
||||
@@ -38,7 +38,12 @@ def test_no_lf():
|
||||
@pytest.mark.docker
|
||||
def test_environment():
|
||||
with DockerContainer(DEFAULT_IMAGE) as container:
|
||||
assert container.call(['sh', '-c', 'echo $TEST_VAR'], env={'TEST_VAR': '1'}, capture_output=True) == '1\n'
|
||||
assert (
|
||||
container.call(
|
||||
['sh', '-c', 'echo $TEST_VAR'], env={'TEST_VAR': '1'}, capture_output=True
|
||||
)
|
||||
== '1\n'
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.docker
|
||||
@@ -51,28 +56,43 @@ def test_cwd():
|
||||
@pytest.mark.docker
|
||||
def test_container_removed():
|
||||
with DockerContainer(DEFAULT_IMAGE) as container:
|
||||
docker_containers_listing = subprocess.run('docker container ls', shell=True, check=True, stdout=subprocess.PIPE, universal_newlines=True).stdout
|
||||
docker_containers_listing = subprocess.run(
|
||||
'docker container ls',
|
||||
shell=True,
|
||||
check=True,
|
||||
stdout=subprocess.PIPE,
|
||||
universal_newlines=True,
|
||||
).stdout
|
||||
assert container.name in docker_containers_listing
|
||||
old_container_name = container.name
|
||||
|
||||
docker_containers_listing = subprocess.run('docker container ls', shell=True, check=True, stdout=subprocess.PIPE, universal_newlines=True).stdout
|
||||
docker_containers_listing = subprocess.run(
|
||||
'docker container ls',
|
||||
shell=True,
|
||||
check=True,
|
||||
stdout=subprocess.PIPE,
|
||||
universal_newlines=True,
|
||||
).stdout
|
||||
assert old_container_name not in docker_containers_listing
|
||||
|
||||
|
||||
@pytest.mark.docker
|
||||
def test_large_environment():
|
||||
# max environment variable size is 128kB
|
||||
long_env_var_length = 127*1024
|
||||
long_env_var_length = 127 * 1024
|
||||
large_environment = {
|
||||
'a': '0'*long_env_var_length,
|
||||
'b': '0'*long_env_var_length,
|
||||
'c': '0'*long_env_var_length,
|
||||
'd': '0'*long_env_var_length,
|
||||
'a': '0' * long_env_var_length,
|
||||
'b': '0' * long_env_var_length,
|
||||
'c': '0' * long_env_var_length,
|
||||
'd': '0' * long_env_var_length,
|
||||
}
|
||||
|
||||
with DockerContainer(DEFAULT_IMAGE) as container:
|
||||
# check the length of d
|
||||
assert container.call(['sh', '-c', 'echo ${#d}'], env=large_environment, capture_output=True) == f'{long_env_var_length}\n'
|
||||
assert (
|
||||
container.call(['sh', '-c', 'echo ${#d}'], env=large_environment, capture_output=True)
|
||||
== f'{long_env_var_length}\n'
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.docker
|
||||
@@ -81,16 +101,33 @@ def test_binary_output():
|
||||
# note: the below embedded snippets are in python2
|
||||
|
||||
# check that we can pass though arbitrary binary data without erroring
|
||||
container.call(['/usr/bin/python2', '-c', textwrap.dedent('''
|
||||
container.call(
|
||||
[
|
||||
'/usr/bin/python2',
|
||||
'-c',
|
||||
textwrap.dedent(
|
||||
'''
|
||||
import sys
|
||||
sys.stdout.write(''.join(chr(n) for n in range(0, 256)))
|
||||
''')])
|
||||
'''
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
# check that we can capture arbitrary binary data
|
||||
output = container.call(['/usr/bin/python2', '-c', textwrap.dedent('''
|
||||
output = container.call(
|
||||
[
|
||||
'/usr/bin/python2',
|
||||
'-c',
|
||||
textwrap.dedent(
|
||||
'''
|
||||
import sys
|
||||
sys.stdout.write(''.join(chr(n % 256) for n in range(0, 512)))
|
||||
''')], capture_output=True)
|
||||
'''
|
||||
),
|
||||
],
|
||||
capture_output=True,
|
||||
)
|
||||
|
||||
data = bytes(output, encoding='utf8', errors='surrogateescape')
|
||||
|
||||
|
||||
@@ -6,9 +6,7 @@ from cibuildwheel.environment import parse_environment
|
||||
def test_basic_parsing():
|
||||
environment_recipe = parse_environment('VAR=1 VBR=2')
|
||||
|
||||
environment_dict = environment_recipe.as_dictionary(
|
||||
prev_environment={}
|
||||
)
|
||||
environment_dict = environment_recipe.as_dictionary(prev_environment={})
|
||||
environment_cmds = environment_recipe.as_shell_commands()
|
||||
|
||||
assert environment_dict == {'VAR': '1', 'VBR': '2'}
|
||||
@@ -18,9 +16,7 @@ def test_basic_parsing():
|
||||
def test_quotes():
|
||||
environment_recipe = parse_environment('A=1 VAR="1 NOT_A_VAR=2" VBR=\'vbr\'')
|
||||
|
||||
environment_dict = environment_recipe.as_dictionary(
|
||||
prev_environment={}
|
||||
)
|
||||
environment_dict = environment_recipe.as_dictionary(prev_environment={})
|
||||
environment_cmds = environment_recipe.as_shell_commands()
|
||||
|
||||
assert environment_dict == {'A': '1', 'VAR': '1 NOT_A_VAR=2', 'VBR': 'vbr'}
|
||||
@@ -30,9 +26,7 @@ def test_quotes():
|
||||
def test_inheritance():
|
||||
environment_recipe = parse_environment('PATH=$PATH:/usr/local/bin')
|
||||
|
||||
environment_dict = environment_recipe.as_dictionary(
|
||||
prev_environment={'PATH': '/usr/bin'}
|
||||
)
|
||||
environment_dict = environment_recipe.as_dictionary(prev_environment={'PATH': '/usr/bin'})
|
||||
environment_cmds = environment_recipe.as_shell_commands()
|
||||
|
||||
assert environment_dict == {'PATH': '/usr/bin:/usr/local/bin'}
|
||||
@@ -45,9 +39,7 @@ def test_shell_eval():
|
||||
env_copy = os.environ.copy()
|
||||
env_copy.pop('VAR', None)
|
||||
|
||||
environment_dict = environment_recipe.as_dictionary(
|
||||
prev_environment=env_copy
|
||||
)
|
||||
environment_dict = environment_recipe.as_dictionary(prev_environment=env_copy)
|
||||
environment_cmds = environment_recipe.as_shell_commands()
|
||||
|
||||
assert environment_dict['VAR'] == 'a test string'
|
||||
@@ -57,9 +49,7 @@ def test_shell_eval():
|
||||
def test_shell_eval_and_env():
|
||||
environment_recipe = parse_environment('VAR="$(echo "$PREV_VAR" string)"')
|
||||
|
||||
environment_dict = environment_recipe.as_dictionary(
|
||||
prev_environment={'PREV_VAR': '1 2 3'}
|
||||
)
|
||||
environment_dict = environment_recipe.as_dictionary(prev_environment={'PREV_VAR': '1 2 3'})
|
||||
environment_cmds = environment_recipe.as_shell_commands()
|
||||
|
||||
assert environment_dict == {'PREV_VAR': '1 2 3', 'VAR': '1 2 3 string'}
|
||||
@@ -69,9 +59,7 @@ def test_shell_eval_and_env():
|
||||
def test_empty_var():
|
||||
environment_recipe = parse_environment('CFLAGS=')
|
||||
|
||||
environment_dict = environment_recipe.as_dictionary(
|
||||
prev_environment={'CFLAGS': '-Wall'}
|
||||
)
|
||||
environment_dict = environment_recipe.as_dictionary(prev_environment={'CFLAGS': '-Wall'})
|
||||
environment_cmds = environment_recipe.as_shell_commands()
|
||||
|
||||
assert environment_dict == {'CFLAGS': ''}
|
||||
@@ -91,7 +79,9 @@ def test_no_vars():
|
||||
def test_no_vars_pass_through():
|
||||
environment_recipe = parse_environment('')
|
||||
|
||||
environment_dict = environment_recipe.as_dictionary(prev_environment={'CIBUILDWHEEL': 'awesome'})
|
||||
environment_dict = environment_recipe.as_dictionary(
|
||||
prev_environment={'CIBUILDWHEEL': 'awesome'}
|
||||
)
|
||||
|
||||
assert environment_dict == {'CIBUILDWHEEL': 'awesome'}
|
||||
|
||||
@@ -109,15 +99,15 @@ def test_substitution_with_backslash():
|
||||
environment_recipe = parse_environment('PATH2="somewhere_else;$PATH1"')
|
||||
|
||||
# pass the existing process env so PATH is available
|
||||
environment_dict = environment_recipe.as_dictionary(prev_environment={
|
||||
'PATH1': 'c:\\folder\\'
|
||||
})
|
||||
environment_dict = environment_recipe.as_dictionary(prev_environment={'PATH1': 'c:\\folder\\'})
|
||||
|
||||
assert environment_dict.get('PATH2') == 'somewhere_else;c:\\folder\\'
|
||||
|
||||
|
||||
def test_awkwardly_quoted_variable():
|
||||
environment_recipe = parse_environment('VAR2=something"like this""$VAR1"$VAR1$(echo "theres more")"$(echo "and more!")"')
|
||||
environment_recipe = parse_environment(
|
||||
'VAR2=something"like this""$VAR1"$VAR1$(echo "theres more")"$(echo "and more!")"'
|
||||
)
|
||||
|
||||
# pass the existing process env so PATH is available
|
||||
environment_dict = environment_recipe.as_dictionary({'VAR1': 'but wait'})
|
||||
|
||||
@@ -66,36 +66,38 @@ def test_empty_selector(platform, intercepted_build_args, monkeypatch):
|
||||
assert e.value.code == 3
|
||||
|
||||
|
||||
@pytest.mark.parametrize('architecture, image, full_image', [
|
||||
('x86_64', None, 'quay.io/pypa/manylinux2010_x86_64:*'),
|
||||
('x86_64', 'manylinux1', 'quay.io/pypa/manylinux1_x86_64:*'),
|
||||
('x86_64', 'manylinux2010', 'quay.io/pypa/manylinux2010_x86_64:*'),
|
||||
('x86_64', 'manylinux2014', 'quay.io/pypa/manylinux2014_x86_64:*'),
|
||||
('x86_64', 'manylinux_2_24', 'quay.io/pypa/manylinux_2_24_x86_64:*'),
|
||||
('x86_64', 'custom_image', 'custom_image'),
|
||||
('i686', None, 'quay.io/pypa/manylinux2010_i686:*'),
|
||||
('i686', 'manylinux1', 'quay.io/pypa/manylinux1_i686:*'),
|
||||
('i686', 'manylinux2010', 'quay.io/pypa/manylinux2010_i686:*'),
|
||||
('i686', 'manylinux2014', 'quay.io/pypa/manylinux2014_i686:*'),
|
||||
('i686', 'manylinux_2_24', 'quay.io/pypa/manylinux_2_24_i686:*'),
|
||||
('i686', 'custom_image', 'custom_image'),
|
||||
('pypy_x86_64', None, 'pypywheels/manylinux2010-pypy_x86_64:*'),
|
||||
('pypy_x86_64', 'manylinux1', 'manylinux1'), # Does not exist
|
||||
('pypy_x86_64', 'manylinux2010', 'pypywheels/manylinux2010-pypy_x86_64:*'),
|
||||
('pypy_x86_64', 'manylinux2014', 'manylinux2014'), # Does not exist (yet)
|
||||
('pypy_x86_64', 'custom_image', 'custom_image'),
|
||||
])
|
||||
def test_manylinux_images(architecture, image, full_image, platform, intercepted_build_args, monkeypatch):
|
||||
@pytest.mark.parametrize(
|
||||
'architecture, image, full_image',
|
||||
[
|
||||
('x86_64', None, 'quay.io/pypa/manylinux2010_x86_64:*'),
|
||||
('x86_64', 'manylinux1', 'quay.io/pypa/manylinux1_x86_64:*'),
|
||||
('x86_64', 'manylinux2010', 'quay.io/pypa/manylinux2010_x86_64:*'),
|
||||
('x86_64', 'manylinux2014', 'quay.io/pypa/manylinux2014_x86_64:*'),
|
||||
('x86_64', 'manylinux_2_24', 'quay.io/pypa/manylinux_2_24_x86_64:*'),
|
||||
('x86_64', 'custom_image', 'custom_image'),
|
||||
('i686', None, 'quay.io/pypa/manylinux2010_i686:*'),
|
||||
('i686', 'manylinux1', 'quay.io/pypa/manylinux1_i686:*'),
|
||||
('i686', 'manylinux2010', 'quay.io/pypa/manylinux2010_i686:*'),
|
||||
('i686', 'manylinux2014', 'quay.io/pypa/manylinux2014_i686:*'),
|
||||
('i686', 'manylinux_2_24', 'quay.io/pypa/manylinux_2_24_i686:*'),
|
||||
('i686', 'custom_image', 'custom_image'),
|
||||
('pypy_x86_64', None, 'pypywheels/manylinux2010-pypy_x86_64:*'),
|
||||
('pypy_x86_64', 'manylinux1', 'manylinux1'), # Does not exist
|
||||
('pypy_x86_64', 'manylinux2010', 'pypywheels/manylinux2010-pypy_x86_64:*'),
|
||||
('pypy_x86_64', 'manylinux2014', 'manylinux2014'), # Does not exist (yet)
|
||||
('pypy_x86_64', 'custom_image', 'custom_image'),
|
||||
],
|
||||
)
|
||||
def test_manylinux_images(
|
||||
architecture, image, full_image, platform, intercepted_build_args, monkeypatch
|
||||
):
|
||||
if image is not None:
|
||||
monkeypatch.setenv('CIBW_MANYLINUX_' + architecture.upper() + '_IMAGE', image)
|
||||
|
||||
main()
|
||||
|
||||
if platform == 'linux':
|
||||
assert fnmatch(
|
||||
intercepted_build_args.args[0].manylinux_images[architecture],
|
||||
full_image
|
||||
)
|
||||
assert fnmatch(intercepted_build_args.args[0].manylinux_images[architecture], full_image)
|
||||
else:
|
||||
assert intercepted_build_args.args[0].manylinux_images is None
|
||||
|
||||
@@ -113,7 +115,9 @@ def get_default_repair_command(platform):
|
||||
|
||||
@pytest.mark.parametrize('repair_command', [None, 'repair', 'repair -w {dest_dir} {wheel}'])
|
||||
@pytest.mark.parametrize('platform_specific', [False, True])
|
||||
def test_repair_command(repair_command, platform_specific, platform, intercepted_build_args, monkeypatch):
|
||||
def test_repair_command(
|
||||
repair_command, platform_specific, platform, intercepted_build_args, monkeypatch
|
||||
):
|
||||
if repair_command is not None:
|
||||
if platform_specific:
|
||||
monkeypatch.setenv('CIBW_REPAIR_WHEEL_COMMAND_' + platform.upper(), repair_command)
|
||||
@@ -127,11 +131,10 @@ def test_repair_command(repair_command, platform_specific, platform, intercepted
|
||||
assert intercepted_build_args.args[0].repair_command == expected_repair
|
||||
|
||||
|
||||
@pytest.mark.parametrize('environment', [
|
||||
{},
|
||||
{'something': 'value'},
|
||||
{'something': 'value', 'something_else': 'other_value'}
|
||||
])
|
||||
@pytest.mark.parametrize(
|
||||
'environment',
|
||||
[{}, {'something': 'value'}, {'something': 'value', 'something_else': 'other_value'}],
|
||||
)
|
||||
@pytest.mark.parametrize('platform_specific', [False, True])
|
||||
def test_environment(environment, platform_specific, platform, intercepted_build_args, monkeypatch):
|
||||
env_string = ' '.join(f'{k}={v}' for k, v in environment.items())
|
||||
@@ -150,7 +153,9 @@ def test_environment(environment, platform_specific, platform, intercepted_build
|
||||
|
||||
@pytest.mark.parametrize('test_requires', [None, 'requirement other_requirement'])
|
||||
@pytest.mark.parametrize('platform_specific', [False, True])
|
||||
def test_test_requires(test_requires, platform_specific, platform, intercepted_build_args, monkeypatch):
|
||||
def test_test_requires(
|
||||
test_requires, platform_specific, platform, intercepted_build_args, monkeypatch
|
||||
):
|
||||
if test_requires is not None:
|
||||
if platform_specific:
|
||||
monkeypatch.setenv('CIBW_TEST_REQUIRES_' + platform.upper(), test_requires)
|
||||
@@ -175,12 +180,16 @@ def test_test_extras(test_extras, platform_specific, platform, intercepted_build
|
||||
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.args[0].test_extras == ('[' + test_extras + ']' if test_extras else '')
|
||||
assert intercepted_build_args.args[0].test_extras == (
|
||||
'[' + test_extras + ']' if test_extras else ''
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('test_command', [None, 'test --command'])
|
||||
@pytest.mark.parametrize('platform_specific', [False, True])
|
||||
def test_test_command(test_command, platform_specific, platform, intercepted_build_args, monkeypatch):
|
||||
def test_test_command(
|
||||
test_command, platform_specific, platform, intercepted_build_args, monkeypatch
|
||||
):
|
||||
if test_command is not None:
|
||||
if platform_specific:
|
||||
monkeypatch.setenv('CIBW_TEST_COMMAND_' + platform.upper(), test_command)
|
||||
@@ -195,7 +204,9 @@ def test_test_command(test_command, platform_specific, platform, intercepted_bui
|
||||
|
||||
@pytest.mark.parametrize('before_build', [None, 'before --build'])
|
||||
@pytest.mark.parametrize('platform_specific', [False, True])
|
||||
def test_before_build(before_build, platform_specific, platform, intercepted_build_args, monkeypatch):
|
||||
def test_before_build(
|
||||
before_build, platform_specific, platform, intercepted_build_args, monkeypatch
|
||||
):
|
||||
if before_build is not None:
|
||||
if platform_specific:
|
||||
monkeypatch.setenv('CIBW_BEFORE_BUILD_' + platform.upper(), before_build)
|
||||
@@ -210,7 +221,9 @@ def test_before_build(before_build, platform_specific, platform, intercepted_bui
|
||||
|
||||
@pytest.mark.parametrize('build_verbosity', [None, 0, 2, -2, 4, -4])
|
||||
@pytest.mark.parametrize('platform_specific', [False, True])
|
||||
def test_build_verbosity(build_verbosity, platform_specific, platform, intercepted_build_args, monkeypatch):
|
||||
def test_build_verbosity(
|
||||
build_verbosity, platform_specific, platform, intercepted_build_args, monkeypatch
|
||||
):
|
||||
if build_verbosity is not None:
|
||||
if platform_specific:
|
||||
monkeypatch.setenv('CIBW_BUILD_VERBOSITY_' + platform.upper(), str(build_verbosity))
|
||||
@@ -225,13 +238,23 @@ def test_build_verbosity(build_verbosity, platform_specific, platform, intercept
|
||||
|
||||
|
||||
@pytest.mark.parametrize('option_name', ['CIBW_BUILD', 'CIBW_SKIP'])
|
||||
@pytest.mark.parametrize('option_value, build_selector_patterns', [
|
||||
('*-manylinux1_*', ['*-manylinux_*']),
|
||||
('*-macosx_10_6_intel', ['*-macosx_x86_64']),
|
||||
('*-macosx_10_9_x86_64', ['*-macosx_x86_64']),
|
||||
('cp37-macosx_10_9_x86_64', ['cp37-macosx_x86_64']),
|
||||
])
|
||||
def test_build_selector_migrations(intercepted_build_args, monkeypatch, option_name, option_value, build_selector_patterns, allow_empty):
|
||||
@pytest.mark.parametrize(
|
||||
'option_value, build_selector_patterns',
|
||||
[
|
||||
('*-manylinux1_*', ['*-manylinux_*']),
|
||||
('*-macosx_10_6_intel', ['*-macosx_x86_64']),
|
||||
('*-macosx_10_9_x86_64', ['*-macosx_x86_64']),
|
||||
('cp37-macosx_10_9_x86_64', ['cp37-macosx_x86_64']),
|
||||
],
|
||||
)
|
||||
def test_build_selector_migrations(
|
||||
intercepted_build_args,
|
||||
monkeypatch,
|
||||
option_name,
|
||||
option_value,
|
||||
build_selector_patterns,
|
||||
allow_empty,
|
||||
):
|
||||
monkeypatch.setenv(option_name, option_value)
|
||||
|
||||
main()
|
||||
|
||||
@@ -167,8 +167,18 @@ def test_archs_platform_all(platform, intercepted_build_args, monkeypatch):
|
||||
build_options = intercepted_build_args.args[0]
|
||||
|
||||
if platform == 'linux':
|
||||
assert build_options.architectures == {Architecture.x86_64, Architecture.i686, Architecture.aarch64, Architecture.ppc64le, Architecture.s390x}
|
||||
assert build_options.architectures == {
|
||||
Architecture.x86_64,
|
||||
Architecture.i686,
|
||||
Architecture.aarch64,
|
||||
Architecture.ppc64le,
|
||||
Architecture.s390x,
|
||||
}
|
||||
elif platform == 'windows':
|
||||
assert build_options.architectures == {Architecture.x86, Architecture.AMD64}
|
||||
elif platform == 'macos':
|
||||
assert build_options.architectures == {Architecture.x86_64, Architecture.arm64, Architecture.universal2}
|
||||
assert build_options.architectures == {
|
||||
Architecture.x86_64,
|
||||
Architecture.arm64,
|
||||
Architecture.universal2,
|
||||
}
|
||||
|
||||
@@ -50,10 +50,14 @@ def test_override_env(platform, monkeypatch, intercepted_build_args):
|
||||
|
||||
def test_override_setup_cfg(platform, monkeypatch, intercepted_build_args, fake_package_dir):
|
||||
|
||||
fake_package_dir.joinpath("setup.cfg").write_text(textwrap.dedent("""
|
||||
fake_package_dir.joinpath("setup.cfg").write_text(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
[options]
|
||||
python_requires = >=3.8
|
||||
"""))
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
main()
|
||||
|
||||
@@ -67,10 +71,14 @@ def test_override_setup_cfg(platform, monkeypatch, intercepted_build_args, fake_
|
||||
|
||||
def test_override_pyproject_toml(platform, monkeypatch, intercepted_build_args, fake_package_dir):
|
||||
|
||||
fake_package_dir.joinpath("pyproject.toml").write_text(textwrap.dedent("""
|
||||
fake_package_dir.joinpath("pyproject.toml").write_text(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
[project]
|
||||
requires-python = ">=3.8"
|
||||
"""))
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
main()
|
||||
|
||||
@@ -84,14 +92,18 @@ def test_override_pyproject_toml(platform, monkeypatch, intercepted_build_args,
|
||||
|
||||
def test_override_setup_py_simple(platform, monkeypatch, intercepted_build_args, fake_package_dir):
|
||||
|
||||
fake_package_dir.joinpath("setup.py").write_text(textwrap.dedent("""
|
||||
fake_package_dir.joinpath("setup.py").write_text(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name = "other",
|
||||
python_requires = ">=3.7",
|
||||
)
|
||||
"""))
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
main()
|
||||
|
||||
|
||||
@@ -5,7 +5,9 @@ from cibuildwheel.projectfiles import get_requires_python_str, setup_py_python_r
|
||||
|
||||
def test_read_setup_py_simple(tmp_path):
|
||||
with open(tmp_path / "setup.py", "w") as f:
|
||||
f.write(dedent("""
|
||||
f.write(
|
||||
dedent(
|
||||
"""
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
@@ -14,7 +16,9 @@ def test_read_setup_py_simple(tmp_path):
|
||||
example = ["item", "other"],
|
||||
python_requires = "1.23",
|
||||
)
|
||||
"""))
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
assert setup_py_python_requires(tmp_path.joinpath("setup.py").read_text()) == "1.23"
|
||||
assert get_requires_python_str(tmp_path) == "1.23"
|
||||
@@ -22,7 +26,9 @@ def test_read_setup_py_simple(tmp_path):
|
||||
|
||||
def test_read_setup_py_full(tmp_path):
|
||||
with open(tmp_path / "setup.py", "w") as f:
|
||||
f.write(dedent("""
|
||||
f.write(
|
||||
dedent(
|
||||
"""
|
||||
import setuptools
|
||||
|
||||
setuptools.randomfunc()
|
||||
@@ -33,7 +39,9 @@ def test_read_setup_py_full(tmp_path):
|
||||
example = ["item", "other"],
|
||||
python_requires = "1.24",
|
||||
)
|
||||
"""))
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
assert setup_py_python_requires(tmp_path.joinpath("setup.py").read_text()) == "1.24"
|
||||
assert get_requires_python_str(tmp_path) == "1.24"
|
||||
@@ -41,7 +49,9 @@ def test_read_setup_py_full(tmp_path):
|
||||
|
||||
def test_read_setup_py_assign(tmp_path):
|
||||
with open(tmp_path / "setup.py", "w") as f:
|
||||
f.write(dedent("""
|
||||
f.write(
|
||||
dedent(
|
||||
"""
|
||||
from setuptools import setup
|
||||
|
||||
REQUIRES = "3.21"
|
||||
@@ -52,7 +62,9 @@ def test_read_setup_py_assign(tmp_path):
|
||||
example = ["item", "other"],
|
||||
python_requires = REQUIRES,
|
||||
)
|
||||
"""))
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
assert setup_py_python_requires(tmp_path.joinpath("setup.py").read_text()) is None
|
||||
assert get_requires_python_str(tmp_path) is None
|
||||
@@ -60,7 +72,9 @@ def test_read_setup_py_assign(tmp_path):
|
||||
|
||||
def test_read_setup_py_None(tmp_path):
|
||||
with open(tmp_path / "setup.py", "w") as f:
|
||||
f.write(dedent("""
|
||||
f.write(
|
||||
dedent(
|
||||
"""
|
||||
from setuptools import setup
|
||||
|
||||
REQUIRES = None
|
||||
@@ -71,7 +85,9 @@ def test_read_setup_py_None(tmp_path):
|
||||
example = ["item", "other"],
|
||||
python_requires = None,
|
||||
)
|
||||
"""))
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
assert setup_py_python_requires(tmp_path.joinpath("setup.py").read_text()) is None
|
||||
assert get_requires_python_str(tmp_path) is None
|
||||
@@ -79,7 +95,9 @@ def test_read_setup_py_None(tmp_path):
|
||||
|
||||
def test_read_setup_py_empty(tmp_path):
|
||||
with open(tmp_path / "setup.py", "w") as f:
|
||||
f.write(dedent("""
|
||||
f.write(
|
||||
dedent(
|
||||
"""
|
||||
from setuptools import setup
|
||||
|
||||
REQUIRES = "3.21"
|
||||
@@ -89,7 +107,9 @@ def test_read_setup_py_empty(tmp_path):
|
||||
other = 23,
|
||||
example = ["item", "other"],
|
||||
)
|
||||
"""))
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
assert setup_py_python_requires(tmp_path.joinpath("setup.py").read_text()) is None
|
||||
assert get_requires_python_str(tmp_path) is None
|
||||
@@ -97,46 +117,62 @@ def test_read_setup_py_empty(tmp_path):
|
||||
|
||||
def test_read_setup_cfg(tmp_path):
|
||||
with open(tmp_path / "setup.cfg", "w") as f:
|
||||
f.write(dedent("""
|
||||
f.write(
|
||||
dedent(
|
||||
"""
|
||||
[options]
|
||||
python_requires = 1.234
|
||||
[metadata]
|
||||
something = other
|
||||
"""))
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
assert get_requires_python_str(tmp_path) == "1.234"
|
||||
|
||||
|
||||
def test_read_setup_cfg_empty(tmp_path):
|
||||
with open(tmp_path / "setup.cfg", "w") as f:
|
||||
f.write(dedent("""
|
||||
f.write(
|
||||
dedent(
|
||||
"""
|
||||
[options]
|
||||
other = 1.234
|
||||
[metadata]
|
||||
something = other
|
||||
"""))
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
assert get_requires_python_str(tmp_path) is None
|
||||
|
||||
|
||||
def test_read_pyproject_toml(tmp_path):
|
||||
with open(tmp_path / "pyproject.toml", "w") as f:
|
||||
f.write(dedent("""
|
||||
f.write(
|
||||
dedent(
|
||||
"""
|
||||
[project]
|
||||
requires-python = "1.654"
|
||||
|
||||
[tool.cibuildwheel]
|
||||
something = "other"
|
||||
"""))
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
assert get_requires_python_str(tmp_path) == "1.654"
|
||||
|
||||
|
||||
def test_read_pyproject_toml_empty(tmp_path):
|
||||
with open(tmp_path / "pyproject.toml", "w") as f:
|
||||
f.write(dedent("""
|
||||
f.write(
|
||||
dedent(
|
||||
"""
|
||||
[project]
|
||||
other = 1.234
|
||||
"""))
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
assert get_requires_python_str(tmp_path) is None
|
||||
|
||||
Reference in New Issue
Block a user