Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  lib.rs   Sprache: unbekannt

 
//! A library for [Cargo build scripts](https://doc.rust-lang.org/cargo/reference/build-scripts.html)
//! to compile a set of C/C++/assembly/CUDA files into a static archive for Cargo
//! to link into the crate being built. This crate does not compile code itself;
//! it calls out to the default compiler for the platform. This crate will
//! automatically detect situations such as cross compilation and
//! [various environment variables](#external-configuration-via-environment-variables) and will build code appropriately.
//!
//! # Example
//!
//! First, you'll want to both add a build script for your crate (`build.rs`) and
//! also add this crate to your `Cargo.toml` via:
//!
//! ```toml
//! [build-dependencies]
//! cc = "1.0"
//! ```
//!
//! Next up, you'll want to write a build script like so:
//!
//! ```rust,no_run
//! // build.rs
//!
//! fn main() {
//!     cc::Build::new()
//!         .file("foo.c")
//!         .file("bar.c")
//!         .compile("foo");
//! }
//! ```
//!
//! And that's it! Running `cargo build` should take care of the rest and your Rust
//! application will now have the C files `foo.c` and `bar.c` compiled into a file
//! named `libfoo.a`. If the C files contain
//!
//! ```c
//! void foo_function(void) { ... }
//! ```
//!
//! and
//!
//! ```c
//! int32_t bar_function(int32_t x) { ... }
//! ```
//!
//! you can call them from Rust by declaring them in
//! your Rust code like so:
//!
//! ```rust,no_run
//! extern "C" {
//!     fn foo_function();
//!     fn bar_function(x: i32) -> i32;
//! }
//!
//! pub fn call() {
//!     unsafe {
//!         foo_function();
//!         bar_function(42);
//!     }
//! }
//!
//! fn main() {
//!     call();
//! }
//! ```
//!
//! See [the Rustonomicon](https://doc.rust-lang.org/nomicon/ffi.html) for more details.
//!
//! # External configuration via environment variables
//!
//! To control the programs and flags used for building, the builder can set a
//! number of different environment variables.
//!
//! * `CFLAGS` - a series of space separated flags passed to compilers. Note that
//! individual flags cannot currently contain spaces, so doing
//! something like: `-L=foo\ bar` is not possible.
//! * `CC` - the actual C compiler used. Note that this is used as an exact
//! executable name, so (for example) no extra flags can be passed inside
//! this variable, and the builder must ensure that there aren't any
//! trailing spaces. This compiler must understand the `-c` flag. For
//! certain `TARGET`s, it also is assumed to know about other flags (most
//! common is `-fPIC`).
//! * `AR` - the `ar` (archiver) executable to use to build the static library.
//! * `CRATE_CC_NO_DEFAULTS` - the default compiler flags may cause conflicts in
//! some cross compiling scenarios. Setting this variable
//! will disable the generation of default compiler
//! flags.
//! * `CC_ENABLE_DEBUG_OUTPUT` - if set, compiler command invocations and exit codes will
//! be logged to stdout. This is useful for debugging build script issues, but can be
//! overly verbose for normal use.
//! * `CXX...` - see [C++ Support](#c-support).
//!
//! Furthermore, projects using this crate may specify custom environment variables
//! to be inspected, for example via the `Build::try_flags_from_environment`
//! function. Consult the project’s own documentation or its use of the `cc` crate
//! for any additional variables it may use.
//!
//! Each of these variables can also be supplied with certain prefixes and suffixes,
//! in the following prioritized order:
//!
//!   1. `<var>_<target>` - for example, `CC_x86_64-unknown-linux-gnu`
//!   2. `<var>_<target_with_underscores>` - for example, `CC_x86_64_unknown_linux_gnu`
//!   3. `<build-kind>_<var>` - for example, `HOST_CC` or `TARGET_CFLAGS`
//!   4. `<var>` - a plain `CC`, `AR` as above.
//!
//! If none of these variables exist, cc-rs uses built-in defaults.
//!
//! In addition to the above optional environment variables, `cc-rs` has some
//! functions with hard requirements on some variables supplied by [cargo's
//! build-script driver][cargo] that it has the `TARGET`, `OUT_DIR`, `OPT_LEVEL`,
//! and `HOST` variables.
//!
//! [cargo]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#inputs-to-the-build-script
//!
//! # Optional features
//!
//! ## Parallel
//!
//! Currently cc-rs supports parallel compilation (think `make -jN`) but this
//! feature is turned off by default. To enable cc-rs to compile C/C++ in parallel,
//! you can change your dependency to:
//!
//! ```toml
//! [build-dependencies]
//! cc = { version = "1.0", features = ["parallel"] }
//! ```
//!
//! By default cc-rs will limit parallelism to `$NUM_JOBS`, or if not present it
//! will limit it to the number of cpus on the machine. If you are using cargo,
//! use `-jN` option of `build`, `test` and `run` commands as `$NUM_JOBS`
//! is supplied by cargo.
//!
//! # Compile-time Requirements
//!
//! To work properly this crate needs access to a C compiler when the build script
//! is being run. This crate does not ship a C compiler with it. The compiler
//! required varies per platform, but there are three broad categories:
//!
//! * Unix platforms require `cc` to be the C compiler. This can be found by
//! installing cc/clang on Linux distributions and Xcode on macOS, for example.
//! * Windows platforms targeting MSVC (e.g. your target triple ends in `-msvc`)
//! require Visual Studio to be installed. `cc-rs` attempts to locate it, and
//! if it fails, `cl.exe` is expected to be available in `PATH`. This can be
//! set up by running the appropriate developer tools shell.
//! * Windows platforms targeting MinGW (e.g. your target triple ends in `-gnu`)
//! require `cc` to be available in `PATH`. We recommend the
//! [MinGW-w64](https://www.mingw-w64.org/) distribution, which is using the
//! [Win-builds](http://win-builds.org/) installation system.
//! You may also acquire it via
//! [MSYS2](https://www.msys2.org/), as explained [here][msys2-help].  Make sure
//! to install the appropriate architecture corresponding to your installation of
//! rustc. GCC from older [MinGW](http://www.mingw.org/) project is compatible
//! only with 32-bit rust compiler.
//!
//! [msys2-help]: https://github.com/rust-lang/rust#building-on-windows
//!
//! # C++ support
//!
//! `cc-rs` supports C++ libraries compilation by using the `cpp` method on
//! `Build`:
//!
//! ```rust,no_run
//! fn main() {
//!     cc::Build::new()
//!         .cpp(true) // Switch to C++ library compilation.
//!         .file("foo.cpp")
//!         .compile("foo");
//! }
//! ```
//!
//! For C++ libraries, the `CXX` and `CXXFLAGS` environment variables are used instead of `CC` and `CFLAGS`.
//!
//! The C++ standard library may be linked to the crate target. By default it's `libc++` for macOS, FreeBSD, and OpenBSD, `libc++_shared` for Android, nothing for MSVC, and `libstdc++` for anything else. It can be changed in one of two ways:
//!
//! 1. by using the `cpp_link_stdlib` method on `Build`:
//! ```rust,no_run
//! fn main() {
//!     cc::Build::new()
//!         .cpp(true)
//!         .file("foo.cpp")
//!         .cpp_link_stdlib("stdc++") // use libstdc++
//!         .compile("foo");
//! }
//! ```
//! 2. by setting the `CXXSTDLIB` environment variable.
//!
//! In particular, for Android you may want to [use `c++_static` if you have at most one shared library](https://developer.android.com/ndk/guides/cpp-support).
//!
//! Remember that C++ does name mangling so `extern "C"` might be required to enable Rust linker to find your functions.
//!
//! # CUDA C++ support
//!
//! `cc-rs` also supports compiling CUDA C++ libraries by using the `cuda` method
//! on `Build`:
//!
//! ```rust,no_run
//! fn main() {
//!     cc::Build::new()
//!         // Switch to CUDA C++ library compilation using NVCC.
//!         .cuda(true)
//!         .cudart("static")
//!         // Generate code for Maxwell (GTX 970, 980, 980 Ti, Titan X).
//!         .flag("-gencode").flag("arch=compute_52,code=sm_52")
//!         // Generate code for Maxwell (Jetson TX1).
//!         .flag("-gencode").flag("arch=compute_53,code=sm_53")
//!         // Generate code for Pascal (GTX 1070, 1080, 1080 Ti, Titan Xp).
//!         .flag("-gencode").flag("arch=compute_61,code=sm_61")
//!         // Generate code for Pascal (Tesla P100).
//!         .flag("-gencode").flag("arch=compute_60,code=sm_60")
//!         // Generate code for Pascal (Jetson TX2).
//!         .flag("-gencode").flag("arch=compute_62,code=sm_62")
//!         // Generate code in parallel
//!         .flag("-t0")
//!         .file("bar.cu")
//!         .compile("bar");
//! }
//! ```

