@@ -34,6 +34,7 @@ use common::{self, Confirm};
3434use errors:: * ;
3535use rustup_dist:: dist;
3636use rustup_utils:: utils;
37+ use same_file:: Handle ;
3738use std:: env;
3839use std:: env:: consts:: EXE_SUFFIX ;
3940use std:: path:: { Path , PathBuf , Component } ;
@@ -657,29 +658,70 @@ pub fn install_proxies() -> Result<()> {
657658 let ref bin_path = try!( utils:: cargo_home ( ) ) . join ( "bin" ) ;
658659 let ref rustup_path = bin_path. join ( & format ! ( "rustup{}" , EXE_SUFFIX ) ) ;
659660
660- // Record the size of the known links, then when we get files which may or
661- // may not be links, we compare their size. Same size means probably a link.
662- let mut file_size = 0 ;
661+ let rustup = Handle :: from_path ( rustup_path) ?;
662+
663+ let mut tool_handles = Vec :: new ( ) ;
664+ let mut link_afterwards = Vec :: new ( ) ;
663665
664666 // Try to hardlink all the Rust exes to the rustup exe. Some systems,
665667 // like Android, does not support hardlinks, so we fallback to symlinks.
668+ //
669+ // Note that this function may not be running in the context of a fresh
670+ // self update but rather as part of a normal update to fill in missing
671+ // proxies. In that case our process may actually have the `rustup.exe`
672+ // file open, and on systems like Windows that means that you can't
673+ // even remove other hard links to the same file. Basically if we have
674+ // `rustup.exe` open and running and `cargo.exe` is a hard link to that
675+ // file, we can't remove `cargo.exe`.
676+ //
677+ // To avoid unnecessary errors from being returned here we use the
678+ // `same-file` crate and its `Handle` type to avoid clobbering hard links
679+ // that are already valid. If a hard link already points to the
680+ // `rustup.exe` file then we leave it alone and move to the next one.
666681 for tool in TOOLS {
667- let ref tool_path = bin_path. join ( & format ! ( "{}{}" , tool, EXE_SUFFIX ) ) ;
668- if tool_path. exists ( ) {
669- file_size = utils:: file_size ( tool_path) ?;
682+ let tool_path = bin_path. join ( & format ! ( "{}{}" , tool, EXE_SUFFIX ) ) ;
683+ if let Ok ( handle) = Handle :: from_path ( & tool_path) {
684+ prev_handles. push ( handle) ;
685+ if rustup == * prev_handles. last ( ) . unwrap ( ) {
686+ continue
687+ }
670688 }
671- try! ( utils :: hard_or_symlink_file ( rustup_path , tool_path) ) ;
689+ link_afterwards . push ( tool_path) ;
672690 }
673691
674692 for tool in DUP_TOOLS {
675693 let ref tool_path = bin_path. join ( & format ! ( "{}{}" , tool, EXE_SUFFIX ) ) ;
676- if tool_path. exists ( ) && ( file_size == 0 || utils:: file_size ( tool_path) ? != file_size) {
677- warn ! ( "tool `{}` is already installed, remove it from `{}`, then run `rustup update` \
678- to have rustup manage this tool.",
679- tool, bin_path. to_string_lossy( ) ) ;
680- } else {
681- try!( utils:: hard_or_symlink_file ( rustup_path, tool_path) ) ;
694+ if let Ok ( handle) = Handle :: from_path ( tool_path) {
695+ // Like above, don't clobber anything that's already hardlinked to
696+ // avoid extraneous errors from being returned.
697+ if rustup == handle {
698+ continue
699+ }
700+
701+ // If this file exists and is *not* equivalent to all other
702+ // preexisting tools we found, then we're going to assume that it
703+ // was preinstalled and actually pointing to a totally different
704+ // binary. This is intended for cases where historically users
705+ // rand `cargo install rustfmt` and so they had custom `rustfmt`
706+ // and `cargo-fmt` executables lying around, but we as rustup have
707+ // since started managing these tools.
708+ //
709+ // If the file is managed by rustup it should be equivalent to some
710+ // previous file, and if it's not equivalent to anything then it's
711+ // pretty likely that it needs to be dealt with manually.
712+ if prev_handles. iter ( ) . all ( |h| * h != handle) {
713+ warn ! ( "tool `{}` is already installed, remove it from `{}`, then run `rustup update` \
714+ to have rustup manage this tool.",
715+ tool, bin_path. to_string_lossy( ) ) ;
716+ continue
717+ }
682718 }
719+ try!( utils:: hard_or_symlink_file ( rustup_path, tool_path) ) ;
720+ }
721+
722+ drop ( prev_handles) ;
723+ for path in link_afterwards {
724+ try!( utils:: hard_or_symlink_file ( rustup_path, & path) ) ;
683725 }
684726
685727 Ok ( ( ) )
0 commit comments