diff --git a/cibuildwheel/platforms/windows.py b/cibuildwheel/platforms/windows.py
index 29665b2ee..07d768b2e 100644
--- a/cibuildwheel/platforms/windows.py
+++ b/cibuildwheel/platforms/windows.py
@@ -321,12 +321,22 @@ def setup_python(
log.step("Installing build tools...")
match build_frontend:
+ case "pip":
+ call(
+ "pip",
+ "install",
+ "--upgrade",
+ "delvewheel",
+ *constraint_flags(dependency_constraint),
+ env=env,
+ )
case "build":
call(
"pip",
"install",
"--upgrade",
"build[virtualenv]",
+ "delvewheel",
*constraint_flags(dependency_constraint),
env=env,
)
@@ -340,9 +350,25 @@ def setup_python(
where_python,
"--upgrade",
"build[virtualenv]",
+ "delvewheel",
+ *constraint_flags(dependency_constraint),
+ env=env,
+ )
+ case "uv":
+ assert uv_path is not None
+ call(
+ uv_path,
+ "pip",
+ "install",
+ "--python",
+ where_python,
+ "--upgrade",
+ "delvewheel",
*constraint_flags(dependency_constraint),
env=env,
)
+ case _:
+ assert_never(build_frontend)
if python_libs_base:
# Set up the environment for various backends to enable cross-compilation
diff --git a/cibuildwheel/resources/constraints-python310.txt b/cibuildwheel/resources/constraints-python310.txt
index 2c5a63897..8d24e818b 100644
--- a/cibuildwheel/resources/constraints-python310.txt
+++ b/cibuildwheel/resources/constraints-python310.txt
@@ -6,6 +6,8 @@ build==1.5.0
# via -r cibuildwheel/resources/constraints.in
delocate==0.13.0
# via -r cibuildwheel/resources/constraints.in
+delvewheel==1.12.1
+ # via -r cibuildwheel/resources/constraints.in
distlib==0.4.0
# via virtualenv
filelock==3.29.0
@@ -20,6 +22,8 @@ packaging==26.2
# via
# build
# delocate
+pefile==2024.8.26
+ # via delvewheel
pip==26.0.1
# via -r cibuildwheel/resources/constraints.in
platformdirs==4.9.6
diff --git a/cibuildwheel/resources/constraints-python311.txt b/cibuildwheel/resources/constraints-python311.txt
index cd2a64268..9f48e79b5 100644
--- a/cibuildwheel/resources/constraints-python311.txt
+++ b/cibuildwheel/resources/constraints-python311.txt
@@ -6,6 +6,8 @@ build==1.5.0
# via -r cibuildwheel/resources/constraints.in
delocate==0.13.0
# via -r cibuildwheel/resources/constraints.in
+delvewheel==1.12.1
+ # via -r cibuildwheel/resources/constraints.in
distlib==0.4.0
# via virtualenv
filelock==3.29.0
@@ -18,6 +20,8 @@ packaging==26.2
# via
# build
# delocate
+pefile==2024.8.26
+ # via delvewheel
pip==26.0.1
# via -r cibuildwheel/resources/constraints.in
platformdirs==4.9.6
diff --git a/cibuildwheel/resources/constraints-python312.txt b/cibuildwheel/resources/constraints-python312.txt
index cd2a64268..9f48e79b5 100644
--- a/cibuildwheel/resources/constraints-python312.txt
+++ b/cibuildwheel/resources/constraints-python312.txt
@@ -6,6 +6,8 @@ build==1.5.0
# via -r cibuildwheel/resources/constraints.in
delocate==0.13.0
# via -r cibuildwheel/resources/constraints.in
+delvewheel==1.12.1
+ # via -r cibuildwheel/resources/constraints.in
distlib==0.4.0
# via virtualenv
filelock==3.29.0
@@ -18,6 +20,8 @@ packaging==26.2
# via
# build
# delocate
+pefile==2024.8.26
+ # via delvewheel
pip==26.0.1
# via -r cibuildwheel/resources/constraints.in
platformdirs==4.9.6
diff --git a/cibuildwheel/resources/constraints-python313.txt b/cibuildwheel/resources/constraints-python313.txt
index cd2a64268..9f48e79b5 100644
--- a/cibuildwheel/resources/constraints-python313.txt
+++ b/cibuildwheel/resources/constraints-python313.txt
@@ -6,6 +6,8 @@ build==1.5.0
# via -r cibuildwheel/resources/constraints.in
delocate==0.13.0
# via -r cibuildwheel/resources/constraints.in
+delvewheel==1.12.1
+ # via -r cibuildwheel/resources/constraints.in
distlib==0.4.0
# via virtualenv
filelock==3.29.0
@@ -18,6 +20,8 @@ packaging==26.2
# via
# build
# delocate
+pefile==2024.8.26
+ # via delvewheel
pip==26.0.1
# via -r cibuildwheel/resources/constraints.in
platformdirs==4.9.6
diff --git a/cibuildwheel/resources/constraints-python314.txt b/cibuildwheel/resources/constraints-python314.txt
index cd2a64268..9f48e79b5 100644
--- a/cibuildwheel/resources/constraints-python314.txt
+++ b/cibuildwheel/resources/constraints-python314.txt
@@ -6,6 +6,8 @@ build==1.5.0
# via -r cibuildwheel/resources/constraints.in
delocate==0.13.0
# via -r cibuildwheel/resources/constraints.in
+delvewheel==1.12.1
+ # via -r cibuildwheel/resources/constraints.in
distlib==0.4.0
# via virtualenv
filelock==3.29.0
@@ -18,6 +20,8 @@ packaging==26.2
# via
# build
# delocate
+pefile==2024.8.26
+ # via delvewheel
pip==26.0.1
# via -r cibuildwheel/resources/constraints.in
platformdirs==4.9.6
diff --git a/cibuildwheel/resources/constraints-python38.txt b/cibuildwheel/resources/constraints-python38.txt
index e53d14813..ad0395a0b 100644
--- a/cibuildwheel/resources/constraints-python38.txt
+++ b/cibuildwheel/resources/constraints-python38.txt
@@ -6,6 +6,8 @@ build==1.2.2.post1
# via -r cibuildwheel/resources/constraints.in
delocate==0.12.0
# via -r cibuildwheel/resources/constraints.in
+delvewheel==1.10.0
+ # via -r cibuildwheel/resources/constraints.in
distlib==0.4.0
# via virtualenv
filelock==3.16.1
@@ -20,6 +22,8 @@ packaging==26.2
# via
# build
# delocate
+pefile==2024.8.26
+ # via delvewheel
pip==25.0.1
# via -r cibuildwheel/resources/constraints.in
platformdirs==4.3.6
diff --git a/cibuildwheel/resources/constraints-python39.txt b/cibuildwheel/resources/constraints-python39.txt
index c98a2d2a4..8bf098fa7 100644
--- a/cibuildwheel/resources/constraints-python39.txt
+++ b/cibuildwheel/resources/constraints-python39.txt
@@ -6,6 +6,8 @@ build==1.4.4
# via -r cibuildwheel/resources/constraints.in
delocate==0.13.0
# via -r cibuildwheel/resources/constraints.in
+delvewheel==1.12.1
+ # via -r cibuildwheel/resources/constraints.in
distlib==0.4.0
# via virtualenv
filelock==3.19.1
@@ -20,6 +22,8 @@ packaging==26.2
# via
# build
# delocate
+pefile==2024.8.26
+ # via delvewheel
pip==26.0.1
# via -r cibuildwheel/resources/constraints.in
platformdirs==4.4.0
diff --git a/cibuildwheel/resources/constraints.in b/cibuildwheel/resources/constraints.in
index 50bfabb6e..8f40eb610 100644
--- a/cibuildwheel/resources/constraints.in
+++ b/cibuildwheel/resources/constraints.in
@@ -1,4 +1,5 @@
pip
build
delocate
+delvewheel
virtualenv
diff --git a/cibuildwheel/resources/constraints.txt b/cibuildwheel/resources/constraints.txt
index cd2a64268..9f48e79b5 100644
--- a/cibuildwheel/resources/constraints.txt
+++ b/cibuildwheel/resources/constraints.txt
@@ -6,6 +6,8 @@ build==1.5.0
# via -r cibuildwheel/resources/constraints.in
delocate==0.13.0
# via -r cibuildwheel/resources/constraints.in
+delvewheel==1.12.1
+ # via -r cibuildwheel/resources/constraints.in
distlib==0.4.0
# via virtualenv
filelock==3.29.0
@@ -18,6 +20,8 @@ packaging==26.2
# via
# build
# delocate
+pefile==2024.8.26
+ # via delvewheel
pip==26.0.1
# via -r cibuildwheel/resources/constraints.in
platformdirs==4.9.6
diff --git a/cibuildwheel/resources/defaults.toml b/cibuildwheel/resources/defaults.toml
index 78895bf9a..2807a1a54 100644
--- a/cibuildwheel/resources/defaults.toml
+++ b/cibuildwheel/resources/defaults.toml
@@ -58,6 +58,7 @@ repair-wheel-command = "auditwheel repair -w {dest_dir} {wheel}"
repair-wheel-command = "delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}"
[tool.cibuildwheel.windows]
+repair-wheel-command = "delvewheel repair -w {dest_dir} -v {wheel}"
[tool.cibuildwheel.android]
diff --git a/docs/options.md b/docs/options.md
index 7e916cad1..ddaaf0641 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -922,6 +922,7 @@ Default:
- on Linux: `'auditwheel repair -w {dest_dir} {wheel}'`
- on macOS: `'delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}'`
+- on Windows: `'delvewheel repair -w {dest_dir} -v {wheel}'`
- on Android: There is no default command, but cibuildwheel will add `libc++` to the
wheel if anything links against it. Setting a command will replace this behavior.
- on Pyodide: You can use `pyodide auditwheel repair --libdir /path/to/libraries --output-dir {dest_dir} {wheel}` command to repair the wheel.
@@ -946,29 +947,35 @@ The command is run in a shell, so you can run multiple commands like `cmd1 && cm
Platform-specific environment variables are also available:
`CIBW_REPAIR_WHEEL_COMMAND_MACOS` | `CIBW_REPAIR_WHEEL_COMMAND_WINDOWS` | `CIBW_REPAIR_WHEEL_COMMAND_LINUX` | `CIBW_REPAIR_WHEEL_COMMAND_ANDROID` | `CIBW_REPAIR_WHEEL_COMMAND_IOS` | `CIBW_REPAIR_WHEEL_COMMAND_PYODIDE`
-!!! tip
- cibuildwheel doesn't yet ship a default repair command for Windows.
+!!! note "Windows: telling delvewheel where to find DLLs"
+ On Windows, delvewheel searches the directories on `PATH` for external DLL dependencies. If your DLLs are already discoverable via `PATH`, (say, installed by a package manager that adds itself and the relevant directories to `PATH`), the default repair command should be sufficient.
- **If that's an issue for you, check out [delvewheel]** - a new package that aims to do the same as auditwheel or delocate for Windows.
+ If your project links against DLLs in a custom location – such as a [vcpkg](https://vcpkg.io/) or [Conan](https://conan.io/) install tree, or a manually built library directory, you may pass `--add-path` to tell delvewheel where to look. The flag can be used multiple times for more than one directory:
- Because delvewheel is still relatively early-stage, cibuildwheel does not yet run it by default. However, we'd recommend giving it a try! See the examples below for usage.
+ ```toml
+ [tool.cibuildwheel.windows]
+ repair-wheel-command = "delvewheel repair --add-path C:/vcpkg/installed/x64-windows/bin --add-path C:/mylibs/bin -w {dest_dir} -v {wheel}"
+ ```
- [Delvewheel]: https://github.com/adang1345/delvewheel
+ You can also reference environment variables expanded by the shell at build time, for example if the path is set during `before-build`:
+
+ ```yaml
+ CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: "delvewheel repair --add-path %VCPKG_INSTALLED_DIR%\\x64-windows\\bin -w {dest_dir} -v {wheel}"
+ ```
#### Examples
!!! tab examples "pyproject.toml"
```toml
- # Use delvewheel on windows
- [tool.cibuildwheel.windows]
- before-build = "pip install delvewheel"
- repair-wheel-command = "delvewheel repair -w {dest_dir} {wheel}"
-
# Don't repair macOS wheels
[tool.cibuildwheel.macos]
repair-wheel-command = ""
+ # Don't repair Windows wheels
+ [tool.cibuildwheel.windows]
+ repair-wheel-command = ""
+
# Pass the `--lib-sdir .` flag to auditwheel on Linux
[tool.cibuildwheel.linux]
repair-wheel-command = "auditwheel repair --lib-sdir . -w {dest_dir} {wheel}"
@@ -993,7 +1000,7 @@ Platform-specific environment variables are also available:
]
[tool.cibuildwheel.windows]
repair-wheel-command = [
- "copy {wheel} {dest_dir}",
+ "delvewheel repair -w {dest_dir} -v {wheel}",
"pipx run abi3audit --strict --report {wheel}",
]
```
@@ -1004,13 +1011,12 @@ Platform-specific environment variables are also available:
!!! tab examples "Environment variables"
```yaml
- # Use delvewheel on windows
- CIBW_BEFORE_BUILD_WINDOWS: "pip install delvewheel"
- CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: "delvewheel repair -w {dest_dir} {wheel}"
-
# Don't repair macOS wheels
CIBW_REPAIR_WHEEL_COMMAND_MACOS: ""
+ # Don't repair Windows wheels
+ CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: ""
+
# Pass the `--lib-sdir .` flag to auditwheel on Linux
CIBW_REPAIR_WHEEL_COMMAND_LINUX: "auditwheel repair --lib-sdir . -w {dest_dir} {wheel}"
@@ -1027,7 +1033,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} &&
+ delvewheel repair -w {dest_dir} -v {wheel} &&
pipx run abi3audit --strict --report {wheel}
```
diff --git a/unit_test/main_tests/main_options_test.py b/unit_test/main_tests/main_options_test.py
index 8e3794d6b..a6d20c0ec 100644
--- a/unit_test/main_tests/main_options_test.py
+++ b/unit_test/main_tests/main_options_test.py
@@ -288,14 +288,16 @@ def get_default_repair_command(platform: str) -> str:
return "auditwheel repair -w {dest_dir} {wheel}"
elif platform == "macos":
return "delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}"
- elif platform in {"windows", "pyodide"}:
+ elif platform == "windows":
+ return "delvewheel repair -w {dest_dir} -v {wheel}"
+ elif platform == "pyodide":
return ""
else:
msg = f"Unknown platform: {platform!r}"
raise ValueError(msg)
-@pytest.mark.parametrize("repair_command", [None, "repair", "repair -w {dest_dir} {wheel}"])
+@pytest.mark.parametrize("repair_command", [None, "", "repair", "repair -w {dest_dir} {wheel}"])
@pytest.mark.parametrize("platform_specific", [False, True])
def test_repair_command(
repair_command: str | None,
@@ -315,7 +317,9 @@ def test_repair_command(
build_options = intercepted_build_args.args[0].build_options(identifier=None)
- expected_repair = repair_command or get_default_repair_command(platform)
+ expected_repair = (
+ get_default_repair_command(platform) if repair_command is None else repair_command
+ )
assert build_options.repair_command == expected_repair