diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 4954d0ba..a588b526 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -21,7 +21,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v3
- - uses: pre-commit/action@v2.0.3
+ - uses: pre-commit/action@v3.0.0
- name: Check manifest
run: pipx run nox -s check_manifest
- name: PyLint checks
@@ -84,7 +84,7 @@ jobs:
- name: Set up QEMU
id: qemu
- uses: docker/setup-qemu-action@v1
+ uses: docker/setup-qemu-action@v2
with:
platforms: all
diff --git a/.github/workflows/update-dependencies.yml b/.github/workflows/update-dependencies.yml
index 70f65e2a..40af3595 100644
--- a/.github/workflows/update-dependencies.yml
+++ b/.github/workflows/update-dependencies.yml
@@ -12,7 +12,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- - uses: excitedleigh/setup-nox@v2.0.0
+ - uses: actions/setup-python@v3
+ with:
+ python-version: "3.11-dev"
+ - uses: excitedleigh/setup-nox@v2.1.0
- name: "Run update: dependencies"
run: nox --force-color -s update_constraints
@@ -41,5 +44,7 @@ jobs:
PR generated by "Update dependencies" [workflow](https://github.com/${{github.repository}}/actions/runs/${{github.run_id}}).
branch: update-dependencies-pr
+ committer: "cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>"
+ author: "cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>"
token: ${{ steps.generate-token.outputs.token }}
delete-branch: true
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index da8b94a2..679254fd 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,7 +1,7 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v4.2.0
+ rev: v4.3.0
hooks:
- id: check-case-conflict
- id: check-merge-conflict
@@ -14,7 +14,7 @@ repos:
- id: trailing-whitespace
- repo: https://github.com/asottile/pyupgrade
- rev: v2.32.0
+ rev: v2.34.0
hooks:
- id: pyupgrade
name: PyUpgrade 3.6+
@@ -27,7 +27,7 @@ repos:
# Autoremoves unused imports
- repo: https://github.com/hadialqattan/pycln
- rev: v1.3.2
+ rev: v1.3.3
hooks:
- id: pycln
args: [--all]
@@ -49,7 +49,7 @@ repos:
- id: setup-cfg-fmt
- repo: https://github.com/pre-commit/mirrors-mypy
- rev: v0.950
+ rev: v0.961
hooks:
- id: mypy
name: mypy 3.6 on cibuildwheel/
diff --git a/.travis.yml b/.travis.yml
index 7115e5d1..aad9e5a5 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -52,4 +52,11 @@ install:
- $PYTHON -m pip install -U pip
- $PYTHON -m pip install -e ".[dev]" pytest-custom-exit-code
-script: $PYTHON ./bin/run_tests.py
+script: |
+ # travis_wait disable the output while waiting
+ # use the same kind of tricks as in multibuild
+ (while true; do echo "travis_keep_alive"; sleep 300; done) &
+ SPINNER_PID=$!
+ disown
+ $PYTHON ./bin/run_tests.py
+ kill -9 ${SPINNER_PID}
diff --git a/README.md b/README.md
index a621e4f5..e66a835e 100644
--- a/README.md
+++ b/README.md
@@ -29,6 +29,7 @@ What does it do?
| CPython 3.8 | ✅ | ✅ | ✅ | ✅ | N/A | ✅ | ✅ | ✅ | ✅ | ✅ |
| CPython 3.9 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅³ | ✅ | ✅ | ✅ | ✅ |
| CPython 3.10 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ |
+| CPython 3.11⁴ | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ |
| PyPy 3.7 v7.3 | ✅ | N/A | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A |
| PyPy 3.8 v7.3 | ✅ | N/A | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A |
| PyPy 3.9 v7.3 | ✅ | N/A | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A |
@@ -36,6 +37,7 @@ What does it do?
¹ PyPy is only supported for manylinux wheels.
² Windows arm64 support is experimental.
³ Alpine 3.14 and very briefly 3.15's default python3 [was not able to load](https://github.com/pypa/cibuildwheel/issues/934) musllinux wheels. This has been fixed; please upgrade the python package if using Alpine from before the fix.
+⁴ CPython 3.11 is available using the [CIBW_PRERELEASE_PYTHONS](https://cibuildwheel.readthedocs.io/en/stable/options/#prerelease-pythons) option.
- Builds manylinux, musllinux, macOS 10.9+, and Windows wheels for CPython and PyPy
- Works on GitHub Actions, Azure Pipelines, Travis CI, AppVeyor, CircleCI, and GitLab CI
@@ -87,7 +89,7 @@ jobs:
- uses: actions/setup-python@v2
- name: Install cibuildwheel
- run: python -m pip install cibuildwheel==2.4.0
+ run: python -m pip install cibuildwheel==2.6.1
- name: Build wheels
run: python -m cibuildwheel --output-dir wheelhouse
@@ -102,6 +104,15 @@ jobs:
For more information, including PyPI deployment, and the use of other CI services or the dedicated GitHub Action, check out the [documentation](https://cibuildwheel.readthedocs.org) and the [examples](https://github.com/pypa/cibuildwheel/tree/main/examples).
+How it works
+------------
+
+The following diagram summarises the steps that cibuildwheel takes on each platform.
+
+
+
+Explore an interactive version of this diagram [in the docs](https://cibuildwheel.readthedocs.io/en/stable/#how-it-works).
+
Options
-------
@@ -117,7 +128,7 @@ Options
| | [`CIBW_ENVIRONMENT_PASS_LINUX`](https://cibuildwheel.readthedocs.io/en/stable/options/#environment-pass) | Set environment variables on the host to pass-through to the container during the build. |
| | [`CIBW_BEFORE_ALL`](https://cibuildwheel.readthedocs.io/en/stable/options/#before-all) | Execute a shell command on the build system before any wheels are built. |
| | [`CIBW_BEFORE_BUILD`](https://cibuildwheel.readthedocs.io/en/stable/options/#before-build) | Execute a shell command preparing each wheel's build |
-| | [`CIBW_REPAIR_WHEEL_COMMAND`](https://cibuildwheel.readthedocs.io/en/stable/options/#repair-wheel-command) | Execute a shell command to repair each (non-pure Python) built wheel |
+| | [`CIBW_REPAIR_WHEEL_COMMAND`](https://cibuildwheel.readthedocs.io/en/stable/options/#repair-wheel-command) | Execute a shell command to repair each built wheel |
| | [`CIBW_MANYLINUX_*_IMAGE`
`CIBW_MUSLLINUX_*_IMAGE`](https://cibuildwheel.readthedocs.io/en/stable/options/#linux-image) | Specify alternative manylinux / musllinux Docker images |
| | [`CIBW_DEPENDENCY_VERSIONS`](https://cibuildwheel.readthedocs.io/en/stable/options/#dependency-versions) | Specify how cibuildwheel controls the versions of the tools it uses |
| **Testing** | [`CIBW_TEST_COMMAND`](https://cibuildwheel.readthedocs.io/en/stable/options/#test-command) | Execute a shell command to test each built wheel |
@@ -141,26 +152,26 @@ Here are some repos that use cibuildwheel.
| Name | CI | OS | Notes |
|-----------------------------------|----|----|:------|
| [scikit-learn][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The machine learning library. A complex but clean config using many of cibuildwheel's features to build a large project with Cython and C++ extensions. |
+| [NumPy][] | ![github icon][] ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The fundamental package for scientific computing with Python. |
| [Tornado][] | ![travisci icon][] | ![apple icon][] ![linux icon][] | Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed. |
| [pytorch-fairseq][] | ![github icon][] | ![apple icon][] ![linux icon][] | Facebook AI Research Sequence-to-Sequence Toolkit written in Python. |
| [Matplotlib][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The venerable Matplotlib, a Python library with C++ portions |
-| [MyPy][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | MyPyC, the compiled component of MyPy. |
+| [NCNN][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | ncnn is a high-performance neural network inference framework optimized for the mobile platform |
+| [Kivy][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Open source UI framework written in Python, running on Windows, Linux, macOS, Android and iOS |
+| [Prophet][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Tool for producing high quality forecasts for time series data that has multiple seasonality with linear or non-linear growth. |
+| [MyPy][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | The compiled version of MyPy using MyPyC. |
| [pydantic][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Data parsing and validation using Python type hints |
-| [uvloop][] | ![github icon][] | ![apple icon][] ![linux icon][] | Ultra fast asyncio event loop. |
-| [psutil][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Cross-platform lib for process and system monitoring in Python |
-| [vaex][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Out-of-Core hybrid Apache Arrow/NumPy DataFrame for Python, ML, visualization and exploration of big tabular data at a billion rows per second 🚀 |
-| [Google Benchmark][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A microbenchmark support library |
[scikit-learn]: https://github.com/scikit-learn/scikit-learn
+[NumPy]: https://github.com/numpy/numpy
[Tornado]: https://github.com/tornadoweb/tornado
[pytorch-fairseq]: https://github.com/pytorch/fairseq
[Matplotlib]: https://github.com/matplotlib/matplotlib
+[NCNN]: https://github.com/Tencent/ncnn
+[Kivy]: https://github.com/kivy/kivy
+[Prophet]: https://github.com/facebook/prophet
[MyPy]: https://github.com/mypyc/mypy_mypyc-wheels
[pydantic]: https://github.com/samuelcolvin/pydantic
-[uvloop]: https://github.com/MagicStack/uvloop
-[psutil]: https://github.com/giampaolo/psutil
-[vaex]: https://github.com/vaexio/vaex
-[Google Benchmark]: https://github.com/google/benchmark
[appveyor icon]: docs/data/readme_icons/appveyor.svg
[github icon]: docs/data/readme_icons/github.svg
@@ -192,6 +203,29 @@ Changelog
+### v2.6.1
+
+_7 June 2022_
+
+- 🛠 Update the prerelease CPython 3.11 to 3.11.0b3
+
+### v2.6.0
+
+_25 May 2022_
+
+- 🌟 Added the ability to test building wheels on CPython 3.11! Because CPython 3.11 is in beta, these wheels should not be distributed, because they might not be compatible with the final release, but it's available to build for testing purposes. Use the flag [`--prerelease-pythons` or `CIBW_PRERELEASE_PYTHONS`](https://cibuildwheel.readthedocs.io/en/stable/options/#prerelease-pythons) to test. This version of cibuildwheel includes CPython 3.11.0b1. (#1109)
+- 📚 Added an interactive diagram showing how cibuildwheel works to the [docs](https://cibuildwheel.readthedocs.io/en/stable/#how-it-works) (#1100)
+
+### v2.5.0
+
+_29 April 2022_
+
+- ✨ Added support for building ABI3 wheels. cibuildwheel will now recognise when an ABI3 wheel was produced, and skip subsequent build steps where the previously built wheel is compatible. Tests still will run on all selected versions of Python, using the ABI3 wheel. Check [this entry](https://cibuildwheel.readthedocs.io/en/stable/faq/#abi3) in the docs for more info. (#1091)
+- ✨ You can now build wheels directly from sdist archives, in addition to source directories. Just call cibuildwheel with an sdist argument on the command line, like `cibuildwheel mypackage-1.0.0.tar.gz`. For more details, check the [`--help` output](https://cibuildwheel.readthedocs.io/en/stable/options/#command-line) (#1096)
+- 🐛 Fix a bug where cibuildwheel would crash when no builds are selected and `--allow-empty` is passed (#1086)
+- 🐛 Workaround a permissions issue on Linux relating to newer versions of git and setuptools_scm (#1095)
+- 📚 Minor docs improvements
+
### v2.4.0
_2 April 2022_
@@ -212,32 +246,6 @@ _14 December 2021_
- 🐛 Setting pip options like `PIP_USE_DEPRECATED` in `CIBW_ENVIRONMENT` no longer adversely affects cibuildwheel's ability to set up a Python environment (#956)
- 📚 Docs fixes and improvements
-### v2.3.0
-
-_26 November 2021_
-
-- 📈 cibuildwheel now defaults to manylinux2014 image for linux builds, rather than manylinux2010. If you want to stick with manylinux2010, it's simple to set this using [the image options](https://cibuildwheel.readthedocs.io/en/stable/options/#linux-image). (#926)
-- ✨ You can now pass environment variables from the host machine into the Docker container during a Linux build. Check out [the docs for `CIBW_ENVIRONMENT_PASS_LINUX `](https://cibuildwheel.readthedocs.io/en/latest/options/#environment-pass) for the details. (#914)
-- ✨ Added support for building PyPy 3.8 wheels. (#881)
-- ✨ Added support for building Windows arm64 CPython wheels on a Windows arm64 runner. We can't test this in CI yet, so for now, this is experimental. (#920)
-- 📚 Improved the deployment documentation (#911)
-- 🛠 Changed the escaping behaviour inside cibuildwheel's option placeholders e.g. `{project}` in `before_build` or `{dest_dir}` in `repair_wheel_command`. This allows bash syntax like `${SOME_VAR}` to passthrough without being interpreted as a placeholder by cibuildwheel. See [this section](https://cibuildwheel.readthedocs.io/en/stable/options/#placeholders) in the docs for more info. (#889)
-- 🛠 Pip updated to 21.3, meaning it now defaults to in-tree builds again. If this causes an issue with your project, setting environment variable `PIP_USE_DEPRECATED=out-of-tree-build` is available as a temporary flag to restore the old behaviour. However, be aware that this flag will probably be removed soon. (#881)
-- 🐛 You can now access the current Python interpreter using `python3` within a build on Windows (#917)
-
-### v2.2.2
-
-_26 October 2021_
-
-- 🐛 Fix bug in the GitHub Action step causing a syntax error (#895)
-
-### v2.2.1
-
-_26 October 2021_
-
-- 🛠 Added a `config-file` option on the GitHub Action to specify something other than pyproject.toml in your GitHub Workflow file. (#883)
-- 🐛 Fix missing resources in sdist and released wheel on PyPI. We've also made some internal changes to our release processes to make them more reliable. (#893, #894)
-
---
diff --git a/bin/update_docker.py b/bin/update_docker.py
index 105a3267..4d1af209 100755
--- a/bin/update_docker.py
+++ b/bin/update_docker.py
@@ -46,6 +46,12 @@ images = [
Image("manylinux_2_24", "pypy_x86_64", "quay.io/pypa/manylinux_2_24_x86_64", None),
Image("manylinux_2_24", "pypy_i686", "quay.io/pypa/manylinux_2_24_i686", None),
Image("manylinux_2_24", "pypy_aarch64", "quay.io/pypa/manylinux_2_24_aarch64", None),
+ # manylinux_2_28 images
+ Image("manylinux_2_28", "x86_64", "quay.io/pypa/manylinux_2_28_x86_64", None),
+ Image("manylinux_2_28", "aarch64", "quay.io/pypa/manylinux_2_28_aarch64", None),
+ Image("manylinux_2_28", "ppc64le", "quay.io/pypa/manylinux_2_28_ppc64le", None),
+ Image("manylinux_2_28", "pypy_x86_64", "quay.io/pypa/manylinux_2_28_x86_64", None),
+ Image("manylinux_2_28", "pypy_aarch64", "quay.io/pypa/manylinux_2_28_aarch64", None),
# musllinux_1_1 images
Image("musllinux_1_1", "x86_64", "quay.io/pypa/musllinux_1_1_x86_64", None),
Image("musllinux_1_1", "i686", "quay.io/pypa/musllinux_1_1_i686", None),
diff --git a/cibuildwheel/__init__.py b/cibuildwheel/__init__.py
index 3d67cd6b..fab833f3 100644
--- a/cibuildwheel/__init__.py
+++ b/cibuildwheel/__init__.py
@@ -1 +1 @@
-__version__ = "2.4.0"
+__version__ = "2.6.1"
diff --git a/cibuildwheel/__main__.py b/cibuildwheel/__main__.py
index 9f24f000..73077735 100644
--- a/cibuildwheel/__main__.py
+++ b/cibuildwheel/__main__.py
@@ -2,6 +2,8 @@ import argparse
import os
import shutil
import sys
+import tarfile
+import tempfile
import textwrap
from pathlib import Path
from tempfile import mkdtemp
@@ -20,13 +22,12 @@ from cibuildwheel.util import (
CIBW_CACHE_PATH,
BuildSelector,
Unbuffered,
+ chdir,
detect_ci_provider,
)
def main() -> None:
- platform: PlatformName
-
parser = argparse.ArgumentParser(
description="Build wheels for all the platforms.",
epilog="""
@@ -65,6 +66,8 @@ def main() -> None:
parser.add_argument(
"--output-dir",
+ type=Path,
+ default=Path(os.environ.get("CIBW_OUTPUT_DIR", "wheelhouse")),
help="Destination folder for the wheels. Default: wheelhouse.",
)
@@ -72,20 +75,26 @@ def main() -> None:
"--config-file",
default="",
help="""
- TOML config file. Default: "", meaning {package}/pyproject.toml,
- if it exists.
+ TOML config file. Default: "", meaning {package}/pyproject.toml, if
+ it exists. To refer to a project inside your project, use {package};
+ this matters if you build from an SDist.
""",
)
parser.add_argument(
"package_dir",
- default=".",
+ metavar="PACKAGE",
+ default=Path("."),
+ type=Path,
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.
+ Path to the package that you want wheels for. Default: the working
+ directory. Can be a directory inside the working directory, or an
+ sdist. When set to a directory, the working directory is still
+ considered the 'project' and is copied into the Docker container
+ on Linux. When set to a tar.gz sdist file, --config-file
+ and --output-dir are relative to the current directory, and other
+ paths are relative to the expanded SDist directory.
""",
)
@@ -109,6 +118,38 @@ def main() -> None:
args = CommandLineArguments(**vars(parser.parse_args()))
+ args.package_dir = args.package_dir.resolve()
+
+ # This are always relative to the base directory, even in SDist builds
+ args.output_dir = args.output_dir.resolve()
+
+ # Standard builds if a directory or non-existent path is given
+ if not args.package_dir.is_file() and not args.package_dir.name.endswith("tar.gz"):
+ build_in_directory(args)
+ return
+
+ # Tarfile builds require extraction and changing the directory
+ with tempfile.TemporaryDirectory(prefix="cibw-sdist-") as temp_dir_str:
+ temp_dir = Path(temp_dir_str)
+ with tarfile.open(args.package_dir) as tar:
+ tar.extractall(path=temp_dir)
+
+ # The extract directory is now the project dir
+ try:
+ (project_dir,) = temp_dir.iterdir()
+ except ValueError:
+ raise SystemExit("invalid sdist: didn't contain a single dir") from None
+
+ # This is now the new package dir
+ args.package_dir = project_dir.resolve()
+
+ with chdir(temp_dir):
+ build_in_directory(args)
+
+
+def build_in_directory(args: CommandLineArguments) -> None:
+ platform: PlatformName
+
if args.platform != "auto":
platform = args.platform
else:
diff --git a/cibuildwheel/docker_container.py b/cibuildwheel/docker_container.py
index 8ee8e573..9dc3ab70 100644
--- a/cibuildwheel/docker_container.py
+++ b/cibuildwheel/docker_container.py
@@ -1,12 +1,12 @@
import io
import json
-import os
import platform
import shlex
+import shutil
import subprocess
import sys
import uuid
-from pathlib import Path, PurePath
+from pathlib import Path, PurePath, PurePosixPath
from types import TracebackType
from typing import IO, Dict, List, Optional, Sequence, Type, cast
@@ -128,11 +128,28 @@ class DockerContainer:
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,
- )
+ with subprocess.Popen(
+ [
+ "docker",
+ "exec",
+ "-i",
+ str(self.name),
+ "sh",
+ "-c",
+ f"cat > {shell_quote(to_path)}",
+ ],
+ stdin=subprocess.PIPE,
+ ) as docker:
+ docker.stdin = cast(IO[bytes], docker.stdin)
+
+ with open(from_path, "rb") as from_file:
+ shutil.copyfileobj(from_file, docker.stdin)
+
+ docker.stdin.close()
+ docker.wait()
+
+ if docker.returncode:
+ raise subprocess.CalledProcessError(docker.returncode, docker.args, None, None)
def copy_out(self, from_path: PurePath, to_path: Path) -> None:
# note: we assume from_path is a dir
@@ -145,21 +162,21 @@ class DockerContainer:
cwd=to_path,
)
- def glob(self, path: PurePath, pattern: str) -> List[PurePath]:
- glob_pattern = os.path.join(str(path), pattern)
+ def glob(self, path: PurePosixPath, pattern: str) -> List[PurePosixPath]:
+ glob_pattern = path.joinpath(pattern)
path_strings = json.loads(
self.call(
[
self.UTILITY_PYTHON,
"-c",
- f"import sys, json, glob; json.dump(glob.glob({glob_pattern!r}), sys.stdout)",
+ f"import sys, json, glob; json.dump(glob.glob({str(glob_pattern)!r}), sys.stdout)",
],
capture_output=True,
)
)
- return [PurePath(p) for p in path_strings]
+ return [PurePosixPath(p) for p in path_strings]
def call(
self,
diff --git a/cibuildwheel/linux.py b/cibuildwheel/linux.py
index a1862d77..b278d48b 100644
--- a/cibuildwheel/linux.py
+++ b/cibuildwheel/linux.py
@@ -2,7 +2,7 @@ import subprocess
import sys
import textwrap
from dataclasses import dataclass
-from pathlib import Path, PurePath
+from pathlib import Path, PurePath, PurePosixPath
from typing import Iterator, List, Set, Tuple
from .architecture import Architecture
@@ -13,6 +13,7 @@ from .typing import OrderedDict, PathOrStr, assert_never
from .util import (
BuildSelector,
NonPlatformWheelError,
+ find_compatible_abi3_wheel,
get_build_verbosity_extra_flags,
prepare_command,
read_python_configs,
@@ -26,8 +27,8 @@ class PythonConfiguration:
path_str: str
@property
- def path(self) -> PurePath:
- return PurePath(self.path_str)
+ def path(self) -> PurePosixPath:
+ return PurePosixPath(self.path_str)
@dataclass(frozen=True)
@@ -110,7 +111,7 @@ def build_on_docker(
container_project_path: PurePath,
container_package_dir: PurePath,
) -> None:
- container_output_dir = PurePath("/output")
+ container_output_dir = PurePosixPath("/output")
log.step("Copying project into Docker...")
docker.copy_into(Path.cwd(), container_project_path)
@@ -135,6 +136,8 @@ def build_on_docker(
)
docker.call(["sh", "-c", before_all_prepared], env=env)
+ built_wheels: List[PurePosixPath] = []
+
for config in platform_configs:
log.build_start(config.identifier)
build_options = options.build_options(config.identifier)
@@ -162,7 +165,7 @@ def build_on_docker(
# check config python is still on PATH
which_python = docker.call(["which", "python"], env=env, capture_output=True).strip()
- if PurePath(which_python) != python_bin / "python":
+ if PurePosixPath(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,
@@ -170,81 +173,90 @@ def build_on_docker(
sys.exit(1)
which_pip = docker.call(["which", "pip"], env=env, capture_output=True).strip()
- if PurePath(which_pip) != python_bin / "pip":
+ if PurePosixPath(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,
)
sys.exit(1)
- if build_options.before_build:
- log.step("Running before_build...")
- before_build_prepared = prepare_command(
- build_options.before_build,
- project=container_project_path,
- package=container_package_dir,
- )
- docker.call(["sh", "-c", before_build_prepared], env=env)
-
- log.step("Building wheel...")
-
- temp_dir = PurePath("/tmp/cibuildwheel")
- built_wheel_dir = temp_dir / "built_wheel"
- docker.call(["rm", "-rf", built_wheel_dir])
- docker.call(["mkdir", "-p", built_wheel_dir])
-
- verbosity_flags = get_build_verbosity_extra_flags(build_options.build_verbosity)
-
- if build_options.build_frontend == "pip":
- docker.call(
- [
- "python",
- "-m",
- "pip",
- "wheel",
- container_package_dir,
- f"--wheel-dir={built_wheel_dir}",
- "--no-deps",
- *verbosity_flags,
- ],
- env=env,
- )
- elif build_options.build_frontend == "build":
- config_setting = " ".join(verbosity_flags)
- docker.call(
- [
- "python",
- "-m",
- "build",
- container_package_dir,
- "--wheel",
- f"--outdir={built_wheel_dir}",
- f"--config-setting={config_setting}",
- ],
- env=env,
+ abi3_wheel = find_compatible_abi3_wheel(built_wheels, config.identifier)
+ if abi3_wheel:
+ log.step_end()
+ print(
+ f"\nFound previously built wheel {abi3_wheel.name}, that's compatible with {config.identifier}. Skipping build step..."
)
+ repaired_wheels = [abi3_wheel]
else:
- assert_never(build_options.build_frontend)
- built_wheel = docker.glob(built_wheel_dir, "*.whl")[0]
+ if build_options.before_build:
+ log.step("Running before_build...")
+ before_build_prepared = prepare_command(
+ build_options.before_build,
+ project=container_project_path,
+ package=container_package_dir,
+ )
+ docker.call(["sh", "-c", before_build_prepared], env=env)
- repaired_wheel_dir = temp_dir / "repaired_wheel"
- docker.call(["rm", "-rf", repaired_wheel_dir])
- docker.call(["mkdir", "-p", repaired_wheel_dir])
+ log.step("Building wheel...")
- if built_wheel.name.endswith("none-any.whl"):
- raise NonPlatformWheelError()
+ temp_dir = PurePosixPath("/tmp/cibuildwheel")
+ built_wheel_dir = temp_dir / "built_wheel"
+ docker.call(["rm", "-rf", built_wheel_dir])
+ docker.call(["mkdir", "-p", built_wheel_dir])
- if build_options.repair_command:
- log.step("Repairing wheel...")
- repair_command_prepared = prepare_command(
- build_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])
+ verbosity_flags = get_build_verbosity_extra_flags(build_options.build_verbosity)
- repaired_wheels = docker.glob(repaired_wheel_dir, "*.whl")
+ if build_options.build_frontend == "pip":
+ docker.call(
+ [
+ "python",
+ "-m",
+ "pip",
+ "wheel",
+ container_package_dir,
+ f"--wheel-dir={built_wheel_dir}",
+ "--no-deps",
+ *verbosity_flags,
+ ],
+ env=env,
+ )
+ elif build_options.build_frontend == "build":
+ config_setting = " ".join(verbosity_flags)
+ docker.call(
+ [
+ "python",
+ "-m",
+ "build",
+ container_package_dir,
+ "--wheel",
+ f"--outdir={built_wheel_dir}",
+ f"--config-setting={config_setting}",
+ ],
+ env=env,
+ )
+ else:
+ assert_never(build_options.build_frontend)
+
+ built_wheel = docker.glob(built_wheel_dir, "*.whl")[0]
+
+ repaired_wheel_dir = temp_dir / "repaired_wheel"
+ docker.call(["rm", "-rf", repaired_wheel_dir])
+ docker.call(["mkdir", "-p", repaired_wheel_dir])
+
+ if built_wheel.name.endswith("none-any.whl"):
+ raise NonPlatformWheelError()
+
+ if build_options.repair_command:
+ log.step("Repairing wheel...")
+ repair_command_prepared = prepare_command(
+ build_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])
+
+ repaired_wheels = docker.glob(repaired_wheel_dir, "*.whl")
if build_options.test_command and build_options.test_selector(config.identifier):
log.step("Testing wheel...")
@@ -295,8 +307,12 @@ def build_on_docker(
docker.call(["rm", "-rf", venv_dir])
# move repaired wheels to output
- docker.call(["mkdir", "-p", container_output_dir])
- docker.call(["mv", *repaired_wheels, container_output_dir])
+ if abi3_wheel is None:
+ docker.call(["mkdir", "-p", container_output_dir])
+ docker.call(["mv", *repaired_wheels, container_output_dir])
+ built_wheels.extend(
+ container_output_dir / repaired_wheel.name for repaired_wheel in repaired_wheels
+ )
log.build_end()
@@ -328,7 +344,7 @@ def build(options: Options, tmp_path: Path) -> None: # pylint: disable=unused-a
if cwd != abs_package_dir and cwd not in abs_package_dir.parents:
raise Exception("package_dir must be inside the working directory")
- container_project_path = PurePath("/project")
+ container_project_path = PurePosixPath("/project")
container_package_dir = container_project_path / abs_package_dir.relative_to(cwd)
for build_step in get_build_steps(options, python_configurations):
diff --git a/cibuildwheel/macos.py b/cibuildwheel/macos.py
index cf9adba1..2da719ea 100644
--- a/cibuildwheel/macos.py
+++ b/cibuildwheel/macos.py
@@ -24,6 +24,7 @@ from .util import (
call,
detect_ci_provider,
download,
+ find_compatible_abi3_wheel,
get_build_verbosity_extra_flags,
get_pip_version,
install_certifi_script,
@@ -293,6 +294,8 @@ def build(options: Options, tmp_path: Path) -> None:
)
shell(before_all_prepared, env=env)
+ built_wheels: List[Path] = []
+
for config in python_configurations:
build_options = options.build_options(config.identifier)
log.build_start(config.identifier)
@@ -320,84 +323,94 @@ def build(options: Options, tmp_path: Path) -> None:
build_options.build_frontend,
)
- if build_options.before_build:
- log.step("Running before_build...")
- before_build_prepared = prepare_command(
- build_options.before_build, project=".", package=build_options.package_dir
+ abi3_wheel = find_compatible_abi3_wheel(built_wheels, config.identifier)
+ if abi3_wheel:
+ log.step_end()
+ print(
+ f"\nFound previously built wheel {abi3_wheel.name}, that's compatible with {config.identifier}. Skipping build step..."
)
- shell(before_build_prepared, env=env)
-
- log.step("Building wheel...")
- built_wheel_dir.mkdir()
-
- verbosity_flags = get_build_verbosity_extra_flags(build_options.build_verbosity)
-
- if build_options.build_frontend == "pip":
- # Path.resolve() is needed. Without it pip wheel may try to fetch package from pypi.org
- # see https://github.com/pypa/cibuildwheel/pull/369
- call(
- "python",
- "-m",
- "pip",
- "wheel",
- build_options.package_dir.resolve(),
- f"--wheel-dir={built_wheel_dir}",
- "--no-deps",
- *verbosity_flags,
- env=env,
- )
- elif build_options.build_frontend == "build":
- config_setting = " ".join(verbosity_flags)
- build_env = env.copy()
- if build_options.dependency_constraints:
- constraint_path = build_options.dependency_constraints.get_for_python_version(
- config.version
+ repaired_wheel = abi3_wheel
+ else:
+ if build_options.before_build:
+ log.step("Running before_build...")
+ before_build_prepared = prepare_command(
+ build_options.before_build, project=".", package=build_options.package_dir
)
- build_env["PIP_CONSTRAINT"] = constraint_path.as_uri()
- build_env["VIRTUALENV_PIP"] = get_pip_version(env)
- call(
- "python",
- "-m",
- "build",
- build_options.package_dir,
- "--wheel",
- f"--outdir={built_wheel_dir}",
- f"--config-setting={config_setting}",
- env=build_env,
- )
- else:
- assert_never(build_options.build_frontend)
+ shell(before_build_prepared, env=env)
- built_wheel = next(built_wheel_dir.glob("*.whl"))
+ log.step("Building wheel...")
+ built_wheel_dir.mkdir()
- repaired_wheel_dir.mkdir()
+ verbosity_flags = get_build_verbosity_extra_flags(build_options.build_verbosity)
- if built_wheel.name.endswith("none-any.whl"):
- raise NonPlatformWheelError()
-
- if build_options.repair_command:
- log.step("Repairing wheel...")
-
- if config_is_universal2:
- delocate_archs = "x86_64,arm64"
- elif config_is_arm64:
- delocate_archs = "arm64"
+ if build_options.build_frontend == "pip":
+ # Path.resolve() is needed. Without it pip wheel may try to fetch package from pypi.org
+ # see https://github.com/pypa/cibuildwheel/pull/369
+ call(
+ "python",
+ "-m",
+ "pip",
+ "wheel",
+ build_options.package_dir.resolve(),
+ f"--wheel-dir={built_wheel_dir}",
+ "--no-deps",
+ *verbosity_flags,
+ env=env,
+ )
+ elif build_options.build_frontend == "build":
+ config_setting = " ".join(verbosity_flags)
+ build_env = env.copy()
+ if build_options.dependency_constraints:
+ constraint_path = (
+ build_options.dependency_constraints.get_for_python_version(
+ config.version
+ )
+ )
+ build_env["PIP_CONSTRAINT"] = constraint_path.as_uri()
+ build_env["VIRTUALENV_PIP"] = get_pip_version(env)
+ call(
+ "python",
+ "-m",
+ "build",
+ build_options.package_dir,
+ "--wheel",
+ f"--outdir={built_wheel_dir}",
+ f"--config-setting={config_setting}",
+ env=build_env,
+ )
else:
- delocate_archs = "x86_64"
+ assert_never(build_options.build_frontend)
- repair_command_prepared = prepare_command(
- build_options.repair_command,
- wheel=built_wheel,
- dest_dir=repaired_wheel_dir,
- delocate_archs=delocate_archs,
- )
- shell(repair_command_prepared, env=env)
- else:
- shutil.move(str(built_wheel), repaired_wheel_dir)
+ built_wheel = next(built_wheel_dir.glob("*.whl"))
- repaired_wheel = next(repaired_wheel_dir.glob("*.whl"))
+ repaired_wheel_dir.mkdir()
- log.step_end()
+ if built_wheel.name.endswith("none-any.whl"):
+ raise NonPlatformWheelError()
+
+ if build_options.repair_command:
+ log.step("Repairing wheel...")
+
+ if config_is_universal2:
+ delocate_archs = "x86_64,arm64"
+ elif config_is_arm64:
+ delocate_archs = "arm64"
+ else:
+ delocate_archs = "x86_64"
+
+ repair_command_prepared = prepare_command(
+ build_options.repair_command,
+ wheel=built_wheel,
+ dest_dir=repaired_wheel_dir,
+ delocate_archs=delocate_archs,
+ )
+ shell(repair_command_prepared, env=env)
+ else:
+ shutil.move(str(built_wheel), repaired_wheel_dir)
+
+ repaired_wheel = next(repaired_wheel_dir.glob("*.whl"))
+
+ log.step_end()
if build_options.test_command and build_options.test_selector(config.identifier):
machine_arch = platform.machine()
@@ -523,7 +536,14 @@ def build(options: Options, tmp_path: Path) -> None:
)
# we're all done here; move it to output (overwrite existing)
- shutil.move(str(repaired_wheel), build_options.output_dir)
+ if abi3_wheel is None:
+ try:
+ (build_options.output_dir / repaired_wheel.name).unlink()
+ except FileNotFoundError:
+ pass
+
+ shutil.move(str(repaired_wheel), build_options.output_dir)
+ built_wheels.append(build_options.output_dir / repaired_wheel.name)
# clean up
shutil.rmtree(identifier_tmp_dir)
diff --git a/cibuildwheel/options.py b/cibuildwheel/options.py
index 0385fc3e..1863469f 100644
--- a/cibuildwheel/options.py
+++ b/cibuildwheel/options.py
@@ -6,7 +6,7 @@ from configparser import ConfigParser
from contextlib import contextmanager
from dataclasses import asdict, dataclass
from pathlib import Path
-from typing import Any, Dict, Iterator, List, Mapping, Optional, Set, Tuple, Union
+from typing import Any, Dict, Generator, List, Mapping, Optional, Set, Tuple, Union
if sys.version_info >= (3, 11):
import tomllib
@@ -27,6 +27,7 @@ from .util import (
DependencyConstraints,
TestSelector,
cached_property,
+ format_safe,
resources_dir,
selector_matches,
strtobool,
@@ -38,9 +39,9 @@ from .util import (
class CommandLineArguments:
platform: Literal["auto", "linux", "macos", "windows"]
archs: Optional[str]
- output_dir: Optional[str]
+ output_dir: Path
config_file: str
- package_dir: str
+ package_dir: Path
print_build_identifiers: bool
allow_empty: bool
prerelease_pythons: bool
@@ -258,7 +259,7 @@ class OptionsReader:
]
@contextmanager
- def identifier(self, identifier: Optional[str]) -> Iterator[None]:
+ def identifier(self, identifier: Optional[str]) -> Generator[None, None, None]:
self.current_identifier = identifier
try:
yield
@@ -339,7 +340,7 @@ class Options:
args = self.command_line_arguments
if args.config_file:
- return Path(args.config_file.format(package=args.package_dir))
+ return Path(format_safe(args.config_file, package=args.package_dir))
# return pyproject.toml, if it's available
pyproject_toml_path = Path(args.package_dir) / "pyproject.toml"
@@ -356,12 +357,8 @@ class Options:
@property
def globals(self) -> GlobalOptions:
args = self.command_line_arguments
- package_dir = Path(args.package_dir)
- output_dir = Path(
- args.output_dir
- if args.output_dir is not None
- else os.environ.get("CIBW_OUTPUT_DIR", "wheelhouse")
- )
+ package_dir = args.package_dir
+ output_dir = args.output_dir
build_config = self.reader.get("build", env_plat=False, sep=" ") or "*"
skip_config = self.reader.get("skip", env_plat=False, sep=" ")
diff --git a/cibuildwheel/resources/build-platforms.toml b/cibuildwheel/resources/build-platforms.toml
index 3a54c74c..fe8528d3 100644
--- a/cibuildwheel/resources/build-platforms.toml
+++ b/cibuildwheel/resources/build-platforms.toml
@@ -5,11 +5,13 @@ python_configurations = [
{ identifier = "cp38-manylinux_x86_64", version = "3.8", path_str = "/opt/python/cp38-cp38" },
{ identifier = "cp39-manylinux_x86_64", version = "3.9", path_str = "/opt/python/cp39-cp39" },
{ identifier = "cp310-manylinux_x86_64", version = "3.10", path_str = "/opt/python/cp310-cp310" },
+ { identifier = "cp311-manylinux_x86_64", version = "3.11", path_str = "/opt/python/cp311-cp311" },
{ identifier = "cp36-manylinux_i686", version = "3.6", path_str = "/opt/python/cp36-cp36m" },
{ identifier = "cp37-manylinux_i686", version = "3.7", path_str = "/opt/python/cp37-cp37m" },
{ identifier = "cp38-manylinux_i686", version = "3.8", path_str = "/opt/python/cp38-cp38" },
{ identifier = "cp39-manylinux_i686", version = "3.9", path_str = "/opt/python/cp39-cp39" },
{ identifier = "cp310-manylinux_i686", version = "3.10", path_str = "/opt/python/cp310-cp310" },
+ { identifier = "cp311-manylinux_i686", version = "3.11", path_str = "/opt/python/cp311-cp311" },
{ identifier = "pp37-manylinux_x86_64", version = "3.7", path_str = "/opt/python/pp37-pypy37_pp73" },
{ identifier = "pp38-manylinux_x86_64", version = "3.8", path_str = "/opt/python/pp38-pypy38_pp73" },
{ identifier = "pp39-manylinux_x86_64", version = "3.9", path_str = "/opt/python/pp39-pypy39_pp73" },
@@ -18,16 +20,19 @@ python_configurations = [
{ identifier = "cp38-manylinux_aarch64", version = "3.8", path_str = "/opt/python/cp38-cp38" },
{ identifier = "cp39-manylinux_aarch64", version = "3.9", path_str = "/opt/python/cp39-cp39" },
{ identifier = "cp310-manylinux_aarch64", version = "3.10", path_str = "/opt/python/cp310-cp310" },
+ { identifier = "cp311-manylinux_aarch64", version = "3.11", path_str = "/opt/python/cp311-cp311" },
{ identifier = "cp36-manylinux_ppc64le", version = "3.6", path_str = "/opt/python/cp36-cp36m" },
{ identifier = "cp37-manylinux_ppc64le", version = "3.7", path_str = "/opt/python/cp37-cp37m" },
{ identifier = "cp38-manylinux_ppc64le", version = "3.8", path_str = "/opt/python/cp38-cp38" },
{ identifier = "cp39-manylinux_ppc64le", version = "3.9", path_str = "/opt/python/cp39-cp39" },
{ identifier = "cp310-manylinux_ppc64le", version = "3.10", path_str = "/opt/python/cp310-cp310" },
+ { identifier = "cp311-manylinux_ppc64le", version = "3.11", path_str = "/opt/python/cp311-cp311" },
{ identifier = "cp36-manylinux_s390x", version = "3.6", path_str = "/opt/python/cp36-cp36m" },
{ identifier = "cp37-manylinux_s390x", version = "3.7", path_str = "/opt/python/cp37-cp37m" },
{ identifier = "cp38-manylinux_s390x", version = "3.8", path_str = "/opt/python/cp38-cp38" },
{ identifier = "cp39-manylinux_s390x", version = "3.9", path_str = "/opt/python/cp39-cp39" },
{ identifier = "cp310-manylinux_s390x", version = "3.10", path_str = "/opt/python/cp310-cp310" },
+ { identifier = "cp311-manylinux_s390x", version = "3.11", path_str = "/opt/python/cp311-cp311" },
{ identifier = "pp37-manylinux_aarch64", version = "3.7", path_str = "/opt/python/pp37-pypy37_pp73" },
{ identifier = "pp38-manylinux_aarch64", version = "3.8", path_str = "/opt/python/pp38-pypy38_pp73" },
{ identifier = "pp39-manylinux_aarch64", version = "3.9", path_str = "/opt/python/pp39-pypy39_pp73" },
@@ -39,26 +44,31 @@ python_configurations = [
{ identifier = "cp38-musllinux_x86_64", version = "3.8", path_str = "/opt/python/cp38-cp38" },
{ identifier = "cp39-musllinux_x86_64", version = "3.9", path_str = "/opt/python/cp39-cp39" },
{ identifier = "cp310-musllinux_x86_64", version = "3.10", path_str = "/opt/python/cp310-cp310" },
+ { identifier = "cp311-musllinux_x86_64", version = "3.11", path_str = "/opt/python/cp311-cp311" },
{ identifier = "cp36-musllinux_i686", version = "3.6", path_str = "/opt/python/cp36-cp36m" },
{ identifier = "cp37-musllinux_i686", version = "3.7", path_str = "/opt/python/cp37-cp37m" },
{ identifier = "cp38-musllinux_i686", version = "3.8", path_str = "/opt/python/cp38-cp38" },
{ identifier = "cp39-musllinux_i686", version = "3.9", path_str = "/opt/python/cp39-cp39" },
{ identifier = "cp310-musllinux_i686", version = "3.10", path_str = "/opt/python/cp310-cp310" },
+ { identifier = "cp311-musllinux_i686", version = "3.11", path_str = "/opt/python/cp311-cp311" },
{ identifier = "cp36-musllinux_aarch64", version = "3.6", path_str = "/opt/python/cp36-cp36m" },
{ identifier = "cp37-musllinux_aarch64", version = "3.7", path_str = "/opt/python/cp37-cp37m" },
{ identifier = "cp38-musllinux_aarch64", version = "3.8", path_str = "/opt/python/cp38-cp38" },
{ identifier = "cp39-musllinux_aarch64", version = "3.9", path_str = "/opt/python/cp39-cp39" },
{ identifier = "cp310-musllinux_aarch64", version = "3.10", path_str = "/opt/python/cp310-cp310" },
+ { identifier = "cp311-musllinux_aarch64", version = "3.11", path_str = "/opt/python/cp311-cp311" },
{ identifier = "cp36-musllinux_ppc64le", version = "3.6", path_str = "/opt/python/cp36-cp36m" },
{ identifier = "cp37-musllinux_ppc64le", version = "3.7", path_str = "/opt/python/cp37-cp37m" },
{ identifier = "cp38-musllinux_ppc64le", version = "3.8", path_str = "/opt/python/cp38-cp38" },
{ identifier = "cp39-musllinux_ppc64le", version = "3.9", path_str = "/opt/python/cp39-cp39" },
{ identifier = "cp310-musllinux_ppc64le", version = "3.10", path_str = "/opt/python/cp310-cp310" },
+ { identifier = "cp311-musllinux_ppc64le", version = "3.11", path_str = "/opt/python/cp311-cp311" },
{ identifier = "cp36-musllinux_s390x", version = "3.6", path_str = "/opt/python/cp36-cp36m" },
{ identifier = "cp37-musllinux_s390x", version = "3.7", path_str = "/opt/python/cp37-cp37m" },
{ identifier = "cp38-musllinux_s390x", version = "3.8", path_str = "/opt/python/cp38-cp38" },
{ identifier = "cp39-musllinux_s390x", version = "3.9", path_str = "/opt/python/cp39-cp39" },
{ identifier = "cp310-musllinux_s390x", version = "3.10", path_str = "/opt/python/cp310-cp310" },
+ { identifier = "cp311-musllinux_s390x", version = "3.11", path_str = "/opt/python/cp311-cp311" },
]
[macos]
@@ -68,12 +78,15 @@ python_configurations = [
{ identifier = "cp38-macosx_x86_64", version = "3.8", url = "https://www.python.org/ftp/python/3.8.10/python-3.8.10-macosx10.9.pkg" },
{ identifier = "cp38-macosx_arm64", version = "3.8", url = "https://www.python.org/ftp/python/3.8.10/python-3.8.10-macosx10.9.pkg" },
{ identifier = "cp38-macosx_universal2", version = "3.8", url = "https://www.python.org/ftp/python/3.8.10/python-3.8.10-macosx10.9.pkg" },
- { identifier = "cp39-macosx_x86_64", version = "3.9", url = "https://www.python.org/ftp/python/3.9.12/python-3.9.12-macos11.pkg" },
- { identifier = "cp39-macosx_arm64", version = "3.9", url = "https://www.python.org/ftp/python/3.9.12/python-3.9.12-macos11.pkg" },
- { identifier = "cp39-macosx_universal2", version = "3.9", url = "https://www.python.org/ftp/python/3.9.12/python-3.9.12-macos11.pkg" },
- { identifier = "cp310-macosx_x86_64", version = "3.10", url = "https://www.python.org/ftp/python/3.10.4/python-3.10.4-macos11.pkg" },
- { identifier = "cp310-macosx_arm64", version = "3.10", url = "https://www.python.org/ftp/python/3.10.4/python-3.10.4-macos11.pkg" },
- { identifier = "cp310-macosx_universal2", version = "3.10", url = "https://www.python.org/ftp/python/3.10.4/python-3.10.4-macos11.pkg" },
+ { identifier = "cp39-macosx_x86_64", version = "3.9", url = "https://www.python.org/ftp/python/3.9.13/python-3.9.13-macos11.pkg" },
+ { identifier = "cp39-macosx_arm64", version = "3.9", url = "https://www.python.org/ftp/python/3.9.13/python-3.9.13-macos11.pkg" },
+ { identifier = "cp39-macosx_universal2", version = "3.9", url = "https://www.python.org/ftp/python/3.9.13/python-3.9.13-macos11.pkg" },
+ { identifier = "cp310-macosx_x86_64", version = "3.10", url = "https://www.python.org/ftp/python/3.10.5/python-3.10.5-macos11.pkg" },
+ { identifier = "cp310-macosx_arm64", version = "3.10", url = "https://www.python.org/ftp/python/3.10.5/python-3.10.5-macos11.pkg" },
+ { identifier = "cp310-macosx_universal2", version = "3.10", url = "https://www.python.org/ftp/python/3.10.5/python-3.10.5-macos11.pkg" },
+ { identifier = "cp311-macosx_x86_64", version = "3.11", url = "https://www.python.org/ftp/python/3.11.0/python-3.11.0b3-macos11.pkg" },
+ { identifier = "cp311-macosx_arm64", version = "3.11", url = "https://www.python.org/ftp/python/3.11.0/python-3.11.0b3-macos11.pkg" },
+ { identifier = "cp311-macosx_universal2", version = "3.11", url = "https://www.python.org/ftp/python/3.11.0/python-3.11.0b3-macos11.pkg" },
{ identifier = "pp37-macosx_x86_64", version = "3.7", url = "https://downloads.python.org/pypy/pypy3.7-v7.3.9-osx64.tar.bz2" },
{ identifier = "pp38-macosx_x86_64", version = "3.8", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.9-osx64.tar.bz2" },
{ identifier = "pp39-macosx_x86_64", version = "3.9", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.9-osx64.tar.bz2" },
@@ -87,12 +100,15 @@ python_configurations = [
{ identifier = "cp37-win_amd64", version = "3.7.9", arch = "64" },
{ identifier = "cp38-win32", version = "3.8.10", arch = "32" },
{ identifier = "cp38-win_amd64", version = "3.8.10", arch = "64" },
- { identifier = "cp39-win32", version = "3.9.12", arch = "32" },
- { identifier = "cp39-win_amd64", version = "3.9.12", arch = "64" },
- { identifier = "cp310-win32", version = "3.10.4", arch = "32" },
- { identifier = "cp310-win_amd64", version = "3.10.4", arch = "64" },
+ { identifier = "cp39-win32", version = "3.9.13", arch = "32" },
+ { identifier = "cp39-win_amd64", version = "3.9.13", arch = "64" },
+ { identifier = "cp310-win32", version = "3.10.5", arch = "32" },
+ { identifier = "cp310-win_amd64", version = "3.10.5", arch = "64" },
+ { identifier = "cp311-win32", version = "3.11.0-b3", arch = "32" },
+ { identifier = "cp311-win_amd64", version = "3.11.0-b3", arch = "64" },
{ identifier = "cp39-win_arm64", version = "3.9.10", arch = "ARM64" },
- { identifier = "cp310-win_arm64", version = "3.10.4", arch = "ARM64" },
+ { identifier = "cp310-win_arm64", version = "3.10.5", arch = "ARM64" },
+ { identifier = "cp311-win_arm64", version = "3.11.0-b3", arch = "ARM64" },
{ identifier = "pp37-win_amd64", version = "3.7", arch = "64", url = "https://downloads.python.org/pypy/pypy3.7-v7.3.9-win64.zip" },
{ identifier = "pp38-win_amd64", version = "3.8", arch = "64", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.9-win64.zip" },
{ identifier = "pp39-win_amd64", version = "3.9", arch = "64", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.9-win64.zip" },
diff --git a/cibuildwheel/resources/constraints-python310.txt b/cibuildwheel/resources/constraints-python310.txt
index 0c016c32..9c96aa12 100644
--- a/cibuildwheel/resources/constraints-python310.txt
+++ b/cibuildwheel/resources/constraints-python310.txt
@@ -8,15 +8,15 @@ delocate==0.10.2
# via -r cibuildwheel/resources/constraints.in
distlib==0.3.4
# via virtualenv
-filelock==3.6.0
+filelock==3.7.1
# via virtualenv
-platformdirs==2.5.1
+platformdirs==2.5.2
# via virtualenv
six==1.16.0
# via virtualenv
-typing-extensions==4.1.1
+typing-extensions==4.2.0
# via delocate
-virtualenv==20.14.0
+virtualenv==20.14.1
# via -r cibuildwheel/resources/constraints.in
wheel==0.37.1
# via
@@ -24,7 +24,7 @@ wheel==0.37.1
# delocate
# The following packages are considered to be unsafe in a requirements file:
-pip==22.0.4
+pip==22.1.2
# via -r cibuildwheel/resources/constraints.in
-setuptools==61.3.1
+setuptools==62.4.0
# via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python311.txt b/cibuildwheel/resources/constraints-python311.txt
new file mode 100644
index 00000000..82e6c46d
--- /dev/null
+++ b/cibuildwheel/resources/constraints-python311.txt
@@ -0,0 +1,30 @@
+#
+# This file is autogenerated by pip-compile with python 3.11
+# To update, run:
+#
+# bin/update_dependencies.py
+#
+delocate==0.10.2
+ # via -r cibuildwheel/resources/constraints.in
+distlib==0.3.4
+ # via virtualenv
+filelock==3.7.1
+ # via virtualenv
+platformdirs==2.5.2
+ # via virtualenv
+six==1.16.0
+ # via virtualenv
+typing-extensions==4.2.0
+ # via delocate
+virtualenv==20.14.1
+ # via -r cibuildwheel/resources/constraints.in
+wheel==0.37.1
+ # via
+ # -r cibuildwheel/resources/constraints.in
+ # delocate
+
+# The following packages are considered to be unsafe in a requirements file:
+pip==22.1.2
+ # via -r cibuildwheel/resources/constraints.in
+setuptools==62.4.0
+ # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python36.txt b/cibuildwheel/resources/constraints-python36.txt
index d78f95a1..9ca0fd11 100644
--- a/cibuildwheel/resources/constraints-python36.txt
+++ b/cibuildwheel/resources/constraints-python36.txt
@@ -22,7 +22,7 @@ typing-extensions==4.1.1
# via
# delocate
# importlib-metadata
-virtualenv==20.14.0
+virtualenv==20.14.1
# via -r cibuildwheel/resources/constraints.in
wheel==0.37.1
# via
diff --git a/cibuildwheel/resources/constraints-python37.txt b/cibuildwheel/resources/constraints-python37.txt
index f5834a1b..e0a25472 100644
--- a/cibuildwheel/resources/constraints-python37.txt
+++ b/cibuildwheel/resources/constraints-python37.txt
@@ -8,19 +8,19 @@ delocate==0.10.2
# via -r cibuildwheel/resources/constraints.in
distlib==0.3.4
# via virtualenv
-filelock==3.6.0
+filelock==3.7.1
# via virtualenv
-importlib-metadata==4.11.3
+importlib-metadata==4.11.4
# via virtualenv
-platformdirs==2.5.1
+platformdirs==2.5.2
# via virtualenv
six==1.16.0
# via virtualenv
-typing-extensions==4.1.1
+typing-extensions==4.2.0
# via
# delocate
# importlib-metadata
-virtualenv==20.14.0
+virtualenv==20.14.1
# via -r cibuildwheel/resources/constraints.in
wheel==0.37.1
# via
@@ -30,7 +30,7 @@ zipp==3.8.0
# via importlib-metadata
# The following packages are considered to be unsafe in a requirements file:
-pip==22.0.4
+pip==22.1.2
# via -r cibuildwheel/resources/constraints.in
-setuptools==61.3.1
+setuptools==62.4.0
# via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python38.txt b/cibuildwheel/resources/constraints-python38.txt
index 3bbb3c3c..971a8e42 100644
--- a/cibuildwheel/resources/constraints-python38.txt
+++ b/cibuildwheel/resources/constraints-python38.txt
@@ -8,15 +8,15 @@ delocate==0.10.2
# via -r cibuildwheel/resources/constraints.in
distlib==0.3.4
# via virtualenv
-filelock==3.6.0
+filelock==3.7.1
# via virtualenv
-platformdirs==2.5.1
+platformdirs==2.5.2
# via virtualenv
six==1.16.0
# via virtualenv
-typing-extensions==4.1.1
+typing-extensions==4.2.0
# via delocate
-virtualenv==20.14.0
+virtualenv==20.14.1
# via -r cibuildwheel/resources/constraints.in
wheel==0.37.1
# via
@@ -24,7 +24,7 @@ wheel==0.37.1
# delocate
# The following packages are considered to be unsafe in a requirements file:
-pip==22.0.4
+pip==22.1.2
# via -r cibuildwheel/resources/constraints.in
-setuptools==61.3.1
+setuptools==62.4.0
# via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python39.txt b/cibuildwheel/resources/constraints-python39.txt
index 84f949c6..8f6d1c00 100644
--- a/cibuildwheel/resources/constraints-python39.txt
+++ b/cibuildwheel/resources/constraints-python39.txt
@@ -8,15 +8,15 @@ delocate==0.10.2
# via -r cibuildwheel/resources/constraints.in
distlib==0.3.4
# via virtualenv
-filelock==3.6.0
+filelock==3.7.1
# via virtualenv
-platformdirs==2.5.1
+platformdirs==2.5.2
# via virtualenv
six==1.16.0
# via virtualenv
-typing-extensions==4.1.1
+typing-extensions==4.2.0
# via delocate
-virtualenv==20.14.0
+virtualenv==20.14.1
# via -r cibuildwheel/resources/constraints.in
wheel==0.37.1
# via
@@ -24,7 +24,7 @@ wheel==0.37.1
# delocate
# The following packages are considered to be unsafe in a requirements file:
-pip==22.0.4
+pip==22.1.2
# via -r cibuildwheel/resources/constraints.in
-setuptools==61.3.1
+setuptools==62.4.0
# via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints.txt b/cibuildwheel/resources/constraints.txt
index 84f949c6..8f6d1c00 100644
--- a/cibuildwheel/resources/constraints.txt
+++ b/cibuildwheel/resources/constraints.txt
@@ -8,15 +8,15 @@ delocate==0.10.2
# via -r cibuildwheel/resources/constraints.in
distlib==0.3.4
# via virtualenv
-filelock==3.6.0
+filelock==3.7.1
# via virtualenv
-platformdirs==2.5.1
+platformdirs==2.5.2
# via virtualenv
six==1.16.0
# via virtualenv
-typing-extensions==4.1.1
+typing-extensions==4.2.0
# via delocate
-virtualenv==20.14.0
+virtualenv==20.14.1
# via -r cibuildwheel/resources/constraints.in
wheel==0.37.1
# via
@@ -24,7 +24,7 @@ wheel==0.37.1
# delocate
# The following packages are considered to be unsafe in a requirements file:
-pip==22.0.4
+pip==22.1.2
# via -r cibuildwheel/resources/constraints.in
-setuptools==61.3.1
+setuptools==62.4.0
# via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/pinned_docker_images.cfg b/cibuildwheel/resources/pinned_docker_images.cfg
index 0b30242e..0a7c6592 100644
--- a/cibuildwheel/resources/pinned_docker_images.cfg
+++ b/cibuildwheel/resources/pinned_docker_images.cfg
@@ -1,43 +1,48 @@
[x86_64]
-manylinux1 = quay.io/pypa/manylinux1_x86_64:2022-04-03-9524524
-manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-04-03-da6ecb3
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2022-04-03-da6ecb3
-manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-04-03-da6ecb3
-musllinux_1_1 = quay.io/pypa/musllinux_1_1_x86_64:2022-04-03-da6ecb3
+manylinux1 = quay.io/pypa/manylinux1_x86_64:2022-06-12-a846b05
+manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-06-13-c365205
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2022-06-13-c365205
+manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-06-13-c365205
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2022-06-13-c365205
+musllinux_1_1 = quay.io/pypa/musllinux_1_1_x86_64:2022-06-13-c365205
[i686]
-manylinux1 = quay.io/pypa/manylinux1_i686:2022-04-03-9524524
-manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-04-03-da6ecb3
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2022-04-03-da6ecb3
-manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-04-03-da6ecb3
-musllinux_1_1 = quay.io/pypa/musllinux_1_1_i686:2022-04-03-da6ecb3
+manylinux1 = quay.io/pypa/manylinux1_i686:2022-06-12-a846b05
+manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-06-13-c365205
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2022-06-13-c365205
+manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-06-13-c365205
+musllinux_1_1 = quay.io/pypa/musllinux_1_1_i686:2022-06-13-c365205
[pypy_x86_64]
-manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-04-03-da6ecb3
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2022-04-03-da6ecb3
-manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-04-03-da6ecb3
+manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-06-13-c365205
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2022-06-13-c365205
+manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-06-13-c365205
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2022-06-13-c365205
[pypy_i686]
-manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-04-03-da6ecb3
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2022-04-03-da6ecb3
-manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-04-03-da6ecb3
+manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-06-13-c365205
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2022-06-13-c365205
+manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-06-13-c365205
[aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2022-04-03-da6ecb3
-manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2022-04-03-da6ecb3
-musllinux_1_1 = quay.io/pypa/musllinux_1_1_aarch64:2022-04-03-da6ecb3
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2022-06-13-c365205
+manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2022-06-13-c365205
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2022-06-13-c365205
+musllinux_1_1 = quay.io/pypa/musllinux_1_1_aarch64:2022-06-13-c365205
[ppc64le]
-manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2022-04-03-da6ecb3
-manylinux_2_24 = quay.io/pypa/manylinux_2_24_ppc64le:2022-04-03-da6ecb3
-musllinux_1_1 = quay.io/pypa/musllinux_1_1_ppc64le:2022-04-03-da6ecb3
+manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2022-06-13-c365205
+manylinux_2_24 = quay.io/pypa/manylinux_2_24_ppc64le:2022-06-13-c365205
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2022-06-13-c365205
+musllinux_1_1 = quay.io/pypa/musllinux_1_1_ppc64le:2022-06-13-c365205
[s390x]
-manylinux2014 = quay.io/pypa/manylinux2014_s390x:2022-04-03-da6ecb3
-manylinux_2_24 = quay.io/pypa/manylinux_2_24_s390x:2022-04-03-da6ecb3
-musllinux_1_1 = quay.io/pypa/musllinux_1_1_s390x:2022-04-03-da6ecb3
+manylinux2014 = quay.io/pypa/manylinux2014_s390x:2022-06-13-c365205
+manylinux_2_24 = quay.io/pypa/manylinux_2_24_s390x:2022-06-13-c365205
+musllinux_1_1 = quay.io/pypa/musllinux_1_1_s390x:2022-06-13-c365205
[pypy_aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2022-04-03-da6ecb3
-manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2022-04-03-da6ecb3
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2022-06-13-c365205
+manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2022-06-13-c365205
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2022-06-13-c365205
diff --git a/cibuildwheel/resources/virtualenv.toml b/cibuildwheel/resources/virtualenv.toml
index 4c781dbf..2296e59f 100644
--- a/cibuildwheel/resources/virtualenv.toml
+++ b/cibuildwheel/resources/virtualenv.toml
@@ -1,2 +1,2 @@
-version = "20.13.3"
-url = "https://github.com/pypa/get-virtualenv/blob/20.13.3/public/virtualenv.pyz?raw=true"
+version = "20.14.1"
+url = "https://github.com/pypa/get-virtualenv/blob/20.14.1/public/virtualenv.pyz?raw=true"
diff --git a/cibuildwheel/util.py b/cibuildwheel/util.py
index 5ce85bf8..9bc151cc 100644
--- a/cibuildwheel/util.py
+++ b/cibuildwheel/util.py
@@ -13,18 +13,20 @@ import urllib.request
from dataclasses import dataclass
from enum import Enum
from functools import lru_cache
-from pathlib import Path
+from pathlib import Path, PurePath
from time import sleep
from typing import (
Any,
ClassVar,
Dict,
+ Generator,
Iterable,
- Iterator,
List,
Optional,
Sequence,
TextIO,
+ TypeVar,
+ Union,
cast,
overload,
)
@@ -40,6 +42,7 @@ else:
from filelock import FileLock
from packaging.requirements import InvalidRequirement, Requirement
from packaging.specifiers import SpecifierSet
+from packaging.utils import parse_wheel_filename
from packaging.version import Version
from platformdirs import user_cache_path
@@ -50,6 +53,7 @@ __all__ = [
"MANYLINUX_ARCHS",
"call",
"shell",
+ "find_compatible_abi3_wheel",
"format_safe",
"prepare_command",
"get_build_verbosity_extra_flags",
@@ -57,6 +61,7 @@ __all__ = [
"selector_matches",
"strtobool",
"cached_property",
+ "chdir",
]
resources_dir: Final = Path(__file__).parent / "resources"
@@ -228,21 +233,20 @@ def selector_matches(patterns: str, string: str) -> bool:
# Once we require Python 3.10+, we can add kw_only=True
@dataclass(frozen=True)
-class IdentifierSelector:
+class BuildSelector:
"""
This class holds a set of build/skip patterns. You call an instance with a
build identifier, and it returns True if that identifier should be
included. Only call this on valid identifiers, ones that have at least 2
- numeric digits before the first dash. If a pre-release version X.Y is present,
- you can filter it with prerelease="XY".
+ numeric digits before the first dash.
"""
- # a pattern that skips prerelease versions, when include_prereleases is False.
- PRERELEASE_SKIP: ClassVar[str] = ""
-
- skip_config: str
build_config: str
+ skip_config: str
requires_python: Optional[SpecifierSet] = None
+
+ # a pattern that skips prerelease versions, when include_prereleases is False.
+ PRERELEASE_SKIP: ClassVar[str] = "cp311-*"
prerelease_pythons: bool = False
def __call__(self, build_id: str) -> bool:
@@ -266,15 +270,16 @@ class IdentifierSelector:
@dataclass(frozen=True)
-class BuildSelector(IdentifierSelector):
- pass
+class TestSelector:
+ """
+ A build selector that can only skip tests according to a skip pattern.
+ """
+ skip_config: str
-# Note that requires-python is not needed for TestSelector, as you can't test
-# what you can't build.
-@dataclass(frozen=True)
-class TestSelector(IdentifierSelector):
- build_config: str = "*"
+ def __call__(self, build_id: str) -> bool:
+ should_skip = selector_matches(self.skip_config, build_id)
+ return not should_skip
# Taken from https://stackoverflow.com/a/107717
@@ -419,7 +424,7 @@ class FileReport:
@contextlib.contextmanager
-def print_new_wheels(msg: str, output_dir: Path) -> Iterator[None]:
+def print_new_wheels(msg: str, output_dir: Path) -> Generator[None, None, None]:
"""
Prints the new items in a directory upon exiting. The message to display
can include {n} for number of wheels, {s} for total number of seconds,
@@ -567,7 +572,56 @@ def virtualenv(
return env
+T = TypeVar("T", bound=PurePath)
+
+
+def find_compatible_abi3_wheel(wheels: Sequence[T], identifier: str) -> Optional[T]:
+ """
+ Finds an ABI3 wheel in `wheels` compatible with the Python interpreter
+ specified by `identifier`.
+ """
+
+ interpreter, platform = identifier.split("-")
+ if not interpreter.startswith("cp3"):
+ return None
+ for wheel in wheels:
+ _, _, _, tags = parse_wheel_filename(wheel.name)
+ for tag in tags:
+ if tag.abi != "abi3":
+ continue
+ if not tag.interpreter.startswith("cp3"):
+ continue
+ if int(tag.interpreter[3:]) > int(interpreter[3:]):
+ continue
+ if platform.startswith(("manylinux", "musllinux", "macosx")):
+ # Linux, macOS
+ os_, arch = platform.split("_", 1)
+ if not tag.platform.startswith(os_):
+ continue
+ if not tag.platform.endswith("_" + arch):
+ continue
+ else:
+ # Windows
+ if not tag.platform == platform:
+ continue
+ return wheel
+ return None
+
+
if sys.version_info >= (3, 8):
from functools import cached_property
else:
from .functools_cached_property_38 import cached_property
+
+
+# Can be replaced by contextlib.chdir in Python 3.11
+@contextlib.contextmanager
+def chdir(new_path: Union[Path, str]) -> Generator[None, None, None]:
+ """Non thread-safe context manager to change the current working directory."""
+
+ cwd = os.getcwd()
+ try:
+ os.chdir(new_path)
+ yield
+ finally:
+ os.chdir(cwd)
diff --git a/cibuildwheel/windows.py b/cibuildwheel/windows.py
index ef9742ee..0ad2e2ae 100644
--- a/cibuildwheel/windows.py
+++ b/cibuildwheel/windows.py
@@ -23,6 +23,7 @@ from .util import (
NonPlatformWheelError,
call,
download,
+ find_compatible_abi3_wheel,
get_build_verbosity_extra_flags,
get_pip_version,
prepare_command,
@@ -251,6 +252,8 @@ def build(options: Options, tmp_path: Path) -> None:
)
shell(before_all_prepared, env=env)
+ built_wheels: List[Path] = []
+
for config in python_configurations:
build_options = options.build_options(config.identifier)
log.build_start(config.identifier)
@@ -276,83 +279,93 @@ def build(options: Options, tmp_path: Path) -> None:
build_options.build_frontend,
)
- # run the before_build command
- if build_options.before_build:
- log.step("Running before_build...")
- before_build_prepared = prepare_command(
- build_options.before_build, project=".", package=options.globals.package_dir
+ abi3_wheel = find_compatible_abi3_wheel(built_wheels, config.identifier)
+ if abi3_wheel:
+ log.step_end()
+ print(
+ f"\nFound previously built wheel {abi3_wheel.name}, that's compatible with {config.identifier}. Skipping build step..."
)
- shell(before_build_prepared, env=env)
-
- log.step("Building wheel...")
- built_wheel_dir.mkdir()
-
- verbosity_flags = get_build_verbosity_extra_flags(build_options.build_verbosity)
-
- if build_options.build_frontend == "pip":
- # Path.resolve() is needed. Without it pip wheel may try to fetch package from pypi.org
- # see https://github.com/pypa/cibuildwheel/pull/369
- call(
- "python",
- "-m",
- "pip",
- "wheel",
- options.globals.package_dir.resolve(),
- f"--wheel-dir={built_wheel_dir}",
- "--no-deps",
- *get_build_verbosity_extra_flags(build_options.build_verbosity),
- env=env,
- )
- elif build_options.build_frontend == "build":
- config_setting = " ".join(verbosity_flags)
- build_env = env.copy()
- if build_options.dependency_constraints:
- constraints_path = build_options.dependency_constraints.get_for_python_version(
- config.version
+ repaired_wheel = abi3_wheel
+ else:
+ # run the before_build command
+ if build_options.before_build:
+ log.step("Running before_build...")
+ before_build_prepared = prepare_command(
+ build_options.before_build, project=".", package=options.globals.package_dir
)
- # Bug in pip <= 21.1.3 - we can't have a space in the
- # constraints file, and pip doesn't support drive letters
- # in uhi. After probably pip 21.2, we can use uri. For
- # now, use a temporary file.
- if " " in str(constraints_path):
- assert " " not in str(identifier_tmp_dir)
- tmp_file = identifier_tmp_dir / "constraints.txt"
- tmp_file.write_bytes(constraints_path.read_bytes())
- constraints_path = tmp_file
+ shell(before_build_prepared, env=env)
- build_env["PIP_CONSTRAINT"] = str(constraints_path)
- build_env["VIRTUALENV_PIP"] = get_pip_version(env)
+ log.step("Building wheel...")
+ built_wheel_dir.mkdir()
+
+ verbosity_flags = get_build_verbosity_extra_flags(build_options.build_verbosity)
+
+ if build_options.build_frontend == "pip":
+ # Path.resolve() is needed. Without it pip wheel may try to fetch package from pypi.org
+ # see https://github.com/pypa/cibuildwheel/pull/369
call(
"python",
"-m",
- "build",
- build_options.package_dir,
- "--wheel",
- f"--outdir={built_wheel_dir}",
- f"--config-setting={config_setting}",
- env=build_env,
+ "pip",
+ "wheel",
+ options.globals.package_dir.resolve(),
+ f"--wheel-dir={built_wheel_dir}",
+ "--no-deps",
+ *get_build_verbosity_extra_flags(build_options.build_verbosity),
+ env=env,
)
- else:
- assert_never(build_options.build_frontend)
+ elif build_options.build_frontend == "build":
+ config_setting = " ".join(verbosity_flags)
+ build_env = env.copy()
+ if build_options.dependency_constraints:
+ constraints_path = (
+ build_options.dependency_constraints.get_for_python_version(
+ config.version
+ )
+ )
+ # Bug in pip <= 21.1.3 - we can't have a space in the
+ # constraints file, and pip doesn't support drive letters
+ # in uhi. After probably pip 21.2, we can use uri. For
+ # now, use a temporary file.
+ if " " in str(constraints_path):
+ assert " " not in str(identifier_tmp_dir)
+ tmp_file = identifier_tmp_dir / "constraints.txt"
+ tmp_file.write_bytes(constraints_path.read_bytes())
+ constraints_path = tmp_file
- built_wheel = next(built_wheel_dir.glob("*.whl"))
+ build_env["PIP_CONSTRAINT"] = str(constraints_path)
+ build_env["VIRTUALENV_PIP"] = get_pip_version(env)
+ call(
+ "python",
+ "-m",
+ "build",
+ build_options.package_dir,
+ "--wheel",
+ f"--outdir={built_wheel_dir}",
+ f"--config-setting={config_setting}",
+ env=build_env,
+ )
+ else:
+ assert_never(build_options.build_frontend)
- # repair the wheel
- repaired_wheel_dir.mkdir()
+ built_wheel = next(built_wheel_dir.glob("*.whl"))
- if built_wheel.name.endswith("none-any.whl"):
- raise NonPlatformWheelError()
+ # repair the wheel
+ repaired_wheel_dir.mkdir()
- if build_options.repair_command:
- log.step("Repairing wheel...")
- repair_command_prepared = prepare_command(
- build_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)
+ if built_wheel.name.endswith("none-any.whl"):
+ raise NonPlatformWheelError()
- repaired_wheel = next(repaired_wheel_dir.glob("*.whl"))
+ if build_options.repair_command:
+ log.step("Repairing wheel...")
+ repair_command_prepared = prepare_command(
+ build_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)
+
+ repaired_wheel = next(repaired_wheel_dir.glob("*.whl"))
if build_options.test_command and options.globals.test_selector(config.identifier):
log.step("Testing wheel...")
@@ -407,7 +420,9 @@ def build(options: Options, tmp_path: Path) -> None:
shell(test_command_prepared, cwd="c:\\", env=virtualenv_env)
# we're all done here; move it to output (remove if already exists)
- shutil.move(str(repaired_wheel), build_options.output_dir)
+ if abi3_wheel is None:
+ shutil.move(str(repaired_wheel), build_options.output_dir)
+ built_wheels.append(build_options.output_dir / repaired_wheel.name)
# clean up
# (we ignore errors because occasionally Windows fails to unlink a file and we
diff --git a/docs/changelog.md b/docs/changelog.md
index 79b03c76..7f6f2101 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -2,6 +2,29 @@
title: Changelog
---
+### v2.6.1
+
+_7 June 2022_
+
+- 🛠 Update the prerelease CPython 3.11 to 3.11.0b3
+
+### v2.6.0
+
+_25 May 2022_
+
+- 🌟 Added the ability to test building wheels on CPython 3.11! Because CPython 3.11 is in beta, these wheels should not be distributed, because they might not be compatible with the final release, but it's available to build for testing purposes. Use the flag [`--prerelease-pythons` or `CIBW_PRERELEASE_PYTHONS`](https://cibuildwheel.readthedocs.io/en/stable/options/#prerelease-pythons) to test. This version of cibuildwheel includes CPython 3.11.0b1. (#1109)
+- 📚 Added an interactive diagram showing how cibuildwheel works to the [docs](https://cibuildwheel.readthedocs.io/en/stable/#how-it-works) (#1100)
+
+### v2.5.0
+
+_29 April 2022_
+
+- ✨ Added support for building ABI3 wheels. cibuildwheel will now recognise when an ABI3 wheel was produced, and skip subsequent build steps where the previously built wheel is compatible. Tests still will run on all selected versions of Python, using the ABI3 wheel. Check [this entry](https://cibuildwheel.readthedocs.io/en/stable/faq/#abi3) in the docs for more info. (#1091)
+- ✨ You can now build wheels directly from sdist archives, in addition to source directories. Just call cibuildwheel with an sdist argument on the command line, like `cibuildwheel mypackage-1.0.0.tar.gz`. For more details, check the [`--help` output](https://cibuildwheel.readthedocs.io/en/stable/options/#command-line) (#1096)
+- 🐛 Fix a bug where cibuildwheel would crash when no builds are selected and `--allow-empty` is passed (#1086)
+- 🐛 Workaround a permissions issue on Linux relating to newer versions of git and setuptools_scm (#1095)
+- 📚 Minor docs improvements
+
### v2.4.0
_2 April 2022_
diff --git a/docs/cpp_standards.md b/docs/cpp_standards.md
index 1f140618..468e4eb1 100644
--- a/docs/cpp_standards.md
+++ b/docs/cpp_standards.md
@@ -14,7 +14,7 @@ The old `manylinux1` image (based on CentOS 5) contains a version of GCC and lib
OS X/macOS allows you to specify a so-called "deployment target" version that will ensure backwards compatibility with older versions of macOS. One way to do this is by setting the `MACOSX_DEPLOYMENT_TARGET` environment variable.
-However, to enable modern C++ standards, the deploment target needs to be set high enough (since older OS X/macOS versions did not have the necessary modern C++ standard library).
+However, to enable modern C++ standards, the deployment target needs to be set high enough (since older OS X/macOS versions did not have the necessary modern C++ standard library).
To get C++11 and C++14 support, `MACOSX_DEPLOYMENT_TARGET` needs to be set to (at least) `"10.9"`. By default, `cibuildwheel` already does this, building 64-bit-only wheels for macOS 10.9 and later.
diff --git a/docs/data/how-it-works.png b/docs/data/how-it-works.png
new file mode 100644
index 00000000..0306280a
Binary files /dev/null and b/docs/data/how-it-works.png differ
diff --git a/docs/data/projects.yml b/docs/data/projects.yml
index e12bd99b..7e8c544a 100644
--- a/docs/data/projects.yml
+++ b/docs/data/projects.yml
@@ -287,7 +287,7 @@
pypi: mypy
ci: [github]
os: [apple, linux, windows]
- notes: MyPyC, the compiled component of MyPy.
+ notes: The compiled version of MyPy using MyPyC.
- name: Imagecodecs (fork)
gh: czaki/imagecodecs_build
@@ -521,10 +521,71 @@
gh: arbor-sim/arbor
ci: [github]
os: [apple, linux]
- pypi: arbor
notes: >
Arbor is a multi-compartment neuron simulation library; compatible with
next-generation accelerators; best-practices applied to research software;
focused on community-driven development. Includes a
[small script](https://github.com/arbor-sim/arbor/blob/master/scripts/patchwheel.py)
patching `rpath` in bundled libraries.
+
+- name: Kivy
+ gh: kivy/kivy
+ ci: [github]
+ os: [windows, apple, linux]
+
+- name: NCNN
+ gh: Tencent/ncnn
+ ci: [github]
+ os: [windows, apple, linux]
+
+- name: Prophet
+ gh: facebook/prophet
+ ci: [github]
+ os: [windows, apple, linux]
+
+- name: MemRay
+ gh: bloomberg/memray
+ ci: [github]
+ os: [linux]
+
+- name: PyGame
+ gh: pygame/pygame
+ ci: [github]
+ os: [apple, linux]
+
+- name: UltraJSON
+ gh: ultrajson/ultrajson
+ ci: [github]
+ os: [windows, apple, linux]
+
+- name: NumPy
+ gh: numpy/numpy
+ ci: [github, travisci]
+ os: [windows, apple, linux]
+
+- name: Wrapt
+ gh: GrahamDumpleton/wrapt
+ ci: [github]
+ os: [windows, apple, linux]
+
+- name: SimpleJSON
+ gh: simplejson/simplejson
+ ci: [github]
+ os: [windows, apple, linux]
+
+- name: Implicit
+ gh: benfred/implicit
+ ci: [github]
+ os: [windows, apple, linux]
+ notes: Includes GPU support for linux wheels
+
+- name: power-grid-model
+ gh: alliander-opensource/power-grid-model
+ ci: [github]
+ os: [windows, apple, linux]
+ notes: Python/C++ library for distribution power system analysis
+
+- name: Python-WebRTC
+ gh: MarshalX/python-webrtc
+ ci: [github]
+ os: [windows, apple, linux]
diff --git a/docs/diagram.md b/docs/diagram.md
new file mode 100644
index 00000000..d0557b4f
--- /dev/null
+++ b/docs/diagram.md
@@ -0,0 +1,487 @@
+