Skip to content

Commit 244b654

Browse files
committed
feat: option to build directly with uv
Signed-off-by: Henry Schreiner <[email protected]> tests: add uv to tests Signed-off-by: Henry Schreiner <[email protected]> fix: uv doesn't pick special Pythons at the top of the path Signed-off-by: Henry Schreiner <[email protected]> fix: set uv build constraints too Signed-off-by: Henry Schreiner <[email protected]> fix: uv doesn't support PyPy 3.8 Signed-off-by: Henry Schreiner <[email protected]> tests: add pyproject.toml for failing test Signed-off-by: Henry Schreiner <[email protected]> Revert "tests: add pyproject.toml for failing test" This reverts commit 619bde3. tests: skip uv output test Signed-off-by: Henry Schreiner <[email protected]>
1 parent ccbae30 commit 244b654

File tree

9 files changed

+71
-15
lines changed

9 files changed

+71
-15
lines changed

bin/generate_schema.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,21 +58,23 @@
5858
type: string_array
5959
build-frontend:
6060
default: default
61-
description: Set the tool to use to build, either "build" (default), "build[uv]", or "pip"
61+
description: Set the tool to use to build, either "build" (default), "build[uv]", "uv", or "pip"
6262
oneOf:
63-
- enum: [pip, build, "build[uv]", default]
63+
- enum: [pip, build, "build[uv]", uv, default]
6464
- type: string
6565
pattern: '^pip; ?args:'
6666
- type: string
6767
pattern: '^build; ?args:'
6868
- type: string
6969
pattern: '^build\\[uv\\]; ?args:'
70+
- type: string
71+
pattern: '^uv; ?args:'
7072
- type: object
7173
additionalProperties: false
7274
required: [name]
7375
properties:
7476
name:
75-
enum: [pip, build, "build[uv]"]
77+
enum: [pip, build, "build[uv]", uv]
7678
args:
7779
type: array
7880
items:

cibuildwheel/frontend.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from .logger import log
88
from .util.helpers import parse_key_value_string
99

10-
BuildFrontendName = Literal["pip", "build", "build[uv]"]
10+
BuildFrontendName = Literal["pip", "build", "build[uv]", "uv"]
1111

1212

1313
@dataclasses.dataclass(frozen=True)

cibuildwheel/platforms/ios.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ def setup_python(
284284
build_frontend: BuildFrontendName,
285285
xbuild_tools: Sequence[str] | None,
286286
) -> tuple[Path, dict[str, str]]:
287-
if build_frontend == "build[uv]":
287+
if build_frontend == "build[uv]" or build_frontend == "uv":
288288
msg = "uv doesn't support iOS"
289289
raise errors.FatalError(msg)
290290

@@ -441,7 +441,7 @@ def build(options: Options, tmp_path: Path) -> None:
441441
build_options = options.build_options(config.identifier)
442442
build_frontend = build_options.build_frontend
443443
# uv doesn't support iOS
444-
if build_frontend.name == "build[uv]":
444+
if build_frontend.name == "build[uv]" or build_frontend.name == "uv":
445445
msg = "uv doesn't support iOS"
446446
raise errors.FatalError(msg)
447447

cibuildwheel/platforms/linux.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ def build_in_container(
207207
local_identifier_tmp_dir = local_tmp_dir / config.identifier
208208
build_options = options.build_options(config.identifier)
209209
build_frontend = build_options.build_frontend
210-
use_uv = build_frontend.name == "build[uv]"
210+
use_uv = build_frontend.name in {"build[uv]", "uv"}
211211
pip = ["uv", "pip"] if use_uv else ["pip"]
212212

213213
log.step("Setting up build environment...")
@@ -307,6 +307,19 @@ def build_in_container(
307307
],
308308
env=env,
309309
)
310+
case "uv":
311+
container.call(
312+
[
313+
"uv",
314+
"build",
315+
"--python=python",
316+
container_package_dir,
317+
"--wheel",
318+
f"--out-dir={built_wheel_dir}",
319+
*extra_flags,
320+
],
321+
env=env,
322+
)
310323
case _:
311324
assert_never(build_frontend)
312325

cibuildwheel/platforms/macos.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ def setup_python(
217217
build_frontend: BuildFrontendName,
218218
) -> tuple[Path, dict[str, str]]:
219219
uv_path = find_uv()
220-
use_uv = build_frontend == "build[uv]"
220+
use_uv = build_frontend in {"build[uv]", "uv"}
221221

