diff --git a/src/lib.rs b/src/lib.rs index 9b2816fc..4c288d64 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -270,6 +270,8 @@ pub struct Build { known_flag_support_status_cache: Arc>>, ar_flags: Vec>, asm_flags: Vec>, + c_flags: Vec>, + cpp_flags: Vec>, no_default_flags: bool, files: Vec>, cpp: bool, @@ -393,6 +395,8 @@ impl Build { known_flag_support_status_cache: Arc::new(Mutex::new(HashMap::new())), ar_flags: Vec::new(), asm_flags: Vec::new(), + c_flags: Vec::new(), + cpp_flags: Vec::new(), no_default_flags: false, files: Vec::new(), shared_flag: None, @@ -569,6 +573,36 @@ impl Build { self } + /// Add an arbitrary flag to the invocation of the compiler for c files + /// + /// # Example + /// + /// ```no_run + /// cc::Build::new() + /// .file("src/foo.c") + /// .c_flag("-std=c99") + /// .compile("foo"); + /// ``` + pub fn c_flag(&mut self, flag: &str) -> &mut Build { + self.c_flags.push(flag.into()); + self + } + + /// Add an arbitrary flag to the invocation of the compiler for cpp files + /// + /// # Example + /// + /// ```no_run + /// cc::Build::new() + /// .file("src/foo.cpp") + /// .cpp_flag("-std=c++17") + /// .compile("foo"); + /// ``` + pub fn cpp_flag(&mut self, flag: &str) -> &mut Build { + self.cpp_flags.push(flag.into()); + self + } + fn ensure_check_file(&self) -> Result { let out_dir = self.get_out_dir()?; let src = if self.cuda { @@ -653,7 +687,7 @@ impl Build { compiler.push_cc_arg("-Wno-unused-command-line-argument".into()); } - let mut cmd = compiler.to_command(); + let mut cmd = compiler.to_command(None); let is_arm = target.contains("aarch64") || target.contains("arm"); let clang = compiler.is_like_clang(); let gnu = compiler.family == ToolFamily::Gnu; @@ -1614,7 +1648,7 @@ impl Build { let (cmd, name) = self.msvc_macro_assembler()?; (cmd, Cow::Borrowed(Path::new(name))) } else { - let mut cmd = compiler.to_command(); + let mut cmd = compiler.to_command(Some(&obj.src)); for (a, b) in self.env.iter() { cmd.env(a, b); } @@ -1670,7 +1704,7 @@ impl Build { /// This will return a result instead of panicking; see expand() for the complete description. pub fn try_expand(&self) -> Result, Error> { let compiler = self.try_get_compiler()?; - let mut cmd = compiler.to_command(); + let mut cmd = compiler.to_command(None); for (a, b) in self.env.iter() { cmd.env(a, b); } @@ -1828,6 +1862,14 @@ impl Build { cmd.push_cc_arg(warnings_to_errors_flag); } + for flag in self.c_flags.iter() { + cmd.c_args.push((**flag).into()); + } + + for flag in self.cpp_flags.iter() { + cmd.cpp_args.push((**flag).into()); + } + Ok(cmd) } @@ -2466,7 +2508,7 @@ impl Build { let out_dir = self.get_out_dir()?; let dlink = out_dir.join(lib_name.to_owned() + "_dlink.o"); - let mut nvcc = self.get_compiler().to_command(); + let mut nvcc = self.get_compiler().to_command(None); nvcc.arg("--device-link").arg("-o").arg(&dlink).arg(dst); run(&mut nvcc, "nvcc", &self.cargo_output)?; self.assemble_progressive(dst, &[dlink.as_path()])?; diff --git a/src/tool.rs b/src/tool.rs index ea0f36fa..aed1f41f 100644 --- a/src/tool.rs +++ b/src/tool.rs @@ -30,6 +30,8 @@ pub struct Tool { pub(crate) cc_wrapper_path: Option, pub(crate) cc_wrapper_args: Vec, pub(crate) args: Vec, + pub(crate) c_args: Vec, + pub(crate) cpp_args: Vec, pub(crate) env: Vec<(OsString, OsString)>, pub(crate) family: ToolFamily, pub(crate) cuda: bool, @@ -78,6 +80,8 @@ impl Tool { cc_wrapper_path: None, cc_wrapper_args: Vec::new(), args: Vec::new(), + c_args: Vec::new(), + cpp_args: Vec::new(), env: Vec::new(), family, cuda: false, @@ -219,6 +223,8 @@ impl Tool { cc_wrapper_path: None, cc_wrapper_args: Vec::new(), args: Vec::new(), + c_args: Vec::new(), + cpp_args: Vec::new(), env: Vec::new(), family, cuda, @@ -284,19 +290,72 @@ impl Tool { } } + /// Returns preferred compiler for source file. + fn preferred_compiler_for_source(&self, src: Option<&PathBuf>) -> (PathBuf, &[OsString]) { + let mut path = self.path.clone(); + let mut extra_args: &[OsString] = &[]; + if let Some(src) = src { + let mut is_c = false; + let mut is_cpp = false; + if let Some(ext) = src.extension().and_then(|x| x.to_str()) { + match ext { + "c" => { + is_c = true; + } + "cc" | "cpp" | "cxx" | "c++" => { + is_cpp = true; + } + _ => {} + } + } + match self.family { + ToolFamily::Clang { zig_cc } if !zig_cc => { + let s = path.to_string_lossy().to_string(); + if is_c { + path = PathBuf::from(s.replace("clang++", "clang")); + extra_args = &self.c_args; + } + if is_cpp { + if s.ends_with("clang") { + path = PathBuf::from(s.replace("clang", "clang++")); + } + extra_args = &self.cpp_args; + } + } + ToolFamily::Gnu => { + let s = path.to_string_lossy().to_string(); + if is_c { + path = PathBuf::from(s.replace("g++", "gcc")); + extra_args = &self.c_args; + } + if is_cpp { + path = PathBuf::from(s.replace("gcc", "g++")); + extra_args = &self.cpp_args; + } + } + _ => {} + } + } + (path, extra_args) + } + /// Converts this compiler into a `Command` that's ready to be run. /// /// This is useful for when the compiler needs to be executed and the /// command returned will already have the initial arguments and environment /// variables configured. - pub fn to_command(&self) -> Command { + /// + /// The `src` argument is used to determine the preferred compiler for the + /// source file. If `None`, the default compiler is used. + pub fn to_command(&self, src: Option<&PathBuf>) -> Command { + let (path, extra_args) = self.preferred_compiler_for_source(src); let mut cmd = match self.cc_wrapper_path { Some(ref cc_wrapper_path) => { let mut cmd = Command::new(cc_wrapper_path); - cmd.arg(&self.path); + cmd.arg(&path); cmd } - None => Command::new(&self.path), + None => Command::new(&path), }; cmd.args(&self.cc_wrapper_args); @@ -307,6 +366,8 @@ impl Tool { .collect::>(); cmd.args(&value); + cmd.args(extra_args); + for (k, v) in self.env.iter() { cmd.env(k, v); } diff --git a/src/windows/find_tools.rs b/src/windows/find_tools.rs index c4a92bde..68fad002 100644 --- a/src/windows/find_tools.rs +++ b/src/windows/find_tools.rs @@ -49,7 +49,7 @@ impl<'a> From> for &'a str { /// /// Note that this function always returns `None` for non-MSVC targets. pub fn find(target: &str, tool: &str) -> Option { - find_tool(target, tool).map(|c| c.to_command()) + find_tool(target, tool).map(|c| c.to_command(None)) } /// Similar to the `find` function above, this function will attempt the same