Skip to content

feat: option to build directly with uv #2322

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ jobs:
needs: lint
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, macos-13, macos-15]
python_version: ['3.13']
Expand Down
8 changes: 5 additions & 3 deletions bin/generate_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,21 +54,23 @@
type: string_array
build-frontend:
default: default
description: Set the tool to use to build, either "pip" (default for now), "build", or "build[uv]"
description: Set the tool to use to build, either "pip" (default for now), "build", "build[uv]", or "uv".
oneOf:
- enum: [pip, build, "build[uv]", default]
- enum: [pip, build, "build[uv]", uv, default]
- type: string
pattern: '^pip; ?args:'
- type: string
pattern: '^build; ?args:'
- type: string
pattern: '^build\\[uv\\]; ?args:'
- type: string
pattern: '^uv; ?args:'
- type: object
additionalProperties: false
required: [name]
properties:
name:
enum: [pip, build, "build[uv]"]
enum: [pip, build, "build[uv]", uv]
args:
type: array
items:
Expand Down
2 changes: 1 addition & 1 deletion cibuildwheel/frontend.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from .logger import log
from .util.helpers import parse_key_value_string

BuildFrontendName = Literal["pip", "build", "build[uv]"]
BuildFrontendName = Literal["pip", "build", "build[uv]", "uv"]


@dataclass(frozen=True)
Expand Down
4 changes: 2 additions & 2 deletions cibuildwheel/platforms/ios.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ def setup_python(
build_frontend: BuildFrontendName,
xbuild_tools: Sequence[str] | None,
) -> tuple[Path, dict[str, str]]:
if build_frontend == "build[uv]":
if build_frontend == "build[uv]" or build_frontend == "uv":
msg = "uv doesn't support iOS"
raise errors.FatalError(msg)

Expand Down Expand Up @@ -439,7 +439,7 @@ def build(options: Options, tmp_path: Path) -> None:
build_options = options.build_options(config.identifier)
build_frontend = build_options.build_frontend or BuildFrontendConfig("build")
# uv doesn't support iOS
if build_frontend.name == "build[uv]":
if build_frontend.name == "build[uv]" or build_frontend.name == "uv":
msg = "uv doesn't support iOS"
raise errors.FatalError(msg)

Expand Down
15 changes: 14 additions & 1 deletion cibuildwheel/platforms/linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ def build_in_container(
local_identifier_tmp_dir = local_tmp_dir / config.identifier
build_options = options.build_options(config.identifier)
build_frontend = build_options.build_frontend or BuildFrontendConfig("build")
use_uv = build_frontend.name == "build[uv]"
use_uv = build_frontend.name in {"build[uv]", "uv"}
pip = ["uv", "pip"] if use_uv else ["pip"]

log.step("Setting up build environment...")
Expand Down Expand Up @@ -303,6 +303,19 @@ def build_in_container(
],
env=env,
)
elif build_frontend.name == "uv":
container.call(
[
"uv",
"build",
"--python=python",
container_package_dir,
"--wheel",
f"--out-dir={built_wheel_dir}",
*extra_flags,
],
env=env,
)
else:
assert_never(build_frontend)

Expand Down
26 changes: 24 additions & 2 deletions cibuildwheel/platforms/macos.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ def setup_python(
build_frontend: BuildFrontendName,
) -> tuple[Path, dict[str, str]]:
uv_path = find_uv()
use_uv = build_frontend == "build[uv]"
use_uv = build_frontend in {"build[uv]", "uv"}

tmp.mkdir()
implementation_id = python_configuration.identifier.split("-")[0]
Expand Down Expand Up @@ -375,6 +375,17 @@ def setup_python(
*constraint_flags(dependency_constraint),
env=env,
)
elif build_frontend == "uv":
assert uv_path is not None
call(
uv_path,
"pip",
"install",
"--upgrade",
"delocate",
*constraint_flags(dependency_constraint),
env=env,
)
else:
assert_never(build_frontend)

