diff --git a/cibuildwheel/options.py b/cibuildwheel/options.py
index 5ab256232..8e8229a29 100644
--- a/cibuildwheel/options.py
+++ b/cibuildwheel/options.py
@@ -795,10 +795,7 @@ def _compute_build_options(self, identifier: str | None) -> BuildOptions:
f"manylinux-{build_platform}-image", ignore_empty=True
)
self._check_pinned_image(config_value, pinned_images)
- if not config_value:
- # default to manylinux2014
- image = pinned_images["manylinux2014"]
- elif config_value in pinned_images:
+ if config_value in pinned_images:
image = pinned_images[config_value]
else:
image = config_value
@@ -806,11 +803,11 @@ def _compute_build_options(self, identifier: str | None) -> BuildOptions:
for build_platform in MUSLLINUX_ARCHS:
pinned_images = all_pinned_container_images[build_platform]
- config_value = self.reader.get(f"musllinux-{build_platform}-image")
+ config_value = self.reader.get(
+ f"musllinux-{build_platform}-image", ignore_empty=True
+ )
self._check_pinned_image(config_value, pinned_images)
- if not config_value:
- image = pinned_images["musllinux_1_2"]
- elif config_value in pinned_images:
+ if config_value in pinned_images:
image = pinned_images[config_value]
else:
image = config_value
diff --git a/cibuildwheel/resources/defaults.toml b/cibuildwheel/resources/defaults.toml
index 6b65e6369..bd17245b4 100644
--- a/cibuildwheel/resources/defaults.toml
+++ b/cibuildwheel/resources/defaults.toml
@@ -25,15 +25,15 @@ test-groups = []
container-engine = "docker"
-manylinux-x86_64-image = "manylinux2014"
+manylinux-x86_64-image = "manylinux_2_28"
manylinux-i686-image = "manylinux2014"
-manylinux-aarch64-image = "manylinux2014"
-manylinux-ppc64le-image = "manylinux2014"
-manylinux-s390x-image = "manylinux2014"
+manylinux-aarch64-image = "manylinux_2_28"
+manylinux-ppc64le-image = "manylinux_2_28"
+manylinux-s390x-image = "manylinux_2_28"
manylinux-armv7l-image = "manylinux_2_31"
-manylinux-pypy_x86_64-image = "manylinux2014"
+manylinux-pypy_x86_64-image = "manylinux_2_28"
manylinux-pypy_i686-image = "manylinux2014"
-manylinux-pypy_aarch64-image = "manylinux2014"
+manylinux-pypy_aarch64-image = "manylinux_2_28"
musllinux-x86_64-image = "musllinux_1_2"
musllinux-i686-image = "musllinux_1_2"
diff --git a/docs/options.md b/docs/options.md
index 2e8a199c7..0c4ade208 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -1168,25 +1168,23 @@ Platform-specific environment variables are also available:
The available options are:
-| Option | Default | Future default* |
-|-----------------------------------|----------------------------------------------------------------|-----------------------------------------------------------------|
-| CIBW_MANYLINUX_X86_64_IMAGE | [`manylinux2014`](https://quay.io/pypa/manylinux2014_x86_64) | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_x86_64) |
-| CIBW_MANYLINUX_I686_IMAGE | [`manylinux2014`](https://quay.io/pypa/manylinux2014_i686) | |
-| CIBW_MANYLINUX_PYPY_X86_64_IMAGE | [`manylinux2014`](https://quay.io/pypa/manylinux2014_x86_64) | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_x86_64) |
-| CIBW_MANYLINUX_AARCH64_IMAGE | [`manylinux2014`](https://quay.io/pypa/manylinux2014_aarch64) | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_aarch64) |
-| CIBW_MANYLINUX_PPC64LE_IMAGE | [`manylinux2014`](https://quay.io/pypa/manylinux2014_ppc64le) | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_ppc64le) |
-| CIBW_MANYLINUX_S390X_IMAGE | [`manylinux2014`](https://quay.io/pypa/manylinux2014_s390x) | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_s390x) |
-| CIBW_MANYLINUX_ARMV7L_IMAGE | [`manylinux_2_31`](https://quay.io/pypa/manylinux_2_31_armv7l) | |
-| CIBW_MANYLINUX_PYPY_AARCH64_IMAGE | [`manylinux2014`](https://quay.io/pypa/manylinux2014_aarch64) | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_aarch64) |
-| CIBW_MANYLINUX_PYPY_I686_IMAGE | [`manylinux2014`](https://quay.io/pypa/manylinux2014_i686) | |
-| CIBW_MUSLLINUX_X86_64_IMAGE | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_x86_64) | |
-| CIBW_MUSLLINUX_I686_IMAGE | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_i686) | |
-| CIBW_MUSLLINUX_AARCH64_IMAGE | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_aarch64) | |
-| CIBW_MUSLLINUX_PPC64LE_IMAGE | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_ppc64le) | |
-| CIBW_MUSLLINUX_S390X_IMAGE | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_s390x) | |
-| CIBW_MUSLLINUX_ARMV7L_IMAGE | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_armv7l) | |
-
-* The default is scheduled to change in a cibuildwheel release on or after 6th May 2025 - if you don't want the new default, you should set the value to `manylinux2014`.
+| Option | Default |
+|-----------------------------------|-----------------------------------------------------------------|
+| CIBW_MANYLINUX_X86_64_IMAGE | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_x86_64) |
+| CIBW_MANYLINUX_I686_IMAGE | [`manylinux2014`](https://quay.io/pypa/manylinux2014_i686) |
+| CIBW_MANYLINUX_PYPY_X86_64_IMAGE | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_x86_64) |
+| CIBW_MANYLINUX_AARCH64_IMAGE | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_aarch64) |
+| CIBW_MANYLINUX_PPC64LE_IMAGE | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_ppc64le) |
+| CIBW_MANYLINUX_S390X_IMAGE | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_s390x) |
+| CIBW_MANYLINUX_ARMV7L_IMAGE | [`manylinux_2_31`](https://quay.io/pypa/manylinux_2_31_armv7l) |
+| CIBW_MANYLINUX_PYPY_AARCH64_IMAGE | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_aarch64) |
+| CIBW_MANYLINUX_PYPY_I686_IMAGE | [`manylinux2014`](https://quay.io/pypa/manylinux2014_i686) |
+| CIBW_MUSLLINUX_X86_64_IMAGE | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_x86_64) |
+| CIBW_MUSLLINUX_I686_IMAGE | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_i686) |
+| CIBW_MUSLLINUX_AARCH64_IMAGE | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_aarch64) |
+| CIBW_MUSLLINUX_PPC64LE_IMAGE | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_ppc64le) |
+| CIBW_MUSLLINUX_S390X_IMAGE | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_s390x) |
+| CIBW_MUSLLINUX_ARMV7L_IMAGE | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_armv7l) |
Set the Docker image to be used for building [manylinux / musllinux](https://github.com/pypa/manylinux) wheels.
diff --git a/test/test_container_images.py b/test/test_container_images.py
index 17626a2ef..f1d4fda8b 100644
--- a/test/test_container_images.py
+++ b/test/test_container_images.py
@@ -43,9 +43,12 @@ def test(tmp_path):
)
# also check that we got the right wheels built
+ manylinux_versions = ["manylinux_2_5", "manylinux1", "manylinux_2_17", "manylinux2014"]
expected_wheels = [
w
- for w in utils.expected_wheels("spam", "0.1.0", musllinux_versions=[])
+ for w in utils.expected_wheels(
+ "spam", "0.1.0", manylinux_versions=manylinux_versions, musllinux_versions=[]
+ )
if "-cp38-" in w or "-cp39-" in w
]
assert set(actual_wheels) == set(expected_wheels)
diff --git a/test/utils.py b/test/utils.py
index 4548f0a52..6846223fb 100644
--- a/test/utils.py
+++ b/test/utils.py
@@ -162,6 +162,57 @@ def expected_wheels(
include_universal2: bool = False,
single_python: bool = False,
single_arch: bool = False,
+) -> list[str]:
+ """
+ Returns a list of expected wheels from a run of cibuildwheel.
+ """
+ if machine_arch is None:
+ machine_arch = pm.machine()
+ if platform == "linux":
+ machine_arch = arch_name_for_linux(machine_arch)
+
+ architectures = [machine_arch]
+ if not single_arch:
+ if platform == "linux":
+ if machine_arch == "x86_64":
+ architectures.append("i686")
+ elif (
+ machine_arch == "aarch64"
+ and sys.platform.startswith("linux")
+ and _AARCH64_CAN_RUN_ARMV7
+ ):
+ architectures.append("armv7l")
+ elif platform == "windows" and machine_arch == "AMD64":
+ architectures.append("x86")
+
+ wheels: list[str] = []
+ for architecture in architectures:
+ wheels.extend(
+ _expected_wheels(
+ package_name,
+ package_version,
+ architecture,
+ manylinux_versions,
+ musllinux_versions,
+ macosx_deployment_target,
+ python_abi_tags,
+ include_universal2,
+ single_python,
+ )
+ )
+ return wheels
+
+
+def _expected_wheels(
+ package_name: str,
+ package_version: str,
+ machine_arch: str,
+ manylinux_versions: list[str] | None,
+ musllinux_versions: list[str] | None,
+ macosx_deployment_target: str,
+ python_abi_tags: list[str] | None,
+ include_universal2: bool,
+ single_python: bool,
) -> list[str]:
"""
Returns a list of expected wheels from a run of cibuildwheel.
@@ -171,24 +222,12 @@ def expected_wheels(
# {python tag} and {abi tag} are closely related to the python interpreter used to build the wheel
# so we'll merge them below as python_abi_tag
- if machine_arch is None:
- machine_arch = pm.machine()
- if platform == "linux" and machine_arch.lower() == "arm64":
- # we're running linux tests from macOS/Windows arm64, override platform
- machine_arch = "aarch64"
-
if manylinux_versions is None:
- if machine_arch in ("armv7l", "aarch64"):
- manylinux_versions = ["manylinux_2_17", "manylinux2014", "manylinux_2_31"]
- elif machine_arch == "x86_64":
- manylinux_versions = [
- "manylinux_2_5",
- "manylinux1",
- "manylinux_2_17",
- "manylinux2014",
- ]
- else:
- manylinux_versions = ["manylinux_2_17", "manylinux2014"]
+ manylinux_versions = {
+ "armv7l": ["manylinux_2_17", "manylinux2014", "manylinux_2_31"],
+ "i686": ["manylinux_2_5", "manylinux1", "manylinux_2_17", "manylinux2014"],
+ "x86_64": ["manylinux_2_5", "manylinux1", "manylinux_2_28"],
+ }.get(machine_arch, ["manylinux_2_17", "manylinux2014", "manylinux_2_28"])
if musllinux_versions is None:
musllinux_versions = ["musllinux_1_2"]
@@ -206,7 +245,7 @@ def expected_wheels(
"cp313-cp313t",
]
- if machine_arch in ["x86_64", "AMD64", "x86", "aarch64"]:
+ if machine_arch in ["x86_64", "i686", "AMD64", "aarch64", "arm64"]:
python_abi_tags += [
"pp38-pypy38_pp73",
"pp39-pypy39_pp73",
@@ -214,22 +253,6 @@ def expected_wheels(
"pp311-pypy311_pp73",
]
- if platform == "macos" and machine_arch == "arm64":
- # arm64 macs are only supported by cp38+
- python_abi_tags = [
- "cp38-cp38",
- "cp39-cp39",
- "cp310-cp310",
- "cp311-cp311",
- "cp312-cp312",
- "cp313-cp313",
- "cp313-cp313t",
- "pp38-pypy38_pp73",
- "pp39-pypy39_pp73",
- "pp310-pypy310_pp73",
- "pp311-pypy311_pp73",
- ]
-
if single_python:
python_tag = "cp{}{}-".format(*SINGLE_PYTHON_VERSION)
python_abi_tags = [
@@ -252,44 +275,23 @@ def expected_wheels(
platform_tags = []
if platform == "linux":
- architectures = [arch_name_for_linux(machine_arch)]
-
- if not single_arch:
- if machine_arch == "x86_64":
- architectures.append("i686")
- elif (
- machine_arch == "aarch64"
- and sys.platform.startswith("linux")
- and not python_abi_tag.startswith("pp")
- and _AARCH64_CAN_RUN_ARMV7
- ):
- architectures.append("armv7l")
-
if len(manylinux_versions) > 0:
platform_tags = [
".".join(
- f"{manylinux_version}_{architecture}"
+ f"{manylinux_version}_{machine_arch}"
for manylinux_version in manylinux_versions
- if (manylinux_version, architecture) != ("manylinux_2_31", "aarch64")
)
- for architecture in architectures
]
if len(musllinux_versions) > 0 and not python_abi_tag.startswith("pp"):
- platform_tags.extend(
- [
- ".".join(
- f"{musllinux_version}_{architecture}"
- for musllinux_version in musllinux_versions
- )
- for architecture in architectures
- ]
+ platform_tags.append(
+ ".".join(
+ f"{musllinux_version}_{machine_arch}"
+ for musllinux_version in musllinux_versions
+ )
)
elif platform == "windows":
- if python_abi_tag.startswith("pp"):
- platform_tags = ["win_amd64"]
- else:
- platform_tags = ["win32", "win_amd64"]
+ platform_tags = ["win_amd64"] if machine_arch == "AMD64" else ["win32"]
elif platform == "macos":
if python_abi_tag.startswith("pp"):
diff --git a/unit_test/main_tests/main_options_test.py b/unit_test/main_tests/main_options_test.py
index a869d615f..b8d773124 100644
--- a/unit_test/main_tests/main_options_test.py
+++ b/unit_test/main_tests/main_options_test.py
@@ -79,7 +79,7 @@ def test_empty_selector(monkeypatch):
@pytest.mark.parametrize(
("architecture", "image", "full_image"),
[
- ("x86_64", None, "quay.io/pypa/manylinux2014_x86_64:*"),
+ ("x86_64", None, "quay.io/pypa/manylinux_2_28_x86_64:*"),
("x86_64", "manylinux2014", "quay.io/pypa/manylinux2014_x86_64:*"),
("x86_64", "manylinux_2_28", "quay.io/pypa/manylinux_2_28_x86_64:*"),
("x86_64", "manylinux_2_34", "quay.io/pypa/manylinux_2_34_x86_64:*"),
@@ -87,7 +87,7 @@ def test_empty_selector(monkeypatch):
("i686", None, "quay.io/pypa/manylinux2014_i686:*"),
("i686", "manylinux2014", "quay.io/pypa/manylinux2014_i686:*"),
("i686", "custom_image", "custom_image"),
- ("pypy_x86_64", None, "quay.io/pypa/manylinux2014_x86_64:*"),
+ ("pypy_x86_64", None, "quay.io/pypa/manylinux_2_28_x86_64:*"),
("pypy_x86_64", "manylinux2014", "quay.io/pypa/manylinux2014_x86_64:*"),
("pypy_x86_64", "manylinux_2_28", "quay.io/pypa/manylinux_2_28_x86_64:*"),
("pypy_x86_64", "manylinux_2_34", "quay.io/pypa/manylinux_2_34_x86_64:*"),
diff --git a/unit_test/option_prepare_test.py b/unit_test/option_prepare_test.py
index 27b25377c..44f4da79b 100644
--- a/unit_test/option_prepare_test.py
+++ b/unit_test/option_prepare_test.py
@@ -59,7 +59,7 @@ def test_build_default_launches(monkeypatch):
# In Python 3.8+, this can be simplified to [0].kwargs
kwargs = build_in_container.call_args_list[0][1]
- assert "quay.io/pypa/manylinux2014_x86_64" in kwargs["container"]["image"]
+ assert "quay.io/pypa/manylinux_2_28_x86_64" in kwargs["container"]["image"]
assert kwargs["container"]["cwd"] == PurePosixPath("/project")
assert kwargs["container"]["oci_platform"] == OCIPlatform.AMD64
diff --git a/unit_test/options_toml_test.py b/unit_test/options_toml_test.py
index 03e125013..e5c8d5593 100644
--- a/unit_test/options_toml_test.py
+++ b/unit_test/options_toml_test.py
@@ -276,7 +276,7 @@ def test_environment_override_empty(tmp_path):
assert options_reader.get("manylinux-i686-image") == ""
assert options_reader.get("manylinux-aarch64-image") == "manylinux1"
- assert options_reader.get("manylinux-x86_64-image", ignore_empty=True) == "manylinux2014"
+ assert options_reader.get("manylinux-x86_64-image", ignore_empty=True) == "manylinux_2_28"
assert options_reader.get("manylinux-i686-image", ignore_empty=True) == "manylinux1"
assert options_reader.get("manylinux-aarch64-image", ignore_empty=True) == "manylinux1"