#![doc(html_root_url = "https://docs.rs/cc/1.0")]
#![cfg_attr(test, deny(warnings))]
#![allow(deprecated)]
#![deny(missing_docs)]

use std::borrow::Cow;
use std::collections::HashMap;
use std::env;
use std::ffi::{OsStr, OsString};
use std::fmt::{self, Display, Formatter};
use std::fs;
use std::io::{self, Write};
use std::path::{Component, Path, PathBuf};
#[cfg(feature = "parallel")]
use std::process::Child;
use std::process::Command;
use std::sync::{Arc, Mutex};

#[cfg(feature = "parallel")]
mod parallel;
mod windows;
// Regardless of whether this should be in this crate's public API,
// it has been since 2015, so don't break it.
pub use windows::find_tools as windows_registry;

mod command_helpers;
use command_helpers::*;

mod tool;
pub use tool::Tool;
use tool::ToolFamily;

/// A builder for compilation of a native library.
///
/// A `Build` is the main type of the `cc` crate and is used to control all the
/// various configuration options and such of a compile. You'll find more
/// documentation on each method itself.
#[derive(Clone, Debug)]
pub struct Build {
    include_directories: Vec<Arc<Path>>,
    definitions: Vec<(Arc<str>, Option<Arc<str>>)>,
    objects: Vec<Arc<Path>>,
    flags: Vec<Arc<str>>,
    flags_supported: Vec<Arc<str>>,
    known_flag_support_status: Arc<Mutex<HashMap<String, bool>>>,
    ar_flags: Vec<Arc<str>>,
    asm_flags: Vec<Arc<str>>,
    no_default_flags: bool,
    files: Vec<Arc<Path>>,
    cpp: bool,
    cpp_link_stdlib: Option<Option<Arc<str>>>,
    cpp_set_stdlib: Option<Arc<str>>,
    cuda: bool,
    cudart: Option<Arc<str>>,
    std: Option<Arc<str>>,
    target: Option<Arc<str>>,
    host: Option<Arc<str>>,
    out_dir: Option<Arc<Path>>,
    opt_level: Option<Arc<str>>,
    debug: Option<bool>,
    force_frame_pointer: Option<bool>,
    env: Vec<(Arc<OsStr>, Arc<OsStr>)>,
    compiler: Option<Arc<Path>>,
    archiver: Option<Arc<Path>>,
    ranlib: Option<Arc<Path>>,
    cargo_output: CargoOutput,
    link_lib_modifiers: Vec<Arc<str>>,
    pic: Option<bool>,
    use_plt: Option<bool>,
    static_crt: Option<bool>,
    shared_flag: Option<bool>,
    static_flag: Option<bool>,
    warnings_into_errors: bool,
    warnings: Option<bool>,
    extra_warnings: Option<bool>,
    env_cache: Arc<Mutex<HashMap<String, Option<Arc<str>>>>>,
    apple_sdk_root_cache: Arc<Mutex<HashMap<String, OsString>>>,
    apple_versions_cache: Arc<Mutex<HashMap<String, String>>>,
    emit_rerun_if_env_changed: bool,
    cached_compiler_family: Arc<Mutex<HashMap<Box<Path>, ToolFamily>>>,
}

