@@ -9,6 +9,7 @@ use std::env;
9
9
use std:: ffi:: OsString ;
10
10
use std:: fmt;
11
11
use std:: fs;
12
+ use std:: iter;
12
13
use std:: io:: { self , Read , Write } ;
13
14
use std:: path:: { Path , PathBuf } ;
14
15
use std:: process:: { self , Command , Stdio } ;
@@ -246,7 +247,7 @@ impl Toolchain {
246
247
match self . spec {
247
248
ToolchainSpec :: Ci { ref commit, alt } => {
248
249
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)
250
251
}
251
252
// N.B. We need to call this with a nonstandard name so that rustup utilizes the
252
253
// fallback cargo logic.
@@ -287,19 +288,16 @@ impl DownloadParams {
287
288
if cfg. args. alt { "-alt" } else { "" }
288
289
) ;
289
290
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)
298
292
}
299
293
300
294
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 {
301
299
DownloadParams {
302
- url_prefix : NIGHTLY_SERVER . to_string ( ) ,
300
+ url_prefix : url_prefix ,
303
301
tmp_dir : cfg. rustup_tmp_path . clone ( ) ,
304
302
install_dir : cfg. toolchains_path . clone ( ) ,
305
303
install_cargo : cfg. args . with_cargo ,
@@ -428,6 +426,8 @@ enum InstallError {
428
426
TempDir ( #[ cause] io:: Error ) ,
429
427
#[ fail( display = "Could not move tempdir into destination: {}" , _0) ]
430
428
Move ( #[ cause] io:: Error ) ,
429
+ #[ fail( display = "Could not run subcommand {}: {}" , command, cause) ]
430
+ Subcommand { command : String , #[ cause] cause : io:: Error }
431
431
}
432
432
433
433
#[ derive( Debug ) ]
@@ -472,11 +472,23 @@ impl Toolchain {
472
472
}
473
473
474
474
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) ?;
480
492
481
493
Ok ( ( ) )
482
494
}
@@ -680,24 +692,53 @@ impl Toolchain {
680
692
}
681
693
682
694
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
-
688
695
debug ! ( "installing {}" , self ) ;
689
696
let tmpdir = TempDir :: new_in ( & dl_params. tmp_dir , & self . rustup_name ( ) )
690
697
. map_err ( InstallError :: TempDir ) ?;
691
698
let dest = dl_params. install_dir . join ( self . rustup_name ( ) ) ;
692
699
if dl_params. force_install {
693
- let _ = fs :: remove_dir_all ( & dest ) ;
700
+ let _ = self . do_remove ( dl_params ) ;
694
701
}
695
702
696
703
if dest. is_dir ( ) {
697
704
// already installed
698
705
return Ok ( ( ) ) ;
699
706
}
700
707
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
+
701
742
let rustc_filename = format ! ( "rustc-nightly-{}" , self . host) ;
702
743
703
744
let location = match self . spec {
@@ -772,6 +813,46 @@ impl Toolchain {
772
813
}
773
814
}
774
815
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
+
775
856
struct Config {
776
857
args : Opts ,
777
858
rustup_tmp_path : PathBuf ,
0 commit comments