Skip to content
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

feat: option to build directly with uv #2322

Draft
wants to merge 7 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
24 changes: 11 additions & 13 deletions cibuildwheel/platforms/ios.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import shutil
import subprocess
import sys
from collections.abc import Sequence, Set
from collections.abc import Set
from dataclasses import dataclass
from pathlib import Path
from typing import assert_never
Expand All @@ -23,7 +23,6 @@
from ..logger import log
from ..options import Options
from ..selector import BuildSelector
from ..typing import PathOrStr
from ..util import resources
from ..util.cmd import call, shell
from ..util.file import (
Expand All @@ -38,7 +37,7 @@
find_compatible_wheel,
get_pip_version,
)
from ..venv import virtualenv
from ..venv import constraint_flags, virtualenv
from .macos import install_cpython as install_build_cpython


Expand Down Expand Up @@ -150,7 +149,7 @@ def cross_virtualenv(
multiarch: str,
build_python: Path,
venv_path: Path,
dependency_constraint_flags: Sequence[PathOrStr],
dependency_constraint: Path | None,
) -> dict[str, str]:
"""Create a cross-compilation virtual environment.

Expand Down Expand Up @@ -184,7 +183,7 @@ def cross_virtualenv(
py_version,
build_python,
venv_path,
dependency_constraint_flags,
dependency_constraint,
use_uv=False,
)

Expand Down Expand Up @@ -236,11 +235,11 @@ def cross_virtualenv(
def setup_python(
tmp: Path,
python_configuration: PythonConfiguration,
dependency_constraint_flags: Sequence[PathOrStr],
dependency_constraint: Path | None,
environment: ParsedEnvironment,
build_frontend: BuildFrontendName,
) -> 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 @@ -283,14 +282,16 @@ def setup_python(

log.step("Creating cross build environment...")

dependency_constraint_flags = constraint_flags(dependency_constraint)

venv_path = tmp / "venv"
env = cross_virtualenv(
py_version=python_configuration.version,
target_python=target_python,
multiarch=python_configuration.multiarch,
build_python=build_python,
venv_path=venv_path,
dependency_constraint_flags=dependency_constraint_flags,
dependency_constraint=dependency_constraint,
)
venv_bin_path = venv_path / "bin"
assert venv_bin_path.exists()
Expand Down Expand Up @@ -395,7 +396,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 All @@ -408,14 +409,11 @@ def build(options: Options, tmp_path: Path) -> None:
constraints_path = build_options.dependency_constraints.get_for_python_version(
version=config.version, tmp_dir=identifier_tmp_dir
)
dependency_constraint_flags: Sequence[PathOrStr] = (
["-c", constraints_path] if constraints_path else []
)

target_install_path, env = setup_python(
identifier_tmp_dir / "build",
config,
dependency_constraint_flags,
constraints_path,
build_options.environment,
build_frontend.name,
)
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
41 changes: 31 additions & 10 deletions cibuildwheel/platforms/macos.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import subprocess
import sys
import typing
from collections.abc import Sequence, Set
from collections.abc import Set
from dataclasses import dataclass
from pathlib import Path
from typing import Literal, assert_never
Expand All @@ -23,7 +23,6 @@
from ..logger import log
from ..options import Options
from ..selector import BuildSelector
from ..typing import PathOrStr
from ..util import resources
from ..util.cmd import call, shell
from ..util.file import (
Expand All @@ -34,7 +33,7 @@
)
from ..util.helpers import prepare_command, unwrap
from ..util.packaging import combine_constraints, find_compatible_wheel, get_pip_version
from ..venv import find_uv, virtualenv
from ..venv import constraint_flags, find_uv, virtualenv


@functools.cache
Expand Down Expand Up @@ -195,7 +194,7 @@ def install_pypy(tmp: Path, url: str) -> Path:
def setup_python(
tmp: Path,
python_configuration: PythonConfiguration,
dependency_constraint_flags: Sequence[PathOrStr],
dependency_constraint: Path | None,
environment: ParsedEnvironment,
build_frontend: BuildFrontendName,
) -> tuple[Path, dict[str, str]]:
Expand All @@ -220,13 +219,15 @@ def setup_python(
f"{base_python.name} not found, has {list(base_python.parent.iterdir())}"
)

dependency_constraint_flags = constraint_flags(dependency_constraint)

log.step("Setting up build environment...")
venv_path = tmp / "venv"
env = virtualenv(
python_configuration.version,
base_python,
venv_path,
dependency_constraint_flags,
dependency_constraint,
use_uv=use_uv,
)
venv_bin_path = venv_path / "bin"
Expand Down Expand Up @@ -376,6 +377,17 @@ def setup_python(
*dependency_constraint_flags,
env=env,
)
elif build_frontend == "uv":
assert uv_path is not None
call(
uv_path,
"pip",
"install",
"--upgrade",
"delocate",
*dependency_constraint_flags,
env=env,
)
else:
assert_never(build_frontend)

Expand Down Expand Up @@ -408,7 +420,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 All @@ -427,14 +439,12 @@ def build(options: Options, tmp_path: Path) -> None:
constraints_path = build_options.dependency_constraints.get_for_python_version(
version=config.version, tmp_dir=identifier_tmp_dir
)
dependency_constraint_flags: Sequence[PathOrStr] = (
["-c", constraints_path] if constraints_path else []
)
dependency_constraint_flags = constraint_flags(constraints_path)

base_python, env = setup_python(
identifier_tmp_dir / "build",
config,
dependency_constraint_flags,
constraints_path,
build_options.environment,
build_frontend.name,
)
Expand Down Expand Up @@ -498,6 +508,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
6 changes: 3 additions & 3 deletions cibuildwheel/platforms/pyodide.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ def setup_python(

log.step("Setting up build environment...")
venv_path = tmp / "venv"
env = virtualenv(python_configuration.version, base_python, venv_path, [], use_uv=False)
env = virtualenv(python_configuration.version, base_python, venv_path, None, use_uv=False)
venv_bin_path = venv_path / "bin"
assert venv_bin_path.exists()
env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1"
Expand Down Expand Up @@ -269,8 +269,8 @@ def build(options: Options, tmp_path: Path) -> None:
constraints_path = build_options.dependency_constraints.get_for_python_version(
version=config.version, variant="pyodide", tmp_dir=identifier_tmp_dir
)
dependency_constraint_flags: Sequence[PathOrStr] = (
["-c", constraints_path] if constraints_path else []
dependency_constraint_flags = (
["-c", constraints_path.as_uri()] if constraints_path else []
)

env = setup_python(
Expand Down
Loading
Loading