Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Overwrite `$CARGO` when the current exe is detected to be a cargo
binary.

See: rust-lang#15099 (comment)
  • Loading branch information
smoelius committed Feb 20, 2025
1 parent 2d61f60 commit bf9fdc2
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 19 deletions.
18 changes: 16 additions & 2 deletions src/cargo/util/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -493,9 +493,23 @@ impl GlobalContext {
paths::resolve_executable(&argv0)
}

fn is_cargo(path: &Path) -> bool {
path.file_stem() == Some(OsStr::new("cargo"))
}

let from_current_exe_result = from_current_exe();
if from_current_exe_result.as_deref().is_ok_and(is_cargo) {
return from_current_exe_result;
}

let from_argv_result = from_argv();
if from_argv_result.as_deref().is_ok_and(is_cargo) {
return from_argv_result;
}

let exe = from_env()
.or_else(|_| from_current_exe())
.or_else(|_| from_argv())
.or(from_current_exe_result)
.or(from_argv_result)
.context("couldn't get the path to cargo executable")?;
Ok(exe)
})
Expand Down
5 changes: 4 additions & 1 deletion tests/testsuite/cargo_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -391,10 +391,13 @@ fn cargo_subcommand_env() {
.canonicalize()
.unwrap();
let envtest_bin = envtest_bin.to_str().unwrap();
// Previously, `$CARGO` would be left at `envtest_bin`. However, with the
// fix for #15099, `$CARGO` is now overwritten with the path to the current
// exe when it is detected to be a cargo binary.
cargo_process("envtest")
.env("PATH", &path)
.env(cargo::CARGO_ENV, &envtest_bin)
.with_stdout_data(format!("{}\n", envtest_bin).raw().raw())
.with_stdout_data(format!("{}\n", cargo.display()).raw())
.run();
}

Expand Down
66 changes: 66 additions & 0 deletions tests/testsuite/freshness.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Tests for fingerprinting (rebuild detection).
use std::env::consts::EXE_SUFFIX;
use std::fs::{self, OpenOptions};
use std::io;
use std::io::prelude::*;
Expand Down Expand Up @@ -3182,3 +3183,68 @@ fn use_mtime_cache_in_cargo_home() {
"#]])
.run();
}

#[cargo_test]
fn overwrite_cargo_environment_variable() {
// If passed arguments `arg1 arg2 ...`, this program runs them as a command.
// If passed no arguments, this program simply prints `$CARGO`.
let p = project()
.file("Cargo.toml", &basic_manifest("foo", "1.0.0"))
.file(
"src/main.rs",
r#"
fn main() {
let mut args = std::env::args().skip(1);
if let Some(arg1) = args.next() {
let status = std::process::Command::new(arg1)
.args(args)
.status()
.unwrap();
assert!(status.success());
} else {
println!("{}", std::env::var("CARGO").unwrap());
}
}
"#,
)
.build();

// Create two other cargo binaries in the project root, one with the wrong
// name and one with the right name.
let cargo_exe = cargo_test_support::cargo_exe();
let wrong_name_path = p.root().join(format!("wrong_name{EXE_SUFFIX}"));
let other_cargo_path = p.root().join(cargo_exe.file_name().unwrap());
std::fs::hard_link(&cargo_exe, &wrong_name_path).unwrap();
std::fs::hard_link(&cargo_exe, &other_cargo_path).unwrap();

// The output of each of the following commands should be `path-to-cargo`:
// ```
// cargo run
// cargo run -- cargo run
// cargo run -- wrong_name run
// ```

let cargo = cargo_exe.display().to_string();
let wrong_name = wrong_name_path.display().to_string();

for cmd in [
"run",
&format!("run -- {cargo} run"),
&format!("run -- {wrong_name} run"),
] {
p.cargo(cmd)
.with_stdout_data(cargo_exe.display().to_string() + "\n")
.run();
}

// The output of the following command should be `path-to-other-cargo`:
// ```
// cargo run -- other_cargo run
// ```

let other_cargo = other_cargo_path.display().to_string();

p.cargo(&format!("run -- {other_cargo} run"))
.with_stdout_data("[ROOT]/foo/cargo\n")
.run();
}
22 changes: 6 additions & 16 deletions tests/testsuite/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3909,22 +3909,12 @@ test env_test ... ok
.run();

// Check that `cargo test` propagates the environment's $CARGO
let rustc = cargo_util::paths::resolve_executable("rustc".as_ref())
.unwrap()
.canonicalize()
.unwrap();
let stderr_rustc = format!(
"{}[EXE]",
rustc
.with_extension("")
.to_str()
.unwrap()
.replace(rustc_host, "[HOST_TARGET]")
);
p.cargo("test --lib -- --nocapture")
// we use rustc since $CARGO is only used if it points to a path that exists
.env(cargo::CARGO_ENV, rustc)
.with_stderr_contains(stderr_rustc)
let cargo_exe = cargo_test_support::cargo_exe();
let other_cargo_path = p.root().join(cargo_exe.file_name().unwrap());
std::fs::hard_link(&cargo_exe, &other_cargo_path).unwrap();
p.process(other_cargo_path)
.args(&["test", "--lib", "--", "--nocapture"])
.with_stderr_contains("[ROOT]/foo/cargo\n")
.with_stdout_data(str![[r#"
...
test env_test ... ok
Expand Down

0 comments on commit bf9fdc2

Please # to comment.