diff --git a/crates/uv/src/commands/pip/install.rs b/crates/uv/src/commands/pip/install.rs index 01b82f6b5..15529b4fd 100644 --- a/crates/uv/src/commands/pip/install.rs +++ b/crates/uv/src/commands/pip/install.rs @@ -138,17 +138,22 @@ pub(crate) async fn pip_install( ) .collect(); + // Determine whether we're modifying the discovered environment, or a separate target. + let mutable = !(target.is_some() || prefix.is_some()); + // Detect the current Python interpreter. let environment = PythonEnvironment::find( &python .as_deref() .map(PythonRequest::parse) .unwrap_or_default(), - EnvironmentPreference::from_system_flag(system, true), + EnvironmentPreference::from_system_flag(system, mutable), &cache, )?; - report_target_environment(&environment, &cache, printer)?; + if mutable { + report_target_environment(&environment, &cache, printer)?; + } // Apply any `--target` or `--prefix` directories. let environment = if let Some(target) = target { diff --git a/crates/uv/src/commands/pip/sync.rs b/crates/uv/src/commands/pip/sync.rs index a65bd1b9a..08ffb1181 100644 --- a/crates/uv/src/commands/pip/sync.rs +++ b/crates/uv/src/commands/pip/sync.rs @@ -122,17 +122,22 @@ pub(crate) async fn pip_sync( } } + // Determine whether we're modifying the discovered environment, or a separate target. + let mutable = !(target.is_some() || prefix.is_some()); + // Detect the current Python interpreter. let environment = PythonEnvironment::find( &python .as_deref() .map(PythonRequest::parse) .unwrap_or_default(), - EnvironmentPreference::from_system_flag(system, true), + EnvironmentPreference::from_system_flag(system, mutable), &cache, )?; - report_target_environment(&environment, &cache, printer)?; + if mutable { + report_target_environment(&environment, &cache, printer)?; + } // Apply any `--target` or `--prefix` directories. let environment = if let Some(target) = target { diff --git a/crates/uv/tests/it/common/mod.rs b/crates/uv/tests/it/common/mod.rs index 82143317a..5201aae1a 100644 --- a/crates/uv/tests/it/common/mod.rs +++ b/crates/uv/tests/it/common/mod.rs @@ -315,7 +315,7 @@ impl TestContext { // Exclude `link-mode` on Windows since we set it in the remote test suite if cfg!(windows) { filters.push(("--link-mode ".to_string(), String::new())); - filters.push(((r#"link-mode = "copy"\n"#).to_string(), String::new())); + filters.push((r#"link-mode = "copy"\n"#.to_string(), String::new())); } filters.extend( diff --git a/crates/uv/tests/it/pip_sync.rs b/crates/uv/tests/it/pip_sync.rs index c3a51e7c7..dee1a6b01 100644 --- a/crates/uv/tests/it/pip_sync.rs +++ b/crates/uv/tests/it/pip_sync.rs @@ -5361,6 +5361,36 @@ fn target_no_build_isolation() -> Result<()> { Ok(()) } +/// Sync to a `--target` directory without a virtual environment. +#[test] +fn target_system() -> Result<()> { + let context = TestContext::new_with_versions(&["3.12"]); + + // Install `iniconfig` to the target directory. + let requirements_in = context.temp_dir.child("requirements.in"); + requirements_in.write_str("iniconfig==2.0.0")?; + + uv_snapshot!(context.filters(), context.pip_sync() + .arg("requirements.in") + .arg("--target") + .arg("target"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 1 package in [TIME] + Prepared 1 package in [TIME] + Installed 1 package in [TIME] + + iniconfig==2.0.0 + "###); + + // Ensure that the package is present in the target directory. + assert!(context.temp_dir.child("target").child("iniconfig").is_dir()); + + Ok(()) +} + /// Sync to a `--prefix` directory. #[test] fn prefix() -> Result<()> {