//! 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};
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
.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);
impl FixArgs {
fn get() -> Result<FixArgs, Error> {
- 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<Item = OsString>) -> Result<Self, Error> {
+ 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();
.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"),
+ }
+ }
+}