/// Represents the types of errors that may occur while using cc-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,
    /// One of the function arguments failed validation.
    InvalidArgument,
    #[cfg(feature = "parallel")]
    /// jobserver helpthread failure
    JobserverHelpThreadError,
}

/// Represents an internal error that occurred, with an explanation.
#[derive(Clone, Debug)]
pub struct Error {
    /// Describes the kind of error that occurred.
    kind: ErrorKind,
    /// More explanation of error that occurred.
    message: Cow<'static, str>,
}

impl Error {
    fn new(kind: ErrorKind, message: impl Into<Cow<'static, str>>) -> Error {
        Error {
            kind,
            message: message.into(),
        }
    }
}

impl From<io::Error> for Error {
    fn from(e: io::Error) -> Error {
        Error::new(ErrorKind::IOError, format!("{}", e))
    }
}

impl Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{:?}: {}", self.kind, self.message)
    }
}

impl std::error::Error for Error {}

/// Represents an object.
///
/// This is a source file -> object file pair.
#[derive(Clone, Debug)]
struct Object {
    src: PathBuf,
    dst: PathBuf,
}

impl Object {
    /// Create a new source file -> object file pair.
    fn new(src: PathBuf, dst: PathBuf) -> Object {
        Object { src, dst }
    }
}

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
    pub fn new() -> Build {
        Build {
            include_directories: Vec::new(),
            definitions: Vec::new(),
            objects: Vec::new(),
            flags: Vec::new(),
            flags_supported: Vec::new(),
            known_flag_support_status: Arc::new(Mutex::new(HashMap::new())),
            ar_flags: Vec::new(),
            asm_flags: Vec::new(),
            no_default_flags: false,
            files: Vec::new(),
            shared_flag: None,
            static_flag: None,
            cpp: false,
            cpp_link_stdlib: None,
            cpp_set_stdlib: None,
            cuda: false,
            cudart: None,
            std: None,
            target: None,
            host: None,
            out_dir: None,
            opt_level: None,
            debug: None,
            force_frame_pointer: None,
            env: Vec::new(),
            compiler: None,
            archiver: None,
            ranlib: None,
            cargo_output: CargoOutput::new(),
            link_lib_modifiers: Vec::new(),
            pic: None,
            use_plt: None,
            static_crt: None,
            warnings: None,
            extra_warnings: None,
            warnings_into_errors: false,
            env_cache: Arc::new(Mutex::new(HashMap::new())),
            apple_sdk_root_cache: Arc::new(Mutex::new(HashMap::new())),
            apple_versions_cache: Arc::new(Mutex::new(HashMap::new())),
            emit_rerun_if_env_changed: true,
            cached_compiler_family: Arc::default(),
        }
    }

    /// 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");
    ///
    /// cc::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().into());
        self
    }

    /// Add multiple directories to the `-I` include path.
    ///
    /// # Example
    ///
    /// ```no_run
    /// # use std::path::Path;
    /// # let condition = true;
    /// #
    /// let mut extra_dir = None;
    /// if condition {
    ///     extra_dir = Some(Path::new("/path/to"));
    /// }
    ///
    /// cc::Build::new()
    ///     .file("src/foo.c")
    ///     .includes(extra_dir)
    ///     .compile("foo");
    /// ```
    pub fn includes<P>(&mut self, dirs: P) -> &mut Build
    where
        P: IntoIterator,
        P::Item: AsRef<Path>,
    {
        for dir in dirs {
            self.include(dir);
        }
        self
    }

    /// Specify a `-D` variable with an optional value.
    ///
    /// # Example
    ///
    /// ```no_run
    /// cc::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.into(), val.into().map(Into::into)));
        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().into());
        self
    }

    /// Add an arbitrary flag to the invocation of the compiler
    ///
    /// # Example
    ///
    /// ```no_run
    /// cc::Build::new()
    ///     .file("src/foo.c")
    ///     .flag("-ffunction-sections")
    ///     .compile("foo");
    /// ```
    pub fn flag(&mut self, flag: &str) -> &mut Build {
        self.flags.push(flag.into());
        self
    }

    /// Removes a compiler flag that was added by [`Build::flag`].
    ///
    /// Will not remove flags added by other means (default flags,
    /// flags from env, and so on).
    ///
    /// # Example
    /// ```
    /// cc::Build::new()
    ///     .file("src/foo.c")
    ///     .flag("unwanted_flag")
    ///     .remove_flag("unwanted_flag");
    /// ```

    pub fn remove_flag(&mut self, flag: &str) -> &mut Build {
        self.flags.retain(|other_flag| &**other_flag != flag);
        self
    }

    /// Add a flag to the invocation of the ar
    ///
    /// # Example
    ///
    /// ```no_run
    /// cc::Build::new()
    ///     .file("src/foo.c")
    ///     .file("src/bar.c")
    ///     .ar_flag("/NODEFAULTLIB:libc.dll")
    ///     .compile("foo");
    /// ```
    pub fn ar_flag(&mut self, flag: &str) -> &mut Build {
        self.ar_flags.push(flag.into());
        self
    }

    /// Add a flag that will only be used with assembly files.
    ///
    /// The flag will be applied to input files with either a `.s` or
    /// `.asm` extension (case insensitive).
    ///
    /// # Example
    ///
    /// ```no_run
    /// cc::Build::new()
    ///     .asm_flag("-Wa,-defsym,abc=1")
    ///     .file("src/foo.S")  // The asm flag will be applied here
    ///     .file("src/bar.c")  // The asm flag will not be applied here
    ///     .compile("foo");
    /// ```
    pub fn asm_flag(&mut self, flag: &str) -> &mut Build {
        self.asm_flags.push(flag.into());
        self
    }

    fn ensure_check_file(&self) -> Result<PathBuf, Error> {
        let out_dir = self.get_out_dir()?;
        let src = if self.cuda {
            assert!(self.cpp);
            out_dir.join("flag_check.cu")
        } else 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)
    }

    /// Run the compiler to test if it accepts the given flag.
    ///
    /// For a convenience method for setting flags conditionally,
    /// see `flag_if_supported()`.
    ///
    /// It may return error if it's unable to run the compiler with a test file
    /// (e.g. the compiler is missing or a write to the `out_dir` failed).
    ///
    /// Note: Once computed, the result of this call is stored in the
    /// `known_flag_support` field. If `is_flag_supported(flag)`
    /// is called again, the result will be read from the hash table.
    pub fn is_flag_supported(&self, flag: &str) -> Result<bool, Error> {
        let mut known_status = self.known_flag_support_status.lock().unwrap();
        if let Some(is_supported) = known_status.get(flag).cloned() {
            return Ok(is_supported);
        }

        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 host = self.get_host()?;
        let mut cfg = Build::new();
        cfg.flag(flag)
            .cargo_metadata(self.cargo_output.metadata)
            .target(&target)
            .opt_level(0)
            .host(&host)
            .debug(false)
            .cpp(self.cpp)
            .cuda(self.cuda);
        if let Some(ref c) = self.compiler {
            cfg.compiler(c.clone());
        }
        let mut compiler = cfg.try_get_compiler()?;

        // Clang uses stderr for verbose output, which yields a false positive
        // result if the CFLAGS/CXXFLAGS include -v to aid in debugging.
        if compiler.family.verbose_stderr() {
            compiler.remove_arg("-v".into());
        }
        if compiler.family == ToolFamily::Clang {
            // Avoid reporting that the arg is unsupported just because the
            // compiler complains that it wasn't used.
            compiler.push_cc_arg("-Wno-unused-command-line-argument".into());
        }

        let mut cmd = compiler.to_command();
        let is_arm = target.contains("aarch64") || target.contains("arm");
        let clang = compiler.family == ToolFamily::Clang;
        let gnu = compiler.family == ToolFamily::Gnu;
        command_add_output_file(
            &mut cmd,
            &obj,
            self.cuda,
            target.contains("msvc"),
            clang,
            gnu,
            false,
            is_arm,
        );

        // Checking for compiler flags does not require linking
        cmd.arg("-c");

        cmd.arg(&src);

        let output = cmd.output()?;
        let is_supported = output.status.success() && output.stderr.is_empty();

        known_status.insert(flag.to_owned(), is_supported);
        Ok(is_supported)
    }

    /// Add an arbitrary flag to the invocation of the compiler if it supports it
    ///
    /// # Example
    ///
    /// ```no_run
    /// cc::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.into());
        self
    }

    /// Add flags from the specified environment variable.
    ///
    /// Normally the `cc` crate will consult with the standard set of environment
    /// variables (such as `CFLAGS` and `CXXFLAGS`) to construct the compiler invocation. Use of
    /// this method provides additional levers for the end user to use when configuring the build
    /// process.
    ///
    /// Just like the standard variables, this method will search for an environment variable with
    /// appropriate target prefixes, when appropriate.
    ///
    /// # Examples
    ///
    /// This method is particularly beneficial in introducing the ability to specify crate-specific
    /// flags.
    ///
    /// ```no_run
    /// cc::Build::new()
    ///     .file("src/foo.c")
    ///     .try_flags_from_environment(concat!(env!("CARGO_PKG_NAME"), "_CFLAGS"))
    ///     .expect("the environment variable must be specified and UTF-8")
    ///     .compile("foo");
    /// ```
    ///
    pub fn try_flags_from_environment(&mut self, environ_key: &str) -> Result<&mut Build, Error> {
        let flags = self.envflags(environ_key)?;
        self.flags.extend(flags.into_iter().map(Into::into));
        Ok(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
    /// cc::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
    /// cc::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
    }

    /// Disables the generation of default compiler flags. The default compiler
    /// flags may cause conflicts in some cross compiling scenarios.
    ///
    /// Setting the `CRATE_CC_NO_DEFAULTS` environment variable has the same
    /// effect as setting this to `true`. The presence of the environment
    /// variable and the value of `no_default_flags` will be OR'd together.
    pub fn no_default_flags(&mut self, no_default_flags: bool) -> &mut Build {
        self.no_default_flags = no_default_flags;
        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().into());
        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
    }

    /// Get the files which will be compiled
    pub fn get_files(&self) -> impl Iterator<Item = &Path> {
        self.files.iter().map(AsRef::as_ref)
    }

    /// Set C++ support.
    ///
    /// The other `cpp_*` options will only become active if this is set to
    /// `true`.
    ///
    /// The name of the C++ standard library to link is decided by:
    /// 1. If [`cpp_link_stdlib`](Build::cpp_link_stdlib) is set, use its value.
    /// 2. Else if the `CXXSTDLIB` environment variable is set, use its value.
    /// 3. Else the default is `libc++` for OS X and BSDs, `libc++_shared` for Android,
    /// `None` for MSVC and `libstdc++` for anything else.
    pub fn cpp(&mut self, cpp: bool) -> &mut Build {
        self.cpp = cpp;
        self
    }

    /// Set CUDA C++ support.
    ///
    /// Enabling CUDA will invoke the CUDA compiler, NVCC. While NVCC accepts
    /// the most common compiler flags, e.g. `-std=c++17`, some project-specific
    /// flags might have to be prefixed with "-Xcompiler" flag, for example as
    /// `.flag("-Xcompiler").flag("-fpermissive")`. See the documentation for
    /// `nvcc`, the CUDA compiler driver, at <https://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/>
    /// for more information.
    ///
    /// If enabled, this also implicitly enables C++ support.
    pub fn cuda(&mut self, cuda: bool) -> &mut Build {
        self.cuda = cuda;
        if cuda {
            self.cpp = true;
            self.cudart = Some("static".into());
        }
        self
    }

    /// Link CUDA run-time.
    ///
    /// This option mimics the `--cudart` NVCC command-line option. Just like
    /// the original it accepts `{none|shared|static}`, with default being
    /// `static`. The method has to be invoked after `.cuda(true)`, or not
    /// at all, if the default is right for the project.
    pub fn cudart(&mut self, cudart: &str) -> &mut Build {
        if self.cuda {
            self.cudart = Some(cudart.into());
        }
        self
    }

    /// Specify the C or C++ language standard version.
    ///
    /// These values are common to modern versions of GCC, Clang and MSVC:
    /// - `c11` for ISO/IEC 9899:2011
    /// - `c17` for ISO/IEC 9899:2018
    /// - `c++14` for ISO/IEC 14882:2014
    /// - `c++17` for ISO/IEC 14882:2017
    /// - `c++20` for ISO/IEC 14882:2020
    ///
    /// Other values have less broad support, e.g. MSVC does not support `c++11`
    /// (`c++14` is the minimum), `c89` (omit the flag instead) or `c99`.
    ///
    /// For compiling C++ code, you should also set `.cpp(true)`.
    ///
    /// The default is that no standard flag is passed to the compiler, so the
    /// language version will be the compiler's default.
    ///
    /// # Example
    ///
    /// ```no_run
    /// cc::Build::new()
    ///     .file("src/modern.cpp")
    ///     .cpp(true)
    ///     .std("c++17")
    ///     .compile("modern");
    /// ```
    pub fn std(&mut self, std: &str) -> &mut Build {
        self.std = Some(std.into());
        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 cc-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
    /// cc::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
    /// cc::Build::new()
    ///     .file("src/foo.c")
    ///     .warnings(false)
    ///     .compile("libfoo.a");
    /// ```
    pub fn warnings(&mut self, warnings: bool) -> &mut Build {
        self.warnings = Some(warnings);
        self.extra_warnings = Some(warnings);
        self
    }

    /// Set extra warnings flags.
    ///
    /// Adds some flags:
    /// - nothing for MSVC.
    /// - "-Wextra" for GNU and Clang.
    ///
    /// Enabled by default.
    ///
    /// # Example
    ///
    /// ```no_run
    /// // Disables -Wextra, -Wall remains enabled:
    /// cc::Build::new()
    ///     .file("src/foo.c")
    ///     .extra_warnings(false)
    ///     .compile("libfoo.a");
    /// ```
    pub fn extra_warnings(&mut self, warnings: bool) -> &mut Build {
        self.extra_warnings = Some(warnings);
        self
    }

    /// Set the standard library to link against when compiling with C++
    /// support.
    ///
    /// If the `CXXSTDLIB` environment variable is set, its value will
    /// override the default value, but not the value explicitly set by calling
    /// this function.
    ///
    /// 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
    /// - `c++_shared` or `c++_static` for Android
    ///
    /// # Example
    ///
    /// ```no_run
    /// cc::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 conjunction 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
    /// cc::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
    /// cc::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.into());
        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
    /// cc::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.into());
        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().into());
        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.into());
        self
    }

    /// Configures whether the compiler will emit debug information when
    /// generating object files.
    ///
    /// This option is automatically scraped from the `DEBUG` environment
    /// variable by build scripts, so it's not required to call this function.
    pub fn debug(&mut self, debug: bool) -> &mut Build {
        self.debug = Some(debug);
        self
    }

    /// Configures whether the compiler will emit instructions to store
    /// frame pointers during codegen.
    ///
    /// This option is automatically enabled when debug information is emitted.
    /// Otherwise the target platform compiler's default will be used.
    /// You can use this option to force a specific setting.
    pub fn force_frame_pointer(&mut self, force: bool) -> &mut Build {
        self.force_frame_pointer = Some(force);
        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().into());
        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().into());
        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().into());
        self
    }

    /// Configures the tool used to index 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 ranlib<P: AsRef<Path>>(&mut self, ranlib: P) -> &mut Build {
        self.ranlib = Some(ranlib.as_ref().into());
        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`
    ///  - If `emit_rerun_if_env_changed` is not `false`, `rerun-if-env-changed=`*env*
    ///
    pub fn cargo_metadata(&mut self, cargo_metadata: bool) -> &mut Build {
        self.cargo_output.metadata = cargo_metadata;
        self
    }

    /// Define whether compile warnings should be emitted for cargo. Defaults to
    /// `true`.
    ///
    /// If disabled, compiler messages will not be printed.
    /// Issues unrelated to the compilation will always produce cargo warnings regardless of this setting.
    pub fn cargo_warnings(&mut self, cargo_warnings: bool) -> &mut Build {
        self.cargo_output.warnings = cargo_warnings;
        self
    }

    /// Define whether debug information should be emitted for cargo. Defaults to whether
    /// or not the environment variable `CC_ENABLE_DEBUG_OUTPUT` is set.
    ///
    /// If enabled, the compiler will emit debug information when generating object files,
    /// such as the command invoked and the exit status.
    pub fn cargo_debug(&mut self, cargo_debug: bool) -> &mut Build {
        self.cargo_output.debug = cargo_debug;
        self
    }

    /// Adds a native library modifier that will be added to the
    /// `rustc-link-lib=static:MODIFIERS=LIBRARY_NAME` metadata line
    /// emitted for cargo if `cargo_metadata` is enabled.
    /// See <https://doc.rust-lang.org/rustc/command-line-arguments.html#-l-link-the-generated-crate-to-a-native-library>
    /// for the list of modifiers accepted by rustc.
    pub fn link_lib_modifier(&mut self, link_lib_modifier: &str) -> &mut Build {
        self.link_lib_modifiers.push(link_lib_modifier.into());
        self
    }

    /// Configures whether the compiler will emit position independent code.
    ///
    /// This option defaults to `false` for `windows-gnu` and bare metal 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 Procedure Linkage Table is used for indirect
    /// calls into shared libraries.
    ///
    /// The PLT is used to provide features like lazy binding, but introduces
    /// a small performance loss due to extra pointer indirection. Setting
    /// `use_plt` to `false` can provide a small performance increase.
    ///
    /// Note that skipping the PLT requires a recent version of GCC/Clang.
    ///
    /// This only applies to ELF targets. It has no effect on other platforms.
    pub fn use_plt(&mut self, use_plt: bool) -> &mut Build {
        self.use_plt = Some(use_plt);
        self
    }

    /// Define whether metadata should be emitted for cargo to detect environment
    /// changes that should trigger a rebuild.
    ///
    /// This has no effect if the `cargo_metadata` option is `false`.
    ///
    /// This option defaults to `true`.
    pub fn emit_rerun_if_env_changed(&mut self, emit_rerun_if_env_changed: bool) -> &mut Build {
        self.emit_rerun_if_env_changed = emit_rerun_if_env_changed;
        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().into(), b.as_ref().into()));
        self
    }

    /// Run the compiler, generating the file `output`
    ///
    /// This will return a result instead of panicking; see compile() for the complete description.
    pub fn try_compile(&self, output: &str) -> Result<(), Error> {
        let mut output_components = Path::new(output).components();
        match (output_components.next(), output_components.next()) {
            (Some(Component::Normal(_)), None) => {}
            _ => {
                return Err(Error::new(
                    ErrorKind::InvalidArgument,
                    "argument of `compile` must be a single normal path component",
                ));
            }
        }

        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 objects = objects_from_files(&self.files, &dst)?;

        self.compile_objects(&objects)?;
        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(|&(var, _)| var.as_os_str() == OsStr::new("LIB"))
                .and_then(|(_, 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.cargo_output.print_metadata(&format_args!(
                    "cargo:rustc-link-search=native={}",
                    atlmfc_lib.display()
                ));
            }
        }

        if self.link_lib_modifiers.is_empty() {
            self.cargo_output
                .print_metadata(&format_args!("cargo:rustc-link-lib=static={}", lib_name));
        } else {
            let m = self.link_lib_modifiers.join(",");
            self.cargo_output.print_metadata(&format_args!(
                "cargo:rustc-link-lib=static:{}={}",
                m, lib_name
            ));
        }
        self.cargo_output.print_metadata(&format_args!(
            "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.cargo_output
                    .print_metadata(&format_args!("cargo:rustc-link-lib={}", stdlib));
            }
        }

        let cudart = match &self.cudart {
            Some(opt) => &*opt, // {none|shared|static}
            None => "none",
        };
        if cudart != "none" {
            if let Some(nvcc) = which(&self.get_compiler().path, None) {
                // Try to figure out the -L search path. If it fails,
                // it's on user to specify one by passing it through
                // RUSTFLAGS environment variable.
                let mut libtst = false;
                let mut libdir = nvcc;
                libdir.pop(); // remove 'nvcc'
                libdir.push("..");
                let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
                if cfg!(target_os = "linux") {
                    libdir.push("targets");
                    libdir.push(target_arch.to_owned() + "-linux");
                    libdir.push("lib");
                    libtst = true;
                } else if cfg!(target_env = "msvc") {
                    libdir.push("lib");
                    match target_arch.as_str() {
                        "x86_64" => {
                            libdir.push("x64");
                            libtst = true;
                        }
                        "x86" => {
                            libdir.push("Win32");
                            libtst = true;
                        }
                        _ => libtst = false,
                    }
                }
                if libtst && libdir.is_dir() {
                    self.cargo_output.print_metadata(&format_args!(
                        "cargo:rustc-link-search=native={}",
                        libdir.to_str().unwrap()
                    ));
                }

                // And now the -l flag.
                let lib = match cudart {
                    "shared" => "cudart",
                    "static" => "cudart_static",
                    bad => panic!("unsupported cudart option: {}", bad),
                };
                self.cargo_output
                    .print_metadata(&format_args!("cargo:rustc-link-lib={}", lib));
            }
        }

        Ok(())
    }

    /// Run the compiler, generating the file `output`
    ///
    /// # Library name
    ///
    /// The `output` string argument determines the file name for the compiled
    /// library. The Rust compiler will create an assembly named "lib"+output+".a".
    /// MSVC will create a file named output+".lib".
    ///
    /// The choice of `output` is close to arbitrary, but:
    ///
    /// - must be nonempty,
    /// - must not contain a path separator (`/`),
    /// - must be unique across all `compile` invocations made by the same build
    ///   script.
    ///
    /// If your build script compiles a single source file, the base name of
    /// that source file would usually be reasonable:
    ///
    /// ```no_run
    /// cc::Build::new().file("blobstore.c").compile("blobstore");
    /// ```
    ///
    /// Compiling multiple source files, some people use their crate's name, or
    /// their crate's name + "-cc".
    ///
    /// Otherwise, please use your imagination.
    ///
    /// For backwards compatibility, if `output` starts with "lib" *and* ends
    /// with ".a", a second "lib" prefix and ".a" suffix do not get added on,
    /// but this usage is deprecated; please omit `lib` and `.a` in the argument
    /// that you pass.
    ///
    /// # 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);
        }
    }

    /// Run the compiler, generating intermediate files, but without linking
    /// them into an archive file.
    ///
    /// This will return a list of compiled object files, in the same order
    /// as they were passed in as `file`/`files` methods.
    pub fn compile_intermediates(&self) -> Vec<PathBuf> {
        match self.try_compile_intermediates() {
            Ok(v) => v,
            Err(e) => fail(&e.message),
        }
    }

    /// Run the compiler, generating intermediate files, but without linking
    /// them into an archive file.
    ///
    /// This will return a result instead of panicking; see `compile_intermediates()` for the complete description.
    pub fn try_compile_intermediates(&self) -> Result<Vec<PathBuf>, Error> {
        let dst = self.get_out_dir()?;
        let objects = objects_from_files(&self.files, &dst)?;

        self.compile_objects(&objects)?;

        Ok(objects.into_iter().map(|v| v.dst).collect())
    }

    #[cfg(feature = "parallel")]
    fn compile_objects(&self, objs: &[Object]) -> Result<(), Error> {
        use std::cell::Cell;

        use parallel::async_executor::{block_on, YieldOnce};

        if objs.len() <= 1 {
            for obj in objs {
                let (mut cmd, name) = self.create_compile_object_cmd(obj)?;
                run(&mut cmd, &name, &self.cargo_output)?;
            }

            return Ok(());
        }

        // Limit our parallelism globally with a jobserver.
        let tokens = parallel::job_token::ActiveJobTokenServer::new()?;

        // When compiling objects in parallel we do a few dirty tricks to speed
        // things up:
        //
        // * First is that we use the `jobserver` crate to limit the parallelism
        //   of this build script. The `jobserver` crate will use a jobserver
        //   configured by Cargo for build scripts to ensure that parallelism is
        //   coordinated across C compilations and Rust compilations. Before we
        //   compile anything we make sure to wait until we acquire a token.
        //
        //   Note that this jobserver is cached globally so we only used one per
        //   process and only worry about creating it once.
        //
        // * Next we use spawn the process to actually compile objects in
        //   parallel after we've acquired a token to perform some work
        //
        // With all that in mind we compile all objects in a loop here, after we
        // acquire the appropriate tokens, Once all objects have been compiled
        // we wait on all the processes and propagate the results of compilation.

        let pendings = Cell::new(Vec::<(
            Command,
            String,
            KillOnDrop,
            parallel::job_token::JobToken,
        )>::new());
        let is_disconnected = Cell::new(false);
        let has_made_progress = Cell::new(false);

        let wait_future = async {
            let mut error = None;
            // Buffer the stdout
            let mut stdout = io::BufWriter::with_capacity(128, io::stdout());

            loop {
                // If the other end of the pipe is already disconnected, then we're not gonna get any new jobs,
                // so it doesn't make sense to reuse the tokens; in fact,
                // releasing them as soon as possible (once we know that the other end is disconnected) is beneficial.
                // Imagine that the last file built takes an hour to finish; in this scenario,
                // by not releasing the tokens before that last file is done we would effectively block other processes from
                // starting sooner - even though we only need one token for that last file, not N others that were acquired.

                let mut pendings_is_empty = false;

                cell_update(&pendings, |mut pendings| {
                    // Try waiting on them.
                    parallel::retain_unordered_mut(
                        &mut pendings,
                        |(cmd, program, child, _token)| {
                            match try_wait_on_child(
                                cmd,
                                program,
                                &mut child.0,
                                &mut stdout,
                                &mut child.1,
                            ) {
                                Ok(Some(())) => {
                                    // Task done, remove the entry
                                    has_made_progress.set(true);
                                    false
                                }
                                Ok(None) => true, // Task still not finished, keep the entry
                                Err(err) => {
                                    // Task fail, remove the entry.
                                    // Since we can only return one error, log the error to make
                                    // sure users always see all the compilation failures.
                                    has_made_progress.set(true);

                                    if self.cargo_output.warnings {
                                        let _ = writeln!(stdout, "cargo:warning={}", err);
                                    }
                                    error = Some(err);

                                    false
                                }
                            }
                        },
                    );
                    pendings_is_empty = pendings.is_empty();
                    pendings
                });

                if pendings_is_empty && is_disconnected.get() {
                    break if let Some(err) = error {
                        Err(err)
                    } else {
                        Ok(())
                    };
                }

                YieldOnce::default().await;
            }
        };
        let spawn_future = async {
            for obj in objs {
                let (mut cmd, program) = self.create_compile_object_cmd(obj)?;
                let token = tokens.acquire().await?;
                let mut child = spawn(&mut cmd, &program, &self.cargo_output)?;
                let mut stderr_forwarder = StderrForwarder::new(&mut child);
                stderr_forwarder.set_non_blocking()?;

                cell_update(&pendings, |mut pendings| {
                    pendings.push((cmd, program, KillOnDrop(child, stderr_forwarder), token));
                    pendings
                });

                has_made_progress.set(true);
            }
            is_disconnected.set(true);

            Ok::<_, Error>(())
        };

        return block_on(wait_future, spawn_future, &has_made_progress);

        struct KillOnDrop(Child, StderrForwarder);

        impl Drop for KillOnDrop {
            fn drop(&mut self) {
                let child = &mut self.0;

                child.kill().ok();
            }
        }

        fn cell_update<T, F>(cell: &Cell<T>, f: F)
        where
            T: Default,
            F: FnOnce(T) -> T,
        {
            let old = cell.take();
            let new = f(old);
            cell.set(new);
        }
    }

    #[cfg(not(feature = "parallel"))]
    fn compile_objects(&self, objs: &[Object]) -> Result<(), Error> {
        for obj in objs {
            let (mut cmd, name) = self.create_compile_object_cmd(obj)?;
            run(&mut cmd, &name, &self.cargo_output)?;
        }

        Ok(())
    }

    fn create_compile_object_cmd(&self, obj: &Object) -> Result<(Command, String), Error> {
        let asm_ext = AsmFileExt::from_path(&obj.src);
        let is_asm = asm_ext.is_some();
        let target = self.get_target()?;
        let msvc = target.contains("msvc");
        let compiler = self.try_get_compiler()?;
        let clang = compiler.family == ToolFamily::Clang;
        let gnu = compiler.family == ToolFamily::Gnu;

        let is_assembler_msvc = msvc && asm_ext == Some(AsmFileExt::DotAsm);
        let (mut cmd, name) = if is_assembler_msvc {
            self.msvc_macro_assembler()?
        } else {
            let mut cmd = compiler.to_command();
            for (a, 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(),
            )
        };
        let is_arm = target.contains("aarch64") || target.contains("arm");
        command_add_output_file(
            &mut cmd, &obj.dst, self.cuda, msvc, clang, gnu, is_asm, is_arm,
        );
        // armasm and armasm64 don't requrie -c option
        if !is_assembler_msvc || !is_arm {
            cmd.arg("-c");
        }
        if self.cuda && self.cuda_file_count() > 1 {
            cmd.arg("--device-c");
        }
        if is_asm {
            cmd.args(self.asm_flags.iter().map(std::ops::Deref::deref));
        }
        if compiler.family == (ToolFamily::Msvc { clang_cl: true }) && !is_assembler_msvc {
            // #513: For `clang-cl`, separate flags/options from the input file.
            // When cross-compiling macOS -> Windows, this avoids interpreting
            // common `/Users/...` paths as the `/U` flag and triggering
            // `-Wslash-u-filename` warning.
            cmd.arg("--");
        }
        cmd.arg(&obj.src);
        if cfg!(target_os = "macos") {
            self.fix_env_for_apple_os(&mut cmd)?;
        }

        Ok((cmd, name))
    }

    /// This will return a result instead of panicking; 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 (a, b) in self.env.iter() {
            cmd.env(a, b);
        }
        cmd.arg("-E");

        assert!(
            self.files.len() <= 1,
            "Expand may only be called for a single file"
        );

        let is_asm = self
            .files
            .iter()
            .map(std::ops::Deref::deref)
            .find_map(AsmFileExt::from_path)
            .is_some();

        if compiler.family == (ToolFamily::Msvc { clang_cl: true }) && !is_asm {
            // #513: For `clang-cl`, separate flags/options from the input file.
            // When cross-compiling macOS -> Windows, this avoids interpreting
            // common `/Users/...` paths as the `/U` flag and triggering
            // `-Wslash-u-filename` warning.
            cmd.arg("--");
        }

        cmd.args(self.files.iter().map(std::ops::Deref::deref));

        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, &self.cargo_output)?)
    }

    /// 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 = cc::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 panicking; see
    /// [`get_compiler()`](Self::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()?;

        // Disable default flag generation via `no_default_flags` or environment variable
        let no_defaults = self.no_default_flags || self.getenv("CRATE_CC_NO_DEFAULTS").is_some();

        if !no_defaults {
            self.add_default_flags(&mut cmd, &target, &opt_level)?;
        } else {
            println!("Info: default compiler flags are disabled");
        }

        if let Some(ref std) = self.std {
            let separator = match cmd.family {
                ToolFamily::Msvc { .. } => ':',
                ToolFamily::Gnu | ToolFamily::Clang => '=',
            };
            cmd.push_cc_arg(format!("-std{}{}", separator, std).into());
        }

        if let Ok(flags) = self.envflags(if self.cpp { "CXXFLAGS" } else { "CFLAGS" }) {
            for arg in flags {
                cmd.push_cc_arg(arg.into());
            }
        }

        for directory in self.include_directories.iter() {
            cmd.args.push("-I".into());
            cmd.args.push(directory.as_os_str().into());
        }

        // If warnings and/or extra_warnings haven't been explicitly set,
        // then we set them only if the environment doesn't already have
        // CFLAGS/CXXFLAGS, since those variables presumably already contain
        // the desired set of warnings flags.

        if self.warnings.unwrap_or(!self.has_flags()) {
            let wflags = cmd.family.warnings_flags().into();
            cmd.push_cc_arg(wflags);
        }

        if self.extra_warnings.unwrap_or(!self.has_flags()) {
            if let Some(wflags) = cmd.family.extra_warnings_flags() {
                cmd.push_cc_arg(wflags.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.push_cc_arg((**flag).into());
            }
        }

        for (key, value) in self.definitions.iter() {
            if let Some(ref value) = *value {
                cmd.args.push(format!("-D{}={}", key, value).into());
            } else {
                cmd.args.push(format!("-D{}", key).into());
            }
        }

        if self.warnings_into_errors {
            let warnings_to_errors_flag = cmd.family.warnings_to_errors_flag().into();
            cmd.push_cc_arg(warnings_to_errors_flag);
        }

        Ok(cmd)
    }

    fn add_default_flags(
        &self,
        cmd: &mut Tool,
        target: &str,
        opt_level: &str,
    ) -> Result<(), Error> {
        // Non-target flags
        // If the flag is not conditioned on target variable, it belongs here :)
        match cmd.family {
            ToolFamily::Msvc { .. } => {
                cmd.push_cc_arg("-nologo".into());

                let crt_flag = match self.static_crt {
                    Some(true) => "-MT",
                    Some(false) => "-MD",
                    None => {
                        let features = self.getenv("CARGO_CFG_TARGET_FEATURE");
                        let features = features.as_deref().unwrap_or_default();
                        if features.contains("crt-static") {
                            "-MT"
                        } else {
                            "-MD"
                        }
                    }
                };
                cmd.push_cc_arg(crt_flag.into());

                match &opt_level[..] {
                    // Msvc uses /O1 to enable all optimizations that minimize code size.
                    "z" | "s" | "1" => cmd.push_opt_unless_duplicate("-O1".into()),
                    // -O3 is a valid value for gcc and clang compilers, but not msvc. Cap to /O2.
                    "2" | "3" => cmd.push_opt_unless_duplicate("-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.push_opt_unless_duplicate("-Os".into());
                } else {
                    cmd.push_opt_unless_duplicate(format!("-O{}", opt_level).into());
                }

                if cmd.family == ToolFamily::Clang && target.contains("windows") {
                    // Disambiguate mingw and msvc on Windows. Problem is that
                    // depending on the origin clang can default to a mismatchig
--> --------------------

--> maximum size reached

--> --------------------

[ Dauer der Verarbeitung: 0.43 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge