Skip to content

Commit fc05277

Browse files
committed
Retry with argfile for cargo in rustc fix-proxy-mode
1 parent 832274d commit fc05277

File tree

1 file changed

+84
-8
lines changed

1 file changed

+84
-8
lines changed

src/cargo/ops/fix.rs

Lines changed: 84 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,10 @@
3939
//! show them to the user.
4040
4141
use std::collections::{BTreeSet, HashMap, HashSet};
42-
use std::env;
4342
use std::ffi::OsString;
4443
use std::path::{Path, PathBuf};
4544
use std::process::{self, ExitStatus};
46-
use std::str;
45+
use std::{env, fs, str};
4746

4847
use anyhow::{bail, Context, Error};
4948
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<()> {
122121

123122
let rustc = ws.config().load_global_rustc(Some(ws))?;
124123
wrapper.arg(&rustc.path);
124+
// This is calling rustc in cargo fix-proxy-mode, so it also need to retry.
125+
// The argfile handling are located at `FixArgs::from_args`.
126+
wrapper.retry_with_argfile(true);
125127

126128
// primary crates are compiled using a cargo subprocess to do extra work of applying fixes and
127129
// repeating build until there are no more changes to be applied
@@ -352,6 +354,7 @@ pub fn fix_maybe_exec_rustc(config: &Config) -> CargoResult<bool> {
352354
.map(PathBuf::from)
353355
.ok();
354356
let mut rustc = ProcessBuilder::new(&args.rustc).wrapped(workspace_rustc.as_ref());
357+
rustc.retry_with_argfile(true);
355358
rustc.env_remove(FIX_ENV);
356359
args.apply(&mut rustc);
357360

@@ -774,35 +777,65 @@ struct FixArgs {
774777

775778
impl FixArgs {
776779
fn get() -> Result<FixArgs, Error> {
777-
let rustc = env::args_os()
780+
Self::from_args(env::args_os())
781+
}
782+
783+
// This is a separate function so that we can use it in tests.
784+
fn from_args(argv: impl IntoIterator<Item = OsString>) -> Result<Self, Error> {
785+
let mut argv = argv.into_iter();
786+
let mut rustc = argv
778787
.nth(1)
779788
.map(PathBuf::from)
780-
.ok_or_else(|| anyhow::anyhow!("expected rustc as first argument"))?;
789+
.ok_or_else(|| anyhow::anyhow!("expected rustc or `@path` as first argument"))?;
781790
let mut file = None;
782791
let mut enabled_edition = None;
783792
let mut other = Vec::new();
784793
let mut format_args = Vec::new();
785794

786-
for arg in env::args_os().skip(2) {
795+
let mut handle_arg = |arg: OsString| -> Result<(), Error> {
787796
let path = PathBuf::from(arg);
788797
if path.extension().and_then(|s| s.to_str()) == Some("rs") && path.exists() {
789798
file = Some(path);
790-
continue;
799+
return Ok(());
791800
}
792801
if let Some(s) = path.to_str() {
793802
if let Some(edition) = s.strip_prefix("--edition=") {
794803
enabled_edition = Some(edition.parse()?);
795-
continue;
804+
return Ok(());
796805
}
797806
if s.starts_with("--error-format=") || s.starts_with("--json=") {
798807
// Cargo may add error-format in some cases, but `cargo
799808
// fix` wants to add its own.
800809
format_args.push(s.to_string());
801-
continue;
810+
return Ok(());
802811
}
803812
}
804813
other.push(path.into());
814+
Ok(())
815+
};
816+
817+
if let Some(argfile_path) = rustc.to_str().unwrap_or_default().strip_prefix("@") {
818+
// Because cargo in fix-proxy-mode might hit the command line size limit,
819+
// cargo fix need handle `@path` argfile for this special case.
820+
if argv.next().is_some() {
821+
bail!("argfile `@path` cannot be combined with other arguments");
822+
}
823+
let contents = fs::read_to_string(argfile_path)
824+
.with_context(|| format!("failed to read argfile at `{argfile_path}`"))?;
825+
let mut iter = contents.lines().map(OsString::from);
826+
rustc = iter
827+
.next()
828+
.map(PathBuf::from)
829+
.ok_or_else(|| anyhow::anyhow!("expected rustc as first argument"))?;
830+
for arg in iter {
831+
handle_arg(arg)?;
832+
}
833+
} else {
834+
for arg in argv {
835+
handle_arg(arg)?;
836+
}
805837
}
838+
806839
let file = file.ok_or_else(|| anyhow::anyhow!("could not find .rs file in rustc args"))?;
807840
let idioms = env::var(IDIOMS_ENV).is_ok();
808841

@@ -914,3 +947,46 @@ impl FixArgs {
914947
.and(Ok(true))
915948
}
916949
}
950+
951+
#[cfg(test)]
952+
mod tests {
953+
use super::FixArgs;
954+
use std::ffi::OsString;
955+
use std::io::Write as _;
956+
use std::path::PathBuf;
957+
958+
#[test]
959+
fn get_fix_args_from_argfile() {
960+
let mut temp = tempfile::Builder::new().tempfile().unwrap();
961+
let main_rs = tempfile::Builder::new().suffix(".rs").tempfile().unwrap();
962+
963+
let content = format!("/path/to/rustc\n{}\nfoobar\n", main_rs.path().display());
964+
temp.write_all(content.as_bytes()).unwrap();
965+
966+
let argfile = format!("@{}", temp.path().display());
967+
let args = ["cargo", &argfile];
968+
let fix_args = FixArgs::from_args(args.map(|x| x.into())).unwrap();
969+
assert_eq!(fix_args.rustc, PathBuf::from("/path/to/rustc"));
970+
assert_eq!(fix_args.file, main_rs.path());
971+
assert_eq!(fix_args.other, vec![OsString::from("foobar")]);
972+
}
973+
974+
#[test]
975+
fn get_fix_args_from_argfile_with_extra_arg() {
976+
let mut temp = tempfile::Builder::new().tempfile().unwrap();
977+
let main_rs = tempfile::Builder::new().suffix(".rs").tempfile().unwrap();
978+
979+
let content = format!("/path/to/rustc\n{}\nfoobar\n", main_rs.path().display());
980+
temp.write_all(content.as_bytes()).unwrap();
981+
982+
let argfile = format!("@{}", temp.path().display());
983+
let args = ["cargo", &argfile, "boo!"];
984+
match FixArgs::from_args(args.map(|x| x.into())) {
985+
Err(e) => assert_eq!(
986+
e.to_string(),
987+
"argfile `@path` cannot be combined with other arguments"
988+
),
989+
Ok(_) => panic!("should fail"),
990+
}
991+
}
992+
}

0 commit comments

Comments
 (0)