From 590a974a8a1066d300d880fe3788e66c3a3caae0 Mon Sep 17 00:00:00 2001 From: Ronny Chevalier Date: Wed, 19 Jul 2017 21:30:32 +0200 Subject: [PATCH] config: add flag_if_supported It allows one to add a flag to the compiler on the condition that the compiler supports it. We check if the compiler supports the flag by trying to compile a simple executable. If there is some output on stderr we consider that the compiler failed and the flag is not supported. --- gcc-test/build.rs | 2 ++ src/lib.rs | 70 +++++++++++++++++++++++++++++++++++++++++------ tests/test.rs | 14 ++++++++++ 3 files changed, 77 insertions(+), 9 deletions(-) diff --git a/gcc-test/build.rs b/gcc-test/build.rs index 841f465..c78e8dd 100644 --- a/gcc-test/build.rs +++ b/gcc-test/build.rs @@ -11,6 +11,8 @@ fn main() { gcc::Config::new() .file("src/foo.c") + .flag_if_supported("-Wall") + .flag_if_supported("-Wfoo-bar-this-flag-does-not-exist") .define("FOO", None) .define("BAR", Some("1")) .compile("libfoo.a"); diff --git a/src/lib.rs b/src/lib.rs index a0e178d..127e2a2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -86,6 +86,7 @@ pub struct Config { static_crt: Option, shared_flag: Option, static_flag: Option, + check_file_created: bool, } /// Configuration used to represent an invocation of a C compiler. @@ -200,6 +201,7 @@ impl Config { cargo_metadata: true, pic: None, static_crt: None, + check_file_created: false, } } @@ -260,6 +262,51 @@ impl Config { self } + fn is_flag_supported(&mut self, flag: &str) -> io::Result { + let out_dir = self.get_out_dir(); + let src = out_dir.join("flag_check.c"); + if !self.check_file_created { + write!(fs::File::create(&src)?, "int main(void) {{ return 0; }}")?; + self.check_file_created = true; + } + + let obj = out_dir.join("flag_check"); + let target = self.get_target(); + let mut cfg = Config::new(); + cfg.flag(flag) + .target(&target) + .opt_level(0) + .host(&target) + .debug(false) + .cpp(self.cpp); + let compiler = cfg.get_compiler(); + let mut cmd = compiler.to_command(); + command_add_output_file(&mut cmd, &obj, target.contains("msvc"), false); + cmd.arg(&src); + + let output = cmd.output()?; + Ok(output.stderr.is_empty()) + } + + /// Add an arbitrary flag to the invocation of the compiler if it supports it + /// + /// # Example + /// + /// ```no_run + /// gcc::Config::new() + /// .file("src/foo.c") + /// .flag_if_supported("-Wlogical-op") // only supported by GCC + /// .flag_if_supported("-Wunreachable-code") // only supported by clang + /// .compile("foo"); + /// ``` + pub fn flag_if_supported(&mut self, flag: &str) -> &mut Config { + if self.is_flag_supported(flag).unwrap_or(false) { + self.flag(flag) + } else { + self + } + } + /// Set the `-shared` flag. /// /// When enabled, the compiler will produce a shared object which can @@ -597,15 +644,7 @@ impl Config { .to_string_lossy() .into_owned()) }; - if msvc && is_asm { - cmd.arg("/Fo").arg(dst); - } else if msvc { - let mut s = OsString::from("/Fo"); - s.push(&dst); - cmd.arg(s); - } else { - cmd.arg("-o").arg(&dst); - } + command_add_output_file(&mut cmd, dst, msvc, is_asm); cmd.arg(if msvc { "/c" } else { "-c" }); cmd.arg(file); @@ -1342,3 +1381,16 @@ fn fail(s: &str) -> ! { println!("\n\n{}\n\n", s); panic!() } + + +fn command_add_output_file(cmd: &mut Command, dst: &Path, msvc: bool, is_asm: bool) { + if msvc && is_asm { + cmd.arg("/Fo").arg(dst); + } else if msvc { + let mut s = OsString::from("/Fo"); + s.push(&dst); + cmd.arg(s); + } else { + cmd.arg("-o").arg(&dst); + } +} diff --git a/tests/test.rs b/tests/test.rs index 2be5ecd..c013099 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -183,6 +183,20 @@ fn gnu_shared() { .must_not_have("-static"); } +#[test] +fn gnu_flag_if_supported() { + let test = Test::gnu(); + test.gcc() + .file("foo.c") + .flag_if_supported("-Wall") + .flag_if_supported("-Wflag-does-not-exist") + .compile("libfoo.a"); + + test.cmd(0) + .must_have("-Wall") + .must_not_have("-Wflag-does-not-exist"); +} + #[test] fn gnu_static() { let test = Test::gnu();