|  | //! A library for build scripts to compile custom C code | 
|  | //! | 
|  | //! This library is intended to be used as a `build-dependencies` entry in | 
|  | //! `Cargo.toml`: | 
|  | //! | 
|  | //! ```toml | 
|  | //! [build-dependencies] | 
|  | //! gcc = "0.3" | 
|  | //! ``` | 
|  | //! | 
|  | //! The purpose of this crate is to provide the utility functions necessary to | 
|  | //! compile C code into a static archive which is then linked into a Rust crate. | 
|  | //! Configuration is available through the `Build` struct. | 
|  | //! | 
|  | //! This crate will automatically detect situations such as cross compilation or | 
|  | //! other environment variables set by Cargo and will build code appropriately. | 
|  | //! | 
|  | //! The crate is not limited to C code, it can accept any source code that can | 
|  | //! be passed to a C or C++ compiler. As such, assembly files with extensions | 
|  | //! `.s` (gcc/clang) and `.asm` (MSVC) can also be compiled. | 
|  | //! | 
|  | //! [`Build`]: struct.Build.html | 
|  | //! | 
|  | //! # Examples | 
|  | //! | 
|  | //! Use the `Build` struct to compile `src/foo.c`: | 
|  | //! | 
|  | //! ```no_run | 
|  | //! extern crate gcc; | 
|  | //! | 
|  | //! fn main() { | 
|  | //!     gcc::Build::new() | 
|  | //!                .file("src/foo.c") | 
|  | //!                .define("FOO", Some("bar")) | 
|  | //!                .include("src") | 
|  | //!                .compile("foo"); | 
|  | //! } | 
|  | //! ``` | 
|  |  | 
|  | #![doc(html_root_url = "https://docs.rs/gcc/0.3")] | 
|  | #![cfg_attr(test, deny(warnings))] | 
|  | #![deny(missing_docs)] | 
|  |  | 
|  | #[cfg(feature = "parallel")] | 
|  | extern crate rayon; | 
|  |  | 
|  | use std::env; | 
|  | use std::ffi::{OsString, OsStr}; | 
|  | use std::fs; | 
|  | use std::path::{PathBuf, Path}; | 
|  | use std::process::{Command, Stdio, Child}; | 
|  | use std::io::{self, BufReader, BufRead, Read, Write}; | 
|  | use std::thread::{self, JoinHandle}; | 
|  |  | 
|  | #[doc(hidden)] | 
|  | #[deprecated(since="0.3.51", note="gcc::Config has been renamed to gcc::Build")] | 
|  | pub type Config = Build; | 
|  |  | 
|  | #[cfg(feature = "parallel")] | 
|  | use std::sync::Mutex; | 
|  |  | 
|  | // These modules are all glue to support reading the MSVC version from | 
|  | // the registry and from COM interfaces | 
|  | #[cfg(windows)] | 
|  | mod registry; | 
|  | #[cfg(windows)] | 
|  | #[macro_use] | 
|  | mod winapi; | 
|  | #[cfg(windows)] | 
|  | mod com; | 
|  | #[cfg(windows)] | 
|  | mod setup_config; | 
|  |  | 
|  | pub mod windows_registry; | 
|  |  | 
|  | /// Extra configuration to pass to gcc. | 
|  | #[derive(Clone, Debug)] | 
|  | #[deprecated(note = "crate has been renamed to `cc`, the `gcc` name is not maintained")] | 
|  | pub struct Build { | 
|  | include_directories: Vec<PathBuf>, | 
|  | definitions: Vec<(String, Option<String>)>, | 
|  | objects: Vec<PathBuf>, | 
|  | flags: Vec<String>, | 
|  | flags_supported: Vec<String>, | 
|  | files: Vec<PathBuf>, | 
|  | cpp: bool, | 
|  | cpp_link_stdlib: Option<Option<String>>, | 
|  | cpp_set_stdlib: Option<String>, | 
|  | target: Option<String>, | 
|  | host: Option<String>, | 
|  | out_dir: Option<PathBuf>, | 
|  | opt_level: Option<String>, | 
|  | debug: Option<bool>, | 
|  | env: Vec<(OsString, OsString)>, | 
|  | compiler: Option<PathBuf>, | 
|  | archiver: Option<PathBuf>, | 
|  | cargo_metadata: bool, | 
|  | pic: Option<bool>, | 
|  | static_crt: Option<bool>, | 
|  | shared_flag: Option<bool>, | 
|  | static_flag: Option<bool>, | 
|  | warnings_into_errors: bool, | 
|  | warnings: bool, | 
|  | } | 
|  |  | 
|  | /// Represents the types of errors that may occur while using gcc-rs. | 
|  | #[derive(Clone, Debug)] | 
|  | enum ErrorKind { | 
|  | /// Error occurred while performing I/O. | 
|  | IOError, | 
|  | /// Invalid architecture supplied. | 
|  | ArchitectureInvalid, | 
|  | /// Environment variable not found, with the var in question as extra info. | 
|  | EnvVarNotFound, | 
|  | /// Error occurred while using external tools (ie: invocation of compiler). | 
|  | ToolExecError, | 
|  | /// Error occurred due to missing external tools. | 
|  | ToolNotFound, | 
|  | } | 
|  |  | 
|  | /// Represents an internal error that occurred, with an explaination. | 
|  | #[derive(Clone, Debug)] | 
|  | pub struct Error { | 
|  | /// Describes the kind of error that occurred. | 
|  | kind: ErrorKind, | 
|  | /// More explaination of error that occurred. | 
|  | message: String, | 
|  | } | 
|  |  | 
|  | impl Error { | 
|  | fn new(kind: ErrorKind, message: &str) -> Error { | 
|  | Error { kind: kind, message: message.to_owned() } | 
|  | } | 
|  | } | 
|  |  | 
|  | impl From<io::Error> for Error { | 
|  | fn from(e: io::Error) -> Error { | 
|  | Error::new(ErrorKind::IOError, &format!("{}", e)) | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Configuration used to represent an invocation of a C compiler. | 
|  | /// | 
|  | /// This can be used to figure out what compiler is in use, what the arguments | 
|  | /// to it are, and what the environment variables look like for the compiler. | 
|  | /// This can be used to further configure other build systems (e.g. forward | 
|  | /// along CC and/or CFLAGS) or the `to_command` method can be used to run the | 
|  | /// compiler itself. | 
|  | #[derive(Clone, Debug)] | 
|  | pub struct Tool { | 
|  | path: PathBuf, | 
|  | args: Vec<OsString>, | 
|  | env: Vec<(OsString, OsString)>, | 
|  | family: ToolFamily | 
|  | } | 
|  |  | 
|  | /// Represents the family of tools this tool belongs to. | 
|  | /// | 
|  | /// Each family of tools differs in how and what arguments they accept. | 
|  | /// | 
|  | /// Detection of a family is done on best-effort basis and may not accurately reflect the tool. | 
|  | #[derive(Copy, Clone, Debug, PartialEq)] | 
|  | enum ToolFamily { | 
|  | /// Tool is GNU Compiler Collection-like. | 
|  | Gnu, | 
|  | /// Tool is Clang-like. It differs from the GCC in a sense that it accepts superset of flags | 
|  | /// and its cross-compilation approach is different. | 
|  | Clang, | 
|  | /// Tool is the MSVC cl.exe. | 
|  | Msvc, | 
|  | } | 
|  |  | 
|  | impl ToolFamily { | 
|  | /// What the flag to request debug info for this family of tools look like | 
|  | fn debug_flag(&self) -> &'static str { | 
|  | match *self { | 
|  | ToolFamily::Msvc => "/Z7", | 
|  | ToolFamily::Gnu | | 
|  | ToolFamily::Clang => "-g", | 
|  | } | 
|  | } | 
|  |  | 
|  | /// What the flag to include directories into header search path looks like | 
|  | fn include_flag(&self) -> &'static str { | 
|  | match *self { | 
|  | ToolFamily::Msvc => "/I", | 
|  | ToolFamily::Gnu | | 
|  | ToolFamily::Clang => "-I", | 
|  | } | 
|  | } | 
|  |  | 
|  | /// What the flag to request macro-expanded source output looks like | 
|  | fn expand_flag(&self) -> &'static str { | 
|  | match *self { | 
|  | ToolFamily::Msvc => "/E", | 
|  | ToolFamily::Gnu | | 
|  | ToolFamily::Clang => "-E", | 
|  | } | 
|  | } | 
|  |  | 
|  | /// What the flags to enable all warnings | 
|  | fn warnings_flags(&self) -> &'static [&'static str] { | 
|  | static MSVC_FLAGS: &'static [&'static str] = &["/W4"]; | 
|  | static GNU_CLANG_FLAGS: &'static [&'static str] = &["-Wall", "-Wextra"]; | 
|  |  | 
|  | match *self { | 
|  | ToolFamily::Msvc => &MSVC_FLAGS, | 
|  | ToolFamily::Gnu | | 
|  | ToolFamily::Clang => &GNU_CLANG_FLAGS, | 
|  | } | 
|  | } | 
|  |  | 
|  | /// What the flag to turn warning into errors | 
|  | fn warnings_to_errors_flag(&self) -> &'static str { | 
|  | match *self { | 
|  | ToolFamily::Msvc => "/WX", | 
|  | ToolFamily::Gnu | | 
|  | ToolFamily::Clang => "-Werror" | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Compile a library from the given set of input C files. | 
|  | /// | 
|  | /// This will simply compile all files into object files and then assemble them | 
|  | /// into the output. This will read the standard environment variables to detect | 
|  | /// cross compilations and such. | 
|  | /// | 
|  | /// This function will also print all metadata on standard output for Cargo. | 
|  | /// | 
|  | /// # Example | 
|  | /// | 
|  | /// ```no_run | 
|  | /// gcc::compile_library("foo", &["foo.c", "bar.c"]); | 
|  | /// ``` | 
|  | #[doc(hidden)] | 
|  | #[deprecated(note = "crate has been renamed to `cc`, the `gcc` name is not maintained")] | 
|  | pub fn compile_library(output: &str, files: &[&str]) { | 
|  | let mut c = Build::new(); | 
|  | for f in files.iter() { | 
|  | c.file(*f); | 
|  | } | 
|  | c.compile(output); | 
|  | } | 
|  |  | 
|  | impl Build { | 
|  | /// Construct a new instance of a blank set of configuration. | 
|  | /// | 
|  | /// This builder is finished with the [`compile`] function. | 
|  | /// | 
|  | /// [`compile`]: struct.Build.html#method.compile | 
|  | #[deprecated(note = "crate has been renamed to `cc`, the `gcc` name is not maintained")] | 
|  | pub fn new() -> Build { | 
|  | Build { | 
|  | include_directories: Vec::new(), | 
|  | definitions: Vec::new(), | 
|  | objects: Vec::new(), | 
|  | flags: Vec::new(), | 
|  | flags_supported: Vec::new(), | 
|  | files: Vec::new(), | 
|  | shared_flag: None, | 
|  | static_flag: None, | 
|  | cpp: false, | 
|  | cpp_link_stdlib: None, | 
|  | cpp_set_stdlib: None, | 
|  | target: None, | 
|  | host: None, | 
|  | out_dir: None, | 
|  | opt_level: None, | 
|  | debug: None, | 
|  | env: Vec::new(), | 
|  | compiler: None, | 
|  | archiver: None, | 
|  | cargo_metadata: true, | 
|  | pic: None, | 
|  | static_crt: None, | 
|  | warnings: true, | 
|  | warnings_into_errors: false, | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Add a directory to the `-I` or include path for headers | 
|  | /// | 
|  | /// # Example | 
|  | /// | 
|  | /// ```no_run | 
|  | /// use std::path::Path; | 
|  | /// | 
|  | /// let library_path = Path::new("/path/to/library"); | 
|  | /// | 
|  | /// gcc::Build::new() | 
|  | ///            .file("src/foo.c") | 
|  | ///            .include(library_path) | 
|  | ///            .include("src") | 
|  | ///            .compile("foo"); | 
|  | /// ``` | 
|  | pub fn include<P: AsRef<Path>>(&mut self, dir: P) -> &mut Build { | 
|  | self.include_directories.push(dir.as_ref().to_path_buf()); | 
|  | self | 
|  | } | 
|  |  | 
|  | /// Specify a `-D` variable with an optional value. | 
|  | /// | 
|  | /// # Example | 
|  | /// | 
|  | /// ```no_run | 
|  | /// gcc::Build::new() | 
|  | ///            .file("src/foo.c") | 
|  | ///            .define("FOO", "BAR") | 
|  | ///            .define("BAZ", None) | 
|  | ///            .compile("foo"); | 
|  | /// ``` | 
|  | pub fn define<'a, V: Into<Option<&'a str>>>(&mut self, var: &str, val: V) -> &mut Build { | 
|  | self.definitions.push((var.to_string(), val.into().map(|s| s.to_string()))); | 
|  | self | 
|  | } | 
|  |  | 
|  | /// Add an arbitrary object file to link in | 
|  | pub fn object<P: AsRef<Path>>(&mut self, obj: P) -> &mut Build { | 
|  | self.objects.push(obj.as_ref().to_path_buf()); | 
|  | self | 
|  | } | 
|  |  | 
|  | /// Add an arbitrary flag to the invocation of the compiler | 
|  | /// | 
|  | /// # Example | 
|  | /// | 
|  | /// ```no_run | 
|  | /// gcc::Build::new() | 
|  | ///            .file("src/foo.c") | 
|  | ///            .flag("-ffunction-sections") | 
|  | ///            .compile("foo"); | 
|  | /// ``` | 
|  | pub fn flag(&mut self, flag: &str) -> &mut Build { | 
|  | self.flags.push(flag.to_string()); | 
|  | self | 
|  | } | 
|  |  | 
|  | fn ensure_check_file(&self) -> Result<PathBuf, Error> { | 
|  | let out_dir = self.get_out_dir()?; | 
|  | let src = if self.cpp { | 
|  | out_dir.join("flag_check.cpp") | 
|  | } else { | 
|  | out_dir.join("flag_check.c") | 
|  | }; | 
|  |  | 
|  | if !src.exists() { | 
|  | let mut f = fs::File::create(&src)?; | 
|  | write!(f, "int main(void) {{ return 0; }}")?; | 
|  | } | 
|  |  | 
|  | Ok(src) | 
|  | } | 
|  |  | 
|  | fn is_flag_supported(&self, flag: &str) -> Result<bool, Error> { | 
|  | let out_dir = self.get_out_dir()?; | 
|  | let src = self.ensure_check_file()?; | 
|  | let obj = out_dir.join("flag_check"); | 
|  | let target = self.get_target()?; | 
|  | let mut cfg = Build::new(); | 
|  | cfg.flag(flag) | 
|  | .target(&target) | 
|  | .opt_level(0) | 
|  | .host(&target) | 
|  | .debug(false) | 
|  | .cpp(self.cpp); | 
|  | let compiler = cfg.try_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::Build::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 Build { | 
|  | self.flags_supported.push(flag.to_string()); | 
|  | self | 
|  | } | 
|  |  | 
|  | /// Set the `-shared` flag. | 
|  | /// | 
|  | /// When enabled, the compiler will produce a shared object which can | 
|  | /// then be linked with other objects to form an executable. | 
|  | /// | 
|  | /// # Example | 
|  | /// | 
|  | /// ```no_run | 
|  | /// gcc::Build::new() | 
|  | ///            .file("src/foo.c") | 
|  | ///            .shared_flag(true) | 
|  | ///            .compile("libfoo.so"); | 
|  | /// ``` | 
|  |  | 
|  | pub fn shared_flag(&mut self, shared_flag: bool) -> &mut Build { | 
|  | self.shared_flag = Some(shared_flag); | 
|  | self | 
|  | } | 
|  |  | 
|  | /// Set the `-static` flag. | 
|  | /// | 
|  | /// When enabled on systems that support dynamic linking, this prevents | 
|  | /// linking with the shared libraries. | 
|  | /// | 
|  | /// # Example | 
|  | /// | 
|  | /// ```no_run | 
|  | /// gcc::Build::new() | 
|  | ///            .file("src/foo.c") | 
|  | ///            .shared_flag(true) | 
|  | ///            .static_flag(true) | 
|  | ///            .compile("foo"); | 
|  | /// ``` | 
|  | pub fn static_flag(&mut self, static_flag: bool) -> &mut Build { | 
|  | self.static_flag = Some(static_flag); | 
|  | self | 
|  | } | 
|  |  | 
|  | /// Add a file which will be compiled | 
|  | pub fn file<P: AsRef<Path>>(&mut self, p: P) -> &mut Build { | 
|  | self.files.push(p.as_ref().to_path_buf()); | 
|  | self | 
|  | } | 
|  |  | 
|  | /// Add files which will be compiled | 
|  | pub fn files<P>(&mut self, p: P) -> &mut Build | 
|  | where P: IntoIterator, | 
|  | P::Item: AsRef<Path> { | 
|  | for file in p.into_iter() { | 
|  | self.file(file); | 
|  | } | 
|  | self | 
|  | } | 
|  |  | 
|  | /// Set C++ support. | 
|  | /// | 
|  | /// The other `cpp_*` options will only become active if this is set to | 
|  | /// `true`. | 
|  | pub fn cpp(&mut self, cpp: bool) -> &mut Build { | 
|  | self.cpp = cpp; | 
|  | self | 
|  | } | 
|  |  | 
|  | /// Set warnings into errors flag. | 
|  | /// | 
|  | /// Disabled by default. | 
|  | /// | 
|  | /// Warning: turning warnings into errors only make sense | 
|  | /// if you are a developer of the crate using gcc-rs. | 
|  | /// Some warnings only appear on some architecture or | 
|  | /// specific version of the compiler. Any user of this crate, | 
|  | /// or any other crate depending on it, could fail during | 
|  | /// compile time. | 
|  | /// | 
|  | /// # Example | 
|  | /// | 
|  | /// ```no_run | 
|  | /// gcc::Build::new() | 
|  | ///            .file("src/foo.c") | 
|  | ///            .warnings_into_errors(true) | 
|  | ///            .compile("libfoo.a"); | 
|  | /// ``` | 
|  | pub fn warnings_into_errors(&mut self, warnings_into_errors: bool) -> &mut Build { | 
|  | self.warnings_into_errors = warnings_into_errors; | 
|  | self | 
|  | } | 
|  |  | 
|  | /// Set warnings flags. | 
|  | /// | 
|  | /// Adds some flags: | 
|  | /// - "/Wall" for MSVC. | 
|  | /// - "-Wall", "-Wextra" for GNU and Clang. | 
|  | /// | 
|  | /// Enabled by default. | 
|  | /// | 
|  | /// # Example | 
|  | /// | 
|  | /// ```no_run | 
|  | /// gcc::Build::new() | 
|  | ///            .file("src/foo.c") | 
|  | ///            .warnings(false) | 
|  | ///            .compile("libfoo.a"); | 
|  | /// ``` | 
|  | pub fn warnings(&mut self, warnings: bool) -> &mut Build { | 
|  | self.warnings = warnings; | 
|  | self | 
|  | } | 
|  |  | 
|  | /// Set the standard library to link against when compiling with C++ | 
|  | /// support. | 
|  | /// | 
|  | /// The default value of this property depends on the current target: On | 
|  | /// OS X `Some("c++")` is used, when compiling for a Visual Studio based | 
|  | /// target `None` is used and for other targets `Some("stdc++")` is used. | 
|  | /// | 
|  | /// A value of `None` indicates that no automatic linking should happen, | 
|  | /// otherwise cargo will link against the specified library. | 
|  | /// | 
|  | /// The given library name must not contain the `lib` prefix. | 
|  | /// | 
|  | /// Common values: | 
|  | /// - `stdc++` for GNU | 
|  | /// - `c++` for Clang | 
|  | /// | 
|  | /// # Example | 
|  | /// | 
|  | /// ```no_run | 
|  | /// gcc::Build::new() | 
|  | ///            .file("src/foo.c") | 
|  | ///            .shared_flag(true) | 
|  | ///            .cpp_link_stdlib("stdc++") | 
|  | ///            .compile("libfoo.so"); | 
|  | /// ``` | 
|  | pub fn cpp_link_stdlib<'a, V: Into<Option<&'a str>>>(&mut self, cpp_link_stdlib: V) -> &mut Build { | 
|  | self.cpp_link_stdlib = Some(cpp_link_stdlib.into().map(|s| s.into())); | 
|  | self | 
|  | } | 
|  |  | 
|  | /// Force the C++ compiler to use the specified standard library. | 
|  | /// | 
|  | /// Setting this option will automatically set `cpp_link_stdlib` to the same | 
|  | /// value. | 
|  | /// | 
|  | /// The default value of this option is always `None`. | 
|  | /// | 
|  | /// This option has no effect when compiling for a Visual Studio based | 
|  | /// target. | 
|  | /// | 
|  | /// This option sets the `-stdlib` flag, which is only supported by some | 
|  | /// compilers (clang, icc) but not by others (gcc). The library will not | 
|  | /// detect which compiler is used, as such it is the responsibility of the | 
|  | /// caller to ensure that this option is only used in conjuction with a | 
|  | /// compiler which supports the `-stdlib` flag. | 
|  | /// | 
|  | /// A value of `None` indicates that no specific C++ standard library should | 
|  | /// be used, otherwise `-stdlib` is added to the compile invocation. | 
|  | /// | 
|  | /// The given library name must not contain the `lib` prefix. | 
|  | /// | 
|  | /// Common values: | 
|  | /// - `stdc++` for GNU | 
|  | /// - `c++` for Clang | 
|  | /// | 
|  | /// # Example | 
|  | /// | 
|  | /// ```no_run | 
|  | /// gcc::Build::new() | 
|  | ///            .file("src/foo.c") | 
|  | ///            .cpp_set_stdlib("c++") | 
|  | ///            .compile("libfoo.a"); | 
|  | /// ``` | 
|  | pub fn cpp_set_stdlib<'a, V: Into<Option<&'a str>>>(&mut self, cpp_set_stdlib: V) -> &mut Build { | 
|  | let cpp_set_stdlib = cpp_set_stdlib.into(); | 
|  | self.cpp_set_stdlib = cpp_set_stdlib.map(|s| s.into()); | 
|  | self.cpp_link_stdlib(cpp_set_stdlib); | 
|  | self | 
|  | } | 
|  |  | 
|  | /// Configures the target this configuration will be compiling for. | 
|  | /// | 
|  | /// This option is automatically scraped from the `TARGET` environment | 
|  | /// variable by build scripts, so it's not required to call this function. | 
|  | /// | 
|  | /// # Example | 
|  | /// | 
|  | /// ```no_run | 
|  | /// gcc::Build::new() | 
|  | ///            .file("src/foo.c") | 
|  | ///            .target("aarch64-linux-android") | 
|  | ///            .compile("foo"); | 
|  | /// ``` | 
|  | pub fn target(&mut self, target: &str) -> &mut Build { | 
|  | self.target = Some(target.to_string()); | 
|  | self | 
|  | } | 
|  |  | 
|  | /// Configures the host assumed by this configuration. | 
|  | /// | 
|  | /// This option is automatically scraped from the `HOST` environment | 
|  | /// variable by build scripts, so it's not required to call this function. | 
|  | /// | 
|  | /// # Example | 
|  | /// | 
|  | /// ```no_run | 
|  | /// gcc::Build::new() | 
|  | ///            .file("src/foo.c") | 
|  | ///            .host("arm-linux-gnueabihf") | 
|  | ///            .compile("foo"); | 
|  | /// ``` | 
|  | pub fn host(&mut self, host: &str) -> &mut Build { | 
|  | self.host = Some(host.to_string()); | 
|  | self | 
|  | } | 
|  |  | 
|  | /// Configures the optimization level of the generated object files. | 
|  | /// | 
|  | /// This option is automatically scraped from the `OPT_LEVEL` environment | 
|  | /// variable by build scripts, so it's not required to call this function. | 
|  | pub fn opt_level(&mut self, opt_level: u32) -> &mut Build { | 
|  | self.opt_level = Some(opt_level.to_string()); | 
|  | self | 
|  | } | 
|  |  | 
|  | /// Configures the optimization level of the generated object files. | 
|  | /// | 
|  | /// This option is automatically scraped from the `OPT_LEVEL` environment | 
|  | /// variable by build scripts, so it's not required to call this function. | 
|  | pub fn opt_level_str(&mut self, opt_level: &str) -> &mut Build { | 
|  | self.opt_level = Some(opt_level.to_string()); | 
|  | self | 
|  | } | 
|  |  | 
|  | /// Configures whether the compiler will emit debug information when | 
|  | /// generating object files. | 
|  | /// | 
|  | /// This option is automatically scraped from the `PROFILE` environment | 
|  | /// variable by build scripts (only enabled when the profile is "debug"), so | 
|  | /// it's not required to call this function. | 
|  | pub fn debug(&mut self, debug: bool) -> &mut Build { | 
|  | self.debug = Some(debug); | 
|  | self | 
|  | } | 
|  |  | 
|  | /// Configures the output directory where all object files and static | 
|  | /// libraries will be located. | 
|  | /// | 
|  | /// This option is automatically scraped from the `OUT_DIR` environment | 
|  | /// variable by build scripts, so it's not required to call this function. | 
|  | pub fn out_dir<P: AsRef<Path>>(&mut self, out_dir: P) -> &mut Build { | 
|  | self.out_dir = Some(out_dir.as_ref().to_owned()); | 
|  | self | 
|  | } | 
|  |  | 
|  | /// Configures the compiler to be used to produce output. | 
|  | /// | 
|  | /// This option is automatically determined from the target platform or a | 
|  | /// number of environment variables, so it's not required to call this | 
|  | /// function. | 
|  | pub fn compiler<P: AsRef<Path>>(&mut self, compiler: P) -> &mut Build { | 
|  | self.compiler = Some(compiler.as_ref().to_owned()); | 
|  | self | 
|  | } | 
|  |  | 
|  | /// Configures the tool used to assemble archives. | 
|  | /// | 
|  | /// This option is automatically determined from the target platform or a | 
|  | /// number of environment variables, so it's not required to call this | 
|  | /// function. | 
|  | pub fn archiver<P: AsRef<Path>>(&mut self, archiver: P) -> &mut Build { | 
|  | self.archiver = Some(archiver.as_ref().to_owned()); | 
|  | self | 
|  | } | 
|  | /// Define whether metadata should be emitted for cargo allowing it to | 
|  | /// automatically link the binary. Defaults to `true`. | 
|  | /// | 
|  | /// The emitted metadata is: | 
|  | /// | 
|  | ///  - `rustc-link-lib=static=`*compiled lib* | 
|  | ///  - `rustc-link-search=native=`*target folder* | 
|  | ///  - When target is MSVC, the ATL-MFC libs are added via `rustc-link-search=native=` | 
|  | ///  - When C++ is enabled, the C++ stdlib is added via `rustc-link-lib` | 
|  | /// | 
|  | pub fn cargo_metadata(&mut self, cargo_metadata: bool) -> &mut Build { | 
|  | self.cargo_metadata = cargo_metadata; | 
|  | self | 
|  | } | 
|  |  | 
|  | /// Configures whether the compiler will emit position independent code. | 
|  | /// | 
|  | /// This option defaults to `false` for `windows-gnu` targets and | 
|  | /// to `true` for all other targets. | 
|  | pub fn pic(&mut self, pic: bool) -> &mut Build { | 
|  | self.pic = Some(pic); | 
|  | self | 
|  | } | 
|  |  | 
|  | /// Configures whether the /MT flag or the /MD flag will be passed to msvc build tools. | 
|  | /// | 
|  | /// This option defaults to `false`, and affect only msvc targets. | 
|  | pub fn static_crt(&mut self, static_crt: bool) -> &mut Build { | 
|  | self.static_crt = Some(static_crt); | 
|  | self | 
|  | } | 
|  |  | 
|  | #[doc(hidden)] | 
|  | pub fn __set_env<A, B>(&mut self, a: A, b: B) -> &mut Build | 
|  | where A: AsRef<OsStr>, | 
|  | B: AsRef<OsStr> | 
|  | { | 
|  | self.env.push((a.as_ref().to_owned(), b.as_ref().to_owned())); | 
|  | self | 
|  | } | 
|  |  | 
|  | /// Run the compiler, generating the file `output` | 
|  | /// | 
|  | /// This will return a result instead of panicing; see compile() for the complete description. | 
|  | pub fn try_compile(&self, output: &str) -> Result<(), Error> { | 
|  | let (lib_name, gnu_lib_name) = if output.starts_with("lib") && output.ends_with(".a") { | 
|  | (&output[3..output.len() - 2], output.to_owned()) | 
|  | } else { | 
|  | let mut gnu = String::with_capacity(5 + output.len()); | 
|  | gnu.push_str("lib"); | 
|  | gnu.push_str(&output); | 
|  | gnu.push_str(".a"); | 
|  | (output, gnu) | 
|  | }; | 
|  | let dst = self.get_out_dir()?; | 
|  |  | 
|  | let mut objects = Vec::new(); | 
|  | let mut src_dst = Vec::new(); | 
|  | for file in self.files.iter() { | 
|  | let obj = dst.join(file).with_extension("o"); | 
|  | let obj = if !obj.starts_with(&dst) { | 
|  | dst.join(obj.file_name().ok_or_else(|| Error::new(ErrorKind::IOError, "Getting object file details failed."))?) | 
|  | } else { | 
|  | obj | 
|  | }; | 
|  |  | 
|  | match obj.parent() { | 
|  | Some(s) => fs::create_dir_all(s)?, | 
|  | None => return Err(Error::new(ErrorKind::IOError, "Getting object file details failed.")), | 
|  | }; | 
|  |  | 
|  | src_dst.push((file.to_path_buf(), obj.clone())); | 
|  | objects.push(obj); | 
|  | } | 
|  | self.compile_objects(&src_dst)?; | 
|  | self.assemble(lib_name, &dst.join(gnu_lib_name), &objects)?; | 
|  |  | 
|  | if self.get_target()?.contains("msvc") { | 
|  | let compiler = self.get_base_compiler()?; | 
|  | let atlmfc_lib = compiler.env() | 
|  | .iter() | 
|  | .find(|&&(ref var, _)| var.as_os_str() == OsStr::new("LIB")) | 
|  | .and_then(|&(_, ref lib_paths)| { | 
|  | env::split_paths(lib_paths).find(|path| { | 
|  | let sub = Path::new("atlmfc/lib"); | 
|  | path.ends_with(sub) || path.parent().map_or(false, |p| p.ends_with(sub)) | 
|  | }) | 
|  | }); | 
|  |  | 
|  | if let Some(atlmfc_lib) = atlmfc_lib { | 
|  | self.print(&format!("cargo:rustc-link-search=native={}", atlmfc_lib.display())); | 
|  | } | 
|  | } | 
|  |  | 
|  | self.print(&format!("cargo:rustc-link-lib=static={}", lib_name)); | 
|  | self.print(&format!("cargo:rustc-link-search=native={}", dst.display())); | 
|  |  | 
|  | // Add specific C++ libraries, if enabled. | 
|  | if self.cpp { | 
|  | if let Some(stdlib) = self.get_cpp_link_stdlib()? { | 
|  | self.print(&format!("cargo:rustc-link-lib={}", stdlib)); | 
|  | } | 
|  | } | 
|  |  | 
|  | Ok(()) | 
|  | } | 
|  |  | 
|  | /// Run the compiler, generating the file `output` | 
|  | /// | 
|  | /// The name `output` should be the name of the library.  For backwards compatibility, | 
|  | /// 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) { | 
|  | if let Err(e) = self.try_compile(output) { | 
|  | fail(&e.message); | 
|  | } | 
|  | } | 
|  |  | 
|  | #[cfg(feature = "parallel")] | 
|  | fn compile_objects(&self, objs: &[(PathBuf, PathBuf)]) -> Result<(), Error> { | 
|  | use self::rayon::prelude::*; | 
|  |  | 
|  | let mut cfg = rayon::Configuration::new(); | 
|  | if let Ok(amt) = env::var("NUM_JOBS") { | 
|  | if let Ok(amt) = amt.parse() { | 
|  | cfg = cfg.num_threads(amt); | 
|  | } | 
|  | } | 
|  | drop(rayon::initialize(cfg)); | 
|  |  | 
|  | let results: Mutex<Vec<Result<(), Error>>> = Mutex::new(Vec::new()); | 
|  |  | 
|  | objs.par_iter().with_max_len(1) | 
|  | .for_each(|&(ref src, ref dst)| results.lock().unwrap().push(self.compile_object(src, dst))); | 
|  |  | 
|  | // Check for any errors and return the first one found. | 
|  | for result in results.into_inner().unwrap().iter() { | 
|  | if result.is_err() { | 
|  | return result.clone(); | 
|  | } | 
|  | } | 
|  |  | 
|  | Ok(()) | 
|  | } | 
|  |  | 
|  | #[cfg(not(feature = "parallel"))] | 
|  | fn compile_objects(&self, objs: &[(PathBuf, PathBuf)]) -> Result<(), Error> { | 
|  | for &(ref src, ref dst) in objs { | 
|  | self.compile_object(src, dst)?; | 
|  | } | 
|  | Ok(()) | 
|  | } | 
|  |  | 
|  | fn compile_object(&self, file: &Path, dst: &Path) -> Result<(), Error> { | 
|  | let is_asm = file.extension().and_then(|s| s.to_str()) == Some("asm"); | 
|  | let msvc = self.get_target()?.contains("msvc"); | 
|  | let (mut cmd, name) = if msvc && is_asm { | 
|  | self.msvc_macro_assembler()? | 
|  | } else { | 
|  | let compiler = self.try_get_compiler()?; | 
|  | let mut cmd = compiler.to_command(); | 
|  | for &(ref a, ref b) in self.env.iter() { | 
|  | cmd.env(a, b); | 
|  | } | 
|  | (cmd, | 
|  | compiler.path | 
|  | .file_name() | 
|  | .ok_or_else(|| Error::new(ErrorKind::IOError, "Failed to get compiler path."))? | 
|  | .to_string_lossy() | 
|  | .into_owned()) | 
|  | }; | 
|  | command_add_output_file(&mut cmd, dst, msvc, is_asm); | 
|  | cmd.arg(if msvc { "/c" } else { "-c" }); | 
|  | cmd.arg(file); | 
|  |  | 
|  | run(&mut cmd, &name)?; | 
|  | Ok(()) | 
|  | } | 
|  |  | 
|  | /// This will return a result instead of panicing; see expand() for the complete description. | 
|  | pub fn try_expand(&self) -> Result<Vec<u8>, Error> { | 
|  | let compiler = self.try_get_compiler()?; | 
|  | let mut cmd = compiler.to_command(); | 
|  | for &(ref a, ref b) in self.env.iter() { | 
|  | cmd.env(a, b); | 
|  | } | 
|  | cmd.arg(compiler.family.expand_flag()); | 
|  |  | 
|  | assert!(self.files.len() <= 1, | 
|  | "Expand may only be called for a single file"); | 
|  |  | 
|  | for file in self.files.iter() { | 
|  | cmd.arg(file); | 
|  | } | 
|  |  | 
|  | let name = compiler.path | 
|  | .file_name() | 
|  | .ok_or_else(|| Error::new(ErrorKind::IOError, "Failed to get compiler path."))? | 
|  | .to_string_lossy() | 
|  | .into_owned(); | 
|  |  | 
|  | Ok(run_output(&mut cmd, &name)?) | 
|  | } | 
|  |  | 
|  | /// Run the compiler, returning the macro-expanded version of the input files. | 
|  | /// | 
|  | /// This is only relevant for C and C++ files. | 
|  | /// | 
|  | /// # Panics | 
|  | /// Panics if more than one file is present in the config, or if compiler | 
|  | /// path has an invalid file name. | 
|  | /// | 
|  | /// # Example | 
|  | /// ```no_run | 
|  | /// let out = gcc::Build::new() | 
|  | ///                       .file("src/foo.c") | 
|  | ///                       .expand(); | 
|  | /// ``` | 
|  | pub fn expand(&self) -> Vec<u8> { | 
|  | match self.try_expand() { | 
|  | Err(e) => fail(&e.message), | 
|  | Ok(v) => v, | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Get the compiler that's in use for this configuration. | 
|  | /// | 
|  | /// This function will return a `Tool` which represents the culmination | 
|  | /// of this configuration at a snapshot in time. The returned compiler can | 
|  | /// be inspected (e.g. the path, arguments, environment) to forward along to | 
|  | /// other tools, or the `to_command` method can be used to invoke the | 
|  | /// compiler itself. | 
|  | /// | 
|  | /// This method will take into account all configuration such as debug | 
|  | /// information, optimization level, include directories, defines, etc. | 
|  | /// Additionally, the compiler binary in use follows the standard | 
|  | /// conventions for this path, e.g. looking at the explicitly set compiler, | 
|  | /// environment variables (a number of which are inspected here), and then | 
|  | /// falling back to the default configuration. | 
|  | /// | 
|  | /// # Panics | 
|  | /// | 
|  | /// Panics if an error occurred while determining the architecture. | 
|  | pub fn get_compiler(&self) -> Tool { | 
|  | match self.try_get_compiler() { | 
|  | Ok(tool) => tool, | 
|  | Err(e) => fail(&e.message), | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Get the compiler that's in use for this configuration. | 
|  | /// | 
|  | /// This will return a result instead of panicing; see get_compiler() for the complete description. | 
|  | pub fn try_get_compiler(&self) -> Result<Tool, Error> { | 
|  | let opt_level = self.get_opt_level()?; | 
|  | let target = self.get_target()?; | 
|  |  | 
|  | let mut cmd = self.get_base_compiler()?; | 
|  | let nvcc = cmd.path.file_name() | 
|  | .and_then(|p| p.to_str()).map(|p| p.contains("nvcc")) | 
|  | .unwrap_or(false); | 
|  |  | 
|  | // Non-target flags | 
|  | // If the flag is not conditioned on target variable, it belongs here :) | 
|  | match cmd.family { | 
|  | ToolFamily::Msvc => { | 
|  | cmd.args.push("/nologo".into()); | 
|  |  | 
|  | let crt_flag = match self.static_crt { | 
|  | Some(true) => "/MT", | 
|  | Some(false) => "/MD", | 
|  | None => { | 
|  | let features = env::var("CARGO_CFG_TARGET_FEATURE") | 
|  | .unwrap_or(String::new()); | 
|  | if features.contains("crt-static") { | 
|  | "/MT" | 
|  | } else { | 
|  | "/MD" | 
|  | } | 
|  | }, | 
|  | }; | 
|  | cmd.args.push(crt_flag.into()); | 
|  |  | 
|  | match &opt_level[..] { | 
|  | "z" | "s" => cmd.args.push("/Os".into()), | 
|  | "1" => cmd.args.push("/O1".into()), | 
|  | // -O3 is a valid value for gcc and clang compilers, but not msvc. Cap to /O2. | 
|  | "2" | "3" => cmd.args.push("/O2".into()), | 
|  | _ => {} | 
|  | } | 
|  | } | 
|  | ToolFamily::Gnu | | 
|  | ToolFamily::Clang => { | 
|  | // arm-linux-androideabi-gcc 4.8 shipped with Android NDK does | 
|  | // not support '-Oz' | 
|  | if opt_level == "z" && cmd.family != ToolFamily::Clang { | 
|  | cmd.args.push("-Os".into()); | 
|  | } else { | 
|  | cmd.args.push(format!("-O{}", opt_level).into()); | 
|  | } | 
|  |  | 
|  | if !nvcc { | 
|  | cmd.args.push("-ffunction-sections".into()); | 
|  | cmd.args.push("-fdata-sections".into()); | 
|  | if self.pic.unwrap_or(!target.contains("windows-gnu")) { | 
|  | cmd.args.push("-fPIC".into()); | 
|  | } | 
|  | } else if self.pic.unwrap_or(false) { | 
|  | cmd.args.push("-Xcompiler".into()); | 
|  | cmd.args.push("\'-fPIC\'".into()); | 
|  | } | 
|  | } | 
|  | } | 
|  | for arg in self.envflags(if self.cpp {"CXXFLAGS"} else {"CFLAGS"}) { | 
|  | cmd.args.push(arg.into()); | 
|  | } | 
|  |  | 
|  | if self.get_debug() { | 
|  | cmd.args.push(cmd.family.debug_flag().into()); | 
|  | } | 
|  |  | 
|  | // Target flags | 
|  | match cmd.family { | 
|  | ToolFamily::Clang => { | 
|  | cmd.args.push(format!("--target={}", target).into()); | 
|  | } | 
|  | ToolFamily::Msvc => { | 
|  | if target.contains("i586") { | 
|  | cmd.args.push("/ARCH:IA32".into()); | 
|  | } | 
|  | } | 
|  | ToolFamily::Gnu => { | 
|  | if target.contains("i686") || target.contains("i586") { | 
|  | cmd.args.push("-m32".into()); | 
|  | } else if target.contains("x86_64") || target.contains("powerpc64") { | 
|  | cmd.args.push("-m64".into()); | 
|  | } | 
|  |  | 
|  | if self.static_flag.is_none() && target.contains("musl") { | 
|  | cmd.args.push("-static".into()); | 
|  | } | 
|  |  | 
|  | // armv7 targets get to use armv7 instructions | 
|  | if target.starts_with("armv7-") && target.contains("-linux-") { | 
|  | cmd.args.push("-march=armv7-a".into()); | 
|  | } | 
|  |  | 
|  | // On android we can guarantee some extra float instructions | 
|  | // (specified in the android spec online) | 
|  | if target.starts_with("armv7-linux-androideabi") { | 
|  | cmd.args.push("-march=armv7-a".into()); | 
|  | cmd.args.push("-mfpu=vfpv3-d16".into()); | 
|  | cmd.args.push("-mfloat-abi=softfp".into()); | 
|  | } | 
|  |  | 
|  | // For us arm == armv6 by default | 
|  | if target.starts_with("arm-unknown-linux-") { | 
|  | cmd.args.push("-march=armv6".into()); | 
|  | cmd.args.push("-marm".into()); | 
|  | } | 
|  |  | 
|  | // We can guarantee some settings for FRC | 
|  | if target.starts_with("arm-frc-") { | 
|  | cmd.args.push("-march=armv7-a".into()); | 
|  | cmd.args.push("-mcpu=cortex-a9".into()); | 
|  | cmd.args.push("-mfpu=vfpv3".into()); | 
|  | cmd.args.push("-mfloat-abi=softfp".into()); | 
|  | cmd.args.push("-marm".into()); | 
|  | } | 
|  |  | 
|  | // Turn codegen down on i586 to avoid some instructions. | 
|  | if target.starts_with("i586-unknown-linux-") { | 
|  | cmd.args.push("-march=pentium".into()); | 
|  | } | 
|  |  | 
|  | // Set codegen level for i686 correctly | 
|  | if target.starts_with("i686-unknown-linux-") { | 
|  | cmd.args.push("-march=i686".into()); | 
|  | } | 
|  |  | 
|  | // Looks like `musl-gcc` makes is hard for `-m32` to make its way | 
|  | // all the way to the linker, so we need to actually instruct the | 
|  | // linker that we're generating 32-bit executables as well. This'll | 
|  | // typically only be used for build scripts which transitively use | 
|  | // these flags that try to compile executables. | 
|  | if target == "i686-unknown-linux-musl" { | 
|  | cmd.args.push("-Wl,-melf_i386".into()); | 
|  | } | 
|  |  | 
|  | if target.starts_with("thumb") { | 
|  | cmd.args.push("-mthumb".into()); | 
|  |  | 
|  | if target.ends_with("eabihf") { | 
|  | cmd.args.push("-mfloat-abi=hard".into()) | 
|  | } | 
|  | } | 
|  | if target.starts_with("thumbv6m") { | 
|  | cmd.args.push("-march=armv6s-m".into()); | 
|  | } | 
|  | if target.starts_with("thumbv7em") { | 
|  | cmd.args.push("-march=armv7e-m".into()); | 
|  |  | 
|  | if target.ends_with("eabihf") { | 
|  | cmd.args.push("-mfpu=fpv4-sp-d16".into()) | 
|  | } | 
|  | } | 
|  | if target.starts_with("thumbv7m") { | 
|  | cmd.args.push("-march=armv7-m".into()); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if target.contains("-ios") { | 
|  | // FIXME: potential bug. iOS is always compiled with Clang, but Gcc compiler may be | 
|  | // detected instead. | 
|  | self.ios_flags(&mut cmd)?; | 
|  | } | 
|  |  | 
|  | if self.static_flag.unwrap_or(false) { | 
|  | cmd.args.push("-static".into()); | 
|  | } | 
|  | if self.shared_flag.unwrap_or(false) { | 
|  | cmd.args.push("-shared".into()); | 
|  | } | 
|  |  | 
|  | if self.cpp { | 
|  | match (self.cpp_set_stdlib.as_ref(), cmd.family) { | 
|  | (None, _) => { } | 
|  | (Some(stdlib), ToolFamily::Gnu) | | 
|  | (Some(stdlib), ToolFamily::Clang) => { | 
|  | cmd.args.push(format!("-stdlib=lib{}", stdlib).into()); | 
|  | } | 
|  | _ => { | 
|  | println!("cargo:warning=cpp_set_stdlib is specified, but the {:?} compiler \ | 
|  | does not support this option, ignored", cmd.family); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | for directory in self.include_directories.iter() { | 
|  | cmd.args.push(cmd.family.include_flag().into()); | 
|  | cmd.args.push(directory.into()); | 
|  | } | 
|  |  | 
|  | for flag in self.flags.iter() { | 
|  | cmd.args.push(flag.into()); | 
|  | } | 
|  |  | 
|  | for flag in self.flags_supported.iter() { | 
|  | if self.is_flag_supported(flag).unwrap_or(false) { | 
|  | cmd.args.push(flag.into()); | 
|  | } | 
|  | } | 
|  |  | 
|  | for &(ref key, ref value) in self.definitions.iter() { | 
|  | let lead = if let ToolFamily::Msvc = cmd.family {"/"} else {"-"}; | 
|  | if let Some(ref value) = *value { | 
|  | cmd.args.push(format!("{}D{}={}", lead, key, value).into()); | 
|  | } else { | 
|  | cmd.args.push(format!("{}D{}", lead, key).into()); | 
|  | } | 
|  | } | 
|  |  | 
|  | if self.warnings { | 
|  | for flag in cmd.family.warnings_flags().iter() { | 
|  | cmd.args.push(flag.into()); | 
|  | } | 
|  | } | 
|  |  | 
|  | if self.warnings_into_errors { | 
|  | cmd.args.push(cmd.family.warnings_to_errors_flag().into()); | 
|  | } | 
|  |  | 
|  | Ok(cmd) | 
|  | } | 
|  |  | 
|  | fn msvc_macro_assembler(&self) -> Result<(Command, String), Error> { | 
|  | let target = self.get_target()?; | 
|  | let tool = if target.contains("x86_64") { | 
|  | "ml64.exe" | 
|  | } else { | 
|  | "ml.exe" | 
|  | }; | 
|  | let mut cmd = windows_registry::find(&target, tool).unwrap_or_else(|| self.cmd(tool)); | 
|  | for directory in self.include_directories.iter() { | 
|  | cmd.arg("/I").arg(directory); | 
|  | } | 
|  | for &(ref key, ref value) in self.definitions.iter() { | 
|  | if let Some(ref value) = *value { | 
|  | cmd.arg(&format!("/D{}={}", key, value)); | 
|  | } else { | 
|  | cmd.arg(&format!("/D{}", key)); | 
|  | } | 
|  | } | 
|  |  | 
|  | if target.contains("i686") || target.contains("i586") { | 
|  | cmd.arg("/safeseh"); | 
|  | } | 
|  | for flag in self.flags.iter() { | 
|  | cmd.arg(flag); | 
|  | } | 
|  |  | 
|  | Ok((cmd, tool.to_string())) | 
|  | } | 
|  |  | 
|  | fn assemble(&self, lib_name: &str, dst: &Path, objects: &[PathBuf]) -> Result<(), Error> { | 
|  | // Delete the destination if it exists as the `ar` tool at least on Unix | 
|  | // appends to it, which we don't want. | 
|  | let _ = fs::remove_file(&dst); | 
|  |  | 
|  | let target = self.get_target()?; | 
|  | if target.contains("msvc") { | 
|  | let mut cmd = match self.archiver { | 
|  | Some(ref s) => self.cmd(s), | 
|  | None => windows_registry::find(&target, "lib.exe").unwrap_or_else(|| self.cmd("lib.exe")), | 
|  | }; | 
|  | let mut out = OsString::from("/OUT:"); | 
|  | out.push(dst); | 
|  | run(cmd.arg(out) | 
|  | .arg("/nologo") | 
|  | .args(objects) | 
|  | .args(&self.objects), | 
|  | "lib.exe")?; | 
|  |  | 
|  | // The Rust compiler will look for libfoo.a and foo.lib, but the | 
|  | // MSVC linker will also be passed foo.lib, so be sure that both | 
|  | // exist for now. | 
|  | let lib_dst = dst.with_file_name(format!("{}.lib", lib_name)); | 
|  | let _ = fs::remove_file(&lib_dst); | 
|  | match fs::hard_link(&dst, &lib_dst) | 
|  | .or_else(|_| { | 
|  | // if hard-link fails, just copy (ignoring the number of bytes written) | 
|  | fs::copy(&dst, &lib_dst).map(|_| ()) | 
|  | }) { | 
|  | Ok(_) => (), | 
|  | Err(_) => return Err(Error::new(ErrorKind::IOError, "Could not copy or create a hard-link to the generated lib file.")), | 
|  | }; | 
|  | } else { | 
|  | let ar = self.get_ar()?; | 
|  | let cmd = ar.file_name() | 
|  | .ok_or_else(|| Error::new(ErrorKind::IOError, "Failed to get archiver (ar) path."))? | 
|  | .to_string_lossy(); | 
|  | run(self.cmd(&ar) | 
|  | .arg("crs") | 
|  | .arg(dst) | 
|  | .args(objects) | 
|  | .args(&self.objects), | 
|  | &cmd)?; | 
|  | } | 
|  |  | 
|  | Ok(()) | 
|  | } | 
|  |  | 
|  | fn ios_flags(&self, cmd: &mut Tool) -> Result<(), Error> { | 
|  | enum ArchSpec { | 
|  | Device(&'static str), | 
|  | Simulator(&'static str), | 
|  | } | 
|  |  | 
|  | let target = self.get_target()?; | 
|  | let arch = target.split('-').nth(0).ok_or_else(|| Error::new(ErrorKind::ArchitectureInvalid, "Unknown architecture for iOS target."))?; | 
|  | let arch = match arch { | 
|  | "arm" | "armv7" | "thumbv7" => ArchSpec::Device("armv7"), | 
|  | "armv7s" | "thumbv7s" => ArchSpec::Device("armv7s"), | 
|  | "arm64" | "aarch64" => ArchSpec::Device("arm64"), | 
|  | "i386" | "i686" => ArchSpec::Simulator("-m32"), | 
|  | "x86_64" => ArchSpec::Simulator("-m64"), | 
|  | _ => return Err(Error::new(ErrorKind::ArchitectureInvalid, "Unknown architecture for iOS target.")), | 
|  | }; | 
|  |  | 
|  | let sdk = match arch { | 
|  | ArchSpec::Device(arch) => { | 
|  | cmd.args.push("-arch".into()); | 
|  | cmd.args.push(arch.into()); | 
|  | cmd.args.push("-miphoneos-version-min=7.0".into()); | 
|  | "iphoneos" | 
|  | } | 
|  | ArchSpec::Simulator(arch) => { | 
|  | cmd.args.push(arch.into()); | 
|  | cmd.args.push("-mios-simulator-version-min=7.0".into()); | 
|  | "iphonesimulator" | 
|  | } | 
|  | }; | 
|  |  | 
|  | self.print(&format!("Detecting iOS SDK path for {}", sdk)); | 
|  | let sdk_path = self.cmd("xcrun") | 
|  | .arg("--show-sdk-path") | 
|  | .arg("--sdk") | 
|  | .arg(sdk) | 
|  | .stderr(Stdio::inherit()) | 
|  | .output()? | 
|  | .stdout; | 
|  |  | 
|  | let sdk_path = match String::from_utf8(sdk_path) { | 
|  | Ok(p) => p, | 
|  | Err(_) => return Err(Error::new(ErrorKind::IOError, "Unable to determine iOS SDK path.")), | 
|  | }; | 
|  |  | 
|  | cmd.args.push("-isysroot".into()); | 
|  | cmd.args.push(sdk_path.trim().into()); | 
|  |  | 
|  | Ok(()) | 
|  | } | 
|  |  | 
|  | fn cmd<P: AsRef<OsStr>>(&self, prog: P) -> Command { | 
|  | let mut cmd = Command::new(prog); | 
|  | for &(ref a, ref b) in self.env.iter() { | 
|  | cmd.env(a, b); | 
|  | } | 
|  | cmd | 
|  | } | 
|  |  | 
|  | fn get_base_compiler(&self) -> Result<Tool, Error> { | 
|  | if let Some(ref c) = self.compiler { | 
|  | return Ok(Tool::new(c.clone())); | 
|  | } | 
|  | let host = self.get_host()?; | 
|  | let target = self.get_target()?; | 
|  | let (env, msvc, gnu) = if self.cpp { | 
|  | ("CXX", "cl.exe", "g++") | 
|  | } else { | 
|  | ("CC", "cl.exe", "gcc") | 
|  | }; | 
|  |  | 
|  | let default = if host.contains("solaris") { | 
|  | // In this case, c++/cc unlikely to exist or be correct. | 
|  | gnu | 
|  | } else if self.cpp { | 
|  | "c++" | 
|  | } else { | 
|  | "cc" | 
|  | }; | 
|  |  | 
|  | let tool_opt: Option<Tool> = self.env_tool(env) | 
|  | .map(|(tool, args)| { | 
|  | let mut t = Tool::new(PathBuf::from(tool)); | 
|  | for arg in args { | 
|  | t.args.push(arg.into()); | 
|  | } | 
|  | t | 
|  | }) | 
|  | .or_else(|| { | 
|  | if target.contains("emscripten") { | 
|  | let tool = if self.cpp { | 
|  | "em++" | 
|  | } else { | 
|  | "emcc" | 
|  | }; | 
|  | // Windows uses bat file so we have to be a bit more specific | 
|  | if cfg!(windows) { | 
|  | let mut t = Tool::new(PathBuf::from("cmd")); | 
|  | t.args.push("/c".into()); | 
|  | t.args.push(format!("{}.bat", tool).into()); | 
|  | Some(t) | 
|  | } else { | 
|  | Some(Tool::new(PathBuf::from(tool))) | 
|  | } | 
|  | } else { | 
|  | None | 
|  | } | 
|  | }) | 
|  | .or_else(|| windows_registry::find_tool(&target, "cl.exe")); | 
|  |  | 
|  | let tool = match tool_opt { | 
|  | Some(t) => t, | 
|  | None => { | 
|  | let compiler = if host.contains("windows") && target.contains("windows") { | 
|  | if target.contains("msvc") { | 
|  | msvc.to_string() | 
|  | } else { | 
|  | format!("{}.exe", gnu) | 
|  | } | 
|  | } else if target.contains("android") { | 
|  | format!("{}-{}", target.replace("armv7", "arm"), gnu) | 
|  | } else if self.get_host()? != target { | 
|  | // CROSS_COMPILE is of the form: "arm-linux-gnueabi-" | 
|  | let cc_env = self.getenv("CROSS_COMPILE"); | 
|  | let cross_compile = cc_env.as_ref().map(|s| s.trim_right_matches('-')); | 
|  | let prefix = cross_compile.or(match &target[..] { | 
|  | "aarch64-unknown-linux-gnu" => Some("aarch64-linux-gnu"), | 
|  | "arm-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"), | 
|  | "arm-frc-linux-gnueabi" => Some("arm-frc-linux-gnueabi"), | 
|  | "arm-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"), | 
|  | "arm-unknown-linux-musleabi" => Some("arm-linux-musleabi"), | 
|  | "arm-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"), | 
|  | "arm-unknown-netbsd-eabi" => Some("arm--netbsdelf-eabi"), | 
|  | "armv6-unknown-netbsd-eabihf" => Some("armv6--netbsdelf-eabihf"), | 
|  | "armv7-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"), | 
|  | "armv7-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"), | 
|  | "armv7-unknown-netbsd-eabihf" => Some("armv7--netbsdelf-eabihf"), | 
|  | "i686-pc-windows-gnu" => Some("i686-w64-mingw32"), | 
|  | "i686-unknown-linux-musl" => Some("musl"), | 
|  | "i686-unknown-netbsd" => Some("i486--netbsdelf"), | 
|  | "mips-unknown-linux-gnu" => Some("mips-linux-gnu"), | 
|  | "mipsel-unknown-linux-gnu" => Some("mipsel-linux-gnu"), | 
|  | "mips64-unknown-linux-gnuabi64" => Some("mips64-linux-gnuabi64"), | 
|  | "mips64el-unknown-linux-gnuabi64" => Some("mips64el-linux-gnuabi64"), | 
|  | "powerpc-unknown-linux-gnu" => Some("powerpc-linux-gnu"), | 
|  | "powerpc-unknown-netbsd" => Some("powerpc--netbsd"), | 
|  | "powerpc64-unknown-linux-gnu" => Some("powerpc-linux-gnu"), | 
|  | "powerpc64le-unknown-linux-gnu" => Some("powerpc64le-linux-gnu"), | 
|  | "s390x-unknown-linux-gnu" => Some("s390x-linux-gnu"), | 
|  | "sparc64-unknown-netbsd" => Some("sparc64--netbsd"), | 
|  | "sparcv9-sun-solaris" => Some("sparcv9-sun-solaris"), | 
|  | "thumbv6m-none-eabi" => Some("arm-none-eabi"), | 
|  | "thumbv7em-none-eabi" => Some("arm-none-eabi"), | 
|  | "thumbv7em-none-eabihf" => Some("arm-none-eabi"), | 
|  | "thumbv7m-none-eabi" => Some("arm-none-eabi"), | 
|  | "x86_64-pc-windows-gnu" => Some("x86_64-w64-mingw32"), | 
|  | "x86_64-rumprun-netbsd" => Some("x86_64-rumprun-netbsd"), | 
|  | "x86_64-unknown-linux-musl" => Some("musl"), | 
|  | "x86_64-unknown-netbsd" => Some("x86_64--netbsd"), | 
|  | _ => None, | 
|  | }); | 
|  | match prefix { | 
|  | Some(prefix) => format!("{}-{}", prefix, gnu), | 
|  | None => default.to_string(), | 
|  | } | 
|  | } else { | 
|  | default.to_string() | 
|  | }; | 
|  | Tool::new(PathBuf::from(compiler)) | 
|  | } | 
|  | }; | 
|  |  | 
|  | Ok(tool) | 
|  | } | 
|  |  | 
|  | fn get_var(&self, var_base: &str) -> Result<String, Error> { | 
|  | let target = self.get_target()?; | 
|  | let host = self.get_host()?; | 
|  | let kind = if host == target { "HOST" } else { "TARGET" }; | 
|  | let target_u = target.replace("-", "_"); | 
|  | let res = self.getenv(&format!("{}_{}", var_base, target)) | 
|  | .or_else(|| self.getenv(&format!("{}_{}", var_base, target_u))) | 
|  | .or_else(|| self.getenv(&format!("{}_{}", kind, var_base))) | 
|  | .or_else(|| self.getenv(var_base)); | 
|  |  | 
|  | match res { | 
|  | Some(res) => Ok(res), | 
|  | None => Err(Error::new(ErrorKind::EnvVarNotFound, &format!("Could not find environment variable {}.", var_base))), | 
|  | } | 
|  | } | 
|  |  | 
|  | fn envflags(&self, name: &str) -> Vec<String> { | 
|  | self.get_var(name) | 
|  | .unwrap_or(String::new()) | 
|  | .split(|c: char| c.is_whitespace()) | 
|  | .filter(|s| !s.is_empty()) | 
|  | .map(|s| s.to_string()) | 
|  | .collect() | 
|  | } | 
|  |  | 
|  | fn env_tool(&self, name: &str) -> Option<(String, Vec<String>)> { | 
|  | self.get_var(name).ok().map(|tool| { | 
|  | let whitelist = ["ccache", "distcc", "sccache"]; | 
|  | for t in whitelist.iter() { | 
|  | if tool.starts_with(t) && tool[t.len()..].starts_with(' ') { | 
|  | return (t.to_string(), vec![tool[t.len()..].trim_left().to_string()]); | 
|  | } | 
|  | } | 
|  | (tool, Vec::new()) | 
|  | }) | 
|  | } | 
|  |  | 
|  | /// Returns the default C++ standard library for the current target: `libc++` | 
|  | /// for OS X and `libstdc++` for anything else. | 
|  | fn get_cpp_link_stdlib(&self) -> Result<Option<String>, Error> { | 
|  | match self.cpp_link_stdlib.clone() { | 
|  | Some(s) => Ok(s), | 
|  | None => { | 
|  | let target = self.get_target()?; | 
|  | if target.contains("msvc") { | 
|  | Ok(None) | 
|  | } else if target.contains("darwin") { | 
|  | Ok(Some("c++".to_string())) | 
|  | } else if target.contains("freebsd") { | 
|  | Ok(Some("c++".to_string())) | 
|  | } else { | 
|  | Ok(Some("stdc++".to_string())) | 
|  | } | 
|  | }, | 
|  | } | 
|  | } | 
|  |  | 
|  | fn get_ar(&self) -> Result<PathBuf, Error> { | 
|  | match self.archiver | 
|  | .clone() | 
|  | .or_else(|| self.get_var("AR").map(PathBuf::from).ok()) { | 
|  | Some(p) => Ok(p), | 
|  | None => { | 
|  | if self.get_target()?.contains("android") { | 
|  | Ok(PathBuf::from(format!("{}-ar", self.get_target()?.replace("armv7", "arm")))) | 
|  | } else if self.get_target()?.contains("emscripten") { | 
|  | //Windows use bat files so we have to be a bit more specific | 
|  | let tool = if cfg!(windows) { | 
|  | "emar.bat" | 
|  | } else { | 
|  | "emar" | 
|  | }; | 
|  |  | 
|  | Ok(PathBuf::from(tool)) | 
|  | } else { | 
|  | Ok(PathBuf::from("ar")) | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | fn get_target(&self) -> Result<String, Error> { | 
|  | match self.target.clone() { | 
|  | Some(t) => Ok(t), | 
|  | None => Ok(self.getenv_unwrap("TARGET")?), | 
|  | } | 
|  | } | 
|  |  | 
|  | fn get_host(&self) -> Result<String, Error> { | 
|  | match self.host.clone() { | 
|  | Some(h) => Ok(h), | 
|  | None => Ok(self.getenv_unwrap("HOST")?), | 
|  | } | 
|  | } | 
|  |  | 
|  | fn get_opt_level(&self) -> Result<String, Error> { | 
|  | match self.opt_level.as_ref().cloned() { | 
|  | Some(ol) => Ok(ol), | 
|  | None => Ok(self.getenv_unwrap("OPT_LEVEL")?), | 
|  | } | 
|  | } | 
|  |  | 
|  | fn get_debug(&self) -> bool { | 
|  | self.debug.unwrap_or_else(|| { | 
|  | match self.getenv("DEBUG") { | 
|  | Some(s) => s != "false", | 
|  | None => false, | 
|  | } | 
|  | }) | 
|  | } | 
|  |  | 
|  | fn get_out_dir(&self) -> Result<PathBuf, Error> { | 
|  | match self.out_dir.clone() { | 
|  | Some(p) => Ok(p), | 
|  | None => Ok(env::var_os("OUT_DIR") | 
|  | .map(PathBuf::from) | 
|  | .ok_or_else(|| Error::new(ErrorKind::EnvVarNotFound, "Environment variable OUT_DIR not defined."))?), | 
|  | } | 
|  | } | 
|  |  | 
|  | fn getenv(&self, v: &str) -> Option<String> { | 
|  | let r = env::var(v).ok(); | 
|  | self.print(&format!("{} = {:?}", v, r)); | 
|  | r | 
|  | } | 
|  |  | 
|  | fn getenv_unwrap(&self, v: &str) -> Result<String, Error> { | 
|  | match self.getenv(v) { | 
|  | Some(s) => Ok(s), | 
|  | None => Err(Error::new(ErrorKind::EnvVarNotFound, &format!("Environment variable {} not defined.", v.to_string()))), | 
|  | } | 
|  | } | 
|  |  | 
|  | fn print(&self, s: &str) { | 
|  | if self.cargo_metadata { | 
|  | println!("{}", s); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | impl Default for Build { | 
|  | fn default() -> Build { | 
|  | Build::new() | 
|  | } | 
|  | } | 
|  |  | 
|  | impl Tool { | 
|  | fn new(path: PathBuf) -> Tool { | 
|  | // Try to detect family of the tool from its name, falling back to Gnu. | 
|  | let family = if let Some(fname) = path.file_name().and_then(|p| p.to_str()) { | 
|  | if fname.contains("clang") { | 
|  | ToolFamily::Clang | 
|  | } else if fname.contains("cl") && !fname.contains("uclibc") { | 
|  | ToolFamily::Msvc | 
|  | } else { | 
|  | ToolFamily::Gnu | 
|  | } | 
|  | } else { | 
|  | ToolFamily::Gnu | 
|  | }; | 
|  | Tool { | 
|  | path: path, | 
|  | args: Vec::new(), | 
|  | env: Vec::new(), | 
|  | family: family | 
|  | } | 
|  | } | 
|  |  | 
|  | /// 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 { | 
|  | let mut cmd = Command::new(&self.path); | 
|  | cmd.args(&self.args); | 
|  | for &(ref k, ref v) in self.env.iter() { | 
|  | cmd.env(k, v); | 
|  | } | 
|  | cmd | 
|  | } | 
|  |  | 
|  | /// Returns the path for this compiler. | 
|  | /// | 
|  | /// Note that this may not be a path to a file on the filesystem, e.g. "cc", | 
|  | /// but rather something which will be resolved when a process is spawned. | 
|  | pub fn path(&self) -> &Path { | 
|  | &self.path | 
|  | } | 
|  |  | 
|  | /// Returns the default set of arguments to the compiler needed to produce | 
|  | /// executables for the target this compiler generates. | 
|  | pub fn args(&self) -> &[OsString] { | 
|  | &self.args | 
|  | } | 
|  |  | 
|  | /// Returns the set of environment variables needed for this compiler to | 
|  | /// operate. | 
|  | /// | 
|  | /// This is typically only used for MSVC compilers currently. | 
|  | pub fn env(&self) -> &[(OsString, OsString)] { | 
|  | &self.env | 
|  | } | 
|  | } | 
|  |  | 
|  | fn run(cmd: &mut Command, program: &str) -> Result<(), Error> { | 
|  | let (mut child, print) = spawn(cmd, program)?; | 
|  | let status = match child.wait() { | 
|  | Ok(s) => s, | 
|  | Err(_) => return Err(Error::new(ErrorKind::ToolExecError, &format!("Failed to wait on spawned child process, command {:?} with args {:?}.", cmd, program))), | 
|  | }; | 
|  | print.join().unwrap(); | 
|  | println!("{}", status); | 
|  |  | 
|  | if status.success() { | 
|  | Ok(()) | 
|  | } else { | 
|  | Err(Error::new(ErrorKind::ToolExecError, &format!("Command {:?} with args {:?} did not execute successfully (status code {}).", cmd, program, status))) | 
|  | } | 
|  | } | 
|  |  | 
|  | fn run_output(cmd: &mut Command, program: &str) -> Result<Vec<u8>, Error> { | 
|  | cmd.stdout(Stdio::piped()); | 
|  | let (mut child, print) = spawn(cmd, program)?; | 
|  | let mut stdout = vec![]; | 
|  | child.stdout.take().unwrap().read_to_end(&mut stdout).unwrap(); | 
|  | let status = match child.wait() { | 
|  | Ok(s) => s, | 
|  | Err(_) => return Err(Error::new(ErrorKind::ToolExecError, &format!("Failed to wait on spawned child process, command {:?} with args {:?}.", cmd, program))), | 
|  | }; | 
|  | print.join().unwrap(); | 
|  | println!("{}", status); | 
|  |  | 
|  | if status.success() { | 
|  | Ok(stdout) | 
|  | } else { | 
|  | Err(Error::new(ErrorKind::ToolExecError, &format!("Command {:?} with args {:?} did not execute successfully (status code {}).", cmd, program, status))) | 
|  | } | 
|  | } | 
|  |  | 
|  | fn spawn(cmd: &mut Command, program: &str) -> Result<(Child, JoinHandle<()>), Error> { | 
|  | println!("running: {:?}", cmd); | 
|  |  | 
|  | // Capture the standard error coming from these programs, and write it out | 
|  | // with cargo:warning= prefixes. Note that this is a bit wonky to avoid | 
|  | // requiring the output to be UTF-8, we instead just ship bytes from one | 
|  | // location to another. | 
|  | match cmd.stderr(Stdio::piped()).spawn() { | 
|  | Ok(mut child) => { | 
|  | let stderr = BufReader::new(child.stderr.take().unwrap()); | 
|  | let print = thread::spawn(move || { | 
|  | for line in stderr.split(b'\n').filter_map(|l| l.ok()) { | 
|  | print!("cargo:warning="); | 
|  | std::io::stdout().write_all(&line).unwrap(); | 
|  | println!(""); | 
|  | } | 
|  | }); | 
|  | Ok((child, print)) | 
|  | } | 
|  | Err(ref e) if e.kind() == io::ErrorKind::NotFound => { | 
|  | let extra = if cfg!(windows) { | 
|  | " (see https://github.com/alexcrichton/gcc-rs#compile-time-requirements \ | 
|  | for help)" | 
|  | } else { | 
|  | "" | 
|  | }; | 
|  | Err(Error::new(ErrorKind::ToolNotFound, &format!("Failed to find tool. Is `{}` installed?{}", program, extra))) | 
|  | } | 
|  | Err(_) => Err(Error::new(ErrorKind::ToolExecError, &format!("Command {:?} with args {:?} failed to start.", cmd, program))), | 
|  | } | 
|  | } | 
|  |  | 
|  | fn fail(s: &str) -> ! { | 
|  | panic!("\n\nInternal error occurred: {}\n\n", s) | 
|  | } | 
|  |  | 
|  |  | 
|  | 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); | 
|  | } | 
|  | } |