1
1
use std:: env;
2
2
use std:: ffi:: OsString ;
3
3
use std:: fs:: { self , File } ;
4
- use std:: io:: { self , BufRead , BufReader , BufWriter , Write } ;
4
+ use std:: io:: { self , BufRead , BufReader , BufWriter , Read , Write } ;
5
5
use std:: ops:: Not ;
6
6
use std:: path:: { Path , PathBuf } ;
7
7
use std:: process:: Command ;
@@ -45,6 +45,8 @@ struct CrateRunInfo {
45
45
env : Vec < ( OsString , OsString ) > ,
46
46
/// The current working directory.
47
47
current_dir : OsString ,
48
+ /// The contents passed via standard input.
49
+ stdin : Vec < u8 > ,
48
50
}
49
51
50
52
impl CrateRunInfo {
@@ -53,7 +55,13 @@ impl CrateRunInfo {
53
55
let args = args. collect ( ) ;
54
56
let env = env:: vars_os ( ) . collect ( ) ;
55
57
let current_dir = env:: current_dir ( ) . unwrap ( ) . into_os_string ( ) ;
56
- CrateRunInfo { args, env, current_dir }
58
+
59
+ let mut stdin = Vec :: new ( ) ;
60
+ if env:: var_os ( "MIRI_CALLED_FROM_RUSTDOC" ) . is_some ( ) {
61
+ std:: io:: stdin ( ) . lock ( ) . read_to_end ( & mut stdin) . expect ( "cannot read stdin" ) ;
62
+ }
63
+
64
+ CrateRunInfo { args, env, current_dir, stdin }
57
65
}
58
66
59
67
fn store ( & self , filename : & Path ) {
@@ -539,17 +547,22 @@ fn phase_cargo_rustc(args: env::Args) {
539
547
}
540
548
541
549
fn out_filename ( prefix : & str , suffix : & str ) -> PathBuf {
542
- let mut path = PathBuf :: from ( get_arg_flag_value ( "--out-dir" ) . unwrap ( ) ) ;
543
- path. push ( format ! (
544
- "{}{}{}{}" ,
545
- prefix,
546
- get_arg_flag_value( "--crate-name" ) . unwrap( ) ,
547
- // This is technically a `-C` flag but the prefix seems unique enough...
548
- // (and cargo passes this before the filename so it should be unique)
549
- get_arg_flag_value( "extra-filename" ) . unwrap_or( String :: new( ) ) ,
550
- suffix,
551
- ) ) ;
552
- path
550
+ if let Some ( out_dir) = get_arg_flag_value ( "--out-dir" ) {
551
+ let mut path = PathBuf :: from ( out_dir) ;
552
+ path. push ( format ! (
553
+ "{}{}{}{}" ,
554
+ prefix,
555
+ get_arg_flag_value( "--crate-name" ) . unwrap( ) ,
556
+ // This is technically a `-C` flag but the prefix seems unique enough...
557
+ // (and cargo passes this before the filename so it should be unique)
558
+ get_arg_flag_value( "extra-filename" ) . unwrap_or( String :: new( ) ) ,
559
+ suffix,
560
+ ) ) ;
561
+ path
562
+ } else {
563
+ let out_file = get_arg_flag_value ( "-o" ) . unwrap ( ) ;
564
+ PathBuf :: from ( out_file)
565
+ }
553
566
}
554
567
555
568
let verbose = std:: env:: var_os ( "MIRI_VERBOSE" ) . is_some ( ) ;
@@ -734,6 +747,44 @@ fn phase_cargo_runner(binary: &Path, binary_args: env::Args) {
734
747
if verbose {
735
748
eprintln ! ( "[cargo-miri runner] {:?}" , cmd) ;
736
749
}
750
+
751
+ cmd. stdin ( std:: process:: Stdio :: piped ( ) ) ;
752
+ let mut child = cmd. spawn ( ) . expect ( "failed to spawn miri process" ) ;
753
+ {
754
+ let stdin = child. stdin . as_mut ( ) . expect ( "failed to open stdin" ) ;
755
+ stdin. write_all ( & info. stdin ) . expect ( "failed to write out test source" ) ;
756
+ }
757
+ let exit_status = child. wait ( ) . expect ( "failed to run command" ) ;
758
+ if exit_status. success ( ) . not ( ) {
759
+ std:: process:: exit ( exit_status. code ( ) . unwrap_or ( -1 ) )
760
+ }
761
+ }
762
+
763
+ fn phase_cargo_rustdoc ( fst_arg : & str , args : env:: Args ) {
764
+ let verbose = std:: env:: var_os ( "MIRI_VERBOSE" ) . is_some ( ) ;
765
+
766
+ // phase_cargo_miri sets the RUSTDOC env var to ourselves, so we can't use that here;
767
+ // just default to a straight-forward invocation for now:
768
+ let mut cmd = Command :: new ( OsString :: from ( "rustdoc" ) ) ;
769
+
770
+ // just pass everything through until we find a reason not to do that:
771
+ cmd. arg ( fst_arg) ;
772
+ cmd. args ( args) ;
773
+
774
+ cmd. arg ( "-Z" ) . arg ( "unstable-options" ) ;
775
+
776
+ let cargo_miri_path = std:: env:: current_exe ( ) . expect ( "current executable path invalid" ) ;
777
+ cmd. arg ( "--test-builder" ) . arg ( & cargo_miri_path) ;
778
+ cmd. arg ( "--runtool" ) . arg ( & cargo_miri_path) ;
779
+
780
+ // rustdoc passes generated code to rustc via stdin, rather than a temporary file,
781
+ // so we need to let the coming invocations know to expect that
782
+ cmd. env ( "MIRI_CALLED_FROM_RUSTDOC" , "1" ) ;
783
+
784
+ if verbose {
785
+ eprintln ! ( "[cargo-miri rustdoc] {:?}" , cmd) ;
786
+ }
787
+
737
788
exec ( cmd)
738
789
}
739
790
@@ -750,6 +801,30 @@ fn main() {
750
801
return ;
751
802
}
752
803
804
+ // The way rustdoc invokes rustc is indistuingishable from the way cargo invokes rustdoc
805
+ // by the arguments alone, and we can't take from the args iterator in this case.
806
+ // phase_cargo_rustdoc sets this environment variable to let us disambiguate here
807
+ let invoked_as_rustc_from_rustdoc = env:: var_os ( "MIRI_CALLED_FROM_RUSTDOC" ) . is_some ( ) ;
808
+ if invoked_as_rustc_from_rustdoc {
809
+ // ...however, we then also see this variable when rustdoc invokes us as the testrunner!
810
+ // The runner is invoked as `$runtool ($runtool-arg)* output_file;
811
+ // since we don't specify any runtool-args, and rustdoc supplies multiple arguments to
812
+ // the test-builder unconditionally, we can just check the number of remaining arguments:
813
+ if args. len ( ) == 1 {
814
+ let arg = args. next ( ) . unwrap ( ) ;
815
+ let binary = Path :: new ( & arg) ;
816
+ if binary. exists ( ) {
817
+ phase_cargo_runner ( binary, args) ;
818
+ } else {
819
+ show_error ( format ! ( "`cargo-miri` called with non-existing path argument `{}`; please invoke this binary through `cargo miri`" , arg) ) ;
820
+ }
821
+ } else {
822
+ phase_cargo_rustc ( args) ;
823
+ }
824
+
825
+ return ;
826
+ }
827
+
753
828
// Dispatch to `cargo-miri` phase. There are three phases:
754
829
// - When we are called via `cargo miri`, we run as the frontend and invoke the underlying
755
830
// cargo. We set RUSTC_WRAPPER and CARGO_TARGET_RUNNER to ourselves.
@@ -762,16 +837,15 @@ fn main() {
762
837
Some ( "miri" ) => phase_cargo_miri ( args) ,
763
838
Some ( "rustc" ) => phase_cargo_rustc ( args) ,
764
839
Some ( arg) => {
765
- // We have to distinguish the "runner" and "rustfmt " cases.
840
+ // We have to distinguish the "runner" and "rustdoc " cases.
766
841
// As runner, the first argument is the binary (a file that should exist, with an absolute path);
767
- // as rustfmt , the first argument is a flag (`--something`).
842
+ // as rustdoc , the first argument is a flag (`--something`).
768
843
let binary = Path :: new ( arg) ;
769
844
if binary. exists ( ) {
770
845
assert ! ( !arg. starts_with( "--" ) ) ; // not a flag
771
846
phase_cargo_runner ( binary, args) ;
772
847
} else if arg. starts_with ( "--" ) {
773
- // We are rustdoc.
774
- eprintln ! ( "Running doctests is not currently supported by Miri." )
848
+ phase_cargo_rustdoc ( arg, args) ;
775
849
} else {
776
850
show_error ( format ! ( "`cargo-miri` called with unexpected first argument `{}`; please only invoke this binary through `cargo miri`" , arg) ) ;
777
851
}
0 commit comments