@@ -31,7 +31,7 @@ use std::env;
31
31
use std:: ffi:: OsString ;
32
32
use std:: fmt;
33
33
use std:: fs;
34
- use std:: io:: { self , Read } ;
34
+ use std:: io:: { self , Read , Write } ;
35
35
use std:: path:: { Path , PathBuf } ;
36
36
use std:: process:: { self , Command , Stdio } ;
37
37
use std:: str:: FromStr ;
@@ -87,6 +87,16 @@ fn get_commits(start: &str, end: &str) -> Result<Vec<git::Commit>, Error> {
87
87
--end 866a713258915e6cbb212d135f751a6a8c9e1c0a --test-dir ../my_project/ --prompt -- build
88
88
```" ) ]
89
89
struct Opts {
90
+ #[ structopt(
91
+ long = "regress" ,
92
+ default_value = "error" ,
93
+ help = "Customize what is treated as regression." ,
94
+ long_help = "Customize what is treated as regression. \
95
+ Values include `--regress=error`, `--regress=non-error`, \
96
+ `--regress=ice`, and `--regress=success`."
97
+ ) ]
98
+ regress : String ,
99
+
90
100
#[ structopt(
91
101
short = "a" , long = "alt" , help = "Download the alt build instead of normal build"
92
102
) ]
@@ -442,6 +452,7 @@ enum InstallError {
442
452
Move ( #[ cause] io:: Error ) ,
443
453
}
444
454
455
+ #[ derive( Debug ) ]
445
456
enum TestOutcome {
446
457
Baseline ,
447
458
Regressed ,
@@ -495,14 +506,20 @@ impl Toolchain {
495
506
fn test ( & self , cfg : & Config ) -> TestOutcome {
496
507
let outcome = if cfg. args . prompt {
497
508
loop {
498
- let status = self . run_test ( cfg) ;
509
+ let output = self . run_test ( cfg) ;
510
+ let status = output. status ;
499
511
500
512
eprintln ! ( "\n \n {} finished with exit code {:?}." , self , status. code( ) ) ;
501
513
eprintln ! ( "please select an action to take:" ) ;
502
514
515
+ let default_choice = match cfg. default_outcome_of_output ( output) {
516
+ TestOutcome :: Regressed => 0 ,
517
+ TestOutcome :: Baseline => 1 ,
518
+ } ;
519
+
503
520
match Select :: new ( )
504
521
. items ( & [ "mark regressed" , "mark baseline" , "retry" ] )
505
- . default ( 0 )
522
+ . default ( default_choice )
506
523
. interact ( )
507
524
. unwrap ( )
508
525
{
@@ -513,17 +530,120 @@ impl Toolchain {
513
530
}
514
531
}
515
532
} else {
516
- if self . run_test ( cfg) . success ( ) {
517
- TestOutcome :: Baseline
518
- } else {
519
- TestOutcome :: Regressed
520
- }
533
+ let output = self . run_test ( cfg) ;
534
+ cfg. default_outcome_of_output ( output)
521
535
} ;
522
536
523
537
outcome
524
538
}
539
+ }
540
+
541
+ impl Config {
542
+ fn default_outcome_of_output ( & self , output : process:: Output ) -> TestOutcome {
543
+ let status = output. status ;
544
+ let stdout_utf8 = String :: from_utf8_lossy ( & output. stdout ) . to_string ( ) ;
545
+ let stderr_utf8 = String :: from_utf8_lossy ( & output. stderr ) . to_string ( ) ;
546
+
547
+ debug ! ( "status: {:?} stdout: {:?} stderr: {:?}" , status, stdout_utf8, stderr_utf8) ;
548
+
549
+ let saw_ice = || -> bool {
550
+ stderr_utf8. contains ( "error: internal compiler error" )
551
+ } ;
552
+
553
+ let input = ( self . output_processing_mode ( ) , status. success ( ) ) ;
554
+ let result = match input {
555
+ ( OutputProcessingMode :: RegressOnErrorStatus , true ) => TestOutcome :: Baseline ,
556
+ ( OutputProcessingMode :: RegressOnErrorStatus , false ) => TestOutcome :: Regressed ,
557
+
558
+ ( OutputProcessingMode :: RegressOnSuccessStatus , true ) => TestOutcome :: Regressed ,
559
+ ( OutputProcessingMode :: RegressOnSuccessStatus , false ) => TestOutcome :: Baseline ,
560
+
561
+ ( OutputProcessingMode :: RegressOnIceAlone , _) => {
562
+ if saw_ice ( ) { TestOutcome :: Regressed } else { TestOutcome :: Baseline }
563
+ }
564
+
565
+ ( OutputProcessingMode :: RegressOnNonCleanError , true ) => TestOutcome :: Regressed ,
566
+ ( OutputProcessingMode :: RegressOnNonCleanError , false ) => {
567
+ if saw_ice ( ) { TestOutcome :: Regressed } else { TestOutcome :: Baseline }
568
+ }
569
+ } ;
570
+ debug ! ( "default_outcome_of_output: input: {:?} result: {:?}" , input, result) ;
571
+ result
572
+ }
525
573
526
- fn run_test ( & self , cfg : & Config ) -> process:: ExitStatus {
574
+ fn output_processing_mode ( & self ) -> OutputProcessingMode {
575
+ match self . args . regress . as_str ( ) {
576
+ "error" => OutputProcessingMode :: RegressOnErrorStatus ,
577
+ "non-error" => OutputProcessingMode :: RegressOnNonCleanError ,
578
+ "ice" => OutputProcessingMode :: RegressOnIceAlone ,
579
+ "success" => OutputProcessingMode :: RegressOnSuccessStatus ,
580
+ setting => panic ! ( "Unknown --regress setting: {:?}" , setting) ,
581
+ }
582
+ }
583
+ }
584
+
585
+ #[ derive( Copy , Clone , PartialEq , Eq , Debug , StructOpt ) ]
586
+ /// Customize what is treated as regression.
587
+ enum OutputProcessingMode {
588
+ /// `RegressOnErrorStatus`: Marks test outcome as `Regressed` if and only if
589
+ /// the `rustc` process reports a non-success status. This corresponds to
590
+ /// when `rustc` has an internal compiler error (ICE) or when it detects an
591
+ /// error in the input program.
592
+ ///
593
+ /// This covers the most common use case for `cargo-bisect-rustc` and is
594
+ /// thus the default setting.
595
+ ///
596
+ /// You explicitly opt into this seting via `--regress=error`.
597
+ RegressOnErrorStatus ,
598
+
599
+ /// `RegressOnSuccessStatus`: Marks test outcome as `Regressed` if and only
600
+ /// if the `rustc` process reports a success status. This corresponds to
601
+ /// when `rustc` believes it has successfully compiled the program. This
602
+ /// covers the use case for when you want to bisect to see when a bug was
603
+ /// fixed.
604
+ ///
605
+ /// You explicitly opt into this seting via `--regress=success`.
606
+ RegressOnSuccessStatus ,
607
+
608
+ /// `RegressOnIceAlone`: Marks test outcome as `Regressed` if and only if
609
+ /// the `rustc` process issues a diagnostic indicating that an internal
610
+ /// compiler error (ICE) occurred. This covers the use case for when you
611
+ /// want to bisect to see when an ICE was introduced pon a codebase that is
612
+ /// meant to produce a clean error.
613
+ ///
614
+ /// You explicitly opt into this seting via `--regress=ice`.
615
+ RegressOnIceAlone ,
616
+
617
+ /// `RegressOnNonCleanError`: Marks test outcome as `Baseline` if and only
618
+ /// if the `rustc` process reports error status and does not issue any
619
+ /// diagnostic indicating that an internal compiler error (ICE) occurred.
620
+ /// This is the use case if the regression is a case where an ill-formed
621
+ /// program has stopped being properly rejected by the compiler.
622
+ ///
623
+ /// (The main difference between this case and `RegressOnSuccessStatus` is
624
+ /// the handling of ICE: `RegressOnSuccessStatus` assumes that ICE should be
625
+ /// considered baseline; `RegressOnNonCleanError` assumes ICE should be
626
+ /// considered a sign of a regression.)
627
+ ///
628
+ /// You explicitly opt into this seting via `--regress=non-error`.
629
+ RegressOnNonCleanError ,
630
+
631
+ }
632
+
633
+ impl OutputProcessingMode {
634
+ fn must_process_stderr ( & self ) -> bool {
635
+ match self {
636
+ OutputProcessingMode :: RegressOnErrorStatus |
637
+ OutputProcessingMode :: RegressOnSuccessStatus => false ,
638
+
639
+ OutputProcessingMode :: RegressOnNonCleanError |
640
+ OutputProcessingMode :: RegressOnIceAlone => true ,
641
+ }
642
+ }
643
+ }
644
+
645
+ impl Toolchain {
646
+ fn run_test ( & self , cfg : & Config ) -> process:: Output {
527
647
if !cfg. args . preserve_target {
528
648
let _ = fs:: remove_dir_all (
529
649
cfg. args
@@ -550,21 +670,35 @@ impl Toolchain {
550
670
} ;
551
671
cmd. current_dir ( & cfg. args . test_dir ) ;
552
672
cmd. env ( "CARGO_TARGET_DIR" , format ! ( "target-{}" , self . rustup_name( ) ) ) ;
553
- if cfg. args . emit_cargo_output ( ) || cfg. args . prompt {
554
- cmd. stdout ( Stdio :: inherit ( ) ) ;
555
- cmd. stderr ( Stdio :: inherit ( ) ) ;
673
+
674
+ // let `cmd` capture stderr for us to process afterward.
675
+ let must_capture_output = cfg. output_processing_mode ( ) . must_process_stderr ( ) ;
676
+ let emit_output = cfg. args . emit_cargo_output ( ) || cfg. args . prompt ;
677
+
678
+ let default_stdio = || if must_capture_output {
679
+ Stdio :: piped ( )
680
+ } else if emit_output {
681
+ Stdio :: inherit ( )
556
682
} else {
557
- cmd. stdout ( Stdio :: null ( ) ) ;
558
- cmd. stderr ( Stdio :: null ( ) ) ;
559
- }
560
- let status = match cmd. status ( ) {
561
- Ok ( status) => status,
683
+ Stdio :: null ( )
684
+ } ;
685
+
686
+ cmd. stdout ( default_stdio ( ) ) ;
687
+ cmd. stderr ( default_stdio ( ) ) ;
688
+
689
+ let output = match cmd. output ( ) {
690
+ Ok ( output) => output,
562
691
Err ( err) => {
563
692
panic ! ( "failed to run {:?}: {:?}" , cmd, err) ;
564
693
}
565
694
} ;
566
695
567
- status
696
+ // if we captured the stdout above but still need to emit it, then do so now
697
+ if must_capture_output && emit_output {
698
+ io:: stdout ( ) . write_all ( & output. stdout ) . unwrap ( ) ;
699
+ io:: stderr ( ) . write_all ( & output. stderr ) . unwrap ( ) ;
700
+ }
701
+ output
568
702
}
569
703
570
704
fn install ( & self , client : & Client , dl_params : & DownloadParams ) -> Result < ( ) , InstallError > {
0 commit comments