Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't prepend CARGO_HOME/bin unnecessarily #2978

Merged
merged 3 commits into from
Apr 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions src/env_var.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::collections::VecDeque;
use std::env;
use std::path::PathBuf;
use std::process::Command;
Expand All @@ -21,15 +22,19 @@ fn append_path(name: &str, value: Vec<PathBuf>, cmd: &mut Command) {
}
}

pub(crate) fn prepend_path(name: &str, value: Vec<PathBuf>, cmd: &mut Command) {
pub(crate) fn prepend_path(name: &str, prepend: Vec<PathBuf>, cmd: &mut Command) {
let old_value = process().var_os(name);
let mut parts: Vec<PathBuf>;
if let Some(ref v) = old_value {
parts = value;
parts.extend(env::split_paths(v).collect::<Vec<_>>());
let parts = if let Some(ref v) = old_value {
let mut tail = env::split_paths(v).collect::<VecDeque<_>>();
for path in prepend.into_iter().rev() {
if !tail.contains(&path) {
tail.push_front(path);
}
}
tail
} else {
parts = value;
}
prepend.into()
};

if let Ok(new_value) = env::join_paths(parts) {
cmd.env(name, new_value);
Expand Down
88 changes: 87 additions & 1 deletion tests/cli-misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

pub mod mock;

use std::env::consts::EXE_SUFFIX;
use std::str;
use std::{env::consts::EXE_SUFFIX, path::Path};

use rustup::for_host;
use rustup::test::this_host_triple;
Expand Down Expand Up @@ -298,6 +298,92 @@ fn rustup_run_searches_path() {
});
}

#[test]
fn rustup_doesnt_prepend_path_unnecessarily() {
setup(&|config| {
expect_ok(config, &["rustup", "default", "nightly"]);

let expect_stderr_ok_env_first_then =
|config: &Config,
args: &[&str],
env: &[(&str, &str)],
first: &Path,
second: Option<&Path>| {
let out = run(config, args[0], &args[1..], env);
let first_then_second = |list: &str| -> bool {
let mut saw_first = false;
let mut saw_second = false;
for path in std::env::split_paths(list) {
if path == first {
if saw_second {
return false;
}
saw_first = true;
}
if Some(&*path) == second {
if !saw_first {
return false;
}
saw_second = true;
}
}
true
};
if !out.ok || !first_then_second(&out.stderr) {
clitools::print_command(args, &out);
println!("expected.ok: true");
clitools::print_indented(
"expected.stderr.first_then",
&format!("{} comes before {:?}", first.display(), second),
);
panic!();
}
};

// For all of these, CARGO_HOME/bin will be auto-prepended.
let cargo_home_bin = config.cargodir.join("bin");
expect_stderr_ok_env_first_then(
config,
&["cargo", "--echo-path"],
&[],
&cargo_home_bin,
None,
);
expect_stderr_ok_env_first_then(
config,
&["cargo", "--echo-path"],
&[("PATH", "")],
&cargo_home_bin,
None,
);

// Check that CARGO_HOME/bin is prepended to path.
expect_stderr_ok_env_first_then(
config,
&["cargo", "--echo-path"],
&[("PATH", &format!("{}", config.exedir.display()))],
&cargo_home_bin,
Some(&config.exedir),
);

// But if CARGO_HOME/bin is already on PATH, it will not be prepended again,
// so exedir will take precedence.
expect_stderr_ok_env_first_then(
config,
&["cargo", "--echo-path"],
&[(
"PATH",
std::env::join_paths([&config.exedir, &cargo_home_bin])
.unwrap()
.to_str()
.unwrap(),
)],
&config.exedir,
Some(&cargo_home_bin),
);
});
}

#[test]
fn rustup_failed_path_search() {
setup(&|config| {
Expand Down
4 changes: 2 additions & 2 deletions tests/mock/clitools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ pub fn expect_component_not_executable(config: &Config, cmd: &str) {
}
}

fn print_command(args: &[&str], out: &SanitizedOutput) {
pub(crate) fn print_command(args: &[&str], out: &SanitizedOutput) {
print!("\n>");
for arg in args {
if arg.contains(' ') {
Expand All @@ -452,7 +452,7 @@ fn print_command(args: &[&str], out: &SanitizedOutput) {
print_indented("out.stderr", &out.stderr);
}

fn print_indented(heading: &str, text: &str) {
pub(crate) fn print_indented(heading: &str, text: &str) {
let mut lines = text.lines().count();
// The standard library treats `a\n` and `a` as both being one line.
// This is confusing when the test fails because of a missing newline.
Expand Down
4 changes: 4 additions & 0 deletions tests/mock/mock_bin_src.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ fn main() {
writeln!(out, "{}", arg.to_string_lossy()).unwrap();
}
}
Some("--echo-path") => {
let mut out = io::stderr();
writeln!(out, "{}", std::env::var("PATH").unwrap()).unwrap();
}
_ => panic!("bad mock proxy commandline"),
}
}
Expand Down