Skip to content

Commit 099d397

Browse files
authored
Merge pull request #1599 from mayeut/manylinux-entrypoint
fix: do not use `linux32` when unnecessary
2 parents 7222265 + 7a8b801 commit 099d397

File tree

6 files changed

+88
-38
lines changed

6 files changed

+88
-38
lines changed

.github/workflows/test.yml

+7
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,13 @@ jobs:
6262
sudo apt-get update
6363
sudo apt-get -y install podman
6464
65+
# free some space to prevent reaching GHA disk space limits
66+
- name: Clean docker images
67+
if: runner.os == 'Linux'
68+
run: |
69+
docker system prune -a -f
70+
df -h
71+
6572
- name: Install dependencies
6673
run: |
6774
python -m pip install ".[test]"

cibuildwheel/linux.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,7 @@ def build(options: Options, tmp_path: Path) -> None: # noqa: ARG001
423423

424424
with OCIContainer(
425425
image=build_step.container_image,
426-
simulate_32_bit=build_step.platform_tag.endswith("i686"),
426+
enforce_32_bit=build_step.platform_tag.endswith("i686"),
427427
cwd=container_project_path,
428428
engine=options.globals.container_engine,
429429
) as container:

cibuildwheel/oci_container.py

+14-4
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from typing import IO, Dict, Literal
1818

1919
from .typing import PathOrStr, PopenBytes
20-
from .util import CIProvider, detect_ci_provider, parse_key_value_string
20+
from .util import CIProvider, call, detect_ci_provider, parse_key_value_string
2121

2222
ContainerEngineName = Literal["docker", "podman"]
2323

