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