Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions bin/update_docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,23 @@ def __init__(self, manylinux_version: str, platforms: list[str], tag: str | None
super().__init__(manylinux_version, platforms, image_name, tag, True)


class CudaImage(Image):
"""
A CUDA manylinux image from the manylinux_cuda project, built by
https://github.com/gpu-ci-demo/manylinux-cuda-container.

Each architecture has its own repository (``{arch}`` in the image name is
substituted per platform), so (unlike the PyPA images) these are resolved
one repository at a time, but they carry the same dated tags and are pinned
to those just like the PyPA images.
"""

def __init__(self, manylinux_version: str, cuda_version: str, platforms: list[str]):
alias = f"{manylinux_version}_cuda{cuda_version}"
image_name = f"quay.io/manylinux_cuda/{manylinux_version}_{{arch}}_cuda{cuda_version}"
super().__init__(alias, platforms, image_name)


images = [
# manylinux2014 images
PyPAImage(
Expand Down Expand Up @@ -86,11 +103,41 @@ def __init__(self, manylinux_version: str, platforms: list[str], tag: str | None
PyPAImage(
"musllinux_1_2", ["x86_64", "i686", "aarch64", "ppc64le", "s390x", "armv7l", "riscv64"]
),
# CUDA manylinux images (x86_64 and aarch64 only, one repository per arch)
*(
CudaImage(manylinux_version, cuda_version, ["x86_64", "aarch64"])
for manylinux_version in ("manylinux_2_28", "manylinux_2_34")
for cuda_version in ("12_9", "13_1")
),
]

config = configparser.ConfigParser()

for image in images:
if "{arch}" in image.image_name:
# Per-architecture repositories: each arch has its own repo, so resolve
# the dated tag matching 'latest' for each one individually.
for platform in image.platforms:
arch = platform.removeprefix("pypy_")
image_name = image.image_name.format(arch=arch)
_, _, repository_name = image_name.partition("/")
response = requests.get(
f"https://quay.io/api/v1/repository/{repository_name}?includeTags=true"
)
response.raise_for_status()
tags_dict = response.json()["tags"]
latest_tag = tags_dict.pop("latest")
# find the tag whose manifest matches 'latest'
tag_name = next(
name
for (name, info) in tags_dict.items()
if info["manifest_digest"] == latest_tag["manifest_digest"]
)
if not config.has_section(platform):
config[platform] = {}
config[platform][image.manylinux_version] = f"{image_name}:{tag_name}"
continue

# get the tag name whose digest matches 'latest'
if image.tag is not None:
# image has been pinned, do not update
Expand Down
8 changes: 8 additions & 0 deletions cibuildwheel/resources/pinned_docker_images.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2026.06.04-1
manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2026.06.04-1
manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2026.06.04-1
musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2026.06.04-1
manylinux_2_28_cuda12_9 = quay.io/manylinux_cuda/manylinux_2_28_x86_64_cuda12_9:2026.06.08-1
manylinux_2_28_cuda13_1 = quay.io/manylinux_cuda/manylinux_2_28_x86_64_cuda13_1:2026.06.08-1
manylinux_2_34_cuda12_9 = quay.io/manylinux_cuda/manylinux_2_34_x86_64_cuda12_9:2026.06.08-1
manylinux_2_34_cuda13_1 = quay.io/manylinux_cuda/manylinux_2_34_x86_64_cuda13_1:2026.06.08-1

[i686]
manylinux2014 = quay.io/pypa/manylinux2014_i686:2026.06.04-1
Expand All @@ -15,6 +19,10 @@ manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2026.06.04-1
manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2026.06.04-1
manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2026.06.04-1
musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2026.06.04-1
manylinux_2_28_cuda12_9 = quay.io/manylinux_cuda/manylinux_2_28_aarch64_cuda12_9:2026.06.08-1
manylinux_2_28_cuda13_1 = quay.io/manylinux_cuda/manylinux_2_28_aarch64_cuda13_1:2026.06.08-1
manylinux_2_34_cuda12_9 = quay.io/manylinux_cuda/manylinux_2_34_aarch64_cuda12_9:2026.06.08-1
manylinux_2_34_cuda13_1 = quay.io/manylinux_cuda/manylinux_2_34_aarch64_cuda13_1:2026.06.08-1

[ppc64le]
manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2026.06.04-1
Expand Down
36 changes: 18 additions & 18 deletions docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,24 +224,26 @@ Consider incorporating these into your package, for example, in `setup.py` using
### Building wheels with CUDA on Linux

On Linux, you can build binary wheels with CUDA to take advantage of NVIDIA GPUs for hardware acceleration.
Specify the custom Docker containers with CUDA Toolkit as follows:
The [manylinux_cuda](https://quay.io/organization/manylinux_cuda) project ([source](https://github.com/gpu-ci-demo/manylinux-cuda-container)) publishes manylinux containers that bundle the CUDA Toolkit, and cibuildwheel ships pinned aliases for them. Just like `manylinux_2_28`, you can pass an alias to the `manylinux-*-image` options and cibuildwheel will expand it to a specific, pinned image:

```yaml
CIBW_MANYLINUX_X86_64_IMAGE: >-
quay.io/manylinux_cuda/manylinux_2_28_x86_64_cuda13_1:latest
CIBW_MANYLINUX_AARCH64_IMAGE: >-
quay.io/manylinux_cuda/manylinux_2_28_aarch64_cuda13_1:latest
CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_28_cuda13_1
CIBW_MANYLINUX_AARCH64_IMAGE: manylinux_2_28_cuda13_1
```
Currently, we support the following CUDA manylinux containers:

* `quay.io/manylinux_cuda/manylinux_2_28_x86_64_cuda12_9:latest`
* `quay.io/manylinux_cuda/manylinux_2_28_aarch64_cuda12_9:latest`
* `quay.io/manylinux_cuda/manylinux_2_28_x86_64_cuda13_1:latest`
* `quay.io/manylinux_cuda/manylinux_2_28_aarch64_cuda13_1:latest`
* `quay.io/manylinux_cuda/manylinux_2_34_x86_64_cuda12_9:latest`
* `quay.io/manylinux_cuda/manylinux_2_34_aarch64_cuda12_9:latest`
* `quay.io/manylinux_cuda/manylinux_2_34_x86_64_cuda13_1:latest`
* `quay.io/manylinux_cuda/manylinux_2_34_aarch64_cuda13_1:latest`
The following CUDA aliases are available (for `x86_64` and `aarch64` only):

* `manylinux_2_28_cuda12_9`
* `manylinux_2_28_cuda13_1`
* `manylinux_2_34_cuda12_9`
* `manylinux_2_34_cuda13_1`

These aliases resolve to images under `quay.io/manylinux_cuda/`, named
`manylinux_<glibc>_<arch>_cuda<version>` (e.g.
`quay.io/manylinux_cuda/manylinux_2_28_x86_64_cuda13_1`). If you want a CUDA/glibc/arch
combination that isn't aliased above, or you'd rather track the latest build yourself, you
can point at the repository directly with an explicit tag or digest, e.g.
`quay.io/manylinux_cuda/manylinux_2_28_x86_64_cuda13_1:latest`.

A typical GitHub Actions workflow will look like this:

Expand All @@ -267,10 +269,8 @@ jobs:
- name: Build wheels
uses: pypa/cibuildwheel@v3
env:
CIBW_MANYLINUX_X86_64_IMAGE: >-
quay.io/manylinux_cuda/${{ matrix.manylinux-base }}_x86_64_cuda${{ matrix.cuda-version }}:latest
CIBW_MANYLINUX_AARCH64_IMAGE: >-
quay.io/manylinux_cuda/${{ matrix.manylinux-base }}_aarch64_cuda${{ matrix.cuda-version }}:latest
CIBW_MANYLINUX_X86_64_IMAGE: ${{ matrix.manylinux-base }}_cuda${{ matrix.cuda-version }}
CIBW_MANYLINUX_AARCH64_IMAGE: ${{ matrix.manylinux-base }}_cuda${{ matrix.cuda-version }}
CIBW_BUILD: cp312-manylinux_${{ matrix.target.arch }}
```

Expand Down
23 changes: 23 additions & 0 deletions unit_test/options_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,29 @@ def test_passthrough(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
}


@pytest.mark.parametrize("arch", ["x86_64", "aarch64"])
@pytest.mark.parametrize(
"alias",
[
"manylinux_2_28_cuda12_9",
"manylinux_2_28_cuda13_1",
"manylinux_2_34_cuda12_9",
"manylinux_2_34_cuda13_1",
],
)
def test_cuda_pinned_images(arch: str, alias: str) -> None:
pinned_images = _get_pinned_container_images()

# CUDA aliases exist for x86_64 and aarch64, each in their own repository
image = pinned_images[arch][alias]
repository, _, tag = image.partition(":")
assert repository == f"quay.io/manylinux_cuda/{alias.replace('cuda', arch + '_cuda', 1)}"
assert tag

# ... but not for architectures without CUDA images
assert alias not in pinned_images["i686"]


@pytest.mark.parametrize(
"env_var_value",
[
Expand Down
Loading