diff --git a/cibuildwheel/errors.py b/cibuildwheel/errors.py index 6f828b63..28939719 100644 --- a/cibuildwheel/errors.py +++ b/cibuildwheel/errors.py @@ -67,3 +67,21 @@ class OCIEngineTooOldError(FatalError): def __init__(self, message: str) -> None: super().__init__(message) self.return_code = 7 + + +class RepairStepProducedNoWheelError(FatalError): + def __init__(self) -> None: + message = textwrap.dedent( + """ + Build failed because the repair step completed successfully but + did not produce a wheel. + + Your `repair-wheel-command` is expected to place the repaired + wheel in the {dest_dir} directory. See the documentation for + example configurations: + + https://cibuildwheel.pypa.io/en/stable/options/#repair-wheel-command + """ + ) + super().__init__(message) + self.return_code = 8 diff --git a/cibuildwheel/linux.py b/cibuildwheel/linux.py index 9af58c9d..897b5f04 100644 --- a/cibuildwheel/linux.py +++ b/cibuildwheel/linux.py @@ -326,6 +326,9 @@ def build_in_container( repaired_wheels = container.glob(repaired_wheel_dir, "*.whl") + if not repaired_wheels: + raise errors.RepairStepProducedNoWheelError() + for repaired_wheel in repaired_wheels: if repaired_wheel.name in {wheel.name for wheel in built_wheels}: raise errors.AlreadyBuiltWheelError(repaired_wheel.name) diff --git a/cibuildwheel/macos.py b/cibuildwheel/macos.py index 2a65da73..f830dbcc 100644 --- a/cibuildwheel/macos.py +++ b/cibuildwheel/macos.py @@ -551,7 +551,10 @@ def build(options: Options, tmp_path: Path) -> None: else: shutil.move(str(built_wheel), repaired_wheel_dir) - repaired_wheel = next(repaired_wheel_dir.glob("*.whl")) + 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) diff --git a/cibuildwheel/windows.py b/cibuildwheel/windows.py index 909a022c..449be810 100644 --- a/cibuildwheel/windows.py +++ b/cibuildwheel/windows.py @@ -490,7 +490,10 @@ def build(options: Options, tmp_path: Path) -> None: else: shutil.move(str(built_wheel), repaired_wheel_dir) - repaired_wheel = next(repaired_wheel_dir.glob("*.whl")) + 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) diff --git a/docs/options.md b/docs/options.md index fc3b95e8..37f54bfa 100644 --- a/docs/options.md +++ b/docs/options.md @@ -1120,6 +1120,7 @@ Platform-specific environment variables are also available:
delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel} && pipx run abi3audit --strict --report {wheel} CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: > + copy {wheel} {dest_dir} && pipx run abi3audit --strict --report {wheel} ``` @@ -1158,7 +1159,10 @@ Platform-specific environment variables are also available:
"pipx run abi3audit --strict --report {wheel}", ] [tool.cibuildwheel.windows] - repair-wheel-command = "pipx run abi3audit --strict --report {wheel}" + repair-wheel-command = [ + "copy {wheel} {dest_dir}", + "pipx run abi3audit --strict --report {wheel}", + ] ``` In configuration mode, you can use an inline array, and the items will be joined with `&&`.