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. + +![](docs/data/how-it-works.png) + +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 @@ +
+
+ + +
+
+
+ +
+
+ Manylinux Docker container +
+
+ +
+
Test virtualenv
+
+
+
+ + +
Linux
+
macOS
+
Windows
+ +
+
For each version of Python
+
+
+
If tests are configured
+
+ +
+ +
+
+
+
+
+
+
+ {{action.label}} +
+
+ +
+
+
+ + + + + + diff --git a/docs/extra.css b/docs/extra.css index e3599627..a971d398 100644 --- a/docs/extra.css +++ b/docs/extra.css @@ -3,6 +3,7 @@ body { overflow-wrap: break-word; + overflow-x: hidden; } p { diff --git a/docs/faq.md b/docs/faq.md index 97e65feb..34a64e6e 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -103,6 +103,12 @@ Linux), and the other architectures are emulated automatically. {% include "../examples/github-with-qemu.yml" %} ``` +### Building CPython ABI3 wheels (Limited API) {: #abi3} + +The CPython Limited API is a subset of the Python C Extension API that's declared to be forward-compatible, meaning you can compile wheels for one version of Python, and they'll be compatible with future versions. Wheels that use the Limited API are known as ABI3 wheels. + +To create a package that builds ABI3 wheels, you'll need to configure your build backend to compile libraries correctly create wheels with the right tags. [Check this repo](https://github.com/joerick/python-abi3-package-sample) for an example of how to do this with setuptools. + ### Building packages with optional C extensions `cibuildwheel` defines the environment variable `CIBUILDWHEEL` to the value `1` allowing projects for which the C extension is optional to make it mandatory when building wheels. @@ -127,7 +133,7 @@ There are two suggested methods for keeping cibuildwheel up to date that instead If you use GitHub Actions for builds, you can use cibuildwheel as an action: ```yaml -uses: pypa/cibuildwheel@v2.4.0 +uses: pypa/cibuildwheel@v2.6.1 ``` This is a composite step that just runs cibuildwheel using pipx. You can set command-line options as `with:` parameters, and use `env:` as normal. @@ -153,7 +159,7 @@ The second option, and the only one that supports other CI systems, is using a ` ```bash # requirements-cibw.txt -cibuildwheel==2.4.0 +cibuildwheel==2.6.1 ``` Then your install step would have `python -m pip install -r requirements-cibw.txt` in it. Your `.github/dependabot.yml` file could look like this: diff --git a/docs/index.md b/docs/index.md index 6cc7ccfd..897eaa10 100644 --- a/docs/index.md +++ b/docs/index.md @@ -9,3 +9,14 @@ title: Home %} To get started, head over to the [setup guide](setup.md). + +How it works +------------ + +This diagram summarises the steps that cibuildwheel takes on each platform to build your package's wheels. + +{% + include-markdown "diagram.md" +%} + +This isn't exhaustive, for a full list of the things cibuildwheel can do, check the [options](options.md) page. diff --git a/docs/options.md b/docs/options.md index c9d7b742..f72b388a 100644 --- a/docs/options.md +++ b/docs/options.md @@ -202,16 +202,17 @@ When setting the options, you can use shell-style globbing syntax, as per [fnmat
-| | macOS | Windows | Linux Intel | Linux Other | -|--------------|------------------------------------------------------------------------|----------------------------------|-----------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Python 3.6 | cp36-macosx_x86_64 | cp36-win_amd64
cp36-win32 | cp36-manylinux_x86_64
cp36-manylinux_i686
cp36-musllinux_x86_64
cp36-musllinux_i686 | cp36-manylinux_aarch64
cp36-manylinux_ppc64le
cp36-manylinux_s390x
cp36-musllinux_aarch64
cp36-musllinux_ppc64le
cp36-musllinux_s390x | -| Python 3.7 | cp37-macosx_x86_64 | cp37-win_amd64
cp37-win32 | cp37-manylinux_x86_64
cp37-manylinux_i686
cp37-musllinux_x86_64
cp37-musllinux_i686 | cp37-manylinux_aarch64
cp37-manylinux_ppc64le
cp37-manylinux_s390x
cp37-musllinux_aarch64
cp37-musllinux_ppc64le
cp37-musllinux_s390x | -| Python 3.8 | cp38-macosx_x86_64
cp38-macosx_universal2
cp38-macosx_arm64 | cp38-win_amd64
cp38-win32 | cp38-manylinux_x86_64
cp38-manylinux_i686
cp38-musllinux_x86_64
cp38-musllinux_i686 | cp38-manylinux_aarch64
cp38-manylinux_ppc64le
cp38-manylinux_s390x
cp38-musllinux_aarch64
cp38-musllinux_ppc64le
cp38-musllinux_s390x | +| | macOS | Windows | Linux Intel | Linux Other | +|--------------|------------------------------------------------------------------------|-----------------------------------------------------|-----------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Python 3.6 | cp36-macosx_x86_64 | cp36-win_amd64
cp36-win32 | cp36-manylinux_x86_64
cp36-manylinux_i686
cp36-musllinux_x86_64
cp36-musllinux_i686 | cp36-manylinux_aarch64
cp36-manylinux_ppc64le
cp36-manylinux_s390x
cp36-musllinux_aarch64
cp36-musllinux_ppc64le
cp36-musllinux_s390x | +| Python 3.7 | cp37-macosx_x86_64 | cp37-win_amd64
cp37-win32 | cp37-manylinux_x86_64
cp37-manylinux_i686
cp37-musllinux_x86_64
cp37-musllinux_i686 | cp37-manylinux_aarch64
cp37-manylinux_ppc64le
cp37-manylinux_s390x
cp37-musllinux_aarch64
cp37-musllinux_ppc64le
cp37-musllinux_s390x | +| Python 3.8 | cp38-macosx_x86_64
cp38-macosx_universal2
cp38-macosx_arm64 | cp38-win_amd64
cp38-win32 | cp38-manylinux_x86_64
cp38-manylinux_i686
cp38-musllinux_x86_64
cp38-musllinux_i686 | cp38-manylinux_aarch64
cp38-manylinux_ppc64le
cp38-manylinux_s390x
cp38-musllinux_aarch64
cp38-musllinux_ppc64le
cp38-musllinux_s390x | | Python 3.9 | cp39-macosx_x86_64
cp39-macosx_universal2
cp39-macosx_arm64 | cp39-win_amd64
cp39-win32
cp39-win_arm64 | cp39-manylinux_x86_64
cp39-manylinux_i686
cp39-musllinux_x86_64
cp39-musllinux_i686 | cp39-manylinux_aarch64
cp39-manylinux_ppc64le
cp39-manylinux_s390x
cp39-musllinux_aarch64
cp39-musllinux_ppc64le
cp39-musllinux_s390x | -| Python 3.10 | cp310-macosx_x86_64
cp310-macosx_universal2
cp310-macosx_arm64 | cp310-win_amd64
cp310-win32
cp310-win_arm64 | cp310-manylinux_x86_64
cp310-manylinux_i686
cp310-musllinux_x86_64
cp310-musllinux_i686 | cp310-manylinux_aarch64
cp310-manylinux_ppc64le
cp310-manylinux_s390x
cp310-musllinux_aarch64
cp310-musllinux_ppc64le
cp310-musllinux_s390x | -| PyPy3.7 v7.3 | pp37-macosx_x86_64 | pp37-win_amd64 | pp37-manylinux_x86_64
pp37-manylinux_i686 | pp37-manylinux_aarch64 | -| PyPy3.8 v7.3 | pp38-macosx_x86_64 | pp38-win_amd64 | pp38-manylinux_x86_64
pp38-manylinux_i686 | pp38-manylinux_aarch64 | -| PyPy3.9 v7.3 | pp39-macosx_x86_64 | pp39-win_amd64 | pp39-manylinux_x86_64
pp39-manylinux_i686 | pp39-manylinux_aarch64 | +| Python 3.10 | cp310-macosx_x86_64
cp310-macosx_universal2
cp310-macosx_arm64 | cp310-win_amd64
cp310-win32
cp310-win_arm64 | cp310-manylinux_x86_64
cp310-manylinux_i686
cp310-musllinux_x86_64
cp310-musllinux_i686 | cp310-manylinux_aarch64
cp310-manylinux_ppc64le
cp310-manylinux_s390x
cp310-musllinux_aarch64
cp310-musllinux_ppc64le
cp310-musllinux_s390x | +| Python 3.11 | cp311-macosx_x86_64
cp311-macosx_universal2
cp311-macosx_arm64 | cp311-win_amd64
cp311-win32
cp311-win_arm64 | cp311-manylinux_x86_64
cp311-manylinux_i686
cp311-musllinux_x86_64
cp311-musllinux_i686 | cp311-manylinux_aarch64
cp311-manylinux_ppc64le
cp311-manylinux_s390x
cp311-musllinux_aarch64
cp311-musllinux_ppc64le
cp311-musllinux_s390x | +| PyPy3.7 v7.3 | pp37-macosx_x86_64 | pp37-win_amd64 | pp37-manylinux_x86_64
pp37-manylinux_i686 | pp37-manylinux_aarch64 | +| PyPy3.8 v7.3 | pp38-macosx_x86_64 | pp38-win_amd64 | pp38-manylinux_x86_64
pp38-manylinux_i686 | pp38-manylinux_aarch64 | +| PyPy3.9 v7.3 | pp39-macosx_x86_64 | pp39-win_amd64 | pp39-manylinux_x86_64
pp39-manylinux_i686 | pp39-manylinux_aarch64 | The list of supported and currently selected build identifiers can also be retrieved by passing the `--print-build-identifiers` flag to cibuildwheel. The format is `python_tag-platform_tag`, with tags similar to those in [PEP 425](https://www.python.org/dev/peps/pep-0425/#details). @@ -783,7 +784,7 @@ Platform-specific environment variables are also available:
### `CIBW_REPAIR_WHEEL_COMMAND` {: #repair-wheel-command} -> Execute a shell command to repair each (non-pure Python) built wheel +> Execute a shell command to repair each built wheel Default: @@ -885,14 +886,14 @@ The available options are (default value): Set an alternative Docker image to be used for building [manylinux / musllinux](https://github.com/pypa/manylinux) wheels. -For `CIBW_MANYLINUX_*_IMAGE`, the value of this option can either be set to `manylinux1`, `manylinux2010`, `manylinux2014` or `manylinux_2_24` to use a pinned version of the [official manylinux images](https://github.com/pypa/manylinux). Alternatively, set these options to any other valid Docker image name. For PyPy, the `manylinux1` image is not available. For architectures other -than x86 (x86\_64 and i686) `manylinux2014` or `manylinux_2_24` must be used, because the first version of the manylinux specification that supports additional architectures is `manylinux2014`. +For `CIBW_MANYLINUX_*_IMAGE`, the value of this option can either be set to `manylinux1`, `manylinux2010`, `manylinux2014`, `manylinux_2_24` or `manylinux_2_28` to use a pinned version of the [official manylinux images](https://github.com/pypa/manylinux). Alternatively, set these options to any other valid Docker image name. For PyPy, the `manylinux1` image is not available. For architectures other +than x86 (x86\_64 and i686) `manylinux2014`, `manylinux_2_24` or `manylinux_2_28` must be used, because the first version of the manylinux specification that supports additional architectures is `manylinux2014`. `manylinux_2_28` is not supported for `i686` & `s390x` architectures. For `CIBW_MUSLLINUX_*_IMAGE`, the value of this option can either be set to `musllinux_1_1` to use a pinned version of the [official musllinux images](https://github.com/pypa/musllinux). Alternatively, set these options to any other valid Docker image name. If this option is blank, it will fall though to the next available definition (environment variable -> pyproject.toml -> default). -If setting a custom Docker image, you'll need to make sure it can be used in the same way as the official, default Docker images: all necessary Python and pip versions need to be present in `/opt/python/`, and the auditwheel tool needs to be present for cibuildwheel to work. Apart from that, the architecture and relevant shared system libraries need to be compatible to the relevant standard to produce valid manylinux1/manylinux2010/manylinux2014/manylinux_2_24/musllinux_1_1 wheels (see [pypa/manylinux on GitHub](https://github.com/pypa/manylinux), [PEP 513](https://www.python.org/dev/peps/pep-0513/), [PEP 571](https://www.python.org/dev/peps/pep-0571/), [PEP 599](https://www.python.org/dev/peps/pep-0599/), [PEP 600](https://www.python.org/dev/peps/pep-0600/) and [PEP 656](https://www.python.org/dev/peps/pep-0656/) for more details). +If setting a custom Docker image, you'll need to make sure it can be used in the same way as the official, default Docker images: all necessary Python and pip versions need to be present in `/opt/python/`, and the auditwheel tool needs to be present for cibuildwheel to work. Apart from that, the architecture and relevant shared system libraries need to be compatible to the relevant standard to produce valid manylinux1/manylinux2010/manylinux2014/manylinux_2_24/manylinux_2_28/musllinux_1_1 wheels (see [pypa/manylinux on GitHub](https://github.com/pypa/manylinux), [PEP 513](https://www.python.org/dev/peps/pep-0513/), [PEP 571](https://www.python.org/dev/peps/pep-0571/), [PEP 599](https://www.python.org/dev/peps/pep-0599/), [PEP 600](https://www.python.org/dev/peps/pep-0600/) and [PEP 656](https://www.python.org/dev/peps/pep-0656/) for more details). Auditwheel detects the version of the manylinux / musllinux standard in the Docker image through the `AUDITWHEEL_PLAT` environment variable, as cibuildwheel has no way of detecting the correct `--plat` command line argument to pass to auditwheel for a custom image. If a Docker image does not correctly set this `AUDITWHEEL_PLAT` environment variable, the `CIBW_ENVIRONMENT` option can be used to do so (e.g., `CIBW_ENVIRONMENT='AUDITWHEEL_PLAT="manylinux2010_$(uname -m)"'`). diff --git a/docs/setup.md b/docs/setup.md index fb1165fc..3871425b 100644 --- a/docs/setup.md +++ b/docs/setup.md @@ -183,7 +183,7 @@ To build Linux, Mac, and Windows wheels using GitHub Actions, create a `.github/ - uses: actions/checkout@v2 - name: Build wheels - run: pipx run cibuildwheel==2.4.0 + run: pipx run cibuildwheel==2.6.1 - uses: actions/upload-artifact@v2 with: @@ -218,7 +218,7 @@ To build Linux, Mac, and Windows wheels using GitHub Actions, create a `.github/ - 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 diff --git a/docs/working-examples.md b/docs/working-examples.md index ae5f5702..2845ebec 100644 --- a/docs/working-examples.md +++ b/docs/working-examples.md @@ -9,30 +9,38 @@ title: Working examples | 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. | +| [MemRay][] | ![github icon][] | ![linux icon][] | Memray is a memory profiler for Python | | [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 | -| [Apache Beam][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Apache Beam is a unified programming model for Batch and Streaming | +| [Apache Beam][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Apache Beam is a unified programming model for Batch and Streaming data processing. | | [asyncpg][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A fast PostgreSQL Database Client Library for Python/asyncio. | -| [scikit-image][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Image processing library. Uses cibuildwheel to build and test a project that uses Cython with platform-native code. | -| [cmake][] | ![github icon][] ![travisci icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Multitagged binary builds for all supported platforms, using cibw 2 config configuration. | | [duckdb][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | DuckDB is an in-process SQL OLAP Database Management System | +| [scikit-image][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Image processing library. Uses cibuildwheel to build and test a project that uses Cython with platform-native code. | +| [PyGame][] | ![github icon][] | ![apple icon][] ![linux icon][] | pygame (the library) is a Free and Open Source python programming language library for making multimedia applications like games built on top of the excellent SDL library. C, Python, Native, OpenGL. | +| [cmake][] | ![github icon][] ![travisci icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Multitagged binary builds for all supported platforms, using cibw 2 config configuration. | | [twisted-iocpsupport][] | ![github icon][] | ![windows icon][] | A submodule of Twisted that hooks into native C APIs using Cython. | | [websockets][] | ![travisci icon][] | ![apple icon][] ![linux icon][] | Library for building WebSocket servers and clients. Mostly written in Python, with a small C 'speedups' extension module. | | [cvxpy][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A Python-embedded modeling language for convex optimization problems. | | [PyOxidizer][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A modern Python application packaging and distribution tool | +| [UltraJSON][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Ultra fast JSON decoder and encoder written in C with Python bindings | | [Triton][] | ![github icon][] | ![linux icon][] | Self hosted runners | | [River][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | 🌊 Online machine learning in Python | | [OpenSpiel][] | ![github icon][] | ![apple icon][] ![linux icon][] | OpenSpiel is a collection of environments and algorithms for research in general reinforcement learning and search/planning in games. | | [pyzmq][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Python bindings for zeromq, the networking library. Uses Cython and CFFI. | | [aiortc][] | ![github icon][] | ![apple icon][] ![linux icon][] | WebRTC and ORTC implementation for Python using asyncio. | | [vispy][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Main repository for Vispy | +| [Implicit][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Includes GPU support for linux wheels | | [Confluent client for Kafka][] | ![travisci icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | setup in `tools/wheels/build-wheels.bat` | | [tinyobjloader][] | ![azurepipelines icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Tiny but powerful single file wavefront obj loader | | [Dependency Injector][] | ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Dependency injection framework for Python, uses Windows TravisCI | @@ -41,12 +49,14 @@ title: Working examples | [PyYAML][] | ![github icon][] | ![apple icon][] | Canonical source repository for PyYAML | | [numexpr][] | ![github icon][] ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Fast numerical array expression evaluator for Python, NumPy, PyTables, pandas, bcolz and more | | [h5py][] | ![azurepipelines icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | HDF5 for Python -- The h5py package is a Pythonic interface to the HDF5 binary data format. | +| [Wrapt][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A Python module for decorators, wrappers and monkey patching. | | [PyAV][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Pythonic bindings for FFmpeg's libraries. | +| [SimpleJSON][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | simplejson is a simple, fast, extensible JSON encoder/decoder for Python | | [OpenColorIO][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A color management framework for visual effects and animation. | +| [pikepdf][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A Python library for reading and writing PDF, powered by qpdf | | [Line Profiler][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Line-by-line profiling for Python | | [PyTables][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A Python package to manage extremely large amounts of data | | [OpenTimelineIO][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Open Source API and interchange format for editorial timeline information. | -| [pikepdf][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A Python library for reading and writing PDF, powered by qpdf | | [ruptures][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Extensive Cython + NumPy [pyproject.toml](https://github.com/deepcharles/ruptures/blob/master/pyproject.toml) example. | | [aioquic][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | QUIC and HTTP/3 implementation in Python | | [DeepForest][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | An Efficient, Scalable and Optimized Python Framework for Deep Forest (2021.2.1) | @@ -55,8 +65,8 @@ title: Working examples | [Parselmouth][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A Python interface to the Praat software package, using pybind11, C++17 and CMake, with the core Praat static library built only once and shared between wheels. | | [AutoPy][] | ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Includes a Windows Travis build. | | [H3-py][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Python bindings for H3, a hierarchical hexagonal geospatial indexing system | -| [markupsafe][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Safely add untrusted strings to HTML/XML markup. | | [Rtree][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Rtree: spatial index for Python GIS ¶ | +| [markupsafe][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Safely add untrusted strings to HTML/XML markup. | | [python-rapidjson][] | ![travisci icon][] ![gitlab icon][] ![appveyor icon][] | ![windows icon][] ![linux icon][] | Python wrapper around rapidjson | | [python-snappy][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Python bindings for the snappy google library | | [pybind11 cmake_example][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Example pybind11 module built with a CMake-based build system | @@ -64,63 +74,73 @@ title: Working examples | [tgcalls][] | ![github icon][] | ![apple icon][] ![windows icon][] | Python `pybind11` binding to Telegram's WebRTC library with third party dependencies like `OpenSSL`, `MozJPEG`, `FFmpeg`, etc. | | [pybind11 python_example][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Example pybind11 module built with a Python-based build system | | [dd-trace-py][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Uses custom alternate arch emulation on GitHub | -| [cyvcf2][] | ![github icon][] | ![apple icon][] ![linux icon][] | cython + htslib == fast VCF and BCF processing | | [sourmash][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Quickly search, compare, and analyze genomic and metagenomic data sets. | | [time-machine][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Time mocking library using only the CPython C API. | -| [abess][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A fast best-subset selection library. It uses cibuildwheel to build a large project with C++ extensions. | | [CTranslate2][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Includes libraries from the [Intel oneAPI toolkit](https://www.intel.com/content/www/us/en/developer/tools/oneapi/base-toolkit.html) and CUDA kernels compiled for multiple GPU architectures. | +| [cyvcf2][] | ![github icon][] | ![apple icon][] ![linux icon][] | cython + htslib == fast VCF and BCF processing | | [matrixprofile][] | ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A Python 3 library making time series data mining tasks, utilizing matrix profile algorithms, accessible to everyone. | +| [abess][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A fast best-subset selection library. It uses cibuildwheel to build a large project with C++ extensions. | | [jq.py][] | ![travisci icon][] | ![apple icon][] ![linux icon][] | Python bindings for jq | | [iminuit][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Jupyter-friendly Python interface for C++ MINUIT2 | | [Tokenizer][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Fast and customizable text tokenization library with BPE and SentencePiece support | | [PyGLM][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Fast OpenGL Mathematics (GLM) for Python | -| [bx-python][] | ![travisci icon][] | ![apple icon][] ![linux icon][] | A library that includes Cython extensions. | | [boost-histogram][] | ![github icon][] ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Supports full range of wheels, including PyPy and alternate archs. | +| [bx-python][] | ![travisci icon][] | ![apple icon][] ![linux icon][] | A library that includes Cython extensions. | | [iDynTree][] | ![github icon][] | ![linux icon][] | Uses manylinux_2_24 | | [TgCrypto][] | ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Includes a Windows Travis build. | +| [Python-WebRTC][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | a Python extension that provides bindings to WebRTC M92 | | [pybase64][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Fast Base64 encoding/decoding in Python | | [Arbor][] | ![github icon][] | ![apple icon][] ![linux icon][] | 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. | -| [etebase-py][] | ![travisci icon][] | ![linux icon][] | Python bindings to a Rust library using `setuptools-rust`, and `sccache` for improved speed. | | [fathon][] | ![travisci icon][] | ![apple icon][] ![linux icon][] | python package for DFA (Detrended Fluctuation Analysis) and related algorithms | +| [etebase-py][] | ![travisci icon][] | ![linux icon][] | Python bindings to a Rust library using `setuptools-rust`, and `sccache` for improved speed. | | [Imagecodecs (fork)][] | ![azurepipelines icon][] | ![apple icon][] ![linux icon][] | Over 20 external dependencies in compiled libraries, custom docker image, `libomp`, `openblas` and `install_name_tool` for macOS. | | [polaroid][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Full range of wheels for setuptools rust, with auto release and PyPI deploy. | -| [numpythia][] | ![github icon][] | ![apple icon][] ![linux icon][] | The interface between PYTHIA and NumPy | -| [pyjet][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The interface between FastJet and NumPy | | [clang-format][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Scikit-build wrapper around LLVM's CMake, all platforms, generic wheels. | -| [ninja][] | ![github icon][] ![travisci icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Multitagged binary builds for all supported platforms, using cibw 2 config configuration. | -| [pybind11 scikit_build_example][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | An example combining scikit-build and pybind11 | -| [GSD][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Cython and NumPy project with 64-bit wheels. | +| [numpythia][] | ![github icon][] | ![apple icon][] ![linux icon][] | The interface between PYTHIA and NumPy | +| [power-grid-model][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Python/C++ library for distribution power system analysis | +| [pyjet][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The interface between FastJet and NumPy | | [pillow-heif][] | ![github icon][] | ![apple icon][] ![linux icon][] | Python CFFI binding to libheif library with third party dependencies like `libde265`, `x265`, `libaom` with test & publishing on PyPi. | +| [pybind11 scikit_build_example][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | An example combining scikit-build and pybind11 | +| [ninja][] | ![github icon][] ![travisci icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Multitagged binary builds for all supported platforms, using cibw 2 config configuration. | +| [GSD][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Cython and NumPy project with 64-bit wheels. | | [pyinstrument_cext][] | ![travisci icon][] ![appveyor icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A simple C extension, without external dependencies | | [xmlstarlet][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Python 3.6+ CFFI bindings with true MSVC build. | | [CorrectionLib][] | ![github icon][] | ![apple icon][] ![linux icon][] | Structured JSON powered correction library for HEP, designed for the CMS experiment at CERN. | | [SiPM][] | ![github icon][] | ![apple icon][] ![linux icon][] | High performance library for SiPM detectors simulation using C++17, OpenMP and AVX2 intrinsics. | [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 +[MemRay]: https://github.com/bloomberg/memray [psutil]: https://github.com/giampaolo/psutil [vaex]: https://github.com/vaexio/vaex [Google Benchmark]: https://github.com/google/benchmark [Apache Beam]: https://github.com/apache/beam [asyncpg]: https://github.com/MagicStack/asyncpg -[scikit-image]: https://github.com/scikit-image/scikit-image -[cmake]: https://github.com/scikit-build/cmake-python-distributions [duckdb]: https://github.com/duckdb/duckdb +[scikit-image]: https://github.com/scikit-image/scikit-image +[PyGame]: https://github.com/pygame/pygame +[cmake]: https://github.com/scikit-build/cmake-python-distributions [twisted-iocpsupport]: https://github.com/twisted/twisted-iocpsupport [websockets]: https://github.com/aaugustin/websockets [cvxpy]: https://github.com/cvxpy/cvxpy [PyOxidizer]: https://github.com/indygreg/PyOxidizer +[UltraJSON]: https://github.com/ultrajson/ultrajson [Triton]: https://github.com/openai/triton [River]: https://github.com/online-ml/river [OpenSpiel]: https://github.com/deepmind/open_spiel [pyzmq]: https://github.com/zeromq/pyzmq [aiortc]: https://github.com/aiortc/aiortc [vispy]: https://github.com/vispy/vispy +[Implicit]: https://github.com/benfred/implicit [Confluent client for Kafka]: https://github.com/confluentinc/confluent-kafka-python [tinyobjloader]: https://github.com/tinyobjloader/tinyobjloader [Dependency Injector]: https://github.com/ets-labs/python-dependency-injector @@ -129,12 +149,14 @@ title: Working examples [PyYAML]: https://github.com/yaml/pyyaml [numexpr]: https://github.com/pydata/numexpr [h5py]: https://github.com/h5py/h5py +[Wrapt]: https://github.com/GrahamDumpleton/wrapt [PyAV]: https://github.com/PyAV-Org/PyAV +[SimpleJSON]: https://github.com/simplejson/simplejson [OpenColorIO]: https://github.com/AcademySoftwareFoundation/OpenColorIO +[pikepdf]: https://github.com/pikepdf/pikepdf [Line Profiler]: https://github.com/pyutils/line_profiler [PyTables]: https://github.com/PyTables/PyTables [OpenTimelineIO]: https://github.com/PixarAnimationStudios/OpenTimelineIO -[pikepdf]: https://github.com/pikepdf/pikepdf [ruptures]: https://github.com/deepcharles/ruptures [aioquic]: https://github.com/aiortc/aioquic [DeepForest]: https://github.com/LAMDA-NJU/Deep-Forest @@ -143,8 +165,8 @@ title: Working examples [Parselmouth]: https://github.com/YannickJadoul/Parselmouth [AutoPy]: https://github.com/autopilot-rs/autopy [H3-py]: https://github.com/uber/h3-py -[markupsafe]: https://github.com/pallets/markupsafe [Rtree]: https://github.com/Toblerity/rtree +[markupsafe]: https://github.com/pallets/markupsafe [python-rapidjson]: https://github.com/python-rapidjson/python-rapidjson [python-snappy]: https://github.com/andrix/python-snappy [pybind11 cmake_example]: https://github.com/pybind/cmake_example @@ -152,33 +174,35 @@ title: Working examples [tgcalls]: https://github.com/MarshalX/tgcalls [pybind11 python_example]: https://github.com/pybind/python_example [dd-trace-py]: https://github.com/DataDog/dd-trace-py -[cyvcf2]: https://github.com/brentp/cyvcf2 [sourmash]: https://github.com/dib-lab/sourmash [time-machine]: https://github.com/adamchainz/time-machine -[abess]: https://github.com/abess-team/abess [CTranslate2]: https://github.com/OpenNMT/CTranslate2 +[cyvcf2]: https://github.com/brentp/cyvcf2 [matrixprofile]: https://github.com/matrix-profile-foundation/matrixprofile +[abess]: https://github.com/abess-team/abess [jq.py]: https://github.com/mwilliamson/jq.py [iminuit]: https://github.com/scikit-hep/iminuit [Tokenizer]: https://github.com/OpenNMT/Tokenizer [PyGLM]: https://github.com/Zuzu-Typ/PyGLM -[bx-python]: https://github.com/bxlab/bx-python [boost-histogram]: https://github.com/scikit-hep/boost-histogram +[bx-python]: https://github.com/bxlab/bx-python [iDynTree]: https://github.com/robotology/idyntree [TgCrypto]: https://github.com/pyrogram/tgcrypto +[Python-WebRTC]: https://github.com/MarshalX/python-webrtc [pybase64]: https://github.com/mayeut/pybase64 [Arbor]: https://github.com/arbor-sim/arbor -[etebase-py]: https://github.com/etesync/etebase-py [fathon]: https://github.com/stfbnc/fathon +[etebase-py]: https://github.com/etesync/etebase-py [Imagecodecs (fork)]: https://github.com/czaki/imagecodecs_build [polaroid]: https://github.com/daggy1234/polaroid -[numpythia]: https://github.com/scikit-hep/numpythia -[pyjet]: https://github.com/scikit-hep/pyjet [clang-format]: https://github.com/ssciwr/clang-format-wheel -[ninja]: https://github.com/scikit-build/ninja-python-distributions -[pybind11 scikit_build_example]: https://github.com/pybind/scikit_build_example -[GSD]: https://github.com/glotzerlab/gsd +[numpythia]: https://github.com/scikit-hep/numpythia +[power-grid-model]: https://github.com/alliander-opensource/power-grid-model +[pyjet]: https://github.com/scikit-hep/pyjet [pillow-heif]: https://github.com/bigcat88/pillow_heif +[pybind11 scikit_build_example]: https://github.com/pybind/scikit_build_example +[ninja]: https://github.com/scikit-build/ninja-python-distributions +[GSD]: https://github.com/glotzerlab/gsd [pyinstrument_cext]: https://github.com/joerick/pyinstrument_cext [xmlstarlet]: https://github.com/dimitern/xmlstarlet [CorrectionLib]: https://github.com/cms-nanoAOD/correctionlib @@ -194,93 +218,105 @@ title: Working examples [apple icon]: data/readme_icons/apple.svg [linux icon]: data/readme_icons/linux.svg - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/appveyor-minimal.yml b/examples/appveyor-minimal.yml index b6162ada..50d5e51e 100644 --- a/examples/appveyor-minimal.yml +++ b/examples/appveyor-minimal.yml @@ -12,7 +12,7 @@ stack: python 3.7 init: - cmd: set PATH=C:\Python37;C:\Python37\Scripts;%PATH% -install: python -m pip install cibuildwheel==2.4.0 +install: python -m pip install cibuildwheel==2.6.1 build_script: python -m cibuildwheel --output-dir wheelhouse diff --git a/examples/azure-pipelines-minimal.yml b/examples/azure-pipelines-minimal.yml index c5c0ae8c..926c338a 100644 --- a/examples/azure-pipelines-minimal.yml +++ b/examples/azure-pipelines-minimal.yml @@ -6,7 +6,7 @@ jobs: - bash: | set -o errexit python3 -m pip install --upgrade pip - pip3 install cibuildwheel==2.4.0 + pip3 install cibuildwheel==2.6.1 displayName: Install dependencies - bash: cibuildwheel --output-dir wheelhouse . displayName: Build wheels @@ -20,7 +20,7 @@ jobs: - bash: | set -o errexit python3 -m pip install --upgrade pip - python3 -m pip install cibuildwheel==2.4.0 + python3 -m pip install cibuildwheel==2.6.1 displayName: Install dependencies - bash: cibuildwheel --output-dir wheelhouse . displayName: Build wheels @@ -34,7 +34,7 @@ jobs: - bash: | set -o errexit python -m pip install --upgrade pip - pip install cibuildwheel==2.4.0 + pip install cibuildwheel==2.6.1 displayName: Install dependencies - bash: cibuildwheel --output-dir wheelhouse . displayName: Build wheels diff --git a/examples/circleci-minimal.yml b/examples/circleci-minimal.yml index 19c45ad9..559367df 100644 --- a/examples/circleci-minimal.yml +++ b/examples/circleci-minimal.yml @@ -11,7 +11,7 @@ jobs: - run: name: Build the Linux wheels. command: | - pip3 install --user cibuildwheel==2.4.0 + pip3 install --user cibuildwheel==2.6.1 cibuildwheel --output-dir wheelhouse - store_artifacts: path: wheelhouse/ @@ -25,7 +25,7 @@ jobs: - run: name: Build the OS X wheels. command: | - pip3 install cibuildwheel==2.4.0 + pip3 install cibuildwheel==2.6.1 cibuildwheel --output-dir wheelhouse - store_artifacts: path: wheelhouse/ diff --git a/examples/github-apple-silicon.yml b/examples/github-apple-silicon.yml index 2f270556..b5d4e859 100644 --- a/examples/github-apple-silicon.yml +++ b/examples/github-apple-silicon.yml @@ -10,7 +10,7 @@ jobs: - uses: actions/checkout@v2 - name: Build wheels - uses: pypa/cibuildwheel@v2.4.0 + uses: pypa/cibuildwheel@v2.6.1 env: CIBW_ARCHS_MACOS: x86_64 universal2 diff --git a/examples/github-deploy.yml b/examples/github-deploy.yml index 4f604eda..8605cd41 100644 --- a/examples/github-deploy.yml +++ b/examples/github-deploy.yml @@ -22,7 +22,7 @@ jobs: - uses: actions/checkout@v2 - name: Build wheels - uses: pypa/cibuildwheel@v2.4.0 + uses: pypa/cibuildwheel@v2.6.1 - uses: actions/upload-artifact@v2 with: diff --git a/examples/github-minimal.yml b/examples/github-minimal.yml index 3b9038c1..a12f5f02 100644 --- a/examples/github-minimal.yml +++ b/examples/github-minimal.yml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v2 - name: Build wheels - uses: pypa/cibuildwheel@v2.4.0 + uses: pypa/cibuildwheel@v2.6.1 # to supply options, put them in 'env', like: # env: # CIBW_SOME_OPTION: value diff --git a/examples/github-with-qemu.yml b/examples/github-with-qemu.yml index c46645ec..54ecc3e8 100644 --- a/examples/github-with-qemu.yml +++ b/examples/github-with-qemu.yml @@ -25,7 +25,7 @@ jobs: platforms: all - name: Build wheels - uses: pypa/cibuildwheel@v2.4.0 + uses: pypa/cibuildwheel@v2.6.1 env: # configure cibuildwheel to build native archs ('auto'), and some # emulated ones diff --git a/examples/gitlab-minimal.yml b/examples/gitlab-minimal.yml index de4cdb77..f3969944 100644 --- a/examples/gitlab-minimal.yml +++ b/examples/gitlab-minimal.yml @@ -12,7 +12,7 @@ linux: DOCKER_TLS_CERTDIR: "" script: - curl -sSL https://get.docker.com/ | sh - - python -m pip install cibuildwheel==2.4.0 + - python -m pip install cibuildwheel==2.6.1 - cibuildwheel --output-dir wheelhouse artifacts: paths: diff --git a/examples/travis-ci-deploy.yml b/examples/travis-ci-deploy.yml index 101f36df..faae9dc2 100644 --- a/examples/travis-ci-deploy.yml +++ b/examples/travis-ci-deploy.yml @@ -19,7 +19,7 @@ jobs: - ln -s /c/Python38/python.exe /c/Python38/python3.exe install: - - python3 -m pip install cibuildwheel==2.4.0 + - python3 -m pip install cibuildwheel==2.6.1 script: # build the wheels, put them into './dist' diff --git a/examples/travis-ci-minimal.yml b/examples/travis-ci-minimal.yml index c0f4ab4b..5fc1fc16 100644 --- a/examples/travis-ci-minimal.yml +++ b/examples/travis-ci-minimal.yml @@ -25,7 +25,7 @@ jobs: - ln -s /c/Python38/python.exe /c/Python38/python3.exe install: - - python3 -m pip install cibuildwheel==2.4.0 + - python3 -m pip install cibuildwheel==2.6.1 script: # build the wheels, put them into './wheelhouse' diff --git a/examples/travis-ci-test-and-deploy.yml b/examples/travis-ci-test-and-deploy.yml index 37a24d99..310df31a 100644 --- a/examples/travis-ci-test-and-deploy.yml +++ b/examples/travis-ci-test-and-deploy.yml @@ -55,7 +55,7 @@ jobs: - stage: deploy name: Build and deploy Linux wheels services: docker - install: python3 -m pip install cibuildwheel==2.4.0 twine + install: python3 -m pip install cibuildwheel==2.6.1 twine script: python3 -m cibuildwheel --output-dir wheelhouse after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl # Deploy on windows @@ -63,7 +63,7 @@ jobs: name: Build and deploy Windows wheels os: windows language: shell - install: python3 -m pip install cibuildwheel==2.4.0 twine + install: python3 -m pip install cibuildwheel==2.6.1 twine script: python3 -m cibuildwheel --output-dir wheelhouse after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl diff --git a/noxfile.py b/noxfile.py index 4479ab60..e5f749f0 100644 --- a/noxfile.py +++ b/noxfile.py @@ -7,7 +7,7 @@ import nox nox.options.sessions = ["lint", "pylint", "check_manifest", "tests"] -PYTHON_ALL_VERSIONS = ["3.6", "3.7", "3.8", "3.9", "3.10"] +PYTHON_ALL_VERSIONS = ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11"] DIR = Path(__file__).parent.resolve() diff --git a/setup.cfg b/setup.cfg index a6773844..cd162b98 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = cibuildwheel -version = 2.4.0 +version = 2.6.1 description = Build Python wheels on CI with minimal configuration. long_description = file: README.md long_description_content_type = text/markdown @@ -35,7 +35,7 @@ install_requires = bracex certifi filelock - packaging + packaging>=20.9 platformdirs dataclasses;python_version < '3.7' tomli;python_version < '3.11' diff --git a/setup.py b/setup.py index 96b4d10c..7a9eee68 100644 --- a/setup.py +++ b/setup.py @@ -13,6 +13,7 @@ extras = { "pytest>=6", "pytest-timeout", "pytest-xdist", + "build", ], "bin": [ "click", diff --git a/test/test_from_sdist.py b/test/test_from_sdist.py new file mode 100644 index 00000000..d1d4ff11 --- /dev/null +++ b/test/test_from_sdist.py @@ -0,0 +1,173 @@ +import os +import subprocess +import sys +import textwrap +from pathlib import Path +from tempfile import TemporaryDirectory +from test.test_projects.base import TestProject + +from . import test_projects, utils + +# utilities + + +def make_sdist(project: TestProject, working_dir: Path) -> Path: + project_dir = working_dir / "project" + project_dir.mkdir(parents=True, exist_ok=True) + project.generate(project_dir) + + sdist_dir = working_dir / "sdist" + subprocess.run( + [sys.executable, "-m", "build", "--sdist", "--outdir", str(sdist_dir), str(project_dir)], + check=True, + ) + + return next(sdist_dir.glob("*.tar.gz")) + + +def cibuildwheel_from_sdist_run(sdist_path, add_env=None, config_file=None): + env = os.environ.copy() + + if add_env: + env.update(add_env) + + with TemporaryDirectory() as tmp_output_dir: + subprocess.run( + [ + sys.executable, + "-m", + "cibuildwheel", + *(["--config-file", config_file] if config_file else []), + "--output-dir", + str(tmp_output_dir), + str(sdist_path), + ], + env=env, + check=True, + ) + return os.listdir(tmp_output_dir) + + +# tests + + +def test_simple(tmp_path): + basic_project = test_projects.new_c_project() + + # make an sdist of the project + sdist_dir = tmp_path / "sdist" + sdist_dir.mkdir() + sdist_path = make_sdist(basic_project, sdist_dir) + + # build the wheels from sdist + actual_wheels = cibuildwheel_from_sdist_run( + sdist_path, + add_env={"CIBW_BUILD": "cp39-*"}, + ) + + # check that the expected wheels are produced + expected_wheels = [w for w in utils.expected_wheels("spam", "0.1.0") if "cp39" in w] + assert set(actual_wheels) == set(expected_wheels) + + +def test_external_config_file_argument(tmp_path, capfd): + basic_project = test_projects.new_c_project() + + # make an sdist of the project + sdist_dir = tmp_path / "sdist" + sdist_dir.mkdir() + sdist_path = make_sdist(basic_project, sdist_dir) + + # add a config file + config_file = tmp_path / "config.toml" + config_file.write_text( + textwrap.dedent( + """ + [tool.cibuildwheel] + before-all = 'echo "test log statement from before-all"' + """ + ) + ) + + # build the wheels from sdist + actual_wheels = cibuildwheel_from_sdist_run( + sdist_path, + add_env={"CIBW_BUILD": "cp39-*"}, + config_file=str(config_file), + ) + + # check that the expected wheels are produced + expected_wheels = [w for w in utils.expected_wheels("spam", "0.1.0") if "cp39" in w] + assert set(actual_wheels) == set(expected_wheels) + + # check that before-all was run + captured = capfd.readouterr() + assert "test log statement from before-all" in captured.out + + +def test_config_in_pyproject_toml(tmp_path, capfd): + # make a project with a pyproject.toml + project = test_projects.new_c_project() + project.files["pyproject.toml"] = textwrap.dedent( + """ + [tool.cibuildwheel] + before-build = 'echo "test log statement from before-build 8419"' + """ + ) + + # make an sdist of the project + sdist_dir = tmp_path / "sdist" + sdist_dir.mkdir() + sdist_path = make_sdist(project, sdist_dir) + + # build the wheels from sdist + actual_wheels = cibuildwheel_from_sdist_run( + sdist_path, + add_env={"CIBW_BUILD": "cp39-*"}, + ) + + # check that the expected wheels are produced + expected_wheels = [w for w in utils.expected_wheels("spam", "0.1.0") if "cp39" in w] + assert set(actual_wheels) == set(expected_wheels) + + # check that before-build was run + captured = capfd.readouterr() + assert "test log statement from before-build 8419" in captured.out + + +def test_internal_config_file_argument(tmp_path, capfd): + # make a project with a config file inside + project = test_projects.new_c_project( + setup_cfg_add="include_package_data = True", + ) + project.files["wheel_build_config.toml"] = textwrap.dedent( + """ + [tool.cibuildwheel] + before-all = 'echo "test log statement from before-all 1829"' + """ + ) + project.files["MANIFEST.in"] = textwrap.dedent( + """ + include wheel_build_config.toml + """ + ) + + # make an sdist of the project + sdist_dir = tmp_path / "sdist" + sdist_dir.mkdir() + sdist_path = make_sdist(project, sdist_dir) + + # build the wheels from sdist, referencing the config file inside + actual_wheels = cibuildwheel_from_sdist_run( + sdist_path, + add_env={"CIBW_BUILD": "cp39-*"}, + config_file="{package}/wheel_build_config.toml", + ) + + # check that the expected wheels are produced + expected_wheels = [w for w in utils.expected_wheels("spam", "0.1.0") if "cp39" in w] + assert set(actual_wheels) == set(expected_wheels) + + # check that before-all was run + captured = capfd.readouterr() + assert "test log statement from before-all 1829" in captured.out diff --git a/test/test_limited_api.py b/test/test_limited_api.py new file mode 100644 index 00000000..ea173bf5 --- /dev/null +++ b/test/test_limited_api.py @@ -0,0 +1,50 @@ +import textwrap + +from . import test_projects, utils + +limited_api_project = test_projects.new_c_project( + setup_py_add=textwrap.dedent( + r""" + cmdclass = {} + extension_kwargs = {} + if sys.version_info[:2] >= (3, 8): + from wheel.bdist_wheel import bdist_wheel as _bdist_wheel + + class bdist_wheel_abi3(_bdist_wheel): + def finalize_options(self): + _bdist_wheel.finalize_options(self) + self.root_is_pure = False + + def get_tag(self): + python, abi, plat = _bdist_wheel.get_tag(self) + return python, "abi3", plat + + cmdclass["bdist_wheel"] = bdist_wheel_abi3 + extension_kwargs["define_macros"] = [("Py_LIMITED_API", "0x03080000")] + extension_kwargs["py_limited_api"] = True + """ + ), + setup_py_extension_args_add="**extension_kwargs", + setup_py_setup_args_add="cmdclass=cmdclass", +) + + +def test(tmp_path): + project_dir = tmp_path / "project" + limited_api_project.generate(project_dir) + + # build the wheels + actual_wheels = utils.cibuildwheel_run( + project_dir, + add_env={ + "CIBW_SKIP": "pp* ", # PyPy does not have a Py_LIMITED_API equivalent + }, + ) + + # check that the expected wheels are produced + expected_wheels = [ + w.replace("cp38-cp38", "cp38-abi3") + for w in utils.expected_wheels("spam", "0.1.0") + if "-pp" not in w and "-cp39" not in w and "-cp310" not in w and "-cp311" not in w + ] + assert set(actual_wheels) == set(expected_wheels) diff --git a/test/test_manylinuxXXXX_only.py b/test/test_manylinuxXXXX_only.py index 7ae5ebb1..2a80cbb3 100644 --- a/test/test_manylinuxXXXX_only.py +++ b/test/test_manylinuxXXXX_only.py @@ -21,17 +21,24 @@ 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 + + #if __GLIBC_PREREQ(2, 28) + #include + #endif """ ), spam_c_function_add=textwrap.dedent( r""" - #if defined(__GLIBC_PREREQ) && __GLIBC_PREREQ(2, 24) + #if __GLIBC_PREREQ(2, 28) + // thrd_equal & thrd_current are only available in manylinux_2_28+ + sts = thrd_equal(thrd_current(), thrd_current()) ? 0 : 1;; + #elif __GLIBC_PREREQ(2, 24) // nextupf is only available in manylinux_2_24+ sts = (int)nextupf(0.0F); - #elif defined(__GLIBC_PREREQ) && __GLIBC_PREREQ(2, 17) /* manylinux2014 is glibc 2.17 */ + #elif __GLIBC_PREREQ(2, 17) /* manylinux2014 is glibc 2.17 */ // secure_getenv is only available in manylinux2014+ sts = (int)(intptr_t)secure_getenv("NON_EXISTING_ENV_VARIABLE"); - #elif defined(__GLIBC_PREREQ) && __GLIBC_PREREQ(2, 10) /* manylinux2010 is glibc 2.12 */ + #elif __GLIBC_PREREQ(2, 10) /* manylinux2010 is glibc 2.12 */ // malloc_info is only available on manylinux2010+ sts = malloc_info(0, stdout); #endif @@ -41,7 +48,8 @@ project_with_manylinux_symbols = test_projects.new_c_project( @pytest.mark.parametrize( - "manylinux_image", ["manylinux1", "manylinux2010", "manylinux2014", "manylinux_2_24"] + "manylinux_image", + ["manylinux1", "manylinux2010", "manylinux2014", "manylinux_2_24", "manylinux_2_28"], ) def test(manylinux_image, tmp_path): if utils.platform != "linux": @@ -49,6 +57,10 @@ def test(manylinux_image, tmp_path): elif platform.machine() not in ["x86_64", "i686"]: if manylinux_image in ["manylinux1", "manylinux2010"]: pytest.skip("manylinux1 and 2010 doesn't exist for non-x86 architectures") + elif manylinux_image == "manylinux_2_28" and platform.machine() == "s390x": + pytest.skip("manylinux_2_28 doesn't exist for s390x architecture") + elif manylinux_image == "manylinux_2_28" and platform.machine() == "i686": + pytest.skip("manylinux_2_28 doesn't exist for i686 architecture") project_dir = tmp_path / "project" project_with_manylinux_symbols.generate(project_dir) @@ -72,8 +84,11 @@ def test(manylinux_image, tmp_path): # We don't have a manylinux1 image for PyPy & CPython 3.10 and above add_env["CIBW_SKIP"] = "pp* cp31*" if manylinux_image in {"manylinux2010"}: - # We don't have a manylinux2010 image for PyPy 3.9 - add_env["CIBW_SKIP"] = "pp39*" + # We don't have a manylinux2010 image for PyPy 3.9, CPython 3.11 + add_env["CIBW_SKIP"] = "pp39* cp311*" + if manylinux_image == "manylinux_2_28" and platform.machine() == "x86_64": + # We don't have a manylinux_2_28 image for i686 + add_env["CIBW_ARCHS"] = "x86_64" actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env) @@ -91,7 +106,13 @@ def test(manylinux_image, tmp_path): if manylinux_image in {"manylinux1"}: # remove PyPy & CPython 3.10 and above expected_wheels = [w for w in expected_wheels if "-pp" not in w and "-cp31" not in w] + if manylinux_image in {"manylinux2010"}: - # remove PyPy 3.9 - expected_wheels = [w for w in expected_wheels if "-pp39" not in w] + # remove PyPy 3.9 & CPython 3.11 + expected_wheels = [w for w in expected_wheels if "-pp39" not in w and "-cp311" not in w] + + if manylinux_image == "manylinux_2_28" and platform.machine() == "x86_64": + # We don't have a manylinux_2_28 image for i686 + expected_wheels = [w for w in expected_wheels if "i686" not in w] + assert set(actual_wheels) == set(expected_wheels) diff --git a/test/test_projects/c.py b/test/test_projects/c.py index 41b72958..d4c37c69 100644 --- a/test/test_projects/c.py +++ b/test/test_projects/c.py @@ -54,6 +54,7 @@ setup( 'spam', sources=['spam.c'], libraries=libraries, + {{ setup_py_extension_args_add | indent(8) }} )], {{ setup_py_setup_args_add | indent(4) }} ) @@ -73,6 +74,7 @@ def new_c_project( spam_c_top_level_add="", spam_c_function_add="", setup_py_add="", + setup_py_extension_args_add="", setup_py_setup_args_add="", setup_cfg_add="", ): @@ -91,6 +93,7 @@ def new_c_project( "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_extension_args_add": setup_py_extension_args_add, "setup_py_setup_args_add": setup_py_setup_args_add, "setup_cfg_add": setup_cfg_add, } diff --git a/test/utils.py b/test/utils.py index 86df69a4..b6212021 100644 --- a/test/utils.py +++ b/test/utils.py @@ -139,14 +139,21 @@ def expected_wheels( if musllinux_versions is None: musllinux_versions = ["musllinux_1_1"] - python_abi_tags = ["cp36-cp36m", "cp37-cp37m", "cp38-cp38", "cp39-cp39", "cp310-cp310"] + python_abi_tags = [ + "cp36-cp36m", + "cp37-cp37m", + "cp38-cp38", + "cp39-cp39", + "cp310-cp310", + "cp311-cp311", + ] if machine_arch in ["x86_64", "AMD64", "x86", "aarch64"]: python_abi_tags += ["pp37-pypy37_pp73", "pp38-pypy38_pp73", "pp39-pypy39_pp73"] if platform == "macos" and machine_arch == "arm64": - # currently, arm64 macs are only supported by cp39 & cp310 - python_abi_tags = ["cp39-cp39", "cp310-cp310"] + # currently, arm64 macs are only supported by cp39, cp310 & cp311 + python_abi_tags = ["cp39-cp39", "cp310-cp310", "cp311-cp311"] wheels = [] diff --git a/unit_test/build_selector_test.py b/unit_test/build_selector_test.py index 7555ab33..b71cb05f 100644 --- a/unit_test/build_selector_test.py +++ b/unit_test/build_selector_test.py @@ -1,4 +1,3 @@ -import pytest from packaging.specifiers import SpecifierSet from cibuildwheel.util import BuildSelector @@ -10,6 +9,7 @@ def test_build(): assert build_selector("cp36-manylinux_x86_64") assert build_selector("cp37-manylinux_x86_64") assert build_selector("cp310-manylinux_x86_64") + assert not build_selector("cp311-manylinux_x86_64") assert build_selector("pp36-manylinux_x86_64") assert build_selector("pp37-manylinux_x86_64") assert build_selector("cp36-manylinux_i686") @@ -28,11 +28,11 @@ def test_build(): assert build_selector("cp36-win_amd64") assert build_selector("cp37-win_amd64") assert build_selector("cp310-win_amd64") + assert not build_selector("cp311-win_amd64") assert not build_selector("pp36-win_amd64") assert not build_selector("pp37-win_amd64") -@pytest.mark.skip("this test only makes sense when we have a prerelease python to test with") def test_build_filter_pre(): build_selector = BuildSelector( build_config="cp3*-* *-manylinux*", @@ -41,9 +41,9 @@ def test_build_filter_pre(): ) assert build_selector("cp37-manylinux_x86_64") - assert build_selector("cp310-manylinux_x86_64") + assert build_selector("cp311-manylinux_x86_64") assert build_selector("cp37-win_amd64") - assert build_selector("cp310-win_amd64") + assert build_selector("cp311-win_amd64") def test_skip(): @@ -136,3 +136,14 @@ def test_build_limited_python_patch(): assert build_selector("cp36-manylinux_x86_64") assert build_selector("cp37-manylinux_x86_64") + + +def test_testing_selector(): + # local import to avoid pytest trying to collect this as a test class! + from cibuildwheel.util import TestSelector + + test_selector = TestSelector(skip_config="cp36-*") + + assert not test_selector("cp36-win_amd64") + assert test_selector("cp37-manylinux_x86_64") + assert test_selector("cp311-manylinux_x86_64") diff --git a/unit_test/conftest.py b/unit_test/conftest.py index 26a28c47..2f794a21 100644 --- a/unit_test/conftest.py +++ b/unit_test/conftest.py @@ -32,7 +32,7 @@ def fake_package_dir(monkeypatch): real_path_exists = Path.exists def mock_path_exists(path): - if path == MOCK_PACKAGE_DIR / "setup.py": + if str(path).endswith(str(MOCK_PACKAGE_DIR / "setup.py")): return True else: return real_path_exists(path) diff --git a/unit_test/docker_container_test.py b/unit_test/docker_container_test.py index 3b250ad3..e9c43e77 100644 --- a/unit_test/docker_container_test.py +++ b/unit_test/docker_container_test.py @@ -3,7 +3,7 @@ import random import shutil import subprocess import textwrap -from pathlib import Path, PurePath +from pathlib import Path, PurePath, PurePosixPath import pytest @@ -178,7 +178,7 @@ def test_dir_operations(tmp_path: Path): test_file = test_dir / "test.dat" shutil.copyfile(original_test_file, test_file) - dst_dir = PurePath("/tmp/test_dir") + dst_dir = PurePosixPath("/tmp/test_dir") dst_file = dst_dir / "test.dat" container.copy_into(test_dir, dst_dir) diff --git a/unit_test/main_tests/main_options_test.py b/unit_test/main_tests/main_options_test.py index 2152ff3d..27a97a2e 100644 --- a/unit_test/main_tests/main_options_test.py +++ b/unit_test/main_tests/main_options_test.py @@ -24,13 +24,13 @@ def test_output_dir(platform, intercepted_build_args, monkeypatch): main() - assert intercepted_build_args.args[0].globals.output_dir == OUTPUT_DIR + assert intercepted_build_args.args[0].globals.output_dir == OUTPUT_DIR.resolve() def test_output_dir_default(platform, intercepted_build_args, monkeypatch): main() - assert intercepted_build_args.args[0].globals.output_dir == Path("wheelhouse") + assert intercepted_build_args.args[0].globals.output_dir == Path("wheelhouse").resolve() @pytest.mark.parametrize("also_set_environment", [False, True]) @@ -43,7 +43,7 @@ def test_output_dir_argument(also_set_environment, platform, intercepted_build_a main() - assert intercepted_build_args.args[0].globals.output_dir == OUTPUT_DIR + assert intercepted_build_args.args[0].globals.output_dir == OUTPUT_DIR.resolve() def test_build_selector(platform, intercepted_build_args, monkeypatch, allow_empty): @@ -80,6 +80,7 @@ def test_empty_selector(platform, intercepted_build_args, monkeypatch): ("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", "manylinux_2_28", "quay.io/pypa/manylinux_2_28_x86_64:*"), ("x86_64", "custom_image", "custom_image"), ("i686", None, "quay.io/pypa/manylinux2014_i686:*"), ("i686", "manylinux1", "quay.io/pypa/manylinux1_i686:*"), @@ -92,6 +93,7 @@ def test_empty_selector(platform, intercepted_build_args, monkeypatch): ("pypy_x86_64", "manylinux2010", "quay.io/pypa/manylinux2010_x86_64:*"), ("pypy_x86_64", "manylinux2014", "quay.io/pypa/manylinux2014_x86_64:*"), ("pypy_x86_64", "manylinux_2_24", "quay.io/pypa/manylinux_2_24_x86_64:*"), + ("pypy_x86_64", "manylinux_2_28", "quay.io/pypa/manylinux_2_28_x86_64:*"), ("pypy_x86_64", "custom_image", "custom_image"), ], ) diff --git a/unit_test/main_tests/main_platform_test.py b/unit_test/main_tests/main_platform_test.py index 09f5b9db..8974a27e 100644 --- a/unit_test/main_tests/main_platform_test.py +++ b/unit_test/main_tests/main_platform_test.py @@ -60,14 +60,14 @@ def test_platform_argument(platform, intercepted_build_args, monkeypatch): options = intercepted_build_args.args[0] - assert options.globals.package_dir == MOCK_PACKAGE_DIR + assert options.globals.package_dir == MOCK_PACKAGE_DIR.resolve() def test_platform_environment(platform, intercepted_build_args, monkeypatch): main() options = intercepted_build_args.args[0] - assert options.globals.package_dir == MOCK_PACKAGE_DIR + assert options.globals.package_dir == MOCK_PACKAGE_DIR.resolve() def test_archs_default(platform, intercepted_build_args, monkeypatch): diff --git a/unit_test/option_prepare_test.py b/unit_test/option_prepare_test.py index 354cda4a..5f2fecfd 100644 --- a/unit_test/option_prepare_test.py +++ b/unit_test/option_prepare_test.py @@ -2,7 +2,7 @@ import platform as platform_module import subprocess import sys from contextlib import contextmanager -from pathlib import Path +from pathlib import PurePosixPath from typing import cast from unittest import mock @@ -53,7 +53,7 @@ def test_build_default_launches(mock_build_docker, fake_package_dir, monkeypatch # In Python 3.8+, this can be simplified to [0].kwargs kwargs = build_on_docker.call_args_list[0][1] assert "quay.io/pypa/manylinux2014_x86_64" in kwargs["docker"]["docker_image"] - assert kwargs["docker"]["cwd"] == Path("/project") + assert kwargs["docker"]["cwd"] == PurePosixPath("/project") assert not kwargs["docker"]["simulate_32_bit"] identifiers = {x.identifier for x in kwargs["platform_configs"]} @@ -61,7 +61,7 @@ def test_build_default_launches(mock_build_docker, fake_package_dir, monkeypatch kwargs = build_on_docker.call_args_list[1][1] assert "quay.io/pypa/manylinux2014_i686" in kwargs["docker"]["docker_image"] - assert kwargs["docker"]["cwd"] == Path("/project") + assert kwargs["docker"]["cwd"] == PurePosixPath("/project") assert kwargs["docker"]["simulate_32_bit"] identifiers = {x.identifier for x in kwargs["platform_configs"]} @@ -69,7 +69,7 @@ def test_build_default_launches(mock_build_docker, fake_package_dir, monkeypatch kwargs = build_on_docker.call_args_list[2][1] assert "quay.io/pypa/musllinux_1_1_x86_64" in kwargs["docker"]["docker_image"] - assert kwargs["docker"]["cwd"] == Path("/project") + assert kwargs["docker"]["cwd"] == PurePosixPath("/project") assert not kwargs["docker"]["simulate_32_bit"] identifiers = {x.identifier for x in kwargs["platform_configs"]} @@ -79,7 +79,7 @@ def test_build_default_launches(mock_build_docker, fake_package_dir, monkeypatch kwargs = build_on_docker.call_args_list[3][1] assert "quay.io/pypa/musllinux_1_1_i686" in kwargs["docker"]["docker_image"] - assert kwargs["docker"]["cwd"] == Path("/project") + assert kwargs["docker"]["cwd"] == PurePosixPath("/project") assert kwargs["docker"]["simulate_32_bit"] identifiers = {x.identifier for x in kwargs["platform_configs"]} @@ -119,7 +119,7 @@ before-all = "true" kwargs = build_on_docker.call_args_list[0][1] assert "quay.io/pypa/manylinux2014_x86_64" in kwargs["docker"]["docker_image"] - assert kwargs["docker"]["cwd"] == Path("/project") + assert kwargs["docker"]["cwd"] == PurePosixPath("/project") assert not kwargs["docker"]["simulate_32_bit"] identifiers = {x.identifier for x in kwargs["platform_configs"]} @@ -128,7 +128,7 @@ before-all = "true" kwargs = build_on_docker.call_args_list[1][1] assert "quay.io/pypa/manylinux2014_x86_64" in kwargs["docker"]["docker_image"] - assert kwargs["docker"]["cwd"] == Path("/project") + assert kwargs["docker"]["cwd"] == PurePosixPath("/project") assert not kwargs["docker"]["simulate_32_bit"] identifiers = {x.identifier for x in kwargs["platform_configs"]} @@ -139,7 +139,7 @@ before-all = "true" kwargs = build_on_docker.call_args_list[2][1] assert "quay.io/pypa/manylinux_2_24_x86_64" in kwargs["docker"]["docker_image"] - assert kwargs["docker"]["cwd"] == Path("/project") + assert kwargs["docker"]["cwd"] == PurePosixPath("/project") assert not kwargs["docker"]["simulate_32_bit"] identifiers = {x.identifier for x in kwargs["platform_configs"]} assert identifiers == { @@ -151,7 +151,7 @@ before-all = "true" kwargs = build_on_docker.call_args_list[3][1] assert "quay.io/pypa/manylinux2014_i686" in kwargs["docker"]["docker_image"] - assert kwargs["docker"]["cwd"] == Path("/project") + assert kwargs["docker"]["cwd"] == PurePosixPath("/project") assert kwargs["docker"]["simulate_32_bit"] identifiers = {x.identifier for x in kwargs["platform_configs"]} @@ -159,7 +159,7 @@ before-all = "true" kwargs = build_on_docker.call_args_list[4][1] assert "quay.io/pypa/musllinux_1_1_x86_64" in kwargs["docker"]["docker_image"] - assert kwargs["docker"]["cwd"] == Path("/project") + assert kwargs["docker"]["cwd"] == PurePosixPath("/project") assert not kwargs["docker"]["simulate_32_bit"] identifiers = {x.identifier for x in kwargs["platform_configs"]} @@ -169,7 +169,7 @@ before-all = "true" kwargs = build_on_docker.call_args_list[5][1] assert "quay.io/pypa/musllinux_1_1_i686" in kwargs["docker"]["docker_image"] - assert kwargs["docker"]["cwd"] == Path("/project") + assert kwargs["docker"]["cwd"] == PurePosixPath("/project") assert kwargs["docker"]["simulate_32_bit"] identifiers = {x.identifier for x in kwargs["platform_configs"]} diff --git a/unit_test/options_test.py b/unit_test/options_test.py index 01a98f3b..fd8a102f 100644 --- a/unit_test/options_test.py +++ b/unit_test/options_test.py @@ -34,7 +34,7 @@ def test_options_1(tmp_path, monkeypatch): f.write(PYPROJECT_1) args = get_default_command_line_arguments() - args.package_dir = str(tmp_path) + args.package_dir = tmp_path monkeypatch.setattr(platform_module, "machine", lambda: "x86_64") @@ -77,7 +77,7 @@ def test_passthrough(tmp_path, monkeypatch): f.write(PYPROJECT_1) args = get_default_command_line_arguments() - args.package_dir = str(tmp_path) + args.package_dir = tmp_path monkeypatch.setattr(platform_module, "machine", lambda: "x86_64") monkeypatch.setenv("EXAMPLE_ENV", "ONE") @@ -105,7 +105,7 @@ def test_passthrough(tmp_path, monkeypatch): ) def test_passthrough_evil(tmp_path, monkeypatch, env_var_value): args = get_default_command_line_arguments() - args.package_dir = str(tmp_path) + args.package_dir = tmp_path monkeypatch.setattr(platform_module, "machine", lambda: "x86_64") monkeypatch.setenv("CIBW_ENVIRONMENT_PASS_LINUX", "ENV_VAR") diff --git a/unit_test/utils.py b/unit_test/utils.py index 32e9331f..2b312eca 100644 --- a/unit_test/utils.py +++ b/unit_test/utils.py @@ -1,3 +1,5 @@ +from pathlib import Path + from cibuildwheel.options import CommandLineArguments @@ -7,8 +9,8 @@ def get_default_command_line_arguments() -> CommandLineArguments: allow_empty=False, archs=None, config_file="", - output_dir=None, - package_dir=".", + output_dir=Path("wheelhouse"), + package_dir=Path("."), prerelease_pythons=False, print_build_identifiers=False, )