Skip to content

Commit df84d25

Browse files
Implement uv build (#6895)
## Summary This PR exposes uv's PEP 517 implementation via a `uv build` frontend, such that you can use `uv build` to build source and binary distributions (i.e., wheels and sdists) from a given directory. There are some TODOs that I'll tackle in separate PRs: - [x] Support building a wheel from a source distribution (rather than from source) (#6898) - [x] Stream the build output (#6912) Closes #1510 Closes #1663.
1 parent a3a1bfd commit df84d25

File tree

16 files changed

+1028
-168
lines changed

16 files changed

+1028
-168
lines changed

Cargo.lock

Lines changed: 2 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/uv-build/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ static DEFAULT_BACKEND: LazyLock<Pep517Backend> = LazyLock::new(|| Pep517Backend
8080
pub enum Error {
8181
#[error(transparent)]
8282
Io(#[from] io::Error),
83-
#[error("{} does not appear to be a Python project, as neither `pyproject.toml` nor `setup.py` is present in the directory", _0.simplified_display())]
83+
#[error("{} does not appear to be a Python project, as neither `pyproject.toml` nor `setup.py` are present in the directory", _0.simplified_display())]
8484
InvalidSourceDist(PathBuf),
8585
#[error("Invalid `pyproject.toml`")]
8686
InvalidPyprojectToml(#[from] toml::de::Error),

crates/uv-cli/src/lib.rs

Lines changed: 78 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,21 @@ pub enum Commands {
339339
after_long_help = ""
340340
)]
341341
Venv(VenvArgs),
342+
/// Build Python packages into source distributions and wheels.
343+
///
344+
/// By default, `uv build` will build a source distribution ("sdist")
345+
/// from the source directory, and a binary distribution ("wheel") from
346+
/// the source distribution.
347+
///
348+
/// `uv build --sdist` can be used to build only the source distribution,
349+
/// `uv build --wheel` can be used to build only the binary distribution,
350+
/// and `uv build --sdist --wheel` can be used to build both distributions
351+
/// from source.
352+
#[command(
353+
after_help = "Use `uv help build` for more details.",
354+
after_long_help = ""
355+
)]
356+
Build(BuildArgs),
342357
/// Manage uv's cache.
343358
#[command(
344359
after_help = "Use `uv help cache` for more details.",
@@ -1126,7 +1141,7 @@ pub struct PipSyncArgs {
11261141

11271142
/// The Python interpreter into which packages should be installed.
11281143
///
1129-
/// By default, syncing requires a virtual environment. An path to an
1144+
/// By default, syncing requires a virtual environment. A path to an
11301145
/// alternative Python can be provided, but it is only recommended in
11311146
/// continuous integration (CI) environments and should be used with
11321147
/// caution, as it can modify the system Python installation.
@@ -1408,7 +1423,7 @@ pub struct PipInstallArgs {
14081423

14091424
/// The Python interpreter into which packages should be installed.
14101425
///
1411-
/// By default, installation requires a virtual environment. An path to an
1426+
/// By default, installation requires a virtual environment. A path to an
14121427
/// alternative Python can be provided, but it is only recommended in
14131428
/// continuous integration (CI) environments and should be used with
14141429
/// caution, as it can modify the system Python installation.
@@ -1573,7 +1588,7 @@ pub struct PipUninstallArgs {
15731588

15741589
/// The Python interpreter from which packages should be uninstalled.
15751590
///
1576-
/// By default, uninstallation requires a virtual environment. An path to an
1591+
/// By default, uninstallation requires a virtual environment. A path to an
15771592
/// alternative Python can be provided, but it is only recommended in
15781593
/// continuous integration (CI) environments and should be used with
15791594
/// caution, as it can modify the system Python installation.
@@ -1924,6 +1939,55 @@ pub struct PipTreeArgs {
19241939
pub compat_args: compat::PipGlobalCompatArgs,
19251940
}
19261941

1942+
#[derive(Args)]
1943+
#[allow(clippy::struct_excessive_bools)]
1944+
pub struct BuildArgs {
1945+
/// The directory from which distributions should be built.
1946+
///
1947+
/// Defaults to the current working directory.
1948+
#[arg(value_parser = parse_file_path)]
1949+
pub src_dir: Option<PathBuf>,
1950+
1951+
/// The output directory to which distributions should be written.
1952+
///
1953+
/// Defaults to the `dist` subdirectory within the source directory.
1954+
#[arg(long, short, value_parser = parse_file_path)]
1955+
pub out_dir: Option<PathBuf>,
1956+
1957+
/// Build a source distribution ("sdist") from the given directory.
1958+
#[arg(long)]
1959+
pub sdist: bool,
1960+
1961+
/// Build a binary distribution ("wheel") from the given directory.
1962+
#[arg(long)]
1963+
pub wheel: bool,
1964+
1965+
/// The Python interpreter to use for the build environment.
1966+
///
1967+
/// By default, builds are executed in isolated virtual environments. The
1968+
/// discovered interpreter will be used to create those environments, and
1969+
/// will be symlinked or copied in depending on the platform.
1970+
///
1971+
/// See `uv help python` to view supported request formats.
1972+
#[arg(
1973+
long,
1974+
short,
1975+
env = "UV_PYTHON",
1976+
verbatim_doc_comment,
1977+
help_heading = "Python options"
1978+
)]
1979+
pub python: Option<String>,
1980+
1981+
#[command(flatten)]
1982+
pub resolver: ResolverArgs,
1983+
1984+
#[command(flatten)]
1985+
pub build: BuildOptionsArgs,
1986+
1987+
#[command(flatten)]
1988+
pub refresh: RefreshArgs,
1989+
}
1990+
19271991
#[derive(Args)]
19281992
#[allow(clippy::struct_excessive_bools)]
19291993
pub struct VenvArgs {
@@ -2318,7 +2382,7 @@ pub struct RunArgs {
23182382
pub installer: ResolverInstallerArgs,
23192383

23202384
#[command(flatten)]
2321-
pub build: BuildArgs,
2385+
pub build: BuildOptionsArgs,
23222386

23232387
#[command(flatten)]
23242388
pub refresh: RefreshArgs,
@@ -2452,7 +2516,7 @@ pub struct SyncArgs {
24522516
pub installer: ResolverInstallerArgs,
24532517

24542518
#[command(flatten)]
2455-
pub build: BuildArgs,
2519+
pub build: BuildOptionsArgs,
24562520

24572521
#[command(flatten)]
24582522
pub refresh: RefreshArgs,
@@ -2505,7 +2569,7 @@ pub struct LockArgs {
25052569
pub resolver: ResolverArgs,
25062570

25072571
#[command(flatten)]
2508-
pub build: BuildArgs,
2572+
pub build: BuildOptionsArgs,
25092573

25102574
#[command(flatten)]
25112575
pub refresh: RefreshArgs,
@@ -2619,7 +2683,7 @@ pub struct AddArgs {
26192683
pub installer: ResolverInstallerArgs,
26202684

26212685
#[command(flatten)]
2622-
pub build: BuildArgs,
2686+
pub build: BuildOptionsArgs,
26232687

26242688
#[command(flatten)]
26252689
pub refresh: RefreshArgs,
@@ -2688,7 +2752,7 @@ pub struct RemoveArgs {
26882752
pub installer: ResolverInstallerArgs,
26892753

26902754
#[command(flatten)]
2691-
pub build: BuildArgs,
2755+
pub build: BuildOptionsArgs,
26922756

26932757
#[command(flatten)]
26942758
pub refresh: RefreshArgs,
@@ -2748,7 +2812,7 @@ pub struct TreeArgs {
27482812
pub frozen: bool,
27492813

27502814
#[command(flatten)]
2751-
pub build: BuildArgs,
2815+
pub build: BuildOptionsArgs,
27522816

27532817
#[command(flatten)]
27542818
pub resolver: ResolverArgs,
@@ -2853,7 +2917,7 @@ pub struct ExportArgs {
28532917
pub resolver: ResolverArgs,
28542918

28552919
#[command(flatten)]
2856-
pub build: BuildArgs,
2920+
pub build: BuildOptionsArgs,
28572921

28582922
#[command(flatten)]
28592923
pub refresh: RefreshArgs,
@@ -3000,7 +3064,7 @@ pub struct ToolRunArgs {
30003064
pub installer: ResolverInstallerArgs,
30013065

30023066
#[command(flatten)]
3003-
pub build: BuildArgs,
3067+
pub build: BuildOptionsArgs,
30043068

30053069
#[command(flatten)]
30063070
pub refresh: RefreshArgs,
@@ -3052,7 +3116,7 @@ pub struct ToolInstallArgs {
30523116
pub installer: ResolverInstallerArgs,
30533117

30543118
#[command(flatten)]
3055-
pub build: BuildArgs,
3119+
pub build: BuildOptionsArgs,
30563120

30573121
#[command(flatten)]
30583122
pub refresh: RefreshArgs,
@@ -3137,7 +3201,7 @@ pub struct ToolUpgradeArgs {
31373201
pub installer: ResolverInstallerArgs,
31383202

31393203
#[command(flatten)]
3140-
pub build: BuildArgs,
3204+
pub build: BuildOptionsArgs,
31413205
}
31423206

31433207
#[derive(Args)]
@@ -3441,7 +3505,7 @@ pub struct RefreshArgs {
34413505

34423506
#[derive(Args)]
34433507
#[allow(clippy::struct_excessive_bools)]
3444-
pub struct BuildArgs {
3508+
pub struct BuildOptionsArgs {
34453509
/// Don't build source distributions.
34463510
///
34473511
/// When enabled, resolving will not run arbitrary Python code. The cached wheels of

crates/uv-cli/src/options.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ use uv_resolver::PrereleaseMode;
44
use uv_settings::{PipOptions, ResolverInstallerOptions, ResolverOptions};
55

66
use crate::{
7-
BuildArgs, IndexArgs, InstallerArgs, Maybe, RefreshArgs, ResolverArgs, ResolverInstallerArgs,
7+
BuildOptionsArgs, IndexArgs, InstallerArgs, Maybe, RefreshArgs, ResolverArgs,
8+
ResolverInstallerArgs,
89
};
910

1011
/// Given a boolean flag pair (like `--upgrade` and `--no-upgrade`), resolve the value of the flag.
@@ -206,8 +207,11 @@ impl From<IndexArgs> for PipOptions {
206207
}
207208
}
208209

209-
/// Construct the [`ResolverOptions`] from the [`ResolverArgs`] and [`BuildArgs`].
210-
pub fn resolver_options(resolver_args: ResolverArgs, build_args: BuildArgs) -> ResolverOptions {
210+
/// Construct the [`ResolverOptions`] from the [`ResolverArgs`] and [`BuildOptionsArgs`].
211+
pub fn resolver_options(
212+
resolver_args: ResolverArgs,
213+
build_args: BuildOptionsArgs,
214+
) -> ResolverOptions {
211215
let ResolverArgs {
212216
index_args,
213217
upgrade,
@@ -228,7 +232,7 @@ pub fn resolver_options(resolver_args: ResolverArgs, build_args: BuildArgs) -> R
228232
no_sources,
229233
} = resolver_args;
230234

231-
let BuildArgs {
235+
let BuildOptionsArgs {
232236
no_build,
233237
build,
234238
no_build_package,
@@ -281,10 +285,10 @@ pub fn resolver_options(resolver_args: ResolverArgs, build_args: BuildArgs) -> R
281285
}
282286
}
283287

284-
/// Construct the [`ResolverInstallerOptions`] from the [`ResolverInstallerArgs`] and [`BuildArgs`].
288+
/// Construct the [`ResolverInstallerOptions`] from the [`ResolverInstallerArgs`] and [`BuildOptionsArgs`].
285289
pub fn resolver_installer_options(
286290
resolver_installer_args: ResolverInstallerArgs,
287-
build_args: BuildArgs,
291+
build_args: BuildOptionsArgs,
288292
) -> ResolverInstallerOptions {
289293
let ResolverInstallerArgs {
290294
index_args,
@@ -311,7 +315,7 @@ pub fn resolver_installer_options(
311315
no_sources,
312316
} = resolver_installer_args;
313317

314-
let BuildArgs {
318+
let BuildOptionsArgs {
315319
no_build,
316320
build,
317321
no_build_package,

crates/uv-dev/Cargo.toml

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,16 @@ workspace = true
1818
[dependencies]
1919
distribution-filename = { workspace = true }
2020
distribution-types = { workspace = true }
21-
install-wheel-rs = { workspace = true }
2221
pep508_rs = { workspace = true }
2322
pypi-types = { workspace = true }
24-
uv-build = { workspace = true }
2523
uv-cache = { workspace = true, features = ["clap"] }
2624
uv-cli = { workspace = true }
2725
uv-client = { workspace = true }
28-
uv-configuration = { workspace = true }
29-
uv-dispatch = { workspace = true }
30-
uv-git = { workspace = true }
3126
uv-installer = { workspace = true }
3227
uv-macros = { workspace = true }
3328
uv-options-metadata = { workspace = true }
3429
uv-python = { workspace = true }
35-
uv-resolver = { workspace = true }
3630
uv-settings = { workspace = true, features = ["schemars"] }
37-
uv-types = { workspace = true }
3831
uv-workspace = { workspace = true, features = ["schemars"] }
3932

4033
# Any dependencies that are exclusively used in `uv-dev` should be listed as non-workspace
@@ -44,12 +37,11 @@ anyhow = { workspace = true }
4437
clap = { workspace = true, features = ["derive", "wrap_help"] }
4538
fs-err = { workspace = true, features = ["tokio"] }
4639
itertools = { workspace = true }
47-
markdown = "0.3.0"
40+
markdown = { version = "0.3.0" }
4841
owo-colors = { workspace = true }
4942
poloto = { version = "19.1.2", optional = true }
5043
pretty_assertions = { version = "1.4.0" }
5144
resvg = { version = "0.29.0", optional = true }
52-
rustc-hash = { workspace = true }
5345
schemars = { workspace = true }
5446
serde = { workspace = true }
5547
serde_json = { workspace = true }

0 commit comments

Comments
 (0)