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