Skip to content

Commit

Permalink
Auto merge of #830 - brson:rustup-dir, r=brson
Browse files Browse the repository at this point in the history
Rename ~/.multirust to ~/.rustup

This renames the ~/.multirust directory to ~/.rustup. The logic for the upgrade is in `do_rustup_home_upgrade`, and is defensive against errors. It amounts to moving an unrecognized `~/.rustup` folder to `~/.rustup.sh`, then moving a `~/.multirust` dir to `~/.rustup`.

If the upgrade fails then rustup will silently continue using `~/.multirust`, though this code path doesn't have tests, and is unlikely to happen...

After the upgrade, or during initial install this also sets up `~/.multirust` as a symlink to `~/.rustup` in case there are tools depending on it. This is intended to be temporary, to avoid breakage while getting it done quickly.

This needs some more manual testing before deployment but is ready for review.

r? @alexcrichton
  • Loading branch information
bors committed Dec 1, 2016
2 parents f6cd385 + a07ee8c commit 0599787
Show file tree
Hide file tree
Showing 5 changed files with 385 additions and 6 deletions.
8 changes: 7 additions & 1 deletion src/rustup-cli/self_update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,10 @@ pub fn install(no_prompt: bool, verbose: bool,
if !opts.no_modify_path {
try!(do_add_to_path(&get_add_path_methods()));
}
// Create ~/.rustup and a compatibility ~/.multirust symlink.
// FIXME: Someday we can stop setting up the symlink, and when
// we do that we can stop creating ~/.rustup as well.
try!(utils::create_rustup_home());
try!(maybe_install_rust(&opts.default_toolchain, &opts.default_host_triple, verbose));

if cfg!(unix) {
Expand Down Expand Up @@ -508,7 +512,7 @@ fn cleanup_legacy() -> Result<()> {

#[cfg(unix)]
fn legacy_multirust_home_dir() -> Result<PathBuf> {
Ok(try!(utils::multirust_home()))
Ok(try!(utils::legacy_multirust_home()))
}

#[cfg(windows)]
Expand Down Expand Up @@ -607,6 +611,8 @@ pub fn uninstall(no_prompt: bool) -> Result<()> {

info!("removing rustup home");

try!(utils::delete_legacy_multirust_symlink());

// Delete RUSTUP_HOME
let ref rustup_dir = try!(utils::multirust_home());
if rustup_dir.exists() {
Expand Down
4 changes: 2 additions & 2 deletions src/rustup-mock/src/clitools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,9 +259,9 @@ pub fn env(config: &Config, cmd: &mut Command) {
cmd.env("CARGO_HOME", config.cargodir.to_string_lossy().to_string());
cmd.env("RUSTUP_OVERRIDE_HOST_TRIPLE", this_host_triple());

// This is only used for some installation tests on unix where CARGO_HOME
// above is unset
// These are used in some installation tests that unset RUSTUP_HOME/CARGO_HOME
cmd.env("HOME", config.homedir.to_string_lossy().to_string());
cmd.env("USERPROFILE", config.homedir.to_string_lossy().to_string());

// Setting HOME will confuse the sudo check for rustup-init. Override it
cmd.env("RUSTUP_INIT_SKIP_SUDO_CHECK", "yes");
Expand Down
184 changes: 183 additions & 1 deletion src/rustup-utils/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -497,12 +497,194 @@ pub fn cargo_home() -> Result<PathBuf> {
cargo_home.or(user_home).ok_or(ErrorKind::CargoHome.into())
}

// Convert the ~/.multirust folder to ~/.rustup while dealing with rustup.sh
// metadata, which used to also live in ~/.rustup, but now lives in ~/rustup.sh.
pub fn do_rustup_home_upgrade() -> bool {

fn rustup_home_is_set() -> bool {
env::var_os("RUSTUP_HOME").is_some()
}

fn rustup_dir() -> Option<PathBuf> {
dot_dir(".rustup")
}

fn rustup_sh_dir() -> Option<PathBuf> {
dot_dir(".rustup.sh")
}

fn multirust_dir() -> Option<PathBuf> {
dot_dir(".multirust")
}

fn rustup_dir_exists() -> bool {
rustup_dir().map(|p| p.exists()).unwrap_or(false)
}

fn rustup_sh_dir_exists() -> bool {
rustup_sh_dir().map(|p| p.exists()).unwrap_or(false)
}

fn multirust_dir_exists() -> bool {
multirust_dir().map(|p| p.exists()).unwrap_or(false)
}

fn rustup_old_version_exists() -> bool {
rustup_dir()
.map(|p| p.join("rustup-version").exists())
.unwrap_or(false)
}

fn delete_rustup_dir() -> Result<()> {
if let Some(dir) = rustup_dir() {
raw::remove_dir(&dir)
.chain_err(|| "unable to delete rustup dir")?;
}

Ok(())
}

fn rename_rustup_dir_to_rustup_sh() -> Result<()> {
let dirs = (rustup_dir(), rustup_sh_dir());
if let (Some(rustup), Some(rustup_sh)) = dirs {
fs::rename(&rustup, &rustup_sh)
.chain_err(|| "unable to rename rustup dir")?;
}

Ok(())
}

fn rename_multirust_dir_to_rustup() -> Result<()> {
let dirs = (multirust_dir(), rustup_dir());
if let (Some(rustup), Some(rustup_sh)) = dirs {
fs::rename(&rustup, &rustup_sh)
.chain_err(|| "unable to rename multirust dir")?;
}

Ok(())
}

// If RUSTUP_HOME is set then its default path doesn't matter, so we're
// not going to risk doing any I/O work and making a mess.
if rustup_home_is_set() { return true }

// Now we are just trying to get a bogus, rustup.sh-created ~/.rustup out
// of the way in the manner that is least likely to take time and generate
// errors. First try to rename it to ~/.rustup.sh, then try to delete it.
// If that doesn't work we can't use the ~/.rustup name.
let old_rustup_dir_removed = if rustup_old_version_exists() {
if !rustup_sh_dir_exists() {
if rename_rustup_dir_to_rustup_sh().is_ok() {
true
} else {
if delete_rustup_dir().is_ok() {
true
} else {
false
}
}
} else {
if delete_rustup_dir().is_ok() {
true
} else {
false
}
}
} else {
true
};

// Now we're trying to move ~/.multirust to ~/.rustup
old_rustup_dir_removed && if multirust_dir_exists() {
if rustup_dir_exists() {
// There appears to be both a ~/.multirust dir and a valid ~/.rustup
// dir. Most likely because one is a symlink to the other, as configured
// below.
true
} else {
if rename_multirust_dir_to_rustup().is_ok() {
// Finally, making the hardlink from ~/.multirust back to
// ~/.rustup, for temporary compatibility.
let _ = create_legacy_multirust_symlink();
true
} else {
false
}
}
} else {
true
}
}

// Creates a ~/.rustup folder and a ~/.multirust symlink
pub fn create_rustup_home() -> Result<()> {
// If RUSTUP_HOME is set then don't make any assumptions about where it's
// ok to put ~/.multirust
if env::var_os("RUSTUP_HOME").is_some() { return Ok(()) }

let home = rustup_home_in_user_dir()?;
fs::create_dir_all(&home)
.chain_err(|| "unable to create ~/.rustup")?;

// This is a temporary compatibility symlink
create_legacy_multirust_symlink()?;

Ok(())
}

// Create a symlink from ~/.multirust to ~/.rustup to temporarily
// accomodate old tools that are expecting that directory
fn create_legacy_multirust_symlink() -> Result<()> {
let newhome = rustup_home_in_user_dir()?;
let oldhome = legacy_multirust_home()?;

raw::symlink_dir(&newhome, &oldhome)
.chain_err(|| format!("unable to symlink {} from {}",
newhome.display(), oldhome.display()))?;

Ok(())
}

pub fn delete_legacy_multirust_symlink() -> Result<()> {
let oldhome = legacy_multirust_home()?;

if oldhome.exists() {
let meta = fs::symlink_metadata(&oldhome)
.chain_err(|| "unable to get metadata for ~/.multirust")?;
if meta.file_type().is_symlink() {
// remove_dir handles unlinking symlinks
raw::remove_dir(&oldhome)
.chain_err(|| format!("unable to delete legacy symlink {}", oldhome.display()))?;
}
}

Ok(())
}

fn dot_dir(name: &str) -> Option<PathBuf> {
home_dir().map(|p| p.join(name))
}

pub fn legacy_multirust_home() -> Result<PathBuf> {
dot_dir(".multirust").ok_or(ErrorKind::MultirustHome.into())
}

pub fn rustup_home_in_user_dir() -> Result<PathBuf> {
dot_dir(".rustup").ok_or(ErrorKind::MultirustHome.into())
}

pub fn multirust_home() -> Result<PathBuf> {
let use_rustup_dir = do_rustup_home_upgrade();

let cwd = try!(env::current_dir().chain_err(|| ErrorKind::MultirustHome));
let multirust_home = env::var_os("RUSTUP_HOME").map(|home| {
cwd.join(home)
});
let user_home = home_dir().map(|p| p.join(".multirust"));
let user_home = if use_rustup_dir {
dot_dir(".rustup")
} else {
dot_dir(".multirust")
};
multirust_home.or(user_home).ok_or(ErrorKind::MultirustHome.into())
}

Expand Down
Loading

0 comments on commit 0599787

Please sign in to comment.