From 58d621be60cba7daf04f0df057c493c065b4c4f1 Mon Sep 17 00:00:00 2001 From: konstin Date: Tue, 3 Dec 2024 16:02:15 +0100 Subject: [PATCH 1/4] Build backend: Add `--list` option --- crates/uv-cli/src/lib.rs | 13 +- crates/uv/src/commands/build_frontend.rs | 497 ++++++++++++++++------- crates/uv/src/commands/python/install.rs | 2 +- crates/uv/src/lib.rs | 2 + crates/uv/src/settings.rs | 3 + crates/uv/tests/it/build.rs | 221 +++++++++- crates/uv/tests/it/python_install.rs | 2 +- 7 files changed, 563 insertions(+), 177 deletions(-) diff --git a/crates/uv-cli/src/lib.rs b/crates/uv-cli/src/lib.rs index fcb382f09136..2db37235f0f8 100644 --- a/crates/uv-cli/src/lib.rs +++ b/crates/uv-cli/src/lib.rs @@ -2171,6 +2171,17 @@ 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. + /// + /// 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, @@ -2183,7 +2194,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 diff --git a/crates/uv/src/commands/build_frontend.rs b/crates/uv/src/commands/build_frontend.rs index c861f52704f1..18f7f6ed0cdc 100644 --- a/crates/uv/src/commands/build_frontend.rs +++ b/crates/uv/src/commands/build_frontend.rs @@ -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, sdist: bool, wheel: bool, + list: bool, build_logs: bool, force_pep517: bool, build_constraints: Vec, @@ -67,6 +68,7 @@ pub(crate) async fn build_frontend( allow_insecure_host: &[TrustedHost], cache: &Cache, printer: Printer, + preview: PreviewMode, ) -> Result { 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 { + 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() - )?; - } - 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() - )?; + Ok(messages) => { + for message in messages { + message.print(printer)?; } - }, + } 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 { + preview: PreviewMode, +) -> Result> { 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(build_results) +} - Ok(assets) +#[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 { - 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 { + 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 { - 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 { + 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 Source<'_> { } } +/// 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)>, + }, +} + +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)] diff --git a/crates/uv/src/commands/python/install.rs b/crates/uv/src/commands/python/install.rs index 7bd67d48ab73..7e0e947a38ec 100644 --- a/crates/uv/src/commands/python/install.rs +++ b/crates/uv/src/commands/python/install.rs @@ -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); } diff --git a/crates/uv/src/lib.rs b/crates/uv/src/lib.rs index c242bfa1653a..3032d048e4f1 100644 --- a/crates/uv/src/lib.rs +++ b/crates/uv/src/lib.rs @@ -731,6 +731,7 @@ async fn run(mut cli: Cli) -> Result { args.out_dir, args.sdist, args.wheel, + args.list, args.build_logs, args.force_pep517, build_constraints, @@ -747,6 +748,7 @@ async fn run(mut cli: Cli) -> Result { &globals.allow_insecure_host, &cache, printer, + globals.preview, ) .await } diff --git a/crates/uv/src/settings.rs b/crates/uv/src/settings.rs index c66a30f90ae0..4423fa733e5b 100644 --- a/crates/uv/src/settings.rs +++ b/crates/uv/src/settings.rs @@ -2028,6 +2028,7 @@ pub(crate) struct BuildSettings { pub(crate) out_dir: Option, 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, @@ -2048,6 +2049,7 @@ impl BuildSettings { all_packages, sdist, wheel, + list, force_pep517, build_constraints, require_hashes, @@ -2074,6 +2076,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() diff --git a/crates/uv/tests/it/build.rs b/crates/uv/tests/it/build.rs index aa9e604ab873..01300c6a3229 100644 --- a/crates/uv/tests/it/build.rs +++ b/crates/uv/tests/it/build.rs @@ -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,166 @@ 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"); + + uv_snapshot!(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 --exclude-newer + + 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"); + uv_snapshot!(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 `/home/konsti/projects/uv/crates/uv/../../scripts/packages/anyio_local` + ╰─▶ Can only use `--list` with the uv backend + "###); + Ok(()) +} diff --git a/crates/uv/tests/it/python_install.rs b/crates/uv/tests/it/python_install.rs index 2a4d3886eacf..621304fb3eb2 100644 --- a/crates/uv/tests/it/python_install.rs +++ b/crates/uv/tests/it/python_install.rs @@ -507,7 +507,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 From 018b47fecdd1917fa835bf47fe6b85bb5023fe1e Mon Sep 17 00:00:00 2001 From: konstin Date: Tue, 3 Dec 2024 17:59:26 +0100 Subject: [PATCH 2/4] Add missing filters --- crates/uv/tests/it/build.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/uv/tests/it/build.rs b/crates/uv/tests/it/build.rs index 01300c6a3229..f6cfbe9d77fd 100644 --- a/crates/uv/tests/it/build.rs +++ b/crates/uv/tests/it/build.rs @@ -2306,7 +2306,7 @@ fn list_files_errors() -> Result<()> { // Not a uv build backend package, we can't list it. let anyio_local = current_dir()?.join("../../scripts/packages/anyio_local"); - uv_snapshot!(context.build() + uv_snapshot!(context.filters(), context.build() .arg("--preview") .arg(&anyio_local) .arg("--out-dir") @@ -2317,7 +2317,7 @@ fn list_files_errors() -> Result<()> { ----- stdout ----- ----- stderr ----- - × Failed to build `/home/konsti/projects/uv/crates/uv/../../scripts/packages/anyio_local` + × Failed to build `[WORKSPACE]/crates/uv/../../scripts/packages/anyio_local` ╰─▶ Can only use `--list` with the uv backend "###); Ok(()) From 8b43ee4b7adc05ad86a52287cd57f82dcb1af9da Mon Sep 17 00:00:00 2001 From: konstin Date: Tue, 3 Dec 2024 20:02:20 +0100 Subject: [PATCH 3/4] Talk about PEP 517 in list --- crates/uv-cli/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/uv-cli/src/lib.rs b/crates/uv-cli/src/lib.rs index 2db37235f0f8..4d29679269d3 100644 --- a/crates/uv-cli/src/lib.rs +++ b/crates/uv-cli/src/lib.rs @@ -2174,7 +2174,8 @@ pub struct BuildArgs { /// 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. + /// 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. From 2e55b8a4eaf4926b6062315500ad2c7eec9080a1 Mon Sep 17 00:00:00 2001 From: konstin Date: Tue, 3 Dec 2024 20:24:14 +0100 Subject: [PATCH 4/4] Fix windows CI --- crates/uv/tests/it/build.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/crates/uv/tests/it/build.rs b/crates/uv/tests/it/build.rs index f6cfbe9d77fd..0c053a3295f1 100644 --- a/crates/uv/tests/it/build.rs +++ b/crates/uv/tests/it/build.rs @@ -2285,7 +2285,10 @@ fn list_files_errors() -> Result<()> { let built_by_uv = current_dir()?.join("../../scripts/packages/built-by-uv"); - uv_snapshot!(context.build() + let mut filters = context.filters(); + // In CI, we run with link mode settings. + filters.push(("--link-mode ", "")); + uv_snapshot!(filters, context.build() .arg("--preview") .arg(&built_by_uv) .arg("--out-dir") @@ -2306,7 +2309,10 @@ fn list_files_errors() -> Result<()> { // Not a uv build backend package, we can't list it. let anyio_local = current_dir()?.join("../../scripts/packages/anyio_local"); - uv_snapshot!(context.filters(), context.build() + let mut filters = context.filters(); + // Windows normalization + filters.push(("/crates/uv/../../", "/")); + uv_snapshot!(filters, context.build() .arg("--preview") .arg(&anyio_local) .arg("--out-dir") @@ -2317,7 +2323,7 @@ fn list_files_errors() -> Result<()> { ----- stdout ----- ----- stderr ----- - × Failed to build `[WORKSPACE]/crates/uv/../../scripts/packages/anyio_local` + × Failed to build `[WORKSPACE]/scripts/packages/anyio_local` ╰─▶ Can only use `--list` with the uv backend "###); Ok(())