diff --git a/crates/uv-cli/src/lib.rs b/crates/uv-cli/src/lib.rs index 3e333d76a581..e46e28435c83 100644 --- a/crates/uv-cli/src/lib.rs +++ b/crates/uv-cli/src/lib.rs @@ -2242,6 +2242,10 @@ pub struct BuildArgs { #[arg(long, overrides_with("build_logs"))] pub no_build_logs: bool, + /// Don't delete the sdist directory upon a build error. + #[arg(long)] + pub keep_on_error: bool, + /// Always build through PEP 517, don't use the fast path for the uv build backend. /// /// By default, uv won't create a PEP 517 build environment for packages using the uv build diff --git a/crates/uv/src/commands/build_frontend.rs b/crates/uv/src/commands/build_frontend.rs index 69be6fda90d0..0214a386f93b 100644 --- a/crates/uv/src/commands/build_frontend.rs +++ b/crates/uv/src/commands/build_frontend.rs @@ -110,6 +110,7 @@ pub(crate) async fn build_frontend( settings: &ResolverSettings, network_settings: &NetworkSettings, no_config: bool, + keep_on_error: bool, python_preference: PythonPreference, python_downloads: PythonDownloads, concurrency: Concurrency, @@ -135,6 +136,7 @@ pub(crate) async fn build_frontend( settings, network_settings, no_config, + keep_on_error, python_preference, python_downloads, concurrency, @@ -178,6 +180,7 @@ async fn build_impl( settings: &ResolverSettings, network_settings: &NetworkSettings, no_config: bool, + keep_on_error: bool, python_preference: PythonPreference, python_downloads: PythonDownloads, concurrency: Concurrency, @@ -327,6 +330,7 @@ async fn build_impl( python_request, install_mirrors.clone(), no_config, + keep_on_error, workspace.as_ref(), python_preference, python_downloads, @@ -405,6 +409,7 @@ async fn build_package( python_request: Option<&str>, install_mirrors: PythonInstallMirrors, no_config: bool, + keep_on_error: bool, workspace: Result<&Workspace, &WorkspaceError>, python_preference: PythonPreference, python_downloads: PythonDownloads, @@ -697,8 +702,18 @@ async fn build_package( build_output, Some(sdist_build.normalized_filename().version()), ) - .await?; - build_results.push(wheel_build); + .await; + + if keep_on_error && wheel_build.is_err() { + let path = temp_dir.into_path(); + writeln!( + printer.stderr(), + "`--keep-on-error` was provided, build can be found in {}", + path.display() + )?; + } + + build_results.push(wheel_build?); } BuildPlan::Sdist => { let sdist_build = build_sdist( @@ -812,8 +827,17 @@ async fn build_package( build_output, version.as_ref(), ) - .await?; - build_results.push(wheel_build); + .await; + + if keep_on_error && wheel_build.is_err() { + let path = temp_dir.into_path(); + writeln!( + printer.stderr(), + "`--keep-on-error` was provided, build can be found in {}", + path.display() + )?; + } + build_results.push(wheel_build?); } } diff --git a/crates/uv/src/lib.rs b/crates/uv/src/lib.rs index f0ed52308cdc..ff2de0f4e09b 100644 --- a/crates/uv/src/lib.rs +++ b/crates/uv/src/lib.rs @@ -895,6 +895,7 @@ async fn run(mut cli: Cli) -> Result { &args.settings, &globals.network_settings, cli.top_level.no_config, + args.keep_on_error, globals.python_preference, globals.python_downloads, globals.concurrency, diff --git a/crates/uv/src/settings.rs b/crates/uv/src/settings.rs index 662a53fed491..d5192778d504 100644 --- a/crates/uv/src/settings.rs +++ b/crates/uv/src/settings.rs @@ -2262,6 +2262,7 @@ pub(crate) struct BuildSettings { pub(crate) list: bool, pub(crate) build_logs: bool, pub(crate) force_pep517: bool, + pub(crate) keep_on_error: bool, pub(crate) build_constraints: Vec, pub(crate) hash_checking: Option, pub(crate) python: Option, @@ -2289,6 +2290,7 @@ impl BuildSettings { no_verify_hashes, build_logs, no_build_logs, + keep_on_error, python, build, refresh, @@ -2314,6 +2316,7 @@ impl BuildSettings { .filter_map(Maybe::into_option) .collect(), force_pep517, + keep_on_error, hash_checking: HashCheckingMode::from_args( flag(require_hashes, no_require_hashes), flag(verify_hashes, no_verify_hashes),