diff --git a/.travis.yml b/.travis.yml index ff57da5..f099e09 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ rust: matrix: include: # Minimum version supported - - rust: 1.6.0 + - rust: 1.9.0 install: script: cargo build diff --git a/README.md b/README.md index 7b9e376..5d46d93 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,8 @@ extern crate gcc; fn main() { gcc::Config::new() - .files(["foo.c", "bar.c"]) + .file("foo.c") + .file("bar.c") .compile("foo"); } ``` diff --git a/appveyor.yml b/appveyor.yml index f6108c6..aa1edb5 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,24 @@ environment: + + # At the time this was added AppVeyor was having troubles with checking + # revocation of SSL certificates of sites like static.rust-lang.org and what + # we think is crates.io. The libcurl HTTP client by default checks for + # revocation on Windows and according to a mailing list [1] this can be + # disabled. + # + # The `CARGO_HTTP_CHECK_REVOKE` env var here tells cargo to disable SSL + # revocation checking on Windows in libcurl. Note, though, that rustup, which + # we're using to download Rust here, also uses libcurl as the default backend. + # Unlike Cargo, however, rustup doesn't have a mechanism to disable revocation + # checking. To get rustup working we set `RUSTUP_USE_HYPER` which forces it to + # use the Hyper instead of libcurl backend. Both Hyper and libcurl use + # schannel on Windows but it appears that Hyper configures it slightly + # differently such that revocation checking isn't turned on by default. + # + # [1]: https://curl.haxx.se/mail/lib-2016-03/0202.html + RUSTUP_USE_HYPER: 1 + CARGO_HTTP_CHECK_REVOKE: false + matrix: - TARGET: x86_64-pc-windows-msvc ARCH: amd64 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 10de632..7744134 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 { + try!(write!(try!(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 = try!(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 @@ -499,10 +546,18 @@ impl Config { /// the `output` may start with `lib` and end with `.a`. The Rust compilier will create /// the assembly with the lib prefix and .a extension. MSVC will create a file without prefix, /// ending with `.lib`. + /// + /// # Panics + /// + /// Panics if `output` is not formatted correctly or if one of the underlying + /// compiler commands fails. It can also panic if it fails reading file names + /// or creating directories. pub fn compile(&self, output: &str) { - let name_start = if output.starts_with("lib") { 3 } else { 0 }; - let name_end = if output.ends_with(".a") { output.len() - 2 } else { output.len() }; - let lib_name = &output[name_start..name_end]; + let lib_name = if output.starts_with("lib") && output.ends_with(".a") { + &output[3..output.len() - 2] + } else { + &output + }; let dst = self.get_out_dir(); let mut objects = Vec::new(); @@ -591,15 +646,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); @@ -611,7 +658,8 @@ impl Config { /// This is only relevant for C and C++ files. /// /// # Panics - /// Panics if more than one file is present in the config. + /// Panics if more than one file is present in the config, or if compiler + /// path has an invalid file name. /// /// # Example /// ```no_run @@ -1346,3 +1394,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();