diff --git a/src/compiler/c.rs b/src/compiler/c.rs index 4193b1c613..92a5e05264 100644 --- a/src/compiler/c.rs +++ b/src/compiler/c.rs @@ -166,6 +166,8 @@ pub enum CCompilerKind { pub trait CCompilerImpl: Clone + fmt::Debug + Send + 'static { /// Return the kind of compiler. fn kind(&self) -> CCompilerKind; + /// Return true iff this is g++ or clang++. + fn plusplus(&self) -> bool; /// Determine whether `arguments` are supported by this compiler. fn parse_arguments( &self, @@ -344,6 +346,7 @@ where &extra_hashes, &env_vars, &preprocessor_result.stdout, + compiler.plusplus(), ) }; // A compiler binary may be a symlink to another and so has the same digest, but that means @@ -621,7 +624,7 @@ impl pkg::ToolchainPackager for CToolchainPackager { } /// The cache is versioned by the inputs to `hash_key`. -pub const CACHE_VERSION: &[u8] = b"9"; +pub const CACHE_VERSION: &[u8] = b"10"; lazy_static! { /// Environment variables that are factored into the cache key. @@ -639,10 +642,14 @@ pub fn hash_key( extra_hashes: &[String], env_vars: &[(OsString, OsString)], preprocessor_output: &[u8], + plusplus: bool, ) -> String { // If you change any of the inputs to the hash, you should change `CACHE_VERSION`. let mut m = Digest::new(); m.update(compiler_digest.as_bytes()); + // clang and clang++ have different behavior despite being byte-for-byte identical binaries, so + // we have to incorporate that into the hash as well. + m.update(&[plusplus as u8]); m.update(CACHE_VERSION); m.update(language.as_str().as_bytes()); for arg in arguments { @@ -667,13 +674,33 @@ pub fn hash_key( mod test { use super::*; + #[test] + fn test_same_content() { + let args = ovec!["a", "b", "c"]; + const PREPROCESSED: &[u8] = b"hello world"; + assert_eq!( + hash_key("abcd", Language::C, &args, &[], &[], &PREPROCESSED, false), + hash_key("abcd", Language::C, &args, &[], &[], &PREPROCESSED, false) + ); + } + + #[test] + fn test_plusplus_differs() { + let args = ovec!["a", "b", "c"]; + const PREPROCESSED: &[u8] = b"hello world"; + assert_neq!( + hash_key("abcd", Language::C, &args, &[], &[], &PREPROCESSED, false), + hash_key("abcd", Language::C, &args, &[], &[], &PREPROCESSED, true) + ); + } + #[test] fn test_hash_key_executable_contents_differs() { let args = ovec!["a", "b", "c"]; const PREPROCESSED: &[u8] = b"hello world"; assert_neq!( - hash_key("abcd", Language::C, &args, &[], &[], &PREPROCESSED), - hash_key("wxyz", Language::C, &args, &[], &[], &PREPROCESSED) + hash_key("abcd", Language::C, &args, &[], &[], &PREPROCESSED, false), + hash_key("wxyz", Language::C, &args, &[], &[], &PREPROCESSED, false) ); } @@ -686,18 +713,18 @@ mod test { let a = ovec!["a"]; const PREPROCESSED: &[u8] = b"hello world"; assert_neq!( - hash_key(digest, Language::C, &abc, &[], &[], &PREPROCESSED), - hash_key(digest, Language::C, &xyz, &[], &[], &PREPROCESSED) + hash_key(digest, Language::C, &abc, &[], &[], &PREPROCESSED, false), + hash_key(digest, Language::C, &xyz, &[], &[], &PREPROCESSED, false) ); assert_neq!( - hash_key(digest, Language::C, &abc, &[], &[], &PREPROCESSED), - hash_key(digest, Language::C, &ab, &[], &[], &PREPROCESSED) + hash_key(digest, Language::C, &abc, &[], &[], &PREPROCESSED, false), + hash_key(digest, Language::C, &ab, &[], &[], &PREPROCESSED, false) ); assert_neq!( - hash_key(digest, Language::C, &abc, &[], &[], &PREPROCESSED), - hash_key(digest, Language::C, &a, &[], &[], &PREPROCESSED) + hash_key(digest, Language::C, &abc, &[], &[], &PREPROCESSED, false), + hash_key(digest, Language::C, &a, &[], &[], &PREPROCESSED, false) ); } @@ -705,8 +732,16 @@ mod test { fn test_hash_key_preprocessed_content_differs() { let args = ovec!["a", "b", "c"]; assert_neq!( - hash_key("abcd", Language::C, &args, &[], &[], &b"hello world"[..]), - hash_key("abcd", Language::C, &args, &[], &[], &b"goodbye"[..]) + hash_key( + "abcd", + Language::C, + &args, + &[], + &[], + &b"hello world"[..], + false + ), + hash_key("abcd", Language::C, &args, &[], &[], &b"goodbye"[..], false) ); } @@ -716,11 +751,11 @@ mod test { let digest = "abcd"; const PREPROCESSED: &[u8] = b"hello world"; for var in CACHED_ENV_VARS.iter() { - let h1 = hash_key(digest, Language::C, &args, &[], &[], &PREPROCESSED); + let h1 = hash_key(digest, Language::C, &args, &[], &[], &PREPROCESSED, false); let vars = vec![(OsString::from(var), OsString::from("something"))]; - let h2 = hash_key(digest, Language::C, &args, &[], &vars, &PREPROCESSED); + let h2 = hash_key(digest, Language::C, &args, &[], &vars, &PREPROCESSED, false); let vars = vec![(OsString::from(var), OsString::from("something else"))]; - let h3 = hash_key(digest, Language::C, &args, &[], &vars, &PREPROCESSED); + let h3 = hash_key(digest, Language::C, &args, &[], &vars, &PREPROCESSED, false); assert_neq!(h1, h2); assert_neq!(h2, h3); } @@ -734,8 +769,16 @@ mod test { let extra_data = stringvec!["hello", "world"]; assert_neq!( - hash_key(digest, Language::C, &args, &extra_data, &[], &PREPROCESSED), - hash_key(digest, Language::C, &args, &[], &[], &PREPROCESSED) + hash_key( + digest, + Language::C, + &args, + &extra_data, + &[], + &PREPROCESSED, + false + ), + hash_key(digest, Language::C, &args, &[], &[], &PREPROCESSED, false) ); } } diff --git a/src/compiler/clang.rs b/src/compiler/clang.rs index 6331b6f3aa..af4c620ddc 100644 --- a/src/compiler/clang.rs +++ b/src/compiler/clang.rs @@ -30,20 +30,31 @@ use std::process; use crate::errors::*; -/// A unit struct on which to implement `CCompilerImpl`. +/// A struct on which to implement `CCompilerImpl`. #[derive(Clone, Debug)] -pub struct Clang; +pub struct Clang { + /// true iff this is clang++. + pub clangplusplus: bool, +} impl CCompilerImpl for Clang { fn kind(&self) -> CCompilerKind { CCompilerKind::Clang } + fn plusplus(&self) -> bool { + self.clangplusplus + } fn parse_arguments( &self, arguments: &[OsString], cwd: &Path, ) -> CompilerArguments { - gcc::parse_arguments(arguments, cwd, (&gcc::ARGS[..], &ARGS[..])) + gcc::parse_arguments( + arguments, + cwd, + (&gcc::ARGS[..], &ARGS[..]), + self.clangplusplus, + ) } fn preprocess( @@ -130,7 +141,10 @@ mod test { fn parse_arguments_(arguments: Vec) -> CompilerArguments { let arguments = arguments.iter().map(OsString::from).collect::>(); - Clang.parse_arguments(&arguments, ".".as_ref()) + Clang { + clangplusplus: false, + } + .parse_arguments(&arguments, ".".as_ref()) } macro_rules! parses { diff --git a/src/compiler/compiler.rs b/src/compiler/compiler.rs index 383d0cffe5..377e95b203 100644 --- a/src/compiler/compiler.rs +++ b/src/compiler/compiler.rs @@ -970,8 +970,12 @@ nvcc msvc-clang #elif defined(_MSC_VER) msvc +#elif defined(__clang__) && defined(__cplusplus) +clang++ #elif defined(__clang__) clang +#elif defined(__GNUC__) && defined(__cplusplus) +g++ #elif defined(__GNUC__) gcc #elif defined(__DCC__) @@ -1008,11 +1012,17 @@ diab for line in stdout.lines() { //TODO: do something smarter here. match line { - "clang" => { - debug!("Found clang"); + "clang" | "clang++" => { + debug!("Found {}", line); return Box::new( - CCompiler::new(Clang, executable, &pool) - .map(|c| Box::new(c) as Box>), + CCompiler::new( + Clang { + clangplusplus: line == "clang++", + }, + executable, + &pool, + ) + .map(|c| Box::new(c) as Box>), ); } "diab" => { @@ -1022,11 +1032,17 @@ diab .map(|c| Box::new(c) as Box>), ); } - "gcc" => { - debug!("Found GCC"); + "gcc" | "g++" => { + debug!("Found {}", line); return Box::new( - CCompiler::new(GCC, executable, &pool) - .map(|c| Box::new(c) as Box>), + CCompiler::new( + GCC { + gplusplus: line == "g++", + }, + executable, + &pool, + ) + .map(|c| Box::new(c) as Box>), ); } "msvc" | "msvc-clang" => { diff --git a/src/compiler/diab.rs b/src/compiler/diab.rs index 00c30198c9..ae02bd522e 100644 --- a/src/compiler/diab.rs +++ b/src/compiler/diab.rs @@ -38,6 +38,9 @@ impl CCompilerImpl for Diab { fn kind(&self) -> CCompilerKind { CCompilerKind::Diab } + fn plusplus(&self) -> bool { + false + } fn parse_arguments( &self, arguments: &[OsString], diff --git a/src/compiler/gcc.rs b/src/compiler/gcc.rs index eec1f9a9ec..ad7a305cc5 100644 --- a/src/compiler/gcc.rs +++ b/src/compiler/gcc.rs @@ -28,20 +28,25 @@ use std::process; use crate::errors::*; -/// A unit struct on which to implement `CCompilerImpl`. +/// A struct on which to implement `CCompilerImpl`. #[derive(Clone, Debug)] -pub struct GCC; +pub struct GCC { + pub gplusplus: bool, +} impl CCompilerImpl for GCC { fn kind(&self) -> CCompilerKind { CCompilerKind::GCC } + fn plusplus(&self) -> bool { + self.gplusplus + } fn parse_arguments( &self, arguments: &[OsString], cwd: &Path, ) -> CompilerArguments { - parse_arguments(arguments, cwd, &ARGS[..]) + parse_arguments(arguments, cwd, &ARGS[..], self.gplusplus) } fn preprocess( @@ -207,6 +212,7 @@ pub fn parse_arguments( arguments: &[OsString], cwd: &Path, arg_info: S, + plusplus: bool, ) -> CompilerArguments where S: SearchableArgInfo, @@ -420,9 +426,17 @@ where // We can't cache compilation without an input. None => cannot_cache!("no input file"), }; - if language == None { - language = Language::from_file_name(Path::new(&input)); - } + let language = match language { + None => { + let mut lang = Language::from_file_name(Path::new(&input)); + // g++ and clang++ override the language to c++ if the -stdlib argument is passed. + if let (Some(Language::C), true) = (lang, plusplus) { + lang = Some(Language::Cxx); + } + lang + } + l => l, + }; let language = match language { Some(l) => l, None => cannot_cache!("unknown source language"), @@ -708,9 +722,12 @@ mod test { use crate::test::utils::*; use futures::Future; - fn parse_arguments_(arguments: Vec) -> CompilerArguments { + fn parse_arguments_( + arguments: Vec, + plusplus: bool, + ) -> CompilerArguments { let args = arguments.iter().map(OsString::from).collect::>(); - parse_arguments(&args, ".".as_ref(), &ARGS[..]) + parse_arguments(&args, ".".as_ref(), &ARGS[..], plusplus) } #[test] @@ -725,7 +742,7 @@ mod test { msvc_show_includes, common_args, .. - } = match parse_arguments_(args) { + } = match parse_arguments_(args, false) { CompilerArguments::Ok(args) => args, o => panic!("Got unexpected parse result: {:?}", o), }; @@ -749,7 +766,7 @@ mod test { msvc_show_includes, common_args, .. - } = match parse_arguments_(args) { + } = match parse_arguments_(args, false) { CompilerArguments::Ok(args) => args, o => panic!("Got unexpected parse result: {:?}", o), }; @@ -764,7 +781,7 @@ mod test { #[test] fn test_parse_arguments_default_outputdir() { let args = stringvec!["-c", "/tmp/foo.c"]; - let ParsedArguments { outputs, .. } = match parse_arguments_(args) { + let ParsedArguments { outputs, .. } = match parse_arguments_(args, false) { CompilerArguments::Ok(args) => args, o => panic!("Got unexpected parse result: {:?}", o), }; @@ -782,7 +799,7 @@ mod test { msvc_show_includes, common_args, .. - } = match parse_arguments_(args) { + } = match parse_arguments_(args, false) { CompilerArguments::Ok(args) => args, o => panic!("Got unexpected parse result: {:?}", o), }; @@ -821,7 +838,7 @@ mod test { msvc_show_includes, common_args, .. - } = match parse_arguments_(args) { + } = match parse_arguments_(args, false) { CompilerArguments::Ok(args) => args, o => panic!("Got unexpected parse result: {:?}", o), }; @@ -845,7 +862,7 @@ mod test { common_args, profile_generate, .. - } = match parse_arguments_(args) { + } = match parse_arguments_(args, false) { CompilerArguments::Ok(args) => args, o => panic!("Got unexpected parse result: {:?}", o), }; @@ -874,7 +891,7 @@ mod test { common_args, profile_generate, .. - } = match parse_arguments_(args) { + } = match parse_arguments_(args, false) { CompilerArguments::Ok(args) => args, o => panic!("Got unexpected parse result: {:?}", o), }; @@ -903,7 +920,7 @@ mod test { common_args, profile_generate, .. - } = match parse_arguments_(args) { + } = match parse_arguments_(args, false) { CompilerArguments::Ok(args) => args, o => panic!("Got unexpected parse result: {:?}", o), }; @@ -927,7 +944,7 @@ mod test { msvc_show_includes, common_args, .. - } = match parse_arguments_(args) { + } = match parse_arguments_(args, false) { CompilerArguments::Ok(args) => args, o => panic!("Got unexpected parse result: {:?}", o), }; @@ -952,7 +969,7 @@ mod test { msvc_show_includes, common_args, .. - } = match parse_arguments_(args) { + } = match parse_arguments_(args, false) { CompilerArguments::Ok(args) => args, o => panic!("Got unexpected parse result: {:?}", o), }; @@ -987,7 +1004,7 @@ mod test { msvc_show_includes, common_args, .. - } = match parse_arguments_(args) { + } = match parse_arguments_(args, false) { CompilerArguments::Ok(args) => args, o => panic!("Got unexpected parse result: {:?}", o), }; @@ -1012,7 +1029,7 @@ mod test { msvc_show_includes, common_args, .. - } = match parse_arguments_(args) { + } = match parse_arguments_(args, false) { CompilerArguments::Ok(args) => args, o => panic!("Got unexpected parse result: {:?}", o), }; @@ -1038,7 +1055,7 @@ mod test { msvc_show_includes, common_args, .. - } = match parse_arguments_(args) { + } = match parse_arguments_(args, false) { CompilerArguments::Ok(args) => args, o => panic!("Got unexpected parse result: {:?}", o), }; @@ -1068,7 +1085,7 @@ mod test { msvc_show_includes, common_args, .. - } = match parse_arguments_(args) { + } = match parse_arguments_(args, false) { CompilerArguments::Ok(args) => args, o => panic!("Got unexpected parse result: {:?}", o), }; @@ -1088,7 +1105,7 @@ mod test { fn test_parse_arguments_diagnostics_color() { fn get_color_mode(color_flag: &str) -> ColorMode { let args = stringvec!["-c", "foo.c", color_flag]; - match parse_arguments_(args) { + match parse_arguments_(args, false) { CompilerArguments::Ok(args) => args.color_mode, o => panic!("Got unexpected parse result: {:?}", o), } @@ -1104,7 +1121,7 @@ mod test { #[test] fn color_mode_preprocess() { let args = stringvec!["-c", "foo.c", "-fdiagnostics-color"]; - let args = match parse_arguments_(args) { + let args = match parse_arguments_(args, false) { CompilerArguments::Ok(args) => args, o => panic!("Got unexpected parse result: {:?}", o), }; @@ -1123,7 +1140,7 @@ mod test { msvc_show_includes, common_args, .. - } = match parse_arguments_(args) { + } = match parse_arguments_(args, false) { CompilerArguments::Ok(args) => args, o => panic!("Got unexpected parse result: {:?}", o), }; @@ -1137,14 +1154,17 @@ mod test { #[test] fn test_parse_arguments_empty_args() { - assert_eq!(CompilerArguments::NotCompilation, parse_arguments_(vec!())); + assert_eq!( + CompilerArguments::NotCompilation, + parse_arguments_(vec!(), false) + ); } #[test] fn test_parse_arguments_not_compile() { assert_eq!( CompilerArguments::NotCompilation, - parse_arguments_(stringvec!["-o", "foo"]) + parse_arguments_(stringvec!["-o", "foo"], false) ); } @@ -1152,7 +1172,7 @@ mod test { fn test_parse_arguments_too_many_inputs() { assert_eq!( CompilerArguments::CannotCache("multiple input files", None), - parse_arguments_(stringvec!["-c", "foo.c", "-o", "foo.o", "bar.c"]) + parse_arguments_(stringvec!["-c", "foo.c", "-o", "foo.o", "bar.c"], false) ); } @@ -1160,7 +1180,10 @@ mod test { fn test_parse_arguments_link() { assert_eq!( CompilerArguments::NotCompilation, - parse_arguments_(stringvec!["-shared", "foo.o", "-o", "foo.so", "bar.o"]) + parse_arguments_( + stringvec!["-shared", "foo.o", "-o", "foo.so", "bar.o"], + false + ) ); } @@ -1168,17 +1191,17 @@ mod test { fn test_parse_arguments_pgo() { assert_eq!( CompilerArguments::CannotCache("-fprofile-use", None), - parse_arguments_(stringvec!["-c", "foo.c", "-fprofile-use", "-o", "foo.o"]) + parse_arguments_( + stringvec!["-c", "foo.c", "-fprofile-use", "-o", "foo.o"], + false + ) ); assert_eq!( CompilerArguments::CannotCache("-fprofile-use", None), - parse_arguments_(stringvec![ - "-c", - "foo.c", - "-fprofile-use=file", - "-o", - "foo.o" - ]) + parse_arguments_( + stringvec!["-c", "foo.c", "-fprofile-use=file", "-o", "foo.o"], + false + ) ); } @@ -1186,11 +1209,11 @@ mod test { fn test_parse_arguments_response_file() { assert_eq!( CompilerArguments::CannotCache("@", None), - parse_arguments_(stringvec!["-c", "foo.c", "@foo", "-o", "foo.o"]) + parse_arguments_(stringvec!["-c", "foo.c", "@foo", "-o", "foo.o"], false) ); assert_eq!( CompilerArguments::CannotCache("@", None), - parse_arguments_(stringvec!["-c", "foo.c", "-o", "@foo"]) + parse_arguments_(stringvec!["-c", "foo.c", "-o", "@foo"], false) ); } @@ -1217,7 +1240,7 @@ mod test { msvc_show_includes, common_args, .. - } = match parse_arguments_(vec![arg]) { + } = match parse_arguments_(vec![arg], false) { CompilerArguments::Ok(args) => args, o => panic!("Got unexpected parse result: {:?}", o), }; @@ -1270,4 +1293,29 @@ mod test { // Ensure that we ran all processes. assert_eq!(0, creator.lock().unwrap().children.len()); } + + #[test] + fn test_parse_arguments_plusplus() { + let args = stringvec!["-c", "foo.c", "-o", "foo.o"]; + let ParsedArguments { + input, + language, + compilation_flag, + outputs, + preprocessor_args, + msvc_show_includes, + common_args, + .. + } = match parse_arguments_(args, true) { + CompilerArguments::Ok(args) => args, + o => panic!("Got unexpected parse result: {:?}", o), + }; + assert_eq!(Some("foo.c"), input.to_str()); + assert_eq!(Language::Cxx, language); + assert_eq!(Some("-c"), compilation_flag.to_str()); + assert_map_contains!(outputs, ("obj", PathBuf::from("foo.o"))); + assert!(preprocessor_args.is_empty()); + assert!(common_args.is_empty()); + assert!(!msvc_show_includes); + } } diff --git a/src/compiler/msvc.rs b/src/compiler/msvc.rs index b822cf4690..89dcb2f0b7 100644 --- a/src/compiler/msvc.rs +++ b/src/compiler/msvc.rs @@ -47,6 +47,9 @@ impl CCompilerImpl for MSVC { fn kind(&self) -> CCompilerKind { CCompilerKind::MSVC } + fn plusplus(&self) -> bool { + false + } fn parse_arguments( &self, arguments: &[OsString], diff --git a/src/compiler/nvcc.rs b/src/compiler/nvcc.rs index b6b64567c8..9c9b72c933 100644 --- a/src/compiler/nvcc.rs +++ b/src/compiler/nvcc.rs @@ -39,12 +39,15 @@ impl CCompilerImpl for NVCC { fn kind(&self) -> CCompilerKind { CCompilerKind::NVCC } + fn plusplus(&self) -> bool { + false + } fn parse_arguments( &self, arguments: &[OsString], cwd: &Path, ) -> CompilerArguments { - gcc::parse_arguments(arguments, cwd, (&gcc::ARGS[..], &ARGS[..])) + gcc::parse_arguments(arguments, cwd, (&gcc::ARGS[..], &ARGS[..]), false) } fn preprocess(