Skip to content

Commit 384c8d5

Browse files
authored
feat: build[uv] (#1856)
1 parent c37e5a2 commit 384c8d5

14 files changed

+397
-178
lines changed

.github/workflows/test.yml

+5-2
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ jobs:
4949
with:
5050
python-version: ${{ matrix.python_version }}
5151

52+
- uses: yezz123/setup-uv@v4
53+
5254
# Install podman on this CI instance for podman tests on linux
5355
# Snippet from: https://github.com/redhat-actions/podman-login/blob/main/.github/workflows/example.yml
5456
- name: Install latest podman
@@ -69,7 +71,7 @@ jobs:
6971
7072
- name: Install dependencies
7173
run: |
72-
python -m pip install ".[test]"
74+
uv pip install --system ".[test]"
7375
7476
- name: Generate a sample project
7577
run: |
@@ -82,6 +84,7 @@ jobs:
8284
output-dir: wheelhouse
8385
env:
8486
CIBW_ARCHS_MACOS: x86_64 universal2 arm64
87+
CIBW_BUILD_FRONTEND: 'build[uv]'
8588
CIBW_FREE_THREADED_SUPPORT: 1
8689
CIBW_PRERELEASE_PYTHONS: 1
8790

@@ -161,7 +164,7 @@ jobs:
161164
with:
162165
python-version: "3.x"
163166
- name: Install dependencies
164-
run: python -m pip install ".[test]"
167+
run: python -m pip install ".[test,uv]"
165168

166169
- name: Set up QEMU
167170
id: qemu

.pre-commit-config.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ repos:
4040
- types-jinja2
4141
- types-pyyaml
4242
- types-requests
43+
- uv
4344
- validate-pyproject
4445
- id: mypy
4546
name: mypy 3.12

bin/generate_schema.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -48,19 +48,21 @@
4848
type: string_array
4949
build-frontend:
5050
default: default
51-
description: Set the tool to use to build, either "pip" (default for now) or "build"
51+
description: Set the tool to use to build, either "pip" (default for now), "build", or "build[uv]"
5252
oneOf:
53-
- enum: [pip, build, default]
53+
- enum: [pip, build, "build[uv]", default]
5454
- type: string
5555
pattern: '^pip; ?args:'
5656
- type: string
5757
pattern: '^build; ?args:'
58+
- type: string
59+
pattern: '^build\[uv\]; ?args:'
5860
- type: object
5961
additionalProperties: false
6062
required: [name]
6163
properties:
6264
name:
63-
enum: [pip, build]
65+
enum: [pip, build, "build[uv]"]
6466
args:
6567
type: array
6668
items:

cibuildwheel/linux.py

+37-18
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,8 @@ def build_in_container(
196196
log.build_start(config.identifier)
197197
build_options = options.build_options(config.identifier)
198198
build_frontend = build_options.build_frontend or BuildFrontendConfig("pip")
199+
use_uv = build_frontend.name == "build[uv]" and Version(config.version) >= Version("3.8")
200+
pip = ["uv", "pip"] if use_uv else ["pip"]
199201

200202
dependency_constraint_flags: list[PathOrStr] = []
201203

@@ -229,13 +231,22 @@ def build_in_container(
229231
)
230232
sys.exit(1)
231233

232-
which_pip = container.call(["which", "pip"], env=env, capture_output=True).strip()
233-
if PurePosixPath(which_pip) != python_bin / "pip":
234-
print(
235-
"cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it.",
236-
file=sys.stderr,
237-
)
238-
sys.exit(1)
234+
if use_uv:
235+
which_uv = container.call(["which", "uv"], env=env, capture_output=True).strip()
236+
if not which_uv:
237+
print(
238+
"cibuildwheel: uv not found on PATH. You must use a supported manylinux or musllinux environment with uv.",
239+
file=sys.stderr,
240+
)
241+
sys.exit(1)
242+
else:
243+
which_pip = container.call(["which", "pip"], env=env, capture_output=True).strip()
244+
if PurePosixPath(which_pip) != python_bin / "pip":
245+
print(
246+
"cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it.",
247+
file=sys.stderr,
248+
)
249+
sys.exit(1)
239250

240251
compatible_wheel = find_compatible_wheel(built_wheels, config.identifier)
241252
if compatible_wheel:
@@ -279,10 +290,12 @@ def build_in_container(
279290
],
280291
env=env,
281292
)
282-
elif build_frontend.name == "build":
293+
elif build_frontend.name == "build" or build_frontend.name == "build[uv]":
283294
if not 0 <= build_options.build_verbosity < 2:
284295
msg = f"build_verbosity {build_options.build_verbosity} is not supported for build frontend. Ignoring."
285296
log.warning(msg)
297+
if use_uv:
298+
extra_flags += ["--installer=uv"]
286299
container.call(
287300
[
288301
"python",
@@ -327,26 +340,32 @@ def build_in_container(
327340

328341
# set up a virtual environment to install and test from, to make sure
329342
# there are no dependencies that were pulled in at build time.
330-
container.call(["pip", "install", "virtualenv", *dependency_constraint_flags], env=env)
343+
if not use_uv:
344+
container.call(
345+
["pip", "install", "virtualenv", *dependency_constraint_flags], env=env
346+
)
331347

332348
testing_temp_dir = PurePosixPath(
333349
container.call(["mktemp", "-d"], capture_output=True).strip()
334350
)
335351
venv_dir = testing_temp_dir / "venv"
336352

337-
# Use embedded dependencies from virtualenv to ensure determinism
338-
venv_args = ["--no-periodic-update", "--pip=embed"]
339-
# In Python<3.12, setuptools & wheel are installed as well
340-
if Version(config.version) < Version("3.12"):
341-
venv_args.extend(("--setuptools=embed", "--wheel=embed"))
342-
container.call(["python", "-m", "virtualenv", *venv_args, venv_dir], env=env)
353+
if use_uv:
354+
container.call(["uv", "venv", venv_dir], env=env)
355+
else:
356+
# Use embedded dependencies from virtualenv to ensure determinism
357+
venv_args = ["--no-periodic-update", "--pip=embed"]
358+
# In Python<3.12, setuptools & wheel are installed as well
359+
if Version(config.version) < Version("3.12"):
360+
venv_args.extend(("--setuptools=embed", "--wheel=embed"))
361+
container.call(["python", "-m", "virtualenv", *venv_args, venv_dir], env=env)
343362

344363
virtualenv_env = env.copy()
345364
virtualenv_env["PATH"] = f"{venv_dir / 'bin'}:{virtualenv_env['PATH']}"
346365
virtualenv_env["VIRTUAL_ENV"] = str(venv_dir)
347366

348367
# TODO remove me once virtualenv provides pip>=24.1b1
349-
if config.version == "3.13":
368+
if config.version == "3.13" and not use_uv:
350369
container.call(["pip", "install", "pip>=24.1b1"], env=virtualenv_env)
351370

352371
if build_options.before_test:
@@ -365,13 +384,13 @@ def build_in_container(
365384
# Let's just pick the first one.
366385
wheel_to_test = repaired_wheels[0]
367386
container.call(
368-
["pip", "install", str(wheel_to_test) + build_options.test_extras],
387+
[*pip, "install", str(wheel_to_test) + build_options.test_extras],
369388
env=virtualenv_env,
370389
)
371390

372391
# Install any requirements to run the tests
373392
if build_options.test_requires:
374-
container.call(["pip", "install", *build_options.test_requires], env=virtualenv_env)
393+
container.call([*pip, "install", *build_options.test_requires], env=virtualenv_env)
375394

376395
# Run the tests from a different directory
377396
test_command_prepared = prepare_command(

0 commit comments

Comments
 (0)