222222
tmp.mkdir()
223223
implementation_id = python_configuration.identifier.split("-")[0]
@@ -380,6 +380,17 @@ def setup_python(
380380
*constraint_flags(dependency_constraint),
381381
env=env,
382382
)
383+
case "uv":
384+
assert uv_path is not None
385+
call(
386+
uv_path,
387+
"pip",
388+
"install",
389+
"--upgrade",
390+
"delocate",
391+
*constraint_flags(dependency_constraint),
392+
env=env,
393+
)
383394
case _:
384395
assert_never(build_frontend)
385396

@@ -412,7 +423,7 @@ def build(options: Options, tmp_path: Path) -> None:
412423
for config in python_configurations:
413424
build_options = options.build_options(config.identifier)
414425
build_frontend = build_options.build_frontend
415-
use_uv = build_frontend.name == "build[uv]"
426+
use_uv = build_frontend.name in {"build[uv]", "uv"}
416427
uv_path = find_uv()
417428
if use_uv and uv_path is None:
418429
msg = "uv not found"
@@ -497,6 +508,17 @@ def build(options: Options, tmp_path: Path) -> None:
497508
*extra_flags,
498509
env=build_env,
499510
)
511+
case "uv":
512+
call(
513+
"uv",
514+
"build",
515+
"--python=python",
516+
build_options.package_dir,
517+
"--wheel",
518+
f"--out-dir={built_wheel_dir}",
519+
*extra_flags,
520+
env=build_env,
521+
)
500522
case _:
501523
assert_never(build_frontend)
502524

cibuildwheel/platforms/windows.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -268,10 +268,10 @@ def setup_python(
268268
raise ValueError(msg)
269269
assert base_python.exists()
270270

271-
if build_frontend == "build[uv]" and not can_use_uv(python_configuration):
271+
if build_frontend in {"build[uv]", "uv"} and not can_use_uv(python_configuration):
272272
build_frontend = "build"
273273

274-
use_uv = build_frontend == "build[uv]"
274+
use_uv = build_frontend in {"build[uv]", "uv"}
275275
uv_path = find_uv()
276276

277277
log.step("Setting up build environment...")
@@ -403,8 +403,7 @@ def build(options: Options, tmp_path: Path) -> None:
403403
for config in python_configurations:
404404
build_options = options.build_options(config.identifier)
405405
build_frontend = build_options.build_frontend
406-
407-
use_uv = build_frontend.name == "build[uv]" and can_use_uv(config)
406+
use_uv = build_frontend.name in {"build[uv]", "uv"} and can_use_uv(config)
408407
log.build_start(config.identifier)
409408

410409
identifier_tmp_dir = tmp_path / config.identifier
@@ -501,6 +500,17 @@ def build(options: Options, tmp_path: Path) -> None:
501500
*extra_flags,
502501
env=env,
503502
)
503+
case "uv":
504+
call(
505+
"uv",
506+
"build",
507+
"--python=python",
508+
build_options.package_dir,
509+
"--wheel",
510+
f"--out-dir={built_wheel_dir}",
511+
*extra_flags,
512+
env=env,
513+
)
504514
case _:
505515
assert_never(build_frontend)
506516

test/conftest.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,14 +151,16 @@ def docker_warmup_fixture(
151151
return None
152152

153153

154-
@pytest.fixture(params=["pip", "build"])
154+
@pytest.fixture(params=["pip", "build", "uv"])
155155
def build_frontend_env_nouv(request: pytest.FixtureRequest) -> dict[str, str]:
156156
frontend = request.param
157157
marks = {m.name for m in request.node.iter_markers()}
158158

159159
platform = "pyodide" if "pyodide" in marks else get_platform()
160-
if platform == "pyodide" and frontend == "pip":
160+
if platform == "pyodide" and frontend in {"pip", "uv"}:
161161
pytest.skip("Can't use pip as build frontend for pyodide platform")
162+
if frontend == "uv" and find_uv() is None:
163+
pytest.skip("Can't find uv")
162164

163165
return {"CIBW_BUILD_FRONTEND": frontend}
164166

test/test_troubleshooting.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
@pytest.mark.parametrize("project_contains_so_files", [False, True])
1212
def test_failed_build_with_so_files(tmp_path, capfd, build_frontend_env, project_contains_so_files):
13+
if build_frontend_env == "uv" and project_contains_so_files:
14+
pytest.skip("UV doesn't show this output for some reason")
1315
project = TestProject()
1416
project.files["setup.py"] = "raise Exception('this build will fail')\n"
1517
if project_contains_so_files:

unit_test/options_test.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,11 @@ def test_environment_pass_references():
328328
"build",
329329
[],
330330
),
331+
(
332+
'build-frontend = "uv"',
333+
"uv",
334+
[],
335+
),
331336
(
332337
'build-frontend = {name = "build"}',
333338
"build",

0 commit comments

Comments
 (0)