diff --git a/src/compiler/args.rs b/src/compiler/args.rs index e551c0f80..64d7eb98a 100644 --- a/src/compiler/args.rs +++ b/src/compiler/args.rs @@ -575,6 +575,7 @@ where { arguments: I, arg_info: S, + seen_double_dashes: Option, phantom: PhantomData, } @@ -592,9 +593,15 @@ where ArgsIter { arguments, arg_info, + seen_double_dashes: None, phantom: PhantomData, } } + + pub fn with_double_dashes(mut self) -> Self { + self.seen_double_dashes = Some(false); + self + } } impl Iterator for ArgsIter @@ -607,6 +614,14 @@ where fn next(&mut self) -> Option { if let Some(arg) = self.arguments.next() { + if let Some(seen_double_dashes) = &mut self.seen_double_dashes { + if !*seen_double_dashes && arg == "--" { + *seen_double_dashes = true; + } + if *seen_double_dashes { + return Some(Ok(Argument::Raw(arg))); + } + } let s = arg.to_string_lossy(); let arguments = &mut self.arguments; Some(match self.arg_info.search(&s[..]) { @@ -957,8 +972,11 @@ mod tests { "-plop", "-quxbar", // -quxbar is not -qux with a value of bar "-qux=value", + "--", + "non_flag", + "-flag-after-double-dashes", ]; - let iter = ArgsIter::new(args.iter().map(OsString::from), &ARGS[..]); + let iter = ArgsIter::new(args.iter().map(OsString::from), &ARGS[..]).with_double_dashes(); let expected = vec![ arg!(UnknownFlag("-nomatch")), arg!(WithValue("-foo", ArgData::Foo("value"), Separated)), @@ -979,6 +997,9 @@ mod tests { ArgData::Qux("value"), CanBeSeparated('=') )), + arg!(Raw("--")), + arg!(Raw("non_flag")), + arg!(Raw("-flag-after-double-dashes")), ]; match diff_with(iter, expected, |a, b| { assert_eq!(a.as_ref().unwrap(), b); diff --git a/src/compiler/c.rs b/src/compiler/c.rs index bf2261642..3aa9ea5a1 100644 --- a/src/compiler/c.rs +++ b/src/compiler/c.rs @@ -85,6 +85,8 @@ pub struct ArtifactDescriptor { pub struct ParsedArguments { /// The input source file. pub input: PathBuf, + /// Whether to prepend the input with `--` + pub double_dash_input: bool, /// The type of language used in the input source file. pub language: Language, /// The flag required to compile for the given language diff --git a/src/compiler/diab.rs b/src/compiler/diab.rs index 307ff17c0..3429d6a27 100644 --- a/src/compiler/diab.rs +++ b/src/compiler/diab.rs @@ -285,6 +285,7 @@ where CompilerArguments::Ok(ParsedArguments { input: input.into(), + double_dash_input: false, language, compilation_flag, depfile: None, @@ -744,6 +745,7 @@ mod test { let f = TestFixture::new(); let parsed_args = ParsedArguments { input: "foo.c".into(), + double_dash_input: false, language: Language::C, compilation_flag: "-c".into(), depfile: None, diff --git a/src/compiler/gcc.rs b/src/compiler/gcc.rs index e422d8735..a9eed4069 100644 --- a/src/compiler/gcc.rs +++ b/src/compiler/gcc.rs @@ -254,6 +254,7 @@ where { let mut output_arg = None; let mut input_arg = None; + let mut double_dash_input = false; let mut dep_target = None; let mut dep_flag = OsString::from("-MT"); let mut common_args = vec![]; @@ -290,7 +291,11 @@ where let mut too_hard_for_preprocessor_cache_mode = false; - for arg in ArgsIter::new(it, arg_info) { + let mut args_iter = ArgsIter::new(it, arg_info); + if kind == CCompilerKind::Clang { + args_iter = args_iter.with_double_dashes(); + } + for arg in args_iter { let arg = try_or_cannot_cache!(arg, "argument parse"); // Check if the value part of this argument begins with '@'. If so, we either // failed to expand it, or it was a concatenated argument - either way, bail. @@ -392,6 +397,11 @@ where } Some(XClang(s)) => xclangs.push(s.clone()), None => match arg { + Argument::Raw(ref val) if val == "--" => { + if input_arg.is_none() { + double_dash_input = true; + } + } Argument::Raw(ref val) => { if input_arg.is_some() { multiple_input = true; @@ -605,6 +615,7 @@ where CompilerArguments::Ok(ParsedArguments { input: input.into(), + double_dash_input, language, compilation_flag, depfile: None, @@ -703,11 +714,14 @@ fn preprocess_cmd( arch_args_to_use = &parsed_args.arch_args; } - cmd.arg(&parsed_args.input) - .args(&parsed_args.preprocessor_args) + cmd.args(&parsed_args.preprocessor_args) .args(&parsed_args.dependency_args) .args(&parsed_args.common_args) - .args(arch_args_to_use) + .args(arch_args_to_use); + if parsed_args.double_dash_input { + cmd.arg("--"); + } + cmd.arg(&parsed_args.input) .env_clear() .envs(env_vars.iter().map(|(k, v)| (k, v))) .current_dir(cwd); @@ -780,7 +794,6 @@ pub fn generate_compile_commands( } arguments.extend(vec![ parsed_args.compilation_flag.clone(), - parsed_args.input.clone().into(), "-o".into(), out_file.into(), ]); @@ -788,6 +801,10 @@ pub fn generate_compile_commands( arguments.extend(parsed_args.unhashed_args.clone()); arguments.extend(parsed_args.common_args.clone()); arguments.extend(parsed_args.arch_args.clone()); + if parsed_args.double_dash_input { + arguments.push("--".into()); + } + arguments.push(parsed_args.input.clone().into()); let command = CompileCommand { executable: executable.to_owned(), arguments, @@ -941,6 +958,20 @@ mod test { parse_arguments(&args, ".".as_ref(), &ARGS[..], plusplus, CCompilerKind::Gcc) } + fn parse_arguments_clang( + arguments: Vec, + plusplus: bool, + ) -> CompilerArguments { + let args = arguments.iter().map(OsString::from).collect::>(); + parse_arguments( + &args, + ".".as_ref(), + &ARGS[..], + plusplus, + CCompilerKind::Clang, + ) + } + #[test] fn test_parse_arguments_simple() { let args = stringvec!["-c", "foo.c", "-o", "foo.o"]; @@ -1336,6 +1367,65 @@ mod test { assert!(!msvc_show_includes); } + #[test] + fn test_parse_arguments_double_dash() { + let args = stringvec!["-c", "-o", "foo.o", "--", "foo.c"]; + let ParsedArguments { + input, + double_dash_input, + common_args, + .. + } = match parse_arguments_(args.clone(), false) { + CompilerArguments::Ok(args) => args, + o => panic!("Got unexpected parse result: {:?}", o), + }; + assert_eq!(Some("foo.c"), input.to_str()); + // GCC doesn't support double dashes. If we got one, we'll pass them + // through to GCC for it to error out. + assert!(!double_dash_input); + assert_eq!(ovec!["--"], common_args); + + let ParsedArguments { + input, + double_dash_input, + common_args, + .. + } = match parse_arguments_clang(args, false) { + CompilerArguments::Ok(args) => args, + o => panic!("Got unexpected parse result: {:?}", o), + }; + assert_eq!(Some("foo.c"), input.to_str()); + assert!(double_dash_input); + assert!(common_args.is_empty()); + + let args = stringvec!["-c", "-o", "foo.o", "foo.c", "--"]; + let ParsedArguments { + input, + double_dash_input, + common_args, + .. + } = match parse_arguments_clang(args, false) { + CompilerArguments::Ok(args) => args, + o => panic!("Got unexpected parse result: {:?}", o), + }; + assert_eq!(Some("foo.c"), input.to_str()); + // Double dash after input file is ignored. + assert!(!double_dash_input); + assert!(common_args.is_empty()); + + let args = stringvec!["-c", "-o", "foo.o", "foo.c", "--", "bar.c"]; + assert_eq!( + CompilerArguments::CannotCache("multiple input files", Some("[\"bar.c\"]".to_string())), + parse_arguments_clang(args, false) + ); + + let args = stringvec!["-c", "-o", "foo.o", "foo.c", "--", "-fPIC"]; + assert_eq!( + CompilerArguments::CannotCache("multiple input files", Some("[\"-fPIC\"]".to_string())), + parse_arguments_clang(args, false) + ); + } + #[test] fn test_parse_arguments_explicit_dep_target() { let args = @@ -1503,9 +1593,9 @@ mod test { "c++", "-E", "-fdirectives-only", - "foo.cc", "-D__arm64__=1", - "-D__i386__=1" + "-D__i386__=1", + "foo.cc" ]; assert_eq!(cmd.args, expected_args); }); @@ -1538,13 +1628,38 @@ mod test { "c++", "-E", "-fdirectives-only", - "foo.cc", "-arch", - "arm64" + "arm64", + "foo.cc" ]; assert_eq!(cmd.args, expected_args); } + #[test] + fn test_preprocess_double_dash_input() { + let args = stringvec!["-c", "-o", "foo.o", "--", "foo.c"]; + let parsed_args = match parse_arguments_clang(args, false) { + CompilerArguments::Ok(args) => args, + o => panic!("Got unexpected parse result: {:?}", o), + }; + let mut cmd = MockCommand { + child: None, + args: vec![], + }; + preprocess_cmd( + &mut cmd, + &parsed_args, + Path::new(""), + &[], + true, + CCompilerKind::Clang, + true, + vec![], + ); + let expected_args = ovec!["-x", "c", "-E", "-frewrite-includes", "--", "foo.c"]; + assert_eq!(cmd.args, expected_args); + } + #[test] fn pedantic_default() { let args = stringvec!["-pedantic", "-c", "foo.cc"]; @@ -1888,6 +2003,7 @@ mod test { let f = TestFixture::new(); let parsed_args = ParsedArguments { input: "foo.c".into(), + double_dash_input: false, language: Language::C, compilation_flag: "-c".into(), depfile: None, @@ -1936,6 +2052,30 @@ mod test { assert_eq!(0, creator.lock().unwrap().children.len()); } + #[test] + fn test_compile_double_dash_input() { + let args = stringvec!["-c", "-o", "foo.o", "--", "foo.c"]; + let parsed_args = match parse_arguments_clang(args, false) { + CompilerArguments::Ok(args) => args, + o => panic!("Got unexpected parse result: {:?}", o), + }; + let f = TestFixture::new(); + let compiler = &f.bins[0]; + let mut path_transformer = dist::PathTransformer::default(); + let (command, _, _) = generate_compile_commands( + &mut path_transformer, + compiler, + &parsed_args, + f.tempdir.path(), + &[], + CCompilerKind::Clang, + false, + ) + .unwrap(); + let expected_args = ovec!["-x", "c", "-c", "-o", "foo.o", "--", "foo.c"]; + assert_eq!(command.arguments, expected_args); + } + #[test] fn test_parse_arguments_plusplus() { let args = stringvec!["-c", "foo.c", "-o", "foo.o"]; diff --git a/src/compiler/msvc.rs b/src/compiler/msvc.rs index c1dbcee22..81336d744 100644 --- a/src/compiler/msvc.rs +++ b/src/compiler/msvc.rs @@ -492,6 +492,7 @@ pub fn parse_arguments( ) -> CompilerArguments { let mut output_arg = None; let mut input_arg = None; + let mut double_dash_input = false; let mut common_args = vec![]; let mut unhashed_args = vec![]; let mut preprocessor_args = vec![]; @@ -512,7 +513,10 @@ pub fn parse_arguments( // Custom iterator to expand `@` arguments which stand for reading a file // and interpreting it as a list of more arguments. let it = ExpandIncludeFile::new(cwd, arguments); - let it = ArgsIter::new(it, (&ARGS[..], &SLASH_ARGS[..])); + let mut it = ArgsIter::new(it, (&ARGS[..], &SLASH_ARGS[..])); + if is_clang { + it = it.with_double_dashes(); + } for arg in it { let arg = try_or_cannot_cache!(arg, "argument parse"); match arg.get_data() { @@ -553,6 +557,11 @@ pub fn parse_arguments( Some(Clang(s)) => clangs.push(s.clone()), None => { match arg { + Argument::Raw(ref val) if val == "--" => { + if input_arg.is_none() { + double_dash_input = true; + } + } Argument::Raw(ref val) => { if input_arg.is_some() { // Can't cache compilations with multiple inputs. @@ -797,6 +806,7 @@ pub fn parse_arguments( CompilerArguments::Ok(ParsedArguments { input: input.into(), + double_dash_input, language, compilation_flag, depfile, @@ -852,22 +862,17 @@ fn normpath(path: &str) -> String { } #[allow(clippy::too_many_arguments)] -pub async fn preprocess( - creator: &T, - executable: &Path, +pub fn preprocess_cmd( + cmd: &mut T, parsed_args: &ParsedArguments, cwd: &Path, env_vars: &[(OsString, OsString)], may_dist: bool, - includes_prefix: &str, rewrite_includes_only: bool, is_clang: bool, -) -> Result -where - T: CommandCreatorSync, +) where + T: RunCommand, { - let mut cmd = creator.clone().new_command_sync(executable); - // When performing distributed compilation, line number info is important for error // reporting and to not cause spurious compilation failure (e.g. no exceptions build // fails due to exceptions transitively included in the stdlib). @@ -880,8 +885,7 @@ where cmd.arg("-EP"); } - cmd.arg(&parsed_args.input) - .arg("-nologo") + cmd.arg("-nologo") .args(&parsed_args.preprocessor_args) .args(&parsed_args.dependency_args) .args(&parsed_args.common_args) @@ -908,6 +912,38 @@ where cmd.arg("-clang:-frewrite-includes"); } + if parsed_args.double_dash_input { + cmd.arg("--"); + } + cmd.arg(&parsed_args.input); +} + +#[allow(clippy::too_many_arguments)] +pub async fn preprocess( + creator: &T, + executable: &Path, + parsed_args: &ParsedArguments, + cwd: &Path, + env_vars: &[(OsString, OsString)], + may_dist: bool, + includes_prefix: &str, + rewrite_includes_only: bool, + is_clang: bool, +) -> Result +where + T: CommandCreatorSync, +{ + let mut cmd = creator.clone().new_command_sync(executable); + preprocess_cmd( + &mut cmd, + parsed_args, + cwd, + env_vars, + may_dist, + rewrite_includes_only, + is_clang, + ); + if log_enabled!(Debug) { debug!("preprocess: {:?}", cmd); } @@ -1013,16 +1049,15 @@ fn generate_compile_commands( let mut fo = OsString::from("-Fo"); fo.push(out_file); - let mut arguments: Vec = vec![ - parsed_args.compilation_flag.clone(), - parsed_args.input.clone().into(), - fo, - ]; + let mut arguments: Vec = vec![parsed_args.compilation_flag.clone(), fo]; arguments.extend(parsed_args.preprocessor_args.clone()); arguments.extend(parsed_args.dependency_args.clone()); arguments.extend(parsed_args.unhashed_args.clone()); arguments.extend(parsed_args.common_args.clone()); - + if parsed_args.double_dash_input { + arguments.push("--".into()); + } + arguments.push(parsed_args.input.clone().into()); let command = CompileCommand { executable: executable.to_owned(), arguments, @@ -1039,17 +1074,19 @@ fn generate_compile_commands( let mut fo = String::from("-Fo"); fo.push_str(&path_transformer.as_dist(out_file)?); - let mut arguments: Vec = vec![ - parsed_args.compilation_flag.clone().into_string().ok()?, - path_transformer.as_dist(&parsed_args.input)?, - fo, - ]; + let mut arguments: Vec = + vec![parsed_args.compilation_flag.clone().into_string().ok()?, fo]; // It's important to avoid preprocessor_args because of things like /FI which // forcibly includes another file. This does mean we're potentially vulnerable // to misidentification of flags like -DYNAMICBASE (though in that specific // case we're safe as it only applies to link time, which sccache avoids). arguments.extend(dist::osstrings_to_strings(&parsed_args.common_args)?); + if parsed_args.double_dash_input { + arguments.push("--".into()); + } + arguments.push(path_transformer.as_dist(&parsed_args.input)?); + Some(dist::CompileCommand { executable: path_transformer.as_dist(executable)?, arguments, @@ -1321,6 +1358,10 @@ mod test { super::parse_arguments(&arguments, &std::env::current_dir().unwrap(), false) } + fn parse_arguments_clang(arguments: Vec) -> CompilerArguments { + super::parse_arguments(&arguments, &std::env::current_dir().unwrap(), true) + } + #[test] fn test_detect_showincludes_prefix() { drop(env_logger::try_init()); @@ -1510,6 +1551,65 @@ mod test { assert!(!msvc_show_includes); } + #[test] + fn test_parse_arguments_double_dash() { + let args = ovec!["-c", "-Fofoo.obj", "--", "foo.c"]; + let ParsedArguments { + input, + double_dash_input, + common_args, + .. + } = match parse_arguments(args.clone()) { + CompilerArguments::Ok(args) => args, + o => panic!("Got unexpected parse result: {:?}", o), + }; + assert_eq!(Some("foo.c"), input.to_str()); + // MSVC doesn't support double dashes. If we got one, we'll pass them + // through to MSVC for it to error out. + assert!(!double_dash_input); + assert_eq!(ovec!["--"], common_args); + + let ParsedArguments { + input, + double_dash_input, + common_args, + .. + } = match parse_arguments_clang(args) { + CompilerArguments::Ok(args) => args, + o => panic!("Got unexpected parse result: {:?}", o), + }; + assert_eq!(Some("foo.c"), input.to_str()); + assert!(double_dash_input); + assert!(common_args.is_empty()); + + let args = ovec!["-c", "-Fofoo.obj", "foo.c", "--"]; + let ParsedArguments { + input, + double_dash_input, + common_args, + .. + } = match parse_arguments_clang(args) { + CompilerArguments::Ok(args) => args, + o => panic!("Got unexpected parse result: {:?}", o), + }; + assert_eq!(Some("foo.c"), input.to_str()); + // Double dash after input file is ignored. + assert!(!double_dash_input); + assert!(common_args.is_empty()); + + let args = ovec!["-c", "-Fofoo.obj", "foo.c", "--", "bar.c"]; + assert_eq!( + CompilerArguments::CannotCache("multiple input files", Some("[\"bar.c\"]".to_string())), + parse_arguments_clang(args) + ); + + let args = ovec!["-c", "-Fofoo.obj", "foo.c", "--", "-fPIC"]; + assert_eq!( + CompilerArguments::CannotCache("multiple input files", Some("[\"-fPIC\"]".to_string())), + parse_arguments_clang(args) + ); + } + #[test] fn parse_argument_slashes() { let args = ovec!["-c", "foo.c", "/Fofoo.obj"]; @@ -2241,12 +2341,29 @@ mod test { ); } + #[test] + fn test_preprocess_double_dash_input() { + let args = ovec!["-c", "-Fofoo.o.bj", "--", "foo.c"]; + let parsed_args = match parse_arguments_clang(args) { + CompilerArguments::Ok(args) => args, + o => panic!("Got unexpected parse result: {:?}", o), + }; + let mut cmd = MockCommand { + child: None, + args: vec![], + }; + preprocess_cmd(&mut cmd, &parsed_args, Path::new(""), &[], true, true, true); + let expected_args = ovec!["-E", "-nologo", "-clang:-frewrite-includes", "--", "foo.c"]; + assert_eq!(cmd.args, expected_args); + } + #[test] fn test_compile_simple() { let creator = new_creator(); let f = TestFixture::new(); let parsed_args = ParsedArguments { input: "foo.c".into(), + double_dash_input: false, language: Language::C, compilation_flag: "-c".into(), depfile: None, @@ -2293,6 +2410,28 @@ mod test { assert_eq!(0, creator.lock().unwrap().children.len()); } + #[test] + fn test_compile_double_dash_input() { + let args = ovec!["-c", "-Fofoo.obj", "--", "foo.c"]; + let parsed_args = match parse_arguments_clang(args) { + CompilerArguments::Ok(args) => args, + o => panic!("Got unexpected parse result: {:?}", o), + }; + let f = TestFixture::new(); + let compiler = &f.bins[0]; + let mut path_transformer = dist::PathTransformer::default(); + let (command, _, _) = generate_compile_commands( + &mut path_transformer, + compiler, + &parsed_args, + f.tempdir.path(), + &[], + ) + .unwrap(); + let expected_args = ovec!["-c", "-Fofoo.obj", "--", "foo.c"]; + assert_eq!(command.arguments, expected_args); + } + #[test] fn test_compile_not_cacheable_pdb() { let creator = new_creator(); @@ -2300,6 +2439,7 @@ mod test { let pdb = f.touch("foo.pdb").unwrap(); let parsed_args = ParsedArguments { input: "foo.c".into(), + double_dash_input: false, language: Language::C, compilation_flag: "/c".into(), depfile: None, diff --git a/src/compiler/tasking_vx.rs b/src/compiler/tasking_vx.rs index 1bd4a02a1..ee3d514a6 100644 --- a/src/compiler/tasking_vx.rs +++ b/src/compiler/tasking_vx.rs @@ -266,6 +266,7 @@ where CompilerArguments::Ok(ParsedArguments { input: input.into(), + double_dash_input: false, language, compilation_flag: "-c".into(), depfile, @@ -683,6 +684,7 @@ mod test { let f = TestFixture::new(); let parsed_args = ParsedArguments { input: "foo.c".into(), + double_dash_input: false, language: Language::C, compilation_flag: "-c".into(), depfile: None, @@ -731,6 +733,7 @@ mod test { let f = TestFixture::new(); let parsed_args = ParsedArguments { input: "foo.cu".into(), + double_dash_input: false, language: Language::Cuda, compilation_flag: "-c".into(), depfile: None,