@@ -9,6 +9,7 @@ use std::env;
99use std:: ffi:: OsString ;
1010use std:: fmt;
1111use std:: fs;
12+ use std:: iter;
1213use std:: io:: { self , Read , Write } ;
1314use std:: path:: { Path , PathBuf } ;
1415use std:: process:: { self , Command , Stdio } ;
@@ -246,7 +247,7 @@ impl Toolchain {
246247 match self . spec {
247248 ToolchainSpec :: Ci { ref commit, alt } => {
248249 let alt_s = if alt { format ! ( "-alt" ) } else { String :: new ( ) } ;
249- format ! ( "ci-{}{}-{}" , commit, alt_s, self . host)
250+ format ! ( "bisector- ci-{}{}-{}" , commit, alt_s, self . host)
250251 }
251252 // N.B. We need to call this with a nonstandard name so that rustup utilizes the
252253 // fallback cargo logic.
@@ -287,19 +288,16 @@ impl DownloadParams {
287288 if cfg. args. alt { "-alt" } else { "" }
288289 ) ;
289290
290- DownloadParams {
291- url_prefix : url_prefix,
292- tmp_dir : cfg. rustup_tmp_path . clone ( ) ,
293- install_dir : cfg. toolchains_path . clone ( ) ,
294- install_cargo : cfg. args . with_cargo ,
295- install_src : cfg. args . with_src ,
296- force_install : cfg. args . force_install ,
297- }
291+ Self :: from_cfg_with_url_prefix ( cfg, url_prefix)
298292 }
299293
300294 fn for_nightly ( cfg : & Config ) -> Self {
295+ Self :: from_cfg_with_url_prefix ( cfg, NIGHTLY_SERVER . to_string ( ) )
296+ }
297+
298+ fn from_cfg_with_url_prefix ( cfg : & Config , url_prefix : String ) -> Self {
301299 DownloadParams {
302- url_prefix : NIGHTLY_SERVER . to_string ( ) ,
300+ url_prefix : url_prefix ,
303301 tmp_dir : cfg. rustup_tmp_path . clone ( ) ,
304302 install_dir : cfg. toolchains_path . clone ( ) ,
305303 install_cargo : cfg. args . with_cargo ,
@@ -428,6 +426,8 @@ enum InstallError {
428426 TempDir ( #[ cause] io:: Error ) ,
429427 #[ fail( display = "Could not move tempdir into destination: {}" , _0) ]
430428 Move ( #[ cause] io:: Error ) ,
429+ #[ fail( display = "Could not run subcommand {}: {}" , command, cause) ]
430+ Subcommand { command : String , #[ cause] cause : io:: Error }
431431}
432432
433433#[ derive( Debug ) ]
@@ -472,11 +472,23 @@ impl Toolchain {
472472 }
473473
474474 fn remove ( & self , dl_params : & DownloadParams ) -> Result < ( ) , Error > {
475- if !self . is_current_nightly ( ) {
476- eprintln ! ( "uninstalling {}" , self ) ;
477- let dir = dl_params. install_dir . join ( self . rustup_name ( ) ) ;
478- fs:: remove_dir_all ( & dir) ?;
479- }
475+ eprintln ! ( "uninstalling {}" , self ) ;
476+ self . do_remove ( dl_params)
477+ }
478+
479+ /// Removes the (previously installed) bisector rustc described by `dl_params`.
480+ ///
481+ /// The main reason to call this (instead of `fs::remove_dir_all` directly)
482+ /// is to guard against deleting state not managed by `cargo-bisect-rustc`.
483+ fn do_remove ( & self , dl_params : & DownloadParams ) -> Result < ( ) , Error > {
484+ let rustup_name = self . rustup_name ( ) ;
485+
486+ // Guard against destroying directories that this tool didn't create.
487+ assert ! ( rustup_name. starts_with( "bisector-nightly" ) ||
488+ rustup_name. starts_with( "bisector-ci" ) ) ;
489+
490+ let dir = dl_params. install_dir . join ( rustup_name) ;
491+ fs:: remove_dir_all ( & dir) ?;
480492
481493 Ok ( ( ) )
482494 }
@@ -680,24 +692,53 @@ impl Toolchain {
680692 }
681693
682694 fn install ( & self , client : & Client , dl_params : & DownloadParams ) -> Result < ( ) , InstallError > {
683- if self . is_current_nightly ( ) {
684- // pre existing installation
685- return Ok ( ( ) ) ;
686- }
687-
688695 debug ! ( "installing {}" , self ) ;
689696 let tmpdir = TempDir :: new_in ( & dl_params. tmp_dir , & self . rustup_name ( ) )
690697 . map_err ( InstallError :: TempDir ) ?;
691698 let dest = dl_params. install_dir . join ( self . rustup_name ( ) ) ;
692699 if dl_params. force_install {
693- let _ = fs :: remove_dir_all ( & dest ) ;
700+ let _ = self . do_remove ( dl_params ) ;
694701 }
695702
696703 if dest. is_dir ( ) {
697704 // already installed
698705 return Ok ( ( ) ) ;
699706 }
700707
708+ if self . is_current_nightly ( ) {
709+ // make link to pre-existing installation
710+ debug ! ( "installing (via link) {}" , self ) ;
711+
712+ let nightly_path: String = {
713+ let cmd = CommandTemplate :: new ( [ "rustc" , "--print" , "sysroot" ]
714+ . iter ( )
715+ . map ( |s| s. to_string ( ) ) ) ;
716+ let stdout = cmd. output ( ) ?. stdout ;
717+ let output = String :: from_utf8_lossy ( & stdout) ;
718+ // the output should be the path, terminated by a newline
719+ let mut path = output. to_string ( ) ;
720+ let last = path. pop ( ) ;
721+ assert_eq ! ( last, Some ( '\n' ) ) ;
722+ path
723+ } ;
724+
725+ let cmd = CommandTemplate :: new ( [ "rustup" , "toolchain" , "link" ]
726+ . iter ( )
727+ . map ( |s| s. to_string ( ) )
728+ . chain ( iter:: once ( self . rustup_name ( ) ) )
729+ . chain ( iter:: once ( nightly_path) ) ) ;
730+ if cmd. status ( ) ?. success ( ) {
731+ return Ok ( ( ) ) ;
732+ } else {
733+ return Err ( InstallError :: Subcommand {
734+ command : cmd. string ( ) ,
735+ cause : io:: Error :: new ( io:: ErrorKind :: Other , "failed to link via `rustup`" ) ,
736+ } ) ;
737+ }
738+ }
739+
740+ debug ! ( "installing via download {}" , self ) ;
741+
701742 let rustc_filename = format ! ( "rustc-nightly-{}" , self . host) ;
702743
703744 let location = match self . spec {
@@ -772,6 +813,46 @@ impl Toolchain {
772813 }
773814}
774815
816+ // A simpler wrapper struct to make up for impoverished `Command` in libstd.
817+ struct CommandTemplate ( Vec < String > ) ;
818+
819+ impl CommandTemplate {
820+ fn new ( strings : impl Iterator < Item =String > ) -> Self {
821+ CommandTemplate ( strings. collect ( ) )
822+ }
823+
824+ fn command ( & self ) -> Command {
825+ assert ! ( self . 0 . len( ) > 0 ) ;
826+ let mut cmd = Command :: new ( & self . 0 [ 0 ] ) ;
827+ for arg in & self . 0 [ 1 ..] {
828+ cmd. arg ( arg) ;
829+ }
830+ cmd
831+ }
832+
833+ fn string ( & self ) -> String {
834+ assert ! ( self . 0 . len( ) > 0 ) ;
835+ let mut s = self . 0 [ 0 ] . to_string ( ) ;
836+ for arg in & self . 0 [ 1 ..] {
837+ s. push_str ( " " ) ;
838+ s. push_str ( arg) ;
839+ }
840+ s
841+ }
842+
843+ fn status ( & self ) -> Result < process:: ExitStatus , InstallError > {
844+ self . command ( ) . status ( ) . map_err ( |cause| {
845+ InstallError :: Subcommand { command : self . string ( ) , cause }
846+ } )
847+ }
848+
849+ fn output ( & self ) -> Result < process:: Output , InstallError > {
850+ self . command ( ) . output ( ) . map_err ( |cause| {
851+ InstallError :: Subcommand { command : self . string ( ) , cause }
852+ } )
853+ }
854+ }
855+
775856struct Config {
776857 args : Opts ,
777858 rustup_tmp_path : PathBuf ,
0 commit comments