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