astral-sh/uv
uv-build-frontend
crates/uv-build-frontend/ is uv's PEP 517 build frontend. When uv needs to turn a source
distribution into a wheel — to get its metadata, or to install it — it spawns Python with the
backend specified by the project's pyproject.toml. This crate handles the entire dance:
spinning up a build environment, calling the backend's hooks, capturing stdout/stderr, and
producing a wheel.
Purpose
A correct PEP 517 frontend must:
- Read
[build-system]from the source'spyproject.tomlto learn the backend (e.g.,setuptools.build_meta,hatchling.build,flit_core.buildapi,meson_python,uv_build). - Materialize the build requirements in a fresh, isolated virtual environment (unless build isolation is explicitly disabled).
- Call the backend's hooks:
get_requires_for_build_wheel,prepare_metadata_for_build_wheel,build_wheel,get_requires_for_build_sdist,build_sdist, plus the editable variants. - Communicate over stdout while letting the backend log freely on stderr.
- Surface backend errors as clear uv errors with hints when possible (e.g., missing system
headers or
cffi/numpy-style build failures).
Directory layout
crates/uv-build-frontend/src/
├── lib.rs # 1296 lines: the SourceBuild struct, hooks, backend invocation
├── error.rs # Error types, including MissingHeaderCause for "did you forget to apt install …"
├── pipreqs.rs # Helpers for parsing build requirements
└── pipreqs/ # Embedded helper Python scriptsKey abstractions
| Type | File | Role |
|---|---|---|
SourceBuild |
lib.rs |
The state machine for a single sdist build: holds the build env, the backend handle, and the source tree. |
Pep517Backend |
lib.rs |
Parsed [build-system] table: backend module, optional backend path, requires list. |
DEFAULT_BACKEND |
lib.rs |
The fallback backend for projects without a [build-system]: setuptools.build_meta:__legacy__ with setuptools >= 40.8.0. |
BuildKind (re-exported from uv-configuration) |
lib.rs |
What to build: wheel, sdist, editable. |
BuildOutput (re-exported) |
lib.rs |
Where backend output goes: hidden, shown, or shown only on failure. |
MissingHeaderCause |
error.rs |
Pattern-match the well-known "missing X.h" build errors and produce actionable hints. |
How it works
sequenceDiagram
participant Caller as uv-distribution / uv-installer
participant Frontend as SourceBuild
participant Venv as Build venv
participant Backend as Backend (setuptools/hatch/uv_build/…)
Caller->>Frontend: SourceBuild::setup(source, build_kind, ...)
Frontend->>Frontend: Read pyproject.toml [build-system]
alt isolated build
Frontend->>Venv: Create ephemeral venv via uv-virtualenv
Frontend->>Venv: Install backend + requires
end
Caller->>Frontend: build()
Frontend->>Backend: get_requires_for_build_wheel
Backend-->>Frontend: extra requires
Frontend->>Venv: Install extra requires
Frontend->>Backend: prepare_metadata_for_build_wheel (if supported)
Backend-->>Frontend: METADATA + RECORD
Frontend->>Backend: build_wheel(out_dir, ...)
Backend-->>Frontend: wheel filename
Frontend-->>Caller: PathBuf to wheelThe frontend writes a small Python harness to a temp file, invokes the backend module by
importing it, and serializes JSON results back to uv. The Python harness lives inline in
lib.rs (look for the formatdoc! blocks).
Build isolation
By default each sdist build happens in a fresh virtual environment that contains only the backend's requires (recursively resolved). This is critical for reproducibility: the project's own runtime requirements are intentionally not available during the build, so a project that imports its own code at build time will fail loudly.
The isolation can be relaxed via:
--no-build-isolation— share the parent venv.tool.uv.no-build-isolation-package = ["pkg-a", "pkg-b"]— share only for specific packages.
These cases set BuildIsolation::Shared (from uv-types).
Concurrency
Multiple sdist builds can run in parallel, but each one holds an exclusive lock on its build
directory (fs-err's LockedFile). The crate uses a shared Semaphore (configurable via
UV_CONCURRENT_BUILDS) to cap the number of simultaneous builds.
Error reporting
error.rs tries hard to translate backend failures into human-readable messages. The
MissingHeaderCause enum is a good example: when a build fails with fatal error: Python.h: No such file or directory, uv recognizes the pattern and suggests the right apt/dnf/brew
package or --with-build-context flag.
Integration points
- Caller:
uv-distributionfor resolver-time metadata builds,uv-installerfor install-time wheel builds. - Build environment:
uv-virtualenvcreates the isolated venv. - Resolution / install of build requires: uses the same
BuildContexttrait that the rest of the binary implements (seeuv-types). - Wheel verification: the resulting wheel is read by
uv-metadataand installed viauv-install-wheel.
Entry points for modification
- Backend-specific quirks —
lib.rs. The frontend is generally backend-agnostic, but a few spots special-casesetuptools(legacysetup.pybootstrap),uv_build(no isolation needed when building uv's own crates), and PEP 660 editables. - Build-error hints —
error.rs::MissingHeaderCause. Adding a new pattern there immediately improves the user-facing message. - Subprocess plumbing —
lib.rs. The Python harness is inline and uses tokio'sCommand::new(...).stdout(Stdio::piped()).stderr(Stdio::piped()). Output capture and streaming are handled withtokio::io::AsyncBufReadExt.
Key source files
| File | Purpose |
|---|---|
crates/uv-build-frontend/src/lib.rs |
The 1296-line SourceBuild orchestrator. |
crates/uv-build-frontend/src/error.rs |
Error types and the missing-header heuristics. |
See also
uv-build-backend— uv's own backend, often paired with this frontend.uv-virtualenv— for ephemeral build envs.uv-types— forBuildContextandBuildIsolation.uv-install-wheel— for what the result gets handed to.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.