Skip to content

Commit 0b350ad

Browse files
committed
[WIP] Add support for platform-defined standard directories
This change stops cargo from violating the operating system rules regarding the placement of config, cache, ... directories on Linux, macOS and Windows. Existing directories and overrides are retained. The precedence is as follows: 1) use the `CARGO_HOME` environment variable if it exists (legacy) 2) use `CARGO_CACHE_DIR`, `CARGO_CONFIG_DIR` etc. env vars if they exist 3) use the ~/.cargo directory if it exists (legacy) 4) follow operating system standards A new cargo command, `dirs`, is added, which can provide path information to other command line tools. Fixes: #1734 #1976 rust-lang/rust#12725 Addresses: rust-lang/rfcs#1615 #148, #3981
1 parent 805fbeb commit 0b350ad

File tree

10 files changed

+199
-74
lines changed

10 files changed

+199
-74
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ crates-io = { path = "src/crates-io", version = "0.16" }
2222
crossbeam = "0.3"
2323
crypto-hash = "0.3"
2424
curl = "0.4.6"
25+
directories = "0.8.0"
2526
env_logger = "0.5"
2627
failure = "0.1.1"
2728
filetime = "0.1"

src/bin/cargo.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ fn is_executable<P: AsRef<Path>>(path: P) -> bool {
174174
}
175175

