Skip to content

Commit 7119683

Browse files
committed
Symlinking 'config' to 'config.toml' should not produce a warning.
Also true cor symlinking 'credentials.toml' to 'credentials'. This will allow users to run multiple versions of Cargo without generating a warning if they want to use the .toml versions. Note that Windows symlinks require special permission, so in the case the user doesn't have that permission, we don't run the symlink tests. This matches behavior in Rust libstd.
1 parent 25e3fee commit 7119683

File tree

2 files changed

+96
-6
lines changed

2 files changed

+96
-6
lines changed

src/cargo/util/config.rs

+19-6
Original file line numberDiff line numberDiff line change
@@ -705,12 +705,25 @@ impl Config {
705705

706706
if fs::metadata(&possible).is_ok() {
707707
if warn && fs::metadata(&possible_with_extension).is_ok() {
708-
self.shell().warn(format!(
709-
"Both `{}` and `{}` exist. Using `{}`",
710-
possible.display(),
711-
possible_with_extension.display(),
712-
possible.display()
713-
))?;
708+
// We don't want to print a warning if the version
709+
// without the extension is just a symlink to the version
710+
// WITH an extension, which people may want to do to
711+
// support multiple Cargo versions at once and not
712+
// get a warning.
713+
let skip_warning = if let Ok(target_path) = fs::read_link(&possible) {
714+
target_path == possible_with_extension
715+
} else {
716+
false
717+
};
718+
719+
if !skip_warning {
720+
self.shell().warn(format!(
721+
"Both `{}` and `{}` exist. Using `{}`",
722+
possible.display(),
723+
possible_with_extension.display(),
724+
possible.display()
725+
))?;
726+
}
714727
}
715728

716729
Ok(Some(possible))

tests/testsuite/config.rs

+77
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
use std::borrow::Borrow;
22
use std::collections;
33
use std::fs;
4+
use std::io;
5+
use std::os;
6+
use std::path::Path;
47

58
use crate::support::{paths, project};
69
use cargo::core::{enable_nightly_features, Shell};
@@ -54,6 +57,44 @@ fn write_config_toml(config: &str) {
5457
fs::write(path, config).unwrap();
5558
}
5659

60+
// Several test fail on windows if the user does not have permission to
61+
// create symlinks (the `SeCreateSymbolicLinkPrivilege`). Instead of
62+
// disabling these test on Windows, use this function to test whether we
63+
// have permission, and return otherwise. This way, we still don't run these
64+
// tests most of the time, but at least we do if the user has the right
65+
// permissions.
66+
// This function is derived from libstd fs tests.
67+
pub fn got_symlink_permission() -> bool {
68+
if cfg!(unix) {
69+
return true;
70+
}
71+
let link = paths::root().join("some_hopefully_unique_link_name");
72+
let target = paths::root().join("nonexisting_target");
73+
74+
match symlink_file(&target, &link) {
75+
Ok(_) => true,
76+
// ERROR_PRIVILEGE_NOT_HELD = 1314
77+
Err(ref err) if err.raw_os_error() == Some(1314) => false,
78+
Err(_) => true,
79+
}
80+
}
81+
82+
#[cfg(unix)]
83+
fn symlink_file(target: &Path, link: &Path) -> io::Result<()> {
84+
os::unix::fs::symlink(target, link)
85+
}
86+
87+
#[cfg(windows)]
88+
fn symlink_file(target: &Path, link: &Path) -> io::Result<()> {
89+
os::windows::fs::symlink_file(target, link)
90+
}
91+
92+
fn symlink_config_to_config_toml() {
93+
let toml_path = paths::root().join(".cargo/config.toml");
94+
let symlink_path = paths::root().join(".cargo/config");
95+
t!(symlink_file(&toml_path, &symlink_path));
96+
}
97+
5798
fn new_config(env: &[(&str, &str)]) -> Config {
5899
enable_nightly_features(); // -Z advanced-env
59100
let output = Box::new(fs::File::create(paths::root().join("shell.out")).unwrap());
@@ -132,6 +173,42 @@ f1 = 1
132173
assert_eq!(config.get::<Option<i32>>("foo.f1").unwrap(), Some(1));
133174
}
134175

176+
#[cargo_test]
177+
fn config_ambiguous_filename_symlink_doesnt_warn() {
178+
// Windows requires special permissions to create symlinks.
179+
// If we don't have permission, just skip this test.
180+
if !got_symlink_permission() {
181+
return;
182+
};
183+
184+
write_config_toml(
185+
"\
186+
[foo]
187+
f1 = 1
188+
",
189+
);
190+
191+
symlink_config_to_config_toml();
192+
193+
let config = new_config(&[]);
194+
195+
assert_eq!(config.get::<Option<i32>>("foo.f1").unwrap(), Some(1));
196+
197+
// It should NOT have warned for the symlink.
198+
drop(config); // Paranoid about flushing the file.
199+
let path = paths::root().join("shell.out");
200+
let output = fs::read_to_string(path).unwrap();
201+
let unexpected = "\
202+
warning: Both `[..]/.cargo/config` and `[..]/.cargo/config.toml` exist. Using `[..]/.cargo/config`
203+
";
204+
if lines_match(unexpected, &output) {
205+
panic!(
206+
"Found unexpected:\n{}\nActual error:\n{}\n",
207+
unexpected, output
208+
);
209+
}
210+
}
211+
135212
#[cargo_test]
136213
fn config_ambiguous_filename() {
137214
write_config(

0 commit comments

Comments
 (0)