Skip to content

Commit 2195854

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 2195854

File tree

1 file changed

+51
-7
lines changed

1 file changed

+51
-7
lines changed

src/librustdoc/doctest.rs

+51-7
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};
@@ -348,13 +351,49 @@ fn run_test(
348351
.unwrap_or_else(|| rustc_interface::util::rustc_path().expect("found rustc"));
349352
let mut compiler = wrapped_rustc_command(&rustdoc_options.test_builder_wrappers, rustc_binary);
350353
compiler.arg("--crate-type").arg("bin");
351-
for cfg in &rustdoc_options.cfgs {
352-
compiler.arg("--cfg").arg(&cfg);
354+
355+
let mut needs_unstable_options = false;
356+
let mut written_cfg_args = false;
357+
#[allow(unused_variables)]
358+
let tmp_dir;
359+
360+
// If there are too many `cfg` arguments, instead of risking reaching `Command`'s limit on
361+
// the number of arguments, we put them into a file which we then pass as `@` argument.
362+
if rustdoc_options.cfgs.len() + rustdoc_options.check_cfgs.len() > 1_000
363+
&& let Ok(temp_dir) = tempdir()
364+
&& let file_path = temp_dir.path().join("cfgs")
365+
&& let Ok(mut file) = File::create(&file_path)
366+
{
367+
let mut content = Vec::new();
368+
for cfg in &rustdoc_options.cfgs {
369+
content.push(format!("--cfg={cfg}"));
370+
}
371+
if !rustdoc_options.check_cfgs.is_empty() {
372+
needs_unstable_options = true;
373+
for check_cfg in &rustdoc_options.check_cfgs {
374+
content.push(format!("--check-cfg={check_cfg}"));
375+
}
376+
}
377+
let content = content.join("\n");
378+
#[allow(unused_assignments)] // Needed for `tmp_dir = temp_dir`.
379+
if file.write(content.as_bytes()).is_ok() {
380+
// To prevent removing the temporary directory too soon.
381+
tmp_dir = temp_dir;
382+
written_cfg_args = true;
383+
compiler.arg(&format!("@{}", file_path.display()));
384+
}
353385
}
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);
386+
// If there were not enough `cfg` argument to make a file, or the file creation failed,
387+
// we use the usual `Command` API.
388+
if !written_cfg_args {
389+
for cfg in &rustdoc_options.cfgs {
390+
compiler.arg("--cfg").arg(&cfg);
391+
}
392+
if !rustdoc_options.check_cfgs.is_empty() {
393+
needs_unstable_options = true;
394+
for check_cfg in &rustdoc_options.check_cfgs {
395+
compiler.arg("--check-cfg").arg(&check_cfg);
396+
}
358397
}
359398
}
360399
if let Some(sysroot) = rustdoc_options.maybe_sysroot {
@@ -370,9 +409,14 @@ fn run_test(
370409
if rustdoc_options.json_unused_externs.is_enabled() && !lang_string.compile_fail {
371410
compiler.arg("--error-format=json");
372411
compiler.arg("--json").arg("unused-externs");
373-
compiler.arg("-Z").arg("unstable-options");
374412
compiler.arg("-W").arg("unused_crate_dependencies");
413+
needs_unstable_options = true;
375414
}
415+
416+
if needs_unstable_options {
417+
compiler.arg("-Z").arg("unstable-options");
418+
}
419+
376420
for lib_str in &rustdoc_options.lib_strs {
377421
compiler.arg("-L").arg(&lib_str);
378422
}

0 commit comments

Comments
 (0)