From: Weihang Lo Date: Fri, 8 Apr 2022 09:00:00 +0000 (+0800) Subject: Retry with argfile for cargo in rustc fix-proxy-mode X-Git-Url: https://git.proxmox.com/?a=commitdiff_plain;h=fc052776fc6d84519c11e259e1a4a3a474846ba4;p=cargo.git Retry with argfile for cargo in rustc fix-proxy-mode --- diff --git a/src/cargo/ops/fix.rs b/src/cargo/ops/fix.rs index 8c970ef17..67e742360 100644 --- a/src/cargo/ops/fix.rs +++ b/src/cargo/ops/fix.rs @@ -39,11 +39,10 @@ //! show them to the user. use std::collections::{BTreeSet, HashMap, HashSet}; -use std::env; use std::ffi::OsString; use std::path::{Path, PathBuf}; use std::process::{self, ExitStatus}; -use std::str; +use std::{env, fs, str}; use anyhow::{bail, Context, Error}; use cargo_util::{exit_status_to_string, is_simple_exit_code, paths, ProcessBuilder}; @@ -122,6 +121,9 @@ pub fn fix(ws: &Workspace<'_>, opts: &mut FixOptions) -> CargoResult<()> { let rustc = ws.config().load_global_rustc(Some(ws))?; wrapper.arg(&rustc.path); + // This is calling rustc in cargo fix-proxy-mode, so it also need to retry. + // The argfile handling are located at `FixArgs::from_args`. + wrapper.retry_with_argfile(true); // primary crates are compiled using a cargo subprocess to do extra work of applying fixes and // repeating build until there are no more changes to be applied @@ -352,6 +354,7 @@ pub fn fix_maybe_exec_rustc(config: &Config) -> CargoResult { .map(PathBuf::from) .ok(); let mut rustc = ProcessBuilder::new(&args.rustc).wrapped(workspace_rustc.as_ref()); + rustc.retry_with_argfile(true); rustc.env_remove(FIX_ENV); args.apply(&mut rustc); @@ -774,35 +777,65 @@ struct FixArgs { impl FixArgs { fn get() -> Result { - let rustc = env::args_os() + Self::from_args(env::args_os()) + } + + // This is a separate function so that we can use it in tests. + fn from_args(argv: impl IntoIterator) -> Result { + let mut argv = argv.into_iter(); + let mut rustc = argv .nth(1) .map(PathBuf::from) - .ok_or_else(|| anyhow::anyhow!("expected rustc as first argument"))?; + .ok_or_else(|| anyhow::anyhow!("expected rustc or `@path` as first argument"))?; let mut file = None; let mut enabled_edition = None; let mut other = Vec::new(); let mut format_args = Vec::new(); - for arg in env::args_os().skip(2) { + let mut handle_arg = |arg: OsString| -> Result<(), Error> { let path = PathBuf::from(arg); if path.extension().and_then(|s| s.to_str()) == Some("rs") && path.exists() { file = Some(path); - continue; + return Ok(()); } if let Some(s) = path.to_str() { if let Some(edition) = s.strip_prefix("--edition=") { enabled_edition = Some(edition.parse()?); - continue; + return Ok(()); } if s.starts_with("--error-format=") || s.starts_with("--json=") { // Cargo may add error-format in some cases, but `cargo // fix` wants to add its own. format_args.push(s.to_string()); - continue; + return Ok(()); } } other.push(path.into()); + Ok(()) + }; + + if let Some(argfile_path) = rustc.to_str().unwrap_or_default().strip_prefix("@") { + // Because cargo in fix-proxy-mode might hit the command line size limit, + // cargo fix need handle `@path` argfile for this special case. + if argv.next().is_some() { + bail!("argfile `@path` cannot be combined with other arguments"); + } + let contents = fs::read_to_string(argfile_path) + .with_context(|| format!("failed to read argfile at `{argfile_path}`"))?; + let mut iter = contents.lines().map(OsString::from); + rustc = iter + .next() + .map(PathBuf::from) + .ok_or_else(|| anyhow::anyhow!("expected rustc as first argument"))?; + for arg in iter { + handle_arg(arg)?; + } + } else { + for arg in argv { + handle_arg(arg)?; + } } + let file = file.ok_or_else(|| anyhow::anyhow!("could not find .rs file in rustc args"))?; let idioms = env::var(IDIOMS_ENV).is_ok(); @@ -914,3 +947,46 @@ impl FixArgs { .and(Ok(true)) } } + +#[cfg(test)] +mod tests { + use super::FixArgs; + use std::ffi::OsString; + use std::io::Write as _; + use std::path::PathBuf; + + #[test] + fn get_fix_args_from_argfile() { + let mut temp = tempfile::Builder::new().tempfile().unwrap(); + let main_rs = tempfile::Builder::new().suffix(".rs").tempfile().unwrap(); + + let content = format!("/path/to/rustc\n{}\nfoobar\n", main_rs.path().display()); + temp.write_all(content.as_bytes()).unwrap(); + + let argfile = format!("@{}", temp.path().display()); + let args = ["cargo", &argfile]; + let fix_args = FixArgs::from_args(args.map(|x| x.into())).unwrap(); + assert_eq!(fix_args.rustc, PathBuf::from("/path/to/rustc")); + assert_eq!(fix_args.file, main_rs.path()); + assert_eq!(fix_args.other, vec![OsString::from("foobar")]); + } + + #[test] + fn get_fix_args_from_argfile_with_extra_arg() { + let mut temp = tempfile::Builder::new().tempfile().unwrap(); + let main_rs = tempfile::Builder::new().suffix(".rs").tempfile().unwrap(); + + let content = format!("/path/to/rustc\n{}\nfoobar\n", main_rs.path().display()); + temp.write_all(content.as_bytes()).unwrap(); + + let argfile = format!("@{}", temp.path().display()); + let args = ["cargo", &argfile, "boo!"]; + match FixArgs::from_args(args.map(|x| x.into())) { + Err(e) => assert_eq!( + e.to_string(), + "argfile `@path` cannot be combined with other arguments" + ), + Ok(_) => panic!("should fail"), + } + } +}