diff --git a/cibuildwheel/platforms/android.py b/cibuildwheel/platforms/android.py index b3f614fba..d9822062b 100644 --- a/cibuildwheel/platforms/android.py +++ b/cibuildwheel/platforms/android.py @@ -435,7 +435,11 @@ def repair_wheel(state: BuildState, built_wheel: Path) -> Path: if state.options.repair_command: shell( prepare_command( - state.options.repair_command, wheel=built_wheel, dest_dir=repaired_wheel_dir + state.options.repair_command, + wheel=built_wheel, + dest_dir=repaired_wheel_dir, + package=state.options.package_dir, + project=".", ), env=state.build_env, ) diff --git a/cibuildwheel/platforms/linux.py b/cibuildwheel/platforms/linux.py index 8411a935a..6903079bd 100644 --- a/cibuildwheel/platforms/linux.py +++ b/cibuildwheel/platforms/linux.py @@ -320,7 +320,11 @@ def build_in_container( 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 + build_options.repair_command, + wheel=built_wheel, + dest_dir=repaired_wheel_dir, + package=container_package_dir, + project=container_project_path, ) container.call(["sh", "-c", repair_command_prepared], env=env) else: diff --git a/cibuildwheel/platforms/macos.py b/cibuildwheel/platforms/macos.py index 05d5d1623..255df9996 100644 --- a/cibuildwheel/platforms/macos.py +++ b/cibuildwheel/platforms/macos.py @@ -525,6 +525,8 @@ def build(options: Options, tmp_path: Path) -> None: wheel=built_wheel, dest_dir=repaired_wheel_dir, delocate_archs=delocate_archs, + package=build_options.package_dir, + project=".", ) shell(repair_command_prepared, env=env) else: diff --git a/cibuildwheel/platforms/pyodide.py b/cibuildwheel/platforms/pyodide.py index 60d707078..8b8ba2aa5 100644 --- a/cibuildwheel/platforms/pyodide.py +++ b/cibuildwheel/platforms/pyodide.py @@ -443,6 +443,8 @@ def build(options: Options, tmp_path: Path) -> None: build_options.repair_command, wheel=built_wheel, dest_dir=repaired_wheel_dir, + package=build_options.package_dir, + project=".", ) shell(repair_command_prepared, env=env) log.step_end() diff --git a/cibuildwheel/platforms/windows.py b/cibuildwheel/platforms/windows.py index ffc0d7761..24a33c05d 100644 --- a/cibuildwheel/platforms/windows.py +++ b/cibuildwheel/platforms/windows.py @@ -525,6 +525,8 @@ def build(options: Options, tmp_path: Path) -> None: 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: diff --git a/docs/options.md b/docs/options.md index 34c082bc0..6d846c4d6 100644 --- a/docs/options.md +++ b/docs/options.md @@ -904,6 +904,8 @@ The following placeholders must be used inside the command and will be replaced - `{dest_dir}` for the absolute path of the directory where to create the repaired wheel - `{delocate_archs}` (macOS only) comma-separated list of architectures in the wheel. +You can use the `{package}` or `{project}` placeholders in your `repair-wheel-command` to refer to the package being built or the project root, respectively. + The command is run in a shell, so you can run multiple commands like `cmd1 && cmd2`. Platform-specific environment variables are also available:
diff --git a/test/test_custom_repair_wheel.py b/test/test_custom_repair_wheel.py index 5af012653..e899cf4d5 100644 --- a/test/test_custom_repair_wheel.py +++ b/test/test_custom_repair_wheel.py @@ -56,3 +56,65 @@ def test(tmp_path, capfd): # We only produced one wheel (perhaps Pyodide) # check that it has the right name assert result[0].startswith("spam-0.1.0-py2-none-") + + +def test_package_placeholder(tmp_path, capfd): + # Identical to the previous test with the additional assumption that the {project} + # placeholder has been replaced correctly + + project_dir = tmp_path / "project" + basic_project.generate(project_dir) + + num_builds = len(utils.cibuildwheel_get_build_identifiers(project_dir)) + expectation = ( + pytest.raises(subprocess.CalledProcessError) if num_builds > 1 else does_not_raise() + ) + + with expectation as exc_info: + result = utils.cibuildwheel_run( + project_dir, + add_env={ + "CIBW_REPAIR_WHEEL_COMMAND": "python {package}/repair.py {wheel} {dest_dir}", + }, + ) + + captured = capfd.readouterr() + if num_builds > 1: + assert exc_info is not None + assert "Build failed because a wheel named" in captured.err + assert exc_info.value.returncode == 6 + else: + # We only produced one wheel (perhaps Pyodide) + # check that it has the right name + assert result[0].startswith("spam-0.1.0-py2-none-") + + +def test_project_placeholder(tmp_path, capfd): + # Identical to the previous two tests with the additional assumption that the {package} + # placeholder has been replaced correctly + + project_dir = tmp_path / "project" + basic_project.generate(project_dir) + + num_builds = len(utils.cibuildwheel_get_build_identifiers(project_dir)) + expectation = ( + pytest.raises(subprocess.CalledProcessError) if num_builds > 1 else does_not_raise() + ) + + with expectation as exc_info: + result = utils.cibuildwheel_run( + project_dir, + add_env={ + "CIBW_REPAIR_WHEEL_COMMAND": "python {project}/repair.py {wheel} {dest_dir}", + }, + ) + + captured = capfd.readouterr() + if num_builds > 1: + assert exc_info is not None + assert "Build failed because a wheel named" in captured.err + assert exc_info.value.returncode == 6 + else: + # We only produced one wheel (perhaps Pyodide) + # check that it has the right name + assert result[0].startswith("spam-0.1.0-py2-none-")