176176
fn search_directories(config: &Config) -> Vec<PathBuf> {
177-
let mut dirs = vec![config.home().clone().into_path_unlocked().join("bin")];
177+
let mut dirs = vec![config.bin_path()];
178178
if let Some(val) = env::var_os("PATH") {
179179
dirs.extend(env::split_paths(&val));
180180
}

src/bin/commands/dirs.rs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
use command_prelude::*;
2+
3+
pub fn cli() -> App {
4+
subcommand("dirs")
5+
.about("Display directories (cache, config, ...) used by cargo")
6+
.after_help("\
7+
")
8+
}
9+
10+
pub fn exec(config: &mut Config, _args: &ArgMatches) -> CliResult {
11+
println!("CARGO_CACHE_DIR: {:?}", config.cache_path().into_path_unlocked());
12+
println!("CARGO_CONFIG_DIR: {:?}", config.config_path().into_path_unlocked());
13+
println!("CARGO_DATA_DIR: {:?}", config.data_path());
14+
println!("CARGO_BIN_DIR: {:?}", config.bin_path());
15+
Ok(())
16+
}

src/bin/commands/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ pub fn builtin() -> Vec<App> {
66
build::cli(),
77
check::cli(),
88
clean::cli(),
9+
dirs::cli(),
910
doc::cli(),
1011
fetch::cli(),
1112
generate_lockfile::cli(),
@@ -40,6 +41,7 @@ pub fn builtin_exec(cmd: &str) -> Option<fn(&mut Config, &ArgMatches) -> CliResu
4041
"build" => build::exec,
4142
"check" => check::exec,
4243
"clean" => clean::exec,
44+
"dirs" => dirs::exec,
4345
"doc" => doc::exec,
4446
"fetch" => fetch::exec,
4547
"generate-lockfile" => generate_lockfile::exec,
@@ -74,6 +76,7 @@ pub mod bench;
7476
pub mod build;
7577
pub mod check;
7678
pub mod clean;
79+
pub mod dirs;
7780
pub mod doc;
7881
pub mod fetch;
7982
pub mod generate_lockfile;

src/cargo/core/workspace.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@ impl<'cfg> Workspace<'cfg> {
373373
// `CARGO_HOME` pointing inside of the workspace root or in the
374374
// current project, but we don't want to mistakenly try to put
375375
// crates.io crates into the workspace by accident.
376-
if self.config.home() == path {
376+
if &self.config.cache_path() == path {
377377
break;
378378
}
379379
}

src/cargo/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ extern crate core_foundation;
1111
extern crate crates_io as registry;
1212
extern crate crossbeam;
1313
extern crate curl;
14+
extern crate directories;
1415
#[macro_use]
1516
extern crate failure;
1617
extern crate filetime;

src/cargo/ops/cargo_install.rs

+46-18
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,27 @@ impl Drop for Transaction {
5353
}
5454
}
5555

56+
#[derive(Clone)]
57+
struct CargoInstallDirs {
58+
config_dir: PathBuf,
59+
bin_dir: PathBuf,
60+
}
61+
62+
impl CargoInstallDirs {
63+
fn from_root(root: PathBuf) -> CargoInstallDirs {
64+
CargoInstallDirs {
65+
bin_dir: root.join("bin"),
66+
config_dir: root,
67+
}
68+
}
69+
fn from_config(config: &Config) -> CargoInstallDirs {
70+
CargoInstallDirs {
71+
config_dir: config.config_path().into_path_unlocked(),
72+
bin_dir: config.bin_path(),
73+
}
74+
}
75+
}
76+
5677
pub fn install(
5778
root: Option<&str>,
5879
krates: Vec<&str>,
@@ -61,7 +82,7 @@ pub fn install(
6182
opts: &ops::CompileOptions,
6283
force: bool,
6384
) -> CargoResult<()> {
64-
let root = resolve_root(root, opts.config)?;
85+
let root = resolve_install_dirs(root, opts.config)?;
6586
let map = SourceConfigMap::new(opts.config)?;
6687

6788
let (installed_anything, scheduled_error) = if krates.len() <= 1 {
@@ -122,7 +143,7 @@ pub fn install(
122143
if installed_anything {
123144
// Print a warning that if this directory isn't in PATH that they won't be
124145
// able to run these commands.
125-
let dst = metadata(opts.config, &root)?.parent().join("bin");
146+
let dst = metadata(opts.config, &Filesystem::new(root.config_dir))?.parent().join("bin");
126147
let path = env::var_os("PATH").unwrap_or_default();
127148
for path in env::split_paths(&path) {
128149
if path == dst {
@@ -145,7 +166,7 @@ pub fn install(
145166
}
146167

147168
fn install_one(
148-
root: &Filesystem,
169+
dirs: &CargoInstallDirs,
149170
map: &SourceConfigMap,
150171
krate: Option<&str>,
151172
source_id: &SourceId,
@@ -235,9 +256,9 @@ fn install_one(
235256
// We have to check this again afterwards, but may as well avoid building
236257
// anything if we're gonna throw it away anyway.
237258
{
238-
let metadata = metadata(config, root)?;
259+
let metadata = metadata(config, &Filesystem::new(dirs.config_dir.clone()))?;
239260
let list = read_crate_list(&metadata)?;
240-
let dst = metadata.parent().join("bin");
261+
let dst = config.bin_path();
241262
check_overwrites(&dst, pkg, &opts.filter, &list, force)?;
242263
}
243264

@@ -274,7 +295,7 @@ fn install_one(
274295
);
275296
}
276297

277-
let metadata = metadata(config, root)?;
298+
let metadata = metadata(config, &Filesystem::new(dirs.config_dir.clone()))?;
278299
let mut list = read_crate_list(&metadata)?;
279300
let dst = metadata.parent().join("bin");
280301
let duplicates = check_overwrites(&dst, pkg, &opts.filter, &list, force)?;
@@ -653,8 +674,8 @@ fn write_crate_list(file: &FileLock, listing: CrateListingV1) -> CargoResult<()>
653674
}
654675

655676
pub fn install_list(dst: Option<&str>, config: &Config) -> CargoResult<()> {
656-
let dst = resolve_root(dst, config)?;
657-
let dst = metadata(config, &dst)?;
677+
let dst = resolve_install_dirs(dst, config)?;
678+
let dst = metadata(config, &Filesystem::new(dst.config_dir))?;
658679
let list = read_crate_list(&dst)?;
659680
for (k, v) in list.v1.iter() {
660681
println!("{}:", k);
@@ -675,7 +696,7 @@ pub fn uninstall(
675696
bail!("A binary can only be associated with a single installed package, specifying multiple specs with --bin is redundant.");
676697
}
677698

678-
let root = resolve_root(root, config)?;
699+
let root = resolve_install_dirs(root, config)?;
679700
let scheduled_error = if specs.len() == 1 {
680701
uninstall_one(&root, specs[0], bins, config)?;
681702
false
@@ -721,13 +742,13 @@ pub fn uninstall(
721742
Ok(())
722743
}
723744

724-
pub fn uninstall_one(
725-
root: &Filesystem,
745+
fn uninstall_one(
746+
dirs: &CargoInstallDirs,
726747
spec: &str,
727748
bins: &[String],
728749
config: &Config,
729750
) -> CargoResult<()> {
730-
let crate_metadata = metadata(config, root)?;
751+
let crate_metadata = metadata(config, &Filesystem::new(dirs.config_dir.clone()))?;
731752
let mut metadata = read_crate_list(&crate_metadata)?;
732753
let mut to_remove = Vec::new();
733754
{
@@ -736,7 +757,7 @@ pub fn uninstall_one(
736757
Entry::Occupied(e) => e,
737758
Entry::Vacant(..) => panic!("entry not found: {}", result),
738759
};
739-
let dst = crate_metadata.parent().join("bin");
760+
let dst = &dirs.bin_dir;
740761
for bin in installed.get() {
741762
let bin = dst.join(bin);
742763
if fs::metadata(&bin).is_err() {
@@ -785,15 +806,22 @@ pub fn uninstall_one(
785806
Ok(())
786807
}
787808

809+
/// Return a file lock for the .crates.toml file at the given root.
810+
/// The config argument is only used for logging to the shell.
788811
fn metadata(config: &Config, root: &Filesystem) -> CargoResult<FileLock> {
789812
root.open_rw(Path::new(".crates.toml"), config, "crate metadata")
790813
}
791814

792-
fn resolve_root(flag: Option<&str>, config: &Config) -> CargoResult<Filesystem> {
815+
// Determine cargo directories by first checking whether an argument was given
816+
// on the command line, if not checking whether the environment variable
817+
// CARGO_INSTALL_ROOT is set, and if not using the paths of the configuration.
818+
fn resolve_install_dirs(
819+
root: Option<&str>,
820+
config: &Config,
821+
) -> CargoResult<CargoInstallDirs> {
793822
let config_root = config.get_path("install.root")?;
794-
Ok(flag.map(PathBuf::from)
823+
Ok(root.map(PathBuf::from)
795824
.or_else(|| env::var_os("CARGO_INSTALL_ROOT").map(PathBuf::from))
796-
.or_else(move || config_root.map(|v| v.val))
797-
.map(Filesystem::new)
798-
.unwrap_or_else(|| config.home().clone()))
825+
.or_else(move || config_root.map(|v| v.val)).map(CargoInstallDirs::from_root)
826+
.unwrap_or_else(|| CargoInstallDirs::from_config(config)))
799827
}

0 commit comments

Comments
 (0)