fix: error out if multiple wheels with the same name are produced
We shouldn't silently overwrite wheels that are produced in the same run that have the same filename, as this implies a misconfiguration.
This commit is contained in:
@@ -10,6 +10,7 @@ from .logger import log
|
|||||||
from .options import Options
|
from .options import Options
|
||||||
from .typing import OrderedDict, PathOrStr, assert_never
|
from .typing import OrderedDict, PathOrStr, assert_never
|
||||||
from .util import (
|
from .util import (
|
||||||
|
AlreadyBuiltWheelError,
|
||||||
BuildSelector,
|
BuildSelector,
|
||||||
NonPlatformWheelError,
|
NonPlatformWheelError,
|
||||||
find_compatible_wheel,
|
find_compatible_wheel,
|
||||||
@@ -255,6 +256,10 @@ def build_on_docker(
|
|||||||
|
|
||||||
repaired_wheels = docker.glob(repaired_wheel_dir, "*.whl")
|
repaired_wheels = docker.glob(repaired_wheel_dir, "*.whl")
|
||||||
|
|
||||||
|
for repaired_wheel in repaired_wheels:
|
||||||
|
if repaired_wheel.name in {wheel.name for wheel in built_wheels}:
|
||||||
|
raise AlreadyBuiltWheelError(repaired_wheel.name)
|
||||||
|
|
||||||
if build_options.test_command and build_options.test_selector(config.identifier):
|
if build_options.test_command and build_options.test_selector(config.identifier):
|
||||||
log.step("Testing wheel...")
|
log.step("Testing wheel...")
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ from .options import Options
|
|||||||
from .typing import Literal, PathOrStr, assert_never
|
from .typing import Literal, PathOrStr, assert_never
|
||||||
from .util import (
|
from .util import (
|
||||||
CIBW_CACHE_PATH,
|
CIBW_CACHE_PATH,
|
||||||
|
AlreadyBuiltWheelError,
|
||||||
BuildFrontend,
|
BuildFrontend,
|
||||||
BuildSelector,
|
BuildSelector,
|
||||||
NonPlatformWheelError,
|
NonPlatformWheelError,
|
||||||
@@ -408,6 +409,9 @@ def build(options: Options, tmp_path: Path) -> None:
|
|||||||
|
|
||||||
repaired_wheel = next(repaired_wheel_dir.glob("*.whl"))
|
repaired_wheel = next(repaired_wheel_dir.glob("*.whl"))
|
||||||
|
|
||||||
|
if repaired_wheel.name in {wheel.name for wheel in built_wheels}:
|
||||||
|
raise AlreadyBuiltWheelError(repaired_wheel.name)
|
||||||
|
|
||||||
log.step_end()
|
log.step_end()
|
||||||
|
|
||||||
if build_options.test_command and build_options.test_selector(config.identifier):
|
if build_options.test_command and build_options.test_selector(config.identifier):
|
||||||
|
|||||||
@@ -373,6 +373,20 @@ class NonPlatformWheelError(Exception):
|
|||||||
super().__init__(message)
|
super().__init__(message)
|
||||||
|
|
||||||
|
|
||||||
|
class AlreadyBuiltWheelError(Exception):
|
||||||
|
def __init__(self, wheel_name: str) -> None:
|
||||||
|
message = textwrap.dedent(
|
||||||
|
f"""
|
||||||
|
cibuildwheel: Build failed because a wheel named {wheel_name} was already generated in the current run.
|
||||||
|
|
||||||
|
If you expected another wheel to be generated, check your project configuration, or run
|
||||||
|
cibuildwheel with CIBW_BUILD_VERBOSITY=1 to view build logs.
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
super().__init__(message)
|
||||||
|
|
||||||
|
|
||||||
def strtobool(val: str) -> bool:
|
def strtobool(val: str) -> bool:
|
||||||
return val.lower() in {"y", "yes", "t", "true", "on", "1"}
|
return val.lower() in {"y", "yes", "t", "true", "on", "1"}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ from .options import Options
|
|||||||
from .typing import PathOrStr, assert_never
|
from .typing import PathOrStr, assert_never
|
||||||
from .util import (
|
from .util import (
|
||||||
CIBW_CACHE_PATH,
|
CIBW_CACHE_PATH,
|
||||||
|
AlreadyBuiltWheelError,
|
||||||
BuildFrontend,
|
BuildFrontend,
|
||||||
BuildSelector,
|
BuildSelector,
|
||||||
NonPlatformWheelError,
|
NonPlatformWheelError,
|
||||||
@@ -365,6 +366,9 @@ def build(options: Options, tmp_path: Path) -> None:
|
|||||||
|
|
||||||
repaired_wheel = next(repaired_wheel_dir.glob("*.whl"))
|
repaired_wheel = next(repaired_wheel_dir.glob("*.whl"))
|
||||||
|
|
||||||
|
if repaired_wheel.name in {wheel.name for wheel in built_wheels}:
|
||||||
|
raise AlreadyBuiltWheelError(repaired_wheel.name)
|
||||||
|
|
||||||
if build_options.test_command and options.globals.test_selector(config.identifier):
|
if build_options.test_command and options.globals.test_selector(config.identifier):
|
||||||
log.step("Testing wheel...")
|
log.step("Testing wheel...")
|
||||||
# set up a virtual environment to install and test from, to make sure
|
# set up a virtual environment to install and test from, to make sure
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import subprocess
|
||||||
|
from test import test_projects
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from . import utils
|
||||||
|
|
||||||
|
basic_project = test_projects.new_c_project()
|
||||||
|
basic_project.files[
|
||||||
|
"repair.py"
|
||||||
|
] = """
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
wheel = Path(sys.argv[1])
|
||||||
|
dest_dir = Path(sys.argv[2])
|
||||||
|
platform = wheel.stem.split("-")[-1]
|
||||||
|
name = f"spam-0.1.0-py2-none-{platform}.whl"
|
||||||
|
dest = dest_dir / name
|
||||||
|
dest_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
if dest.exists():
|
||||||
|
dest.unlink()
|
||||||
|
shutil.copy(wheel, dest)
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def test(tmp_path, capfd):
|
||||||
|
# this test checks that a generated wheel name shall be unique in a given cibuildwheel run
|
||||||
|
project_dir = tmp_path / "project"
|
||||||
|
basic_project.generate(project_dir)
|
||||||
|
|
||||||
|
with pytest.raises(subprocess.CalledProcessError):
|
||||||
|
utils.cibuildwheel_run(
|
||||||
|
project_dir,
|
||||||
|
add_env={
|
||||||
|
"CIBW_REPAIR_WHEEL_COMMAND": "python repair.py {wheel} {dest_dir}",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
captured = capfd.readouterr()
|
||||||
|
assert "Build failed because a wheel named" in captured.err
|
||||||
Reference in New Issue
Block a user