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