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 https://github.com/astral-sh/uv/issues/1510

Closes https://github.com/astral-sh/uv/issues/1663.
This commit is contained in:
Charlie Marsh 2024-09-04 11:23:46 -04:00 committed by GitHub
parent a3a1bfd5ec
commit df84d25a7e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 1028 additions and 168 deletions

View file

@ -339,6 +339,21 @@ pub enum Commands {
after_long_help = ""
)]
Venv(VenvArgs),
/// Build Python packages into source distributions and wheels.
///
/// By default, `uv build` will build a source distribution ("sdist")
/// from the source directory, and a binary distribution ("wheel") from
/// the source distribution.
///
/// `uv build --sdist` can be used to build only the source distribution,
/// `uv build --wheel` can be used to build only the binary distribution,
/// and `uv build --sdist --wheel` can be used to build both distributions
/// from source.
#[command(
after_help = "Use `uv help build` for more details.",
after_long_help = ""
)]
Build(BuildArgs),
/// Manage uv's cache.
#[command(
after_help = "Use `uv help cache` for more details.",
@ -1126,7 +1141,7 @@ pub struct PipSyncArgs {
/// The Python interpreter into which packages should be installed.
///
/// By default, syncing requires a virtual environment. An path to an
/// By default, syncing requires a virtual environment. A path to an
/// alternative Python can be provided, but it is only recommended in
/// continuous integration (CI) environments and should be used with
/// caution, as it can modify the system Python installation.
@ -1408,7 +1423,7 @@ pub struct PipInstallArgs {
/// The Python interpreter into which packages should be installed.
///
/// By default, installation requires a virtual environment. An path to an
/// By default, installation requires a virtual environment. A path to an
/// alternative Python can be provided, but it is only recommended in
/// continuous integration (CI) environments and should be used with
/// caution, as it can modify the system Python installation.
@ -1573,7 +1588,7 @@ pub struct PipUninstallArgs {
/// The Python interpreter from which packages should be uninstalled.
///
/// By default, uninstallation requires a virtual environment. An path to an
/// By default, uninstallation requires a virtual environment. A path to an
/// alternative Python can be provided, but it is only recommended in
/// continuous integration (CI) environments and should be used with
/// caution, as it can modify the system Python installation.
@ -1924,6 +1939,55 @@ pub struct PipTreeArgs {
pub compat_args: compat::PipGlobalCompatArgs,
}
#[derive(Args)]
#[allow(clippy::struct_excessive_bools)]
pub struct BuildArgs {
/// The directory from which distributions should be built.
///
/// Defaults to the current working directory.
#[arg(value_parser = parse_file_path)]
pub src_dir: Option<PathBuf>,
/// The output directory to which distributions should be written.
///
/// Defaults to the `dist` subdirectory within the source directory.
#[arg(long, short, value_parser = parse_file_path)]
pub out_dir: Option<PathBuf>,
/// Build a source distribution ("sdist") from the given directory.
#[arg(long)]
pub sdist: bool,
/// Build a binary distribution ("wheel") from the given directory.
#[arg(long)]
pub wheel: bool,
/// The Python interpreter to use for the build environment.
///
/// By default, builds are executed in isolated virtual environments. The
/// discovered interpreter will be used to create those environments, and
/// will be symlinked or copied in depending on the platform.
///
/// See `uv help python` to view supported request formats.
#[arg(
long,
short,
env = "UV_PYTHON",
verbatim_doc_comment,
help_heading = "Python options"
)]
pub python: Option<String>,
#[command(flatten)]
pub resolver: ResolverArgs,
#[command(flatten)]
pub build: BuildOptionsArgs,
#[command(flatten)]
pub refresh: RefreshArgs,
}
#[derive(Args)]
#[allow(clippy::struct_excessive_bools)]
pub struct VenvArgs {
@ -2318,7 +2382,7 @@ pub struct RunArgs {
pub installer: ResolverInstallerArgs,
#[command(flatten)]
pub build: BuildArgs,
pub build: BuildOptionsArgs,
#[command(flatten)]
pub refresh: RefreshArgs,
@ -2452,7 +2516,7 @@ pub struct SyncArgs {
pub installer: ResolverInstallerArgs,
#[command(flatten)]
pub build: BuildArgs,
pub build: BuildOptionsArgs,
#[command(flatten)]
pub refresh: RefreshArgs,
@ -2505,7 +2569,7 @@ pub struct LockArgs {
pub resolver: ResolverArgs,
#[command(flatten)]
pub build: BuildArgs,
pub build: BuildOptionsArgs,
#[command(flatten)]
pub refresh: RefreshArgs,
@ -2619,7 +2683,7 @@ pub struct AddArgs {
pub installer: ResolverInstallerArgs,
#[command(flatten)]
pub build: BuildArgs,
pub build: BuildOptionsArgs,
#[command(flatten)]
pub refresh: RefreshArgs,
@ -2688,7 +2752,7 @@ pub struct RemoveArgs {
pub installer: ResolverInstallerArgs,
#[command(flatten)]
pub build: BuildArgs,
pub build: BuildOptionsArgs,
#[command(flatten)]
pub refresh: RefreshArgs,
@ -2748,7 +2812,7 @@ pub struct TreeArgs {
pub frozen: bool,
#[command(flatten)]
pub build: BuildArgs,
pub build: BuildOptionsArgs,
#[command(flatten)]
pub resolver: ResolverArgs,
@ -2853,7 +2917,7 @@ pub struct ExportArgs {
pub resolver: ResolverArgs,
#[command(flatten)]
pub build: BuildArgs,
pub build: BuildOptionsArgs,
#[command(flatten)]
pub refresh: RefreshArgs,
@ -3000,7 +3064,7 @@ pub struct ToolRunArgs {
pub installer: ResolverInstallerArgs,
#[command(flatten)]
pub build: BuildArgs,
pub build: BuildOptionsArgs,
#[command(flatten)]
pub refresh: RefreshArgs,
@ -3052,7 +3116,7 @@ pub struct ToolInstallArgs {
pub installer: ResolverInstallerArgs,
#[command(flatten)]
pub build: BuildArgs,
pub build: BuildOptionsArgs,
#[command(flatten)]
pub refresh: RefreshArgs,
@ -3137,7 +3201,7 @@ pub struct ToolUpgradeArgs {
pub installer: ResolverInstallerArgs,
#[command(flatten)]
pub build: BuildArgs,
pub build: BuildOptionsArgs,
}
#[derive(Args)]
@ -3441,7 +3505,7 @@ pub struct RefreshArgs {
#[derive(Args)]
#[allow(clippy::struct_excessive_bools)]
pub struct BuildArgs {
pub struct BuildOptionsArgs {
/// Don't build source distributions.
///
/// When enabled, resolving will not run arbitrary Python code. The cached wheels of

View file

@ -4,7 +4,8 @@ use uv_resolver::PrereleaseMode;
use uv_settings::{PipOptions, ResolverInstallerOptions, ResolverOptions};
use crate::{
BuildArgs, IndexArgs, InstallerArgs, Maybe, RefreshArgs, ResolverArgs, ResolverInstallerArgs,
BuildOptionsArgs, IndexArgs, InstallerArgs, Maybe, RefreshArgs, ResolverArgs,
ResolverInstallerArgs,
};
/// 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 {
}
}
/// Construct the [`ResolverOptions`] from the [`ResolverArgs`] and [`BuildArgs`].
pub fn resolver_options(resolver_args: ResolverArgs, build_args: BuildArgs) -> ResolverOptions {
/// Construct the [`ResolverOptions`] from the [`ResolverArgs`] and [`BuildOptionsArgs`].
pub fn resolver_options(
resolver_args: ResolverArgs,
build_args: BuildOptionsArgs,
) -> ResolverOptions {
let ResolverArgs {
index_args,
upgrade,
@ -228,7 +232,7 @@ pub fn resolver_options(resolver_args: ResolverArgs, build_args: BuildArgs) -> R
no_sources,
} = resolver_args;
let BuildArgs {
let BuildOptionsArgs {
no_build,
build,
no_build_package,
@ -281,10 +285,10 @@ pub fn resolver_options(resolver_args: ResolverArgs, build_args: BuildArgs) -> R
}
}
/// Construct the [`ResolverInstallerOptions`] from the [`ResolverInstallerArgs`] and [`BuildArgs`].
/// Construct the [`ResolverInstallerOptions`] from the [`ResolverInstallerArgs`] and [`BuildOptionsArgs`].
pub fn resolver_installer_options(
resolver_installer_args: ResolverInstallerArgs,
build_args: BuildArgs,
build_args: BuildOptionsArgs,
) -> ResolverInstallerOptions {
let ResolverInstallerArgs {
index_args,
@ -311,7 +315,7 @@ pub fn resolver_installer_options(
no_sources,
} = resolver_installer_args;
let BuildArgs {
let BuildOptionsArgs {
no_build,
build,
no_build_package,