From fd27532d12a7817f5e77461d90171d87f2a1092b Mon Sep 17 00:00:00 2001 From: Matthieu Darbois Date: Mon, 9 Mar 2026 20:56:45 +0100 Subject: [PATCH] fix: call `repair-wheel-command` in iOS builds (#2761) --- cibuildwheel/platforms/ios.py | 34 ++++++++++++++++++++++++++++++---- test/test_ios.py | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/cibuildwheel/platforms/ios.py b/cibuildwheel/platforms/ios.py index a4229e99..bdb2ad8b 100644 --- a/cibuildwheel/platforms/ios.py +++ b/cibuildwheel/platforms/ios.py @@ -450,6 +450,7 @@ def build(options: Options, tmp_path: Path) -> None: identifier_tmp_dir = tmp_path / config.identifier identifier_tmp_dir.mkdir() built_wheel_dir = identifier_tmp_dir / "built_wheel" + repaired_wheel_dir = identifier_tmp_dir / "repaired_wheel" constraints_path = build_options.dependency_constraints.get_for_python_version( version=config.version, tmp_dir=identifier_tmp_dir @@ -523,11 +524,36 @@ def build(options: Options, tmp_path: Path) -> None: case _: assert_never(build_frontend) - test_wheel = built_wheel = next(built_wheel_dir.glob("*.whl")) + built_wheel = next(built_wheel_dir.glob("*.whl")) if built_wheel.name.endswith("none-any.whl"): raise errors.NonPlatformWheelError() + 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, + package=build_options.package_dir, + project=".", + ) + shell(repair_command_prepared, env=env) + else: + shutil.move(str(built_wheel), repaired_wheel_dir) + + try: + repaired_wheel = next(repaired_wheel_dir.glob("*.whl")) + except StopIteration: + raise errors.RepairStepProducedNoWheelError() from None + + if repaired_wheel.name in {wheel.name for wheel in built_wheels}: + raise errors.AlreadyBuiltWheelError(repaired_wheel.name) + + test_wheel = repaired_wheel + log.step_end() if build_options.test_command and build_options.test_selector(config.identifier): @@ -695,11 +721,11 @@ def build(options: Options, tmp_path: Path) -> None: # We're all done here; move it to output (overwrite existing) output_wheel: Path | None = None if compatible_wheel is None: - output_wheel = build_options.output_dir.joinpath(built_wheel.name) - moved_wheel = move_file(built_wheel, output_wheel) + output_wheel = build_options.output_dir.joinpath(repaired_wheel.name) + moved_wheel = move_file(repaired_wheel, output_wheel) if moved_wheel != output_wheel.resolve(): log.warning( - f"{built_wheel} was moved to {moved_wheel} instead of {output_wheel}" + f"{repaired_wheel} was moved to {moved_wheel} instead of {output_wheel}" ) built_wheels.append(output_wheel) diff --git a/test/test_ios.py b/test/test_ios.py index be64e36f..73f380ca 100644 --- a/test/test_ios.py +++ b/test/test_ios.py @@ -347,3 +347,37 @@ def test_ios_test_command_invalid(tmp_path, capfd): ) _, err = capfd.readouterr() assert "iOS tests configured with a test command which doesn't start with 'python -m'" in err + + +def test_repair_step(tmp_path): + """Build will succeed & the custom repair step is called.""" + skip_if_ios_testing_not_supported() + + project_dir = tmp_path / "project" + basic_project = test_projects.new_c_project() + basic_project.files.update(basic_project_files) + basic_project.generate(project_dir) + + # Build, but don't test the wheels; we're only checking that the right + # warning was raised. + actual_wheels = utils.cibuildwheel_run( + project_dir, + add_env={ + "CIBW_PLATFORM": "ios", + "CIBW_BUILD": "cp314-*", + "CIBW_TEST_SKIP": "*", + "CIBW_REPAIR_WHEEL_COMMAND": "touch .ios-repair-step && mv {wheel} {dest_dir}", + }, + ) + + # The expected wheels were produced. + expected_wheels = utils.expected_wheels( + "spam", + "0.1.0", + platform="ios", + python_abi_tags=["cp314-cp314"], + ) + assert set(actual_wheels) == set(expected_wheels) + + # The custom repair step is called. + assert project_dir.joinpath(".ios-repair-step").is_file()