diff --git a/cibuildwheel/errors.py b/cibuildwheel/errors.py index dee96d97e..2a6ae594e 100644 --- a/cibuildwheel/errors.py +++ b/cibuildwheel/errors.py @@ -109,3 +109,19 @@ class AuditCommandFailedError(FatalError): def __init__(self, message: str) -> None: super().__init__(message) self.return_code = 9 + + +class BuildProducedNoWheelError(FatalError): + def __init__(self) -> None: + message = textwrap.dedent( + """ + Build failed because the build frontend completed successfully but + did not produce a wheel. + + This usually indicates a problem with your project configuration or + build backend. Check your project configuration, or run cibuildwheel + with CIBW_BUILD_VERBOSITY=1 to view build logs. + """ + ) + super().__init__(message) + self.return_code = 10 diff --git a/cibuildwheel/platforms/android.py b/cibuildwheel/platforms/android.py index 52773e058..d65de5ac5 100644 --- a/cibuildwheel/platforms/android.py +++ b/cibuildwheel/platforms/android.py @@ -410,10 +410,14 @@ def setup_android_env( # * a key=value string, without quotes for i, token in enumerate(shlex.split(env_output)): if i % 2 == 0: - assert token == "export", token + if token != "export": + msg = f"Unexpected output from android.py env: expected 'export', got {token!r}" + raise errors.FatalError(msg) else: key, sep, value = token.partition("=") - assert sep == "=", token + if sep != "=": + msg = f"Unexpected output from android.py env: expected 'key=value', got {token!r}" + raise errors.FatalError(msg) android_env[key] = value # localized_vars cleared the CFLAGS and CXXFLAGS in the sysconfigdata, but most diff --git a/cibuildwheel/platforms/ios.py b/cibuildwheel/platforms/ios.py index 751f3616e..61e7574dd 100644 --- a/cibuildwheel/platforms/ios.py +++ b/cibuildwheel/platforms/ios.py @@ -95,6 +95,14 @@ def build_python_config(config_dict: dict[str, str]) -> dict[str, str]: matching = [ config for config in macos_python_configs if config["identifier"] == macos_identifier ] + if not matching: + msg = ( + f"Internal error: no macOS configuration found matching " + f"{macos_identifier!r}, needed to build the iOS configuration " + f"{config_dict['identifier']!r}. The bundled build-platforms.toml " + f"resources are inconsistent." + ) + raise errors.FatalError(msg) return matching[0] # Load the platform configuration @@ -560,7 +568,10 @@ def build(options: Options, tmp_path: Path) -> None: case _: assert_never(build_frontend) - built_wheel = next(built_wheel_dir.glob("*.whl")) + try: + built_wheel = next(built_wheel_dir.glob("*.whl")) + except StopIteration: + raise errors.BuildProducedNoWheelError() from None if built_wheel.name.endswith("none-any.whl"): raise errors.NonPlatformWheelError() diff --git a/cibuildwheel/platforms/linux.py b/cibuildwheel/platforms/linux.py index f97f6a6a1..606eacc48 100644 --- a/cibuildwheel/platforms/linux.py +++ b/cibuildwheel/platforms/linux.py @@ -346,7 +346,10 @@ def build_in_container( case _: assert_never(build_frontend) - built_wheel = container.glob(built_wheel_dir, "*.whl")[0] + try: + built_wheel = container.glob(built_wheel_dir, "*.whl")[0] + except IndexError: + raise errors.BuildProducedNoWheelError() from None repaired_wheel_dir = temp_dir / "repaired_wheel" container.call(["rm", "-rf", repaired_wheel_dir]) diff --git a/cibuildwheel/platforms/macos.py b/cibuildwheel/platforms/macos.py index c2aa23df2..4a20012d8 100644 --- a/cibuildwheel/platforms/macos.py +++ b/cibuildwheel/platforms/macos.py @@ -566,7 +566,10 @@ def build(options: Options, tmp_path: Path) -> None: case _: assert_never(build_frontend) - built_wheel = next(built_wheel_dir.glob("*.whl")) + try: + built_wheel = next(built_wheel_dir.glob("*.whl")) + except StopIteration: + raise errors.BuildProducedNoWheelError() from None repaired_wheel_dir.mkdir() diff --git a/cibuildwheel/platforms/pyodide.py b/cibuildwheel/platforms/pyodide.py index b4f7cd1b7..eb2bc032c 100644 --- a/cibuildwheel/platforms/pyodide.py +++ b/cibuildwheel/platforms/pyodide.py @@ -451,7 +451,10 @@ def build(options: Options, tmp_path: Path) -> None: *extra_flags, env=env, ) - built_wheel = next(built_wheel_dir.glob("*.whl")) + try: + built_wheel = next(built_wheel_dir.glob("*.whl")) + except StopIteration: + raise errors.BuildProducedNoWheelError() from None if built_wheel.name.endswith("none-any.whl"): raise errors.NonPlatformWheelError() @@ -471,7 +474,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/platforms/windows.py b/cibuildwheel/platforms/windows.py index dc0a2977b..6d1eaa4af 100644 --- a/cibuildwheel/platforms/windows.py +++ b/cibuildwheel/platforms/windows.py @@ -522,7 +522,10 @@ def build(options: Options, tmp_path: Path) -> None: case _: assert_never(build_frontend) - built_wheel = next(built_wheel_dir.glob("*.whl")) + try: + built_wheel = next(built_wheel_dir.glob("*.whl")) + except StopIteration: + raise errors.BuildProducedNoWheelError() from None # repair the wheel repaired_wheel_dir.mkdir()