1 //! A library for build scripts to compile custom C code
2 //!
3 //! This library is intended to be used as a `build-dependencies` entry in
4 //! `Cargo.toml`:
5 //!
6 //! ```toml
7 //! [build-dependencies]
8 //! cc = "1.0"
9 //! ```
10 //!
11 //! The purpose of this crate is to provide the utility functions necessary to
12 //! compile C code into a static archive which is then linked into a Rust crate.
13 //! Configuration is available through the `Build` struct.
14 //!
15 //! This crate will automatically detect situations such as cross compilation or
16 //! other environment variables set by Cargo and will build code appropriately.
17 //!
18 //! The crate is not limited to C code, it can accept any source code that can
19 //! be passed to a C or C++ compiler. As such, assembly files with extensions
20 //! `.s` (gcc/clang) and `.asm` (MSVC) can also be compiled.
21 //!
22 //! [`Build`]: struct.Build.html
23 //!
24 //! # Parallelism
25 //!
26 //! To parallelize computation, enable the `parallel` feature for the crate.
27 //!
28 //! ```toml
29 //! [build-dependencies]
30 //! cc = { version = "1.0", features = ["parallel"] }
31 //! ```
32 //! To specify the max number of concurrent compilation jobs, set the `NUM_JOBS`
33 //! environment variable to the desired amount.
34 //!
35 //! Cargo will also set this environment variable when executed with the `-jN` flag.
36 //!
37 //! If `NUM_JOBS` is not set, the `RAYON_NUM_THREADS` environment variable can
38 //! also specify the build parallelism.
39 //!
40 //! # Examples
41 //!
42 //! Use the `Build` struct to compile `src/foo.c`:
43 //!
44 //! ```no_run
45 //! fn main() {
46 //!     cc::Build::new()
47 //!         .file("src/foo.c")
48 //!         .define("FOO", Some("bar"))
49 //!         .include("src")
50 //!         .compile("foo");
51 //! }
52 //! ```
53 
54 #![doc(html_root_url = "https://docs.rs/cc/1.0")]
55 #![cfg_attr(test, deny(warnings))]
56 #![allow(deprecated)]
57 #![deny(missing_docs)]
58 
59 use std::collections::HashMap;
60 use std::env;
61 use std::ffi::{OsStr, OsString};
62 use std::fmt::{self, Display};
63 use std::fs;
64 use std::io::{self, BufRead, BufReader, Read, Write};
65 use std::path::{Path, PathBuf};
66 use std::process::{Child, Command, Stdio};
67 use std::sync::{Arc, Mutex};
68 use std::thread::{self, JoinHandle};
69 
70 // These modules are all glue to support reading the MSVC version from
71 // the registry and from COM interfaces
72 #[cfg(windows)]
73 mod registry;
74 #[cfg(windows)]
75 #[macro_use]
76 mod winapi;
77 #[cfg(windows)]
78 mod com;
79 #[cfg(windows)]
80 mod setup_config;
81 
82 pub mod windows_registry;
83 
84 /// A builder for compilation of a native static library.
85 ///
86 /// A `Build` is the main type of the `cc` crate and is used to control all the
87 /// various configuration options and such of a compile. You'll find more
88 /// documentation on each method itself.
89 #[derive(Clone, Debug)]
90 pub struct Build {
91     include_directories: Vec<PathBuf>,
92     definitions: Vec<(String, Option<String>)>,
93     objects: Vec<PathBuf>,
94     flags: Vec<String>,
95     flags_supported: Vec<String>,
96     known_flag_support_status: Arc<Mutex<HashMap<String, bool>>>,
97     ar_flags: Vec<String>,
98     no_default_flags: bool,
99     files: Vec<PathBuf>,
100     cpp: bool,
101     cpp_link_stdlib: Option<Option<String>>,
102     cpp_set_stdlib: Option<String>,
103     cuda: bool,
104     target: Option<String>,
105     host: Option<String>,
106     out_dir: Option<PathBuf>,
107     opt_level: Option<String>,
108     debug: Option<bool>,
109     force_frame_pointer: Option<bool>,
110     env: Vec<(OsString, OsString)>,
111     compiler: Option<PathBuf>,
112     archiver: Option<PathBuf>,
113     cargo_metadata: bool,
114     pic: Option<bool>,
115     use_plt: Option<bool>,
116     static_crt: Option<bool>,
117     shared_flag: Option<bool>,
118     static_flag: Option<bool>,
119     warnings_into_errors: bool,
120     warnings: Option<bool>,
121     extra_warnings: Option<bool>,
122     env_cache: Arc<Mutex<HashMap<String, Option<String>>>>,
123     apple_sdk_root_cache: Arc<Mutex<HashMap<String, OsString>>>,
124 }
125 
126 /// Represents the types of errors that may occur while using cc-rs.
127 #[derive(Clone, Debug)]
128 enum ErrorKind {
129     /// Error occurred while performing I/O.
130     IOError,
131     /// Invalid architecture supplied.
132     ArchitectureInvalid,
133     /// Environment variable not found, with the var in question as extra info.
134     EnvVarNotFound,
135     /// Error occurred while using external tools (ie: invocation of compiler).
136     ToolExecError,
137     /// Error occurred due to missing external tools.
138     ToolNotFound,
139 }
140 
141 /// Represents an internal error that occurred, with an explanation.
142 #[derive(Clone, Debug)]
143 pub struct Error {
144     /// Describes the kind of error that occurred.
145     kind: ErrorKind,
146     /// More explanation of error that occurred.
147     message: String,
148 }
149 
150 impl Error {
new(kind: ErrorKind, message: &str) -> Error151     fn new(kind: ErrorKind, message: &str) -> Error {
152         Error {
153             kind: kind,
154             message: message.to_owned(),
155         }
156     }
157 }
158 
159 impl From<io::Error> for Error {
from(e: io::Error) -> Error160     fn from(e: io::Error) -> Error {
161         Error::new(ErrorKind::IOError, &format!("{}", e))
162     }
163 }
164 
165 impl Display for Error {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result166     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
167         write!(f, "{:?}: {}", self.kind, self.message)
168     }
169 }
170 
171 impl std::error::Error for Error {}
172 
173 /// Configuration used to represent an invocation of a C compiler.
174 ///
175 /// This can be used to figure out what compiler is in use, what the arguments
176 /// to it are, and what the environment variables look like for the compiler.
177 /// This can be used to further configure other build systems (e.g. forward
178 /// along CC and/or CFLAGS) or the `to_command` method can be used to run the
179 /// compiler itself.
180 #[derive(Clone, Debug)]
181 pub struct Tool {
182     path: PathBuf,
183     cc_wrapper_path: Option<PathBuf>,
184     cc_wrapper_args: Vec<OsString>,
185     args: Vec<OsString>,
186     env: Vec<(OsString, OsString)>,
187     family: ToolFamily,
188     cuda: bool,
189     removed_args: Vec<OsString>,
190 }
191 
192 /// Represents the family of tools this tool belongs to.
193 ///
194 /// Each family of tools differs in how and what arguments they accept.
195 ///
196 /// Detection of a family is done on best-effort basis and may not accurately reflect the tool.
197 #[derive(Copy, Clone, Debug, PartialEq)]
198 enum ToolFamily {
199     /// Tool is GNU Compiler Collection-like.
200     Gnu,
201     /// Tool is Clang-like. It differs from the GCC in a sense that it accepts superset of flags
202     /// and its cross-compilation approach is different.
203     Clang,
204     /// Tool is the MSVC cl.exe.
205     Msvc { clang_cl: bool },
206 }
207 
208 impl ToolFamily {
209     /// What the flag to request debug info for this family of tools look like
add_debug_flags(&self, cmd: &mut Tool)210     fn add_debug_flags(&self, cmd: &mut Tool) {
211         match *self {
212             ToolFamily::Msvc { .. } => {
213                 cmd.push_cc_arg("-Z7".into());
214             }
215             ToolFamily::Gnu | ToolFamily::Clang => {
216                 cmd.push_cc_arg("-g".into());
217             }
218         }
219     }
220 
221     /// What the flag to force frame pointers.
add_force_frame_pointer(&self, cmd: &mut Tool)222     fn add_force_frame_pointer(&self, cmd: &mut Tool) {
223         match *self {
224             ToolFamily::Gnu | ToolFamily::Clang => {
225                 cmd.push_cc_arg("-fno-omit-frame-pointer".into());
226             }
227             _ => (),
228         }
229     }
230 
231     /// What the flags to enable all warnings
warnings_flags(&self) -> &'static str232     fn warnings_flags(&self) -> &'static str {
233         match *self {
234             ToolFamily::Msvc { .. } => "-W4",
235             ToolFamily::Gnu | ToolFamily::Clang => "-Wall",
236         }
237     }
238 
239     /// What the flags to enable extra warnings
extra_warnings_flags(&self) -> Option<&'static str>240     fn extra_warnings_flags(&self) -> Option<&'static str> {
241         match *self {
242             ToolFamily::Msvc { .. } => None,
243             ToolFamily::Gnu | ToolFamily::Clang => Some("-Wextra"),
244         }
245     }
246 
247     /// What the flag to turn warning into errors
warnings_to_errors_flag(&self) -> &'static str248     fn warnings_to_errors_flag(&self) -> &'static str {
249         match *self {
250             ToolFamily::Msvc { .. } => "-WX",
251             ToolFamily::Gnu | ToolFamily::Clang => "-Werror",
252         }
253     }
254 
verbose_stderr(&self) -> bool255     fn verbose_stderr(&self) -> bool {
256         *self == ToolFamily::Clang
257     }
258 }
259 
260 /// Represents an object.
261 ///
262 /// This is a source file -> object file pair.
263 #[derive(Clone, Debug)]
264 struct Object {
265     src: PathBuf,
266     dst: PathBuf,
267 }
268 
269 impl Object {
270     /// Create a new source file -> object file pair.
new(src: PathBuf, dst: PathBuf) -> Object271     fn new(src: PathBuf, dst: PathBuf) -> Object {
272         Object { src: src, dst: dst }
273     }
274 }
275 
276 impl Build {
277     /// Construct a new instance of a blank set of configuration.
278     ///
279     /// This builder is finished with the [`compile`] function.
280     ///
281     /// [`compile`]: struct.Build.html#method.compile
new() -> Build282     pub fn new() -> Build {
283         Build {
284             include_directories: Vec::new(),
285             definitions: Vec::new(),
286             objects: Vec::new(),
287             flags: Vec::new(),
288             flags_supported: Vec::new(),
289             known_flag_support_status: Arc::new(Mutex::new(HashMap::new())),
290             ar_flags: Vec::new(),
291             no_default_flags: false,
292             files: Vec::new(),
293             shared_flag: None,
294             static_flag: None,
295             cpp: false,
296             cpp_link_stdlib: None,
297             cpp_set_stdlib: None,
298             cuda: false,
299             target: None,
300             host: None,
301             out_dir: None,
302             opt_level: None,
303             debug: None,
304             force_frame_pointer: None,
305             env: Vec::new(),
306             compiler: None,
307             archiver: None,
308             cargo_metadata: true,
309             pic: None,
310             use_plt: None,
311             static_crt: None,
312             warnings: None,
313             extra_warnings: None,
314             warnings_into_errors: false,
315             env_cache: Arc::new(Mutex::new(HashMap::new())),
316             apple_sdk_root_cache: Arc::new(Mutex::new(HashMap::new())),
317         }
318     }
319 
320     /// Add a directory to the `-I` or include path for headers
321     ///
322     /// # Example
323     ///
324     /// ```no_run
325     /// use std::path::Path;
326     ///
327     /// let library_path = Path::new("/path/to/library");
328     ///
329     /// cc::Build::new()
330     ///     .file("src/foo.c")
331     ///     .include(library_path)
332     ///     .include("src")
333     ///     .compile("foo");
334     /// ```
include<P: AsRef<Path>>(&mut self, dir: P) -> &mut Build335     pub fn include<P: AsRef<Path>>(&mut self, dir: P) -> &mut Build {
336         self.include_directories.push(dir.as_ref().to_path_buf());
337         self
338     }
339 
340     /// Specify a `-D` variable with an optional value.
341     ///
342     /// # Example
343     ///
344     /// ```no_run
345     /// cc::Build::new()
346     ///     .file("src/foo.c")
347     ///     .define("FOO", "BAR")
348     ///     .define("BAZ", None)
349     ///     .compile("foo");
350     /// ```
define<'a, V: Into<Option<&'a str>>>(&mut self, var: &str, val: V) -> &mut Build351     pub fn define<'a, V: Into<Option<&'a str>>>(&mut self, var: &str, val: V) -> &mut Build {
352         self.definitions
353             .push((var.to_string(), val.into().map(|s| s.to_string())));
354         self
355     }
356 
357     /// Add an arbitrary object file to link in
object<P: AsRef<Path>>(&mut self, obj: P) -> &mut Build358     pub fn object<P: AsRef<Path>>(&mut self, obj: P) -> &mut Build {
359         self.objects.push(obj.as_ref().to_path_buf());
360         self
361     }
362 
363     /// Add an arbitrary flag to the invocation of the compiler
364     ///
365     /// # Example
366     ///
367     /// ```no_run
368     /// cc::Build::new()
369     ///     .file("src/foo.c")
370     ///     .flag("-ffunction-sections")
371     ///     .compile("foo");
372     /// ```
flag(&mut self, flag: &str) -> &mut Build373     pub fn flag(&mut self, flag: &str) -> &mut Build {
374         self.flags.push(flag.to_string());
375         self
376     }
377 
378     /// Add an arbitrary flag to the invocation of the compiler
379     ///
380     /// # Example
381     ///
382     /// ```no_run
383     /// cc::Build::new()
384     ///     .file("src/foo.c")
385     ///     .file("src/bar.c")
386     ///     .ar_flag("/NODEFAULTLIB:libc.dll")
387     ///     .compile("foo");
388     /// ```
389 
ar_flag(&mut self, flag: &str) -> &mut Build390     pub fn ar_flag(&mut self, flag: &str) -> &mut Build {
391         self.ar_flags.push(flag.to_string());
392         self
393     }
394 
ensure_check_file(&self) -> Result<PathBuf, Error>395     fn ensure_check_file(&self) -> Result<PathBuf, Error> {
396         let out_dir = self.get_out_dir()?;
397         let src = if self.cuda {
398             assert!(self.cpp);
399             out_dir.join("flag_check.cu")
400         } else if self.cpp {
401             out_dir.join("flag_check.cpp")
402         } else {
403             out_dir.join("flag_check.c")
404         };
405 
406         if !src.exists() {
407             let mut f = fs::File::create(&src)?;
408             write!(f, "int main(void) {{ return 0; }}")?;
409         }
410 
411         Ok(src)
412     }
413 
414     /// Run the compiler to test if it accepts the given flag.
415     ///
416     /// For a convenience method for setting flags conditionally,
417     /// see `flag_if_supported()`.
418     ///
419     /// It may return error if it's unable to run the compiler with a test file
420     /// (e.g. the compiler is missing or a write to the `out_dir` failed).
421     ///
422     /// Note: Once computed, the result of this call is stored in the
423     /// `known_flag_support` field. If `is_flag_supported(flag)`
424     /// is called again, the result will be read from the hash table.
is_flag_supported(&self, flag: &str) -> Result<bool, Error>425     pub fn is_flag_supported(&self, flag: &str) -> Result<bool, Error> {
426         let mut known_status = self.known_flag_support_status.lock().unwrap();
427         if let Some(is_supported) = known_status.get(flag).cloned() {
428             return Ok(is_supported);
429         }
430 
431         let out_dir = self.get_out_dir()?;
432         let src = self.ensure_check_file()?;
433         let obj = out_dir.join("flag_check");
434         let target = self.get_target()?;
435         let host = self.get_host()?;
436         let mut cfg = Build::new();
437         cfg.flag(flag)
438             .target(&target)
439             .opt_level(0)
440             .host(&host)
441             .debug(false)
442             .cpp(self.cpp)
443             .cuda(self.cuda);
444         let mut compiler = cfg.try_get_compiler()?;
445 
446         // Clang uses stderr for verbose output, which yields a false positive
447         // result if the CFLAGS/CXXFLAGS include -v to aid in debugging.
448         if compiler.family.verbose_stderr() {
449             compiler.remove_arg("-v".into());
450         }
451 
452         let mut cmd = compiler.to_command();
453         let is_arm = target.contains("aarch64") || target.contains("arm");
454         let clang = compiler.family == ToolFamily::Clang;
455         command_add_output_file(
456             &mut cmd,
457             &obj,
458             self.cuda,
459             target.contains("msvc"),
460             clang,
461             false,
462             is_arm,
463         );
464 
465         // We need to explicitly tell msvc not to link and create an exe
466         // in the root directory of the crate
467         if target.contains("msvc") && !self.cuda {
468             cmd.arg("-c");
469         }
470 
471         cmd.arg(&src);
472 
473         let output = cmd.output()?;
474         let is_supported = output.stderr.is_empty();
475 
476         known_status.insert(flag.to_owned(), is_supported);
477         Ok(is_supported)
478     }
479 
480     /// Add an arbitrary flag to the invocation of the compiler if it supports it
481     ///
482     /// # Example
483     ///
484     /// ```no_run
485     /// cc::Build::new()
486     ///     .file("src/foo.c")
487     ///     .flag_if_supported("-Wlogical-op") // only supported by GCC
488     ///     .flag_if_supported("-Wunreachable-code") // only supported by clang
489     ///     .compile("foo");
490     /// ```
flag_if_supported(&mut self, flag: &str) -> &mut Build491     pub fn flag_if_supported(&mut self, flag: &str) -> &mut Build {
492         self.flags_supported.push(flag.to_string());
493         self
494     }
495 
496     /// Set the `-shared` flag.
497     ///
498     /// When enabled, the compiler will produce a shared object which can
499     /// then be linked with other objects to form an executable.
500     ///
501     /// # Example
502     ///
503     /// ```no_run
504     /// cc::Build::new()
505     ///     .file("src/foo.c")
506     ///     .shared_flag(true)
507     ///     .compile("libfoo.so");
508     /// ```
shared_flag(&mut self, shared_flag: bool) -> &mut Build509     pub fn shared_flag(&mut self, shared_flag: bool) -> &mut Build {
510         self.shared_flag = Some(shared_flag);
511         self
512     }
513 
514     /// Set the `-static` flag.
515     ///
516     /// When enabled on systems that support dynamic linking, this prevents
517     /// linking with the shared libraries.
518     ///
519     /// # Example
520     ///
521     /// ```no_run
522     /// cc::Build::new()
523     ///     .file("src/foo.c")
524     ///     .shared_flag(true)
525     ///     .static_flag(true)
526     ///     .compile("foo");
527     /// ```
static_flag(&mut self, static_flag: bool) -> &mut Build528     pub fn static_flag(&mut self, static_flag: bool) -> &mut Build {
529         self.static_flag = Some(static_flag);
530         self
531     }
532 
533     /// Disables the generation of default compiler flags. The default compiler
534     /// flags may cause conflicts in some cross compiling scenarios.
535     ///
536     /// Setting the `CRATE_CC_NO_DEFAULTS` environment variable has the same
537     /// effect as setting this to `true`. The presence of the environment
538     /// variable and the value of `no_default_flags` will be OR'd together.
no_default_flags(&mut self, no_default_flags: bool) -> &mut Build539     pub fn no_default_flags(&mut self, no_default_flags: bool) -> &mut Build {
540         self.no_default_flags = no_default_flags;
541         self
542     }
543 
544     /// Add a file which will be compiled
file<P: AsRef<Path>>(&mut self, p: P) -> &mut Build545     pub fn file<P: AsRef<Path>>(&mut self, p: P) -> &mut Build {
546         self.files.push(p.as_ref().to_path_buf());
547         self
548     }
549 
550     /// Add files which will be compiled
files<P>(&mut self, p: P) -> &mut Build where P: IntoIterator, P::Item: AsRef<Path>,551     pub fn files<P>(&mut self, p: P) -> &mut Build
552     where
553         P: IntoIterator,
554         P::Item: AsRef<Path>,
555     {
556         for file in p.into_iter() {
557             self.file(file);
558         }
559         self
560     }
561 
562     /// Set C++ support.
563     ///
564     /// The other `cpp_*` options will only become active if this is set to
565     /// `true`.
cpp(&mut self, cpp: bool) -> &mut Build566     pub fn cpp(&mut self, cpp: bool) -> &mut Build {
567         self.cpp = cpp;
568         self
569     }
570 
571     /// Set CUDA C++ support.
572     ///
573     /// Enabling CUDA will pass the detected C/C++ toolchain as an argument to
574     /// the CUDA compiler, NVCC. NVCC itself accepts some limited GNU-like args;
575     /// any other arguments for the C/C++ toolchain will be redirected using
576     /// "-Xcompiler" flags.
577     ///
578     /// If enabled, this also implicitly enables C++ support.
cuda(&mut self, cuda: bool) -> &mut Build579     pub fn cuda(&mut self, cuda: bool) -> &mut Build {
580         self.cuda = cuda;
581         if cuda {
582             self.cpp = true;
583         }
584         self
585     }
586 
587     /// Set warnings into errors flag.
588     ///
589     /// Disabled by default.
590     ///
591     /// Warning: turning warnings into errors only make sense
592     /// if you are a developer of the crate using cc-rs.
593     /// Some warnings only appear on some architecture or
594     /// specific version of the compiler. Any user of this crate,
595     /// or any other crate depending on it, could fail during
596     /// compile time.
597     ///
598     /// # Example
599     ///
600     /// ```no_run
601     /// cc::Build::new()
602     ///     .file("src/foo.c")
603     ///     .warnings_into_errors(true)
604     ///     .compile("libfoo.a");
605     /// ```
warnings_into_errors(&mut self, warnings_into_errors: bool) -> &mut Build606     pub fn warnings_into_errors(&mut self, warnings_into_errors: bool) -> &mut Build {
607         self.warnings_into_errors = warnings_into_errors;
608         self
609     }
610 
611     /// Set warnings flags.
612     ///
613     /// Adds some flags:
614     /// - "-Wall" for MSVC.
615     /// - "-Wall", "-Wextra" for GNU and Clang.
616     ///
617     /// Enabled by default.
618     ///
619     /// # Example
620     ///
621     /// ```no_run
622     /// cc::Build::new()
623     ///     .file("src/foo.c")
624     ///     .warnings(false)
625     ///     .compile("libfoo.a");
626     /// ```
warnings(&mut self, warnings: bool) -> &mut Build627     pub fn warnings(&mut self, warnings: bool) -> &mut Build {
628         self.warnings = Some(warnings);
629         self.extra_warnings = Some(warnings);
630         self
631     }
632 
633     /// Set extra warnings flags.
634     ///
635     /// Adds some flags:
636     /// - nothing for MSVC.
637     /// - "-Wextra" for GNU and Clang.
638     ///
639     /// Enabled by default.
640     ///
641     /// # Example
642     ///
643     /// ```no_run
644     /// // Disables -Wextra, -Wall remains enabled:
645     /// cc::Build::new()
646     ///     .file("src/foo.c")
647     ///     .extra_warnings(false)
648     ///     .compile("libfoo.a");
649     /// ```
extra_warnings(&mut self, warnings: bool) -> &mut Build650     pub fn extra_warnings(&mut self, warnings: bool) -> &mut Build {
651         self.extra_warnings = Some(warnings);
652         self
653     }
654 
655     /// Set the standard library to link against when compiling with C++
656     /// support.
657     ///
658     /// The default value of this property depends on the current target: On
659     /// OS X `Some("c++")` is used, when compiling for a Visual Studio based
660     /// target `None` is used and for other targets `Some("stdc++")` is used.
661     /// If the `CXXSTDLIB` environment variable is set, its value will
662     /// override the default value.
663     ///
664     /// A value of `None` indicates that no automatic linking should happen,
665     /// otherwise cargo will link against the specified library.
666     ///
667     /// The given library name must not contain the `lib` prefix.
668     ///
669     /// Common values:
670     /// - `stdc++` for GNU
671     /// - `c++` for Clang
672     ///
673     /// # Example
674     ///
675     /// ```no_run
676     /// cc::Build::new()
677     ///     .file("src/foo.c")
678     ///     .shared_flag(true)
679     ///     .cpp_link_stdlib("stdc++")
680     ///     .compile("libfoo.so");
681     /// ```
cpp_link_stdlib<'a, V: Into<Option<&'a str>>>( &mut self, cpp_link_stdlib: V, ) -> &mut Build682     pub fn cpp_link_stdlib<'a, V: Into<Option<&'a str>>>(
683         &mut self,
684         cpp_link_stdlib: V,
685     ) -> &mut Build {
686         self.cpp_link_stdlib = Some(cpp_link_stdlib.into().map(|s| s.into()));
687         self
688     }
689 
690     /// Force the C++ compiler to use the specified standard library.
691     ///
692     /// Setting this option will automatically set `cpp_link_stdlib` to the same
693     /// value.
694     ///
695     /// The default value of this option is always `None`.
696     ///
697     /// This option has no effect when compiling for a Visual Studio based
698     /// target.
699     ///
700     /// This option sets the `-stdlib` flag, which is only supported by some
701     /// compilers (clang, icc) but not by others (gcc). The library will not
702     /// detect which compiler is used, as such it is the responsibility of the
703     /// caller to ensure that this option is only used in conjuction with a
704     /// compiler which supports the `-stdlib` flag.
705     ///
706     /// A value of `None` indicates that no specific C++ standard library should
707     /// be used, otherwise `-stdlib` is added to the compile invocation.
708     ///
709     /// The given library name must not contain the `lib` prefix.
710     ///
711     /// Common values:
712     /// - `stdc++` for GNU
713     /// - `c++` for Clang
714     ///
715     /// # Example
716     ///
717     /// ```no_run
718     /// cc::Build::new()
719     ///     .file("src/foo.c")
720     ///     .cpp_set_stdlib("c++")
721     ///     .compile("libfoo.a");
722     /// ```
cpp_set_stdlib<'a, V: Into<Option<&'a str>>>( &mut self, cpp_set_stdlib: V, ) -> &mut Build723     pub fn cpp_set_stdlib<'a, V: Into<Option<&'a str>>>(
724         &mut self,
725         cpp_set_stdlib: V,
726     ) -> &mut Build {
727         let cpp_set_stdlib = cpp_set_stdlib.into();
728         self.cpp_set_stdlib = cpp_set_stdlib.map(|s| s.into());
729         self.cpp_link_stdlib(cpp_set_stdlib);
730         self
731     }
732 
733     /// Configures the target this configuration will be compiling for.
734     ///
735     /// This option is automatically scraped from the `TARGET` environment
736     /// variable by build scripts, so it's not required to call this function.
737     ///
738     /// # Example
739     ///
740     /// ```no_run
741     /// cc::Build::new()
742     ///     .file("src/foo.c")
743     ///     .target("aarch64-linux-android")
744     ///     .compile("foo");
745     /// ```
target(&mut self, target: &str) -> &mut Build746     pub fn target(&mut self, target: &str) -> &mut Build {
747         self.target = Some(target.to_string());
748         self
749     }
750 
751     /// Configures the host assumed by this configuration.
752     ///
753     /// This option is automatically scraped from the `HOST` environment
754     /// variable by build scripts, so it's not required to call this function.
755     ///
756     /// # Example
757     ///
758     /// ```no_run
759     /// cc::Build::new()
760     ///     .file("src/foo.c")
761     ///     .host("arm-linux-gnueabihf")
762     ///     .compile("foo");
763     /// ```
host(&mut self, host: &str) -> &mut Build764     pub fn host(&mut self, host: &str) -> &mut Build {
765         self.host = Some(host.to_string());
766         self
767     }
768 
769     /// Configures the optimization level of the generated object files.
770     ///
771     /// This option is automatically scraped from the `OPT_LEVEL` environment
772     /// variable by build scripts, so it's not required to call this function.
opt_level(&mut self, opt_level: u32) -> &mut Build773     pub fn opt_level(&mut self, opt_level: u32) -> &mut Build {
774         self.opt_level = Some(opt_level.to_string());
775         self
776     }
777 
778     /// Configures the optimization level of the generated object files.
779     ///
780     /// This option is automatically scraped from the `OPT_LEVEL` environment
781     /// variable by build scripts, so it's not required to call this function.
opt_level_str(&mut self, opt_level: &str) -> &mut Build782     pub fn opt_level_str(&mut self, opt_level: &str) -> &mut Build {
783         self.opt_level = Some(opt_level.to_string());
784         self
785     }
786 
787     /// Configures whether the compiler will emit debug information when
788     /// generating object files.
789     ///
790     /// This option is automatically scraped from the `DEBUG` environment
791     /// variable by build scripts, so it's not required to call this function.
debug(&mut self, debug: bool) -> &mut Build792     pub fn debug(&mut self, debug: bool) -> &mut Build {
793         self.debug = Some(debug);
794         self
795     }
796 
797     /// Configures whether the compiler will emit instructions to store
798     /// frame pointers during codegen.
799     ///
800     /// This option is automatically enabled when debug information is emitted.
801     /// Otherwise the target platform compiler's default will be used.
802     /// You can use this option to force a specific setting.
force_frame_pointer(&mut self, force: bool) -> &mut Build803     pub fn force_frame_pointer(&mut self, force: bool) -> &mut Build {
804         self.force_frame_pointer = Some(force);
805         self
806     }
807 
808     /// Configures the output directory where all object files and static
809     /// libraries will be located.
810     ///
811     /// This option is automatically scraped from the `OUT_DIR` environment
812     /// variable by build scripts, so it's not required to call this function.
out_dir<P: AsRef<Path>>(&mut self, out_dir: P) -> &mut Build813     pub fn out_dir<P: AsRef<Path>>(&mut self, out_dir: P) -> &mut Build {
814         self.out_dir = Some(out_dir.as_ref().to_owned());
815         self
816     }
817 
818     /// Configures the compiler to be used to produce output.
819     ///
820     /// This option is automatically determined from the target platform or a
821     /// number of environment variables, so it's not required to call this
822     /// function.
compiler<P: AsRef<Path>>(&mut self, compiler: P) -> &mut Build823     pub fn compiler<P: AsRef<Path>>(&mut self, compiler: P) -> &mut Build {
824         self.compiler = Some(compiler.as_ref().to_owned());
825         self
826     }
827 
828     /// Configures the tool used to assemble archives.
829     ///
830     /// This option is automatically determined from the target platform or a
831     /// number of environment variables, so it's not required to call this
832     /// function.
archiver<P: AsRef<Path>>(&mut self, archiver: P) -> &mut Build833     pub fn archiver<P: AsRef<Path>>(&mut self, archiver: P) -> &mut Build {
834         self.archiver = Some(archiver.as_ref().to_owned());
835         self
836     }
837     /// Define whether metadata should be emitted for cargo allowing it to
838     /// automatically link the binary. Defaults to `true`.
839     ///
840     /// The emitted metadata is:
841     ///
842     ///  - `rustc-link-lib=static=`*compiled lib*
843     ///  - `rustc-link-search=native=`*target folder*
844     ///  - When target is MSVC, the ATL-MFC libs are added via `rustc-link-search=native=`
845     ///  - When C++ is enabled, the C++ stdlib is added via `rustc-link-lib`
846     ///
cargo_metadata(&mut self, cargo_metadata: bool) -> &mut Build847     pub fn cargo_metadata(&mut self, cargo_metadata: bool) -> &mut Build {
848         self.cargo_metadata = cargo_metadata;
849         self
850     }
851 
852     /// Configures whether the compiler will emit position independent code.
853     ///
854     /// This option defaults to `false` for `windows-gnu` and bare metal targets and
855     /// to `true` for all other targets.
pic(&mut self, pic: bool) -> &mut Build856     pub fn pic(&mut self, pic: bool) -> &mut Build {
857         self.pic = Some(pic);
858         self
859     }
860 
861     /// Configures whether the Procedure Linkage Table is used for indirect
862     /// calls into shared libraries.
863     ///
864     /// The PLT is used to provide features like lazy binding, but introduces
865     /// a small performance loss due to extra pointer indirection. Setting
866     /// `use_plt` to `false` can provide a small performance increase.
867     ///
868     /// Note that skipping the PLT requires a recent version of GCC/Clang.
869     ///
870     /// This only applies to ELF targets. It has no effect on other platforms.
use_plt(&mut self, use_plt: bool) -> &mut Build871     pub fn use_plt(&mut self, use_plt: bool) -> &mut Build {
872         self.use_plt = Some(use_plt);
873         self
874     }
875 
876     /// Configures whether the /MT flag or the /MD flag will be passed to msvc build tools.
877     ///
878     /// This option defaults to `false`, and affect only msvc targets.
static_crt(&mut self, static_crt: bool) -> &mut Build879     pub fn static_crt(&mut self, static_crt: bool) -> &mut Build {
880         self.static_crt = Some(static_crt);
881         self
882     }
883 
884     #[doc(hidden)]
__set_env<A, B>(&mut self, a: A, b: B) -> &mut Build where A: AsRef<OsStr>, B: AsRef<OsStr>,885     pub fn __set_env<A, B>(&mut self, a: A, b: B) -> &mut Build
886     where
887         A: AsRef<OsStr>,
888         B: AsRef<OsStr>,
889     {
890         self.env
891             .push((a.as_ref().to_owned(), b.as_ref().to_owned()));
892         self
893     }
894 
895     /// Run the compiler, generating the file `output`
896     ///
897     /// This will return a result instead of panicing; see compile() for the complete description.
try_compile(&self, output: &str) -> Result<(), Error>898     pub fn try_compile(&self, output: &str) -> Result<(), Error> {
899         let (lib_name, gnu_lib_name) = if output.starts_with("lib") && output.ends_with(".a") {
900             (&output[3..output.len() - 2], output.to_owned())
901         } else {
902             let mut gnu = String::with_capacity(5 + output.len());
903             gnu.push_str("lib");
904             gnu.push_str(&output);
905             gnu.push_str(".a");
906             (output, gnu)
907         };
908         let dst = self.get_out_dir()?;
909 
910         let mut objects = Vec::new();
911         for file in self.files.iter() {
912             let obj = dst.join(file).with_extension("o");
913             let obj = if !obj.starts_with(&dst) {
914                 dst.join(obj.file_name().ok_or_else(|| {
915                     Error::new(ErrorKind::IOError, "Getting object file details failed.")
916                 })?)
917             } else {
918                 obj
919             };
920 
921             match obj.parent() {
922                 Some(s) => fs::create_dir_all(s)?,
923                 None => {
924                     return Err(Error::new(
925                         ErrorKind::IOError,
926                         "Getting object file details failed.",
927                     ));
928                 }
929             };
930 
931             objects.push(Object::new(file.to_path_buf(), obj));
932         }
933         self.compile_objects(&objects)?;
934         self.assemble(lib_name, &dst.join(gnu_lib_name), &objects)?;
935 
936         if self.get_target()?.contains("msvc") {
937             let compiler = self.get_base_compiler()?;
938             let atlmfc_lib = compiler
939                 .env()
940                 .iter()
941                 .find(|&&(ref var, _)| var.as_os_str() == OsStr::new("LIB"))
942                 .and_then(|&(_, ref lib_paths)| {
943                     env::split_paths(lib_paths).find(|path| {
944                         let sub = Path::new("atlmfc/lib");
945                         path.ends_with(sub) || path.parent().map_or(false, |p| p.ends_with(sub))
946                     })
947                 });
948 
949             if let Some(atlmfc_lib) = atlmfc_lib {
950                 self.print(&format!(
951                     "cargo:rustc-link-search=native={}",
952                     atlmfc_lib.display()
953                 ));
954             }
955         }
956 
957         self.print(&format!("cargo:rustc-link-lib=static={}", lib_name));
958         self.print(&format!("cargo:rustc-link-search=native={}", dst.display()));
959 
960         // Add specific C++ libraries, if enabled.
961         if self.cpp {
962             if let Some(stdlib) = self.get_cpp_link_stdlib()? {
963                 self.print(&format!("cargo:rustc-link-lib={}", stdlib));
964             }
965         }
966 
967         Ok(())
968     }
969 
970     /// Run the compiler, generating the file `output`
971     ///
972     /// The name `output` should be the name of the library.  For backwards compatibility,
973     /// the `output` may start with `lib` and end with `.a`.  The Rust compiler will create
974     /// the assembly with the lib prefix and .a extension.  MSVC will create a file without prefix,
975     /// ending with `.lib`.
976     ///
977     /// # Panics
978     ///
979     /// Panics if `output` is not formatted correctly or if one of the underlying
980     /// compiler commands fails. It can also panic if it fails reading file names
981     /// or creating directories.
compile(&self, output: &str)982     pub fn compile(&self, output: &str) {
983         if let Err(e) = self.try_compile(output) {
984             fail(&e.message);
985         }
986     }
987 
988     #[cfg(feature = "parallel")]
compile_objects<'me>(&'me self, objs: &[Object]) -> Result<(), Error>989     fn compile_objects<'me>(&'me self, objs: &[Object]) -> Result<(), Error> {
990         use std::sync::atomic::{AtomicBool, Ordering::SeqCst};
991         use std::sync::Once;
992 
993         // Limit our parallelism globally with a jobserver. Start off by
994         // releasing our own token for this process so we can have a bit of an
995         // easier to write loop below. If this fails, though, then we're likely
996         // on Windows with the main implicit token, so we just have a bit extra
997         // parallelism for a bit and don't reacquire later.
998         let server = jobserver();
999         let reacquire = server.release_raw().is_ok();
1000 
1001         // When compiling objects in parallel we do a few dirty tricks to speed
1002         // things up:
1003         //
1004         // * First is that we use the `jobserver` crate to limit the parallelism
1005         //   of this build script. The `jobserver` crate will use a jobserver
1006         //   configured by Cargo for build scripts to ensure that parallelism is
1007         //   coordinated across C compilations and Rust compilations. Before we
1008         //   compile anything we make sure to wait until we acquire a token.
1009         //
1010         //   Note that this jobserver is cached globally so we only used one per
1011         //   process and only worry about creating it once.
1012         //
1013         // * Next we use a raw `thread::spawn` per thread to actually compile
1014         //   objects in parallel. We only actually spawn a thread after we've
1015         //   acquired a token to perform some work
1016         //
1017         // * Finally though we want to keep the dependencies of this crate
1018         //   pretty light, so we avoid using a safe abstraction like `rayon` and
1019         //   instead rely on some bits of `unsafe` code. We know that this stack
1020         //   frame persists while everything is compiling so we use all the
1021         //   stack-allocated objects without cloning/reallocating. We use a
1022         //   transmute to `State` with a `'static` lifetime to persist
1023         //   everything we need across the boundary, and the join-on-drop
1024         //   semantics of `JoinOnDrop` should ensure that our stack frame is
1025         //   alive while threads are alive.
1026         //
1027         // With all that in mind we compile all objects in a loop here, after we
1028         // acquire the appropriate tokens, Once all objects have been compiled
1029         // we join on all the threads and propagate the results of compilation.
1030         //
1031         // Note that as a slight optimization we try to break out as soon as
1032         // possible as soon as any compilation fails to ensure that errors get
1033         // out to the user as fast as possible.
1034         let error = AtomicBool::new(false);
1035         let mut threads = Vec::new();
1036         for obj in objs {
1037             if error.load(SeqCst) {
1038                 break;
1039             }
1040             let token = server.acquire()?;
1041             let state = State {
1042                 build: self,
1043                 obj,
1044                 error: &error,
1045             };
1046             let state = unsafe { std::mem::transmute::<State, State<'static>>(state) };
1047             let thread = thread::spawn(|| {
1048                 let state: State<'me> = state; // erase the `'static` lifetime
1049                 let result = state.build.compile_object(state.obj);
1050                 if result.is_err() {
1051                     state.error.store(true, SeqCst);
1052                 }
1053                 drop(token); // make sure our jobserver token is released after the compile
1054                 return result;
1055             });
1056             threads.push(JoinOnDrop(Some(thread)));
1057         }
1058 
1059         for mut thread in threads {
1060             if let Some(thread) = thread.0.take() {
1061                 thread.join().expect("thread should not panic")?;
1062             }
1063         }
1064 
1065         // Reacquire our process's token before we proceed, which we released
1066         // before entering the loop above.
1067         if reacquire {
1068             server.acquire_raw()?;
1069         }
1070 
1071         return Ok(());
1072 
1073         /// Shared state from the parent thread to the child thread. This
1074         /// package of pointers is temporarily transmuted to a `'static`
1075         /// lifetime to cross the thread boundary and then once the thread is
1076         /// running we erase the `'static` to go back to an anonymous lifetime.
1077         struct State<'a> {
1078             build: &'a Build,
1079             obj: &'a Object,
1080             error: &'a AtomicBool,
1081         }
1082 
1083         /// Returns a suitable `jobserver::Client` used to coordinate
1084         /// parallelism between build scripts.
1085         fn jobserver() -> &'static jobserver::Client {
1086             static INIT: Once = Once::new();
1087             static mut JOBSERVER: Option<jobserver::Client> = None;
1088 
1089             fn _assert_sync<T: Sync>() {}
1090             _assert_sync::<jobserver::Client>();
1091 
1092             unsafe {
1093                 INIT.call_once(|| {
1094                     let server = default_jobserver();
1095                     JOBSERVER = Some(server);
1096                 });
1097                 JOBSERVER.as_ref().unwrap()
1098             }
1099         }
1100 
1101         unsafe fn default_jobserver() -> jobserver::Client {
1102             // Try to use the environmental jobserver which Cargo typically
1103             // initializes for us...
1104             if let Some(client) = jobserver::Client::from_env() {
1105                 return client;
1106             }
1107 
1108             // ... but if that fails for whatever reason select something
1109             // reasonable and crate a new jobserver. Use `NUM_JOBS` if set (it's
1110             // configured by Cargo) and otherwise just fall back to a
1111             // semi-reasonable number. Note that we could use `num_cpus` here
1112             // but it's an extra dependency that will almost never be used, so
1113             // it's generally not too worth it.
1114             let mut parallelism = 4;
1115             if let Ok(amt) = env::var("NUM_JOBS") {
1116                 if let Ok(amt) = amt.parse() {
1117                     parallelism = amt;
1118                 }
1119             }
1120 
1121             // If we create our own jobserver then be sure to reserve one token
1122             // for ourselves.
1123             let client = jobserver::Client::new(parallelism).expect("failed to create jobserver");
1124             client.acquire_raw().expect("failed to acquire initial");
1125             return client;
1126         }
1127 
1128         struct JoinOnDrop(Option<thread::JoinHandle<Result<(), Error>>>);
1129 
1130         impl Drop for JoinOnDrop {
1131             fn drop(&mut self) {
1132                 if let Some(thread) = self.0.take() {
1133                     drop(thread.join());
1134                 }
1135             }
1136         }
1137     }
1138 
1139     #[cfg(not(feature = "parallel"))]
compile_objects(&self, objs: &[Object]) -> Result<(), Error>1140     fn compile_objects(&self, objs: &[Object]) -> Result<(), Error> {
1141         for obj in objs {
1142             self.compile_object(obj)?;
1143         }
1144         Ok(())
1145     }
1146 
compile_object(&self, obj: &Object) -> Result<(), Error>1147     fn compile_object(&self, obj: &Object) -> Result<(), Error> {
1148         let is_asm = obj.src.extension().and_then(|s| s.to_str()) == Some("asm");
1149         let target = self.get_target()?;
1150         let msvc = target.contains("msvc");
1151         let compiler = self.try_get_compiler()?;
1152         let clang = compiler.family == ToolFamily::Clang;
1153         let (mut cmd, name) = if msvc && is_asm {
1154             self.msvc_macro_assembler()?
1155         } else {
1156             let mut cmd = compiler.to_command();
1157             for &(ref a, ref b) in self.env.iter() {
1158                 cmd.env(a, b);
1159             }
1160             (
1161                 cmd,
1162                 compiler
1163                     .path
1164                     .file_name()
1165                     .ok_or_else(|| Error::new(ErrorKind::IOError, "Failed to get compiler path."))?
1166                     .to_string_lossy()
1167                     .into_owned(),
1168             )
1169         };
1170         let is_arm = target.contains("aarch64") || target.contains("arm");
1171         command_add_output_file(&mut cmd, &obj.dst, self.cuda, msvc, clang, is_asm, is_arm);
1172         // armasm and armasm64 don't requrie -c option
1173         if !msvc || !is_asm || !is_arm {
1174             cmd.arg("-c");
1175         }
1176         cmd.arg(&obj.src);
1177         if cfg!(target_os = "macos") {
1178             self.fix_env_for_apple_os(&mut cmd)?;
1179         }
1180 
1181         run(&mut cmd, &name)?;
1182         Ok(())
1183     }
1184 
1185     /// This will return a result instead of panicing; see expand() for the complete description.
try_expand(&self) -> Result<Vec<u8>, Error>1186     pub fn try_expand(&self) -> Result<Vec<u8>, Error> {
1187         let compiler = self.try_get_compiler()?;
1188         let mut cmd = compiler.to_command();
1189         for &(ref a, ref b) in self.env.iter() {
1190             cmd.env(a, b);
1191         }
1192         cmd.arg("-E");
1193 
1194         assert!(
1195             self.files.len() <= 1,
1196             "Expand may only be called for a single file"
1197         );
1198 
1199         for file in self.files.iter() {
1200             cmd.arg(file);
1201         }
1202 
1203         let name = compiler
1204             .path
1205             .file_name()
1206             .ok_or_else(|| Error::new(ErrorKind::IOError, "Failed to get compiler path."))?
1207             .to_string_lossy()
1208             .into_owned();
1209 
1210         Ok(run_output(&mut cmd, &name)?)
1211     }
1212 
1213     /// Run the compiler, returning the macro-expanded version of the input files.
1214     ///
1215     /// This is only relevant for C and C++ files.
1216     ///
1217     /// # Panics
1218     /// Panics if more than one file is present in the config, or if compiler
1219     /// path has an invalid file name.
1220     ///
1221     /// # Example
1222     /// ```no_run
1223     /// let out = cc::Build::new().file("src/foo.c").expand();
1224     /// ```
expand(&self) -> Vec<u8>1225     pub fn expand(&self) -> Vec<u8> {
1226         match self.try_expand() {
1227             Err(e) => fail(&e.message),
1228             Ok(v) => v,
1229         }
1230     }
1231 
1232     /// Get the compiler that's in use for this configuration.
1233     ///
1234     /// This function will return a `Tool` which represents the culmination
1235     /// of this configuration at a snapshot in time. The returned compiler can
1236     /// be inspected (e.g. the path, arguments, environment) to forward along to
1237     /// other tools, or the `to_command` method can be used to invoke the
1238     /// compiler itself.
1239     ///
1240     /// This method will take into account all configuration such as debug
1241     /// information, optimization level, include directories, defines, etc.
1242     /// Additionally, the compiler binary in use follows the standard
1243     /// conventions for this path, e.g. looking at the explicitly set compiler,
1244     /// environment variables (a number of which are inspected here), and then
1245     /// falling back to the default configuration.
1246     ///
1247     /// # Panics
1248     ///
1249     /// Panics if an error occurred while determining the architecture.
get_compiler(&self) -> Tool1250     pub fn get_compiler(&self) -> Tool {
1251         match self.try_get_compiler() {
1252             Ok(tool) => tool,
1253             Err(e) => fail(&e.message),
1254         }
1255     }
1256 
1257     /// Get the compiler that's in use for this configuration.
1258     ///
1259     /// This will return a result instead of panicing; see get_compiler() for the complete description.
try_get_compiler(&self) -> Result<Tool, Error>1260     pub fn try_get_compiler(&self) -> Result<Tool, Error> {
1261         let opt_level = self.get_opt_level()?;
1262         let target = self.get_target()?;
1263 
1264         let mut cmd = self.get_base_compiler()?;
1265         let envflags = self.envflags(if self.cpp { "CXXFLAGS" } else { "CFLAGS" });
1266 
1267         // Disable default flag generation via `no_default_flags` or environment variable
1268         let no_defaults = self.no_default_flags || self.getenv("CRATE_CC_NO_DEFAULTS").is_some();
1269 
1270         if !no_defaults {
1271             self.add_default_flags(&mut cmd, &target, &opt_level)?;
1272         } else {
1273             println!("Info: default compiler flags are disabled");
1274         }
1275 
1276         for arg in envflags {
1277             cmd.push_cc_arg(arg.into());
1278         }
1279 
1280         for directory in self.include_directories.iter() {
1281             cmd.args.push("-I".into());
1282             cmd.args.push(directory.into());
1283         }
1284 
1285         // If warnings and/or extra_warnings haven't been explicitly set,
1286         // then we set them only if the environment doesn't already have
1287         // CFLAGS/CXXFLAGS, since those variables presumably already contain
1288         // the desired set of warnings flags.
1289 
1290         if self
1291             .warnings
1292             .unwrap_or(if self.has_flags() { false } else { true })
1293         {
1294             let wflags = cmd.family.warnings_flags().into();
1295             cmd.push_cc_arg(wflags);
1296         }
1297 
1298         if self
1299             .extra_warnings
1300             .unwrap_or(if self.has_flags() { false } else { true })
1301         {
1302             if let Some(wflags) = cmd.family.extra_warnings_flags() {
1303                 cmd.push_cc_arg(wflags.into());
1304             }
1305         }
1306 
1307         for flag in self.flags.iter() {
1308             cmd.args.push(flag.into());
1309         }
1310 
1311         for flag in self.flags_supported.iter() {
1312             if self.is_flag_supported(flag).unwrap_or(false) {
1313                 cmd.push_cc_arg(flag.into());
1314             }
1315         }
1316 
1317         for &(ref key, ref value) in self.definitions.iter() {
1318             if let Some(ref value) = *value {
1319                 cmd.args.push(format!("-D{}={}", key, value).into());
1320             } else {
1321                 cmd.args.push(format!("-D{}", key).into());
1322             }
1323         }
1324 
1325         if self.warnings_into_errors {
1326             let warnings_to_errors_flag = cmd.family.warnings_to_errors_flag().into();
1327             cmd.push_cc_arg(warnings_to_errors_flag);
1328         }
1329 
1330         Ok(cmd)
1331     }
1332 
add_default_flags( &self, cmd: &mut Tool, target: &str, opt_level: &str, ) -> Result<(), Error>1333     fn add_default_flags(
1334         &self,
1335         cmd: &mut Tool,
1336         target: &str,
1337         opt_level: &str,
1338     ) -> Result<(), Error> {
1339         // Non-target flags
1340         // If the flag is not conditioned on target variable, it belongs here :)
1341         match cmd.family {
1342             ToolFamily::Msvc { .. } => {
1343                 cmd.push_cc_arg("-nologo".into());
1344 
1345                 let crt_flag = match self.static_crt {
1346                     Some(true) => "-MT",
1347                     Some(false) => "-MD",
1348                     None => {
1349                         let features = self
1350                             .getenv("CARGO_CFG_TARGET_FEATURE")
1351                             .unwrap_or(String::new());
1352                         if features.contains("crt-static") {
1353                             "-MT"
1354                         } else {
1355                             "-MD"
1356                         }
1357                     }
1358                 };
1359                 cmd.push_cc_arg(crt_flag.into());
1360 
1361                 match &opt_level[..] {
1362                     // Msvc uses /O1 to enable all optimizations that minimize code size.
1363                     "z" | "s" | "1" => cmd.push_opt_unless_duplicate("-O1".into()),
1364                     // -O3 is a valid value for gcc and clang compilers, but not msvc. Cap to /O2.
1365                     "2" | "3" => cmd.push_opt_unless_duplicate("-O2".into()),
1366                     _ => {}
1367                 }
1368             }
1369             ToolFamily::Gnu | ToolFamily::Clang => {
1370                 // arm-linux-androideabi-gcc 4.8 shipped with Android NDK does
1371                 // not support '-Oz'
1372                 if opt_level == "z" && cmd.family != ToolFamily::Clang {
1373                     cmd.push_opt_unless_duplicate("-Os".into());
1374                 } else {
1375                     cmd.push_opt_unless_duplicate(format!("-O{}", opt_level).into());
1376                 }
1377 
1378                 if cmd.family == ToolFamily::Clang && target.contains("android") {
1379                     // For compatibility with code that doesn't use pre-defined `__ANDROID__` macro.
1380                     // If compiler used via ndk-build or cmake (officially supported build methods)
1381                     // this macros is defined.
1382                     // See https://android.googlesource.com/platform/ndk/+/refs/heads/ndk-release-r21/build/cmake/android.toolchain.cmake#456
1383                     // https://android.googlesource.com/platform/ndk/+/refs/heads/ndk-release-r21/build/core/build-binary.mk#141
1384                     cmd.push_opt_unless_duplicate("-DANDROID".into());
1385                 }
1386 
1387                 if !target.contains("-ios") {
1388                     cmd.push_cc_arg("-ffunction-sections".into());
1389                     cmd.push_cc_arg("-fdata-sections".into());
1390                 }
1391                 // Disable generation of PIC on bare-metal for now: rust-lld doesn't support this yet
1392                 if self
1393                     .pic
1394                     .unwrap_or(!target.contains("windows") && !target.contains("-none-"))
1395                 {
1396                     cmd.push_cc_arg("-fPIC".into());
1397                     // PLT only applies if code is compiled with PIC support,
1398                     // and only for ELF targets.
1399                     if target.contains("linux") && !self.use_plt.unwrap_or(true) {
1400                         cmd.push_cc_arg("-fno-plt".into());
1401                     }
1402                 }
1403             }
1404         }
1405 
1406         if self.get_debug() {
1407             if self.cuda {
1408                 // NVCC debug flag
1409                 cmd.args.push("-G".into());
1410             }
1411             let family = cmd.family;
1412             family.add_debug_flags(cmd);
1413         }
1414 
1415         if self.get_force_frame_pointer() {
1416             let family = cmd.family;
1417             family.add_force_frame_pointer(cmd);
1418         }
1419 
1420         // Target flags
1421         match cmd.family {
1422             ToolFamily::Clang => {
1423                 if !(target.contains("android")
1424                     && android_clang_compiler_uses_target_arg_internally(&cmd.path))
1425                 {
1426                     if target.contains("darwin") {
1427                         if let Some(arch) =
1428                             map_darwin_target_from_rust_to_compiler_architecture(target)
1429                         {
1430                             cmd.args
1431                                 .push(format!("--target={}-apple-darwin", arch).into());
1432                         }
1433                     } else {
1434                         cmd.args.push(format!("--target={}", target).into());
1435                     }
1436                 }
1437             }
1438             ToolFamily::Msvc { clang_cl } => {
1439                 // This is an undocumented flag from MSVC but helps with making
1440                 // builds more reproducible by avoiding putting timestamps into
1441                 // files.
1442                 cmd.push_cc_arg("-Brepro".into());
1443 
1444                 if clang_cl {
1445                     if target.contains("x86_64") {
1446                         cmd.push_cc_arg("-m64".into());
1447                     } else if target.contains("86") {
1448                         cmd.push_cc_arg("-m32".into());
1449                         cmd.push_cc_arg("-arch:IA32".into());
1450                     } else {
1451                         cmd.push_cc_arg(format!("--target={}", target).into());
1452                     }
1453                 } else {
1454                     if target.contains("i586") {
1455                         cmd.push_cc_arg("-arch:IA32".into());
1456                     }
1457                 }
1458 
1459                 // There is a check in corecrt.h that will generate a
1460                 // compilation error if
1461                 // _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE is
1462                 // not defined to 1. The check was added in Windows
1463                 // 8 days because only store apps were allowed on ARM.
1464                 // This changed with the release of Windows 10 IoT Core.
1465                 // The check will be going away in future versions of
1466                 // the SDK, but for all released versions of the
1467                 // Windows SDK it is required.
1468                 if target.contains("arm") || target.contains("thumb") {
1469                     cmd.args
1470                         .push("-D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1".into());
1471                 }
1472             }
1473             ToolFamily::Gnu => {
1474                 if target.contains("i686") || target.contains("i586") {
1475                     cmd.args.push("-m32".into());
1476                 } else if target == "x86_64-unknown-linux-gnux32" {
1477                     cmd.args.push("-mx32".into());
1478                 } else if target.contains("x86_64") || target.contains("powerpc64") {
1479                     cmd.args.push("-m64".into());
1480                 }
1481 
1482                 if target.contains("darwin") {
1483                     if let Some(arch) = map_darwin_target_from_rust_to_compiler_architecture(target)
1484                     {
1485                         cmd.args.push("-arch".into());
1486                         cmd.args.push(arch.into());
1487                     }
1488                 }
1489 
1490                 if self.static_flag.is_none() {
1491                     let features = self
1492                         .getenv("CARGO_CFG_TARGET_FEATURE")
1493                         .unwrap_or(String::new());
1494                     if features.contains("crt-static") {
1495                         cmd.args.push("-static".into());
1496                     }
1497                 }
1498 
1499                 // armv7 targets get to use armv7 instructions
1500                 if (target.starts_with("armv7") || target.starts_with("thumbv7"))
1501                     && target.contains("-linux-")
1502                 {
1503                     cmd.args.push("-march=armv7-a".into());
1504                 }
1505 
1506                 // (x86 Android doesn't say "eabi")
1507                 if target.contains("-androideabi") && target.contains("v7") {
1508                     // -march=armv7-a handled above
1509                     cmd.args.push("-mthumb".into());
1510                     if !target.contains("neon") {
1511                         // On android we can guarantee some extra float instructions
1512                         // (specified in the android spec online)
1513                         // NEON guarantees even more; see below.
1514                         cmd.args.push("-mfpu=vfpv3-d16".into());
1515                     }
1516                     cmd.args.push("-mfloat-abi=softfp".into());
1517                 }
1518 
1519                 if target.contains("neon") {
1520                     cmd.args.push("-mfpu=neon-vfpv4".into());
1521                 }
1522 
1523                 if target.starts_with("armv4t-unknown-linux-") {
1524                     cmd.args.push("-march=armv4t".into());
1525                     cmd.args.push("-marm".into());
1526                     cmd.args.push("-mfloat-abi=soft".into());
1527                 }
1528 
1529                 if target.starts_with("armv5te-unknown-linux-") {
1530                     cmd.args.push("-march=armv5te".into());
1531                     cmd.args.push("-marm".into());
1532                     cmd.args.push("-mfloat-abi=soft".into());
1533                 }
1534 
1535                 // For us arm == armv6 by default
1536                 if target.starts_with("arm-unknown-linux-") {
1537                     cmd.args.push("-march=armv6".into());
1538                     cmd.args.push("-marm".into());
1539                     if target.ends_with("hf") {
1540                         cmd.args.push("-mfpu=vfp".into());
1541                     } else {
1542                         cmd.args.push("-mfloat-abi=soft".into());
1543                     }
1544                 }
1545 
1546                 // We can guarantee some settings for FRC
1547                 if target.starts_with("arm-frc-") {
1548                     cmd.args.push("-march=armv7-a".into());
1549                     cmd.args.push("-mcpu=cortex-a9".into());
1550                     cmd.args.push("-mfpu=vfpv3".into());
1551                     cmd.args.push("-mfloat-abi=softfp".into());
1552                     cmd.args.push("-marm".into());
1553                 }
1554 
1555                 // Turn codegen down on i586 to avoid some instructions.
1556                 if target.starts_with("i586-unknown-linux-") {
1557                     cmd.args.push("-march=pentium".into());
1558                 }
1559 
1560                 // Set codegen level for i686 correctly
1561                 if target.starts_with("i686-unknown-linux-") {
1562                     cmd.args.push("-march=i686".into());
1563                 }
1564 
1565                 // Looks like `musl-gcc` makes is hard for `-m32` to make its way
1566                 // all the way to the linker, so we need to actually instruct the
1567                 // linker that we're generating 32-bit executables as well. This'll
1568                 // typically only be used for build scripts which transitively use
1569                 // these flags that try to compile executables.
1570                 if target == "i686-unknown-linux-musl" || target == "i586-unknown-linux-musl" {
1571                     cmd.args.push("-Wl,-melf_i386".into());
1572                 }
1573 
1574                 if target.starts_with("thumb") {
1575                     cmd.args.push("-mthumb".into());
1576 
1577                     if target.ends_with("eabihf") {
1578                         cmd.args.push("-mfloat-abi=hard".into())
1579                     }
1580                 }
1581                 if target.starts_with("thumbv6m") {
1582                     cmd.args.push("-march=armv6s-m".into());
1583                 }
1584                 if target.starts_with("thumbv7em") {
1585                     cmd.args.push("-march=armv7e-m".into());
1586 
1587                     if target.ends_with("eabihf") {
1588                         cmd.args.push("-mfpu=fpv4-sp-d16".into())
1589                     }
1590                 }
1591                 if target.starts_with("thumbv7m") {
1592                     cmd.args.push("-march=armv7-m".into());
1593                 }
1594                 if target.starts_with("thumbv8m.base") {
1595                     cmd.args.push("-march=armv8-m.base".into());
1596                 }
1597                 if target.starts_with("thumbv8m.main") {
1598                     cmd.args.push("-march=armv8-m.main".into());
1599 
1600                     if target.ends_with("eabihf") {
1601                         cmd.args.push("-mfpu=fpv5-sp-d16".into())
1602                     }
1603                 }
1604                 if target.starts_with("armebv7r") | target.starts_with("armv7r") {
1605                     if target.starts_with("armeb") {
1606                         cmd.args.push("-mbig-endian".into());
1607                     } else {
1608                         cmd.args.push("-mlittle-endian".into());
1609                     }
1610 
1611                     // ARM mode
1612                     cmd.args.push("-marm".into());
1613 
1614                     // R Profile
1615                     cmd.args.push("-march=armv7-r".into());
1616 
1617                     if target.ends_with("eabihf") {
1618                         // Calling convention
1619                         cmd.args.push("-mfloat-abi=hard".into());
1620 
1621                         // lowest common denominator FPU
1622                         // (see Cortex-R4 technical reference manual)
1623                         cmd.args.push("-mfpu=vfpv3-d16".into())
1624                     } else {
1625                         // Calling convention
1626                         cmd.args.push("-mfloat-abi=soft".into());
1627                     }
1628                 }
1629                 if target.starts_with("armv7a") {
1630                     cmd.args.push("-march=armv7-a".into());
1631 
1632                     if target.ends_with("eabihf") {
1633                         // lowest common denominator FPU
1634                         cmd.args.push("-mfpu=vfpv3-d16".into());
1635                     }
1636                 }
1637                 if target.starts_with("riscv32") || target.starts_with("riscv64") {
1638                     // get the 32i/32imac/32imc/64gc/64imac/... part
1639                     let mut parts = target.split('-');
1640                     if let Some(arch) = parts.next() {
1641                         let arch = &arch[5..];
1642                         cmd.args.push(("-march=rv".to_owned() + arch).into());
1643                         if target.contains("linux") && arch.starts_with("64") {
1644                             cmd.args.push("-mabi=lp64d".into());
1645                         } else if target.contains("linux") && arch.starts_with("32") {
1646                             cmd.args.push("-mabi=ilp32d".into());
1647                         } else if arch.starts_with("64") {
1648                             cmd.args.push("-mabi=lp64".into());
1649                         } else {
1650                             cmd.args.push("-mabi=ilp32".into());
1651                         }
1652                         cmd.args.push("-mcmodel=medany".into());
1653                     }
1654                 }
1655             }
1656         }
1657 
1658         if target.contains("-ios") {
1659             // FIXME: potential bug. iOS is always compiled with Clang, but Gcc compiler may be
1660             // detected instead.
1661             self.ios_flags(cmd)?;
1662         }
1663 
1664         if self.static_flag.unwrap_or(false) {
1665             cmd.args.push("-static".into());
1666         }
1667         if self.shared_flag.unwrap_or(false) {
1668             cmd.args.push("-shared".into());
1669         }
1670 
1671         if self.cpp {
1672             match (self.cpp_set_stdlib.as_ref(), cmd.family) {
1673                 (None, _) => {}
1674                 (Some(stdlib), ToolFamily::Gnu) | (Some(stdlib), ToolFamily::Clang) => {
1675                     cmd.push_cc_arg(format!("-stdlib=lib{}", stdlib).into());
1676                 }
1677                 _ => {
1678                     println!(
1679                         "cargo:warning=cpp_set_stdlib is specified, but the {:?} compiler \
1680                          does not support this option, ignored",
1681                         cmd.family
1682                     );
1683                 }
1684             }
1685         }
1686 
1687         Ok(())
1688     }
1689 
has_flags(&self) -> bool1690     fn has_flags(&self) -> bool {
1691         let flags_env_var_name = if self.cpp { "CXXFLAGS" } else { "CFLAGS" };
1692         let flags_env_var_value = self.get_var(flags_env_var_name);
1693         if let Ok(_) = flags_env_var_value {
1694             true
1695         } else {
1696             false
1697         }
1698     }
1699 
msvc_macro_assembler(&self) -> Result<(Command, String), Error>1700     fn msvc_macro_assembler(&self) -> Result<(Command, String), Error> {
1701         let target = self.get_target()?;
1702         let tool = if target.contains("x86_64") {
1703             "ml64.exe"
1704         } else if target.contains("arm") {
1705             "armasm.exe"
1706         } else if target.contains("aarch64") {
1707             "armasm64.exe"
1708         } else {
1709             "ml.exe"
1710         };
1711         let mut cmd = windows_registry::find(&target, tool).unwrap_or_else(|| self.cmd(tool));
1712         cmd.arg("-nologo"); // undocumented, yet working with armasm[64]
1713         for directory in self.include_directories.iter() {
1714             cmd.arg("-I").arg(directory);
1715         }
1716         if target.contains("aarch64") || target.contains("arm") {
1717             println!("cargo:warning=The MSVC ARM assemblers do not support -D flags");
1718         } else {
1719             for &(ref key, ref value) in self.definitions.iter() {
1720                 if let Some(ref value) = *value {
1721                     cmd.arg(&format!("-D{}={}", key, value));
1722                 } else {
1723                     cmd.arg(&format!("-D{}", key));
1724                 }
1725             }
1726         }
1727 
1728         if target.contains("i686") || target.contains("i586") {
1729             cmd.arg("-safeseh");
1730         }
1731         for flag in self.flags.iter() {
1732             cmd.arg(flag);
1733         }
1734 
1735         Ok((cmd, tool.to_string()))
1736     }
1737 
assemble(&self, lib_name: &str, dst: &Path, objs: &[Object]) -> Result<(), Error>1738     fn assemble(&self, lib_name: &str, dst: &Path, objs: &[Object]) -> Result<(), Error> {
1739         // Delete the destination if it exists as the `ar` tool at least on Unix
1740         // appends to it, which we don't want.
1741         let _ = fs::remove_file(&dst);
1742 
1743         let objects: Vec<_> = objs.iter().map(|obj| obj.dst.clone()).collect();
1744         let target = self.get_target()?;
1745         if target.contains("msvc") {
1746             let (mut cmd, program) = self.get_ar()?;
1747             let mut out = OsString::from("-out:");
1748             out.push(dst);
1749             cmd.arg(out).arg("-nologo");
1750             for flag in self.ar_flags.iter() {
1751                 cmd.arg(flag);
1752             }
1753 
1754             // Similar to https://github.com/rust-lang/rust/pull/47507
1755             // and https://github.com/rust-lang/rust/pull/48548
1756             let estimated_command_line_len = objects
1757                 .iter()
1758                 .chain(&self.objects)
1759                 .map(|a| a.as_os_str().len())
1760                 .sum::<usize>();
1761             if estimated_command_line_len > 1024 * 6 {
1762                 let mut args = String::from("\u{FEFF}"); // BOM
1763                 for arg in objects.iter().chain(&self.objects) {
1764                     args.push('"');
1765                     for c in arg.to_str().unwrap().chars() {
1766                         if c == '"' {
1767                             args.push('\\')
1768                         }
1769                         args.push(c)
1770                     }
1771                     args.push('"');
1772                     args.push('\n');
1773                 }
1774 
1775                 let mut utf16le = Vec::new();
1776                 for code_unit in args.encode_utf16() {
1777                     utf16le.push(code_unit as u8);
1778                     utf16le.push((code_unit >> 8) as u8);
1779                 }
1780 
1781                 let mut args_file = OsString::from(dst);
1782                 args_file.push(".args");
1783                 fs::File::create(&args_file)
1784                     .unwrap()
1785                     .write_all(&utf16le)
1786                     .unwrap();
1787 
1788                 let mut args_file_arg = OsString::from("@");
1789                 args_file_arg.push(args_file);
1790                 cmd.arg(args_file_arg);
1791             } else {
1792                 cmd.args(&objects).args(&self.objects);
1793             }
1794             run(&mut cmd, &program)?;
1795 
1796             // The Rust compiler will look for libfoo.a and foo.lib, but the
1797             // MSVC linker will also be passed foo.lib, so be sure that both
1798             // exist for now.
1799             let lib_dst = dst.with_file_name(format!("{}.lib", lib_name));
1800             let _ = fs::remove_file(&lib_dst);
1801             match fs::hard_link(&dst, &lib_dst).or_else(|_| {
1802                 // if hard-link fails, just copy (ignoring the number of bytes written)
1803                 fs::copy(&dst, &lib_dst).map(|_| ())
1804             }) {
1805                 Ok(_) => (),
1806                 Err(_) => {
1807                     return Err(Error::new(
1808                         ErrorKind::IOError,
1809                         "Could not copy or create a hard-link to the generated lib file.",
1810                     ));
1811                 }
1812             };
1813         } else {
1814             let (mut ar, cmd) = self.get_ar()?;
1815 
1816             // Set an environment variable to tell the OSX archiver to ensure
1817             // that all dates listed in the archive are zero, improving
1818             // determinism of builds. AFAIK there's not really official
1819             // documentation of this but there's a lot of references to it if
1820             // you search google.
1821             //
1822             // You can reproduce this locally on a mac with:
1823             //
1824             //      $ touch foo.c
1825             //      $ cc -c foo.c -o foo.o
1826             //
1827             //      # Notice that these two checksums are different
1828             //      $ ar crus libfoo1.a foo.o && sleep 2 && ar crus libfoo2.a foo.o
1829             //      $ md5sum libfoo*.a
1830             //
1831             //      # Notice that these two checksums are the same
1832             //      $ export ZERO_AR_DATE=1
1833             //      $ ar crus libfoo1.a foo.o && sleep 2 && touch foo.o && ar crus libfoo2.a foo.o
1834             //      $ md5sum libfoo*.a
1835             //
1836             // In any case if this doesn't end up getting read, it shouldn't
1837             // cause that many issues!
1838             ar.env("ZERO_AR_DATE", "1");
1839             for flag in self.ar_flags.iter() {
1840                 ar.arg(flag);
1841             }
1842             run(
1843                 ar.arg("crs").arg(dst).args(&objects).args(&self.objects),
1844                 &cmd,
1845             )?;
1846         }
1847 
1848         Ok(())
1849     }
1850 
ios_flags(&self, cmd: &mut Tool) -> Result<(), Error>1851     fn ios_flags(&self, cmd: &mut Tool) -> Result<(), Error> {
1852         enum ArchSpec {
1853             Device(&'static str),
1854             Simulator(&'static str),
1855         }
1856 
1857         let target = self.get_target()?;
1858         let arch = target.split('-').nth(0).ok_or_else(|| {
1859             Error::new(
1860                 ErrorKind::ArchitectureInvalid,
1861                 "Unknown architecture for iOS target.",
1862             )
1863         })?;
1864         let arch = match arch {
1865             "arm" | "armv7" | "thumbv7" => ArchSpec::Device("armv7"),
1866             "armv7s" | "thumbv7s" => ArchSpec::Device("armv7s"),
1867             "arm64e" => ArchSpec::Device("arm64e"),
1868             "arm64" | "aarch64" => ArchSpec::Device("arm64"),
1869             "i386" | "i686" => ArchSpec::Simulator("-m32"),
1870             "x86_64" => ArchSpec::Simulator("-m64"),
1871             _ => {
1872                 return Err(Error::new(
1873                     ErrorKind::ArchitectureInvalid,
1874                     "Unknown architecture for iOS target.",
1875                 ));
1876             }
1877         };
1878 
1879         let min_version =
1880             std::env::var("IPHONEOS_DEPLOYMENT_TARGET").unwrap_or_else(|_| "7.0".into());
1881 
1882         let sdk = match arch {
1883             ArchSpec::Device(arch) => {
1884                 cmd.args.push("-arch".into());
1885                 cmd.args.push(arch.into());
1886                 cmd.args
1887                     .push(format!("-miphoneos-version-min={}", min_version).into());
1888                 "iphoneos"
1889             }
1890             ArchSpec::Simulator(arch) => {
1891                 cmd.args.push(arch.into());
1892                 cmd.args
1893                     .push(format!("-mios-simulator-version-min={}", min_version).into());
1894                 "iphonesimulator"
1895             }
1896         };
1897 
1898         self.print(&format!("Detecting iOS SDK path for {}", sdk));
1899         let sdk_path = self.apple_sdk_root(sdk)?;
1900         cmd.args.push("-isysroot".into());
1901         cmd.args.push(sdk_path);
1902         cmd.args.push("-fembed-bitcode".into());
1903         /*
1904          * TODO we probably ultimately want the -fembed-bitcode-marker flag
1905          * but can't have it now because of an issue in LLVM:
1906          * https://github.com/alexcrichton/cc-rs/issues/301
1907          * https://github.com/rust-lang/rust/pull/48896#comment-372192660
1908          */
1909         /*
1910         if self.get_opt_level()? == "0" {
1911             cmd.args.push("-fembed-bitcode-marker".into());
1912         }
1913         */
1914 
1915         Ok(())
1916     }
1917 
cmd<P: AsRef<OsStr>>(&self, prog: P) -> Command1918     fn cmd<P: AsRef<OsStr>>(&self, prog: P) -> Command {
1919         let mut cmd = Command::new(prog);
1920         for &(ref a, ref b) in self.env.iter() {
1921             cmd.env(a, b);
1922         }
1923         cmd
1924     }
1925 
get_base_compiler(&self) -> Result<Tool, Error>1926     fn get_base_compiler(&self) -> Result<Tool, Error> {
1927         if let Some(ref c) = self.compiler {
1928             return Ok(Tool::new(c.clone()));
1929         }
1930         let host = self.get_host()?;
1931         let target = self.get_target()?;
1932         let (env, msvc, gnu, traditional, clang) = if self.cpp {
1933             ("CXX", "cl.exe", "g++", "c++", "clang++")
1934         } else {
1935             ("CC", "cl.exe", "gcc", "cc", "clang")
1936         };
1937 
1938         // On historical Solaris systems, "cc" may have been Sun Studio, which
1939         // is not flag-compatible with "gcc".  This history casts a long shadow,
1940         // and many modern illumos distributions today ship GCC as "gcc" without
1941         // also making it available as "cc".
1942         let default = if host.contains("solaris") || host.contains("illumos") {
1943             gnu
1944         } else {
1945             traditional
1946         };
1947 
1948         let cl_exe = windows_registry::find_tool(&target, "cl.exe");
1949 
1950         let tool_opt: Option<Tool> = self
1951             .env_tool(env)
1952             .map(|(tool, wrapper, args)| {
1953                 // find the driver mode, if any
1954                 const DRIVER_MODE: &str = "--driver-mode=";
1955                 let driver_mode = args
1956                     .iter()
1957                     .find(|a| a.starts_with(DRIVER_MODE))
1958                     .map(|a| &a[DRIVER_MODE.len()..]);
1959                 // Chop off leading/trailing whitespace to work around
1960                 // semi-buggy build scripts which are shared in
1961                 // makefiles/configure scripts (where spaces are far more
1962                 // lenient)
1963                 let mut t = Tool::with_clang_driver(PathBuf::from(tool.trim()), driver_mode);
1964                 if let Some(cc_wrapper) = wrapper {
1965                     t.cc_wrapper_path = Some(PathBuf::from(cc_wrapper));
1966                 }
1967                 for arg in args {
1968                     t.cc_wrapper_args.push(arg.into());
1969                 }
1970                 t
1971             })
1972             .or_else(|| {
1973                 if target.contains("emscripten") {
1974                     let tool = if self.cpp { "em++" } else { "emcc" };
1975                     // Windows uses bat file so we have to be a bit more specific
1976                     if cfg!(windows) {
1977                         let mut t = Tool::new(PathBuf::from("cmd"));
1978                         t.args.push("/c".into());
1979                         t.args.push(format!("{}.bat", tool).into());
1980                         Some(t)
1981                     } else {
1982                         Some(Tool::new(PathBuf::from(tool)))
1983                     }
1984                 } else {
1985                     None
1986                 }
1987             })
1988             .or_else(|| cl_exe.clone());
1989 
1990         let tool = match tool_opt {
1991             Some(t) => t,
1992             None => {
1993                 let compiler = if host.contains("windows") && target.contains("windows") {
1994                     if target.contains("msvc") {
1995                         msvc.to_string()
1996                     } else {
1997                         format!("{}.exe", gnu)
1998                     }
1999                 } else if target.contains("android") {
2000                     autodetect_android_compiler(&target, &host, gnu, clang)
2001                 } else if target.contains("cloudabi") {
2002                     format!("{}-{}", target, traditional)
2003                 } else if target == "wasm32-wasi"
2004                     || target == "wasm32-unknown-wasi"
2005                     || target == "wasm32-unknown-unknown"
2006                 {
2007                     "clang".to_string()
2008                 } else if target.contains("vxworks") {
2009                     if self.cpp {
2010                         "wr-c++".to_string()
2011                     } else {
2012                         "wr-cc".to_string()
2013                     }
2014                 } else if self.get_host()? != target {
2015                     let prefix = self.prefix_for_target(&target);
2016                     match prefix {
2017                         Some(prefix) => format!("{}-{}", prefix, gnu),
2018                         None => default.to_string(),
2019                     }
2020                 } else {
2021                     default.to_string()
2022                 };
2023 
2024                 let mut t = Tool::new(PathBuf::from(compiler));
2025                 if let Some(cc_wrapper) = Self::rustc_wrapper_fallback() {
2026                     t.cc_wrapper_path = Some(PathBuf::from(cc_wrapper));
2027                 }
2028                 t
2029             }
2030         };
2031 
2032         let mut tool = if self.cuda {
2033             assert!(
2034                 tool.args.is_empty(),
2035                 "CUDA compilation currently assumes empty pre-existing args"
2036             );
2037             let nvcc = match self.get_var("NVCC") {
2038                 Err(_) => "nvcc".into(),
2039                 Ok(nvcc) => nvcc,
2040             };
2041             let mut nvcc_tool = Tool::with_features(PathBuf::from(nvcc), None, self.cuda);
2042             nvcc_tool
2043                 .args
2044                 .push(format!("-ccbin={}", tool.path.display()).into());
2045             nvcc_tool.family = tool.family;
2046             nvcc_tool
2047         } else {
2048             tool
2049         };
2050 
2051         // If we found `cl.exe` in our environment, the tool we're returning is
2052         // an MSVC-like tool, *and* no env vars were set then set env vars for
2053         // the tool that we're returning.
2054         //
2055         // Env vars are needed for things like `link.exe` being put into PATH as
2056         // well as header include paths sometimes. These paths are automatically
2057         // included by default but if the `CC` or `CXX` env vars are set these
2058         // won't be used. This'll ensure that when the env vars are used to
2059         // configure for invocations like `clang-cl` we still get a "works out
2060         // of the box" experience.
2061         if let Some(cl_exe) = cl_exe {
2062             if tool.family == (ToolFamily::Msvc { clang_cl: true })
2063                 && tool.env.len() == 0
2064                 && target.contains("msvc")
2065             {
2066                 for &(ref k, ref v) in cl_exe.env.iter() {
2067                     tool.env.push((k.to_owned(), v.to_owned()));
2068                 }
2069             }
2070         }
2071 
2072         Ok(tool)
2073     }
2074 
get_var(&self, var_base: &str) -> Result<String, Error>2075     fn get_var(&self, var_base: &str) -> Result<String, Error> {
2076         let target = self.get_target()?;
2077         let host = self.get_host()?;
2078         let kind = if host == target { "HOST" } else { "TARGET" };
2079         let target_u = target.replace("-", "_");
2080         let res = self
2081             .getenv(&format!("{}_{}", var_base, target))
2082             .or_else(|| self.getenv(&format!("{}_{}", var_base, target_u)))
2083             .or_else(|| self.getenv(&format!("{}_{}", kind, var_base)))
2084             .or_else(|| self.getenv(var_base));
2085 
2086         match res {
2087             Some(res) => Ok(res),
2088             None => Err(Error::new(
2089                 ErrorKind::EnvVarNotFound,
2090                 &format!("Could not find environment variable {}.", var_base),
2091             )),
2092         }
2093     }
2094 
envflags(&self, name: &str) -> Vec<String>2095     fn envflags(&self, name: &str) -> Vec<String> {
2096         self.get_var(name)
2097             .unwrap_or(String::new())
2098             .split(|c: char| c.is_whitespace())
2099             .filter(|s| !s.is_empty())
2100             .map(|s| s.to_string())
2101             .collect()
2102     }
2103 
2104     /// Returns a fallback `cc_compiler_wrapper` by introspecting `RUSTC_WRAPPER`
rustc_wrapper_fallback() -> Option<String>2105     fn rustc_wrapper_fallback() -> Option<String> {
2106         // No explicit CC wrapper was detected, but check if RUSTC_WRAPPER
2107         // is defined and is a build accelerator that is compatible with
2108         // C/C++ compilers (e.g. sccache)
2109         let valid_wrappers = ["sccache"];
2110 
2111         let rustc_wrapper = std::env::var_os("RUSTC_WRAPPER")?;
2112         let wrapper_path = Path::new(&rustc_wrapper);
2113         let wrapper_stem = wrapper_path.file_stem()?;
2114 
2115         if valid_wrappers.contains(&wrapper_stem.to_str()?) {
2116             Some(rustc_wrapper.to_str()?.to_owned())
2117         } else {
2118             None
2119         }
2120     }
2121 
2122     /// Returns compiler path, optional modifier name from whitelist, and arguments vec
env_tool(&self, name: &str) -> Option<(String, Option<String>, Vec<String>)>2123     fn env_tool(&self, name: &str) -> Option<(String, Option<String>, Vec<String>)> {
2124         let tool = match self.get_var(name) {
2125             Ok(tool) => tool,
2126             Err(_) => return None,
2127         };
2128 
2129         // If this is an exact path on the filesystem we don't want to do any
2130         // interpretation at all, just pass it on through. This'll hopefully get
2131         // us to support spaces-in-paths.
2132         if Path::new(&tool).exists() {
2133             return Some((tool, None, Vec::new()));
2134         }
2135 
2136         // Ok now we want to handle a couple of scenarios. We'll assume from
2137         // here on out that spaces are splitting separate arguments. Two major
2138         // features we want to support are:
2139         //
2140         //      CC='sccache cc'
2141         //
2142         // aka using `sccache` or any other wrapper/caching-like-thing for
2143         // compilations. We want to know what the actual compiler is still,
2144         // though, because our `Tool` API support introspection of it to see
2145         // what compiler is in use.
2146         //
2147         // additionally we want to support
2148         //
2149         //      CC='cc -flag'
2150         //
2151         // where the CC env var is used to also pass default flags to the C
2152         // compiler.
2153         //
2154         // It's true that everything here is a bit of a pain, but apparently if
2155         // you're not literally make or bash then you get a lot of bug reports.
2156         let known_wrappers = ["ccache", "distcc", "sccache", "icecc"];
2157 
2158         let mut parts = tool.split_whitespace();
2159         let maybe_wrapper = match parts.next() {
2160             Some(s) => s,
2161             None => return None,
2162         };
2163 
2164         let file_stem = Path::new(maybe_wrapper)
2165             .file_stem()
2166             .unwrap()
2167             .to_str()
2168             .unwrap();
2169         if known_wrappers.contains(&file_stem) {
2170             if let Some(compiler) = parts.next() {
2171                 return Some((
2172                     compiler.to_string(),
2173                     Some(maybe_wrapper.to_string()),
2174                     parts.map(|s| s.to_string()).collect(),
2175                 ));
2176             }
2177         }
2178 
2179         Some((
2180             maybe_wrapper.to_string(),
2181             Self::rustc_wrapper_fallback(),
2182             parts.map(|s| s.to_string()).collect(),
2183         ))
2184     }
2185 
2186     /// Returns the default C++ standard library for the current target: `libc++`
2187     /// for OS X and `libstdc++` for anything else.
get_cpp_link_stdlib(&self) -> Result<Option<String>, Error>2188     fn get_cpp_link_stdlib(&self) -> Result<Option<String>, Error> {
2189         match self.cpp_link_stdlib.clone() {
2190             Some(s) => Ok(s),
2191             None => {
2192                 if let Ok(stdlib) = self.get_var("CXXSTDLIB") {
2193                     if stdlib.is_empty() {
2194                         Ok(None)
2195                     } else {
2196                         Ok(Some(stdlib))
2197                     }
2198                 } else {
2199                     let target = self.get_target()?;
2200                     if target.contains("msvc") {
2201                         Ok(None)
2202                     } else if target.contains("apple") {
2203                         Ok(Some("c++".to_string()))
2204                     } else if target.contains("freebsd") {
2205                         Ok(Some("c++".to_string()))
2206                     } else if target.contains("openbsd") {
2207                         Ok(Some("c++".to_string()))
2208                     } else {
2209                         Ok(Some("stdc++".to_string()))
2210                     }
2211                 }
2212             }
2213         }
2214     }
2215 
get_ar(&self) -> Result<(Command, String), Error>2216     fn get_ar(&self) -> Result<(Command, String), Error> {
2217         if let Some(ref p) = self.archiver {
2218             let name = p.file_name().and_then(|s| s.to_str()).unwrap_or("ar");
2219             return Ok((self.cmd(p), name.to_string()));
2220         }
2221         if let Ok(p) = self.get_var("AR") {
2222             return Ok((self.cmd(&p), p));
2223         }
2224         let target = self.get_target()?;
2225         let default_ar = "ar".to_string();
2226         let program = if target.contains("android") {
2227             format!("{}-ar", target.replace("armv7", "arm"))
2228         } else if target.contains("emscripten") {
2229             // Windows use bat files so we have to be a bit more specific
2230             if cfg!(windows) {
2231                 let mut cmd = self.cmd("cmd");
2232                 cmd.arg("/c").arg("emar.bat");
2233                 return Ok((cmd, "emar.bat".to_string()));
2234             }
2235 
2236             "emar".to_string()
2237         } else if target.contains("msvc") {
2238             match windows_registry::find(&target, "lib.exe") {
2239                 Some(t) => return Ok((t, "lib.exe".to_string())),
2240                 None => "lib.exe".to_string(),
2241             }
2242         } else if self.get_host()? != target {
2243             match self.prefix_for_target(&target) {
2244                 Some(p) => {
2245                     let target_ar = format!("{}-ar", p);
2246                     if Command::new(&target_ar).output().is_ok() {
2247                         target_ar
2248                     } else {
2249                         default_ar
2250                     }
2251                 }
2252                 None => default_ar,
2253             }
2254         } else {
2255             default_ar
2256         };
2257         Ok((self.cmd(&program), program))
2258     }
2259 
prefix_for_target(&self, target: &str) -> Option<String>2260     fn prefix_for_target(&self, target: &str) -> Option<String> {
2261         // CROSS_COMPILE is of the form: "arm-linux-gnueabi-"
2262         let cc_env = self.getenv("CROSS_COMPILE");
2263         let cross_compile = cc_env
2264             .as_ref()
2265             .map(|s| s.trim_right_matches('-').to_owned());
2266         cross_compile.or(match &target[..] {
2267             "aarch64-unknown-linux-gnu" => Some("aarch64-linux-gnu"),
2268             "aarch64-unknown-linux-musl" => Some("aarch64-linux-musl"),
2269             "aarch64-unknown-netbsd" => Some("aarch64--netbsd"),
2270             "arm-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"),
2271             "armv4t-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"),
2272             "armv5te-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"),
2273             "armv5te-unknown-linux-musleabi" => Some("arm-linux-gnueabi"),
2274             "arm-frc-linux-gnueabi" => Some("arm-frc-linux-gnueabi"),
2275             "arm-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"),
2276             "arm-unknown-linux-musleabi" => Some("arm-linux-musleabi"),
2277             "arm-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"),
2278             "arm-unknown-netbsd-eabi" => Some("arm--netbsdelf-eabi"),
2279             "armv6-unknown-netbsd-eabihf" => Some("armv6--netbsdelf-eabihf"),
2280             "armv7-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"),
2281             "armv7-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"),
2282             "armv7-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"),
2283             "armv7neon-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"),
2284             "armv7neon-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"),
2285             "thumbv7-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"),
2286             "thumbv7-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"),
2287             "thumbv7neon-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"),
2288             "thumbv7neon-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"),
2289             "armv7-unknown-netbsd-eabihf" => Some("armv7--netbsdelf-eabihf"),
2290             "hexagon-unknown-linux-musl" => Some("hexagon-linux-musl"),
2291             "i586-unknown-linux-musl" => Some("musl"),
2292             "i686-pc-windows-gnu" => Some("i686-w64-mingw32"),
2293             "i686-uwp-windows-gnu" => Some("i686-w64-mingw32"),
2294             "i686-unknown-linux-musl" => Some("musl"),
2295             "i686-unknown-netbsd" => Some("i486--netbsdelf"),
2296             "mips-unknown-linux-gnu" => Some("mips-linux-gnu"),
2297             "mipsel-unknown-linux-gnu" => Some("mipsel-linux-gnu"),
2298             "mips64-unknown-linux-gnuabi64" => Some("mips64-linux-gnuabi64"),
2299             "mips64el-unknown-linux-gnuabi64" => Some("mips64el-linux-gnuabi64"),
2300             "mipsisa32r6-unknown-linux-gnu" => Some("mipsisa32r6-linux-gnu"),
2301             "mipsisa32r6el-unknown-linux-gnu" => Some("mipsisa32r6el-linux-gnu"),
2302             "mipsisa64r6-unknown-linux-gnuabi64" => Some("mipsisa64r6-linux-gnuabi64"),
2303             "mipsisa64r6el-unknown-linux-gnuabi64" => Some("mipsisa64r6el-linux-gnuabi64"),
2304             "powerpc-unknown-linux-gnu" => Some("powerpc-linux-gnu"),
2305             "powerpc-unknown-linux-gnuspe" => Some("powerpc-linux-gnuspe"),
2306             "powerpc-unknown-netbsd" => Some("powerpc--netbsd"),
2307             "powerpc64-unknown-linux-gnu" => Some("powerpc-linux-gnu"),
2308             "powerpc64le-unknown-linux-gnu" => Some("powerpc64le-linux-gnu"),
2309             "riscv32i-unknown-none-elf" => self.find_working_gnu_prefix(&[
2310                 "riscv32-unknown-elf",
2311                 "riscv64-unknown-elf",
2312                 "riscv-none-embed",
2313             ]),
2314             "riscv32imac-unknown-none-elf" => self.find_working_gnu_prefix(&[
2315                 "riscv32-unknown-elf",
2316                 "riscv64-unknown-elf",
2317                 "riscv-none-embed",
2318             ]),
2319             "riscv32imc-unknown-none-elf" => self.find_working_gnu_prefix(&[
2320                 "riscv32-unknown-elf",
2321                 "riscv64-unknown-elf",
2322                 "riscv-none-embed",
2323             ]),
2324             "riscv64gc-unknown-none-elf" => self.find_working_gnu_prefix(&[
2325                 "riscv64-unknown-elf",
2326                 "riscv32-unknown-elf",
2327                 "riscv-none-embed",
2328             ]),
2329             "riscv64imac-unknown-none-elf" => self.find_working_gnu_prefix(&[
2330                 "riscv64-unknown-elf",
2331                 "riscv32-unknown-elf",
2332                 "riscv-none-embed",
2333             ]),
2334             "riscv64gc-unknown-linux-gnu" => Some("riscv64-linux-gnu"),
2335             "s390x-unknown-linux-gnu" => Some("s390x-linux-gnu"),
2336             "sparc-unknown-linux-gnu" => Some("sparc-linux-gnu"),
2337             "sparc64-unknown-linux-gnu" => Some("sparc64-linux-gnu"),
2338             "sparc64-unknown-netbsd" => Some("sparc64--netbsd"),
2339             "sparcv9-sun-solaris" => Some("sparcv9-sun-solaris"),
2340             "armv7a-none-eabi" => Some("arm-none-eabi"),
2341             "armv7a-none-eabihf" => Some("arm-none-eabi"),
2342             "armebv7r-none-eabi" => Some("arm-none-eabi"),
2343             "armebv7r-none-eabihf" => Some("arm-none-eabi"),
2344             "armv7r-none-eabi" => Some("arm-none-eabi"),
2345             "armv7r-none-eabihf" => Some("arm-none-eabi"),
2346             "thumbv6m-none-eabi" => Some("arm-none-eabi"),
2347             "thumbv7em-none-eabi" => Some("arm-none-eabi"),
2348             "thumbv7em-none-eabihf" => Some("arm-none-eabi"),
2349             "thumbv7m-none-eabi" => Some("arm-none-eabi"),
2350             "thumbv8m.base-none-eabi" => Some("arm-none-eabi"),
2351             "thumbv8m.main-none-eabi" => Some("arm-none-eabi"),
2352             "thumbv8m.main-none-eabihf" => Some("arm-none-eabi"),
2353             "x86_64-pc-windows-gnu" => Some("x86_64-w64-mingw32"),
2354             "x86_64-uwp-windows-gnu" => Some("x86_64-w64-mingw32"),
2355             "x86_64-rumprun-netbsd" => Some("x86_64-rumprun-netbsd"),
2356             "x86_64-unknown-linux-musl" => Some("musl"),
2357             "x86_64-unknown-netbsd" => Some("x86_64--netbsd"),
2358             _ => None,
2359         }
2360         .map(|x| x.to_owned()))
2361     }
2362 
2363     /// Some platforms have multiple, compatible, canonical prefixes. Look through
2364     /// each possible prefix for a compiler that exists and return it. The prefixes
2365     /// should be ordered from most-likely to least-likely.
find_working_gnu_prefix(&self, prefixes: &[&'static str]) -> Option<&'static str>2366     fn find_working_gnu_prefix(&self, prefixes: &[&'static str]) -> Option<&'static str> {
2367         let suffix = if self.cpp { "-g++" } else { "-gcc" };
2368         let extension = std::env::consts::EXE_SUFFIX;
2369 
2370         // Loop through PATH entries searching for each toolchain. This ensures that we
2371         // are more likely to discover the toolchain early on, because chances are good
2372         // that the desired toolchain is in one of the higher-priority paths.
2373         env::var_os("PATH")
2374             .as_ref()
2375             .and_then(|path_entries| {
2376                 env::split_paths(path_entries).find_map(|path_entry| {
2377                     for prefix in prefixes {
2378                         let target_compiler = format!("{}{}{}", prefix, suffix, extension);
2379                         if path_entry.join(&target_compiler).exists() {
2380                             return Some(prefix);
2381                         }
2382                     }
2383                     None
2384                 })
2385             })
2386             .map(|prefix| *prefix)
2387             .or_else(||
2388             // If no toolchain was found, provide the first toolchain that was passed in.
2389             // This toolchain has been shown not to exist, however it will appear in the
2390             // error that is shown to the user which should make it easier to search for
2391             // where it should be obtained.
2392             prefixes.first().map(|prefix| *prefix))
2393     }
2394 
get_target(&self) -> Result<String, Error>2395     fn get_target(&self) -> Result<String, Error> {
2396         match self.target.clone() {
2397             Some(t) => Ok(t),
2398             None => Ok(self.getenv_unwrap("TARGET")?),
2399         }
2400     }
2401 
get_host(&self) -> Result<String, Error>2402     fn get_host(&self) -> Result<String, Error> {
2403         match self.host.clone() {
2404             Some(h) => Ok(h),
2405             None => Ok(self.getenv_unwrap("HOST")?),
2406         }
2407     }
2408 
get_opt_level(&self) -> Result<String, Error>2409     fn get_opt_level(&self) -> Result<String, Error> {
2410         match self.opt_level.as_ref().cloned() {
2411             Some(ol) => Ok(ol),
2412             None => Ok(self.getenv_unwrap("OPT_LEVEL")?),
2413         }
2414     }
2415 
get_debug(&self) -> bool2416     fn get_debug(&self) -> bool {
2417         self.debug.unwrap_or_else(|| match self.getenv("DEBUG") {
2418             Some(s) => s != "false",
2419             None => false,
2420         })
2421     }
2422 
get_force_frame_pointer(&self) -> bool2423     fn get_force_frame_pointer(&self) -> bool {
2424         self.force_frame_pointer.unwrap_or_else(|| self.get_debug())
2425     }
2426 
get_out_dir(&self) -> Result<PathBuf, Error>2427     fn get_out_dir(&self) -> Result<PathBuf, Error> {
2428         match self.out_dir.clone() {
2429             Some(p) => Ok(p),
2430             None => Ok(env::var_os("OUT_DIR").map(PathBuf::from).ok_or_else(|| {
2431                 Error::new(
2432                     ErrorKind::EnvVarNotFound,
2433                     "Environment variable OUT_DIR not defined.",
2434                 )
2435             })?),
2436         }
2437     }
2438 
getenv(&self, v: &str) -> Option<String>2439     fn getenv(&self, v: &str) -> Option<String> {
2440         let mut cache = self.env_cache.lock().unwrap();
2441         if let Some(val) = cache.get(v) {
2442             return val.clone();
2443         }
2444         let r = env::var(v).ok();
2445         self.print(&format!("{} = {:?}", v, r));
2446         cache.insert(v.to_string(), r.clone());
2447         r
2448     }
2449 
getenv_unwrap(&self, v: &str) -> Result<String, Error>2450     fn getenv_unwrap(&self, v: &str) -> Result<String, Error> {
2451         match self.getenv(v) {
2452             Some(s) => Ok(s),
2453             None => Err(Error::new(
2454                 ErrorKind::EnvVarNotFound,
2455                 &format!("Environment variable {} not defined.", v.to_string()),
2456             )),
2457         }
2458     }
2459 
print(&self, s: &str)2460     fn print(&self, s: &str) {
2461         if self.cargo_metadata {
2462             println!("{}", s);
2463         }
2464     }
2465 
fix_env_for_apple_os(&self, cmd: &mut Command) -> Result<(), Error>2466     fn fix_env_for_apple_os(&self, cmd: &mut Command) -> Result<(), Error> {
2467         let target = self.get_target()?;
2468         let host = self.get_host()?;
2469         if host.contains("apple-darwin") && target.contains("apple-darwin") {
2470             // If, for example, `cargo` runs during the build of an XCode project, then `SDKROOT` environment variable
2471             // would represent the current target, and this is the problem for us, if we want to compile something
2472             // for the host, when host != target.
2473             // We can not just remove `SDKROOT`, because, again, for example, XCode add to PATH
2474             // /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
2475             // and `cc` from this path can not find system include files, like `pthread.h`, if `SDKROOT`
2476             // is not set
2477             if let Ok(sdkroot) = env::var("SDKROOT") {
2478                 if !sdkroot.contains("MacOSX") {
2479                     let macos_sdk = self.apple_sdk_root("macosx")?;
2480                     cmd.env("SDKROOT", macos_sdk);
2481                 }
2482             }
2483             // Additionally, `IPHONEOS_DEPLOYMENT_TARGET` must not be set when using the Xcode linker at
2484             // "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld",
2485             // although this is apparently ignored when using the linker at "/usr/bin/ld".
2486             cmd.env_remove("IPHONEOS_DEPLOYMENT_TARGET");
2487         }
2488         Ok(())
2489     }
2490 
apple_sdk_root(&self, sdk: &str) -> Result<OsString, Error>2491     fn apple_sdk_root(&self, sdk: &str) -> Result<OsString, Error> {
2492         let mut cache = self
2493             .apple_sdk_root_cache
2494             .lock()
2495             .expect("apple_sdk_root_cache lock failed");
2496         if let Some(ret) = cache.get(sdk) {
2497             return Ok(ret.clone());
2498         }
2499 
2500         let sdk_path = self
2501             .cmd("xcrun")
2502             .arg("--show-sdk-path")
2503             .arg("--sdk")
2504             .arg(sdk)
2505             .stderr(Stdio::inherit())
2506             .output()?
2507             .stdout;
2508 
2509         let sdk_path = match String::from_utf8(sdk_path) {
2510             Ok(p) => p,
2511             Err(_) => {
2512                 return Err(Error::new(
2513                     ErrorKind::IOError,
2514                     "Unable to determine iOS SDK path.",
2515                 ));
2516             }
2517         };
2518         let ret: OsString = sdk_path.trim().into();
2519         cache.insert(sdk.into(), ret.clone());
2520         Ok(ret)
2521     }
2522 }
2523 
2524 impl Default for Build {
default() -> Build2525     fn default() -> Build {
2526         Build::new()
2527     }
2528 }
2529 
2530 impl Tool {
new(path: PathBuf) -> Self2531     fn new(path: PathBuf) -> Self {
2532         Tool::with_features(path, None, false)
2533     }
2534 
with_clang_driver(path: PathBuf, clang_driver: Option<&str>) -> Self2535     fn with_clang_driver(path: PathBuf, clang_driver: Option<&str>) -> Self {
2536         Self::with_features(path, clang_driver, false)
2537     }
2538 
2539     #[cfg(windows)]
2540     /// Explictly set the `ToolFamily`, skipping name-based detection.
with_family(path: PathBuf, family: ToolFamily) -> Self2541     fn with_family(path: PathBuf, family: ToolFamily) -> Self {
2542         Self {
2543             path: path,
2544             cc_wrapper_path: None,
2545             cc_wrapper_args: Vec::new(),
2546             args: Vec::new(),
2547             env: Vec::new(),
2548             family: family,
2549             cuda: false,
2550             removed_args: Vec::new(),
2551         }
2552     }
2553 
with_features(path: PathBuf, clang_driver: Option<&str>, cuda: bool) -> Self2554     fn with_features(path: PathBuf, clang_driver: Option<&str>, cuda: bool) -> Self {
2555         // Try to detect family of the tool from its name, falling back to Gnu.
2556         let family = if let Some(fname) = path.file_name().and_then(|p| p.to_str()) {
2557             if fname.contains("clang-cl") {
2558                 ToolFamily::Msvc { clang_cl: true }
2559             } else if fname.contains("cl")
2560                 && !fname.contains("cloudabi")
2561                 && !fname.contains("uclibc")
2562                 && !fname.contains("clang")
2563             {
2564                 ToolFamily::Msvc { clang_cl: false }
2565             } else if fname.contains("clang") {
2566                 match clang_driver {
2567                     Some("cl") => ToolFamily::Msvc { clang_cl: true },
2568                     _ => ToolFamily::Clang,
2569                 }
2570             } else {
2571                 ToolFamily::Gnu
2572             }
2573         } else {
2574             ToolFamily::Gnu
2575         };
2576 
2577         Tool {
2578             path: path,
2579             cc_wrapper_path: None,
2580             cc_wrapper_args: Vec::new(),
2581             args: Vec::new(),
2582             env: Vec::new(),
2583             family: family,
2584             cuda: cuda,
2585             removed_args: Vec::new(),
2586         }
2587     }
2588 
2589     /// Add an argument to be stripped from the final command arguments.
remove_arg(&mut self, flag: OsString)2590     fn remove_arg(&mut self, flag: OsString) {
2591         self.removed_args.push(flag);
2592     }
2593 
2594     /// Add a flag, and optionally prepend the NVCC wrapper flag "-Xcompiler".
2595     ///
2596     /// Currently this is only used for compiling CUDA sources, since NVCC only
2597     /// accepts a limited set of GNU-like flags, and the rest must be prefixed
2598     /// with a "-Xcompiler" flag to get passed to the underlying C++ compiler.
push_cc_arg(&mut self, flag: OsString)2599     fn push_cc_arg(&mut self, flag: OsString) {
2600         if self.cuda {
2601             self.args.push("-Xcompiler".into());
2602         }
2603         self.args.push(flag);
2604     }
2605 
is_duplicate_opt_arg(&self, flag: &OsString) -> bool2606     fn is_duplicate_opt_arg(&self, flag: &OsString) -> bool {
2607         let flag = flag.to_str().unwrap();
2608         let mut chars = flag.chars();
2609 
2610         // Only duplicate check compiler flags
2611         if self.is_like_msvc() {
2612             if chars.next() != Some('/') {
2613                 return false;
2614             }
2615         } else if self.is_like_gnu() || self.is_like_clang() {
2616             if chars.next() != Some('-') {
2617                 return false;
2618             }
2619         }
2620 
2621         // Check for existing optimization flags (-O, /O)
2622         if chars.next() == Some('O') {
2623             return self
2624                 .args()
2625                 .iter()
2626                 .any(|ref a| a.to_str().unwrap_or("").chars().nth(1) == Some('O'));
2627         }
2628 
2629         // TODO Check for existing -m..., -m...=..., /arch:... flags
2630         return false;
2631     }
2632 
2633     /// Don't push optimization arg if it conflicts with existing args
push_opt_unless_duplicate(&mut self, flag: OsString)2634     fn push_opt_unless_duplicate(&mut self, flag: OsString) {
2635         if self.is_duplicate_opt_arg(&flag) {
2636             println!("Info: Ignoring duplicate arg {:?}", &flag);
2637         } else {
2638             self.push_cc_arg(flag);
2639         }
2640     }
2641 
2642     /// Converts this compiler into a `Command` that's ready to be run.
2643     ///
2644     /// This is useful for when the compiler needs to be executed and the
2645     /// command returned will already have the initial arguments and environment
2646     /// variables configured.
to_command(&self) -> Command2647     pub fn to_command(&self) -> Command {
2648         let mut cmd = match self.cc_wrapper_path {
2649             Some(ref cc_wrapper_path) => {
2650                 let mut cmd = Command::new(&cc_wrapper_path);
2651                 cmd.arg(&self.path);
2652                 cmd
2653             }
2654             None => Command::new(&self.path),
2655         };
2656         cmd.args(&self.cc_wrapper_args);
2657 
2658         let value = self
2659             .args
2660             .iter()
2661             .filter(|a| !self.removed_args.contains(a))
2662             .collect::<Vec<_>>();
2663         cmd.args(&value);
2664 
2665         for &(ref k, ref v) in self.env.iter() {
2666             cmd.env(k, v);
2667         }
2668         cmd
2669     }
2670 
2671     /// Returns the path for this compiler.
2672     ///
2673     /// Note that this may not be a path to a file on the filesystem, e.g. "cc",
2674     /// but rather something which will be resolved when a process is spawned.
path(&self) -> &Path2675     pub fn path(&self) -> &Path {
2676         &self.path
2677     }
2678 
2679     /// Returns the default set of arguments to the compiler needed to produce
2680     /// executables for the target this compiler generates.
args(&self) -> &[OsString]2681     pub fn args(&self) -> &[OsString] {
2682         &self.args
2683     }
2684 
2685     /// Returns the set of environment variables needed for this compiler to
2686     /// operate.
2687     ///
2688     /// This is typically only used for MSVC compilers currently.
env(&self) -> &[(OsString, OsString)]2689     pub fn env(&self) -> &[(OsString, OsString)] {
2690         &self.env
2691     }
2692 
2693     /// Returns the compiler command in format of CC environment variable.
2694     /// Or empty string if CC env was not present
2695     ///
2696     /// This is typically used by configure script
cc_env(&self) -> OsString2697     pub fn cc_env(&self) -> OsString {
2698         match self.cc_wrapper_path {
2699             Some(ref cc_wrapper_path) => {
2700                 let mut cc_env = cc_wrapper_path.as_os_str().to_owned();
2701                 cc_env.push(" ");
2702                 cc_env.push(self.path.to_path_buf().into_os_string());
2703                 for arg in self.cc_wrapper_args.iter() {
2704                     cc_env.push(" ");
2705                     cc_env.push(arg);
2706                 }
2707                 cc_env
2708             }
2709             None => OsString::from(""),
2710         }
2711     }
2712 
2713     /// Returns the compiler flags in format of CFLAGS environment variable.
2714     /// Important here - this will not be CFLAGS from env, its internal gcc's flags to use as CFLAGS
2715     /// This is typically used by configure script
cflags_env(&self) -> OsString2716     pub fn cflags_env(&self) -> OsString {
2717         let mut flags = OsString::new();
2718         for (i, arg) in self.args.iter().enumerate() {
2719             if i > 0 {
2720                 flags.push(" ");
2721             }
2722             flags.push(arg);
2723         }
2724         flags
2725     }
2726 
2727     /// Whether the tool is GNU Compiler Collection-like.
is_like_gnu(&self) -> bool2728     pub fn is_like_gnu(&self) -> bool {
2729         self.family == ToolFamily::Gnu
2730     }
2731 
2732     /// Whether the tool is Clang-like.
is_like_clang(&self) -> bool2733     pub fn is_like_clang(&self) -> bool {
2734         self.family == ToolFamily::Clang
2735     }
2736 
2737     /// Whether the tool is MSVC-like.
is_like_msvc(&self) -> bool2738     pub fn is_like_msvc(&self) -> bool {
2739         match self.family {
2740             ToolFamily::Msvc { .. } => true,
2741             _ => false,
2742         }
2743     }
2744 }
2745 
run(cmd: &mut Command, program: &str) -> Result<(), Error>2746 fn run(cmd: &mut Command, program: &str) -> Result<(), Error> {
2747     let (mut child, print) = spawn(cmd, program)?;
2748     let status = match child.wait() {
2749         Ok(s) => s,
2750         Err(_) => {
2751             return Err(Error::new(
2752                 ErrorKind::ToolExecError,
2753                 &format!(
2754                     "Failed to wait on spawned child process, command {:?} with args {:?}.",
2755                     cmd, program
2756                 ),
2757             ));
2758         }
2759     };
2760     print.join().unwrap();
2761     println!("{}", status);
2762 
2763     if status.success() {
2764         Ok(())
2765     } else {
2766         Err(Error::new(
2767             ErrorKind::ToolExecError,
2768             &format!(
2769                 "Command {:?} with args {:?} did not execute successfully (status code {}).",
2770                 cmd, program, status
2771             ),
2772         ))
2773     }
2774 }
2775 
run_output(cmd: &mut Command, program: &str) -> Result<Vec<u8>, Error>2776 fn run_output(cmd: &mut Command, program: &str) -> Result<Vec<u8>, Error> {
2777     cmd.stdout(Stdio::piped());
2778     let (mut child, print) = spawn(cmd, program)?;
2779     let mut stdout = vec![];
2780     child
2781         .stdout
2782         .take()
2783         .unwrap()
2784         .read_to_end(&mut stdout)
2785         .unwrap();
2786     let status = match child.wait() {
2787         Ok(s) => s,
2788         Err(_) => {
2789             return Err(Error::new(
2790                 ErrorKind::ToolExecError,
2791                 &format!(
2792                     "Failed to wait on spawned child process, command {:?} with args {:?}.",
2793                     cmd, program
2794                 ),
2795             ));
2796         }
2797     };
2798     print.join().unwrap();
2799     println!("{}", status);
2800 
2801     if status.success() {
2802         Ok(stdout)
2803     } else {
2804         Err(Error::new(
2805             ErrorKind::ToolExecError,
2806             &format!(
2807                 "Command {:?} with args {:?} did not execute successfully (status code {}).",
2808                 cmd, program, status
2809             ),
2810         ))
2811     }
2812 }
2813 
spawn(cmd: &mut Command, program: &str) -> Result<(Child, JoinHandle<()>), Error>2814 fn spawn(cmd: &mut Command, program: &str) -> Result<(Child, JoinHandle<()>), Error> {
2815     println!("running: {:?}", cmd);
2816 
2817     // Capture the standard error coming from these programs, and write it out
2818     // with cargo:warning= prefixes. Note that this is a bit wonky to avoid
2819     // requiring the output to be UTF-8, we instead just ship bytes from one
2820     // location to another.
2821     match cmd.stderr(Stdio::piped()).spawn() {
2822         Ok(mut child) => {
2823             let stderr = BufReader::new(child.stderr.take().unwrap());
2824             let print = thread::spawn(move || {
2825                 for line in stderr.split(b'\n').filter_map(|l| l.ok()) {
2826                     print!("cargo:warning=");
2827                     std::io::stdout().write_all(&line).unwrap();
2828                     println!("");
2829                 }
2830             });
2831             Ok((child, print))
2832         }
2833         Err(ref e) if e.kind() == io::ErrorKind::NotFound => {
2834             let extra = if cfg!(windows) {
2835                 " (see https://github.com/alexcrichton/cc-rs#compile-time-requirements \
2836                  for help)"
2837             } else {
2838                 ""
2839             };
2840             Err(Error::new(
2841                 ErrorKind::ToolNotFound,
2842                 &format!("Failed to find tool. Is `{}` installed?{}", program, extra),
2843             ))
2844         }
2845         Err(_) => Err(Error::new(
2846             ErrorKind::ToolExecError,
2847             &format!("Command {:?} with args {:?} failed to start.", cmd, program),
2848         )),
2849     }
2850 }
2851 
fail(s: &str) -> !2852 fn fail(s: &str) -> ! {
2853     let _ = writeln!(io::stderr(), "\n\nerror occurred: {}\n\n", s);
2854     std::process::exit(1);
2855 }
2856 
command_add_output_file( cmd: &mut Command, dst: &Path, cuda: bool, msvc: bool, clang: bool, is_asm: bool, is_arm: bool, )2857 fn command_add_output_file(
2858     cmd: &mut Command,
2859     dst: &Path,
2860     cuda: bool,
2861     msvc: bool,
2862     clang: bool,
2863     is_asm: bool,
2864     is_arm: bool,
2865 ) {
2866     if msvc && !clang && !cuda && !(is_asm && is_arm) {
2867         let mut s = OsString::from("-Fo");
2868         s.push(&dst);
2869         cmd.arg(s);
2870     } else {
2871         cmd.arg("-o").arg(&dst);
2872     }
2873 }
2874 
2875 // Use by default minimum available API level
2876 // See note about naming here
2877 // https://android.googlesource.com/platform/ndk/+/refs/heads/ndk-release-r21/docs/BuildSystemMaintainers.md#Clang
2878 static NEW_STANDALONE_ANDROID_COMPILERS: [&str; 4] = [
2879     "aarch64-linux-android21-clang",
2880     "armv7a-linux-androideabi16-clang",
2881     "i686-linux-android16-clang",
2882     "x86_64-linux-android21-clang",
2883 ];
2884 
2885 // New "standalone" C/C++ cross-compiler executables from recent Android NDK
2886 // are just shell scripts that call main clang binary (from Android NDK) with
2887 // proper `--target` argument.
2888 //
2889 // For example, armv7a-linux-androideabi16-clang passes
2890 // `--target=armv7a-linux-androideabi16` to clang.
2891 // So to construct proper command line check if
2892 // `--target` argument would be passed or not to clang
android_clang_compiler_uses_target_arg_internally(clang_path: &Path) -> bool2893 fn android_clang_compiler_uses_target_arg_internally(clang_path: &Path) -> bool {
2894     if let Some(filename) = clang_path.file_name() {
2895         if let Some(filename_str) = filename.to_str() {
2896             filename_str.contains("android")
2897         } else {
2898             false
2899         }
2900     } else {
2901         false
2902     }
2903 }
2904 
2905 #[test]
test_android_clang_compiler_uses_target_arg_internally()2906 fn test_android_clang_compiler_uses_target_arg_internally() {
2907     for version in 16..21 {
2908         assert!(android_clang_compiler_uses_target_arg_internally(
2909             &PathBuf::from(format!("armv7a-linux-androideabi{}-clang", version))
2910         ));
2911         assert!(android_clang_compiler_uses_target_arg_internally(
2912             &PathBuf::from(format!("armv7a-linux-androideabi{}-clang++", version))
2913         ));
2914     }
2915     assert!(!android_clang_compiler_uses_target_arg_internally(
2916         &PathBuf::from("clang")
2917     ));
2918     assert!(!android_clang_compiler_uses_target_arg_internally(
2919         &PathBuf::from("clang++")
2920     ));
2921 }
2922 
autodetect_android_compiler(target: &str, host: &str, gnu: &str, clang: &str) -> String2923 fn autodetect_android_compiler(target: &str, host: &str, gnu: &str, clang: &str) -> String {
2924     let new_clang_key = match target {
2925         "aarch64-linux-android" => Some("aarch64"),
2926         "armv7-linux-androideabi" => Some("armv7a"),
2927         "i686-linux-android" => Some("i686"),
2928         "x86_64-linux-android" => Some("x86_64"),
2929         _ => None,
2930     };
2931 
2932     let new_clang = new_clang_key
2933         .map(|key| {
2934             NEW_STANDALONE_ANDROID_COMPILERS
2935                 .iter()
2936                 .find(|x| x.starts_with(key))
2937         })
2938         .unwrap_or(None);
2939 
2940     if let Some(new_clang) = new_clang {
2941         if Command::new(new_clang).output().is_ok() {
2942             return (*new_clang).into();
2943         }
2944     }
2945 
2946     let target = target
2947         .replace("armv7neon", "arm")
2948         .replace("armv7", "arm")
2949         .replace("thumbv7neon", "arm")
2950         .replace("thumbv7", "arm");
2951     let gnu_compiler = format!("{}-{}", target, gnu);
2952     let clang_compiler = format!("{}-{}", target, clang);
2953 
2954     // On Windows, the Android clang compiler is provided as a `.cmd` file instead
2955     // of a `.exe` file. `std::process::Command` won't run `.cmd` files unless the
2956     // `.cmd` is explicitly appended to the command name, so we do that here.
2957     let clang_compiler_cmd = format!("{}-{}.cmd", target, clang);
2958 
2959     // Check if gnu compiler is present
2960     // if not, use clang
2961     if Command::new(&gnu_compiler).output().is_ok() {
2962         gnu_compiler
2963     } else if host.contains("windows") && Command::new(&clang_compiler_cmd).output().is_ok() {
2964         clang_compiler_cmd
2965     } else {
2966         clang_compiler
2967     }
2968 }
2969 
2970 // Rust and clang/cc don't agree on how to name the target.
map_darwin_target_from_rust_to_compiler_architecture(target: &str) -> Option<&'static str>2971 fn map_darwin_target_from_rust_to_compiler_architecture(target: &str) -> Option<&'static str> {
2972     if target.contains("x86_64") {
2973         Some("x86_64")
2974     } else if target.contains("arm64e") {
2975         Some("arm64e")
2976     } else if target.contains("aarch64") {
2977         Some("arm64")
2978     } else {
2979         None
2980     }
2981 }
2982