Expand Down Expand Up @@ -407,7 +418,7 @@ def build(options: Options, tmp_path: Path) -> None:
for config in python_configurations:
build_options = options.build_options(config.identifier)
build_frontend = build_options.build_frontend or BuildFrontendConfig("build")
use_uv = build_frontend.name == "build[uv]"
use_uv = build_frontend.name in {"build[uv]", "uv"}
uv_path = find_uv()
if use_uv and uv_path is None:
msg = "uv not found"
Expand Down Expand Up @@ -494,6 +505,17 @@ def build(options: Options, tmp_path: Path) -> None:
*extra_flags,
env=build_env,
)
elif build_frontend.name == "uv":
call(
"uv",
"build",
"--python=python",
build_options.package_dir,
"--wheel",
f"--out-dir={built_wheel_dir}",
*extra_flags,
env=build_env,
)
else:
assert_never(build_frontend)

Expand Down
17 changes: 14 additions & 3 deletions cibuildwheel/platforms/windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,10 +244,10 @@ def setup_python(
raise ValueError(msg)
assert base_python.exists()

if build_frontend == "build[uv]" and not can_use_uv(python_configuration):
if build_frontend in {"build[uv]", "uv"} and not can_use_uv(python_configuration):
build_frontend = "build"

use_uv = build_frontend == "build[uv]"
use_uv = build_frontend in {"build[uv]", "uv"}
uv_path = find_uv()

log.step("Setting up build environment...")
Expand Down Expand Up @@ -357,7 +357,7 @@ def build(options: Options, tmp_path: Path) -> None:
for config in python_configurations:
build_options = options.build_options(config.identifier)
build_frontend = build_options.build_frontend or BuildFrontendConfig("build")
use_uv = build_frontend.name == "build[uv]" and can_use_uv(config)
use_uv = build_frontend.name in {"build[uv]", "uv"} and can_use_uv(config)
log.build_start(config.identifier)

identifier_tmp_dir = tmp_path / config.identifier
Expand Down Expand Up @@ -440,6 +440,17 @@ def build(options: Options, tmp_path: Path) -> None:
*extra_flags,
env=build_env,
)
elif build_frontend.name == "uv":
call(
"uv",
"build",
"--python=python",
build_options.package_dir,
"--wheel",
f"--out-dir={built_wheel_dir}",
*extra_flags,
env=build_env,
)
else:
assert_never(build_frontend)

Expand Down
2 changes: 1 addition & 1 deletion cibuildwheel/util/packaging.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,6 @@ def combine_constraints(

user_constraints = env.get("PIP_CONSTRAINT")

env["UV_CONSTRAINT"] = env["PIP_CONSTRAINT"] = " ".join(
env["UV_BUILD_CONSTRAINT"] = env["UV_CONSTRAINT"] = env["PIP_CONSTRAINT"] = " ".join(
c for c in [our_constraints, user_constraints] if c
)
8 changes: 5 additions & 3 deletions test/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,13 @@ def docker_warmup_fixture(
return None


@pytest.fixture(params=["pip", "build"])
@pytest.fixture(params=["pip", "build", "uv"])
def build_frontend_env_nouv(request: pytest.FixtureRequest) -> dict[str, str]:
frontend = request.param
if platform == "pyodide" and frontend == "pip":
pytest.skip("Can't use pip as build frontend for pyodide platform")
if platform == "pyodide" and frontend in {"pip", "uv"}:
pytest.skip("Can't use pip or uv as build frontend for pyodide platform")
if frontend == "uv" and find_uv() is None:
pytest.skip("Can't find uv")

return {"CIBW_BUILD_FRONTEND": frontend}

Expand Down
5 changes: 5 additions & 0 deletions unit_test/options_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,11 @@ def test_environment_pass_references():
"build",
[],
),
(
'build-frontend = "uv"',
"uv",
[],
),
(
'build-frontend = {name = "build"}',
"build",
Expand Down
Loading