Skip to content

Commit de64c9c

Browse files
committed
Use ~/.rustup instead of ~/.multirust
This will silently do the upgrade when rustup is run, and uses several defenses to avoid breaking due to interactions with rustup.sh.
1 parent a6db7a3 commit de64c9c

File tree

3 files changed

+266
-2
lines changed

3 files changed

+266
-2
lines changed

src/rustup-cli/self_update.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,7 @@ fn cleanup_legacy() -> Result<()> {
508508

509509
#[cfg(unix)]
510510
fn legacy_multirust_home_dir() -> Result<PathBuf> {
511-
Ok(try!(utils::multirust_home()))
511+
Ok(try!(utils::legacy_multirust_home()))
512512
}
513513

514514
#[cfg(windows)]

src/rustup-utils/src/utils.rs

Lines changed: 130 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -497,12 +497,141 @@ pub fn cargo_home() -> Result<PathBuf> {
497497
cargo_home.or(user_home).ok_or(ErrorKind::CargoHome.into())
498498
}
499499

500+
// Convert the ~/.multirust folder to ~/.rustup while dealing with rustup.sh
501+
// metadata, which used to also live in ~/.rustup, but now lives in ~/rustup.sh.
502+
pub fn do_rustup_home_upgrade() -> bool {
503+
504+
fn rustup_home_is_set() -> bool {
505+
env::var_os("RUSTUP_HOME").is_some()
506+
}
507+
508+
fn rustup_dir() -> Option<PathBuf> {
509+
dot_dir(".rustup")
510+
}
511+
512+
fn rustup_sh_dir() -> Option<PathBuf> {
513+
dot_dir(".rustup.sh")
514+
}
515+
516+
fn multirust_dir() -> Option<PathBuf> {
517+
dot_dir(".multirust")
518+
}
519+
520+
fn rustup_dir_exists() -> bool {
521+
rustup_dir().map(|p| p.exists()).unwrap_or(false)
522+
}
523+
524+
fn rustup_sh_dir_exists() -> bool {
525+
rustup_sh_dir().map(|p| p.exists()).unwrap_or(false)
526+
}
527+
528+
fn multirust_dir_exists() -> bool {
529+
multirust_dir().map(|p| p.exists()).unwrap_or(false)
530+
}
531+
532+
fn rustup_old_version_exists() -> bool {
533+
rustup_dir()
534+
.map(|p| p.join("rustup-version").exists())
535+
.unwrap_or(false)
536+
}
537+
538+
fn delete_rustup_dir() -> Result<()> {
539+
if let Some(dir) = rustup_dir() {
540+
raw::remove_dir(&dir)
541+
.chain_err(|| "unable to delete rustup dir")?;
542+
}
543+
544+
Ok(())
545+
}
546+
547+
fn rename_rustup_dir_to_rustup_sh() -> Result<()> {
548+
let dirs = (rustup_dir(), rustup_sh_dir());
549+
if let (Some(rustup), Some(rustup_sh)) = dirs {
550+
fs::rename(&rustup, &rustup_sh)
551+
.chain_err(|| "unable to rename rustup dir")?;
552+
}
553+
554+
Ok(())
555+
}
556+
557+
fn rename_multirust_dir_to_rustup() -> Result<()> {
558+
let dirs = (multirust_dir(), rustup_dir());
559+
if let (Some(rustup), Some(rustup_sh)) = dirs {
560+
fs::rename(&rustup, &rustup_sh)
561+
.chain_err(|| "unable to rename multirust dir")?;
562+
}
563+
564+
Ok(())
565+
}
566+
567+
// If RUSTUP_HOME is set then its default path doesn't matter, so we're
568+
// not going to risk doing any I/O work and making a mess.
569+
if rustup_home_is_set() { return true }
570+
571+
// Now we are just trying to get a bogus, rustup.sh-created ~/.rustup out
572+
// of the way in the manner that is least likely to take time and generate
573+
// errors. First try to rename it to ~/.rustup.sh, then try to delete it.
574+
// If that doesn't work we can't use the ~/.rustup name.
575+
let old_rustup_dir_removed = if rustup_old_version_exists() {
576+
if !rustup_sh_dir_exists() {
577+
if rename_rustup_dir_to_rustup_sh().is_ok() {
578+
true
579+
} else {
580+
if delete_rustup_dir().is_ok() {
581+
true
582+
} else {
583+
false
584+
}
585+
}
586+
} else {
587+
if delete_rustup_dir().is_ok() {
588+
true
589+
} else {
590+
false
591+
}
592+
}
593+
} else {
594+
true
595+
};
596+
597+
// Now we're trying to move ~/.multirust to ~/.rustup
598+
old_rustup_dir_removed && if multirust_dir_exists() {
599+
if rustup_dir_exists() {
600+
// There appears to be both a ~/.multirust dir and a valid ~/.rustup
601+
// dir. Weird situation. Pick ~/.rustup.
602+
true
603+
} else {
604+
if rename_multirust_dir_to_rustup().is_ok() {
605+
true
606+
} else {
607+
false
608+
}
609+
}
610+
} else {
611+
true
612+
}
613+
}
614+
615+
fn dot_dir(name: &str) -> Option<PathBuf> {
616+
home_dir().map(|p| p.join(name))
617+
}
618+
619+
pub fn legacy_multirust_home() -> Result<PathBuf> {
620+
dot_dir(".multirust").ok_or(ErrorKind::MultirustHome.into())
621+
}
622+
500623
pub fn multirust_home() -> Result<PathBuf> {
624+
let use_rustup_dir = do_rustup_home_upgrade();
625+
501626
let cwd = try!(env::current_dir().chain_err(|| ErrorKind::MultirustHome));
502627
let multirust_home = env::var_os("RUSTUP_HOME").map(|home| {
503628
cwd.join(home)
504629
});
505-
let user_home = home_dir().map(|p| p.join(".multirust"));
630+
let user_home = if use_rustup_dir {
631+
dot_dir(".rustup")
632+
} else {
633+
dot_dir(".multirust")
634+
};
506635
multirust_home.or(user_home).ok_or(ErrorKind::MultirustHome.into())
507636
}
508637

tests/cli-rustup.rs

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ extern crate rustup_utils;
55
extern crate rustup_mock;
66
extern crate tempdir;
77

8+
use std::fs;
9+
use std::process;
10+
use rustup_utils::raw;
811
use rustup_mock::clitools::{self, Config, Scenario,
912
expect_ok, expect_ok_ex,
1013
expect_stdout_ok,
@@ -544,3 +547,135 @@ fn remove_component() {
544547
assert!(!path.parent().unwrap().exists());
545548
});
546549
}
550+
551+
// Run without setting RUSTUP_HOME, with setting HOME and USERPROFILE
552+
fn run_no_home(config: &Config, args: &[&str], env: &[(&str, &str)]) -> process::Output {
553+
let home_dir_str = &format!("{}", config.homedir.display());
554+
let mut cmd = clitools::cmd(config, "rustup", args);
555+
clitools::env(config, &mut cmd);
556+
cmd.env_remove("RUSTUP_HOME");
557+
cmd.env("HOME", home_dir_str);
558+
cmd.env("USERPROFILE", home_dir_str);
559+
for &(name, val) in env {
560+
cmd.env(name, val);
561+
}
562+
let out = cmd.output().unwrap();
563+
assert!(out.status.success());
564+
565+
out
566+
}
567+
568+
// Rename ~/.multirust to ~/.rustup
569+
#[test]
570+
fn multirust_dir_upgrade_rename_multirust_dir_to_rustup() {
571+
setup(&|config| {
572+
let multirust_dir = config.homedir.join(".multirust");
573+
let rustup_dir = config.homedir.join(".rustup");
574+
let multirust_dir_str = &format!("{}", multirust_dir.display());
575+
576+
// First write data into ~/.multirust
577+
run_no_home(config, &["default", "stable"],
578+
&[("RUSTUP_HOME", multirust_dir_str)]);
579+
let out = run_no_home(config, &["toolchain", "list"],
580+
&[("RUSTUP_HOME", multirust_dir_str)]);
581+
assert!(String::from_utf8(out.stdout).unwrap().contains("stable"));
582+
583+
assert!(multirust_dir.exists());
584+
assert!(!rustup_dir.exists());
585+
586+
// Next run without RUSTUP_DIR, but with HOME/USERPROFILE set so rustup
587+
// can infer RUSTUP_DIR. It will silently move ~/.multirust to
588+
// ~/.rustup.
589+
let out = run_no_home(config, &["toolchain", "list"], &[]);
590+
assert!(String::from_utf8(out.stdout).unwrap().contains("stable"));
591+
592+
assert!(!multirust_dir.exists());
593+
assert!(rustup_dir.exists());
594+
});
595+
}
596+
597+
// Renaming ~/.multirust to ~/.rustup but ~/.rustup/rustup-version (rustup.sh) exists
598+
#[test]
599+
fn multirust_dir_upgrade_old_rustup_exists() {
600+
setup(&|config| {
601+
let multirust_dir = config.homedir.join(".multirust");
602+
let rustup_dir = config.homedir.join(".rustup");
603+
let rustup_sh_dir = config.homedir.join(".rustup.sh");
604+
605+
let multirust_dir_str = &format!("{}", multirust_dir.display());
606+
let old_rustup_sh_version_file = rustup_dir.join("rustup-version");
607+
let new_rustup_sh_version_file = rustup_sh_dir.join("rustup-version");
608+
609+
// First write data into ~/.multirust
610+
run_no_home(config, &["default", "stable"],
611+
&[("RUSTUP_HOME", multirust_dir_str)]);
612+
let out = run_no_home(config, &["toolchain", "list"],
613+
&[("RUSTUP_HOME", multirust_dir_str)]);
614+
assert!(String::from_utf8(out.stdout).unwrap().contains("stable"));
615+
616+
assert!(multirust_dir.exists());
617+
assert!(!rustup_dir.exists());
618+
619+
// Now add rustup.sh data to ~/.rustup
620+
fs::create_dir_all(&rustup_dir).unwrap();
621+
raw::write_file(&old_rustup_sh_version_file, "1").unwrap();
622+
assert!(old_rustup_sh_version_file.exists());
623+
624+
// Now do the upgrade, and ~/.rustup will be moved to ~/.rustup.sh
625+
let out = run_no_home(config, &["toolchain", "list"], &[]);
626+
assert!(String::from_utf8(out.stdout).unwrap().contains("stable"));
627+
628+
assert!(!multirust_dir.exists());
629+
assert!(rustup_dir.exists());
630+
assert!(!old_rustup_sh_version_file.exists());
631+
assert!(new_rustup_sh_version_file.exists());
632+
});
633+
}
634+
635+
// Renaming ~/.multirust to ~/.rustup but ~/.rustup/rustup-version (rustup.sh) exists,
636+
// oh and alse ~/.rustup.sh exists
637+
#[test]
638+
fn multirust_dir_upgrade_old_rustup_existsand_new_rustup_sh_exists() {
639+
setup(&|config| {
640+
let multirust_dir = config.homedir.join(".multirust");
641+
let rustup_dir = config.homedir.join(".rustup");
642+
let rustup_sh_dir = config.homedir.join(".rustup.sh");
643+
644+
let multirust_dir_str = &format!("{}", multirust_dir.display());
645+
let old_rustup_sh_version_file = rustup_dir.join("rustup-version");
646+
let new_rustup_sh_version_file = rustup_sh_dir.join("rustup-version");
647+
648+
// First write data into ~/.multirust
649+
run_no_home(config, &["default", "stable"],
650+
&[("RUSTUP_HOME", multirust_dir_str)]);
651+
let out = run_no_home(config, &["toolchain", "list"],
652+
&[("RUSTUP_HOME", multirust_dir_str)]);
653+
assert!(String::from_utf8(out.stdout).unwrap().contains("stable"));
654+
655+
assert!(multirust_dir.exists());
656+
assert!(!rustup_dir.exists());
657+
658+
// This time there are two things that look like rustup.sh.
659+
// Only one can win. It doesn't matter much which.
660+
661+
// Now add rustup.sh data to ~/.rustup
662+
fs::create_dir_all(&rustup_dir).unwrap();
663+
raw::write_file(&old_rustup_sh_version_file, "1").unwrap();
664+
665+
// Also to ~/.rustup.sh
666+
fs::create_dir_all(&rustup_sh_dir).unwrap();
667+
raw::write_file(&new_rustup_sh_version_file, "1").unwrap();
668+
669+
assert!(old_rustup_sh_version_file.exists());
670+
assert!(new_rustup_sh_version_file.exists());
671+
672+
// Now do the upgrade, and ~/.rustup will be moved to ~/.rustup.sh
673+
let out = run_no_home(config, &["toolchain", "list"], &[]);
674+
assert!(String::from_utf8(out.stdout).unwrap().contains("stable"));
675+
676+
assert!(!multirust_dir.exists());
677+
assert!(rustup_dir.exists());
678+
assert!(!old_rustup_sh_version_file.exists());
679+
assert!(new_rustup_sh_version_file.exists());
680+
});
681+
}

0 commit comments

Comments
 (0)