mirror of
https://github.com/rust-lang/cargo
synced 2024-10-14 03:32:39 +00:00
Do not add home bin path to PATH if it's already there
This is to allow users to control the order via PATH if they so desire. Fix #11020
This commit is contained in:
parent
404889d869
commit
c712f08862
|
@ -212,11 +212,28 @@ fn is_executable<P: AsRef<Path>>(path: P) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search_directories(config: &Config) -> Vec<PathBuf> {
|
fn search_directories(config: &Config) -> Vec<PathBuf> {
|
||||||
let mut dirs = vec![config.home().clone().into_path_unlocked().join("bin")];
|
let mut path_dirs = if let Some(val) = env::var_os("PATH") {
|
||||||
if let Some(val) = env::var_os("PATH") {
|
env::split_paths(&val).collect()
|
||||||
dirs.extend(env::split_paths(&val));
|
} else {
|
||||||
}
|
vec![]
|
||||||
dirs
|
};
|
||||||
|
|
||||||
|
let home_bin = config.home().clone().into_path_unlocked().join("bin");
|
||||||
|
|
||||||
|
// If any of that PATH elements contains `home_bin`, do not
|
||||||
|
// add it again. This is so that the users can control priority
|
||||||
|
// of it using PATH, while preserving the historical
|
||||||
|
// behavior of preferring it over system global directories even
|
||||||
|
// when not in PATH at all.
|
||||||
|
// See https://github.com/rust-lang/cargo/issues/11020 for details.
|
||||||
|
//
|
||||||
|
// Note: `p == home_bin` will ignore trailing slash, but we don't
|
||||||
|
// `canonicalize` the paths.
|
||||||
|
if !path_dirs.iter().any(|p| p == &home_bin) {
|
||||||
|
path_dirs.insert(0, home_bin);
|
||||||
|
};
|
||||||
|
|
||||||
|
path_dirs
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_git_transports(config: &Config) {
|
fn init_git_transports(config: &Config) {
|
||||||
|
|
|
@ -7,11 +7,14 @@ use std::path::{Path, PathBuf};
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
|
use cargo_test_support::basic_manifest;
|
||||||
|
use cargo_test_support::paths::CargoPathExt;
|
||||||
use cargo_test_support::registry::Package;
|
use cargo_test_support::registry::Package;
|
||||||
use cargo_test_support::tools::echo_subcommand;
|
use cargo_test_support::tools::echo_subcommand;
|
||||||
use cargo_test_support::{
|
use cargo_test_support::{
|
||||||
basic_bin_manifest, cargo_exe, cargo_process, paths, project, project_in_home,
|
basic_bin_manifest, cargo_exe, cargo_process, paths, project, project_in_home,
|
||||||
};
|
};
|
||||||
|
use cargo_util::paths::join_paths;
|
||||||
|
|
||||||
fn path() -> Vec<PathBuf> {
|
fn path() -> Vec<PathBuf> {
|
||||||
env::split_paths(&env::var_os("PATH").unwrap_or_default()).collect()
|
env::split_paths(&env::var_os("PATH").unwrap_or_default()).collect()
|
||||||
|
@ -341,6 +344,83 @@ fn cargo_subcommand_env() {
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cargo_test]
|
||||||
|
fn cargo_cmd_bins_vs_explicit_path() {
|
||||||
|
// Set up `cargo-foo` binary in two places: inside `$HOME/.cargo/bin` and outside of it
|
||||||
|
//
|
||||||
|
// Return paths to both places
|
||||||
|
fn set_up_cargo_foo() -> (PathBuf, PathBuf) {
|
||||||
|
let p = project()
|
||||||
|
.at("cargo-foo")
|
||||||
|
.file("Cargo.toml", &basic_manifest("cargo-foo", "1.0.0"))
|
||||||
|
.file(
|
||||||
|
"src/bin/cargo-foo.rs",
|
||||||
|
r#"fn main() { println!("INSIDE"); }"#,
|
||||||
|
)
|
||||||
|
.file(
|
||||||
|
"src/bin/cargo-foo2.rs",
|
||||||
|
r#"fn main() { println!("OUTSIDE"); }"#,
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
p.cargo("build").run();
|
||||||
|
let cargo_bin_dir = paths::home().join(".cargo/bin");
|
||||||
|
cargo_bin_dir.mkdir_p();
|
||||||
|
let root_bin_dir = paths::root().join("bin");
|
||||||
|
root_bin_dir.mkdir_p();
|
||||||
|
let exe_name = format!("cargo-foo{}", env::consts::EXE_SUFFIX);
|
||||||
|
fs::rename(p.bin("cargo-foo"), cargo_bin_dir.join(&exe_name)).unwrap();
|
||||||
|
fs::rename(p.bin("cargo-foo2"), root_bin_dir.join(&exe_name)).unwrap();
|
||||||
|
|
||||||
|
(root_bin_dir, cargo_bin_dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
let (outside_dir, inside_dir) = set_up_cargo_foo();
|
||||||
|
|
||||||
|
// If `$CARGO_HOME/bin` is not in a path, prefer it over anything in `$PATH`.
|
||||||
|
//
|
||||||
|
// This is the historical behavior we don't want to break.
|
||||||
|
cargo_process("foo").with_stdout_contains("INSIDE").run();
|
||||||
|
|
||||||
|
// When `$CARGO_HOME/bin` is in the `$PATH`
|
||||||
|
// use only `$PATH` so the user-defined ordering is respected.
|
||||||
|
{
|
||||||
|
cargo_process("foo")
|
||||||
|
.env(
|
||||||
|
"PATH",
|
||||||
|
join_paths(&[&inside_dir, &outside_dir], "PATH").unwrap(),
|
||||||
|
)
|
||||||
|
.with_stdout_contains("INSIDE")
|
||||||
|
.run();
|
||||||
|
|
||||||
|
cargo_process("foo")
|
||||||
|
// Note: trailing slash
|
||||||
|
.env(
|
||||||
|
"PATH",
|
||||||
|
join_paths(&[inside_dir.join(""), outside_dir.join("")], "PATH").unwrap(),
|
||||||
|
)
|
||||||
|
.with_stdout_contains("INSIDE")
|
||||||
|
.run();
|
||||||
|
|
||||||
|
cargo_process("foo")
|
||||||
|
.env(
|
||||||
|
"PATH",
|
||||||
|
join_paths(&[&outside_dir, &inside_dir], "PATH").unwrap(),
|
||||||
|
)
|
||||||
|
.with_stdout_contains("OUTSIDE")
|
||||||
|
.run();
|
||||||
|
|
||||||
|
cargo_process("foo")
|
||||||
|
// Note: trailing slash
|
||||||
|
.env(
|
||||||
|
"PATH",
|
||||||
|
join_paths(&[outside_dir.join(""), inside_dir.join("")], "PATH").unwrap(),
|
||||||
|
)
|
||||||
|
.with_stdout_contains("OUTSIDE")
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
#[cargo_test]
|
#[cargo_test]
|
||||||
fn cargo_subcommand_args() {
|
fn cargo_subcommand_args() {
|
||||||
let p = echo_subcommand();
|
let p = echo_subcommand();
|
||||||
|
|
Loading…
Reference in a new issue