|
39 | 39 | //! show them to the user.
|
40 | 40 |
|
41 | 41 | use std::collections::{BTreeSet, HashMap, HashSet};
|
42 |
| -use std::env; |
43 | 42 | use std::ffi::OsString;
|
44 | 43 | use std::path::{Path, PathBuf};
|
45 | 44 | use std::process::{self, ExitStatus};
|
46 |
| -use std::str; |
| 45 | +use std::{env, fs, str}; |
47 | 46 |
|
48 | 47 | use anyhow::{bail, Context, Error};
|
49 | 48 | 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<()> {
|
122 | 121 |
|
123 | 122 | let rustc = ws.config().load_global_rustc(Some(ws))?;
|
124 | 123 | 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); |
125 | 127 |
|
126 | 128 | // primary crates are compiled using a cargo subprocess to do extra work of applying fixes and
|
127 | 129 | // 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> {
|
352 | 354 | .map(PathBuf::from)
|
353 | 355 | .ok();
|
354 | 356 | let mut rustc = ProcessBuilder::new(&args.rustc).wrapped(workspace_rustc.as_ref());
|
| 357 | + rustc.retry_with_argfile(true); |
355 | 358 | rustc.env_remove(FIX_ENV);
|
356 | 359 | args.apply(&mut rustc);
|
357 | 360 |
|
@@ -774,35 +777,65 @@ struct FixArgs {
|
774 | 777 |
|
775 | 778 | impl FixArgs {
|
776 | 779 | 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 |
778 | 787 | .nth(1)
|
779 | 788 | .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"))?; |
781 | 790 | let mut file = None;
|
782 | 791 | let mut enabled_edition = None;
|
783 | 792 | let mut other = Vec::new();
|
784 | 793 | let mut format_args = Vec::new();
|
785 | 794 |
|
786 |
| - for arg in env::args_os().skip(2) { |
| 795 | + let mut handle_arg = |arg: OsString| -> Result<(), Error> { |
787 | 796 | let path = PathBuf::from(arg);
|
788 | 797 | if path.extension().and_then(|s| s.to_str()) == Some("rs") && path.exists() {
|
789 | 798 | file = Some(path);
|
790 |
| - continue; |
| 799 | + return Ok(()); |
791 | 800 | }
|
792 | 801 | if let Some(s) = path.to_str() {
|
793 | 802 | if let Some(edition) = s.strip_prefix("--edition=") {
|
794 | 803 | enabled_edition = Some(edition.parse()?);
|
795 |
| - continue; |
| 804 | + return Ok(()); |
796 | 805 | }
|
797 | 806 | if s.starts_with("--error-format=") || s.starts_with("--json=") {
|
798 | 807 | // Cargo may add error-format in some cases, but `cargo
|
799 | 808 | // fix` wants to add its own.
|
800 | 809 | format_args.push(s.to_string());
|
801 |
| - continue; |
| 810 | + return Ok(()); |
802 | 811 | }
|
803 | 812 | }
|
804 | 813 | 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 | + } |
805 | 837 | }
|
| 838 | + |
806 | 839 | let file = file.ok_or_else(|| anyhow::anyhow!("could not find .rs file in rustc args"))?;
|
807 | 840 | let idioms = env::var(IDIOMS_ENV).is_ok();
|
808 | 841 |
|
@@ -914,3 +947,46 @@ impl FixArgs {
|
914 | 947 | .and(Ok(true))
|
915 | 948 | }
|
916 | 949 | }
|
| 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