]> git.proxmox.com Git - rustc.git/blob - vendor/cc/src/lib.rs
New upstream version 1.54.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 conjuction 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 {
1488 cmd.args.push(format!("--target={}", target).into());
1489 }
1490 }
1491 }
1492 ToolFamily::Msvc { clang_cl } => {
1493 // This is an undocumented flag from MSVC but helps with making
1494 // builds more reproducible by avoiding putting timestamps into
1495 // files.
1496 cmd.push_cc_arg("-Brepro".into());
1497
1498 if clang_cl {
1499 if target.contains("x86_64") {
1500 cmd.push_cc_arg("-m64".into());
1501 } else if target.contains("86") {
1502 cmd.push_cc_arg("-m32".into());
1503 cmd.push_cc_arg("-arch:IA32".into());
1504 } else {
1505 cmd.push_cc_arg(format!("--target={}", target).into());
1506 }
1507 } else {
1508 if target.contains("i586") {
1509 cmd.push_cc_arg("-arch:IA32".into());
1510 }
1511 }
1512
1513 // There is a check in corecrt.h that will generate a
1514 // compilation error if
1515 // _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE is
1516 // not defined to 1. The check was added in Windows
1517 // 8 days because only store apps were allowed on ARM.
1518 // This changed with the release of Windows 10 IoT Core.
1519 // The check will be going away in future versions of
1520 // the SDK, but for all released versions of the
1521 // Windows SDK it is required.
1522 if target.contains("arm") || target.contains("thumb") {
1523 cmd.args
1524 .push("-D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1".into());
1525 }
1526 }
1527 ToolFamily::Gnu => {
1528 if target.contains("i686") || target.contains("i586") {
1529 cmd.args.push("-m32".into());
1530 } else if target == "x86_64-unknown-linux-gnux32" {
1531 cmd.args.push("-mx32".into());
1532 } else if target.contains("x86_64") || target.contains("powerpc64") {
1533 cmd.args.push("-m64".into());
1534 }
1535
1536 if target.contains("darwin") {
1537 if let Some(arch) = map_darwin_target_from_rust_to_compiler_architecture(target)
1538 {
1539 cmd.args.push("-arch".into());
1540 cmd.args.push(arch.into());
1541 }
1542 }
1543
1544 if self.static_flag.is_none() {
1545 let features = self
1546 .getenv("CARGO_CFG_TARGET_FEATURE")
1547 .unwrap_or(String::new());
1548 if features.contains("crt-static") {
1549 cmd.args.push("-static".into());
1550 }
1551 }
1552
1553 // armv7 targets get to use armv7 instructions
1554 if (target.starts_with("armv7") || target.starts_with("thumbv7"))
1555 && target.contains("-linux-")
1556 {
1557 cmd.args.push("-march=armv7-a".into());
1558 }
1559
1560 // (x86 Android doesn't say "eabi")
1561 if target.contains("-androideabi") && target.contains("v7") {
1562 // -march=armv7-a handled above
1563 cmd.args.push("-mthumb".into());
1564 if !target.contains("neon") {
1565 // On android we can guarantee some extra float instructions
1566 // (specified in the android spec online)
1567 // NEON guarantees even more; see below.
1568 cmd.args.push("-mfpu=vfpv3-d16".into());
1569 }
1570 cmd.args.push("-mfloat-abi=softfp".into());
1571 }
1572
1573 if target.contains("neon") {
1574 cmd.args.push("-mfpu=neon-vfpv4".into());
1575 }
1576
1577 if target.starts_with("armv4t-unknown-linux-") {
1578 cmd.args.push("-march=armv4t".into());
1579 cmd.args.push("-marm".into());
1580 cmd.args.push("-mfloat-abi=soft".into());
1581 }
1582
1583 if target.starts_with("armv5te-unknown-linux-") {
1584 cmd.args.push("-march=armv5te".into());
1585 cmd.args.push("-marm".into());
1586 cmd.args.push("-mfloat-abi=soft".into());
1587 }
1588
1589 // For us arm == armv6 by default
1590 if target.starts_with("arm-unknown-linux-") {
1591 cmd.args.push("-march=armv6".into());
1592 cmd.args.push("-marm".into());
1593 if target.ends_with("hf") {
1594 cmd.args.push("-mfpu=vfp".into());
1595 } else {
1596 cmd.args.push("-mfloat-abi=soft".into());
1597 }
1598 }
1599
1600 // We can guarantee some settings for FRC
1601 if target.starts_with("arm-frc-") {
1602 cmd.args.push("-march=armv7-a".into());
1603 cmd.args.push("-mcpu=cortex-a9".into());
1604 cmd.args.push("-mfpu=vfpv3".into());
1605 cmd.args.push("-mfloat-abi=softfp".into());
1606 cmd.args.push("-marm".into());
1607 }
1608
1609 // Turn codegen down on i586 to avoid some instructions.
1610 if target.starts_with("i586-unknown-linux-") {
1611 cmd.args.push("-march=pentium".into());
1612 }
1613
1614 // Set codegen level for i686 correctly
1615 if target.starts_with("i686-unknown-linux-") {
1616 cmd.args.push("-march=i686".into());
1617 }
1618
1619 // Looks like `musl-gcc` makes it hard for `-m32` to make its way
1620 // all the way to the linker, so we need to actually instruct the
1621 // linker that we're generating 32-bit executables as well. This'll
1622 // typically only be used for build scripts which transitively use
1623 // these flags that try to compile executables.
1624 if target == "i686-unknown-linux-musl" || target == "i586-unknown-linux-musl" {
1625 cmd.args.push("-Wl,-melf_i386".into());
1626 }
1627
1628 if target.starts_with("thumb") {
1629 cmd.args.push("-mthumb".into());
1630
1631 if target.ends_with("eabihf") {
1632 cmd.args.push("-mfloat-abi=hard".into())
1633 }
1634 }
1635 if target.starts_with("thumbv6m") {
1636 cmd.args.push("-march=armv6s-m".into());
1637 }
1638 if target.starts_with("thumbv7em") {
1639 cmd.args.push("-march=armv7e-m".into());
1640
1641 if target.ends_with("eabihf") {
1642 cmd.args.push("-mfpu=fpv4-sp-d16".into())
1643 }
1644 }
1645 if target.starts_with("thumbv7m") {
1646 cmd.args.push("-march=armv7-m".into());
1647 }
1648 if target.starts_with("thumbv8m.base") {
1649 cmd.args.push("-march=armv8-m.base".into());
1650 }
1651 if target.starts_with("thumbv8m.main") {
1652 cmd.args.push("-march=armv8-m.main".into());
1653
1654 if target.ends_with("eabihf") {
1655 cmd.args.push("-mfpu=fpv5-sp-d16".into())
1656 }
1657 }
1658 if target.starts_with("armebv7r") | target.starts_with("armv7r") {
1659 if target.starts_with("armeb") {
1660 cmd.args.push("-mbig-endian".into());
1661 } else {
1662 cmd.args.push("-mlittle-endian".into());
1663 }
1664
1665 // ARM mode
1666 cmd.args.push("-marm".into());
1667
1668 // R Profile
1669 cmd.args.push("-march=armv7-r".into());
1670
1671 if target.ends_with("eabihf") {
1672 // Calling convention
1673 cmd.args.push("-mfloat-abi=hard".into());
1674
1675 // lowest common denominator FPU
1676 // (see Cortex-R4 technical reference manual)
1677 cmd.args.push("-mfpu=vfpv3-d16".into())
1678 } else {
1679 // Calling convention
1680 cmd.args.push("-mfloat-abi=soft".into());
1681 }
1682 }
1683 if target.starts_with("armv7a") {
1684 cmd.args.push("-march=armv7-a".into());
1685
1686 if target.ends_with("eabihf") {
1687 // lowest common denominator FPU
1688 cmd.args.push("-mfpu=vfpv3-d16".into());
1689 }
1690 }
1691 if target.starts_with("riscv32") || target.starts_with("riscv64") {
1692 // get the 32i/32imac/32imc/64gc/64imac/... part
1693 let mut parts = target.split('-');
1694 if let Some(arch) = parts.next() {
1695 let arch = &arch[5..];
1696 if target.contains("linux") && arch.starts_with("64") {
1697 cmd.args.push(("-march=rv64gc").into());
1698 cmd.args.push("-mabi=lp64d".into());
1699 } else if target.contains("linux") && arch.starts_with("32") {
1700 cmd.args.push(("-march=rv32gc").into());
1701 cmd.args.push("-mabi=ilp32d".into());
1702 } else if arch.starts_with("64") {
1703 cmd.args.push(("-march=rv".to_owned() + arch).into());
1704 cmd.args.push("-mabi=lp64".into());
1705 } else {
1706 cmd.args.push(("-march=rv".to_owned() + arch).into());
1707 cmd.args.push("-mabi=ilp32".into());
1708 }
1709 cmd.args.push("-mcmodel=medany".into());
1710 }
1711 }
1712 }
1713 }
1714
1715 if target.contains("apple-ios") {
1716 self.ios_flags(cmd)?;
1717 }
1718
1719 if self.static_flag.unwrap_or(false) {
1720 cmd.args.push("-static".into());
1721 }
1722 if self.shared_flag.unwrap_or(false) {
1723 cmd.args.push("-shared".into());
1724 }
1725
1726 if self.cpp {
1727 match (self.cpp_set_stdlib.as_ref(), cmd.family) {
1728 (None, _) => {}
1729 (Some(stdlib), ToolFamily::Gnu) | (Some(stdlib), ToolFamily::Clang) => {
1730 cmd.push_cc_arg(format!("-stdlib=lib{}", stdlib).into());
1731 }
1732 _ => {
1733 println!(
1734 "cargo:warning=cpp_set_stdlib is specified, but the {:?} compiler \
1735 does not support this option, ignored",
1736 cmd.family
1737 );
1738 }
1739 }
1740 }
1741
1742 Ok(())
1743 }
1744
1745 fn has_flags(&self) -> bool {
1746 let flags_env_var_name = if self.cpp { "CXXFLAGS" } else { "CFLAGS" };
1747 let flags_env_var_value = self.get_var(flags_env_var_name);
1748 if let Ok(_) = flags_env_var_value {
1749 true
1750 } else {
1751 false
1752 }
1753 }
1754
1755 fn msvc_macro_assembler(&self) -> Result<(Command, String), Error> {
1756 let target = self.get_target()?;
1757 let tool = if target.contains("x86_64") {
1758 "ml64.exe"
1759 } else if target.contains("arm") {
1760 "armasm.exe"
1761 } else if target.contains("aarch64") {
1762 "armasm64.exe"
1763 } else {
1764 "ml.exe"
1765 };
1766 let mut cmd = windows_registry::find(&target, tool).unwrap_or_else(|| self.cmd(tool));
1767 cmd.arg("-nologo"); // undocumented, yet working with armasm[64]
1768 for directory in self.include_directories.iter() {
1769 cmd.arg("-I").arg(directory);
1770 }
1771 if target.contains("aarch64") || target.contains("arm") {
1772 println!("cargo:warning=The MSVC ARM assemblers do not support -D flags");
1773 } else {
1774 for &(ref key, ref value) in self.definitions.iter() {
1775 if let Some(ref value) = *value {
1776 cmd.arg(&format!("-D{}={}", key, value));
1777 } else {
1778 cmd.arg(&format!("-D{}", key));
1779 }
1780 }
1781 }
1782
1783 if target.contains("i686") || target.contains("i586") {
1784 cmd.arg("-safeseh");
1785 }
1786 for flag in self.flags.iter() {
1787 cmd.arg(flag);
1788 }
1789
1790 Ok((cmd, tool.to_string()))
1791 }
1792
1793 fn assemble(&self, lib_name: &str, dst: &Path, objs: &[Object]) -> Result<(), Error> {
1794 // Delete the destination if it exists as we want to
1795 // create on the first iteration instead of appending.
1796 let _ = fs::remove_file(&dst);
1797
1798 // Add objects to the archive in limited-length batches. This helps keep
1799 // the length of the command line within a reasonable length to avoid
1800 // blowing system limits on limiting platforms like Windows.
1801 let objs: Vec<_> = objs
1802 .iter()
1803 .map(|o| o.dst.clone())
1804 .chain(self.objects.clone())
1805 .collect();
1806 for chunk in objs.chunks(100) {
1807 self.assemble_progressive(dst, chunk)?;
1808 }
1809
1810 let target = self.get_target()?;
1811 if target.contains("msvc") {
1812 // The Rust compiler will look for libfoo.a and foo.lib, but the
1813 // MSVC linker will also be passed foo.lib, so be sure that both
1814 // exist for now.
1815
1816 let lib_dst = dst.with_file_name(format!("{}.lib", lib_name));
1817 let _ = fs::remove_file(&lib_dst);
1818 match fs::hard_link(&dst, &lib_dst).or_else(|_| {
1819 // if hard-link fails, just copy (ignoring the number of bytes written)
1820 fs::copy(&dst, &lib_dst).map(|_| ())
1821 }) {
1822 Ok(_) => (),
1823 Err(_) => {
1824 return Err(Error::new(
1825 ErrorKind::IOError,
1826 "Could not copy or create a hard-link to the generated lib file.",
1827 ));
1828 }
1829 };
1830 } else {
1831 // Non-msvc targets (those using `ar`) need a separate step to add
1832 // the symbol table to archives since our construction command of
1833 // `cq` doesn't add it for us.
1834 let (mut ar, cmd) = self.get_ar()?;
1835 run(ar.arg("s").arg(dst), &cmd)?;
1836 }
1837
1838 Ok(())
1839 }
1840
1841 fn assemble_progressive(&self, dst: &Path, objs: &[PathBuf]) -> Result<(), Error> {
1842 let target = self.get_target()?;
1843
1844 if target.contains("msvc") {
1845 let (mut cmd, program) = self.get_ar()?;
1846 let mut out = OsString::from("-out:");
1847 out.push(dst);
1848 cmd.arg(out).arg("-nologo");
1849 for flag in self.ar_flags.iter() {
1850 cmd.arg(flag);
1851 }
1852 // If the library file already exists, add the libary name
1853 // as an argument to let lib.exe know we are appending the objs.
1854 if dst.exists() {
1855 cmd.arg(dst);
1856 }
1857 cmd.args(objs);
1858 run(&mut cmd, &program)?;
1859 } else {
1860 let (mut ar, cmd) = self.get_ar()?;
1861
1862 // Set an environment variable to tell the OSX archiver to ensure
1863 // that all dates listed in the archive are zero, improving
1864 // determinism of builds. AFAIK there's not really official
1865 // documentation of this but there's a lot of references to it if
1866 // you search google.
1867 //
1868 // You can reproduce this locally on a mac with:
1869 //
1870 // $ touch foo.c
1871 // $ cc -c foo.c -o foo.o
1872 //
1873 // # Notice that these two checksums are different
1874 // $ ar crus libfoo1.a foo.o && sleep 2 && ar crus libfoo2.a foo.o
1875 // $ md5sum libfoo*.a
1876 //
1877 // # Notice that these two checksums are the same
1878 // $ export ZERO_AR_DATE=1
1879 // $ ar crus libfoo1.a foo.o && sleep 2 && touch foo.o && ar crus libfoo2.a foo.o
1880 // $ md5sum libfoo*.a
1881 //
1882 // In any case if this doesn't end up getting read, it shouldn't
1883 // cause that many issues!
1884 ar.env("ZERO_AR_DATE", "1");
1885 for flag in self.ar_flags.iter() {
1886 ar.arg(flag);
1887 }
1888 run(ar.arg("cq").arg(dst).args(objs), &cmd)?;
1889 }
1890
1891 Ok(())
1892 }
1893
1894 fn ios_flags(&self, cmd: &mut Tool) -> Result<(), Error> {
1895 enum ArchSpec {
1896 Device(&'static str),
1897 Simulator(&'static str),
1898 Catalyst(&'static str),
1899 }
1900
1901 let target = self.get_target()?;
1902 let arch = target.split('-').nth(0).ok_or_else(|| {
1903 Error::new(
1904 ErrorKind::ArchitectureInvalid,
1905 "Unknown architecture for iOS target.",
1906 )
1907 })?;
1908
1909 let is_catalyst = match target.split('-').nth(3) {
1910 Some(v) => v == "macabi",
1911 None => false,
1912 };
1913
1914 let arch = if is_catalyst {
1915 match arch {
1916 "arm64e" => ArchSpec::Catalyst("arm64e"),
1917 "arm64" | "aarch64" => ArchSpec::Catalyst("arm64"),
1918 "x86_64" => ArchSpec::Catalyst("-m64"),
1919 _ => {
1920 return Err(Error::new(
1921 ErrorKind::ArchitectureInvalid,
1922 "Unknown architecture for iOS target.",
1923 ));
1924 }
1925 }
1926 } else {
1927 match arch {
1928 "arm" | "armv7" | "thumbv7" => ArchSpec::Device("armv7"),
1929 "armv7s" | "thumbv7s" => ArchSpec::Device("armv7s"),
1930 "arm64e" => ArchSpec::Device("arm64e"),
1931 "arm64" | "aarch64" => ArchSpec::Device("arm64"),
1932 "i386" | "i686" => ArchSpec::Simulator("-m32"),
1933 "x86_64" => ArchSpec::Simulator("-m64"),
1934 _ => {
1935 return Err(Error::new(
1936 ErrorKind::ArchitectureInvalid,
1937 "Unknown architecture for iOS target.",
1938 ));
1939 }
1940 }
1941 };
1942
1943 let min_version =
1944 std::env::var("IPHONEOS_DEPLOYMENT_TARGET").unwrap_or_else(|_| "7.0".into());
1945
1946 let sdk = match arch {
1947 ArchSpec::Device(arch) => {
1948 cmd.args.push("-arch".into());
1949 cmd.args.push(arch.into());
1950 cmd.args
1951 .push(format!("-miphoneos-version-min={}", min_version).into());
1952 "iphoneos"
1953 }
1954 ArchSpec::Simulator(arch) => {
1955 cmd.args.push(arch.into());
1956 cmd.args
1957 .push(format!("-mios-simulator-version-min={}", min_version).into());
1958 "iphonesimulator"
1959 }
1960 ArchSpec::Catalyst(_) => "macosx",
1961 };
1962
1963 self.print(&format!("Detecting iOS SDK path for {}", sdk));
1964 let sdk_path = self.apple_sdk_root(sdk)?;
1965 cmd.args.push("-isysroot".into());
1966 cmd.args.push(sdk_path);
1967 cmd.args.push("-fembed-bitcode".into());
1968 /*
1969 * TODO we probably ultimately want the -fembed-bitcode-marker flag
1970 * but can't have it now because of an issue in LLVM:
1971 * https://github.com/alexcrichton/cc-rs/issues/301
1972 * https://github.com/rust-lang/rust/pull/48896#comment-372192660
1973 */
1974 /*
1975 if self.get_opt_level()? == "0" {
1976 cmd.args.push("-fembed-bitcode-marker".into());
1977 }
1978 */
1979
1980 Ok(())
1981 }
1982
1983 fn cmd<P: AsRef<OsStr>>(&self, prog: P) -> Command {
1984 let mut cmd = Command::new(prog);
1985 for &(ref a, ref b) in self.env.iter() {
1986 cmd.env(a, b);
1987 }
1988 cmd
1989 }
1990
1991 fn get_base_compiler(&self) -> Result<Tool, Error> {
1992 if let Some(ref c) = self.compiler {
1993 return Ok(Tool::new(c.clone()));
1994 }
1995 let host = self.get_host()?;
1996 let target = self.get_target()?;
1997 let (env, msvc, gnu, traditional, clang) = if self.cpp {
1998 ("CXX", "cl.exe", "g++", "c++", "clang++")
1999 } else {
2000 ("CC", "cl.exe", "gcc", "cc", "clang")
2001 };
2002
2003 // On historical Solaris systems, "cc" may have been Sun Studio, which
2004 // is not flag-compatible with "gcc". This history casts a long shadow,
2005 // and many modern illumos distributions today ship GCC as "gcc" without
2006 // also making it available as "cc".
2007 let default = if host.contains("solaris") || host.contains("illumos") {
2008 gnu
2009 } else {
2010 traditional
2011 };
2012
2013 let cl_exe = windows_registry::find_tool(&target, "cl.exe");
2014
2015 let tool_opt: Option<Tool> = self
2016 .env_tool(env)
2017 .map(|(tool, wrapper, args)| {
2018 // find the driver mode, if any
2019 const DRIVER_MODE: &str = "--driver-mode=";
2020 let driver_mode = args
2021 .iter()
2022 .find(|a| a.starts_with(DRIVER_MODE))
2023 .map(|a| &a[DRIVER_MODE.len()..]);
2024 // Chop off leading/trailing whitespace to work around
2025 // semi-buggy build scripts which are shared in
2026 // makefiles/configure scripts (where spaces are far more
2027 // lenient)
2028 let mut t = Tool::with_clang_driver(PathBuf::from(tool.trim()), driver_mode);
2029 if let Some(cc_wrapper) = wrapper {
2030 t.cc_wrapper_path = Some(PathBuf::from(cc_wrapper));
2031 }
2032 for arg in args {
2033 t.cc_wrapper_args.push(arg.into());
2034 }
2035 t
2036 })
2037 .or_else(|| {
2038 if target.contains("emscripten") {
2039 let tool = if self.cpp { "em++" } else { "emcc" };
2040 // Windows uses bat file so we have to be a bit more specific
2041 if cfg!(windows) {
2042 let mut t = Tool::new(PathBuf::from("cmd"));
2043 t.args.push("/c".into());
2044 t.args.push(format!("{}.bat", tool).into());
2045 Some(t)
2046 } else {
2047 Some(Tool::new(PathBuf::from(tool)))
2048 }
2049 } else {
2050 None
2051 }
2052 })
2053 .or_else(|| cl_exe.clone());
2054
2055 let tool = match tool_opt {
2056 Some(t) => t,
2057 None => {
2058 let compiler = if host.contains("windows") && target.contains("windows") {
2059 if target.contains("msvc") {
2060 msvc.to_string()
2061 } else {
2062 format!("{}.exe", gnu)
2063 }
2064 } else if target.contains("apple-ios") {
2065 clang.to_string()
2066 } else if target.contains("android") {
2067 autodetect_android_compiler(&target, &host, gnu, clang)
2068 } else if target.contains("cloudabi") {
2069 format!("{}-{}", target, traditional)
2070 } else if target == "wasm32-wasi"
2071 || target == "wasm32-unknown-wasi"
2072 || target == "wasm32-unknown-unknown"
2073 {
2074 "clang".to_string()
2075 } else if target.contains("vxworks") {
2076 if self.cpp {
2077 "wr-c++".to_string()
2078 } else {
2079 "wr-cc".to_string()
2080 }
2081 } else if self.get_host()? != target {
2082 let prefix = self.prefix_for_target(&target);
2083 match prefix {
2084 Some(prefix) => format!("{}-{}", prefix, gnu),
2085 None => default.to_string(),
2086 }
2087 } else {
2088 default.to_string()
2089 };
2090
2091 let mut t = Tool::new(PathBuf::from(compiler));
2092 if let Some(cc_wrapper) = Self::rustc_wrapper_fallback() {
2093 t.cc_wrapper_path = Some(PathBuf::from(cc_wrapper));
2094 }
2095 t
2096 }
2097 };
2098
2099 let mut tool = if self.cuda {
2100 assert!(
2101 tool.args.is_empty(),
2102 "CUDA compilation currently assumes empty pre-existing args"
2103 );
2104 let nvcc = match self.get_var("NVCC") {
2105 Err(_) => "nvcc".into(),
2106 Ok(nvcc) => nvcc,
2107 };
2108 let mut nvcc_tool = Tool::with_features(PathBuf::from(nvcc), None, self.cuda);
2109 nvcc_tool
2110 .args
2111 .push(format!("-ccbin={}", tool.path.display()).into());
2112 nvcc_tool.family = tool.family;
2113 nvcc_tool
2114 } else {
2115 tool
2116 };
2117
2118 // New "standalone" C/C++ cross-compiler executables from recent Android NDK
2119 // are just shell scripts that call main clang binary (from Android NDK) with
2120 // proper `--target` argument.
2121 //
2122 // For example, armv7a-linux-androideabi16-clang passes
2123 // `--target=armv7a-linux-androideabi16` to clang.
2124 //
2125 // As the shell script calls the main clang binary, the command line limit length
2126 // on Windows is restricted to around 8k characters instead of around 32k characters.
2127 // To remove this limit, we call the main clang binary directly and contruct the
2128 // `--target=` ourselves.
2129 if host.contains("windows") && android_clang_compiler_uses_target_arg_internally(&tool.path)
2130 {
2131 if let Some(path) = tool.path.file_name() {
2132 let file_name = path.to_str().unwrap().to_owned();
2133 let (target, clang) = file_name.split_at(file_name.rfind("-").unwrap());
2134
2135 tool.path.set_file_name(clang.trim_start_matches("-"));
2136 tool.path.set_extension("exe");
2137 tool.args.push(format!("--target={}", target).into());
2138
2139 // Additionally, shell scripts for target i686-linux-android versions 16 to 24
2140 // pass the `mstackrealign` option so we do that here as well.
2141 if target.contains("i686-linux-android") {
2142 let (_, version) = target.split_at(target.rfind("d").unwrap() + 1);
2143 if let Ok(version) = version.parse::<u32>() {
2144 if version > 15 && version < 25 {
2145 tool.args.push("-mstackrealign".into());
2146 }
2147 }
2148 }
2149 };
2150 }
2151
2152 // If we found `cl.exe` in our environment, the tool we're returning is
2153 // an MSVC-like tool, *and* no env vars were set then set env vars for
2154 // the tool that we're returning.
2155 //
2156 // Env vars are needed for things like `link.exe` being put into PATH as
2157 // well as header include paths sometimes. These paths are automatically
2158 // included by default but if the `CC` or `CXX` env vars are set these
2159 // won't be used. This'll ensure that when the env vars are used to
2160 // configure for invocations like `clang-cl` we still get a "works out
2161 // of the box" experience.
2162 if let Some(cl_exe) = cl_exe {
2163 if tool.family == (ToolFamily::Msvc { clang_cl: true })
2164 && tool.env.len() == 0
2165 && target.contains("msvc")
2166 {
2167 for &(ref k, ref v) in cl_exe.env.iter() {
2168 tool.env.push((k.to_owned(), v.to_owned()));
2169 }
2170 }
2171 }
2172
2173 Ok(tool)
2174 }
2175
2176 fn get_var(&self, var_base: &str) -> Result<String, Error> {
2177 let target = self.get_target()?;
2178 let host = self.get_host()?;
2179 let kind = if host == target { "HOST" } else { "TARGET" };
2180 let target_u = target.replace("-", "_");
2181 let res = self
2182 .getenv(&format!("{}_{}", var_base, target))
2183 .or_else(|| self.getenv(&format!("{}_{}", var_base, target_u)))
2184 .or_else(|| self.getenv(&format!("{}_{}", kind, var_base)))
2185 .or_else(|| self.getenv(var_base));
2186
2187 match res {
2188 Some(res) => Ok(res),
2189 None => Err(Error::new(
2190 ErrorKind::EnvVarNotFound,
2191 &format!("Could not find environment variable {}.", var_base),
2192 )),
2193 }
2194 }
2195
2196 fn envflags(&self, name: &str) -> Vec<String> {
2197 self.get_var(name)
2198 .unwrap_or(String::new())
2199 .split_ascii_whitespace()
2200 .map(|slice| slice.to_string())
2201 .collect()
2202 }
2203
2204 /// Returns a fallback `cc_compiler_wrapper` by introspecting `RUSTC_WRAPPER`
2205 fn rustc_wrapper_fallback() -> Option<String> {
2206 // No explicit CC wrapper was detected, but check if RUSTC_WRAPPER
2207 // is defined and is a build accelerator that is compatible with
2208 // C/C++ compilers (e.g. sccache)
2209 let valid_wrappers = ["sccache"];
2210
2211 let rustc_wrapper = std::env::var_os("RUSTC_WRAPPER")?;
2212 let wrapper_path = Path::new(&rustc_wrapper);
2213 let wrapper_stem = wrapper_path.file_stem()?;
2214
2215 if valid_wrappers.contains(&wrapper_stem.to_str()?) {
2216 Some(rustc_wrapper.to_str()?.to_owned())
2217 } else {
2218 None
2219 }
2220 }
2221
2222 /// Returns compiler path, optional modifier name from whitelist, and arguments vec
2223 fn env_tool(&self, name: &str) -> Option<(String, Option<String>, Vec<String>)> {
2224 let tool = match self.get_var(name) {
2225 Ok(tool) => tool,
2226 Err(_) => return None,
2227 };
2228
2229 // If this is an exact path on the filesystem we don't want to do any
2230 // interpretation at all, just pass it on through. This'll hopefully get
2231 // us to support spaces-in-paths.
2232 if Path::new(&tool).exists() {
2233 return Some((tool, None, Vec::new()));
2234 }
2235
2236 // Ok now we want to handle a couple of scenarios. We'll assume from
2237 // here on out that spaces are splitting separate arguments. Two major
2238 // features we want to support are:
2239 //
2240 // CC='sccache cc'
2241 //
2242 // aka using `sccache` or any other wrapper/caching-like-thing for
2243 // compilations. We want to know what the actual compiler is still,
2244 // though, because our `Tool` API support introspection of it to see
2245 // what compiler is in use.
2246 //
2247 // additionally we want to support
2248 //
2249 // CC='cc -flag'
2250 //
2251 // where the CC env var is used to also pass default flags to the C
2252 // compiler.
2253 //
2254 // It's true that everything here is a bit of a pain, but apparently if
2255 // you're not literally make or bash then you get a lot of bug reports.
2256 let known_wrappers = ["ccache", "distcc", "sccache", "icecc"];
2257
2258 let mut parts = tool.split_whitespace();
2259 let maybe_wrapper = match parts.next() {
2260 Some(s) => s,
2261 None => return None,
2262 };
2263
2264 let file_stem = Path::new(maybe_wrapper)
2265 .file_stem()
2266 .unwrap()
2267 .to_str()
2268 .unwrap();
2269 if known_wrappers.contains(&file_stem) {
2270 if let Some(compiler) = parts.next() {
2271 return Some((
2272 compiler.to_string(),
2273 Some(maybe_wrapper.to_string()),
2274 parts.map(|s| s.to_string()).collect(),
2275 ));
2276 }
2277 }
2278
2279 Some((
2280 maybe_wrapper.to_string(),
2281 Self::rustc_wrapper_fallback(),
2282 parts.map(|s| s.to_string()).collect(),
2283 ))
2284 }
2285
2286 /// Returns the C++ standard library:
2287 /// 1. If [cpp_link_stdlib](cc::Build::cpp_link_stdlib) is set, uses its value.
2288 /// 2. Else if the `CXXSTDLIB` environment variable is set, uses its value.
2289 /// 3. Else the default is `libc++` for OS X and BSDs, `libc++_shared` for Android,
2290 /// `None` for MSVC and `libstdc++` for anything else.
2291 fn get_cpp_link_stdlib(&self) -> Result<Option<String>, Error> {
2292 match self.cpp_link_stdlib.clone() {
2293 Some(s) => Ok(s),
2294 None => {
2295 if let Ok(stdlib) = self.get_var("CXXSTDLIB") {
2296 if stdlib.is_empty() {
2297 Ok(None)
2298 } else {
2299 Ok(Some(stdlib))
2300 }
2301 } else {
2302 let target = self.get_target()?;
2303 if target.contains("msvc") {
2304 Ok(None)
2305 } else if target.contains("apple") {
2306 Ok(Some("c++".to_string()))
2307 } else if target.contains("freebsd") {
2308 Ok(Some("c++".to_string()))
2309 } else if target.contains("openbsd") {
2310 Ok(Some("c++".to_string()))
2311 } else if target.contains("android") {
2312 Ok(Some("c++_shared".to_string()))
2313 } else {
2314 Ok(Some("stdc++".to_string()))
2315 }
2316 }
2317 }
2318 }
2319 }
2320
2321 fn get_ar(&self) -> Result<(Command, String), Error> {
2322 if let Some(ref p) = self.archiver {
2323 let name = p.file_name().and_then(|s| s.to_str()).unwrap_or("ar");
2324 return Ok((self.cmd(p), name.to_string()));
2325 }
2326 if let Ok(p) = self.get_var("AR") {
2327 return Ok((self.cmd(&p), p));
2328 }
2329 let target = self.get_target()?;
2330 let default_ar = "ar".to_string();
2331 let program = if target.contains("android") {
2332 format!("{}-ar", target.replace("armv7", "arm"))
2333 } else if target.contains("emscripten") {
2334 // Windows use bat files so we have to be a bit more specific
2335 if cfg!(windows) {
2336 let mut cmd = self.cmd("cmd");
2337 cmd.arg("/c").arg("emar.bat");
2338 return Ok((cmd, "emar.bat".to_string()));
2339 }
2340
2341 "emar".to_string()
2342 } else if target.contains("msvc") {
2343 match windows_registry::find(&target, "lib.exe") {
2344 Some(t) => return Ok((t, "lib.exe".to_string())),
2345 None => "lib.exe".to_string(),
2346 }
2347 } else if target.contains("illumos") {
2348 // The default 'ar' on illumos uses a non-standard flags,
2349 // but the OS comes bundled with a GNU-compatible variant.
2350 //
2351 // Use the GNU-variant to match other Unix systems.
2352 "gar".to_string()
2353 } else if self.get_host()? != target {
2354 match self.prefix_for_target(&target) {
2355 Some(p) => {
2356 let target_ar = format!("{}-ar", p);
2357 if Command::new(&target_ar).output().is_ok() {
2358 target_ar
2359 } else {
2360 default_ar
2361 }
2362 }
2363 None => default_ar,
2364 }
2365 } else {
2366 default_ar
2367 };
2368 Ok((self.cmd(&program), program))
2369 }
2370
2371 fn prefix_for_target(&self, target: &str) -> Option<String> {
2372 // CROSS_COMPILE is of the form: "arm-linux-gnueabi-"
2373 let cc_env = self.getenv("CROSS_COMPILE");
2374 let cross_compile = cc_env
2375 .as_ref()
2376 .map(|s| s.trim_right_matches('-').to_owned());
2377 cross_compile.or(match &target[..] {
2378 "aarch64-unknown-linux-gnu" => Some("aarch64-linux-gnu"),
2379 "aarch64-unknown-linux-musl" => Some("aarch64-linux-musl"),
2380 "aarch64-unknown-netbsd" => Some("aarch64--netbsd"),
2381 "arm-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"),
2382 "armv4t-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"),
2383 "armv5te-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"),
2384 "armv5te-unknown-linux-musleabi" => Some("arm-linux-gnueabi"),
2385 "arm-frc-linux-gnueabi" => Some("arm-frc-linux-gnueabi"),
2386 "arm-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"),
2387 "arm-unknown-linux-musleabi" => Some("arm-linux-musleabi"),
2388 "arm-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"),
2389 "arm-unknown-netbsd-eabi" => Some("arm--netbsdelf-eabi"),
2390 "armv6-unknown-netbsd-eabihf" => Some("armv6--netbsdelf-eabihf"),
2391 "armv7-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"),
2392 "armv7-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"),
2393 "armv7-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"),
2394 "armv7neon-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"),
2395 "armv7neon-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"),
2396 "thumbv7-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"),
2397 "thumbv7-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"),
2398 "thumbv7neon-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"),
2399 "thumbv7neon-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"),
2400 "armv7-unknown-netbsd-eabihf" => Some("armv7--netbsdelf-eabihf"),
2401 "hexagon-unknown-linux-musl" => Some("hexagon-linux-musl"),
2402 "i586-unknown-linux-musl" => Some("musl"),
2403 "i686-pc-windows-gnu" => Some("i686-w64-mingw32"),
2404 "i686-uwp-windows-gnu" => Some("i686-w64-mingw32"),
2405 "i686-unknown-linux-musl" => Some("musl"),
2406 "i686-unknown-netbsd" => Some("i486--netbsdelf"),
2407 "mips-unknown-linux-gnu" => Some("mips-linux-gnu"),
2408 "mips-unknown-linux-musl" => Some("mips-linux-musl"),
2409 "mipsel-unknown-linux-gnu" => Some("mipsel-linux-gnu"),
2410 "mipsel-unknown-linux-musl" => Some("mipsel-linux-musl"),
2411 "mips64-unknown-linux-gnuabi64" => Some("mips64-linux-gnuabi64"),
2412 "mips64el-unknown-linux-gnuabi64" => Some("mips64el-linux-gnuabi64"),
2413 "mipsisa32r6-unknown-linux-gnu" => Some("mipsisa32r6-linux-gnu"),
2414 "mipsisa32r6el-unknown-linux-gnu" => Some("mipsisa32r6el-linux-gnu"),
2415 "mipsisa64r6-unknown-linux-gnuabi64" => Some("mipsisa64r6-linux-gnuabi64"),
2416 "mipsisa64r6el-unknown-linux-gnuabi64" => Some("mipsisa64r6el-linux-gnuabi64"),
2417 "powerpc-unknown-linux-gnu" => Some("powerpc-linux-gnu"),
2418 "powerpc-unknown-linux-gnuspe" => Some("powerpc-linux-gnuspe"),
2419 "powerpc-unknown-netbsd" => Some("powerpc--netbsd"),
2420 "powerpc64-unknown-linux-gnu" => Some("powerpc-linux-gnu"),
2421 "powerpc64le-unknown-linux-gnu" => Some("powerpc64le-linux-gnu"),
2422 "riscv32i-unknown-none-elf" => self.find_working_gnu_prefix(&[
2423 "riscv32-unknown-elf",
2424 "riscv64-unknown-elf",
2425 "riscv-none-embed",
2426 ]),
2427 "riscv32imac-unknown-none-elf" => self.find_working_gnu_prefix(&[
2428 "riscv32-unknown-elf",
2429 "riscv64-unknown-elf",
2430 "riscv-none-embed",
2431 ]),
2432 "riscv32imc-unknown-none-elf" => self.find_working_gnu_prefix(&[
2433 "riscv32-unknown-elf",
2434 "riscv64-unknown-elf",
2435 "riscv-none-embed",
2436 ]),
2437 "riscv64gc-unknown-none-elf" => self.find_working_gnu_prefix(&[
2438 "riscv64-unknown-elf",
2439 "riscv32-unknown-elf",
2440 "riscv-none-embed",
2441 ]),
2442 "riscv64imac-unknown-none-elf" => self.find_working_gnu_prefix(&[
2443 "riscv64-unknown-elf",
2444 "riscv32-unknown-elf",
2445 "riscv-none-embed",
2446 ]),
2447 "riscv64gc-unknown-linux-gnu" => Some("riscv64-linux-gnu"),
2448 "riscv32gc-unknown-linux-gnu" => Some("riscv32-linux-gnu"),
2449 "riscv64gc-unknown-linux-musl" => Some("riscv64-linux-musl"),
2450 "riscv32gc-unknown-linux-musl" => Some("riscv32-linux-musl"),
2451 "s390x-unknown-linux-gnu" => Some("s390x-linux-gnu"),
2452 "sparc-unknown-linux-gnu" => Some("sparc-linux-gnu"),
2453 "sparc64-unknown-linux-gnu" => Some("sparc64-linux-gnu"),
2454 "sparc64-unknown-netbsd" => Some("sparc64--netbsd"),
2455 "sparcv9-sun-solaris" => Some("sparcv9-sun-solaris"),
2456 "armv7a-none-eabi" => Some("arm-none-eabi"),
2457 "armv7a-none-eabihf" => Some("arm-none-eabi"),
2458 "armebv7r-none-eabi" => Some("arm-none-eabi"),
2459 "armebv7r-none-eabihf" => Some("arm-none-eabi"),
2460 "armv7r-none-eabi" => Some("arm-none-eabi"),
2461 "armv7r-none-eabihf" => Some("arm-none-eabi"),
2462 "thumbv6m-none-eabi" => Some("arm-none-eabi"),
2463 "thumbv7em-none-eabi" => Some("arm-none-eabi"),
2464 "thumbv7em-none-eabihf" => Some("arm-none-eabi"),
2465 "thumbv7m-none-eabi" => Some("arm-none-eabi"),
2466 "thumbv8m.base-none-eabi" => Some("arm-none-eabi"),
2467 "thumbv8m.main-none-eabi" => Some("arm-none-eabi"),
2468 "thumbv8m.main-none-eabihf" => Some("arm-none-eabi"),
2469 "x86_64-pc-windows-gnu" => Some("x86_64-w64-mingw32"),
2470 "x86_64-uwp-windows-gnu" => Some("x86_64-w64-mingw32"),
2471 "x86_64-rumprun-netbsd" => Some("x86_64-rumprun-netbsd"),
2472 "x86_64-unknown-linux-musl" => Some("musl"),
2473 "x86_64-unknown-netbsd" => Some("x86_64--netbsd"),
2474 _ => None,
2475 }
2476 .map(|x| x.to_owned()))
2477 }
2478
2479 /// Some platforms have multiple, compatible, canonical prefixes. Look through
2480 /// each possible prefix for a compiler that exists and return it. The prefixes
2481 /// should be ordered from most-likely to least-likely.
2482 fn find_working_gnu_prefix(&self, prefixes: &[&'static str]) -> Option<&'static str> {
2483 let suffix = if self.cpp { "-g++" } else { "-gcc" };
2484 let extension = std::env::consts::EXE_SUFFIX;
2485
2486 // Loop through PATH entries searching for each toolchain. This ensures that we
2487 // are more likely to discover the toolchain early on, because chances are good
2488 // that the desired toolchain is in one of the higher-priority paths.
2489 env::var_os("PATH")
2490 .as_ref()
2491 .and_then(|path_entries| {
2492 env::split_paths(path_entries).find_map(|path_entry| {
2493 for prefix in prefixes {
2494 let target_compiler = format!("{}{}{}", prefix, suffix, extension);
2495 if path_entry.join(&target_compiler).exists() {
2496 return Some(prefix);
2497 }
2498 }
2499 None
2500 })
2501 })
2502 .map(|prefix| *prefix)
2503 .or_else(||
2504 // If no toolchain was found, provide the first toolchain that was passed in.
2505 // This toolchain has been shown not to exist, however it will appear in the
2506 // error that is shown to the user which should make it easier to search for
2507 // where it should be obtained.
2508 prefixes.first().map(|prefix| *prefix))
2509 }
2510
2511 fn get_target(&self) -> Result<String, Error> {
2512 match self.target.clone() {
2513 Some(t) => Ok(t),
2514 None => Ok(self.getenv_unwrap("TARGET")?),
2515 }
2516 }
2517
2518 fn get_host(&self) -> Result<String, Error> {
2519 match self.host.clone() {
2520 Some(h) => Ok(h),
2521 None => Ok(self.getenv_unwrap("HOST")?),
2522 }
2523 }
2524
2525 fn get_opt_level(&self) -> Result<String, Error> {
2526 match self.opt_level.as_ref().cloned() {
2527 Some(ol) => Ok(ol),
2528 None => Ok(self.getenv_unwrap("OPT_LEVEL")?),
2529 }
2530 }
2531
2532 fn get_debug(&self) -> bool {
2533 self.debug.unwrap_or_else(|| match self.getenv("DEBUG") {
2534 Some(s) => s != "false",
2535 None => false,
2536 })
2537 }
2538
2539 fn get_force_frame_pointer(&self) -> bool {
2540 self.force_frame_pointer.unwrap_or_else(|| self.get_debug())
2541 }
2542
2543 fn get_out_dir(&self) -> Result<PathBuf, Error> {
2544 match self.out_dir.clone() {
2545 Some(p) => Ok(p),
2546 None => Ok(env::var_os("OUT_DIR").map(PathBuf::from).ok_or_else(|| {
2547 Error::new(
2548 ErrorKind::EnvVarNotFound,
2549 "Environment variable OUT_DIR not defined.",
2550 )
2551 })?),
2552 }
2553 }
2554
2555 fn getenv(&self, v: &str) -> Option<String> {
2556 let mut cache = self.env_cache.lock().unwrap();
2557 if let Some(val) = cache.get(v) {
2558 return val.clone();
2559 }
2560 let r = env::var(v).ok();
2561 self.print(&format!("{} = {:?}", v, r));
2562 cache.insert(v.to_string(), r.clone());
2563 r
2564 }
2565
2566 fn getenv_unwrap(&self, v: &str) -> Result<String, Error> {
2567 match self.getenv(v) {
2568 Some(s) => Ok(s),
2569 None => Err(Error::new(
2570 ErrorKind::EnvVarNotFound,
2571 &format!("Environment variable {} not defined.", v.to_string()),
2572 )),
2573 }
2574 }
2575
2576 fn print(&self, s: &str) {
2577 if self.cargo_metadata {
2578 println!("{}", s);
2579 }
2580 }
2581
2582 fn fix_env_for_apple_os(&self, cmd: &mut Command) -> Result<(), Error> {
2583 let target = self.get_target()?;
2584 let host = self.get_host()?;
2585 if host.contains("apple-darwin") && target.contains("apple-darwin") {
2586 // If, for example, `cargo` runs during the build of an XCode project, then `SDKROOT` environment variable
2587 // would represent the current target, and this is the problem for us, if we want to compile something
2588 // for the host, when host != target.
2589 // We can not just remove `SDKROOT`, because, again, for example, XCode add to PATH
2590 // /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
2591 // and `cc` from this path can not find system include files, like `pthread.h`, if `SDKROOT`
2592 // is not set
2593 if let Ok(sdkroot) = env::var("SDKROOT") {
2594 if !sdkroot.contains("MacOSX") {
2595 let macos_sdk = self.apple_sdk_root("macosx")?;
2596 cmd.env("SDKROOT", macos_sdk);
2597 }
2598 }
2599 // Additionally, `IPHONEOS_DEPLOYMENT_TARGET` must not be set when using the Xcode linker at
2600 // "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld",
2601 // although this is apparently ignored when using the linker at "/usr/bin/ld".
2602 cmd.env_remove("IPHONEOS_DEPLOYMENT_TARGET");
2603 }
2604 Ok(())
2605 }
2606
2607 fn apple_sdk_root(&self, sdk: &str) -> Result<OsString, Error> {
2608 let mut cache = self
2609 .apple_sdk_root_cache
2610 .lock()
2611 .expect("apple_sdk_root_cache lock failed");
2612 if let Some(ret) = cache.get(sdk) {
2613 return Ok(ret.clone());
2614 }
2615
2616 let sdk_path = run_output(
2617 self.cmd("xcrun")
2618 .arg("--show-sdk-path")
2619 .arg("--sdk")
2620 .arg(sdk),
2621 "xcrun",
2622 )?;
2623
2624 let sdk_path = match String::from_utf8(sdk_path) {
2625 Ok(p) => p,
2626 Err(_) => {
2627 return Err(Error::new(
2628 ErrorKind::IOError,
2629 "Unable to determine iOS SDK path.",
2630 ));
2631 }
2632 };
2633 let ret: OsString = sdk_path.trim().into();
2634 cache.insert(sdk.into(), ret.clone());
2635 Ok(ret)
2636 }
2637 }
2638
2639 impl Default for Build {
2640 fn default() -> Build {
2641 Build::new()
2642 }
2643 }
2644
2645 impl Tool {
2646 fn new(path: PathBuf) -> Self {
2647 Tool::with_features(path, None, false)
2648 }
2649
2650 fn with_clang_driver(path: PathBuf, clang_driver: Option<&str>) -> Self {
2651 Self::with_features(path, clang_driver, false)
2652 }
2653
2654 #[cfg(windows)]
2655 /// Explictly set the `ToolFamily`, skipping name-based detection.
2656 fn with_family(path: PathBuf, family: ToolFamily) -> Self {
2657 Self {
2658 path: path,
2659 cc_wrapper_path: None,
2660 cc_wrapper_args: Vec::new(),
2661 args: Vec::new(),
2662 env: Vec::new(),
2663 family: family,
2664 cuda: false,
2665 removed_args: Vec::new(),
2666 }
2667 }
2668
2669 fn with_features(path: PathBuf, clang_driver: Option<&str>, cuda: bool) -> Self {
2670 // Try to detect family of the tool from its name, falling back to Gnu.
2671 let family = if let Some(fname) = path.file_name().and_then(|p| p.to_str()) {
2672 if fname.contains("clang-cl") {
2673 ToolFamily::Msvc { clang_cl: true }
2674 } else if fname.ends_with("cl") || fname == "cl.exe" {
2675 ToolFamily::Msvc { clang_cl: false }
2676 } else if fname.contains("clang") {
2677 match clang_driver {
2678 Some("cl") => ToolFamily::Msvc { clang_cl: true },
2679 _ => ToolFamily::Clang,
2680 }
2681 } else {
2682 ToolFamily::Gnu
2683 }
2684 } else {
2685 ToolFamily::Gnu
2686 };
2687
2688 Tool {
2689 path: path,
2690 cc_wrapper_path: None,
2691 cc_wrapper_args: Vec::new(),
2692 args: Vec::new(),
2693 env: Vec::new(),
2694 family: family,
2695 cuda: cuda,
2696 removed_args: Vec::new(),
2697 }
2698 }
2699
2700 /// Add an argument to be stripped from the final command arguments.
2701 fn remove_arg(&mut self, flag: OsString) {
2702 self.removed_args.push(flag);
2703 }
2704
2705 /// Add a flag, and optionally prepend the NVCC wrapper flag "-Xcompiler".
2706 ///
2707 /// Currently this is only used for compiling CUDA sources, since NVCC only
2708 /// accepts a limited set of GNU-like flags, and the rest must be prefixed
2709 /// with a "-Xcompiler" flag to get passed to the underlying C++ compiler.
2710 fn push_cc_arg(&mut self, flag: OsString) {
2711 if self.cuda {
2712 self.args.push("-Xcompiler".into());
2713 }
2714 self.args.push(flag);
2715 }
2716
2717 fn is_duplicate_opt_arg(&self, flag: &OsString) -> bool {
2718 let flag = flag.to_str().unwrap();
2719 let mut chars = flag.chars();
2720
2721 // Only duplicate check compiler flags
2722 if self.is_like_msvc() {
2723 if chars.next() != Some('/') {
2724 return false;
2725 }
2726 } else if self.is_like_gnu() || self.is_like_clang() {
2727 if chars.next() != Some('-') {
2728 return false;
2729 }
2730 }
2731
2732 // Check for existing optimization flags (-O, /O)
2733 if chars.next() == Some('O') {
2734 return self
2735 .args()
2736 .iter()
2737 .any(|ref a| a.to_str().unwrap_or("").chars().nth(1) == Some('O'));
2738 }
2739
2740 // TODO Check for existing -m..., -m...=..., /arch:... flags
2741 return false;
2742 }
2743
2744 /// Don't push optimization arg if it conflicts with existing args
2745 fn push_opt_unless_duplicate(&mut self, flag: OsString) {
2746 if self.is_duplicate_opt_arg(&flag) {
2747 println!("Info: Ignoring duplicate arg {:?}", &flag);
2748 } else {
2749 self.push_cc_arg(flag);
2750 }
2751 }
2752
2753 /// Converts this compiler into a `Command` that's ready to be run.
2754 ///
2755 /// This is useful for when the compiler needs to be executed and the
2756 /// command returned will already have the initial arguments and environment
2757 /// variables configured.
2758 pub fn to_command(&self) -> Command {
2759 let mut cmd = match self.cc_wrapper_path {
2760 Some(ref cc_wrapper_path) => {
2761 let mut cmd = Command::new(&cc_wrapper_path);
2762 cmd.arg(&self.path);
2763 cmd
2764 }
2765 None => Command::new(&self.path),
2766 };
2767 cmd.args(&self.cc_wrapper_args);
2768
2769 let value = self
2770 .args
2771 .iter()
2772 .filter(|a| !self.removed_args.contains(a))
2773 .collect::<Vec<_>>();
2774 cmd.args(&value);
2775
2776 for &(ref k, ref v) in self.env.iter() {
2777 cmd.env(k, v);
2778 }
2779 cmd
2780 }
2781
2782 /// Returns the path for this compiler.
2783 ///
2784 /// Note that this may not be a path to a file on the filesystem, e.g. "cc",
2785 /// but rather something which will be resolved when a process is spawned.
2786 pub fn path(&self) -> &Path {
2787 &self.path
2788 }
2789
2790 /// Returns the default set of arguments to the compiler needed to produce
2791 /// executables for the target this compiler generates.
2792 pub fn args(&self) -> &[OsString] {
2793 &self.args
2794 }
2795
2796 /// Returns the set of environment variables needed for this compiler to
2797 /// operate.
2798 ///
2799 /// This is typically only used for MSVC compilers currently.
2800 pub fn env(&self) -> &[(OsString, OsString)] {
2801 &self.env
2802 }
2803
2804 /// Returns the compiler command in format of CC environment variable.
2805 /// Or empty string if CC env was not present
2806 ///
2807 /// This is typically used by configure script
2808 pub fn cc_env(&self) -> OsString {
2809 match self.cc_wrapper_path {
2810 Some(ref cc_wrapper_path) => {
2811 let mut cc_env = cc_wrapper_path.as_os_str().to_owned();
2812 cc_env.push(" ");
2813 cc_env.push(self.path.to_path_buf().into_os_string());
2814 for arg in self.cc_wrapper_args.iter() {
2815 cc_env.push(" ");
2816 cc_env.push(arg);
2817 }
2818 cc_env
2819 }
2820 None => OsString::from(""),
2821 }
2822 }
2823
2824 /// Returns the compiler flags in format of CFLAGS environment variable.
2825 /// Important here - this will not be CFLAGS from env, its internal gcc's flags to use as CFLAGS
2826 /// This is typically used by configure script
2827 pub fn cflags_env(&self) -> OsString {
2828 let mut flags = OsString::new();
2829 for (i, arg) in self.args.iter().enumerate() {
2830 if i > 0 {
2831 flags.push(" ");
2832 }
2833 flags.push(arg);
2834 }
2835 flags
2836 }
2837
2838 /// Whether the tool is GNU Compiler Collection-like.
2839 pub fn is_like_gnu(&self) -> bool {
2840 self.family == ToolFamily::Gnu
2841 }
2842
2843 /// Whether the tool is Clang-like.
2844 pub fn is_like_clang(&self) -> bool {
2845 self.family == ToolFamily::Clang
2846 }
2847
2848 /// Whether the tool is MSVC-like.
2849 pub fn is_like_msvc(&self) -> bool {
2850 match self.family {
2851 ToolFamily::Msvc { .. } => true,
2852 _ => false,
2853 }
2854 }
2855 }
2856
2857 fn run(cmd: &mut Command, program: &str) -> Result<(), Error> {
2858 let (mut child, print) = spawn(cmd, program)?;
2859 let status = match child.wait() {
2860 Ok(s) => s,
2861 Err(_) => {
2862 return Err(Error::new(
2863 ErrorKind::ToolExecError,
2864 &format!(
2865 "Failed to wait on spawned child process, command {:?} with args {:?}.",
2866 cmd, program
2867 ),
2868 ));
2869 }
2870 };
2871 print.join().unwrap();
2872 println!("{}", status);
2873
2874 if status.success() {
2875 Ok(())
2876 } else {
2877 Err(Error::new(
2878 ErrorKind::ToolExecError,
2879 &format!(
2880 "Command {:?} with args {:?} did not execute successfully (status code {}).",
2881 cmd, program, status
2882 ),
2883 ))
2884 }
2885 }
2886
2887 fn run_output(cmd: &mut Command, program: &str) -> Result<Vec<u8>, Error> {
2888 cmd.stdout(Stdio::piped());
2889 let (mut child, print) = spawn(cmd, program)?;
2890 let mut stdout = vec![];
2891 child
2892 .stdout
2893 .take()
2894 .unwrap()
2895 .read_to_end(&mut stdout)
2896 .unwrap();
2897 let status = match child.wait() {
2898 Ok(s) => s,
2899 Err(_) => {
2900 return Err(Error::new(
2901 ErrorKind::ToolExecError,
2902 &format!(
2903 "Failed to wait on spawned child process, command {:?} with args {:?}.",
2904 cmd, program
2905 ),
2906 ));
2907 }
2908 };
2909 print.join().unwrap();
2910 println!("{}", status);
2911
2912 if status.success() {
2913 Ok(stdout)
2914 } else {
2915 Err(Error::new(
2916 ErrorKind::ToolExecError,
2917 &format!(
2918 "Command {:?} with args {:?} did not execute successfully (status code {}).",
2919 cmd, program, status
2920 ),
2921 ))
2922 }
2923 }
2924
2925 fn spawn(cmd: &mut Command, program: &str) -> Result<(Child, JoinHandle<()>), Error> {
2926 println!("running: {:?}", cmd);
2927
2928 // Capture the standard error coming from these programs, and write it out
2929 // with cargo:warning= prefixes. Note that this is a bit wonky to avoid
2930 // requiring the output to be UTF-8, we instead just ship bytes from one
2931 // location to another.
2932 match cmd.stderr(Stdio::piped()).spawn() {
2933 Ok(mut child) => {
2934 let stderr = BufReader::new(child.stderr.take().unwrap());
2935 let print = thread::spawn(move || {
2936 for line in stderr.split(b'\n').filter_map(|l| l.ok()) {
2937 print!("cargo:warning=");
2938 std::io::stdout().write_all(&line).unwrap();
2939 println!("");
2940 }
2941 });
2942 Ok((child, print))
2943 }
2944 Err(ref e) if e.kind() == io::ErrorKind::NotFound => {
2945 let extra = if cfg!(windows) {
2946 " (see https://github.com/alexcrichton/cc-rs#compile-time-requirements \
2947 for help)"
2948 } else {
2949 ""
2950 };
2951 Err(Error::new(
2952 ErrorKind::ToolNotFound,
2953 &format!("Failed to find tool. Is `{}` installed?{}", program, extra),
2954 ))
2955 }
2956 Err(_) => Err(Error::new(
2957 ErrorKind::ToolExecError,
2958 &format!("Command {:?} with args {:?} failed to start.", cmd, program),
2959 )),
2960 }
2961 }
2962
2963 fn fail(s: &str) -> ! {
2964 eprintln!("\n\nerror occurred: {}\n\n", s);
2965 std::process::exit(1);
2966 }
2967
2968 fn command_add_output_file(
2969 cmd: &mut Command,
2970 dst: &Path,
2971 cuda: bool,
2972 msvc: bool,
2973 clang: bool,
2974 is_asm: bool,
2975 is_arm: bool,
2976 ) {
2977 if msvc && !clang && !cuda && !(is_asm && is_arm) {
2978 let mut s = OsString::from("-Fo");
2979 s.push(&dst);
2980 cmd.arg(s);
2981 } else {
2982 cmd.arg("-o").arg(&dst);
2983 }
2984 }
2985
2986 // Use by default minimum available API level
2987 // See note about naming here
2988 // https://android.googlesource.com/platform/ndk/+/refs/heads/ndk-release-r21/docs/BuildSystemMaintainers.md#Clang
2989 static NEW_STANDALONE_ANDROID_COMPILERS: [&str; 4] = [
2990 "aarch64-linux-android21-clang",
2991 "armv7a-linux-androideabi16-clang",
2992 "i686-linux-android16-clang",
2993 "x86_64-linux-android21-clang",
2994 ];
2995
2996 // New "standalone" C/C++ cross-compiler executables from recent Android NDK
2997 // are just shell scripts that call main clang binary (from Android NDK) with
2998 // proper `--target` argument.
2999 //
3000 // For example, armv7a-linux-androideabi16-clang passes
3001 // `--target=armv7a-linux-androideabi16` to clang.
3002 // So to construct proper command line check if
3003 // `--target` argument would be passed or not to clang
3004 fn android_clang_compiler_uses_target_arg_internally(clang_path: &Path) -> bool {
3005 if let Some(filename) = clang_path.file_name() {
3006 if let Some(filename_str) = filename.to_str() {
3007 filename_str.contains("android")
3008 } else {
3009 false
3010 }
3011 } else {
3012 false
3013 }
3014 }
3015
3016 #[test]
3017 fn test_android_clang_compiler_uses_target_arg_internally() {
3018 for version in 16..21 {
3019 assert!(android_clang_compiler_uses_target_arg_internally(
3020 &PathBuf::from(format!("armv7a-linux-androideabi{}-clang", version))
3021 ));
3022 assert!(android_clang_compiler_uses_target_arg_internally(
3023 &PathBuf::from(format!("armv7a-linux-androideabi{}-clang++", version))
3024 ));
3025 }
3026 assert!(!android_clang_compiler_uses_target_arg_internally(
3027 &PathBuf::from("clang")
3028 ));
3029 assert!(!android_clang_compiler_uses_target_arg_internally(
3030 &PathBuf::from("clang++")
3031 ));
3032 }
3033
3034 fn autodetect_android_compiler(target: &str, host: &str, gnu: &str, clang: &str) -> String {
3035 let new_clang_key = match target {
3036 "aarch64-linux-android" => Some("aarch64"),
3037 "armv7-linux-androideabi" => Some("armv7a"),
3038 "i686-linux-android" => Some("i686"),
3039 "x86_64-linux-android" => Some("x86_64"),
3040 _ => None,
3041 };
3042
3043 let new_clang = new_clang_key
3044 .map(|key| {
3045 NEW_STANDALONE_ANDROID_COMPILERS
3046 .iter()
3047 .find(|x| x.starts_with(key))
3048 })
3049 .unwrap_or(None);
3050
3051 if let Some(new_clang) = new_clang {
3052 if Command::new(new_clang).output().is_ok() {
3053 return (*new_clang).into();
3054 }
3055 }
3056
3057 let target = target
3058 .replace("armv7neon", "arm")
3059 .replace("armv7", "arm")
3060 .replace("thumbv7neon", "arm")
3061 .replace("thumbv7", "arm");
3062 let gnu_compiler = format!("{}-{}", target, gnu);
3063 let clang_compiler = format!("{}-{}", target, clang);
3064
3065 // On Windows, the Android clang compiler is provided as a `.cmd` file instead
3066 // of a `.exe` file. `std::process::Command` won't run `.cmd` files unless the
3067 // `.cmd` is explicitly appended to the command name, so we do that here.
3068 let clang_compiler_cmd = format!("{}-{}.cmd", target, clang);
3069
3070 // Check if gnu compiler is present
3071 // if not, use clang
3072 if Command::new(&gnu_compiler).output().is_ok() {
3073 gnu_compiler
3074 } else if host.contains("windows") && Command::new(&clang_compiler_cmd).output().is_ok() {
3075 clang_compiler_cmd
3076 } else {
3077 clang_compiler
3078 }
3079 }
3080
3081 // Rust and clang/cc don't agree on how to name the target.
3082 fn map_darwin_target_from_rust_to_compiler_architecture(target: &str) -> Option<&'static str> {
3083 if target.contains("x86_64") {
3084 Some("x86_64")
3085 } else if target.contains("arm64e") {
3086 Some("arm64e")
3087 } else if target.contains("aarch64") {
3088 Some("arm64")
3089 } else {
3090 None
3091 }
3092 }