@@ -20,9 +20,9 @@ use rustc_span::source_map::SourceMap;
20
20
use rustc_span:: symbol:: sym;
21
21
use rustc_span:: { BytePos , FileName , Pos , Span , DUMMY_SP } ;
22
22
use rustc_target:: spec:: { Target , TargetTriple } ;
23
- use tempfile:: Builder as TempFileBuilder ;
24
23
25
24
use std:: env;
25
+ use std:: fs:: File ;
26
26
use std:: io:: { self , Write } ;
27
27
use std:: panic;
28
28
use std:: path:: { Path , PathBuf } ;
@@ -31,6 +31,8 @@ use std::str;
31
31
use std:: sync:: atomic:: { AtomicUsize , Ordering } ;
32
32
use std:: sync:: { Arc , Mutex } ;
33
33
34
+ use tempfile:: { Builder as TempFileBuilder , TempDir } ;
35
+
34
36
use crate :: clean:: { types:: AttributesExt , Attributes } ;
35
37
use crate :: config:: Options as RustdocOptions ;
36
38
use crate :: html:: markdown:: { self , ErrorCodes , Ignore , LangString } ;
@@ -48,7 +50,55 @@ pub(crate) struct GlobalTestOptions {
48
50
pub ( crate ) attrs : Vec < String > ,
49
51
}
50
52
51
- pub ( crate ) fn run ( options : RustdocOptions ) -> Result < ( ) , ErrorGuaranteed > {
53
+ pub ( crate ) fn generate_args_file ( file_path : & Path , options : & RustdocOptions ) -> Result < ( ) , String > {
54
+ let mut file = File :: create ( file_path)
55
+ . map_err ( |error| format ! ( "failed to create args file: {error:?}" ) ) ?;
56
+
57
+ // We now put the common arguments into the file we created.
58
+ let mut content = vec ! [ "--crate-type=bin" . to_string( ) ] ;
59
+
60
+ for cfg in & options. cfgs {
61
+ content. push ( format ! ( "--cfg={cfg}" ) ) ;
62
+ }
63
+ if !options. check_cfgs . is_empty ( ) {
64
+ content. push ( "-Zunstable-options" . to_string ( ) ) ;
65
+ for check_cfg in & options. check_cfgs {
66
+ content. push ( format ! ( "--check-cfg={check_cfg}" ) ) ;
67
+ }
68
+ }
69
+
70
+ if let Some ( sysroot) = & options. maybe_sysroot {
71
+ content. push ( format ! ( "--sysroot={}" , sysroot. display( ) ) ) ;
72
+ }
73
+ for lib_str in & options. lib_strs {
74
+ content. push ( format ! ( "-L{lib_str}" ) ) ;
75
+ }
76
+ for extern_str in & options. extern_strs {
77
+ content. push ( format ! ( "--extern={extern_str}" ) ) ;
78
+ }
79
+ content. push ( "-Ccodegen-units=1" . to_string ( ) ) ;
80
+ for codegen_options_str in & options. codegen_options_strs {
81
+ content. push ( format ! ( "-C{codegen_options_str}" ) ) ;
82
+ }
83
+ for unstable_option_str in & options. unstable_opts_strs {
84
+ content. push ( format ! ( "-Z{unstable_option_str}" ) ) ;
85
+ }
86
+
87
+ let content = content. join ( "\n " ) ;
88
+
89
+ file. write ( content. as_bytes ( ) )
90
+ . map_err ( |error| format ! ( "failed to write arguments to temporary file: {error:?}" ) ) ?;
91
+ Ok ( ( ) )
92
+ }
93
+
94
+ fn get_doctest_dir ( ) -> io:: Result < TempDir > {
95
+ TempFileBuilder :: new ( ) . prefix ( "rustdoctest" ) . tempdir ( )
96
+ }
97
+
98
+ pub ( crate ) fn run (
99
+ dcx : & rustc_errors:: DiagCtxt ,
100
+ options : RustdocOptions ,
101
+ ) -> Result < ( ) , ErrorGuaranteed > {
52
102
let input = config:: Input :: File ( options. input . clone ( ) ) ;
53
103
54
104
let invalid_codeblock_attributes_name = crate :: lint:: INVALID_CODEBLOCK_ATTRIBUTES . name ;
@@ -118,6 +168,15 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> {
118
168
let externs = options. externs . clone ( ) ;
119
169
let json_unused_externs = options. json_unused_externs ;
120
170
171
+ let temp_dir = match get_doctest_dir ( )
172
+ . map_err ( |error| format ! ( "failed to create temporary directory: {error:?}" ) )
173
+ {
174
+ Ok ( temp_dir) => temp_dir,
175
+ Err ( error) => return crate :: wrap_return ( dcx, Err ( error) ) ,
176
+ } ;
177
+ let file_path = temp_dir. path ( ) . join ( "rustdoc-cfgs" ) ;
178
+ crate :: wrap_return ( dcx, generate_args_file ( & file_path, & options) ) ?;
179
+
121
180
let ( tests, unused_extern_reports, compiling_test_count) =
122
181
interface:: run_compiler ( config, |compiler| {
123
182
compiler. enter ( |queries| {
@@ -134,6 +193,7 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> {
134
193
Some ( compiler. sess . psess . clone_source_map ( ) ) ,
135
194
None ,
136
195
enable_per_target_ignores,
196
+ file_path,
137
197
) ;
138
198
139
199
let mut hir_collector = HirCollector {
@@ -322,74 +382,53 @@ fn run_test(
322
382
test : & str ,
323
383
crate_name : & str ,
324
384
line : usize ,
325
- rustdoc_options : RustdocOptions ,
385
+ rustdoc_options : IndividualTestOptions ,
326
386
mut lang_string : LangString ,
327
387
no_run : bool ,
328
- runtool : Option < String > ,
329
- runtool_args : Vec < String > ,
330
- target : TargetTriple ,
331
388
opts : & GlobalTestOptions ,
332
389
edition : Edition ,
333
- outdir : DirState ,
334
390
path : PathBuf ,
335
- test_id : & str ,
336
391
report_unused_externs : impl Fn ( UnusedExterns ) ,
337
392
) -> Result < ( ) , TestFailure > {
338
- let ( test, line_offset, supports_color) =
339
- make_test ( test, Some ( crate_name) , lang_string. test_harness , opts, edition, Some ( test_id) ) ;
393
+ let ( test, line_offset, supports_color) = make_test (
394
+ test,
395
+ Some ( crate_name) ,
396
+ lang_string. test_harness ,
397
+ opts,
398
+ edition,
399
+ Some ( & rustdoc_options. test_id ) ,
400
+ ) ;
340
401
341
402
// Make sure we emit well-formed executable names for our target.
342
- let rust_out = add_exe_suffix ( "rust_out" . to_owned ( ) , & target) ;
343
- let output_file = outdir. path ( ) . join ( rust_out) ;
403
+ let rust_out = add_exe_suffix ( "rust_out" . to_owned ( ) , & rustdoc_options . target ) ;
404
+ let output_file = rustdoc_options . outdir . path ( ) . join ( rust_out) ;
344
405
345
406
let rustc_binary = rustdoc_options
346
407
. test_builder
347
408
. as_deref ( )
348
409
. unwrap_or_else ( || rustc_interface:: util:: rustc_path ( ) . expect ( "found rustc" ) ) ;
349
410
let mut compiler = wrapped_rustc_command ( & rustdoc_options. test_builder_wrappers , rustc_binary) ;
350
- compiler. arg ( "--crate-type" ) . arg ( "bin" ) ;
351
- for cfg in & rustdoc_options. cfgs {
352
- compiler. arg ( "--cfg" ) . arg ( & cfg) ;
353
- }
354
- if !rustdoc_options. check_cfgs . is_empty ( ) {
355
- compiler. arg ( "-Z" ) . arg ( "unstable-options" ) ;
356
- for check_cfg in & rustdoc_options. check_cfgs {
357
- compiler. arg ( "--check-cfg" ) . arg ( & check_cfg) ;
358
- }
359
- }
360
- if let Some ( sysroot) = rustdoc_options. maybe_sysroot {
361
- compiler. arg ( "--sysroot" ) . arg ( sysroot) ;
362
- }
411
+
412
+ compiler. arg ( & format ! ( "@{}" , rustdoc_options. arg_file. display( ) ) ) ;
413
+
363
414
compiler. arg ( "--edition" ) . arg ( & edition. to_string ( ) ) ;
364
415
compiler. env ( "UNSTABLE_RUSTDOC_TEST_PATH" , path) ;
365
416
compiler. env ( "UNSTABLE_RUSTDOC_TEST_LINE" , format ! ( "{}" , line as isize - line_offset as isize ) ) ;
366
417
compiler. arg ( "-o" ) . arg ( & output_file) ;
367
418
if lang_string. test_harness {
368
419
compiler. arg ( "--test" ) ;
369
420
}
370
- if rustdoc_options. json_unused_externs . is_enabled ( ) && !lang_string. compile_fail {
421
+ if rustdoc_options. is_json_unused_externs_enabled && !lang_string. compile_fail {
371
422
compiler. arg ( "--error-format=json" ) ;
372
423
compiler. arg ( "--json" ) . arg ( "unused-externs" ) ;
373
- compiler. arg ( "-Z" ) . arg ( "unstable-options" ) ;
374
424
compiler. arg ( "-W" ) . arg ( "unused_crate_dependencies" ) ;
425
+ compiler. arg ( "-Z" ) . arg ( "unstable-options" ) ;
375
426
}
376
- for lib_str in & rustdoc_options. lib_strs {
377
- compiler. arg ( "-L" ) . arg ( & lib_str) ;
378
- }
379
- for extern_str in & rustdoc_options. extern_strs {
380
- compiler. arg ( "--extern" ) . arg ( & extern_str) ;
381
- }
382
- compiler. arg ( "-Ccodegen-units=1" ) ;
383
- for codegen_options_str in & rustdoc_options. codegen_options_strs {
384
- compiler. arg ( "-C" ) . arg ( & codegen_options_str) ;
385
- }
386
- for unstable_option_str in & rustdoc_options. unstable_opts_strs {
387
- compiler. arg ( "-Z" ) . arg ( & unstable_option_str) ;
388
- }
389
- if no_run && !lang_string. compile_fail && rustdoc_options. persist_doctests . is_none ( ) {
427
+
428
+ if no_run && !lang_string. compile_fail && rustdoc_options. should_persist_doctests {
390
429
compiler. arg ( "--emit=metadata" ) ;
391
430
}
392
- compiler. arg ( "--target" ) . arg ( match target {
431
+ compiler. arg ( "--target" ) . arg ( match rustdoc_options . target {
393
432
TargetTriple :: TargetTriple ( s) => s,
394
433
TargetTriple :: TargetJson { path_for_rustdoc, .. } => {
395
434
path_for_rustdoc. to_str ( ) . expect ( "target path must be valid unicode" ) . to_string ( )
@@ -485,10 +524,10 @@ fn run_test(
485
524
let mut cmd;
486
525
487
526
let output_file = make_maybe_absolute_path ( output_file) ;
488
- if let Some ( tool) = runtool {
527
+ if let Some ( tool) = rustdoc_options . runtool {
489
528
let tool = make_maybe_absolute_path ( tool. into ( ) ) ;
490
529
cmd = Command :: new ( tool) ;
491
- cmd. args ( runtool_args) ;
530
+ cmd. args ( rustdoc_options . runtool_args ) ;
492
531
cmd. arg ( output_file) ;
493
532
} else {
494
533
cmd = Command :: new ( output_file) ;
@@ -897,6 +936,56 @@ fn partition_source(s: &str, edition: Edition) -> (String, String, String) {
897
936
( before, after, crates)
898
937
}
899
938
939
+ pub ( crate ) struct IndividualTestOptions {
940
+ test_builder : Option < PathBuf > ,
941
+ test_builder_wrappers : Vec < PathBuf > ,
942
+ is_json_unused_externs_enabled : bool ,
943
+ should_persist_doctests : bool ,
944
+ error_format : ErrorOutputType ,
945
+ test_run_directory : Option < PathBuf > ,
946
+ nocapture : bool ,
947
+ arg_file : PathBuf ,
948
+ outdir : DirState ,
949
+ runtool : Option < String > ,
950
+ runtool_args : Vec < String > ,
951
+ target : TargetTriple ,
952
+ test_id : String ,
953
+ }
954
+
955
+ impl IndividualTestOptions {
956
+ fn new ( options : & RustdocOptions , arg_file : & Path , test_id : String ) -> Self {
957
+ let outdir = if let Some ( ref path) = options. persist_doctests {
958
+ let mut path = path. clone ( ) ;
959
+ path. push ( & test_id) ;
960
+
961
+ if let Err ( err) = std:: fs:: create_dir_all ( & path) {
962
+ eprintln ! ( "Couldn't create directory for doctest executables: {err}" ) ;
963
+ panic:: resume_unwind ( Box :: new ( ( ) ) ) ;
964
+ }
965
+
966
+ DirState :: Perm ( path)
967
+ } else {
968
+ DirState :: Temp ( get_doctest_dir ( ) . expect ( "rustdoc needs a tempdir" ) )
969
+ } ;
970
+
971
+ Self {
972
+ test_builder : options. test_builder . clone ( ) ,
973
+ test_builder_wrappers : options. test_builder_wrappers . clone ( ) ,
974
+ is_json_unused_externs_enabled : options. json_unused_externs . is_enabled ( ) ,
975
+ should_persist_doctests : options. persist_doctests . is_none ( ) ,
976
+ error_format : options. error_format ,
977
+ test_run_directory : options. test_run_directory . clone ( ) ,
978
+ nocapture : options. nocapture ,
979
+ arg_file : arg_file. into ( ) ,
980
+ outdir,
981
+ runtool : options. runtool . clone ( ) ,
982
+ runtool_args : options. runtool_args . clone ( ) ,
983
+ target : options. target . clone ( ) ,
984
+ test_id,
985
+ }
986
+ }
987
+ }
988
+
900
989
pub ( crate ) trait Tester {
901
990
fn add_test ( & mut self , test : String , config : LangString , line : usize ) ;
902
991
fn get_line ( & self ) -> usize {
@@ -941,6 +1030,7 @@ pub(crate) struct Collector {
941
1030
visited_tests : FxHashMap < ( String , usize ) , usize > ,
942
1031
unused_extern_reports : Arc < Mutex < Vec < UnusedExterns > > > ,
943
1032
compiling_test_count : AtomicUsize ,
1033
+ arg_file : PathBuf ,
944
1034
}
945
1035
946
1036
impl Collector {
@@ -952,6 +1042,7 @@ impl Collector {
952
1042
source_map : Option < Lrc < SourceMap > > ,
953
1043
filename : Option < PathBuf > ,
954
1044
enable_per_target_ignores : bool ,
1045
+ arg_file : PathBuf ,
955
1046
) -> Collector {
956
1047
Collector {
957
1048
tests : Vec :: new ( ) ,
@@ -967,6 +1058,7 @@ impl Collector {
967
1058
visited_tests : FxHashMap :: default ( ) ,
968
1059
unused_extern_reports : Default :: default ( ) ,
969
1060
compiling_test_count : AtomicUsize :: new ( 0 ) ,
1061
+ arg_file,
970
1062
}
971
1063
}
972
1064
@@ -1009,13 +1101,9 @@ impl Tester for Collector {
1009
1101
let crate_name = self . crate_name . clone ( ) ;
1010
1102
let opts = self . opts . clone ( ) ;
1011
1103
let edition = config. edition . unwrap_or ( self . rustdoc_options . edition ) ;
1012
- let rustdoc_options = self . rustdoc_options . clone ( ) ;
1013
- let runtool = self . rustdoc_options . runtool . clone ( ) ;
1014
- let runtool_args = self . rustdoc_options . runtool_args . clone ( ) ;
1015
- let target = self . rustdoc_options . target . clone ( ) ;
1016
- let target_str = target. to_string ( ) ;
1104
+ let target_str = self . rustdoc_options . target . to_string ( ) ;
1017
1105
let unused_externs = self . unused_extern_reports . clone ( ) ;
1018
- let no_run = config. no_run || rustdoc_options. no_run ;
1106
+ let no_run = config. no_run || self . rustdoc_options . no_run ;
1019
1107
if !config. compile_fail {
1020
1108
self . compiling_test_count . fetch_add ( 1 , Ordering :: SeqCst ) ;
1021
1109
}
@@ -1049,23 +1137,9 @@ impl Tester for Collector {
1049
1137
self . visited_tests. entry( ( file. clone( ) , line) ) . and_modify( |v| * v += 1 ) . or_insert( 0 )
1050
1138
} ,
1051
1139
) ;
1052
- let outdir = if let Some ( mut path) = rustdoc_options. persist_doctests . clone ( ) {
1053
- path. push ( & test_id) ;
1054
1140
1055
- if let Err ( err) = std:: fs:: create_dir_all ( & path) {
1056
- eprintln ! ( "Couldn't create directory for doctest executables: {err}" ) ;
1057
- panic:: resume_unwind ( Box :: new ( ( ) ) ) ;
1058
- }
1059
-
1060
- DirState :: Perm ( path)
1061
- } else {
1062
- DirState :: Temp (
1063
- TempFileBuilder :: new ( )
1064
- . prefix ( "rustdoctest" )
1065
- . tempdir ( )
1066
- . expect ( "rustdoc needs a tempdir" ) ,
1067
- )
1068
- } ;
1141
+ let rustdoc_test_options =
1142
+ IndividualTestOptions :: new ( & self . rustdoc_options , & self . arg_file , test_id) ;
1069
1143
1070
1144
debug ! ( "creating test {name}: {test}" ) ;
1071
1145
self . tests . push ( test:: TestDescAndFn {
@@ -1096,17 +1170,12 @@ impl Tester for Collector {
1096
1170
& test,
1097
1171
& crate_name,
1098
1172
line,
1099
- rustdoc_options ,
1173
+ rustdoc_test_options ,
1100
1174
config,
1101
1175
no_run,
1102
- runtool,
1103
- runtool_args,
1104
- target,
1105
1176
& opts,
1106
1177
edition,
1107
- outdir,
1108
1178
path,
1109
- & test_id,
1110
1179
report_unused_externs,
1111
1180
) ;
1112
1181
0 commit comments