From 1a732c54211a9091959744bf1225525516b60da2 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 21 Mar 2024 21:11:19 +0100 Subject: [PATCH] `rustdoc --test`: Prevent reaching the maximum size of command-line by using files for arguments if there are too many --- src/librustdoc/doctest.rs | 58 ++++++++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 7 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index c6eb7be08cd81..d793049f151f1 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -23,6 +23,7 @@ use rustc_target::spec::{Target, TargetTriple}; use tempfile::Builder as TempFileBuilder; use std::env; +use std::fs::File; use std::io::{self, Write}; use std::panic; use std::path::{Path, PathBuf}; @@ -31,6 +32,8 @@ use std::str; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; +use tempfile::tempdir; + use crate::clean::{types::AttributesExt, Attributes}; use crate::config::Options as RustdocOptions; use crate::html::markdown::{self, ErrorCodes, Ignore, LangString}; @@ -348,13 +351,49 @@ fn run_test( .unwrap_or_else(|| rustc_interface::util::rustc_path().expect("found rustc")); let mut compiler = wrapped_rustc_command(&rustdoc_options.test_builder_wrappers, rustc_binary); compiler.arg("--crate-type").arg("bin"); - for cfg in &rustdoc_options.cfgs { - compiler.arg("--cfg").arg(&cfg); + + let mut needs_unstable_options = false; + let mut written_cfg_args = false; + #[allow(unused_variables)] + let tmp_dir; + + // If there are too many `cfg` arguments, instead of risking reaching `Command`'s limit on + // the number of arguments, we put them into a file which we then pass as `@` argument. + if rustdoc_options.cfgs.len() + rustdoc_options.check_cfgs.len() > 1_000 + && let Ok(temp_dir) = tempdir() + && let file_path = temp_dir.path().join("cfgs") + && let Ok(mut file) = File::create(&file_path) + { + let mut content = Vec::new(); + for cfg in &rustdoc_options.cfgs { + content.push(format!("--cfg={cfg}")); + } + if !rustdoc_options.check_cfgs.is_empty() { + needs_unstable_options = true; + for check_cfg in &rustdoc_options.check_cfgs { + content.push(format!("--check-cfg={check_cfg}")); + } + } + let content = content.join("\n"); + #[allow(unused_assignments)] // Needed for `tmp_dir = temp_dir`. + if file.write(content.as_bytes()).is_ok() { + // To prevent removing the temporary directory too soon. + tmp_dir = temp_dir; + written_cfg_args = true; + compiler.arg(&format!("@{}", file_path.display())); + } } - if !rustdoc_options.check_cfgs.is_empty() { - compiler.arg("-Z").arg("unstable-options"); - for check_cfg in &rustdoc_options.check_cfgs { - compiler.arg("--check-cfg").arg(&check_cfg); + // If there were not enough `cfg` argument to make a file, or the file creation failed, + // we use the usual `Command` API. + if !written_cfg_args { + for cfg in &rustdoc_options.cfgs { + compiler.arg("--cfg").arg(&cfg); + } + if !rustdoc_options.check_cfgs.is_empty() { + needs_unstable_options = true; + for check_cfg in &rustdoc_options.check_cfgs { + compiler.arg("--check-cfg").arg(&check_cfg); + } } } if let Some(sysroot) = rustdoc_options.maybe_sysroot { @@ -370,9 +409,14 @@ fn run_test( if rustdoc_options.json_unused_externs.is_enabled() && !lang_string.compile_fail { compiler.arg("--error-format=json"); compiler.arg("--json").arg("unused-externs"); - compiler.arg("-Z").arg("unstable-options"); compiler.arg("-W").arg("unused_crate_dependencies"); + needs_unstable_options = true; } + + if needs_unstable_options { + compiler.arg("-Z").arg("unstable-options"); + } + for lib_str in &rustdoc_options.lib_strs { compiler.arg("-L").arg(&lib_str); }