Build backend: Add --list option (#9610)

Add the `uv build --list`, a "subcommand" to list the files that would
be included when building a distribution. It does not build the
distribution, except when a source dist is required for source dist ->
wheel. This is an important debugging tool for the include and exclude
options: Did i actually include the files I wanted, or am i shipping a
broken distribution? Are there any temporary files I still need to
exclude?

Cargo offers this as `cargo package --list`.

`--list` is preview-exclusive, since it requires the fast path, which I
also put into preview.

Examples:


![image](https://github.com/user-attachments/assets/55e3f169-3051-4217-987d-0cb01ae5050e)


![image](https://github.com/user-attachments/assets/1da75245-358d-4bee-9199-f720089f0a70)


![image](https://github.com/user-attachments/assets/4d97a893-552e-43a1-9c22-78fc67f1e9f5)

I'll fix the error handling in a follow-up.

Tagging as enhancement because it changes the stable output slightly
(two lines instead of one).

CC @charliermarsh for uv-wide consistency in the stdout/stderr handling.
This commit is contained in:
konsti 2024-12-04 09:52:27 +01:00 committed by GitHub
parent d7b74f964e
commit 7d82cbff01
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 570 additions and 177 deletions

View file

@ -2175,6 +2175,18 @@ pub struct BuildArgs {
#[arg(long)]
pub wheel: bool,
/// When using the uv build backend, list the files that would be included when building.
///
/// Skips building the actual distribution, except when the source distribution is needed to
/// build the wheel. The file list is collected directly without a PEP 517 environment. It only
/// works with the uv build backend, there is no PEP 517 file list build hook.
///
/// This option can be combined with `--sdist` and `--wheel` for inspecting different build
/// paths.
// Hidden while in preview.
#[arg(long, hide = true)]
pub list: bool,
#[arg(long, overrides_with("no_build_logs"), hide = true)]
pub build_logs: bool,
@ -2187,7 +2199,7 @@ pub struct BuildArgs {
/// By default, uv won't create a PEP 517 build environment for packages using the uv build
/// backend, but use a fast path that calls into the build backend directly. This option forces
/// always using PEP 517.
#[arg(long)]
#[arg(long, conflicts_with = "list")]
pub force_pep517: bool,
/// Constrain build dependencies using the given requirements files when building

View file

@ -4,13 +4,9 @@ use std::io;
use std::io::Write as _;
use std::path::{Path, PathBuf};
use anyhow::Result;
use anyhow::{bail, Context, Result};
use owo_colors::OwoColorize;
use tracing::{debug, instrument, trace};
use uv_distribution_filename::SourceDistExtension;
use uv_distribution_types::{DependencyMetadata, Index, IndexLocations, SourceDist};
use uv_install_wheel::linker::LinkMode;
use uv_auth::store_credentials;
use uv_build_backend::PyProjectToml;
@ -18,10 +14,14 @@ use uv_cache::{Cache, CacheBucket};
use uv_client::{BaseClientBuilder, Connectivity, FlatIndexClient, RegistryClientBuilder};
use uv_configuration::{
BuildKind, BuildOptions, BuildOutput, Concurrency, ConfigSettings, Constraints,
HashCheckingMode, IndexStrategy, KeyringProviderType, LowerBound, SourceStrategy, TrustedHost,
HashCheckingMode, IndexStrategy, KeyringProviderType, LowerBound, PreviewMode, SourceStrategy,
TrustedHost,
};
use uv_dispatch::{BuildDispatch, SharedState};
use uv_fs::Simplified;
use uv_distribution_filename::SourceDistExtension;
use uv_distribution_types::{DependencyMetadata, Index, IndexLocations, SourceDist};
use uv_fs::{relative_to, Simplified};
use uv_install_wheel::linker::LinkMode;
use uv_normalize::PackageName;
use uv_python::{
EnvironmentPreference, PythonDownloads, PythonEnvironment, PythonInstallation,
@ -51,6 +51,7 @@ pub(crate) async fn build_frontend(
output_dir: Option<PathBuf>,
sdist: bool,
wheel: bool,
list: bool,
build_logs: bool,
force_pep517: bool,
build_constraints: Vec<RequirementsSource>,
@ -67,6 +68,7 @@ pub(crate) async fn build_frontend(
allow_insecure_host: &[TrustedHost],
cache: &Cache,
printer: Printer,
preview: PreviewMode,
) -> Result<ExitStatus> {
let build_result = build_impl(
project_dir,
@ -76,6 +78,7 @@ pub(crate) async fn build_frontend(
output_dir.as_deref(),
sdist,
wheel,
list,
build_logs,
force_pep517,
&build_constraints,
@ -92,6 +95,7 @@ pub(crate) async fn build_frontend(
allow_insecure_host,
cache,
printer,
preview,
)
.await?;
@ -119,6 +123,7 @@ async fn build_impl(
output_dir: Option<&Path>,
sdist: bool,
wheel: bool,
list: bool,
build_logs: bool,
force_pep517: bool,
build_constraints: &[RequirementsSource],
@ -135,7 +140,17 @@ async fn build_impl(
allow_insecure_host: &[TrustedHost],
cache: &Cache,
printer: Printer,
preview: PreviewMode,
) -> Result<BuildResult> {
if list && preview.is_disabled() {
// We need the fast path for list and that is preview only.
writeln!(
printer.stderr(),
"The `--list` option is only available in preview mode; add the `--preview` flag to use `--list`"
)?;
return Ok(BuildResult::Failure);
}
// Extract the resolver settings.
let ResolverSettingsRef {
index_locations,
@ -286,9 +301,11 @@ async fn build_impl(
build_options,
sdist,
wheel,
list,
dependency_metadata,
link_mode,
config_setting,
preview,
);
async {
let result = future.await;
@ -300,30 +317,11 @@ async fn build_impl(
let mut success = true;
for (source, result) in results {
match result {
Ok(assets) => match assets {
BuiltDistributions::Wheel(wheel) => {
writeln!(
printer.stderr(),
"Successfully built {}",
wheel.user_display().bold().cyan()
)?;
Ok(messages) => {
for message in messages {
message.print(printer)?;
}
BuiltDistributions::Sdist(sdist) => {
writeln!(
printer.stderr(),
"Successfully built {}",
sdist.user_display().bold().cyan()
)?;
}
BuiltDistributions::Both(sdist, wheel) => {
writeln!(
printer.stderr(),
"Successfully built {} and {}",
sdist.user_display().bold().cyan(),
wheel.user_display().bold().cyan()
)?;
}
},
}
Err(err) => {
#[derive(Debug, miette::Diagnostic, thiserror::Error)]
#[error("Failed to build `{source}`", source = source.cyan())]
@ -383,10 +381,12 @@ async fn build_package(
build_options: &BuildOptions,
sdist: bool,
wheel: bool,
list: bool,
dependency_metadata: &DependencyMetadata,
link_mode: LinkMode,
config_setting: &ConfigSettings,
) -> Result<BuiltDistributions> {
preview: PreviewMode,
) -> Result<Vec<BuildMessage>> {
let output_dir = if let Some(output_dir) = output_dir {
Cow::Owned(std::path::absolute(output_dir)?)
} else {
@ -538,7 +538,22 @@ async fn build_package(
// Check if the build backend is matching uv version that allows calling in the uv build backend
// directly.
let fast_path = !force_pep517 && check_fast_path(source.path());
let build_action = if list {
if force_pep517 {
bail!("Can't use `--force-pep517` with `--list`");
}
if !check_fast_path(source.path()) {
// TODO(konsti): Provide more context on what mismatched
bail!("Can only use `--list` with the uv backend");
}
BuildAction::List
} else if preview.is_enabled() && !force_pep517 && check_fast_path(source.path()) {
BuildAction::FastPath
} else {
BuildAction::Pep517
};
// Prepare some common arguments for the build.
let dist = None;
@ -556,15 +571,36 @@ async fn build_package(
Printer::Quiet => BuildOutput::Quiet,
};
let assets = match plan {
let mut build_results = Vec::new();
match plan {
BuildPlan::SdistToWheel => {
let sdist = build_sdist(
// Even when listing files, we still need to build the source distribution for the wheel
// build.
if list {
let sdist_list = build_sdist(
source.path(),
&output_dir,
build_action,
&source,
printer,
"source distribution",
&build_dispatch,
sources,
dist,
subdirectory,
version_id,
build_output,
)
.await?;
build_results.push(sdist_list);
}
let sdist_build = build_sdist(
source.path(),
&output_dir,
fast_path,
build_action.force_build(),
&source,
printer,
"Building source distribution",
"source distribution",
&build_dispatch,
sources,
dist,
@ -573,9 +609,10 @@ async fn build_package(
build_output,
)
.await?;
build_results.push(sdist_build.clone());
// Extract the source distribution into a temporary directory.
let path = output_dir.join(&sdist);
let path = output_dir.join(sdist_build.filename());
let reader = fs_err::tokio::File::open(&path).await?;
let ext = SourceDistExtension::from_path(path.as_path()).map_err(|err| {
anyhow::anyhow!("`{}` is not a valid source distribution, as it ends with an unsupported extension. Expected one of: {err}.", path.user_display())
@ -590,13 +627,13 @@ async fn build_package(
Err(err) => return Err(err.into()),
};
let wheel = build_wheel(
let wheel_build = build_wheel(
&extracted,
&output_dir,
fast_path,
build_action,
&source,
printer,
"Building wheel from source distribution",
"wheel from source distribution",
&build_dispatch,
sources,
dist,
@ -605,17 +642,16 @@ async fn build_package(
build_output,
)
.await?;
BuiltDistributions::Both(output_dir.join(sdist), output_dir.join(wheel))
build_results.push(wheel_build);
}
BuildPlan::Sdist => {
let sdist = build_sdist(
let sdist_build = build_sdist(
source.path(),
&output_dir,
fast_path,
build_action,
&source,
printer,
"Building source distribution",
"source distribution",
&build_dispatch,
sources,
dist,
@ -624,17 +660,16 @@ async fn build_package(
build_output,
)
.await?;
BuiltDistributions::Sdist(output_dir.join(sdist))
build_results.push(sdist_build);
}
BuildPlan::Wheel => {
let wheel = build_wheel(
let wheel_build = build_wheel(
source.path(),
&output_dir,
fast_path,
build_action,
&source,
printer,
"Building wheel",
"wheel",
&build_dispatch,
sources,
dist,
@ -643,17 +678,16 @@ async fn build_package(
build_output,
)
.await?;
BuiltDistributions::Wheel(output_dir.join(wheel))
build_results.push(wheel_build);
}
BuildPlan::SdistAndWheel => {
let sdist = build_sdist(
let sdist_build = build_sdist(
source.path(),
&output_dir,
fast_path,
build_action,
&source,
printer,
"Building source distribution",
"source distribution",
&build_dispatch,
sources,
dist,
@ -662,14 +696,15 @@ async fn build_package(
build_output,
)
.await?;
build_results.push(sdist_build);
let wheel = build_wheel(
let wheel_build = build_wheel(
source.path(),
&output_dir,
fast_path,
build_action,
&source,
printer,
"Building wheel",
"wheel",
&build_dispatch,
sources,
dist,
@ -678,8 +713,7 @@ async fn build_package(
build_output,
)
.await?;
BuiltDistributions::Both(output_dir.join(&sdist), output_dir.join(&wheel))
build_results.push(wheel_build);
}
BuildPlan::WheelFromSdist => {
// Extract the source distribution into a temporary directory.
@ -700,13 +734,13 @@ async fn build_package(
Err(err) => return Err(err.into()),
};
let wheel = build_wheel(
let wheel_build = build_wheel(
&extracted,
&output_dir,
fast_path,
build_action,
&source,
printer,
"Building wheel from source distribution",
"wheel from source distribution",
&build_dispatch,
sources,
dist,
@ -715,12 +749,33 @@ async fn build_package(
build_output,
)
.await?;
BuiltDistributions::Wheel(output_dir.join(wheel))
build_results.push(wheel_build);
}
};
}
Ok(assets)
Ok(build_results)
}
#[derive(Copy, Clone, PartialEq, Eq)]
enum BuildAction {
/// Only list the files that would be included, don't actually build.
List,
/// Build by calling directly into the build backend.
FastPath,
/// Build through the PEP 517 hooks.
Pep517,
}
impl BuildAction {
/// If in list mode, still build the distribution.
fn force_build(self) -> Self {
match self {
// List is only available for the uv build backend
Self::List => Self::FastPath,
Self::FastPath => Self::FastPath,
Self::Pep517 => Self::Pep517,
}
}
}
/// Build a source distribution, either through PEP 517 or through the fast path.
@ -728,10 +783,10 @@ async fn build_package(
async fn build_sdist(
source_tree: &Path,
output_dir: &Path,
fast_path: bool,
action: BuildAction,
source: &AnnotatedSource<'_>,
printer: Printer,
message: &str,
build_kind_message: &str,
// Below is only used with PEP 517 builds
build_dispatch: &BuildDispatch<'_>,
sources: SourceStrategy,
@ -739,46 +794,80 @@ async fn build_sdist(
subdirectory: Option<&Path>,
version_id: Option<&str>,
build_output: BuildOutput,
) -> Result<String> {
let sdist = if fast_path {
writeln!(
printer.stderr(),
"{}",
format!(
"{}{} (uv build backend)...",
source.message_prefix(),
message
)
.bold()
)?;
let source_tree = source_tree.to_path_buf();
let output_dir = output_dir.to_path_buf();
tokio::task::spawn_blocking(move || {
uv_build_backend::build_source_dist(&source_tree, &output_dir, uv_version::version())
})
.await??
.to_string()
} else {
writeln!(
printer.stderr(),
"{}",
format!("{}{}...", source.message_prefix(), message).bold()
)?;
let builder = build_dispatch
.setup_build(
source_tree,
subdirectory,
source.path(),
version_id.map(ToString::to_string),
dist,
sources,
BuildKind::Sdist,
build_output,
)
.await?;
builder.build(output_dir).await?
) -> Result<BuildMessage> {
let build_result = match action {
BuildAction::List => {
let source_tree_ = source_tree.to_path_buf();
let (filename, file_list) = tokio::task::spawn_blocking(move || {
uv_build_backend::list_source_dist(&source_tree_, uv_version::version())
})
.await??;
BuildMessage::List {
filename: filename.to_string(),
source_tree: source_tree.to_path_buf(),
file_list,
}
}
BuildAction::FastPath => {
writeln!(
printer.stderr(),
"{}",
format!(
"{}Building {} (uv build backend)...",
source.message_prefix(),
build_kind_message
)
.bold()
)?;
let source_tree = source_tree.to_path_buf();
let output_dir_ = output_dir.to_path_buf();
let filename = tokio::task::spawn_blocking(move || {
uv_build_backend::build_source_dist(
&source_tree,
&output_dir_,
uv_version::version(),
)
})
.await??
.to_string();
BuildMessage::Build {
filename,
output_dir: output_dir.to_path_buf(),
}
}
BuildAction::Pep517 => {
writeln!(
printer.stderr(),
"{}",
format!(
"{}Building {}...",
source.message_prefix(),
build_kind_message
)
.bold()
)?;
let builder = build_dispatch
.setup_build(
source_tree,
subdirectory,
source.path(),
version_id.map(ToString::to_string),
dist,
sources,
BuildKind::Sdist,
build_output,
)
.await?;
let filename = builder.build(output_dir).await?;
BuildMessage::Build {
filename,
output_dir: output_dir.to_path_buf(),
}
}
};
Ok(sdist)
Ok(build_result)
}
/// Build a wheel, either through PEP 517 or through the fast path.
@ -786,10 +875,10 @@ async fn build_sdist(
async fn build_wheel(
source_tree: &Path,
output_dir: &Path,
fast_path: bool,
action: BuildAction,
source: &AnnotatedSource<'_>,
printer: Printer,
message: &str,
build_kind_message: &str,
// Below is only used with PEP 517 builds
build_dispatch: &BuildDispatch<'_>,
sources: SourceStrategy,
@ -797,46 +886,80 @@ async fn build_wheel(
subdirectory: Option<&Path>,
version_id: Option<&str>,
build_output: BuildOutput,
) -> Result<String> {
let wheel = if fast_path {
writeln!(
printer.stderr(),
"{}",
format!(
"{}{} (uv build backend)...",
source.message_prefix(),
message
)
.bold()
)?;
let source_tree = source_tree.to_path_buf();
let output_dir = output_dir.to_path_buf();
tokio::task::spawn_blocking(move || {
uv_build_backend::build_wheel(&source_tree, &output_dir, None, uv_version::version())
})
.await??
.to_string()
} else {
writeln!(
printer.stderr(),
"{}",
format!("{}{}...", source.message_prefix(), message).bold()
)?;
let builder = build_dispatch
.setup_build(
source_tree,
subdirectory,
source.path(),
version_id.map(ToString::to_string),
dist,
sources,
BuildKind::Wheel,
build_output,
)
.await?;
builder.build(output_dir).await?
) -> Result<BuildMessage> {
let build_message = match action {
BuildAction::List => {
let source_tree_ = source_tree.to_path_buf();
let (name, file_list) = tokio::task::spawn_blocking(move || {
uv_build_backend::list_wheel(&source_tree_, uv_version::version())
})
.await??;
BuildMessage::List {
filename: name.to_string(),
source_tree: source_tree.to_path_buf(),
file_list,
}
}
BuildAction::FastPath => {
writeln!(
printer.stderr(),
"{}",
format!(
"{}Building {} (uv build backend)...",
source.message_prefix(),
build_kind_message
)
.bold()
)?;
let source_tree = source_tree.to_path_buf();
let output_dir_ = output_dir.to_path_buf();
let filename = tokio::task::spawn_blocking(move || {
uv_build_backend::build_wheel(
&source_tree,
&output_dir_,
None,
uv_version::version(),
)
})
.await??
.to_string();
BuildMessage::Build {
filename,
output_dir: output_dir.to_path_buf(),
}
}
BuildAction::Pep517 => {
writeln!(
printer.stderr(),
"{}",
format!(
"{}Building {}...",
source.message_prefix(),
build_kind_message
)
.bold()
)?;
let builder = build_dispatch
.setup_build(
source_tree,
subdirectory,
source.path(),
version_id.map(ToString::to_string),
dist,
sources,
BuildKind::Wheel,
build_output,
)
.await?;
let filename = builder.build(output_dir).await?;
BuildMessage::Build {
filename,
output_dir: output_dir.to_path_buf(),
}
}
};
Ok(wheel)
Ok(build_message)
}
/// Create the output directory and add a `.gitignore`.
@ -926,14 +1049,76 @@ impl<'a> Source<'a> {
}
}
/// We run all builds in parallel, so we wait until all builds are done to show the success messages
/// in order.
#[derive(Debug, Clone, PartialEq, Eq)]
enum BuiltDistributions {
/// A built wheel.
Wheel(PathBuf),
/// A built source distribution.
Sdist(PathBuf),
/// A built source distribution and wheel.
Both(PathBuf, PathBuf),
enum BuildMessage {
/// A built wheel or source distribution.
Build {
/// The name of the built distribution.
filename: String,
/// The location of the built distribution.
output_dir: PathBuf,
},
/// Show the list of files that would be included in a distribution.
List {
/// The name of the build distribution.
filename: String,
// All source files are relative to the source tree.
source_tree: PathBuf,
// Included file and source file, if not generated.
file_list: Vec<(String, Option<PathBuf>)>,
},
}
impl BuildMessage {
/// The filename of the wheel or source distribution.
fn filename(&self) -> &str {
match self {
BuildMessage::Build { filename: name, .. } => name,
BuildMessage::List { filename: name, .. } => name,
}
}
fn print(&self, printer: Printer) -> Result<()> {
match self {
BuildMessage::Build {
filename,
output_dir,
} => {
writeln!(
printer.stderr(),
"Successfully built {}",
output_dir.join(filename).user_display().bold().cyan()
)?;
}
BuildMessage::List {
filename,
file_list,
source_tree,
} => {
writeln!(
printer.stdout(),
"{}",
format!("Building {filename} will include the following files:").bold()
)?;
for (file, source) in file_list {
if let Some(source) = source {
writeln!(
printer.stdout(),
"{file} ({})",
relative_to(source, source_tree)
.context("Included files must be relative to source tree")?
.display()
)?;
} else {
writeln!(printer.stdout(), "{file} (generated)")?;
}
}
}
}
Ok(())
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]

View file

@ -138,7 +138,7 @@ pub(crate) async fn install(
let start = std::time::Instant::now();
if default && !preview.is_enabled() {
writeln!(printer.stderr(), "The `--default` flag is only available in preview mode; add the `--preview` flag to use `--default")?;
writeln!(printer.stderr(), "The `--default` flag is only available in preview mode; add the `--preview` flag to use `--default`")?;
return Ok(ExitStatus::Failure);
}

View file

@ -733,6 +733,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
args.out_dir,
args.sdist,
args.wheel,
args.list,
args.build_logs,
args.force_pep517,
build_constraints,
@ -749,6 +750,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
&globals.allow_insecure_host,
&cache,
printer,
globals.preview,
)
.await
}

View file

@ -2030,6 +2030,7 @@ pub(crate) struct BuildSettings {
pub(crate) out_dir: Option<PathBuf>,
pub(crate) sdist: bool,
pub(crate) wheel: bool,
pub(crate) list: bool,
pub(crate) build_logs: bool,
pub(crate) force_pep517: bool,
pub(crate) build_constraints: Vec<PathBuf>,
@ -2050,6 +2051,7 @@ impl BuildSettings {
all_packages,
sdist,
wheel,
list,
force_pep517,
build_constraints,
require_hashes,
@ -2076,6 +2078,7 @@ impl BuildSettings {
out_dir,
sdist,
wheel,
list,
build_logs: flag(build_logs, no_build_logs).unwrap_or(true),
build_constraints: build_constraints
.into_iter()

View file

@ -121,7 +121,8 @@ fn build() -> Result<()> {
adding 'project-0.1.0.dist-info/top_level.txt'
adding 'project-0.1.0.dist-info/RECORD'
removing build/bdist.linux-x86_64/wheel
Successfully built project/dist/project-0.1.0.tar.gz and project/dist/project-0.1.0-py3-none-any.whl
Successfully built project/dist/project-0.1.0.tar.gz
Successfully built project/dist/project-0.1.0-py3-none-any.whl
"###);
project
@ -213,7 +214,8 @@ fn build() -> Result<()> {
adding 'project-0.1.0.dist-info/top_level.txt'
adding 'project-0.1.0.dist-info/RECORD'
removing build/bdist.linux-x86_64/wheel
Successfully built dist/project-0.1.0.tar.gz and dist/project-0.1.0-py3-none-any.whl
Successfully built dist/project-0.1.0.tar.gz
Successfully built dist/project-0.1.0-py3-none-any.whl
"###);
project
@ -317,7 +319,8 @@ fn build() -> Result<()> {
adding 'project-0.1.0.dist-info/top_level.txt'
adding 'project-0.1.0.dist-info/RECORD'
removing build/bdist.linux-x86_64/wheel
Successfully built out/project-0.1.0.tar.gz and out/project-0.1.0-py3-none-any.whl
Successfully built out/project-0.1.0.tar.gz
Successfully built out/project-0.1.0-py3-none-any.whl
"###);
project
@ -630,7 +633,8 @@ fn sdist_wheel() -> Result<()> {
adding 'project-0.1.0.dist-info/top_level.txt'
adding 'project-0.1.0.dist-info/RECORD'
removing build/bdist.linux-x86_64/wheel
Successfully built dist/project-0.1.0.tar.gz and dist/project-0.1.0-py3-none-any.whl
Successfully built dist/project-0.1.0.tar.gz
Successfully built dist/project-0.1.0-py3-none-any.whl
"###);
project
@ -1052,7 +1056,8 @@ fn workspace() -> Result<()> {
adding 'member-0.1.0.dist-info/top_level.txt'
adding 'member-0.1.0.dist-info/RECORD'
removing build/bdist.linux-x86_64/wheel
Successfully built dist/member-0.1.0.tar.gz and dist/member-0.1.0-py3-none-any.whl
Successfully built dist/member-0.1.0.tar.gz
Successfully built dist/member-0.1.0-py3-none-any.whl
"###);
project
@ -1075,8 +1080,10 @@ fn workspace() -> Result<()> {
[PKG] Building source distribution...
[PKG] Building wheel from source distribution...
[PKG] Building wheel from source distribution...
Successfully built dist/member-0.1.0.tar.gz and dist/member-0.1.0-py3-none-any.whl
Successfully built dist/project-0.1.0.tar.gz and dist/project-0.1.0-py3-none-any.whl
Successfully built dist/member-0.1.0.tar.gz
Successfully built dist/member-0.1.0-py3-none-any.whl
Successfully built dist/project-0.1.0.tar.gz
Successfully built dist/project-0.1.0-py3-none-any.whl
"###);
project
@ -1174,7 +1181,8 @@ fn workspace() -> Result<()> {
adding 'member-0.1.0.dist-info/top_level.txt'
adding 'member-0.1.0.dist-info/RECORD'
removing build/bdist.linux-x86_64/wheel
Successfully built project/dist/member-0.1.0.tar.gz and project/dist/member-0.1.0-py3-none-any.whl
Successfully built project/dist/member-0.1.0.tar.gz
Successfully built project/dist/member-0.1.0-py3-none-any.whl
"###);
// If a source is provided, discover the workspace from the source.
@ -1188,8 +1196,10 @@ fn workspace() -> Result<()> {
[PKG] Building source distribution...
[PKG] Building wheel from source distribution...
[PKG] Building wheel from source distribution...
Successfully built project/dist/member-0.1.0.tar.gz and project/dist/member-0.1.0-py3-none-any.whl
Successfully built project/dist/project-0.1.0.tar.gz and project/dist/project-0.1.0-py3-none-any.whl
Successfully built project/dist/member-0.1.0.tar.gz
Successfully built project/dist/member-0.1.0-py3-none-any.whl
Successfully built project/dist/project-0.1.0.tar.gz
Successfully built project/dist/project-0.1.0-py3-none-any.whl
"###);
// Fail when `--package` is provided without a workspace.
@ -1331,10 +1341,12 @@ fn build_all_with_failure() -> Result<()> {
[PKG] Building source distribution...
[PKG] Building wheel from source distribution...
[PKG] Building wheel from source distribution...
Successfully built dist/member_a-0.1.0.tar.gz and dist/member_a-0.1.0-py3-none-any.whl
Successfully built dist/member_a-0.1.0.tar.gz
Successfully built dist/member_a-0.1.0-py3-none-any.whl
× Failed to build `member-b @ [TEMP_DIR]/project/packages/member_b`
Build backend failed to determine requirements with `build_sdist()` (exit status: 1)
Successfully built dist/project-0.1.0.tar.gz and dist/project-0.1.0-py3-none-any.whl
Successfully built dist/project-0.1.0.tar.gz
Successfully built dist/project-0.1.0-py3-none-any.whl
"###);
// project and member_a should be built, regardless of member_b build failure
@ -1628,7 +1640,8 @@ fn sha() -> Result<()> {
adding 'project-0.1.0.dist-info/top_level.txt'
adding 'project-0.1.0.dist-info/RECORD'
removing build/bdist.linux-x86_64/wheel
Successfully built dist/project-0.1.0.tar.gz and dist/project-0.1.0-py3-none-any.whl
Successfully built dist/project-0.1.0.tar.gz
Successfully built dist/project-0.1.0-py3-none-any.whl
"###);
project
@ -1710,7 +1723,8 @@ fn build_no_build_logs() -> Result<()> {
----- stderr -----
Building source distribution...
Building wheel from source distribution...
Successfully built project/dist/project-0.1.0.tar.gz and project/dist/project-0.1.0-py3-none-any.whl
Successfully built project/dist/project-0.1.0.tar.gz
Successfully built project/dist/project-0.1.0-py3-none-any.whl
"###);
Ok(())
@ -1867,7 +1881,8 @@ fn tool_uv_sources() -> Result<()> {
adding 'project-0.1.0.dist-info/top_level.txt'
adding 'project-0.1.0.dist-info/RECORD'
removing build/bdist.linux-x86_64/wheel
Successfully built dist/project-0.1.0.tar.gz and dist/project-0.1.0-py3-none-any.whl
Successfully built dist/project-0.1.0.tar.gz
Successfully built dist/project-0.1.0-py3-none-any.whl
"###);
project
@ -1915,7 +1930,8 @@ fn git_boundary_in_dist_build() -> Result<()> {
----- stderr -----
Building source distribution...
Building wheel from source distribution...
Successfully built dist/demo-0.1.0.tar.gz and dist/demo-0.1.0-py3-none-any.whl
Successfully built dist/demo-0.1.0.tar.gz
Successfully built dist/demo-0.1.0-py3-none-any.whl
"###);
// Check that the source file is included
@ -2048,6 +2064,7 @@ fn build_fast_path() -> Result<()> {
let built_by_uv = current_dir()?.join("../../scripts/packages/built-by-uv");
uv_snapshot!(context.build()
.arg("--preview")
.arg(&built_by_uv)
.arg("--out-dir")
.arg(context.temp_dir.join("output1")), @r###"
@ -2058,7 +2075,8 @@ fn build_fast_path() -> Result<()> {
----- stderr -----
Building source distribution (uv build backend)...
Building wheel from source distribution (uv build backend)...
Successfully built output1/built_by_uv-0.1.0.tar.gz and output1/built_by_uv-0.1.0-py3-none-any.whl
Successfully built output1/built_by_uv-0.1.0.tar.gz
Successfully built output1/built_by_uv-0.1.0-py3-none-any.whl
"###);
context
.temp_dir
@ -2072,6 +2090,7 @@ fn build_fast_path() -> Result<()> {
.assert(predicate::path::is_file());
uv_snapshot!(context.build()
.arg("--preview")
.arg(&built_by_uv)
.arg("--out-dir")
.arg(context.temp_dir.join("output2"))
@ -2091,6 +2110,7 @@ fn build_fast_path() -> Result<()> {
.assert(predicate::path::is_file());
uv_snapshot!(context.build()
.arg("--preview")
.arg(&built_by_uv)
.arg("--out-dir")
.arg(context.temp_dir.join("output3"))
@ -2110,6 +2130,7 @@ fn build_fast_path() -> Result<()> {
.assert(predicate::path::is_file());
uv_snapshot!(context.build()
.arg("--preview")
.arg(&built_by_uv)
.arg("--out-dir")
.arg(context.temp_dir.join("output4"))
@ -2122,7 +2143,8 @@ fn build_fast_path() -> Result<()> {
----- stderr -----
Building source distribution (uv build backend)...
Building wheel (uv build backend)...
Successfully built output4/built_by_uv-0.1.0.tar.gz and output4/built_by_uv-0.1.0-py3-none-any.whl
Successfully built output4/built_by_uv-0.1.0.tar.gz
Successfully built output4/built_by_uv-0.1.0-py3-none-any.whl
"###);
context
.temp_dir
@ -2137,3 +2159,172 @@ fn build_fast_path() -> Result<()> {
Ok(())
}
/// Test the `--list` option.
#[test]
fn list_files() -> Result<()> {
let context = TestContext::new("3.12");
let built_by_uv = current_dir()?.join("../../scripts/packages/built-by-uv");
// By default, we build the wheel from the source dist, which we need to do even for the list
// task.
uv_snapshot!(context.build()
.arg("--preview")
.arg(&built_by_uv)
.arg("--out-dir")
.arg(context.temp_dir.join("output1"))
.arg("--list"), @r###"
success: true
exit_code: 0
----- stdout -----
Building built_by_uv-0.1.0.tar.gz will include the following files:
built_by_uv-0.1.0/LICENSE-APACHE (LICENSE-APACHE)
built_by_uv-0.1.0/LICENSE-MIT (LICENSE-MIT)
built_by_uv-0.1.0/PKG-INFO (generated)
built_by_uv-0.1.0/README.md (README.md)
built_by_uv-0.1.0/assets/data.csv (assets/data.csv)
built_by_uv-0.1.0/header/built_by_uv.h (header/built_by_uv.h)
built_by_uv-0.1.0/pyproject.toml (pyproject.toml)
built_by_uv-0.1.0/scripts/whoami.sh (scripts/whoami.sh)
built_by_uv-0.1.0/src/built_by_uv/__init__.py (src/built_by_uv/__init__.py)
built_by_uv-0.1.0/src/built_by_uv/arithmetic/__init__.py (src/built_by_uv/arithmetic/__init__.py)
built_by_uv-0.1.0/src/built_by_uv/arithmetic/circle.py (src/built_by_uv/arithmetic/circle.py)
built_by_uv-0.1.0/src/built_by_uv/arithmetic/pi.txt (src/built_by_uv/arithmetic/pi.txt)
built_by_uv-0.1.0/src/built_by_uv/build-only.h (src/built_by_uv/build-only.h)
built_by_uv-0.1.0/third-party-licenses/PEP-401.txt (third-party-licenses/PEP-401.txt)
Building built_by_uv-0.1.0-py3-none-any.whl will include the following files:
built_by_uv-0.1.0.data/data/data.csv (assets/data.csv)
built_by_uv-0.1.0.data/headers/built_by_uv.h (header/built_by_uv.h)
built_by_uv-0.1.0.data/scripts/whoami.sh (scripts/whoami.sh)
built_by_uv-0.1.0.dist-info/METADATA (generated)
built_by_uv-0.1.0.dist-info/WHEEL (generated)
built_by_uv-0.1.0.dist-info/licenses/LICENSE-APACHE (LICENSE-APACHE)
built_by_uv-0.1.0.dist-info/licenses/LICENSE-MIT (LICENSE-MIT)
built_by_uv-0.1.0.dist-info/licenses/third-party-licenses/PEP-401.txt (third-party-licenses/PEP-401.txt)
built_by_uv/__init__.py (src/built_by_uv/__init__.py)
built_by_uv/arithmetic/__init__.py (src/built_by_uv/arithmetic/__init__.py)
built_by_uv/arithmetic/circle.py (src/built_by_uv/arithmetic/circle.py)
built_by_uv/arithmetic/pi.txt (src/built_by_uv/arithmetic/pi.txt)
----- stderr -----
Building source distribution (uv build backend)...
Successfully built output1/built_by_uv-0.1.0.tar.gz
"###);
context
.temp_dir
.child("output1")
.child("built_by_uv-0.1.0.tar.gz")
.assert(predicate::path::is_file());
context
.temp_dir
.child("output1")
.child("built_by_uv-0.1.0-py3-none-any.whl")
.assert(predicate::path::missing());
uv_snapshot!(context.build()
.arg("--preview")
.arg(&built_by_uv)
.arg("--out-dir")
.arg(context.temp_dir.join("output2"))
.arg("--list")
.arg("--sdist")
.arg("--wheel"), @r###"
success: true
exit_code: 0
----- stdout -----
Building built_by_uv-0.1.0.tar.gz will include the following files:
built_by_uv-0.1.0/LICENSE-APACHE (LICENSE-APACHE)
built_by_uv-0.1.0/LICENSE-MIT (LICENSE-MIT)
built_by_uv-0.1.0/PKG-INFO (generated)
built_by_uv-0.1.0/README.md (README.md)
built_by_uv-0.1.0/assets/data.csv (assets/data.csv)
built_by_uv-0.1.0/header/built_by_uv.h (header/built_by_uv.h)
built_by_uv-0.1.0/pyproject.toml (pyproject.toml)
built_by_uv-0.1.0/scripts/whoami.sh (scripts/whoami.sh)
built_by_uv-0.1.0/src/built_by_uv/__init__.py (src/built_by_uv/__init__.py)
built_by_uv-0.1.0/src/built_by_uv/arithmetic/__init__.py (src/built_by_uv/arithmetic/__init__.py)
built_by_uv-0.1.0/src/built_by_uv/arithmetic/circle.py (src/built_by_uv/arithmetic/circle.py)
built_by_uv-0.1.0/src/built_by_uv/arithmetic/pi.txt (src/built_by_uv/arithmetic/pi.txt)
built_by_uv-0.1.0/src/built_by_uv/build-only.h (src/built_by_uv/build-only.h)
built_by_uv-0.1.0/third-party-licenses/PEP-401.txt (third-party-licenses/PEP-401.txt)
Building built_by_uv-0.1.0-py3-none-any.whl will include the following files:
built_by_uv-0.1.0.data/data/data.csv (assets/data.csv)
built_by_uv-0.1.0.data/headers/built_by_uv.h (header/built_by_uv.h)
built_by_uv-0.1.0.data/scripts/whoami.sh (scripts/whoami.sh)
built_by_uv-0.1.0.dist-info/METADATA (generated)
built_by_uv-0.1.0.dist-info/WHEEL (generated)
built_by_uv-0.1.0.dist-info/licenses/LICENSE-APACHE (LICENSE-APACHE)
built_by_uv-0.1.0.dist-info/licenses/LICENSE-MIT (LICENSE-MIT)
built_by_uv-0.1.0.dist-info/licenses/third-party-licenses/PEP-401.txt (third-party-licenses/PEP-401.txt)
built_by_uv/__init__.py (src/built_by_uv/__init__.py)
built_by_uv/arithmetic/__init__.py (src/built_by_uv/arithmetic/__init__.py)
built_by_uv/arithmetic/circle.py (src/built_by_uv/arithmetic/circle.py)
built_by_uv/arithmetic/pi.txt (src/built_by_uv/arithmetic/pi.txt)
----- stderr -----
"###);
context
.temp_dir
.child("output2")
.child("built_by_uv-0.1.0.tar.gz")
.assert(predicate::path::missing());
context
.temp_dir
.child("output2")
.child("built_by_uv-0.1.0-py3-none-any.whl")
.assert(predicate::path::missing());
Ok(())
}
/// Test `--list` option errors.
#[test]
fn list_files_errors() -> Result<()> {
let context = TestContext::new("3.12");
let built_by_uv = current_dir()?.join("../../scripts/packages/built-by-uv");
let mut filters = context.filters();
// In CI, we run with link mode settings.
filters.push(("--link-mode <LINK_MODE> ", ""));
uv_snapshot!(filters, context.build()
.arg("--preview")
.arg(&built_by_uv)
.arg("--out-dir")
.arg(context.temp_dir.join("output1"))
.arg("--list")
.arg("--force-pep517"), @r###"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
error: the argument '--list' cannot be used with '--force-pep517'
Usage: uv build --cache-dir [CACHE_DIR] --out-dir <OUT_DIR> --exclude-newer <EXCLUDE_NEWER> <SRC>
For more information, try '--help'.
"###);
// Not a uv build backend package, we can't list it.
let anyio_local = current_dir()?.join("../../scripts/packages/anyio_local");
let mut filters = context.filters();
// Windows normalization
filters.push(("/crates/uv/../../", "/"));
uv_snapshot!(filters, context.build()
.arg("--preview")
.arg(&anyio_local)
.arg("--out-dir")
.arg(context.temp_dir.join("output2"))
.arg("--list"), @r###"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
× Failed to build `[WORKSPACE]/scripts/packages/anyio_local`
Can only use `--list` with the uv backend
"###);
Ok(())
}

View file

@ -543,7 +543,7 @@ fn python_install_default() {
----- stdout -----
----- stderr -----
The `--default` flag is only available in preview mode; add the `--preview` flag to use `--default
The `--default` flag is only available in preview mode; add the `--preview` flag to use `--default`
"###);
// Install a specific version