@@ -85,7 +85,7 @@ def __init__(
8585
self,
8686
*,
8787
image: str,
88-
simulate_32_bit: bool = False,
88+
enforce_32_bit: bool = False,
8989
cwd: PathOrStr | None = None,
9090
engine: OCIContainerEngineConfig = DEFAULT_ENGINE,
9191
):
@@ -94,7 +94,7 @@ def __init__(
9494
raise ValueError(msg)
9595

9696
self.image = image
97-
self.simulate_32_bit = simulate_32_bit
97+
self.enforce_32_bit = enforce_32_bit
9898
self.cwd = cwd
9999
self.name: str | None = None
100100
self.engine = engine
@@ -110,7 +110,17 @@ def __enter__(self) -> OCIContainer:
110110
if detect_ci_provider() == CIProvider.travis_ci and platform.machine() == "ppc64le":
111111
network_args = ["--network=host"]
112112

113-
shell_args = ["linux32", "/bin/bash"] if self.simulate_32_bit else ["/bin/bash"]
113+
simulate_32_bit = False
114+
if self.enforce_32_bit:
115+
# If the architecture running the image is already the right one
116+
# or the image entrypoint takes care of enforcing this, then we don't need to
117+
# simulate this
118+
container_machine = call(
119+
self.engine.name, "run", "--rm", self.image, "uname", "-m", capture_stdout=True
120+
).strip()
121+
simulate_32_bit = container_machine != "i686"
122+
123+
shell_args = ["linux32", "/bin/bash"] if simulate_32_bit else ["/bin/bash"]
114124

115125
subprocess.run(
116126
[

test/conftest.py

+2-6
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ def build_frontend_env(request) -> dict[str, str]:
3434
@pytest.fixture()
3535
def docker_cleanup() -> Generator[None, None, None]:
3636
def get_images() -> set[str]:
37+
if detect_ci_provider() is None or platform != "linux":
38+
return set()
3739
images = subprocess.run(
3840
["docker", "image", "ls", "--format", "{{json .ID}}"],
3941
text=True,
@@ -42,12 +44,6 @@ def get_images() -> set[str]:
4244
).stdout
4345
return {json.loads(image.strip()) for image in images.splitlines() if image.strip()}
4446

45-
if detect_ci_provider() is None or platform != "linux":
46-
try:
47-
yield
48-
finally:
49-
pass
50-
return
5147
images_before = get_images()
5248
try:
5349
yield

unit_test/oci_container_test.py

+53-16
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import json
34
import os
45
import platform
56
import random
@@ -13,33 +14,49 @@
1314

1415
from cibuildwheel.environment import EnvironmentAssignmentBash
1516
from cibuildwheel.oci_container import OCIContainer, OCIContainerEngineConfig
17+
from cibuildwheel.util import detect_ci_provider
1618

1719
# Test utilities
1820

1921
# for these tests we use manylinux2014 images, because they're available on
2022
# multi architectures and include python3.8
23+
DEFAULT_IMAGE_TEMPLATE = "quay.io/pypa/manylinux2014_{machine}:2023-09-04-0828984"
2124
pm = platform.machine()
22-
if pm == "x86_64":
23-
DEFAULT_IMAGE = "quay.io/pypa/manylinux2014_x86_64:2020-05-17-2f8ac3b"
25+
if pm in {"x86_64", "ppc64le", "s390x"}:
26+
DEFAULT_IMAGE = DEFAULT_IMAGE_TEMPLATE.format(machine=pm)
2427
elif pm in {"aarch64", "arm64"}:
25-
DEFAULT_IMAGE = "quay.io/pypa/manylinux2014_aarch64:2020-05-17-2f8ac3b"
26-
elif pm == "ppc64le":
27-
DEFAULT_IMAGE = "quay.io/pypa/manylinux2014_ppc64le:2020-05-17-2f8ac3b"
28-
elif pm == "s390x":
29-
DEFAULT_IMAGE = "quay.io/pypa/manylinux2014_s390x:2020-05-17-2f8ac3b"
28+
DEFAULT_IMAGE = DEFAULT_IMAGE_TEMPLATE.format(machine="aarch64")
3029
else:
3130
DEFAULT_IMAGE = ""
3231

3332
PODMAN = OCIContainerEngineConfig(name="podman")
3433

3534

36-
@pytest.fixture(params=["docker", "podman"])
35+
@pytest.fixture(params=["docker", "podman"], scope="module")
3736
def container_engine(request):
3837
if request.param == "docker" and not request.config.getoption("--run-docker"):
3938
pytest.skip("need --run-docker option to run")
4039
if request.param == "podman" and not request.config.getoption("--run-podman"):
4140
pytest.skip("need --run-podman option to run")
42-
return OCIContainerEngineConfig(name=request.param)
41+
42+
def get_images() -> set[str]:
43+
if detect_ci_provider() is None:
44+
return set()
45+
images = subprocess.run(
46+
[request.param, "image", "ls", "--format", "{{json .ID}}"],
47+
text=True,
48+
check=True,
49+
stdout=subprocess.PIPE,
50+
).stdout
51+
return {json.loads(image.strip()) for image in images.splitlines() if image.strip()}
52+
53+
images_before = get_images()
54+
try:
55+
yield OCIContainerEngineConfig(name=request.param)
56+
finally:
57+
images_after = get_images()
58+
for image in images_after - images_before:
59+
subprocess.run([request.param, "rmi", image], check=False)
4360

4461

4562
# Tests
@@ -232,10 +249,9 @@ def test_environment_executor(container_engine):
232249
assert assignment.evaluated_value({}, container.environment_executor) == "42"
233250

234251

235-
def test_podman_vfs(tmp_path: Path, monkeypatch, request):
236-
# Tests podman VFS, for the podman in docker use-case
237-
if not request.config.getoption("--run-podman"):
238-
pytest.skip("need --run-podman option to run")
252+
def test_podman_vfs(tmp_path: Path, monkeypatch, container_engine):
253+
if container_engine.name != "podman":
254+
pytest.skip("only runs with podman")
239255

240256
# create the VFS configuration
241257
vfs_path = tmp_path / "podman_vfs"
@@ -311,9 +327,9 @@ def test_podman_vfs(tmp_path: Path, monkeypatch, request):
311327
subprocess.run(["podman", "unshare", "rm", "-rf", vfs_path], check=True)
312328

313329

314-
def test_create_args_volume(tmp_path: Path, request):
315-
if not request.config.getoption("--run-docker"):
316-
pytest.skip("need --run-docker option to run")
330+
def test_create_args_volume(tmp_path: Path, container_engine):
331+
if container_engine.name != "docker":
332+
pytest.skip("only runs with docker")
317333

318334
if "CIRCLECI" in os.environ or "GITLAB_CI" in os.environ:
319335
pytest.skip(
@@ -378,3 +394,24 @@ def test_parse_engine_config(config, name, create_args):
378394
engine_config = OCIContainerEngineConfig.from_config_string(config)
379395
assert engine_config.name == name
380396
assert engine_config.create_args == create_args
397+
398+
399+
@pytest.mark.skipif(pm != "x86_64", reason="Only runs on x86_64")
400+
@pytest.mark.parametrize(
401+
("image", "shell_args"),
402+
[
403+
(DEFAULT_IMAGE_TEMPLATE.format(machine="i686"), ["/bin/bash"]),
404+
(DEFAULT_IMAGE_TEMPLATE.format(machine="x86_64"), ["linux32", "/bin/bash"]),
405+
],
406+
)
407+
def test_enforce_32_bit(container_engine, image, shell_args):
408+
with OCIContainer(engine=container_engine, image=image, enforce_32_bit=True) as container:
409+
assert container.call(["uname", "-m"], capture_output=True).strip() == "i686"
410+
container_args = subprocess.run(
411+
f"{container.engine.name} inspect -f '{{{{json .Args }}}}' {container.name}",
412+
shell=True,
413+
check=True,
414+
stdout=subprocess.PIPE,
415+
text=True,
416+
).stdout
417+
assert json.loads(container_args) == shell_args

unit_test/option_prepare_test.py

+11-11
Original file line numberDiff line numberDiff line change
@@ -72,23 +72,23 @@ def test_build_default_launches(monkeypatch):
7272
kwargs = build_in_container.call_args_list[0][1]
7373
assert "quay.io/pypa/manylinux2014_x86_64" in kwargs["container"]["image"]
7474
assert kwargs["container"]["cwd"] == PurePosixPath("/project")
75-
assert not kwargs["container"]["simulate_32_bit"]
75+
assert not kwargs["container"]["enforce_32_bit"]
7676

7777
identifiers = {x.identifier for x in kwargs["platform_configs"]}
7878
assert identifiers == {f"{x}-manylinux_x86_64" for x in ALL_IDS}
7979

8080
kwargs = build_in_container.call_args_list[1][1]
8181
assert "quay.io/pypa/manylinux2014_i686" in kwargs["container"]["image"]
8282
assert kwargs["container"]["cwd"] == PurePosixPath("/project")
83-
assert kwargs["container"]["simulate_32_bit"]
83+
assert kwargs["container"]["enforce_32_bit"]
8484

8585
identifiers = {x.identifier for x in kwargs["platform_configs"]}
8686
assert identifiers == {f"{x}-manylinux_i686" for x in ALL_IDS}
8787

8888
kwargs = build_in_container.call_args_list[2][1]
8989
assert "quay.io/pypa/musllinux_1_1_x86_64" in kwargs["container"]["image"]
9090
assert kwargs["container"]["cwd"] == PurePosixPath("/project")
91-
assert not kwargs["container"]["simulate_32_bit"]
91+
assert not kwargs["container"]["enforce_32_bit"]
9292

9393
identifiers = {x.identifier for x in kwargs["platform_configs"]}
9494
assert identifiers == {
@@ -98,7 +98,7 @@ def test_build_default_launches(monkeypatch):
9898
kwargs = build_in_container.call_args_list[3][1]
9999
assert "quay.io/pypa/musllinux_1_1_i686" in kwargs["container"]["image"]
100100
assert kwargs["container"]["cwd"] == PurePosixPath("/project")
101-
assert kwargs["container"]["simulate_32_bit"]
101+
assert kwargs["container"]["enforce_32_bit"]
102102

103103
identifiers = {x.identifier for x in kwargs["platform_configs"]}
104104
assert identifiers == {f"{x}-musllinux_i686" for x in ALL_IDS if "pp" not in x}
@@ -141,7 +141,7 @@ def test_build_with_override_launches(monkeypatch, tmp_path):
141141
kwargs = build_in_container.call_args_list[0][1]
142142
assert "quay.io/pypa/manylinux2014_x86_64" in kwargs["container"]["image"]
143143
assert kwargs["container"]["cwd"] == PurePosixPath("/project")
144-
assert not kwargs["container"]["simulate_32_bit"]
144+
assert not kwargs["container"]["enforce_32_bit"]
145145

146146
identifiers = {x.identifier for x in kwargs["platform_configs"]}
147147
assert identifiers == {"cp36-manylinux_x86_64"}
@@ -150,7 +150,7 @@ def test_build_with_override_launches(monkeypatch, tmp_path):
150150
kwargs = build_in_container.call_args_list[1][1]
151151
assert "quay.io/pypa/manylinux2014_x86_64" in kwargs["container"]["image"]
152152
assert kwargs["container"]["cwd"] == PurePosixPath("/project")
153-
assert not kwargs["container"]["simulate_32_bit"]
153+
assert not kwargs["container"]["enforce_32_bit"]
154154

155155
identifiers = {x.identifier for x in kwargs["platform_configs"]}
156156
assert identifiers == {
@@ -162,7 +162,7 @@ def test_build_with_override_launches(monkeypatch, tmp_path):
162162
kwargs = build_in_container.call_args_list[2][1]
163163
assert "quay.io/pypa/manylinux_2_28_x86_64" in kwargs["container"]["image"]
164164
assert kwargs["container"]["cwd"] == PurePosixPath("/project")
165-
assert not kwargs["container"]["simulate_32_bit"]
165+
assert not kwargs["container"]["enforce_32_bit"]
166166
identifiers = {x.identifier for x in kwargs["platform_configs"]}
167167
assert identifiers == {
168168
f"{x}-manylinux_x86_64"
@@ -172,15 +172,15 @@ def test_build_with_override_launches(monkeypatch, tmp_path):
172172
kwargs = build_in_container.call_args_list[3][1]
173173
assert "quay.io/pypa/manylinux2014_i686" in kwargs["container"]["image"]
174174
assert kwargs["container"]["cwd"] == PurePosixPath("/project")
175-
assert kwargs["container"]["simulate_32_bit"]
175+
assert kwargs["container"]["enforce_32_bit"]
176176

177177
identifiers = {x.identifier for x in kwargs["platform_configs"]}
178178
assert identifiers == {f"{x}-manylinux_i686" for x in ALL_IDS}
179179

180180
kwargs = build_in_container.call_args_list[4][1]
181181
assert "quay.io/pypa/musllinux_1_1_x86_64" in kwargs["container"]["image"]
182182
assert kwargs["container"]["cwd"] == PurePosixPath("/project")
183-
assert not kwargs["container"]["simulate_32_bit"]
183+
assert not kwargs["container"]["enforce_32_bit"]
184184

185185
identifiers = {x.identifier for x in kwargs["platform_configs"]}
186186
assert identifiers == {
@@ -190,7 +190,7 @@ def test_build_with_override_launches(monkeypatch, tmp_path):
190190
kwargs = build_in_container.call_args_list[5][1]
191191
assert "quay.io/pypa/musllinux_1_2_x86_64" in kwargs["container"]["image"]
192192
assert kwargs["container"]["cwd"] == PurePosixPath("/project")
193-
assert not kwargs["container"]["simulate_32_bit"]
193+
assert not kwargs["container"]["enforce_32_bit"]
194194
identifiers = {x.identifier for x in kwargs["platform_configs"]}
195195
assert identifiers == {
196196
f"{x}-musllinux_x86_64" for x in ALL_IDS - {"cp36", "cp37", "cp38", "cp39"} if "pp" not in x
@@ -199,7 +199,7 @@ def test_build_with_override_launches(monkeypatch, tmp_path):
199199
kwargs = build_in_container.call_args_list[6][1]
200200
assert "quay.io/pypa/musllinux_1_1_i686" in kwargs["container"]["image"]
201201
assert kwargs["container"]["cwd"] == PurePosixPath("/project")
202-
assert kwargs["container"]["simulate_32_bit"]
202+
assert kwargs["container"]["enforce_32_bit"]
203203

204204
identifiers = {x.identifier for x in kwargs["platform_configs"]}
205205
assert identifiers == {f"{x}-musllinux_i686" for x in ALL_IDS if "pp" not in x}

0 commit comments

Comments
 (0)