Skip to content

Commit 6c2e8dc

Browse files
rustdoc --test: Prevent reaching the maximum size of command-line by using files for arguments if there are too many
1 parent 2627e9f commit 6c2e8dc

File tree

3 files changed

+82
-31
lines changed

3 files changed

+82
-31
lines changed

src/librustdoc/doctest.rs

Lines changed: 70 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use rustc_target::spec::{Target, TargetTriple};
2323
use tempfile::Builder as TempFileBuilder;
2424

2525
use std::env;
26+
use std::fs::File;
2627
use std::io::{self, Write};
2728
use std::panic;
2829
use std::path::{Path, PathBuf};
@@ -31,6 +32,8 @@ use std::str;
3132
use std::sync::atomic::{AtomicUsize, Ordering};
3233
use std::sync::{Arc, Mutex};
3334

35+
use tempfile::tempdir;
36+
3437
use crate::clean::{types::AttributesExt, Attributes};
3538
use crate::config::Options as RustdocOptions;
3639
use crate::html::markdown::{self, ErrorCodes, Ignore, LangString};
@@ -48,7 +51,51 @@ pub(crate) struct GlobalTestOptions {
4851
pub(crate) attrs: Vec<String>,
4952
}
5053

51-
pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> {
54+
pub(crate) fn generate_args_file(file_path: &Path, options: &RustdocOptions) -> Result<(), String> {
55+
let mut file = File::create(file_path)
56+
.map_err(|error| format!("failed to create args file: {error:?}"))?;
57+
58+
// We now put the common arguments into the file we created.
59+
let mut content = vec!["--crate-type=bin".to_string()];
60+
61+
for cfg in &options.cfgs {
62+
content.push(format!("--cfg={cfg}"));
63+
}
64+
if !options.check_cfgs.is_empty() {
65+
content.push("-Zunstable-options".to_string());
66+
for check_cfg in &options.check_cfgs {
67+
content.push(format!("--check-cfg={check_cfg}"));
68+
}
69+
}
70+
71+
if let Some(sysroot) = &options.maybe_sysroot {
72+
content.push(format!("--sysroot={}", sysroot.display()));
73+
}
74+
for lib_str in &options.lib_strs {
75+
content.push(format!("-L{lib_str}"));
76+
}
77+
for extern_str in &options.extern_strs {
78+
content.push(format!("--extern={extern_str}"));
79+
}
80+
content.push("-Ccodegen-units=1".to_string());
81+
for codegen_options_str in &options.codegen_options_strs {
82+
content.push(format!("-C{codegen_options_str}"));
83+
}
84+
for unstable_option_str in &options.unstable_opts_strs {
85+
content.push(format!("-Z{unstable_option_str}"));
86+
}
87+
88+
let content = content.join("\n");
89+
90+
file.write(content.as_bytes())
91+
.map_err(|error| format!("failed to write arguments to temporary file: {error:?}"))?;
92+
Ok(())
93+
}
94+
95+
pub(crate) fn run(
96+
dcx: &rustc_errors::DiagCtxt,
97+
options: RustdocOptions,
98+
) -> Result<(), ErrorGuaranteed> {
5299
let input = config::Input::File(options.input.clone());
53100

54101
let invalid_codeblock_attributes_name = crate::lint::INVALID_CODEBLOCK_ATTRIBUTES.name;
@@ -118,6 +165,15 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> {
118165
let externs = options.externs.clone();
119166
let json_unused_externs = options.json_unused_externs;
120167

168+
let temp_dir = match tempdir()
169+
.map_err(|error| format!("failed to create temporary directory: {error:?}"))
170+
{
171+
Ok(temp_dir) => temp_dir,
172+
Err(error) => return crate::wrap_return(dcx, Err(error)),
173+
};
174+
let file_path = temp_dir.path().join("rustdoc-cfgs");
175+
crate::wrap_return(dcx, generate_args_file(&file_path, &options))?;
176+
121177
let (tests, unused_extern_reports, compiling_test_count) =
122178
interface::run_compiler(config, |compiler| {
123179
compiler.enter(|queries| {
@@ -134,6 +190,7 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> {
134190
Some(compiler.sess.psess.clone_source_map()),
135191
None,
136192
enable_per_target_ignores,
193+
file_path,
137194
);
138195

139196
let mut hir_collector = HirCollector {
@@ -334,6 +391,7 @@ fn run_test(
334391
path: PathBuf,
335392
test_id: &str,
336393
report_unused_externs: impl Fn(UnusedExterns),
394+
arg_file: PathBuf,
337395
) -> Result<(), TestFailure> {
338396
let (test, line_offset, supports_color) =
339397
make_test(test, Some(crate_name), lang_string.test_harness, opts, edition, Some(test_id));
@@ -347,19 +405,9 @@ fn run_test(
347405
.as_deref()
348406
.unwrap_or_else(|| rustc_interface::util::rustc_path().expect("found rustc"));
349407
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-
}
408+
409+
compiler.arg(&format!("@{}", arg_file.display()));
410+
363411
compiler.arg("--edition").arg(&edition.to_string());
364412
compiler.env("UNSTABLE_RUSTDOC_TEST_PATH", path);
365413
compiler.env("UNSTABLE_RUSTDOC_TEST_LINE", format!("{}", line as isize - line_offset as isize));
@@ -370,22 +418,10 @@ fn run_test(
370418
if rustdoc_options.json_unused_externs.is_enabled() && !lang_string.compile_fail {
371419
compiler.arg("--error-format=json");
372420
compiler.arg("--json").arg("unused-externs");
373-
compiler.arg("-Z").arg("unstable-options");
374421
compiler.arg("-W").arg("unused_crate_dependencies");
422+
compiler.arg("-Z").arg("unstable-options");
375423
}
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-
}
424+
389425
if no_run && !lang_string.compile_fail && rustdoc_options.persist_doctests.is_none() {
390426
compiler.arg("--emit=metadata");
391427
}
@@ -941,6 +977,7 @@ pub(crate) struct Collector {
941977
visited_tests: FxHashMap<(String, usize), usize>,
942978
unused_extern_reports: Arc<Mutex<Vec<UnusedExterns>>>,
943979
compiling_test_count: AtomicUsize,
980+
arg_file: PathBuf,
944981
}
945982

946983
impl Collector {
@@ -952,6 +989,7 @@ impl Collector {
952989
source_map: Option<Lrc<SourceMap>>,
953990
filename: Option<PathBuf>,
954991
enable_per_target_ignores: bool,
992+
arg_file: PathBuf,
955993
) -> Collector {
956994
Collector {
957995
tests: Vec::new(),
@@ -967,6 +1005,7 @@ impl Collector {
9671005
visited_tests: FxHashMap::default(),
9681006
unused_extern_reports: Default::default(),
9691007
compiling_test_count: AtomicUsize::new(0),
1008+
arg_file,
9701009
}
9711010
}
9721011

@@ -1067,6 +1106,8 @@ impl Tester for Collector {
10671106
)
10681107
};
10691108

1109+
let arg_file = self.arg_file.clone();
1110+
10701111
debug!("creating test {name}: {test}");
10711112
self.tests.push(test::TestDescAndFn {
10721113
desc: test::TestDesc {
@@ -1108,6 +1149,7 @@ impl Tester for Collector {
11081149
path,
11091150
&test_id,
11101151
report_unused_externs,
1152+
arg_file,
11111153
);
11121154

11131155
if let Err(err) = res {

src/librustdoc/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -664,7 +664,7 @@ fn usage(argv0: &str) {
664664
/// A result type used by several functions under `main()`.
665665
type MainResult = Result<(), ErrorGuaranteed>;
666666

667-
fn wrap_return(dcx: &rustc_errors::DiagCtxt, res: Result<(), String>) -> MainResult {
667+
pub(crate) fn wrap_return(dcx: &rustc_errors::DiagCtxt, res: Result<(), String>) -> MainResult {
668668
match res {
669669
Ok(()) => dcx.has_errors().map_or(Ok(()), Err),
670670
Err(err) => Err(dcx.err(err)),
@@ -731,7 +731,7 @@ fn main_args(
731731

732732
match (options.should_test, options.markdown_input()) {
733733
(true, true) => return wrap_return(&diag, markdown::test(options)),
734-
(true, false) => return doctest::run(options),
734+
(true, false) => return doctest::run(&diag, options),
735735
(false, true) => {
736736
let input = options.input.clone();
737737
let edition = options.edition;

src/librustdoc/markdown.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ use std::fs::{create_dir_all, read_to_string, File};
33
use std::io::prelude::*;
44
use std::path::Path;
55

6+
use tempfile::tempdir;
7+
68
use rustc_span::edition::Edition;
79
use rustc_span::DUMMY_SP;
810

911
use crate::config::{Options, RenderOptions};
10-
use crate::doctest::{Collector, GlobalTestOptions};
12+
use crate::doctest::{generate_args_file, Collector, GlobalTestOptions};
1113
use crate::html::escape::Escape;
1214
use crate::html::markdown;
1315
use crate::html::markdown::{
@@ -146,6 +148,12 @@ pub(crate) fn test(options: Options) -> Result<(), String> {
146148
.map_err(|err| format!("{input}: {err}", input = options.input.display()))?;
147149
let mut opts = GlobalTestOptions::default();
148150
opts.no_crate_inject = true;
151+
152+
let temp_dir =
153+
tempdir().map_err(|error| format!("failed to create temporary directory: {error:?}"))?;
154+
let file_path = temp_dir.path().join("rustdoc-cfgs");
155+
generate_args_file(&file_path, &options)?;
156+
149157
let mut collector = Collector::new(
150158
options.input.display().to_string(),
151159
options.clone(),
@@ -154,6 +162,7 @@ pub(crate) fn test(options: Options) -> Result<(), String> {
154162
None,
155163
Some(options.input),
156164
options.enable_per_target_ignores,
165+
file_path,
157166
);
158167
collector.set_position(DUMMY_SP);
159168
let codes = ErrorCodes::from(options.unstable_features.is_nightly_build());

0 commit comments

Comments
 (0)