mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-04 10:58:28 +00:00
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:
parent
a3a1bfd5ec
commit
df84d25a7e
16 changed files with 1028 additions and 168 deletions
10
Cargo.lock
generated
10
Cargo.lock
generated
|
@ -4464,6 +4464,7 @@ dependencies = [
|
|||
"cache-key",
|
||||
"clap",
|
||||
"ctrlc",
|
||||
"distribution-filename",
|
||||
"distribution-types",
|
||||
"etcetera",
|
||||
"filetime",
|
||||
|
@ -4512,6 +4513,7 @@ dependencies = [
|
|||
"uv-configuration",
|
||||
"uv-dispatch",
|
||||
"uv-distribution",
|
||||
"uv-extract",
|
||||
"uv-fs",
|
||||
"uv-git",
|
||||
"uv-installer",
|
||||
|
@ -4716,7 +4718,6 @@ dependencies = [
|
|||
"distribution-filename",
|
||||
"distribution-types",
|
||||
"fs-err",
|
||||
"install-wheel-rs",
|
||||
"itertools 0.13.0",
|
||||
"markdown",
|
||||
"mimalloc",
|
||||
|
@ -4726,7 +4727,6 @@ dependencies = [
|
|||
"pretty_assertions",
|
||||
"pypi-types",
|
||||
"resvg",
|
||||
"rustc-hash",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -4737,20 +4737,14 @@ dependencies = [
|
|||
"tracing",
|
||||
"tracing-durations-export",
|
||||
"tracing-subscriber",
|
||||
"uv-build",
|
||||
"uv-cache",
|
||||
"uv-cli",
|
||||
"uv-client",
|
||||
"uv-configuration",
|
||||
"uv-dispatch",
|
||||
"uv-git",
|
||||
"uv-installer",
|
||||
"uv-macros",
|
||||
"uv-options-metadata",
|
||||
"uv-python",
|
||||
"uv-resolver",
|
||||
"uv-settings",
|
||||
"uv-types",
|
||||
"uv-workspace",
|
||||
"walkdir",
|
||||
]
|
||||
|
|
|
@ -80,7 +80,7 @@ static DEFAULT_BACKEND: LazyLock<Pep517Backend> = LazyLock::new(|| Pep517Backend
|
|||
pub enum Error {
|
||||
#[error(transparent)]
|
||||
Io(#[from] io::Error),
|
||||
#[error("{} does not appear to be a Python project, as neither `pyproject.toml` nor `setup.py` is present in the directory", _0.simplified_display())]
|
||||
#[error("{} does not appear to be a Python project, as neither `pyproject.toml` nor `setup.py` are present in the directory", _0.simplified_display())]
|
||||
InvalidSourceDist(PathBuf),
|
||||
#[error("Invalid `pyproject.toml`")]
|
||||
InvalidPyprojectToml(#[from] toml::de::Error),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -18,23 +18,16 @@ workspace = true
|
|||
[dependencies]
|
||||
distribution-filename = { workspace = true }
|
||||
distribution-types = { workspace = true }
|
||||
install-wheel-rs = { workspace = true }
|
||||
pep508_rs = { workspace = true }
|
||||
pypi-types = { workspace = true }
|
||||
uv-build = { workspace = true }
|
||||
uv-cache = { workspace = true, features = ["clap"] }
|
||||
uv-cli = { workspace = true }
|
||||
uv-client = { workspace = true }
|
||||
uv-configuration = { workspace = true }
|
||||
uv-dispatch = { workspace = true }
|
||||
uv-git = { workspace = true }
|
||||
uv-installer = { workspace = true }
|
||||
uv-macros = { workspace = true }
|
||||
uv-options-metadata = { workspace = true }
|
||||
uv-python = { workspace = true }
|
||||
uv-resolver = { workspace = true }
|
||||
uv-settings = { workspace = true, features = ["schemars"] }
|
||||
uv-types = { workspace = true }
|
||||
uv-workspace = { workspace = true, features = ["schemars"] }
|
||||
|
||||
# Any dependencies that are exclusively used in `uv-dev` should be listed as non-workspace
|
||||
|
@ -44,12 +37,11 @@ anyhow = { workspace = true }
|
|||
clap = { workspace = true, features = ["derive", "wrap_help"] }
|
||||
fs-err = { workspace = true, features = ["tokio"] }
|
||||
itertools = { workspace = true }
|
||||
markdown = "0.3.0"
|
||||
markdown = { version = "0.3.0" }
|
||||
owo-colors = { workspace = true }
|
||||
poloto = { version = "19.1.2", optional = true }
|
||||
pretty_assertions = { version = "1.4.0" }
|
||||
resvg = { version = "0.29.0", optional = true }
|
||||
rustc-hash = { workspace = true }
|
||||
schemars = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
|
|
|
@ -1,114 +0,0 @@
|
|||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use clap::Parser;
|
||||
use fs_err as fs;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use distribution_types::IndexLocations;
|
||||
use uv_build::{SourceBuild, SourceBuildContext};
|
||||
use uv_cache::{Cache, CacheArgs};
|
||||
use uv_client::RegistryClientBuilder;
|
||||
use uv_configuration::{
|
||||
BuildKind, BuildOptions, Concurrency, ConfigSettings, IndexStrategy, SourceStrategy,
|
||||
};
|
||||
use uv_dispatch::BuildDispatch;
|
||||
use uv_git::GitResolver;
|
||||
use uv_python::{EnvironmentPreference, PythonEnvironment, PythonRequest};
|
||||
use uv_resolver::{FlatIndex, InMemoryIndex};
|
||||
use uv_types::{BuildIsolation, InFlight};
|
||||
|
||||
#[derive(Parser)]
|
||||
pub(crate) struct BuildArgs {
|
||||
/// Base python in a way that can be found with `which`
|
||||
/// TODO(konstin): Also use proper python parsing here
|
||||
#[clap(short, long)]
|
||||
python: Option<PathBuf>,
|
||||
/// Directory to story the built wheel in
|
||||
#[clap(short, long)]
|
||||
wheels: Option<PathBuf>,
|
||||
/// The source distribution to build, as a directory.
|
||||
sdist: PathBuf,
|
||||
/// The subdirectory to build within the source distribution.
|
||||
subdirectory: Option<PathBuf>,
|
||||
/// You can edit the python sources of an editable install and the changes will be used without
|
||||
/// the need to reinstall it.
|
||||
#[clap(short, long)]
|
||||
editable: bool,
|
||||
#[command(flatten)]
|
||||
cache_args: CacheArgs,
|
||||
}
|
||||
|
||||
/// Build a source distribution to a wheel
|
||||
pub(crate) async fn build(args: BuildArgs) -> Result<PathBuf> {
|
||||
let wheel_dir = if let Some(wheel_dir) = args.wheels {
|
||||
fs::create_dir_all(&wheel_dir).context("Invalid wheel directory")?;
|
||||
wheel_dir
|
||||
} else {
|
||||
env::current_dir()?
|
||||
};
|
||||
let build_kind = if args.editable {
|
||||
BuildKind::Editable
|
||||
} else {
|
||||
BuildKind::Wheel
|
||||
};
|
||||
|
||||
let cache = Cache::try_from(args.cache_args)?.init()?;
|
||||
|
||||
let client = RegistryClientBuilder::new(cache.clone()).build();
|
||||
let concurrency = Concurrency::default();
|
||||
let config_settings = ConfigSettings::default();
|
||||
let exclude_newer = None;
|
||||
let flat_index = FlatIndex::default();
|
||||
let git = GitResolver::default();
|
||||
let in_flight = InFlight::default();
|
||||
let index = InMemoryIndex::default();
|
||||
let index_urls = IndexLocations::default();
|
||||
let index_strategy = IndexStrategy::default();
|
||||
let sources = SourceStrategy::default();
|
||||
let python = PythonEnvironment::find(
|
||||
&PythonRequest::default(),
|
||||
EnvironmentPreference::OnlyVirtual,
|
||||
&cache,
|
||||
)?;
|
||||
let build_options = BuildOptions::default();
|
||||
let build_constraints = [];
|
||||
|
||||
let build_dispatch = BuildDispatch::new(
|
||||
&client,
|
||||
&cache,
|
||||
&build_constraints,
|
||||
python.interpreter(),
|
||||
&index_urls,
|
||||
&flat_index,
|
||||
&index,
|
||||
&git,
|
||||
&in_flight,
|
||||
index_strategy,
|
||||
&config_settings,
|
||||
BuildIsolation::Isolated,
|
||||
install_wheel_rs::linker::LinkMode::default(),
|
||||
&build_options,
|
||||
exclude_newer,
|
||||
sources,
|
||||
concurrency,
|
||||
);
|
||||
|
||||
let builder = SourceBuild::setup(
|
||||
&args.sdist,
|
||||
args.subdirectory.as_deref(),
|
||||
None,
|
||||
python.interpreter(),
|
||||
&build_dispatch,
|
||||
SourceBuildContext::default(),
|
||||
args.sdist.display().to_string(),
|
||||
config_settings.clone(),
|
||||
BuildIsolation::Isolated,
|
||||
build_kind,
|
||||
FxHashMap::default(),
|
||||
concurrency.builds,
|
||||
)
|
||||
.await?;
|
||||
Ok(wheel_dir.join(builder.build(&wheel_dir).await?))
|
||||
}
|
|
@ -4,7 +4,7 @@ use std::process::ExitCode;
|
|||
use std::str::FromStr;
|
||||
use std::time::Instant;
|
||||
|
||||
use anstream::{eprintln, println};
|
||||
use anstream::eprintln;
|
||||
use anyhow::Result;
|
||||
use clap::Parser;
|
||||
use owo_colors::OwoColorize;
|
||||
|
@ -16,7 +16,6 @@ use tracing_subscriber::layer::SubscriberExt;
|
|||
use tracing_subscriber::util::SubscriberInitExt;
|
||||
use tracing_subscriber::{EnvFilter, Layer};
|
||||
|
||||
use crate::build::{build, BuildArgs};
|
||||
use crate::clear_compile::ClearCompileArgs;
|
||||
use crate::compile::CompileArgs;
|
||||
use crate::generate_all::Args as GenerateAllArgs;
|
||||
|
@ -43,7 +42,6 @@ static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
|||
#[global_allocator]
|
||||
static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
|
||||
|
||||
mod build;
|
||||
mod clear_compile;
|
||||
mod compile;
|
||||
mod generate_all;
|
||||
|
@ -57,8 +55,6 @@ const ROOT_DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../../");
|
|||
|
||||
#[derive(Parser)]
|
||||
enum Cli {
|
||||
/// Build a source distribution into a wheel.
|
||||
Build(BuildArgs),
|
||||
/// Display the metadata for a `.whl` at a given URL.
|
||||
WheelMetadata(WheelMetadataArgs),
|
||||
/// Compile all `.py` to `.pyc` files in the tree.
|
||||
|
@ -82,10 +78,6 @@ enum Cli {
|
|||
async fn run() -> Result<()> {
|
||||
let cli = Cli::parse();
|
||||
match cli {
|
||||
Cli::Build(args) => {
|
||||
let target = build(args).await?;
|
||||
println!("Wheel built to {}", target.display());
|
||||
}
|
||||
Cli::WheelMetadata(args) => wheel_metadata::wheel_metadata(args).await?,
|
||||
Cli::Compile(args) => compile::compile(args).await?,
|
||||
Cli::ClearCompile(args) => clear_compile::clear_compile(&args)?,
|
||||
|
|
|
@ -15,6 +15,7 @@ workspace = true
|
|||
|
||||
[dependencies]
|
||||
cache-key = { workspace = true }
|
||||
distribution-filename = { workspace = true }
|
||||
distribution-types = { workspace = true }
|
||||
install-wheel-rs = { workspace = true, default-features = false }
|
||||
pep440_rs = { workspace = true }
|
||||
|
@ -28,6 +29,7 @@ uv-client = { workspace = true }
|
|||
uv-configuration = { workspace = true }
|
||||
uv-dispatch = { workspace = true }
|
||||
uv-distribution = { workspace = true }
|
||||
uv-extract = { workspace = true }
|
||||
uv-fs = { workspace = true }
|
||||
uv-git = { workspace = true }
|
||||
uv-installer = { workspace = true }
|
||||
|
@ -64,6 +66,7 @@ regex = { workspace = true }
|
|||
rustc-hash = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
tempfile = { workspace = true }
|
||||
textwrap = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
|
|
383
crates/uv/src/commands/build.rs
Normal file
383
crates/uv/src/commands/build.rs
Normal file
|
@ -0,0 +1,383 @@
|
|||
use crate::commands::project::find_requires_python;
|
||||
use crate::commands::reporters::PythonDownloadReporter;
|
||||
use crate::commands::{ExitStatus, SharedState};
|
||||
use crate::printer::Printer;
|
||||
use crate::settings::{ResolverSettings, ResolverSettingsRef};
|
||||
use std::borrow::Cow;
|
||||
|
||||
use anyhow::Result;
|
||||
use distribution_filename::SourceDistExtension;
|
||||
use owo_colors::OwoColorize;
|
||||
use std::path::{Path, PathBuf};
|
||||
use uv_auth::store_credentials_from_url;
|
||||
use uv_cache::Cache;
|
||||
use uv_client::{BaseClientBuilder, Connectivity, FlatIndexClient, RegistryClientBuilder};
|
||||
use uv_configuration::{BuildKind, Concurrency};
|
||||
use uv_dispatch::BuildDispatch;
|
||||
use uv_fs::{Simplified, CWD};
|
||||
use uv_python::{
|
||||
EnvironmentPreference, PythonDownloads, PythonEnvironment, PythonInstallation,
|
||||
PythonPreference, PythonRequest, PythonVersionFile, VersionRequest,
|
||||
};
|
||||
use uv_resolver::{FlatIndex, RequiresPython};
|
||||
use uv_types::{BuildContext, BuildIsolation, HashStrategy};
|
||||
use uv_warnings::warn_user_once;
|
||||
use uv_workspace::{DiscoveryOptions, VirtualProject, WorkspaceError};
|
||||
|
||||
/// Build source distributions and wheels.
|
||||
#[allow(clippy::fn_params_excessive_bools)]
|
||||
pub(crate) async fn build(
|
||||
src_dir: Option<PathBuf>,
|
||||
output_dir: Option<PathBuf>,
|
||||
sdist: bool,
|
||||
wheel: bool,
|
||||
python: Option<String>,
|
||||
settings: ResolverSettings,
|
||||
no_config: bool,
|
||||
python_preference: PythonPreference,
|
||||
python_downloads: PythonDownloads,
|
||||
connectivity: Connectivity,
|
||||
concurrency: Concurrency,
|
||||
native_tls: bool,
|
||||
cache: &Cache,
|
||||
printer: Printer,
|
||||
) -> Result<ExitStatus> {
|
||||
let assets = build_impl(
|
||||
src_dir.as_deref(),
|
||||
output_dir.as_deref(),
|
||||
sdist,
|
||||
wheel,
|
||||
python.as_deref(),
|
||||
settings.as_ref(),
|
||||
no_config,
|
||||
python_preference,
|
||||
python_downloads,
|
||||
connectivity,
|
||||
concurrency,
|
||||
native_tls,
|
||||
cache,
|
||||
printer,
|
||||
)
|
||||
.await?;
|
||||
|
||||
match assets {
|
||||
BuiltDistributions::Wheel(wheel) => {
|
||||
anstream::eprintln!("Successfully built {}", wheel.user_display().bold().cyan());
|
||||
}
|
||||
BuiltDistributions::Sdist(sdist) => {
|
||||
anstream::eprintln!("Successfully built {}", sdist.user_display().bold().cyan());
|
||||
}
|
||||
BuiltDistributions::Both(sdist, wheel) => {
|
||||
anstream::eprintln!(
|
||||
"Successfully built {} and {}",
|
||||
sdist.user_display().bold().cyan(),
|
||||
wheel.user_display().bold().cyan()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ExitStatus::Success)
|
||||
}
|
||||
|
||||
#[allow(clippy::fn_params_excessive_bools)]
|
||||
async fn build_impl(
|
||||
src_dir: Option<&Path>,
|
||||
output_dir: Option<&Path>,
|
||||
sdist: bool,
|
||||
wheel: bool,
|
||||
python_request: Option<&str>,
|
||||
settings: ResolverSettingsRef<'_>,
|
||||
no_config: bool,
|
||||
python_preference: PythonPreference,
|
||||
python_downloads: PythonDownloads,
|
||||
connectivity: Connectivity,
|
||||
concurrency: Concurrency,
|
||||
native_tls: bool,
|
||||
cache: &Cache,
|
||||
printer: Printer,
|
||||
) -> Result<BuiltDistributions> {
|
||||
// Extract the resolver settings.
|
||||
let ResolverSettingsRef {
|
||||
index_locations,
|
||||
index_strategy,
|
||||
keyring_provider,
|
||||
allow_insecure_host,
|
||||
resolution: _,
|
||||
prerelease: _,
|
||||
config_setting,
|
||||
no_build_isolation,
|
||||
no_build_isolation_package,
|
||||
exclude_newer,
|
||||
link_mode,
|
||||
upgrade: _,
|
||||
build_options,
|
||||
sources,
|
||||
} = settings;
|
||||
|
||||
let client_builder = BaseClientBuilder::default()
|
||||
.connectivity(connectivity)
|
||||
.native_tls(native_tls);
|
||||
|
||||
let src_dir = if let Some(src_dir) = src_dir {
|
||||
Cow::Owned(std::path::absolute(src_dir)?)
|
||||
} else {
|
||||
Cow::Borrowed(&*CWD)
|
||||
};
|
||||
|
||||
let output_dir = if let Some(output_dir) = output_dir {
|
||||
std::path::absolute(output_dir)?
|
||||
} else {
|
||||
src_dir.join("dist")
|
||||
};
|
||||
|
||||
// (1) Explicit request from user
|
||||
let mut interpreter_request = python_request.map(PythonRequest::parse);
|
||||
|
||||
// (2) Request from `.python-version`
|
||||
if interpreter_request.is_none() {
|
||||
interpreter_request = PythonVersionFile::discover(src_dir.as_ref(), no_config, false)
|
||||
.await?
|
||||
.and_then(PythonVersionFile::into_version);
|
||||
}
|
||||
|
||||
// (3) `Requires-Python` in `pyproject.toml`
|
||||
if interpreter_request.is_none() {
|
||||
let project =
|
||||
match VirtualProject::discover(src_dir.as_ref(), &DiscoveryOptions::default()).await {
|
||||
Ok(project) => Some(project),
|
||||
Err(WorkspaceError::MissingProject(_)) => None,
|
||||
Err(WorkspaceError::MissingPyprojectToml) => None,
|
||||
Err(WorkspaceError::NonWorkspace(_)) => None,
|
||||
Err(err) => {
|
||||
warn_user_once!("{err}");
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(project) = project {
|
||||
interpreter_request = find_requires_python(project.workspace())?
|
||||
.as_ref()
|
||||
.map(RequiresPython::specifiers)
|
||||
.map(|specifiers| {
|
||||
PythonRequest::Version(VersionRequest::Range(specifiers.clone()))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Locate the Python interpreter to use in the environment.
|
||||
let interpreter = PythonInstallation::find_or_download(
|
||||
interpreter_request.as_ref(),
|
||||
EnvironmentPreference::Any,
|
||||
python_preference,
|
||||
python_downloads,
|
||||
&client_builder,
|
||||
cache,
|
||||
Some(&PythonDownloadReporter::single(printer)),
|
||||
)
|
||||
.await?
|
||||
.into_interpreter();
|
||||
|
||||
// Add all authenticated sources to the cache.
|
||||
for url in index_locations.urls() {
|
||||
store_credentials_from_url(url);
|
||||
}
|
||||
|
||||
// Initialize the registry client.
|
||||
let client = RegistryClientBuilder::new(cache.clone())
|
||||
.native_tls(native_tls)
|
||||
.connectivity(connectivity)
|
||||
.index_urls(index_locations.index_urls())
|
||||
.index_strategy(index_strategy)
|
||||
.keyring(keyring_provider)
|
||||
.allow_insecure_host(allow_insecure_host.to_vec())
|
||||
.markers(interpreter.markers())
|
||||
.platform(interpreter.platform())
|
||||
.build();
|
||||
|
||||
// Determine whether to enable build isolation.
|
||||
let environment;
|
||||
let build_isolation = if no_build_isolation {
|
||||
environment = PythonEnvironment::from_interpreter(interpreter.clone());
|
||||
BuildIsolation::Shared(&environment)
|
||||
} else if no_build_isolation_package.is_empty() {
|
||||
BuildIsolation::Isolated
|
||||
} else {
|
||||
environment = PythonEnvironment::from_interpreter(interpreter.clone());
|
||||
BuildIsolation::SharedPackage(&environment, no_build_isolation_package)
|
||||
};
|
||||
|
||||
// TODO(charlie): These are all default values. We should consider whether we want to make them
|
||||
// optional on the downstream APIs.
|
||||
let build_constraints = [];
|
||||
let hasher = HashStrategy::None;
|
||||
|
||||
// Resolve the flat indexes from `--find-links`.
|
||||
let flat_index = {
|
||||
let client = FlatIndexClient::new(&client, cache);
|
||||
let entries = client.fetch(index_locations.flat_index()).await?;
|
||||
FlatIndex::from_entries(entries, None, &hasher, build_options)
|
||||
};
|
||||
|
||||
// Initialize any shared state.
|
||||
let state = SharedState::default();
|
||||
|
||||
// Create a build dispatch.
|
||||
let build_dispatch = BuildDispatch::new(
|
||||
&client,
|
||||
cache,
|
||||
&build_constraints,
|
||||
&interpreter,
|
||||
index_locations,
|
||||
&flat_index,
|
||||
&state.index,
|
||||
&state.git,
|
||||
&state.in_flight,
|
||||
index_strategy,
|
||||
config_setting,
|
||||
build_isolation,
|
||||
link_mode,
|
||||
build_options,
|
||||
exclude_newer,
|
||||
sources,
|
||||
concurrency,
|
||||
);
|
||||
|
||||
fs_err::tokio::create_dir_all(&output_dir).await?;
|
||||
|
||||
// Determine the build plan from the command-line arguments.
|
||||
let plan = match (sdist, wheel) {
|
||||
(false, false) => BuildPlan::SdistToWheel,
|
||||
(true, false) => BuildPlan::Sdist,
|
||||
(false, true) => BuildPlan::Wheel,
|
||||
(true, true) => BuildPlan::SdistAndWheel,
|
||||
};
|
||||
|
||||
// Prepare some common arguments for the build.
|
||||
let subdirectory = None;
|
||||
let version_id = src_dir.file_name().unwrap().to_string_lossy();
|
||||
let dist = None;
|
||||
|
||||
let assets = match plan {
|
||||
BuildPlan::SdistToWheel => {
|
||||
// Build the sdist.
|
||||
let builder = build_dispatch
|
||||
.setup_build(
|
||||
src_dir.as_ref(),
|
||||
subdirectory,
|
||||
&version_id,
|
||||
dist,
|
||||
BuildKind::Sdist,
|
||||
)
|
||||
.await?;
|
||||
let sdist = builder.build(&output_dir).await?;
|
||||
|
||||
// Extract the source distribution into a temporary directory.
|
||||
let path = output_dir.join(&sdist);
|
||||
let reader = fs_err::tokio::File::open(&path).await?;
|
||||
let ext = SourceDistExtension::from_path(&path)?;
|
||||
let temp_dir = tempfile::tempdir_in(&output_dir)?;
|
||||
uv_extract::stream::archive(reader, ext, temp_dir.path()).await?;
|
||||
|
||||
// Extract the top-level directory from the archive.
|
||||
let extracted = match uv_extract::strip_component(temp_dir.path()) {
|
||||
Ok(top_level) => top_level,
|
||||
Err(uv_extract::Error::NonSingularArchive(_)) => temp_dir.path().to_path_buf(),
|
||||
Err(err) => return Err(err.into()),
|
||||
};
|
||||
|
||||
// Build a wheel from the source distribution.
|
||||
let builder = build_dispatch
|
||||
.setup_build(
|
||||
&extracted,
|
||||
subdirectory,
|
||||
&version_id,
|
||||
dist,
|
||||
BuildKind::Wheel,
|
||||
)
|
||||
.await?;
|
||||
let wheel = builder.build(&output_dir).await?;
|
||||
|
||||
BuiltDistributions::Both(output_dir.join(sdist), output_dir.join(wheel))
|
||||
}
|
||||
BuildPlan::Sdist => {
|
||||
let builder = build_dispatch
|
||||
.setup_build(
|
||||
src_dir.as_ref(),
|
||||
subdirectory,
|
||||
&version_id,
|
||||
dist,
|
||||
BuildKind::Sdist,
|
||||
)
|
||||
.await?;
|
||||
let sdist = builder.build(&output_dir).await?;
|
||||
|
||||
BuiltDistributions::Sdist(output_dir.join(sdist))
|
||||
}
|
||||
BuildPlan::Wheel => {
|
||||
let builder = build_dispatch
|
||||
.setup_build(
|
||||
src_dir.as_ref(),
|
||||
subdirectory,
|
||||
&version_id,
|
||||
dist,
|
||||
BuildKind::Wheel,
|
||||
)
|
||||
.await?;
|
||||
let wheel = builder.build(&output_dir).await?;
|
||||
|
||||
BuiltDistributions::Wheel(output_dir.join(wheel))
|
||||
}
|
||||
BuildPlan::SdistAndWheel => {
|
||||
let builder = build_dispatch
|
||||
.setup_build(
|
||||
src_dir.as_ref(),
|
||||
subdirectory,
|
||||
&version_id,
|
||||
dist,
|
||||
BuildKind::Sdist,
|
||||
)
|
||||
.await?;
|
||||
let sdist = builder.build(&output_dir).await?;
|
||||
|
||||
let builder = build_dispatch
|
||||
.setup_build(
|
||||
src_dir.as_ref(),
|
||||
subdirectory,
|
||||
&version_id,
|
||||
dist,
|
||||
BuildKind::Wheel,
|
||||
)
|
||||
.await?;
|
||||
let wheel = builder.build(&output_dir).await?;
|
||||
|
||||
BuiltDistributions::Both(output_dir.join(&sdist), output_dir.join(&wheel))
|
||||
}
|
||||
};
|
||||
|
||||
Ok(assets)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
enum BuiltDistributions {
|
||||
/// A built wheel.
|
||||
Wheel(PathBuf),
|
||||
/// A built source distribution.
|
||||
Sdist(PathBuf),
|
||||
/// A built source distribution and wheel.
|
||||
Both(PathBuf, PathBuf),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
enum BuildPlan {
|
||||
/// Build a source distribution from source, then build the wheel from the source distribution.
|
||||
SdistToWheel,
|
||||
|
||||
/// Build a source distribution from source.
|
||||
Sdist,
|
||||
|
||||
/// Build a wheel from source.
|
||||
Wheel,
|
||||
|
||||
/// Build a source distribution and a wheel from source.
|
||||
SdistAndWheel,
|
||||
}
|
|
@ -4,6 +4,7 @@ use std::{fmt::Display, fmt::Write, process::ExitCode};
|
|||
use anyhow::Context;
|
||||
use owo_colors::OwoColorize;
|
||||
|
||||
pub(crate) use build::build;
|
||||
pub(crate) use cache_clean::cache_clean;
|
||||
pub(crate) use cache_dir::cache_dir;
|
||||
pub(crate) use cache_prune::cache_prune;
|
||||
|
@ -65,6 +66,7 @@ mod python;
|
|||
pub(crate) mod reporters;
|
||||
mod tool;
|
||||
|
||||
mod build;
|
||||
#[cfg(feature = "self-update")]
|
||||
mod self_update;
|
||||
mod venv;
|
||||
|
|
|
@ -659,6 +659,35 @@ async fn run(cli: Cli) -> Result<ExitStatus> {
|
|||
commands::cache_dir(&cache);
|
||||
Ok(ExitStatus::Success)
|
||||
}
|
||||
Commands::Build(args) => {
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
let args = settings::BuildSettings::resolve(args, filesystem);
|
||||
show_settings!(args);
|
||||
|
||||
// Initialize the cache.
|
||||
let cache = cache.init()?.with_refresh(
|
||||
args.refresh
|
||||
.combine(Refresh::from(args.settings.upgrade.clone())),
|
||||
);
|
||||
|
||||
commands::build(
|
||||
args.src_dir,
|
||||
args.out_dir,
|
||||
args.sdist,
|
||||
args.wheel,
|
||||
args.python,
|
||||
args.settings,
|
||||
cli.no_config,
|
||||
globals.python_preference,
|
||||
globals.python_downloads,
|
||||
globals.connectivity,
|
||||
globals.concurrency,
|
||||
globals.native_tls,
|
||||
&cache,
|
||||
printer,
|
||||
)
|
||||
.await
|
||||
}
|
||||
Commands::Venv(args) => {
|
||||
args.compat_args.validate()?;
|
||||
|
||||
|
@ -1134,7 +1163,10 @@ async fn run_project(
|
|||
show_settings!(args);
|
||||
|
||||
// Initialize the cache.
|
||||
let cache = cache.init()?;
|
||||
let cache = cache.init()?.with_refresh(
|
||||
args.refresh
|
||||
.combine(Refresh::from(args.settings.upgrade.clone())),
|
||||
);
|
||||
|
||||
commands::lock(
|
||||
args.locked,
|
||||
|
|
|
@ -11,7 +11,7 @@ use pypi_types::{Requirement, SupportedEnvironments};
|
|||
use uv_cache::{CacheArgs, Refresh};
|
||||
use uv_cli::{
|
||||
options::{flag, resolver_installer_options, resolver_options},
|
||||
ExportArgs, ToolUpgradeArgs,
|
||||
BuildArgs, ExportArgs, ToolUpgradeArgs,
|
||||
};
|
||||
use uv_cli::{
|
||||
AddArgs, ColorChoice, ExternalCommand, GlobalArgs, InitArgs, ListFormat, LockArgs, Maybe,
|
||||
|
@ -1611,7 +1611,46 @@ impl PipCheckSettings {
|
|||
}
|
||||
}
|
||||
|
||||
/// The resolved settings to use for a `pip check` invocation.
|
||||
/// The resolved settings to use for a `build` invocation.
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct BuildSettings {
|
||||
pub(crate) src_dir: Option<PathBuf>,
|
||||
pub(crate) out_dir: Option<PathBuf>,
|
||||
pub(crate) sdist: bool,
|
||||
pub(crate) wheel: bool,
|
||||
pub(crate) python: Option<String>,
|
||||
pub(crate) refresh: Refresh,
|
||||
pub(crate) settings: ResolverSettings,
|
||||
}
|
||||
|
||||
impl BuildSettings {
|
||||
/// Resolve the [`BuildSettings`] from the CLI and filesystem configuration.
|
||||
pub(crate) fn resolve(args: BuildArgs, filesystem: Option<FilesystemOptions>) -> Self {
|
||||
let BuildArgs {
|
||||
src_dir,
|
||||
out_dir,
|
||||
sdist,
|
||||
wheel,
|
||||
python,
|
||||
build,
|
||||
refresh,
|
||||
resolver,
|
||||
} = args;
|
||||
|
||||
Self {
|
||||
src_dir,
|
||||
out_dir,
|
||||
sdist,
|
||||
wheel,
|
||||
python,
|
||||
refresh: Refresh::from(refresh),
|
||||
settings: ResolverSettings::combine(resolver_options(resolver, build), filesystem),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The resolved settings to use for a `venv` invocation.
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct VenvSettings {
|
||||
|
|
191
crates/uv/tests/build.rs
Normal file
191
crates/uv/tests/build.rs
Normal file
|
@ -0,0 +1,191 @@
|
|||
#![cfg(all(feature = "python", feature = "pypi"))]
|
||||
|
||||
use anyhow::Result;
|
||||
use assert_fs::prelude::*;
|
||||
use common::{uv_snapshot, TestContext};
|
||||
use predicates::prelude::predicate;
|
||||
|
||||
mod common;
|
||||
|
||||
#[test]
|
||||
fn build() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let project = context.temp_dir.child("project");
|
||||
|
||||
let pyproject_toml = project.child("pyproject.toml");
|
||||
pyproject_toml.write_str(
|
||||
r#"
|
||||
[project]
|
||||
name = "project"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = ["anyio==3.7.0"]
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools>=42"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
"#,
|
||||
)?;
|
||||
|
||||
project.child("src").child("__init__.py").touch()?;
|
||||
|
||||
// Build the specified path.
|
||||
uv_snapshot!(context.filters(), context.build().arg("project"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Successfully built project/dist/project-0.1.0.tar.gz and project/dist/project-0.1.0-py3-none-any.whl
|
||||
"###);
|
||||
|
||||
// Build the current working directory.
|
||||
uv_snapshot!(context.filters(), context.build().current_dir(project.path()), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Successfully built dist/project-0.1.0.tar.gz and dist/project-0.1.0-py3-none-any.whl
|
||||
"###);
|
||||
|
||||
// Error if there's nothing to build.
|
||||
uv_snapshot!(context.filters(), context.build(), @r###"
|
||||
success: false
|
||||
exit_code: 2
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
error: [TEMP_DIR]/ does not appear to be a Python project, as neither `pyproject.toml` nor `setup.py` are present in the directory
|
||||
"###);
|
||||
|
||||
// Build to a specified path.
|
||||
uv_snapshot!(context.filters(), context.build().arg("--out-dir").arg("out").current_dir(project.path()), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Successfully built out/project-0.1.0.tar.gz and out/project-0.1.0-py3-none-any.whl
|
||||
"###);
|
||||
|
||||
project
|
||||
.child("out")
|
||||
.child("project-0.1.0.tar.gz")
|
||||
.assert(predicate::path::is_file());
|
||||
project
|
||||
.child("out")
|
||||
.child("project-0.1.0-py3-none-any.whl")
|
||||
.assert(predicate::path::is_file());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sdist() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let project = context.temp_dir.child("project");
|
||||
|
||||
let pyproject_toml = project.child("pyproject.toml");
|
||||
pyproject_toml.write_str(
|
||||
r#"
|
||||
[project]
|
||||
name = "project"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = ["anyio==3.7.0"]
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools>=42"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
"#,
|
||||
)?;
|
||||
|
||||
project.child("src").child("__init__.py").touch()?;
|
||||
|
||||
// Build the specified path.
|
||||
uv_snapshot!(context.filters(), context.build().arg("--sdist").current_dir(&project), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Successfully built dist/project-0.1.0.tar.gz
|
||||
"###);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wheel() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let project = context.temp_dir.child("project");
|
||||
|
||||
let pyproject_toml = project.child("pyproject.toml");
|
||||
pyproject_toml.write_str(
|
||||
r#"
|
||||
[project]
|
||||
name = "project"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = ["anyio==3.7.0"]
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools>=42"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
"#,
|
||||
)?;
|
||||
|
||||
project.child("src").child("__init__.py").touch()?;
|
||||
|
||||
// Build the specified path.
|
||||
uv_snapshot!(context.filters(), context.build().arg("--wheel").current_dir(&project), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Successfully built dist/project-0.1.0-py3-none-any.whl
|
||||
"###);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sdist_wheel() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let project = context.temp_dir.child("project");
|
||||
|
||||
let pyproject_toml = project.child("pyproject.toml");
|
||||
pyproject_toml.write_str(
|
||||
r#"
|
||||
[project]
|
||||
name = "project"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = ["anyio==3.7.0"]
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools>=42"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
"#,
|
||||
)?;
|
||||
|
||||
project.child("src").child("__init__.py").touch()?;
|
||||
|
||||
// Build the specified path.
|
||||
uv_snapshot!(context.filters(), context.build().arg("--sdist").arg("--wheel").current_dir(&project), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Successfully built dist/project-0.1.0.tar.gz and dist/project-0.1.0-py3-none-any.whl
|
||||
"###);
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -569,6 +569,14 @@ impl TestContext {
|
|||
command
|
||||
}
|
||||
|
||||
/// Create a `uv build` command with options shared across scenarios.
|
||||
pub fn build(&self) -> Command {
|
||||
let mut command = Command::new(get_bin());
|
||||
command.arg("build");
|
||||
self.add_shared_args(&mut command, false);
|
||||
command
|
||||
}
|
||||
|
||||
/// Create a `uv python find` command with options shared across scenarios.
|
||||
pub fn python_find(&self) -> Command {
|
||||
let mut command = Command::new(get_bin());
|
||||
|
|
|
@ -28,6 +28,7 @@ fn help() {
|
|||
python Manage Python versions and installations
|
||||
pip Manage Python packages with a pip-compatible interface
|
||||
venv Create a virtual environment
|
||||
build Build Python packages into source distributions and wheels
|
||||
cache Manage uv's cache
|
||||
version Display uv's version
|
||||
generate-shell-completion Generate shell completion
|
||||
|
@ -92,6 +93,7 @@ fn help_flag() {
|
|||
python Manage Python versions and installations
|
||||
pip Manage Python packages with a pip-compatible interface
|
||||
venv Create a virtual environment
|
||||
build Build Python packages into source distributions and wheels
|
||||
cache Manage uv's cache
|
||||
version Display uv's version
|
||||
help Display documentation for a command
|
||||
|
@ -154,6 +156,7 @@ fn help_short_flag() {
|
|||
python Manage Python versions and installations
|
||||
pip Manage Python packages with a pip-compatible interface
|
||||
venv Create a virtual environment
|
||||
build Build Python packages into source distributions and wheels
|
||||
cache Manage uv's cache
|
||||
version Display uv's version
|
||||
help Display documentation for a command
|
||||
|
@ -633,6 +636,7 @@ fn help_unknown_subcommand() {
|
|||
python
|
||||
pip
|
||||
venv
|
||||
build
|
||||
cache
|
||||
version
|
||||
generate-shell-completion
|
||||
|
@ -657,6 +661,7 @@ fn help_unknown_subcommand() {
|
|||
python
|
||||
pip
|
||||
venv
|
||||
build
|
||||
cache
|
||||
version
|
||||
generate-shell-completion
|
||||
|
@ -708,6 +713,7 @@ fn help_with_global_option() {
|
|||
python Manage Python versions and installations
|
||||
pip Manage Python packages with a pip-compatible interface
|
||||
venv Create a virtual environment
|
||||
build Build Python packages into source distributions and wheels
|
||||
cache Manage uv's cache
|
||||
version Display uv's version
|
||||
generate-shell-completion Generate shell completion
|
||||
|
@ -808,6 +814,7 @@ fn help_with_no_pager() {
|
|||
python Manage Python versions and installations
|
||||
pip Manage Python packages with a pip-compatible interface
|
||||
venv Create a virtual environment
|
||||
build Build Python packages into source distributions and wheels
|
||||
cache Manage uv's cache
|
||||
version Display uv's version
|
||||
generate-shell-completion Generate shell completion
|
||||
|
|
|
@ -36,6 +36,8 @@ uv [OPTIONS] <COMMAND>
|
|||
</dd>
|
||||
<dt><a href="#uv-venv"><code>uv venv</code></a></dt><dd><p>Create a virtual environment</p>
|
||||
</dd>
|
||||
<dt><a href="#uv-build"><code>uv build</code></a></dt><dd><p>Build Python packages into source distributions and wheels</p>
|
||||
</dd>
|
||||
<dt><a href="#uv-cache"><code>uv cache</code></a></dt><dd><p>Manage uv’s cache</p>
|
||||
</dd>
|
||||
<dt><a href="#uv-version"><code>uv version</code></a></dt><dd><p>Display uv’s version</p>
|
||||
|
@ -4766,7 +4768,7 @@ uv pip sync [OPTIONS] <SRC_FILE>...
|
|||
|
||||
</dd><dt><code>--python</code>, <code>-p</code> <i>python</i></dt><dd><p>The Python interpreter into which packages should be installed.</p>
|
||||
|
||||
<p>By default, syncing requires a virtual environment. An 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.</p>
|
||||
<p>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.</p>
|
||||
|
||||
<p>See <a href="#uv-python">uv python</a> for details on Python discovery and supported request formats.</p>
|
||||
|
||||
|
@ -5128,7 +5130,7 @@ uv pip install [OPTIONS] <PACKAGE|--requirement <REQUIREMENT>|--editable <EDITAB
|
|||
</ul>
|
||||
</dd><dt><code>--python</code>, <code>-p</code> <i>python</i></dt><dd><p>The Python interpreter into which packages should be installed.</p>
|
||||
|
||||
<p>By default, installation requires a virtual environment. An 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.</p>
|
||||
<p>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.</p>
|
||||
|
||||
<p>See <a href="#uv-python">uv python</a> for details on Python discovery and supported request formats.</p>
|
||||
|
||||
|
@ -5364,7 +5366,7 @@ uv pip uninstall [OPTIONS] <PACKAGE|--requirement <REQUIREMENT>>
|
|||
|
||||
</dd><dt><code>--python</code>, <code>-p</code> <i>python</i></dt><dd><p>The Python interpreter from which packages should be uninstalled.</p>
|
||||
|
||||
<p>By default, uninstallation requires a virtual environment. An 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.</p>
|
||||
<p>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.</p>
|
||||
|
||||
<p>See <a href="#uv-python">uv python</a> for details on Python discovery and supported request formats.</p>
|
||||
|
||||
|
@ -6184,6 +6186,267 @@ uv venv [OPTIONS] [PATH]
|
|||
|
||||
</dd></dl>
|
||||
|
||||
## uv build
|
||||
|
||||
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.
|
||||
|
||||
<h3 class="cli-reference">Usage</h3>
|
||||
|
||||
```
|
||||
uv build [OPTIONS] [SRC_DIR]
|
||||
```
|
||||
|
||||
<h3 class="cli-reference">Arguments</h3>
|
||||
|
||||
<dl class="cli-reference"><dt><code>SRC_DIR</code></dt><dd><p>The directory from which distributions should be built.</p>
|
||||
|
||||
<p>Defaults to the current working directory.</p>
|
||||
|
||||
</dd></dl>
|
||||
|
||||
<h3 class="cli-reference">Options</h3>
|
||||
|
||||
<dl class="cli-reference"><dt><code>--allow-insecure-host</code> <i>allow-insecure-host</i></dt><dd><p>Allow insecure connections to a host.</p>
|
||||
|
||||
<p>Can be provided multiple times.</p>
|
||||
|
||||
<p>Expects to receive either a hostname (e.g., <code>localhost</code>), a host-port pair (e.g., <code>localhost:8080</code>), or a URL (e.g., <code>https://localhost</code>).</p>
|
||||
|
||||
<p>WARNING: Hosts included in this list will not be verified against the system’s certificate store. Only use <code>--allow-insecure-host</code> in a secure network with verified sources, as it bypasses SSL verification and could expose you to MITM attacks.</p>
|
||||
|
||||
<p>May also be set with the <code>UV_INSECURE_HOST</code> environment variable.</p>
|
||||
</dd><dt><code>--cache-dir</code> <i>cache-dir</i></dt><dd><p>Path to the cache directory.</p>
|
||||
|
||||
<p>Defaults to <code>$HOME/Library/Caches/uv</code> on macOS, <code>$XDG_CACHE_HOME/uv</code> or <code>$HOME/.cache/uv</code> on Linux, and <code>%LOCALAPPDATA%\uv\cache</code> on Windows.</p>
|
||||
|
||||
<p>May also be set with the <code>UV_CACHE_DIR</code> environment variable.</p>
|
||||
</dd><dt><code>--color</code> <i>color-choice</i></dt><dd><p>Control colors in output</p>
|
||||
|
||||
<p>[default: auto]</p>
|
||||
<p>Possible values:</p>
|
||||
|
||||
<ul>
|
||||
<li><code>auto</code>: Enables colored output only when the output is going to a terminal or TTY with support</li>
|
||||
|
||||
<li><code>always</code>: Enables colored output regardless of the detected environment</li>
|
||||
|
||||
<li><code>never</code>: Disables colored output</li>
|
||||
</ul>
|
||||
</dd><dt><code>--config-file</code> <i>config-file</i></dt><dd><p>The path to a <code>uv.toml</code> file to use for configuration.</p>
|
||||
|
||||
<p>While uv configuration can be included in a <code>pyproject.toml</code> file, it is not allowed in this context.</p>
|
||||
|
||||
<p>May also be set with the <code>UV_CONFIG_FILE</code> environment variable.</p>
|
||||
</dd><dt><code>--config-setting</code>, <code>-C</code> <i>config-setting</i></dt><dd><p>Settings to pass to the PEP 517 build backend, specified as <code>KEY=VALUE</code> pairs</p>
|
||||
|
||||
</dd><dt><code>--exclude-newer</code> <i>exclude-newer</i></dt><dd><p>Limit candidate packages to those that were uploaded prior to the given date.</p>
|
||||
|
||||
<p>Accepts both RFC 3339 timestamps (e.g., <code>2006-12-02T02:07:43Z</code>) and local dates in the same format (e.g., <code>2006-12-02</code>) in your system’s configured time zone.</p>
|
||||
|
||||
<p>May also be set with the <code>UV_EXCLUDE_NEWER</code> environment variable.</p>
|
||||
</dd><dt><code>--extra-index-url</code> <i>extra-index-url</i></dt><dd><p>Extra URLs of package indexes to use, in addition to <code>--index-url</code>.</p>
|
||||
|
||||
<p>Accepts either a repository compliant with PEP 503 (the simple repository API), or a local directory laid out in the same format.</p>
|
||||
|
||||
<p>All indexes provided via this flag take priority over the index specified by <code>--index-url</code> (which defaults to PyPI). When multiple <code>--extra-index-url</code> flags are provided, earlier values take priority.</p>
|
||||
|
||||
<p>May also be set with the <code>UV_EXTRA_INDEX_URL</code> environment variable.</p>
|
||||
</dd><dt><code>--find-links</code>, <code>-f</code> <i>find-links</i></dt><dd><p>Locations to search for candidate distributions, in addition to those found in the registry indexes.</p>
|
||||
|
||||
<p>If a path, the target must be a directory that contains packages as wheel files (<code>.whl</code>) or source distributions (<code>.tar.gz</code> or <code>.zip</code>) at the top level.</p>
|
||||
|
||||
<p>If a URL, the page must contain a flat list of links to package files adhering to the formats described above.</p>
|
||||
|
||||
</dd><dt><code>--help</code>, <code>-h</code></dt><dd><p>Display the concise help for this command</p>
|
||||
|
||||
</dd><dt><code>--index-strategy</code> <i>index-strategy</i></dt><dd><p>The strategy to use when resolving against multiple index URLs.</p>
|
||||
|
||||
<p>By default, uv will stop at the first index on which a given package is available, and limit resolutions to those present on that first index (<code>first-match</code>). This prevents "dependency confusion" attacks, whereby an attack can upload a malicious package under the same name to a secondary.</p>
|
||||
|
||||
<p>May also be set with the <code>UV_INDEX_STRATEGY</code> environment variable.</p>
|
||||
<p>Possible values:</p>
|
||||
|
||||
<ul>
|
||||
<li><code>first-index</code>: Only use results from the first index that returns a match for a given package name</li>
|
||||
|
||||
<li><code>unsafe-first-match</code>: Search for every package name across all indexes, exhausting the versions from the first index before moving on to the next</li>
|
||||
|
||||
<li><code>unsafe-best-match</code>: Search for every package name across all indexes, preferring the "best" version found. If a package version is in multiple indexes, only look at the entry for the first index</li>
|
||||
</ul>
|
||||
</dd><dt><code>--index-url</code>, <code>-i</code> <i>index-url</i></dt><dd><p>The URL of the Python package index (by default: <https://pypi.org/simple>).</p>
|
||||
|
||||
<p>Accepts either a repository compliant with PEP 503 (the simple repository API), or a local directory laid out in the same format.</p>
|
||||
|
||||
<p>The index given by this flag is given lower priority than all other indexes specified via the <code>--extra-index-url</code> flag.</p>
|
||||
|
||||
<p>May also be set with the <code>UV_INDEX_URL</code> environment variable.</p>
|
||||
</dd><dt><code>--keyring-provider</code> <i>keyring-provider</i></dt><dd><p>Attempt to use <code>keyring</code> for authentication for index URLs.</p>
|
||||
|
||||
<p>At present, only <code>--keyring-provider subprocess</code> is supported, which configures uv to use the <code>keyring</code> CLI to handle authentication.</p>
|
||||
|
||||
<p>Defaults to <code>disabled</code>.</p>
|
||||
|
||||
<p>May also be set with the <code>UV_KEYRING_PROVIDER</code> environment variable.</p>
|
||||
<p>Possible values:</p>
|
||||
|
||||
<ul>
|
||||
<li><code>disabled</code>: Do not use keyring for credential lookup</li>
|
||||
|
||||
<li><code>subprocess</code>: Use the <code>keyring</code> command for credential lookup</li>
|
||||
</ul>
|
||||
</dd><dt><code>--link-mode</code> <i>link-mode</i></dt><dd><p>The method to use when installing packages from the global cache.</p>
|
||||
|
||||
<p>This option is only used when building source distributions.</p>
|
||||
|
||||
<p>Defaults to <code>clone</code> (also known as Copy-on-Write) on macOS, and <code>hardlink</code> on Linux and Windows.</p>
|
||||
|
||||
<p>May also be set with the <code>UV_LINK_MODE</code> environment variable.</p>
|
||||
<p>Possible values:</p>
|
||||
|
||||
<ul>
|
||||
<li><code>clone</code>: Clone (i.e., copy-on-write) packages from the wheel into the <code>site-packages</code> directory</li>
|
||||
|
||||
<li><code>copy</code>: Copy packages from the wheel into the <code>site-packages</code> directory</li>
|
||||
|
||||
<li><code>hardlink</code>: Hard link packages from the wheel into the <code>site-packages</code> directory</li>
|
||||
|
||||
<li><code>symlink</code>: Symbolically link packages from the wheel into the <code>site-packages</code> directory</li>
|
||||
</ul>
|
||||
</dd><dt><code>--native-tls</code></dt><dd><p>Whether to load TLS certificates from the platform’s native certificate store.</p>
|
||||
|
||||
<p>By default, uv loads certificates from the bundled <code>webpki-roots</code> crate. The <code>webpki-roots</code> are a reliable set of trust roots from Mozilla, and including them in uv improves portability and performance (especially on macOS).</p>
|
||||
|
||||
<p>However, in some cases, you may want to use the platform’s native certificate store, especially if you’re relying on a corporate trust root (e.g., for a mandatory proxy) that’s included in your system’s certificate store.</p>
|
||||
|
||||
<p>May also be set with the <code>UV_NATIVE_TLS</code> environment variable.</p>
|
||||
</dd><dt><code>--no-binary</code></dt><dd><p>Don’t install pre-built wheels.</p>
|
||||
|
||||
<p>The given packages will be built and installed from source. The resolver will still use pre-built wheels to extract package metadata, if available.</p>
|
||||
|
||||
</dd><dt><code>--no-binary-package</code> <i>no-binary-package</i></dt><dd><p>Don’t install pre-built wheels for a specific package</p>
|
||||
|
||||
</dd><dt><code>--no-build</code></dt><dd><p>Don’t build source distributions.</p>
|
||||
|
||||
<p>When enabled, resolving will not run arbitrary Python code. The cached wheels of already-built source distributions will be reused, but operations that require building distributions will exit with an error.</p>
|
||||
|
||||
</dd><dt><code>--no-build-isolation</code></dt><dd><p>Disable isolation when building source distributions.</p>
|
||||
|
||||
<p>Assumes that build dependencies specified by PEP 518 are already installed.</p>
|
||||
|
||||
<p>May also be set with the <code>UV_NO_BUILD_ISOLATION</code> environment variable.</p>
|
||||
</dd><dt><code>--no-build-isolation-package</code> <i>no-build-isolation-package</i></dt><dd><p>Disable isolation when building source distributions for a specific package.</p>
|
||||
|
||||
<p>Assumes that the packages’ build dependencies specified by PEP 518 are already installed.</p>
|
||||
|
||||
</dd><dt><code>--no-build-package</code> <i>no-build-package</i></dt><dd><p>Don’t build source distributions for a specific package</p>
|
||||
|
||||
</dd><dt><code>--no-cache</code>, <code>-n</code></dt><dd><p>Avoid reading from or writing to the cache, instead using a temporary directory for the duration of the operation</p>
|
||||
|
||||
<p>May also be set with the <code>UV_NO_CACHE</code> environment variable.</p>
|
||||
</dd><dt><code>--no-config</code></dt><dd><p>Avoid discovering configuration files (<code>pyproject.toml</code>, <code>uv.toml</code>).</p>
|
||||
|
||||
<p>Normally, configuration files are discovered in the current directory, parent directories, or user configuration directories.</p>
|
||||
|
||||
<p>May also be set with the <code>UV_NO_CONFIG</code> environment variable.</p>
|
||||
</dd><dt><code>--no-index</code></dt><dd><p>Ignore the registry index (e.g., PyPI), instead relying on direct URL dependencies and those provided via <code>--find-links</code></p>
|
||||
|
||||
</dd><dt><code>--no-progress</code></dt><dd><p>Hide all progress outputs.</p>
|
||||
|
||||
<p>For example, spinners or progress bars.</p>
|
||||
|
||||
</dd><dt><code>--no-python-downloads</code></dt><dd><p>Disable automatic downloads of Python.</p>
|
||||
|
||||
</dd><dt><code>--no-sources</code></dt><dd><p>Ignore the <code>tool.uv.sources</code> table when resolving dependencies. Used to lock against the standards-compliant, publishable package metadata, as opposed to using any local or Git sources</p>
|
||||
|
||||
</dd><dt><code>--offline</code></dt><dd><p>Disable network access.</p>
|
||||
|
||||
<p>When disabled, uv will only use locally cached data and locally available files.</p>
|
||||
|
||||
</dd><dt><code>--out-dir</code>, <code>-o</code> <i>out-dir</i></dt><dd><p>The output directory to which distributions should be written.</p>
|
||||
|
||||
<p>Defaults to the <code>dist</code> subdirectory within the source directory.</p>
|
||||
|
||||
</dd><dt><code>--prerelease</code> <i>prerelease</i></dt><dd><p>The strategy to use when considering pre-release versions.</p>
|
||||
|
||||
<p>By default, uv will accept pre-releases for packages that <em>only</em> publish pre-releases, along with first-party requirements that contain an explicit pre-release marker in the declared specifiers (<code>if-necessary-or-explicit</code>).</p>
|
||||
|
||||
<p>May also be set with the <code>UV_PRERELEASE</code> environment variable.</p>
|
||||
<p>Possible values:</p>
|
||||
|
||||
<ul>
|
||||
<li><code>disallow</code>: Disallow all pre-release versions</li>
|
||||
|
||||
<li><code>allow</code>: Allow all pre-release versions</li>
|
||||
|
||||
<li><code>if-necessary</code>: Allow pre-release versions if all versions of a package are pre-release</li>
|
||||
|
||||
<li><code>explicit</code>: Allow pre-release versions for first-party packages with explicit pre-release markers in their version requirements</li>
|
||||
|
||||
<li><code>if-necessary-or-explicit</code>: Allow pre-release versions if all versions of a package are pre-release, or if the package has an explicit pre-release marker in its version requirements</li>
|
||||
</ul>
|
||||
</dd><dt><code>--python</code>, <code>-p</code> <i>python</i></dt><dd><p>The Python interpreter to use for the build environment.</p>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p>See <a href="#uv-python">uv python</a> to view supported request formats.</p>
|
||||
|
||||
<p>May also be set with the <code>UV_PYTHON</code> environment variable.</p>
|
||||
</dd><dt><code>--python-preference</code> <i>python-preference</i></dt><dd><p>Whether to prefer uv-managed or system Python installations.</p>
|
||||
|
||||
<p>By default, uv prefers using Python versions it manages. However, it will use system Python installations if a uv-managed Python is not installed. This option allows prioritizing or ignoring system Python installations.</p>
|
||||
|
||||
<p>May also be set with the <code>UV_PYTHON_PREFERENCE</code> environment variable.</p>
|
||||
<p>Possible values:</p>
|
||||
|
||||
<ul>
|
||||
<li><code>only-managed</code>: Only use managed Python installations; never use system Python installations</li>
|
||||
|
||||
<li><code>managed</code>: Prefer managed Python installations over system Python installations</li>
|
||||
|
||||
<li><code>system</code>: Prefer system Python installations over managed Python installations</li>
|
||||
|
||||
<li><code>only-system</code>: Only use system Python installations; never use managed Python installations</li>
|
||||
</ul>
|
||||
</dd><dt><code>--quiet</code>, <code>-q</code></dt><dd><p>Do not print any output</p>
|
||||
|
||||
</dd><dt><code>--refresh</code></dt><dd><p>Refresh all cached data</p>
|
||||
|
||||
</dd><dt><code>--refresh-package</code> <i>refresh-package</i></dt><dd><p>Refresh cached data for a specific package</p>
|
||||
|
||||
</dd><dt><code>--resolution</code> <i>resolution</i></dt><dd><p>The strategy to use when selecting between the different compatible versions for a given package requirement.</p>
|
||||
|
||||
<p>By default, uv will use the latest compatible version of each package (<code>highest</code>).</p>
|
||||
|
||||
<p>May also be set with the <code>UV_RESOLUTION</code> environment variable.</p>
|
||||
<p>Possible values:</p>
|
||||
|
||||
<ul>
|
||||
<li><code>highest</code>: Resolve the highest compatible version of each package</li>
|
||||
|
||||
<li><code>lowest</code>: Resolve the lowest compatible version of each package</li>
|
||||
|
||||
<li><code>lowest-direct</code>: Resolve the lowest compatible version of any direct dependencies, and the highest compatible version of any transitive dependencies</li>
|
||||
</ul>
|
||||
</dd><dt><code>--sdist</code></dt><dd><p>Build a source distribution ("sdist") from the given directory</p>
|
||||
|
||||
</dd><dt><code>--upgrade</code>, <code>-U</code></dt><dd><p>Allow package upgrades, ignoring pinned versions in any existing output file. Implies <code>--refresh</code></p>
|
||||
|
||||
</dd><dt><code>--upgrade-package</code>, <code>-P</code> <i>upgrade-package</i></dt><dd><p>Allow upgrades for a specific package, ignoring pinned versions in any existing output file. Implies <code>--refresh-package</code></p>
|
||||
|
||||
</dd><dt><code>--verbose</code>, <code>-v</code></dt><dd><p>Use verbose output.</p>
|
||||
|
||||
<p>You can configure fine-grained logging using the <code>RUST_LOG</code> environment variable. (<https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives>)</p>
|
||||
|
||||
</dd><dt><code>--version</code>, <code>-V</code></dt><dd><p>Display the uv version</p>
|
||||
|
||||
</dd><dt><code>--wheel</code></dt><dd><p>Build a binary distribution ("wheel") from the given directory</p>
|
||||
|
||||
</dd></dl>
|
||||
|
||||
## uv cache
|
||||
|
||||
Manage uv's cache
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue