Skip to content

Commit 9c65e60

Browse files
committed
Fix #15099
Overwrite `$CARGO` when the current exe is detected to be a cargo binary. See: #15099 (comment)
1 parent 2d61f60 commit 9c65e60

File tree

4 files changed

+106
-14
lines changed

4 files changed

+106
-14
lines changed

src/cargo/util/context/mod.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -493,9 +493,25 @@ impl GlobalContext {
493493
paths::resolve_executable(&argv0)
494494
}
495495

496+
// Determines whether `path` is a cargo binary.
497+
// See: https://github.com/rust-lang/cargo/issues/15099#issuecomment-2666737150
498+
fn is_cargo(path: &Path) -> bool {
499+
path.file_stem() == Some(OsStr::new("cargo"))
500+
}
501+
502+
let from_current_exe = from_current_exe();
503+
if from_current_exe.as_deref().is_ok_and(is_cargo) {
504+
return from_current_exe;
505+
}
506+
507+
let from_argv = from_argv();
508+
if from_argv.as_deref().is_ok_and(is_cargo) {
509+
return from_argv;
510+
}
511+
496512
let exe = from_env()
497-
.or_else(|_| from_current_exe())
498-
.or_else(|_| from_argv())
513+
.or(from_current_exe)
514+
.or(from_argv)
499515
.context("couldn't get the path to cargo executable")?;
500516
Ok(exe)
501517
})

tests/testsuite/cargo_command.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -391,10 +391,13 @@ fn cargo_subcommand_env() {
391391
.canonicalize()
392392
.unwrap();
393393
let envtest_bin = envtest_bin.to_str().unwrap();
394+
// Previously, `$CARGO` would be left at `envtest_bin`. However, with the
395+
// fix for #15099, `$CARGO` is now overwritten with the path to the current
396+
// exe when it is detected to be a cargo binary.
394397
cargo_process("envtest")
395398
.env("PATH", &path)
396399
.env(cargo::CARGO_ENV, &envtest_bin)
397-
.with_stdout_data(format!("{}\n", envtest_bin).raw().raw())
400+
.with_stdout_data(format!("{}\n", cargo.display()).raw())
398401
.run();
399402
}
400403

tests/testsuite/freshness.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! Tests for fingerprinting (rebuild detection).
22
3+
use std::env::consts::EXE_SUFFIX;
34
use std::fs::{self, OpenOptions};
45
use std::io;
56
use std::io::prelude::*;
@@ -3182,3 +3183,76 @@ fn use_mtime_cache_in_cargo_home() {
31823183
"#]])
31833184
.run();
31843185
}
3186+
3187+
#[cargo_test]
3188+
fn overwrite_cargo_environment_variable() {
3189+
// If passed arguments `arg1 arg2 ...`, this program runs them as a command.
3190+
// If passed no arguments, this program simply prints `$CARGO`.
3191+
let p = project()
3192+
.file("Cargo.toml", &basic_manifest("foo", "1.0.0"))
3193+
.file(
3194+
"src/main.rs",
3195+
r#"
3196+
fn main() {
3197+
let mut args = std::env::args().skip(1);
3198+
if let Some(arg1) = args.next() {
3199+
let status = std::process::Command::new(arg1)
3200+
.args(args)
3201+
.status()
3202+
.unwrap();
3203+
assert!(status.success());
3204+
} else {
3205+
println!("{}", std::env::var("CARGO").unwrap());
3206+
}
3207+
}
3208+
"#,
3209+
)
3210+
.build();
3211+
3212+
// Create two other cargo binaries in the project root, one with the wrong
3213+
// name and one with the right name.
3214+
let cargo_exe = cargo_test_support::cargo_exe();
3215+
let root = p.root().canonicalize().unwrap();
3216+
let wrong_name_path = root.join(format!("wrong_name{EXE_SUFFIX}"));
3217+
let other_cargo_path = root.join(cargo_exe.file_name().unwrap());
3218+
std::fs::hard_link(&cargo_exe, &wrong_name_path).unwrap();
3219+
std::fs::hard_link(&cargo_exe, &other_cargo_path).unwrap();
3220+
3221+
// The output of each of the following commands should be `path-to-cargo`:
3222+
// ```
3223+
// cargo run
3224+
// cargo run -- cargo run
3225+
// cargo run -- wrong_name run
3226+
// ```
3227+
3228+
let cargo = cargo_exe.display().to_string();
3229+
let wrong_name = wrong_name_path.display().to_string();
3230+
let stdout_cargo = format!("{}[EXE]\n", cargo_exe.with_extension("").display());
3231+
3232+
for cmd in [
3233+
"run",
3234+
&format!("run -- {cargo} run"),
3235+
&format!("run -- {wrong_name} run"),
3236+
] {
3237+
p.cargo(cmd).with_stdout_data(&stdout_cargo).run();
3238+
}
3239+
3240+
// The output of the following command should be `path-to-other-cargo`:
3241+
// ```
3242+
// cargo run -- other_cargo run
3243+
// ```
3244+
3245+
let other_cargo = other_cargo_path.display().to_string();
3246+
let stdout_other_cargo = format!(
3247+
"{}[EXE]\n",
3248+
other_cargo_path
3249+
.with_extension("")
3250+
.to_str()
3251+
.unwrap()
3252+
.replace(root.parent().unwrap().to_str().unwrap(), "[ROOT]")
3253+
);
3254+
3255+
p.cargo(&format!("run -- {other_cargo} run"))
3256+
.with_stdout_data(stdout_other_cargo)
3257+
.run();
3258+
}

tests/testsuite/test.rs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3909,22 +3909,21 @@ test env_test ... ok
39093909
.run();
39103910

39113911
// Check that `cargo test` propagates the environment's $CARGO
3912-
let rustc = cargo_util::paths::resolve_executable("rustc".as_ref())
3913-
.unwrap()
3914-
.canonicalize()
3915-
.unwrap();
3916-
let stderr_rustc = format!(
3912+
let cargo_exe = cargo_test_support::cargo_exe();
3913+
let root = p.root().canonicalize().unwrap();
3914+
let other_cargo_path = root.join(cargo_exe.file_name().unwrap());
3915+
std::fs::hard_link(&cargo_exe, &other_cargo_path).unwrap();
3916+
let stderr_other_cargo = format!(
39173917
"{}[EXE]",
3918-
rustc
3918+
other_cargo_path
39193919
.with_extension("")
39203920
.to_str()
39213921
.unwrap()
3922-
.replace(rustc_host, "[HOST_TARGET]")
3922+
.replace(root.parent().unwrap().to_str().unwrap(), "[ROOT]")
39233923
);
3924-
p.cargo("test --lib -- --nocapture")
3925-
// we use rustc since $CARGO is only used if it points to a path that exists
3926-
.env(cargo::CARGO_ENV, rustc)
3927-
.with_stderr_contains(stderr_rustc)
3924+
p.process(other_cargo_path)
3925+
.args(&["test", "--lib", "--", "--nocapture"])
3926+
.with_stderr_contains(stderr_other_cargo)
39283927
.with_stdout_data(str![[r#"
39293928
...
39303929
test env_test ... ok

0 commit comments

Comments
 (0)