1 //! A library for build scripts to compile custom C code
3 //! This library is intended to be used as a `build-dependencies` entry in
7 //! [build-dependencies]
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.
15 //! This crate will automatically detect situations such as cross compilation or
16 //! other environment variables set by Cargo and will build code appropriately.
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.
22 //! [`Build`]: struct.Build.html
26 //! To parallelize computation, enable the `parallel` feature for the crate.
29 //! [build-dependencies]
30 //! cc = { version = "1.0", features = ["parallel"] }
32 //! To specify the max number of concurrent compilation jobs, set the `NUM_JOBS`
33 //! environment variable to the desired amount.
35 //! Cargo will also set this environment variable when executed with the `-jN` flag.
37 //! If `NUM_JOBS` is not set, the `RAYON_NUM_THREADS` environment variable can
38 //! also specify the build parallelism.
42 //! Use the `Build` struct to compile `src/foo.c`:
47 //! .file("src/foo.c")
48 //! .define("FOO", Some("bar"))
54 #![doc(html_root_url = "https://docs.rs/cc/1.0")]
55 #![cfg_attr(test, deny(warnings))]
57 #![deny(missing_docs)]
59 use std
::collections
::HashMap
;
61 use std
::ffi
::{OsStr, OsString}
;
62 use std
::fmt
::{self, Display}
;
64 use std
::io
::{self, BufRead, BufReader, Read, Write}
;
65 use std
::path
::{Path, PathBuf}
;
66 use std
::process
::{Child, Command, Stdio}
;
67 use std
::sync
::{Arc, Mutex}
;
68 use std
::thread
::{self, JoinHandle}
;
70 // These modules are all glue to support reading the MSVC version from
71 // the registry and from COM interfaces
84 pub mod windows_registry
;
86 /// A builder for compilation of a native library.
88 /// A `Build` is the main type of the `cc` crate and is used to control all the
89 /// various configuration options and such of a compile. You'll find more
90 /// documentation on each method itself.
91 #[derive(Clone, Debug)]
93 include_directories
: Vec
<PathBuf
>,
94 definitions
: Vec
<(String
, Option
<String
>)>,
95 objects
: Vec
<PathBuf
>,
97 flags_supported
: Vec
<String
>,
98 known_flag_support_status
: Arc
<Mutex
<HashMap
<String
, bool
>>>,
99 ar_flags
: Vec
<String
>,
100 no_default_flags
: bool
,
103 cpp_link_stdlib
: Option
<Option
<String
>>,
104 cpp_set_stdlib
: Option
<String
>,
106 target
: Option
<String
>,
107 host
: Option
<String
>,
108 out_dir
: Option
<PathBuf
>,
109 opt_level
: Option
<String
>,
111 force_frame_pointer
: Option
<bool
>,
112 env
: Vec
<(OsString
, OsString
)>,
113 compiler
: Option
<PathBuf
>,
114 archiver
: Option
<PathBuf
>,
115 cargo_metadata
: bool
,
117 use_plt
: Option
<bool
>,
118 static_crt
: Option
<bool
>,
119 shared_flag
: Option
<bool
>,
120 static_flag
: Option
<bool
>,
121 warnings_into_errors
: bool
,
122 warnings
: Option
<bool
>,
123 extra_warnings
: Option
<bool
>,
124 env_cache
: Arc
<Mutex
<HashMap
<String
, Option
<String
>>>>,
125 apple_sdk_root_cache
: Arc
<Mutex
<HashMap
<String
, OsString
>>>,
128 /// Represents the types of errors that may occur while using cc-rs.
129 #[derive(Clone, Debug)]
131 /// Error occurred while performing I/O.
133 /// Invalid architecture supplied.
135 /// Environment variable not found, with the var in question as extra info.
137 /// Error occurred while using external tools (ie: invocation of compiler).
139 /// Error occurred due to missing external tools.
143 /// Represents an internal error that occurred, with an explanation.
144 #[derive(Clone, Debug)]
146 /// Describes the kind of error that occurred.
148 /// More explanation of error that occurred.
153 fn new(kind
: ErrorKind
, message
: &str) -> Error
{
156 message
: message
.to_owned(),
161 impl From
<io
::Error
> for Error
{
162 fn from(e
: io
::Error
) -> Error
{
163 Error
::new(ErrorKind
::IOError
, &format
!("{}", e
))
167 impl Display
for Error
{
168 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
169 write
!(f
, "{:?}: {}", self.kind
, self.message
)
173 impl std
::error
::Error
for Error {}
175 /// Configuration used to represent an invocation of a C compiler.
177 /// This can be used to figure out what compiler is in use, what the arguments
178 /// to it are, and what the environment variables look like for the compiler.
179 /// This can be used to further configure other build systems (e.g. forward
180 /// along CC and/or CFLAGS) or the `to_command` method can be used to run the
182 #[derive(Clone, Debug)]
185 cc_wrapper_path
: Option
<PathBuf
>,
186 cc_wrapper_args
: Vec
<OsString
>,
188 env
: Vec
<(OsString
, OsString
)>,
191 removed_args
: Vec
<OsString
>,
194 /// Represents the family of tools this tool belongs to.
196 /// Each family of tools differs in how and what arguments they accept.
198 /// Detection of a family is done on best-effort basis and may not accurately reflect the tool.
199 #[derive(Copy, Clone, Debug, PartialEq)]
201 /// Tool is GNU Compiler Collection-like.
203 /// Tool is Clang-like. It differs from the GCC in a sense that it accepts superset of flags
204 /// and its cross-compilation approach is different.
206 /// Tool is the MSVC cl.exe.
207 Msvc { clang_cl: bool }
,
211 /// What the flag to request debug info for this family of tools look like
212 fn add_debug_flags(&self, cmd
: &mut Tool
) {
214 ToolFamily
::Msvc { .. }
=> {
215 cmd
.push_cc_arg("-Z7".into());
217 ToolFamily
::Gnu
| ToolFamily
::Clang
=> {
218 cmd
.push_cc_arg("-g".into());
223 /// What the flag to force frame pointers.
224 fn add_force_frame_pointer(&self, cmd
: &mut Tool
) {
226 ToolFamily
::Gnu
| ToolFamily
::Clang
=> {
227 cmd
.push_cc_arg("-fno-omit-frame-pointer".into());
233 /// What the flags to enable all warnings
234 fn warnings_flags(&self) -> &'
static str {
236 ToolFamily
::Msvc { .. }
=> "-W4",
237 ToolFamily
::Gnu
| ToolFamily
::Clang
=> "-Wall",
241 /// What the flags to enable extra warnings
242 fn extra_warnings_flags(&self) -> Option
<&'
static str> {
244 ToolFamily
::Msvc { .. }
=> None
,
245 ToolFamily
::Gnu
| ToolFamily
::Clang
=> Some("-Wextra"),
249 /// What the flag to turn warning into errors
250 fn warnings_to_errors_flag(&self) -> &'
static str {
252 ToolFamily
::Msvc { .. }
=> "-WX",
253 ToolFamily
::Gnu
| ToolFamily
::Clang
=> "-Werror",
257 fn verbose_stderr(&self) -> bool
{
258 *self == ToolFamily
::Clang
262 /// Represents an object.
264 /// This is a source file -> object file pair.
265 #[derive(Clone, Debug)]
272 /// Create a new source file -> object file pair.
273 fn new(src
: PathBuf
, dst
: PathBuf
) -> Object
{
274 Object { src: src, dst: dst }
279 /// Construct a new instance of a blank set of configuration.
281 /// This builder is finished with the [`compile`] function.
283 /// [`compile`]: struct.Build.html#method.compile
284 pub fn new() -> Build
{
286 include_directories
: Vec
::new(),
287 definitions
: Vec
::new(),
290 flags_supported
: Vec
::new(),
291 known_flag_support_status
: Arc
::new(Mutex
::new(HashMap
::new())),
292 ar_flags
: Vec
::new(),
293 no_default_flags
: false,
298 cpp_link_stdlib
: None
,
299 cpp_set_stdlib
: None
,
306 force_frame_pointer
: None
,
310 cargo_metadata
: true,
315 extra_warnings
: None
,
316 warnings_into_errors
: false,
317 env_cache
: Arc
::new(Mutex
::new(HashMap
::new())),
318 apple_sdk_root_cache
: Arc
::new(Mutex
::new(HashMap
::new())),
322 /// Add a directory to the `-I` or include path for headers
327 /// use std::path::Path;
329 /// let library_path = Path::new("/path/to/library");
332 /// .file("src/foo.c")
333 /// .include(library_path)
337 pub fn include
<P
: AsRef
<Path
>>(&mut self, dir
: P
) -> &mut Build
{
338 self.include_directories
.push(dir
.as_ref().to_path_buf());
342 /// Add multiple directories to the `-I` include path.
347 /// # use std::path::Path;
348 /// # let condition = true;
350 /// let mut extra_dir = None;
352 /// extra_dir = Some(Path::new("/path/to"));
356 /// .file("src/foo.c")
357 /// .includes(extra_dir)
360 pub fn includes
<P
>(&mut self, dirs
: P
) -> &mut Build
363 P
::Item
: AsRef
<Path
>,
371 /// Specify a `-D` variable with an optional value.
377 /// .file("src/foo.c")
378 /// .define("FOO", "BAR")
379 /// .define("BAZ", None)
382 pub fn define
<'a
, V
: Into
<Option
<&'a
str>>>(&mut self, var
: &str, val
: V
) -> &mut Build
{
384 .push((var
.to_string(), val
.into().map(|s
| s
.to_string())));
388 /// Add an arbitrary object file to link in
389 pub fn object
<P
: AsRef
<Path
>>(&mut self, obj
: P
) -> &mut Build
{
390 self.objects
.push(obj
.as_ref().to_path_buf());
394 /// Add an arbitrary flag to the invocation of the compiler
400 /// .file("src/foo.c")
401 /// .flag("-ffunction-sections")
404 pub fn flag(&mut self, flag
: &str) -> &mut Build
{
405 self.flags
.push(flag
.to_string());
409 /// Add an arbitrary flag to the invocation of the compiler
415 /// .file("src/foo.c")
416 /// .file("src/bar.c")
417 /// .ar_flag("/NODEFAULTLIB:libc.dll")
421 pub fn ar_flag(&mut self, flag
: &str) -> &mut Build
{
422 self.ar_flags
.push(flag
.to_string());
426 fn ensure_check_file(&self) -> Result
<PathBuf
, Error
> {
427 let out_dir
= self.get_out_dir()?
;
428 let src
= if self.cuda
{
430 out_dir
.join("flag_check.cu")
432 out_dir
.join("flag_check.cpp")
434 out_dir
.join("flag_check.c")
438 let mut f
= fs
::File
::create(&src
)?
;
439 write
!(f
, "int main(void) {{ return 0; }}")?
;
445 /// Run the compiler to test if it accepts the given flag.
447 /// For a convenience method for setting flags conditionally,
448 /// see `flag_if_supported()`.
450 /// It may return error if it's unable to run the compiler with a test file
451 /// (e.g. the compiler is missing or a write to the `out_dir` failed).
453 /// Note: Once computed, the result of this call is stored in the
454 /// `known_flag_support` field. If `is_flag_supported(flag)`
455 /// is called again, the result will be read from the hash table.
456 pub fn is_flag_supported(&self, flag
: &str) -> Result
<bool
, Error
> {
457 let mut known_status
= self.known_flag_support_status
.lock().unwrap();
458 if let Some(is_supported
) = known_status
.get(flag
).cloned() {
459 return Ok(is_supported
);
462 let out_dir
= self.get_out_dir()?
;
463 let src
= self.ensure_check_file()?
;
464 let obj
= out_dir
.join("flag_check");
465 let target
= self.get_target()?
;
466 let host
= self.get_host()?
;
467 let mut cfg
= Build
::new();
475 let mut compiler
= cfg
.try_get_compiler()?
;
477 // Clang uses stderr for verbose output, which yields a false positive
478 // result if the CFLAGS/CXXFLAGS include -v to aid in debugging.
479 if compiler
.family
.verbose_stderr() {
480 compiler
.remove_arg("-v".into());
483 let mut cmd
= compiler
.to_command();
484 let is_arm
= target
.contains("aarch64") || target
.contains("arm");
485 let clang
= compiler
.family
== ToolFamily
::Clang
;
486 command_add_output_file(
490 target
.contains("msvc"),
496 // We need to explicitly tell msvc not to link and create an exe
497 // in the root directory of the crate
498 if target
.contains("msvc") && !self.cuda
{
504 let output
= cmd
.output()?
;
505 let is_supported
= output
.stderr
.is_empty();
507 known_status
.insert(flag
.to_owned(), is_supported
);
511 /// Add an arbitrary flag to the invocation of the compiler if it supports it
517 /// .file("src/foo.c")
518 /// .flag_if_supported("-Wlogical-op") // only supported by GCC
519 /// .flag_if_supported("-Wunreachable-code") // only supported by clang
522 pub fn flag_if_supported(&mut self, flag
: &str) -> &mut Build
{
523 self.flags_supported
.push(flag
.to_string());
527 /// Set the `-shared` flag.
529 /// When enabled, the compiler will produce a shared object which can
530 /// then be linked with other objects to form an executable.
536 /// .file("src/foo.c")
537 /// .shared_flag(true)
538 /// .compile("libfoo.so");
540 pub fn shared_flag(&mut self, shared_flag
: bool
) -> &mut Build
{
541 self.shared_flag
= Some(shared_flag
);
545 /// Set the `-static` flag.
547 /// When enabled on systems that support dynamic linking, this prevents
548 /// linking with the shared libraries.
554 /// .file("src/foo.c")
555 /// .shared_flag(true)
556 /// .static_flag(true)
559 pub fn static_flag(&mut self, static_flag
: bool
) -> &mut Build
{
560 self.static_flag
= Some(static_flag
);
564 /// Disables the generation of default compiler flags. The default compiler
565 /// flags may cause conflicts in some cross compiling scenarios.
567 /// Setting the `CRATE_CC_NO_DEFAULTS` environment variable has the same
568 /// effect as setting this to `true`. The presence of the environment
569 /// variable and the value of `no_default_flags` will be OR'd together.
570 pub fn no_default_flags(&mut self, no_default_flags
: bool
) -> &mut Build
{
571 self.no_default_flags
= no_default_flags
;
575 /// Add a file which will be compiled
576 pub fn file
<P
: AsRef
<Path
>>(&mut self, p
: P
) -> &mut Build
{
577 self.files
.push(p
.as_ref().to_path_buf());
581 /// Add files which will be compiled
582 pub fn files
<P
>(&mut self, p
: P
) -> &mut Build
585 P
::Item
: AsRef
<Path
>,
587 for file
in p
.into_iter() {
595 /// The other `cpp_*` options will only become active if this is set to
597 pub fn cpp(&mut self, cpp
: bool
) -> &mut Build
{
602 /// Set CUDA C++ support.
604 /// Enabling CUDA will pass the detected C/C++ toolchain as an argument to
605 /// the CUDA compiler, NVCC. NVCC itself accepts some limited GNU-like args;
606 /// any other arguments for the C/C++ toolchain will be redirected using
607 /// "-Xcompiler" flags.
609 /// If enabled, this also implicitly enables C++ support.
610 pub fn cuda(&mut self, cuda
: bool
) -> &mut Build
{
618 /// Set warnings into errors flag.
620 /// Disabled by default.
622 /// Warning: turning warnings into errors only make sense
623 /// if you are a developer of the crate using cc-rs.
624 /// Some warnings only appear on some architecture or
625 /// specific version of the compiler. Any user of this crate,
626 /// or any other crate depending on it, could fail during
633 /// .file("src/foo.c")
634 /// .warnings_into_errors(true)
635 /// .compile("libfoo.a");
637 pub fn warnings_into_errors(&mut self, warnings_into_errors
: bool
) -> &mut Build
{
638 self.warnings_into_errors
= warnings_into_errors
;
642 /// Set warnings flags.
645 /// - "-Wall" for MSVC.
646 /// - "-Wall", "-Wextra" for GNU and Clang.
648 /// Enabled by default.
654 /// .file("src/foo.c")
656 /// .compile("libfoo.a");
658 pub fn warnings(&mut self, warnings
: bool
) -> &mut Build
{
659 self.warnings
= Some(warnings
);
660 self.extra_warnings
= Some(warnings
);
664 /// Set extra warnings flags.
667 /// - nothing for MSVC.
668 /// - "-Wextra" for GNU and Clang.
670 /// Enabled by default.
675 /// // Disables -Wextra, -Wall remains enabled:
677 /// .file("src/foo.c")
678 /// .extra_warnings(false)
679 /// .compile("libfoo.a");
681 pub fn extra_warnings(&mut self, warnings
: bool
) -> &mut Build
{
682 self.extra_warnings
= Some(warnings
);
686 /// Set the standard library to link against when compiling with C++
689 /// See [`get_cpp_link_stdlib`](cc::Build::get_cpp_link_stdlib) documentation
690 /// for the default value.
691 /// If the `CXXSTDLIB` environment variable is set, its value will
692 /// override the default value, but not the value explicitly set by calling
695 /// A value of `None` indicates that no automatic linking should happen,
696 /// otherwise cargo will link against the specified library.
698 /// The given library name must not contain the `lib` prefix.
701 /// - `stdc++` for GNU
702 /// - `c++` for Clang
703 /// - `c++_shared` or `c++_static` for Android
709 /// .file("src/foo.c")
710 /// .shared_flag(true)
711 /// .cpp_link_stdlib("stdc++")
712 /// .compile("libfoo.so");
714 pub fn cpp_link_stdlib
<'a
, V
: Into
<Option
<&'a
str>>>(
718 self.cpp_link_stdlib
= Some(cpp_link_stdlib
.into().map(|s
| s
.into()));
722 /// Force the C++ compiler to use the specified standard library.
724 /// Setting this option will automatically set `cpp_link_stdlib` to the same
727 /// The default value of this option is always `None`.
729 /// This option has no effect when compiling for a Visual Studio based
732 /// This option sets the `-stdlib` flag, which is only supported by some
733 /// compilers (clang, icc) but not by others (gcc). The library will not
734 /// detect which compiler is used, as such it is the responsibility of the
735 /// caller to ensure that this option is only used in conjunction with a
736 /// compiler which supports the `-stdlib` flag.
738 /// A value of `None` indicates that no specific C++ standard library should
739 /// be used, otherwise `-stdlib` is added to the compile invocation.
741 /// The given library name must not contain the `lib` prefix.
744 /// - `stdc++` for GNU
745 /// - `c++` for Clang
751 /// .file("src/foo.c")
752 /// .cpp_set_stdlib("c++")
753 /// .compile("libfoo.a");
755 pub fn cpp_set_stdlib
<'a
, V
: Into
<Option
<&'a
str>>>(
759 let cpp_set_stdlib
= cpp_set_stdlib
.into();
760 self.cpp_set_stdlib
= cpp_set_stdlib
.map(|s
| s
.into());
761 self.cpp_link_stdlib(cpp_set_stdlib
);
765 /// Configures the target this configuration will be compiling for.
767 /// This option is automatically scraped from the `TARGET` environment
768 /// variable by build scripts, so it's not required to call this function.
774 /// .file("src/foo.c")
775 /// .target("aarch64-linux-android")
778 pub fn target(&mut self, target
: &str) -> &mut Build
{
779 self.target
= Some(target
.to_string());
783 /// Configures the host assumed by this configuration.
785 /// This option is automatically scraped from the `HOST` environment
786 /// variable by build scripts, so it's not required to call this function.
792 /// .file("src/foo.c")
793 /// .host("arm-linux-gnueabihf")
796 pub fn host(&mut self, host
: &str) -> &mut Build
{
797 self.host
= Some(host
.to_string());
801 /// Configures the optimization level of the generated object files.
803 /// This option is automatically scraped from the `OPT_LEVEL` environment
804 /// variable by build scripts, so it's not required to call this function.
805 pub fn opt_level(&mut self, opt_level
: u32) -> &mut Build
{
806 self.opt_level
= Some(opt_level
.to_string());
810 /// Configures the optimization level of the generated object files.
812 /// This option is automatically scraped from the `OPT_LEVEL` environment
813 /// variable by build scripts, so it's not required to call this function.
814 pub fn opt_level_str(&mut self, opt_level
: &str) -> &mut Build
{
815 self.opt_level
= Some(opt_level
.to_string());
819 /// Configures whether the compiler will emit debug information when
820 /// generating object files.
822 /// This option is automatically scraped from the `DEBUG` environment
823 /// variable by build scripts, so it's not required to call this function.
824 pub fn debug(&mut self, debug
: bool
) -> &mut Build
{
825 self.debug
= Some(debug
);
829 /// Configures whether the compiler will emit instructions to store
830 /// frame pointers during codegen.
832 /// This option is automatically enabled when debug information is emitted.
833 /// Otherwise the target platform compiler's default will be used.
834 /// You can use this option to force a specific setting.
835 pub fn force_frame_pointer(&mut self, force
: bool
) -> &mut Build
{
836 self.force_frame_pointer
= Some(force
);
840 /// Configures the output directory where all object files and static
841 /// libraries will be located.
843 /// This option is automatically scraped from the `OUT_DIR` environment
844 /// variable by build scripts, so it's not required to call this function.
845 pub fn out_dir
<P
: AsRef
<Path
>>(&mut self, out_dir
: P
) -> &mut Build
{
846 self.out_dir
= Some(out_dir
.as_ref().to_owned());
850 /// Configures the compiler to be used to produce output.
852 /// This option is automatically determined from the target platform or a
853 /// number of environment variables, so it's not required to call this
855 pub fn compiler
<P
: AsRef
<Path
>>(&mut self, compiler
: P
) -> &mut Build
{
856 self.compiler
= Some(compiler
.as_ref().to_owned());
860 /// Configures the tool used to assemble archives.
862 /// This option is automatically determined from the target platform or a
863 /// number of environment variables, so it's not required to call this
865 pub fn archiver
<P
: AsRef
<Path
>>(&mut self, archiver
: P
) -> &mut Build
{
866 self.archiver
= Some(archiver
.as_ref().to_owned());
869 /// Define whether metadata should be emitted for cargo allowing it to
870 /// automatically link the binary. Defaults to `true`.
872 /// The emitted metadata is:
874 /// - `rustc-link-lib=static=`*compiled lib*
875 /// - `rustc-link-search=native=`*target folder*
876 /// - When target is MSVC, the ATL-MFC libs are added via `rustc-link-search=native=`
877 /// - When C++ is enabled, the C++ stdlib is added via `rustc-link-lib`
879 pub fn cargo_metadata(&mut self, cargo_metadata
: bool
) -> &mut Build
{
880 self.cargo_metadata
= cargo_metadata
;
884 /// Configures whether the compiler will emit position independent code.
886 /// This option defaults to `false` for `windows-gnu` and bare metal targets and
887 /// to `true` for all other targets.
888 pub fn pic(&mut self, pic
: bool
) -> &mut Build
{
889 self.pic
= Some(pic
);
893 /// Configures whether the Procedure Linkage Table is used for indirect
894 /// calls into shared libraries.
896 /// The PLT is used to provide features like lazy binding, but introduces
897 /// a small performance loss due to extra pointer indirection. Setting
898 /// `use_plt` to `false` can provide a small performance increase.
900 /// Note that skipping the PLT requires a recent version of GCC/Clang.
902 /// This only applies to ELF targets. It has no effect on other platforms.
903 pub fn use_plt(&mut self, use_plt
: bool
) -> &mut Build
{
904 self.use_plt
= Some(use_plt
);
908 /// Configures whether the /MT flag or the /MD flag will be passed to msvc build tools.
910 /// This option defaults to `false`, and affect only msvc targets.
911 pub fn static_crt(&mut self, static_crt
: bool
) -> &mut Build
{
912 self.static_crt
= Some(static_crt
);
917 pub fn __set_env
<A
, B
>(&mut self, a
: A
, b
: B
) -> &mut Build
923 .push((a
.as_ref().to_owned(), b
.as_ref().to_owned()));
927 /// Run the compiler, generating the file `output`
929 /// This will return a result instead of panicing; see compile() for the complete description.
930 pub fn try_compile(&self, output
: &str) -> Result
<(), Error
> {
931 let (lib_name
, gnu_lib_name
) = if output
.starts_with("lib") && output
.ends_with(".a") {
932 (&output
[3..output
.len() - 2], output
.to_owned())
934 let mut gnu
= String
::with_capacity(5 + output
.len());
936 gnu
.push_str(&output
);
940 let dst
= self.get_out_dir()?
;
942 let mut objects
= Vec
::new();
943 for file
in self.files
.iter() {
944 let obj
= dst
.join(file
).with_extension("o");
945 let obj
= if !obj
.starts_with(&dst
) {
946 dst
.join(obj
.file_name().ok_or_else(|| {
947 Error
::new(ErrorKind
::IOError
, "Getting object file details failed.")
954 Some(s
) => fs
::create_dir_all(s
)?
,
956 return Err(Error
::new(
958 "Getting object file details failed.",
963 objects
.push(Object
::new(file
.to_path_buf(), obj
));
965 self.compile_objects(&objects
)?
;
966 self.assemble(lib_name
, &dst
.join(gnu_lib_name
), &objects
)?
;
968 if self.get_target()?
.contains("msvc") {
969 let compiler
= self.get_base_compiler()?
;
970 let atlmfc_lib
= compiler
973 .find(|&&(ref var
, _
)| var
.as_os_str() == OsStr
::new("LIB"))
974 .and_then(|&(_
, ref lib_paths
)| {
975 env
::split_paths(lib_paths
).find(|path
| {
976 let sub
= Path
::new("atlmfc/lib");
977 path
.ends_with(sub
) || path
.parent().map_or(false, |p
| p
.ends_with(sub
))
981 if let Some(atlmfc_lib
) = atlmfc_lib
{
983 "cargo:rustc-link-search=native={}",
989 self.print(&format
!("cargo:rustc-link-lib=static={}", lib_name
));
990 self.print(&format
!("cargo:rustc-link-search=native={}", dst
.display()));
992 // Add specific C++ libraries, if enabled.
994 if let Some(stdlib
) = self.get_cpp_link_stdlib()?
{
995 self.print(&format
!("cargo:rustc-link-lib={}", stdlib
));
1002 /// Run the compiler, generating the file `output`
1004 /// The name `output` should be the name of the library. For backwards compatibility,
1005 /// the `output` may start with `lib` and end with `.a`. The Rust compiler will create
1006 /// the assembly with the lib prefix and .a extension. MSVC will create a file without prefix,
1007 /// ending with `.lib`.
1011 /// Panics if `output` is not formatted correctly or if one of the underlying
1012 /// compiler commands fails. It can also panic if it fails reading file names
1013 /// or creating directories.
1014 pub fn compile(&self, output
: &str) {
1015 if let Err(e
) = self.try_compile(output
) {
1020 #[cfg(feature = "parallel")]
1021 fn compile_objects
<'me
>(&'me
self, objs
: &[Object
]) -> Result
<(), Error
> {
1022 use std
::sync
::atomic
::{AtomicBool, Ordering::SeqCst}
;
1023 use std
::sync
::Once
;
1025 // Limit our parallelism globally with a jobserver. Start off by
1026 // releasing our own token for this process so we can have a bit of an
1027 // easier to write loop below. If this fails, though, then we're likely
1028 // on Windows with the main implicit token, so we just have a bit extra
1029 // parallelism for a bit and don't reacquire later.
1030 let server
= jobserver();
1031 let reacquire
= server
.release_raw().is_ok();
1033 // When compiling objects in parallel we do a few dirty tricks to speed
1036 // * First is that we use the `jobserver` crate to limit the parallelism
1037 // of this build script. The `jobserver` crate will use a jobserver
1038 // configured by Cargo for build scripts to ensure that parallelism is
1039 // coordinated across C compilations and Rust compilations. Before we
1040 // compile anything we make sure to wait until we acquire a token.
1042 // Note that this jobserver is cached globally so we only used one per
1043 // process and only worry about creating it once.
1045 // * Next we use a raw `thread::spawn` per thread to actually compile
1046 // objects in parallel. We only actually spawn a thread after we've
1047 // acquired a token to perform some work
1049 // * Finally though we want to keep the dependencies of this crate
1050 // pretty light, so we avoid using a safe abstraction like `rayon` and
1051 // instead rely on some bits of `unsafe` code. We know that this stack
1052 // frame persists while everything is compiling so we use all the
1053 // stack-allocated objects without cloning/reallocating. We use a
1054 // transmute to `State` with a `'static` lifetime to persist
1055 // everything we need across the boundary, and the join-on-drop
1056 // semantics of `JoinOnDrop` should ensure that our stack frame is
1057 // alive while threads are alive.
1059 // With all that in mind we compile all objects in a loop here, after we
1060 // acquire the appropriate tokens, Once all objects have been compiled
1061 // we join on all the threads and propagate the results of compilation.
1063 // Note that as a slight optimization we try to break out as soon as
1064 // possible as soon as any compilation fails to ensure that errors get
1065 // out to the user as fast as possible.
1066 let error
= AtomicBool
::new(false);
1067 let mut threads
= Vec
::new();
1069 if error
.load(SeqCst
) {
1072 let token
= server
.acquire()?
;
1078 let state
= unsafe { std::mem::transmute::<State, State<'static>>(state) }
;
1079 let thread
= thread
::spawn(|| {
1080 let state
: State
<'me
> = state
; // erase the `'static` lifetime
1081 let result
= state
.build
.compile_object(state
.obj
);
1082 if result
.is_err() {
1083 state
.error
.store(true, SeqCst
);
1085 drop(token
); // make sure our jobserver token is released after the compile
1088 threads
.push(JoinOnDrop(Some(thread
)));
1091 for mut thread
in threads
{
1092 if let Some(thread
) = thread
.0.take() {
1093 thread
.join().expect("thread should not panic")?
;
1097 // Reacquire our process's token before we proceed, which we released
1098 // before entering the loop above.
1100 server
.acquire_raw()?
;
1105 /// Shared state from the parent thread to the child thread. This
1106 /// package of pointers is temporarily transmuted to a `'static`
1107 /// lifetime to cross the thread boundary and then once the thread is
1108 /// running we erase the `'static` to go back to an anonymous lifetime.
1112 error
: &'a AtomicBool
,
1115 /// Returns a suitable `jobserver::Client` used to coordinate
1116 /// parallelism between build scripts.
1117 fn jobserver() -> &'
static jobserver
::Client
{
1118 static INIT
: Once
= Once
::new();
1119 static mut JOBSERVER
: Option
<jobserver
::Client
> = None
;
1121 fn _assert_sync
<T
: Sync
>() {}
1122 _assert_sync
::<jobserver
::Client
>();
1126 let server
= default_jobserver();
1127 JOBSERVER
= Some(server
);
1129 JOBSERVER
.as_ref().unwrap()
1133 unsafe fn default_jobserver() -> jobserver
::Client
{
1134 // Try to use the environmental jobserver which Cargo typically
1135 // initializes for us...
1136 if let Some(client
) = jobserver
::Client
::from_env() {
1140 // ... but if that fails for whatever reason select something
1141 // reasonable and crate a new jobserver. Use `NUM_JOBS` if set (it's
1142 // configured by Cargo) and otherwise just fall back to a
1143 // semi-reasonable number. Note that we could use `num_cpus` here
1144 // but it's an extra dependency that will almost never be used, so
1145 // it's generally not too worth it.
1146 let mut parallelism
= 4;
1147 if let Ok(amt
) = env
::var("NUM_JOBS") {
1148 if let Ok(amt
) = amt
.parse() {
1153 // If we create our own jobserver then be sure to reserve one token
1155 let client
= jobserver
::Client
::new(parallelism
).expect("failed to create jobserver");
1156 client
.acquire_raw().expect("failed to acquire initial");
1160 struct JoinOnDrop(Option
<thread
::JoinHandle
<Result
<(), Error
>>>);
1162 impl Drop
for JoinOnDrop
{
1163 fn drop(&mut self) {
1164 if let Some(thread
) = self.0.take() {
1165 drop(thread
.join());
1171 #[cfg(not(feature = "parallel"))]
1172 fn compile_objects(&self, objs
: &[Object
]) -> Result
<(), Error
> {
1174 self.compile_object(obj
)?
;
1179 fn compile_object(&self, obj
: &Object
) -> Result
<(), Error
> {
1180 let is_asm
= obj
.src
.extension().and_then(|s
| s
.to_str()) == Some("asm");
1181 let target
= self.get_target()?
;
1182 let msvc
= target
.contains("msvc");
1183 let compiler
= self.try_get_compiler()?
;
1184 let clang
= compiler
.family
== ToolFamily
::Clang
;
1185 let (mut cmd
, name
) = if msvc
&& is_asm
{
1186 self.msvc_macro_assembler()?
1188 let mut cmd
= compiler
.to_command();
1189 for &(ref a
, ref b
) in self.env
.iter() {
1197 .ok_or_else(|| Error
::new(ErrorKind
::IOError
, "Failed to get compiler path."))?
1202 let is_arm
= target
.contains("aarch64") || target
.contains("arm");
1203 command_add_output_file(&mut cmd
, &obj
.dst
, self.cuda
, msvc
, clang
, is_asm
, is_arm
);
1204 // armasm and armasm64 don't requrie -c option
1205 if !msvc
|| !is_asm
|| !is_arm
{
1209 if cfg
!(target_os
= "macos") {
1210 self.fix_env_for_apple_os(&mut cmd
)?
;
1213 run(&mut cmd
, &name
)?
;
1217 /// This will return a result instead of panicing; see expand() for the complete description.
1218 pub fn try_expand(&self) -> Result
<Vec
<u8>, Error
> {
1219 let compiler
= self.try_get_compiler()?
;
1220 let mut cmd
= compiler
.to_command();
1221 for &(ref a
, ref b
) in self.env
.iter() {
1227 self.files
.len() <= 1,
1228 "Expand may only be called for a single file"
1231 for file
in self.files
.iter() {
1238 .ok_or_else(|| Error
::new(ErrorKind
::IOError
, "Failed to get compiler path."))?
1242 Ok(run_output(&mut cmd
, &name
)?
)
1245 /// Run the compiler, returning the macro-expanded version of the input files.
1247 /// This is only relevant for C and C++ files.
1250 /// Panics if more than one file is present in the config, or if compiler
1251 /// path has an invalid file name.
1255 /// let out = cc::Build::new().file("src/foo.c").expand();
1257 pub fn expand(&self) -> Vec
<u8> {
1258 match self.try_expand() {
1259 Err(e
) => fail(&e
.message
),
1264 /// Get the compiler that's in use for this configuration.
1266 /// This function will return a `Tool` which represents the culmination
1267 /// of this configuration at a snapshot in time. The returned compiler can
1268 /// be inspected (e.g. the path, arguments, environment) to forward along to
1269 /// other tools, or the `to_command` method can be used to invoke the
1270 /// compiler itself.
1272 /// This method will take into account all configuration such as debug
1273 /// information, optimization level, include directories, defines, etc.
1274 /// Additionally, the compiler binary in use follows the standard
1275 /// conventions for this path, e.g. looking at the explicitly set compiler,
1276 /// environment variables (a number of which are inspected here), and then
1277 /// falling back to the default configuration.
1281 /// Panics if an error occurred while determining the architecture.
1282 pub fn get_compiler(&self) -> Tool
{
1283 match self.try_get_compiler() {
1285 Err(e
) => fail(&e
.message
),
1289 /// Get the compiler that's in use for this configuration.
1291 /// This will return a result instead of panicing; see get_compiler() for the complete description.
1292 pub fn try_get_compiler(&self) -> Result
<Tool
, Error
> {
1293 let opt_level
= self.get_opt_level()?
;
1294 let target
= self.get_target()?
;
1296 let mut cmd
= self.get_base_compiler()?
;
1297 let envflags
= self.envflags(if self.cpp { "CXXFLAGS" }
else { "CFLAGS" }
);
1299 // Disable default flag generation via `no_default_flags` or environment variable
1300 let no_defaults
= self.no_default_flags
|| self.getenv("CRATE_CC_NO_DEFAULTS").is_some();
1303 self.add_default_flags(&mut cmd
, &target
, &opt_level
)?
;
1305 println
!("Info: default compiler flags are disabled");
1308 for arg
in envflags
{
1309 cmd
.push_cc_arg(arg
.into());
1312 for directory
in self.include_directories
.iter() {
1313 cmd
.args
.push("-I".into());
1314 cmd
.args
.push(directory
.into());
1317 // If warnings and/or extra_warnings haven't been explicitly set,
1318 // then we set them only if the environment doesn't already have
1319 // CFLAGS/CXXFLAGS, since those variables presumably already contain
1320 // the desired set of warnings flags.
1324 .unwrap_or(if self.has_flags() { false }
else { true }
)
1326 let wflags
= cmd
.family
.warnings_flags().into();
1327 cmd
.push_cc_arg(wflags
);
1332 .unwrap_or(if self.has_flags() { false }
else { true }
)
1334 if let Some(wflags
) = cmd
.family
.extra_warnings_flags() {
1335 cmd
.push_cc_arg(wflags
.into());
1339 for flag
in self.flags
.iter() {
1340 cmd
.args
.push(flag
.into());
1343 for flag
in self.flags_supported
.iter() {
1344 if self.is_flag_supported(flag
).unwrap_or(false) {
1345 cmd
.push_cc_arg(flag
.into());
1349 for &(ref key
, ref value
) in self.definitions
.iter() {
1350 if let Some(ref value
) = *value
{
1351 cmd
.args
.push(format
!("-D{}={}", key
, value
).into());
1353 cmd
.args
.push(format
!("-D{}", key
).into());
1357 if self.warnings_into_errors
{
1358 let warnings_to_errors_flag
= cmd
.family
.warnings_to_errors_flag().into();
1359 cmd
.push_cc_arg(warnings_to_errors_flag
);
1365 fn add_default_flags(
1370 ) -> Result
<(), Error
> {
1372 // If the flag is not conditioned on target variable, it belongs here :)
1374 ToolFamily
::Msvc { .. }
=> {
1375 cmd
.push_cc_arg("-nologo".into());
1377 let crt_flag
= match self.static_crt
{
1378 Some(true) => "-MT",
1379 Some(false) => "-MD",
1382 .getenv("CARGO_CFG_TARGET_FEATURE")
1383 .unwrap_or(String
::new());
1384 if features
.contains("crt-static") {
1391 cmd
.push_cc_arg(crt_flag
.into());
1393 match &opt_level
[..] {
1394 // Msvc uses /O1 to enable all optimizations that minimize code size.
1395 "z" | "s" | "1" => cmd
.push_opt_unless_duplicate("-O1".into()),
1396 // -O3 is a valid value for gcc and clang compilers, but not msvc. Cap to /O2.
1397 "2" | "3" => cmd
.push_opt_unless_duplicate("-O2".into()),
1401 ToolFamily
::Gnu
| ToolFamily
::Clang
=> {
1402 // arm-linux-androideabi-gcc 4.8 shipped with Android NDK does
1403 // not support '-Oz'
1404 if opt_level
== "z" && cmd
.family
!= ToolFamily
::Clang
{
1405 cmd
.push_opt_unless_duplicate("-Os".into());
1407 cmd
.push_opt_unless_duplicate(format
!("-O{}", opt_level
).into());
1410 if cmd
.family
== ToolFamily
::Clang
&& target
.contains("android") {
1411 // For compatibility with code that doesn't use pre-defined `__ANDROID__` macro.
1412 // If compiler used via ndk-build or cmake (officially supported build methods)
1413 // this macros is defined.
1414 // See https://android.googlesource.com/platform/ndk/+/refs/heads/ndk-release-r21/build/cmake/android.toolchain.cmake#456
1415 // https://android.googlesource.com/platform/ndk/+/refs/heads/ndk-release-r21/build/core/build-binary.mk#141
1416 cmd
.push_opt_unless_duplicate("-DANDROID".into());
1419 if !target
.contains("apple-ios") {
1420 cmd
.push_cc_arg("-ffunction-sections".into());
1421 cmd
.push_cc_arg("-fdata-sections".into());
1423 // Disable generation of PIC on bare-metal for now: rust-lld doesn't support this yet
1426 .unwrap_or(!target
.contains("windows") && !target
.contains("-none-"))
1428 cmd
.push_cc_arg("-fPIC".into());
1429 // PLT only applies if code is compiled with PIC support,
1430 // and only for ELF targets.
1431 if target
.contains("linux") && !self.use_plt
.unwrap_or(true) {
1432 cmd
.push_cc_arg("-fno-plt".into());
1438 if self.get_debug() {
1441 cmd
.args
.push("-G".into());
1443 let family
= cmd
.family
;
1444 family
.add_debug_flags(cmd
);
1447 if self.get_force_frame_pointer() {
1448 let family
= cmd
.family
;
1449 family
.add_force_frame_pointer(cmd
);
1454 ToolFamily
::Clang
=> {
1455 if !(target
.contains("android")
1456 && android_clang_compiler_uses_target_arg_internally(&cmd
.path
))
1458 if target
.contains("darwin") {
1460 map_darwin_target_from_rust_to_compiler_architecture(target
)
1463 .push(format
!("--target={}-apple-darwin", arch
).into());
1465 } else if target
.contains("macabi") {
1467 map_darwin_target_from_rust_to_compiler_architecture(target
)
1469 let ios
= if arch
== "arm64" { "ios" }
else { "ios13.0" }
;
1471 .push(format
!("--target={}-apple-{}-macabi", arch
, ios
).into());
1473 } else if target
.contains("ios-sim") {
1475 map_darwin_target_from_rust_to_compiler_architecture(target
)
1477 let deployment_target
= env
::var("IPHONEOS_DEPLOYMENT_TARGET")
1478 .unwrap_or_else(|_
| "7.0".into());
1481 "--target={}-apple-ios{}-simulator",
1482 arch
, deployment_target
1487 } else if target
.starts_with("riscv64gc-") {
1489 format
!("--target={}", target
.replace("riscv64gc", "riscv64")).into(),
1492 cmd
.args
.push(format
!("--target={}", target
).into());
1496 ToolFamily
::Msvc { clang_cl }
=> {
1497 // This is an undocumented flag from MSVC but helps with making
1498 // builds more reproducible by avoiding putting timestamps into
1500 cmd
.push_cc_arg("-Brepro".into());
1503 if target
.contains("x86_64") {
1504 cmd
.push_cc_arg("-m64".into());
1505 } else if target
.contains("86") {
1506 cmd
.push_cc_arg("-m32".into());
1507 cmd
.push_cc_arg("-arch:IA32".into());
1509 cmd
.push_cc_arg(format
!("--target={}", target
).into());
1512 if target
.contains("i586") {
1513 cmd
.push_cc_arg("-arch:IA32".into());
1517 // There is a check in corecrt.h that will generate a
1518 // compilation error if
1519 // _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE is
1520 // not defined to 1. The check was added in Windows
1521 // 8 days because only store apps were allowed on ARM.
1522 // This changed with the release of Windows 10 IoT Core.
1523 // The check will be going away in future versions of
1524 // the SDK, but for all released versions of the
1525 // Windows SDK it is required.
1526 if target
.contains("arm") || target
.contains("thumb") {
1528 .push("-D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1".into());
1531 ToolFamily
::Gnu
=> {
1532 if target
.contains("i686") || target
.contains("i586") {
1533 cmd
.args
.push("-m32".into());
1534 } else if target
== "x86_64-unknown-linux-gnux32" {
1535 cmd
.args
.push("-mx32".into());
1536 } else if target
.contains("x86_64") || target
.contains("powerpc64") {
1537 cmd
.args
.push("-m64".into());
1540 if target
.contains("darwin") {
1541 if let Some(arch
) = map_darwin_target_from_rust_to_compiler_architecture(target
)
1543 cmd
.args
.push("-arch".into());
1544 cmd
.args
.push(arch
.into());
1548 if self.static_flag
.is_none() {
1550 .getenv("CARGO_CFG_TARGET_FEATURE")
1551 .unwrap_or(String
::new());
1552 if features
.contains("crt-static") {
1553 cmd
.args
.push("-static".into());
1557 // armv7 targets get to use armv7 instructions
1558 if (target
.starts_with("armv7") || target
.starts_with("thumbv7"))
1559 && target
.contains("-linux-")
1561 cmd
.args
.push("-march=armv7-a".into());
1564 // (x86 Android doesn't say "eabi")
1565 if target
.contains("-androideabi") && target
.contains("v7") {
1566 // -march=armv7-a handled above
1567 cmd
.args
.push("-mthumb".into());
1568 if !target
.contains("neon") {
1569 // On android we can guarantee some extra float instructions
1570 // (specified in the android spec online)
1571 // NEON guarantees even more; see below.
1572 cmd
.args
.push("-mfpu=vfpv3-d16".into());
1574 cmd
.args
.push("-mfloat-abi=softfp".into());
1577 if target
.contains("neon") {
1578 cmd
.args
.push("-mfpu=neon-vfpv4".into());
1581 if target
.starts_with("armv4t-unknown-linux-") {
1582 cmd
.args
.push("-march=armv4t".into());
1583 cmd
.args
.push("-marm".into());
1584 cmd
.args
.push("-mfloat-abi=soft".into());
1587 if target
.starts_with("armv5te-unknown-linux-") {
1588 cmd
.args
.push("-march=armv5te".into());
1589 cmd
.args
.push("-marm".into());
1590 cmd
.args
.push("-mfloat-abi=soft".into());
1593 // For us arm == armv6 by default
1594 if target
.starts_with("arm-unknown-linux-") {
1595 cmd
.args
.push("-march=armv6".into());
1596 cmd
.args
.push("-marm".into());
1597 if target
.ends_with("hf") {
1598 cmd
.args
.push("-mfpu=vfp".into());
1600 cmd
.args
.push("-mfloat-abi=soft".into());
1604 // We can guarantee some settings for FRC
1605 if target
.starts_with("arm-frc-") {
1606 cmd
.args
.push("-march=armv7-a".into());
1607 cmd
.args
.push("-mcpu=cortex-a9".into());
1608 cmd
.args
.push("-mfpu=vfpv3".into());
1609 cmd
.args
.push("-mfloat-abi=softfp".into());
1610 cmd
.args
.push("-marm".into());
1613 // Turn codegen down on i586 to avoid some instructions.
1614 if target
.starts_with("i586-unknown-linux-") {
1615 cmd
.args
.push("-march=pentium".into());
1618 // Set codegen level for i686 correctly
1619 if target
.starts_with("i686-unknown-linux-") {
1620 cmd
.args
.push("-march=i686".into());
1623 // Looks like `musl-gcc` makes it hard for `-m32` to make its way
1624 // all the way to the linker, so we need to actually instruct the
1625 // linker that we're generating 32-bit executables as well. This'll
1626 // typically only be used for build scripts which transitively use
1627 // these flags that try to compile executables.
1628 if target
== "i686-unknown-linux-musl" || target
== "i586-unknown-linux-musl" {
1629 cmd
.args
.push("-Wl,-melf_i386".into());
1632 if target
.starts_with("thumb") {
1633 cmd
.args
.push("-mthumb".into());
1635 if target
.ends_with("eabihf") {
1636 cmd
.args
.push("-mfloat-abi=hard".into())
1639 if target
.starts_with("thumbv6m") {
1640 cmd
.args
.push("-march=armv6s-m".into());
1642 if target
.starts_with("thumbv7em") {
1643 cmd
.args
.push("-march=armv7e-m".into());
1645 if target
.ends_with("eabihf") {
1646 cmd
.args
.push("-mfpu=fpv4-sp-d16".into())
1649 if target
.starts_with("thumbv7m") {
1650 cmd
.args
.push("-march=armv7-m".into());
1652 if target
.starts_with("thumbv8m.base") {
1653 cmd
.args
.push("-march=armv8-m.base".into());
1655 if target
.starts_with("thumbv8m.main") {
1656 cmd
.args
.push("-march=armv8-m.main".into());
1658 if target
.ends_with("eabihf") {
1659 cmd
.args
.push("-mfpu=fpv5-sp-d16".into())
1662 if target
.starts_with("armebv7r") | target
.starts_with("armv7r") {
1663 if target
.starts_with("armeb") {
1664 cmd
.args
.push("-mbig-endian".into());
1666 cmd
.args
.push("-mlittle-endian".into());
1670 cmd
.args
.push("-marm".into());
1673 cmd
.args
.push("-march=armv7-r".into());
1675 if target
.ends_with("eabihf") {
1676 // Calling convention
1677 cmd
.args
.push("-mfloat-abi=hard".into());
1679 // lowest common denominator FPU
1680 // (see Cortex-R4 technical reference manual)
1681 cmd
.args
.push("-mfpu=vfpv3-d16".into())
1683 // Calling convention
1684 cmd
.args
.push("-mfloat-abi=soft".into());
1687 if target
.starts_with("armv7a") {
1688 cmd
.args
.push("-march=armv7-a".into());
1690 if target
.ends_with("eabihf") {
1691 // lowest common denominator FPU
1692 cmd
.args
.push("-mfpu=vfpv3-d16".into());
1695 if target
.starts_with("riscv32") || target
.starts_with("riscv64") {
1696 // get the 32i/32imac/32imc/64gc/64imac/... part
1697 let mut parts
= target
.split('
-'
);
1698 if let Some(arch
) = parts
.next() {
1699 let arch
= &arch
[5..];
1700 if target
.contains("linux") && arch
.starts_with("64") {
1701 cmd
.args
.push(("-march=rv64gc").into());
1702 cmd
.args
.push("-mabi=lp64d".into());
1703 } else if target
.contains("linux") && arch
.starts_with("32") {
1704 cmd
.args
.push(("-march=rv32gc").into());
1705 cmd
.args
.push("-mabi=ilp32d".into());
1706 } else if arch
.starts_with("64") {
1707 cmd
.args
.push(("-march=rv".to_owned() + arch
).into());
1708 cmd
.args
.push("-mabi=lp64".into());
1710 cmd
.args
.push(("-march=rv".to_owned() + arch
).into());
1711 cmd
.args
.push("-mabi=ilp32".into());
1713 cmd
.args
.push("-mcmodel=medany".into());
1719 if target
.contains("apple-ios") {
1720 self.ios_flags(cmd
)?
;
1723 if self.static_flag
.unwrap_or(false) {
1724 cmd
.args
.push("-static".into());
1726 if self.shared_flag
.unwrap_or(false) {
1727 cmd
.args
.push("-shared".into());
1731 match (self.cpp_set_stdlib
.as_ref(), cmd
.family
) {
1733 (Some(stdlib
), ToolFamily
::Gnu
) | (Some(stdlib
), ToolFamily
::Clang
) => {
1734 cmd
.push_cc_arg(format
!("-stdlib=lib{}", stdlib
).into());
1738 "cargo:warning=cpp_set_stdlib is specified, but the {:?} compiler \
1739 does not support this option, ignored",
1749 fn has_flags(&self) -> bool
{
1750 let flags_env_var_name
= if self.cpp { "CXXFLAGS" }
else { "CFLAGS" }
;
1751 let flags_env_var_value
= self.get_var(flags_env_var_name
);
1752 if let Ok(_
) = flags_env_var_value
{
1759 fn msvc_macro_assembler(&self) -> Result
<(Command
, String
), Error
> {
1760 let target
= self.get_target()?
;
1761 let tool
= if target
.contains("x86_64") {
1763 } else if target
.contains("arm") {
1765 } else if target
.contains("aarch64") {
1770 let mut cmd
= windows_registry
::find(&target
, tool
).unwrap_or_else(|| self.cmd(tool
));
1771 cmd
.arg("-nologo"); // undocumented, yet working with armasm[64]
1772 for directory
in self.include_directories
.iter() {
1773 cmd
.arg("-I").arg(directory
);
1775 if target
.contains("aarch64") || target
.contains("arm") {
1776 println
!("cargo:warning=The MSVC ARM assemblers do not support -D flags");
1778 for &(ref key
, ref value
) in self.definitions
.iter() {
1779 if let Some(ref value
) = *value
{
1780 cmd
.arg(&format
!("-D{}={}", key
, value
));
1782 cmd
.arg(&format
!("-D{}", key
));
1787 if target
.contains("i686") || target
.contains("i586") {
1788 cmd
.arg("-safeseh");
1790 for flag
in self.flags
.iter() {
1794 Ok((cmd
, tool
.to_string()))
1797 fn assemble(&self, lib_name
: &str, dst
: &Path
, objs
: &[Object
]) -> Result
<(), Error
> {
1798 // Delete the destination if it exists as we want to
1799 // create on the first iteration instead of appending.
1800 let _
= fs
::remove_file(&dst
);
1802 // Add objects to the archive in limited-length batches. This helps keep
1803 // the length of the command line within a reasonable length to avoid
1804 // blowing system limits on limiting platforms like Windows.
1805 let objs
: Vec
<_
> = objs
1807 .map(|o
| o
.dst
.clone())
1808 .chain(self.objects
.clone())
1810 for chunk
in objs
.chunks(100) {
1811 self.assemble_progressive(dst
, chunk
)?
;
1814 let target
= self.get_target()?
;
1815 if target
.contains("msvc") {
1816 // The Rust compiler will look for libfoo.a and foo.lib, but the
1817 // MSVC linker will also be passed foo.lib, so be sure that both
1820 let lib_dst
= dst
.with_file_name(format
!("{}.lib", lib_name
));
1821 let _
= fs
::remove_file(&lib_dst
);
1822 match fs
::hard_link(&dst
, &lib_dst
).or_else(|_
| {
1823 // if hard-link fails, just copy (ignoring the number of bytes written)
1824 fs
::copy(&dst
, &lib_dst
).map(|_
| ())
1828 return Err(Error
::new(
1830 "Could not copy or create a hard-link to the generated lib file.",
1835 // Non-msvc targets (those using `ar`) need a separate step to add
1836 // the symbol table to archives since our construction command of
1837 // `cq` doesn't add it for us.
1838 let (mut ar
, cmd
) = self.get_ar()?
;
1839 run(ar
.arg("s").arg(dst
), &cmd
)?
;
1845 fn assemble_progressive(&self, dst
: &Path
, objs
: &[PathBuf
]) -> Result
<(), Error
> {
1846 let target
= self.get_target()?
;
1848 if target
.contains("msvc") {
1849 let (mut cmd
, program
) = self.get_ar()?
;
1850 let mut out
= OsString
::from("-out:");
1852 cmd
.arg(out
).arg("-nologo");
1853 for flag
in self.ar_flags
.iter() {
1856 // If the library file already exists, add the library name
1857 // as an argument to let lib.exe know we are appending the objs.
1862 run(&mut cmd
, &program
)?
;
1864 let (mut ar
, cmd
) = self.get_ar()?
;
1866 // Set an environment variable to tell the OSX archiver to ensure
1867 // that all dates listed in the archive are zero, improving
1868 // determinism of builds. AFAIK there's not really official
1869 // documentation of this but there's a lot of references to it if
1870 // you search google.
1872 // You can reproduce this locally on a mac with:
1875 // $ cc -c foo.c -o foo.o
1877 // # Notice that these two checksums are different
1878 // $ ar crus libfoo1.a foo.o && sleep 2 && ar crus libfoo2.a foo.o
1879 // $ md5sum libfoo*.a
1881 // # Notice that these two checksums are the same
1882 // $ export ZERO_AR_DATE=1
1883 // $ ar crus libfoo1.a foo.o && sleep 2 && touch foo.o && ar crus libfoo2.a foo.o
1884 // $ md5sum libfoo*.a
1886 // In any case if this doesn't end up getting read, it shouldn't
1887 // cause that many issues!
1888 ar
.env("ZERO_AR_DATE", "1");
1889 for flag
in self.ar_flags
.iter() {
1892 run(ar
.arg("cq").arg(dst
).args(objs
), &cmd
)?
;
1898 fn ios_flags(&self, cmd
: &mut Tool
) -> Result
<(), Error
> {
1900 Device(&'
static str),
1901 Simulator(&'
static str),
1902 Catalyst(&'
static str),
1905 let target
= self.get_target()?
;
1906 let arch
= target
.split('
-'
).nth(0).ok_or_else(|| {
1908 ErrorKind
::ArchitectureInvalid
,
1909 "Unknown architecture for iOS target.",
1913 let is_catalyst
= match target
.split('
-'
).nth(3) {
1914 Some(v
) => v
== "macabi",
1918 let arch
= if is_catalyst
{
1920 "arm64e" => ArchSpec
::Catalyst("arm64e"),
1921 "arm64" | "aarch64" => ArchSpec
::Catalyst("arm64"),
1922 "x86_64" => ArchSpec
::Catalyst("-m64"),
1924 return Err(Error
::new(
1925 ErrorKind
::ArchitectureInvalid
,
1926 "Unknown architecture for iOS target.",
1932 "arm" | "armv7" | "thumbv7" => ArchSpec
::Device("armv7"),
1933 "armv7s" | "thumbv7s" => ArchSpec
::Device("armv7s"),
1934 "arm64e" => ArchSpec
::Device("arm64e"),
1935 "arm64" | "aarch64" => ArchSpec
::Device("arm64"),
1936 "i386" | "i686" => ArchSpec
::Simulator("-m32"),
1937 "x86_64" => ArchSpec
::Simulator("-m64"),
1939 return Err(Error
::new(
1940 ErrorKind
::ArchitectureInvalid
,
1941 "Unknown architecture for iOS target.",
1948 std
::env
::var("IPHONEOS_DEPLOYMENT_TARGET").unwrap_or_else(|_
| "7.0".into());
1950 let sdk
= match arch
{
1951 ArchSpec
::Device(arch
) => {
1952 cmd
.args
.push("-arch".into());
1953 cmd
.args
.push(arch
.into());
1955 .push(format
!("-miphoneos-version-min={}", min_version
).into());
1958 ArchSpec
::Simulator(arch
) => {
1959 cmd
.args
.push(arch
.into());
1961 .push(format
!("-mios-simulator-version-min={}", min_version
).into());
1964 ArchSpec
::Catalyst(_
) => "macosx",
1967 self.print(&format
!("Detecting iOS SDK path for {}", sdk
));
1968 let sdk_path
= self.apple_sdk_root(sdk
)?
;
1969 cmd
.args
.push("-isysroot".into());
1970 cmd
.args
.push(sdk_path
);
1971 cmd
.args
.push("-fembed-bitcode".into());
1973 * TODO we probably ultimately want the -fembed-bitcode-marker flag
1974 * but can't have it now because of an issue in LLVM:
1975 * https://github.com/alexcrichton/cc-rs/issues/301
1976 * https://github.com/rust-lang/rust/pull/48896#comment-372192660
1979 if self.get_opt_level()? == "0" {
1980 cmd.args.push("-fembed-bitcode-marker".into());
1987 fn cmd
<P
: AsRef
<OsStr
>>(&self, prog
: P
) -> Command
{
1988 let mut cmd
= Command
::new(prog
);
1989 for &(ref a
, ref b
) in self.env
.iter() {
1995 fn get_base_compiler(&self) -> Result
<Tool
, Error
> {
1996 if let Some(ref c
) = self.compiler
{
1997 return Ok(Tool
::new(c
.clone()));
1999 let host
= self.get_host()?
;
2000 let target
= self.get_target()?
;
2001 let (env
, msvc
, gnu
, traditional
, clang
) = if self.cpp
{
2002 ("CXX", "cl.exe", "g++", "c++", "clang++")
2004 ("CC", "cl.exe", "gcc", "cc", "clang")
2007 // On historical Solaris systems, "cc" may have been Sun Studio, which
2008 // is not flag-compatible with "gcc". This history casts a long shadow,
2009 // and many modern illumos distributions today ship GCC as "gcc" without
2010 // also making it available as "cc".
2011 let default = if host
.contains("solaris") || host
.contains("illumos") {
2017 let cl_exe
= windows_registry
::find_tool(&target
, "cl.exe");
2019 let tool_opt
: Option
<Tool
> = self
2021 .map(|(tool
, wrapper
, args
)| {
2022 // find the driver mode, if any
2023 const DRIVER_MODE
: &str = "--driver-mode=";
2024 let driver_mode
= args
2026 .find(|a
| a
.starts_with(DRIVER_MODE
))
2027 .map(|a
| &a
[DRIVER_MODE
.len()..]);
2028 // Chop off leading/trailing whitespace to work around
2029 // semi-buggy build scripts which are shared in
2030 // makefiles/configure scripts (where spaces are far more
2032 let mut t
= Tool
::with_clang_driver(PathBuf
::from(tool
.trim()), driver_mode
);
2033 if let Some(cc_wrapper
) = wrapper
{
2034 t
.cc_wrapper_path
= Some(PathBuf
::from(cc_wrapper
));
2037 t
.cc_wrapper_args
.push(arg
.into());
2042 if target
.contains("emscripten") {
2043 let tool
= if self.cpp { "em++" }
else { "emcc" }
;
2044 // Windows uses bat file so we have to be a bit more specific
2046 let mut t
= Tool
::new(PathBuf
::from("cmd"));
2047 t
.args
.push("/c".into());
2048 t
.args
.push(format
!("{}.bat", tool
).into());
2051 Some(Tool
::new(PathBuf
::from(tool
)))
2057 .or_else(|| cl_exe
.clone());
2059 let tool
= match tool_opt
{
2062 let compiler
= if host
.contains("windows") && target
.contains("windows") {
2063 if target
.contains("msvc") {
2066 format
!("{}.exe", gnu
)
2068 } else if target
.contains("apple-ios") {
2070 } else if target
.contains("android") {
2071 autodetect_android_compiler(&target
, &host
, gnu
, clang
)
2072 } else if target
.contains("cloudabi") {
2073 format
!("{}-{}", target
, traditional
)
2074 } else if target
== "wasm32-wasi"
2075 || target
== "wasm32-unknown-wasi"
2076 || target
== "wasm32-unknown-unknown"
2079 } else if target
.contains("vxworks") {
2081 "wr-c++".to_string()
2085 } else if self.get_host()?
!= target
{
2086 let prefix
= self.prefix_for_target(&target
);
2088 Some(prefix
) => format
!("{}-{}", prefix
, gnu
),
2089 None
=> default.to_string(),
2095 let mut t
= Tool
::new(PathBuf
::from(compiler
));
2096 if let Some(cc_wrapper
) = Self::rustc_wrapper_fallback() {
2097 t
.cc_wrapper_path
= Some(PathBuf
::from(cc_wrapper
));
2103 let mut tool
= if self.cuda
{
2105 tool
.args
.is_empty(),
2106 "CUDA compilation currently assumes empty pre-existing args"
2108 let nvcc
= match self.get_var("NVCC") {
2109 Err(_
) => "nvcc".into(),
2112 let mut nvcc_tool
= Tool
::with_features(PathBuf
::from(nvcc
), None
, self.cuda
);
2115 .push(format
!("-ccbin={}", tool
.path
.display()).into());
2116 nvcc_tool
.family
= tool
.family
;
2122 // New "standalone" C/C++ cross-compiler executables from recent Android NDK
2123 // are just shell scripts that call main clang binary (from Android NDK) with
2124 // proper `--target` argument.
2126 // For example, armv7a-linux-androideabi16-clang passes
2127 // `--target=armv7a-linux-androideabi16` to clang.
2129 // As the shell script calls the main clang binary, the command line limit length
2130 // on Windows is restricted to around 8k characters instead of around 32k characters.
2131 // To remove this limit, we call the main clang binary directly and construct the
2132 // `--target=` ourselves.
2133 if host
.contains("windows") && android_clang_compiler_uses_target_arg_internally(&tool
.path
)
2135 if let Some(path
) = tool
.path
.file_name() {
2136 let file_name
= path
.to_str().unwrap().to_owned();
2137 let (target
, clang
) = file_name
.split_at(file_name
.rfind("-").unwrap());
2139 tool
.path
.set_file_name(clang
.trim_start_matches("-"));
2140 tool
.path
.set_extension("exe");
2141 tool
.args
.push(format
!("--target={}", target
).into());
2143 // Additionally, shell scripts for target i686-linux-android versions 16 to 24
2144 // pass the `mstackrealign` option so we do that here as well.
2145 if target
.contains("i686-linux-android") {
2146 let (_
, version
) = target
.split_at(target
.rfind("d").unwrap() + 1);
2147 if let Ok(version
) = version
.parse
::<u32>() {
2148 if version
> 15 && version
< 25 {
2149 tool
.args
.push("-mstackrealign".into());
2156 // If we found `cl.exe` in our environment, the tool we're returning is
2157 // an MSVC-like tool, *and* no env vars were set then set env vars for
2158 // the tool that we're returning.
2160 // Env vars are needed for things like `link.exe` being put into PATH as
2161 // well as header include paths sometimes. These paths are automatically
2162 // included by default but if the `CC` or `CXX` env vars are set these
2163 // won't be used. This'll ensure that when the env vars are used to
2164 // configure for invocations like `clang-cl` we still get a "works out
2165 // of the box" experience.
2166 if let Some(cl_exe
) = cl_exe
{
2167 if tool
.family
== (ToolFamily
::Msvc { clang_cl: true }
)
2168 && tool
.env
.len() == 0
2169 && target
.contains("msvc")
2171 for &(ref k
, ref v
) in cl_exe
.env
.iter() {
2172 tool
.env
.push((k
.to_owned(), v
.to_owned()));
2180 fn get_var(&self, var_base
: &str) -> Result
<String
, Error
> {
2181 let target
= self.get_target()?
;
2182 let host
= self.get_host()?
;
2183 let kind
= if host
== target { "HOST" }
else { "TARGET" }
;
2184 let target_u
= target
.replace("-", "_");
2186 .getenv(&format
!("{}_{}", var_base
, target
))
2187 .or_else(|| self.getenv(&format
!("{}_{}", var_base
, target_u
)))
2188 .or_else(|| self.getenv(&format
!("{}_{}", kind
, var_base
)))
2189 .or_else(|| self.getenv(var_base
));
2192 Some(res
) => Ok(res
),
2193 None
=> Err(Error
::new(
2194 ErrorKind
::EnvVarNotFound
,
2195 &format
!("Could not find environment variable {}.", var_base
),
2200 fn envflags(&self, name
: &str) -> Vec
<String
> {
2202 .unwrap_or(String
::new())
2203 .split_ascii_whitespace()
2204 .map(|slice
| slice
.to_string())
2208 /// Returns a fallback `cc_compiler_wrapper` by introspecting `RUSTC_WRAPPER`
2209 fn rustc_wrapper_fallback() -> Option
<String
> {
2210 // No explicit CC wrapper was detected, but check if RUSTC_WRAPPER
2211 // is defined and is a build accelerator that is compatible with
2212 // C/C++ compilers (e.g. sccache)
2213 let valid_wrappers
= ["sccache"];
2215 let rustc_wrapper
= std
::env
::var_os("RUSTC_WRAPPER")?
;
2216 let wrapper_path
= Path
::new(&rustc_wrapper
);
2217 let wrapper_stem
= wrapper_path
.file_stem()?
;
2219 if valid_wrappers
.contains(&wrapper_stem
.to_str()?
) {
2220 Some(rustc_wrapper
.to_str()?
.to_owned())
2226 /// Returns compiler path, optional modifier name from whitelist, and arguments vec
2227 fn env_tool(&self, name
: &str) -> Option
<(String
, Option
<String
>, Vec
<String
>)> {
2228 let tool
= match self.get_var(name
) {
2230 Err(_
) => return None
,
2233 // If this is an exact path on the filesystem we don't want to do any
2234 // interpretation at all, just pass it on through. This'll hopefully get
2235 // us to support spaces-in-paths.
2236 if Path
::new(&tool
).exists() {
2237 return Some((tool
, None
, Vec
::new()));
2240 // Ok now we want to handle a couple of scenarios. We'll assume from
2241 // here on out that spaces are splitting separate arguments. Two major
2242 // features we want to support are:
2246 // aka using `sccache` or any other wrapper/caching-like-thing for
2247 // compilations. We want to know what the actual compiler is still,
2248 // though, because our `Tool` API support introspection of it to see
2249 // what compiler is in use.
2251 // additionally we want to support
2255 // where the CC env var is used to also pass default flags to the C
2258 // It's true that everything here is a bit of a pain, but apparently if
2259 // you're not literally make or bash then you get a lot of bug reports.
2260 let known_wrappers
= ["ccache", "distcc", "sccache", "icecc"];
2262 let mut parts
= tool
.split_whitespace();
2263 let maybe_wrapper
= match parts
.next() {
2265 None
=> return None
,
2268 let file_stem
= Path
::new(maybe_wrapper
)
2273 if known_wrappers
.contains(&file_stem
) {
2274 if let Some(compiler
) = parts
.next() {
2276 compiler
.to_string(),
2277 Some(maybe_wrapper
.to_string()),
2278 parts
.map(|s
| s
.to_string()).collect(),
2284 maybe_wrapper
.to_string(),
2285 Self::rustc_wrapper_fallback(),
2286 parts
.map(|s
| s
.to_string()).collect(),
2290 /// Returns the C++ standard library:
2291 /// 1. If [cpp_link_stdlib](cc::Build::cpp_link_stdlib) is set, uses its value.
2292 /// 2. Else if the `CXXSTDLIB` environment variable is set, uses its value.
2293 /// 3. Else the default is `libc++` for OS X and BSDs, `libc++_shared` for Android,
2294 /// `None` for MSVC and `libstdc++` for anything else.
2295 fn get_cpp_link_stdlib(&self) -> Result
<Option
<String
>, Error
> {
2296 match self.cpp_link_stdlib
.clone() {
2299 if let Ok(stdlib
) = self.get_var("CXXSTDLIB") {
2300 if stdlib
.is_empty() {
2306 let target
= self.get_target()?
;
2307 if target
.contains("msvc") {
2309 } else if target
.contains("apple") {
2310 Ok(Some("c++".to_string()))
2311 } else if target
.contains("freebsd") {
2312 Ok(Some("c++".to_string()))
2313 } else if target
.contains("openbsd") {
2314 Ok(Some("c++".to_string()))
2315 } else if target
.contains("android") {
2316 Ok(Some("c++_shared".to_string()))
2318 Ok(Some("stdc++".to_string()))
2325 fn get_ar(&self) -> Result
<(Command
, String
), Error
> {
2326 if let Some(ref p
) = self.archiver
{
2327 let name
= p
.file_name().and_then(|s
| s
.to_str()).unwrap_or("ar");
2328 return Ok((self.cmd(p
), name
.to_string()));
2330 if let Ok(p
) = self.get_var("AR") {
2331 return Ok((self.cmd(&p
), p
));
2333 let target
= self.get_target()?
;
2334 let default_ar
= "ar".to_string();
2335 let program
= if target
.contains("android") {
2336 format
!("{}-ar", target
.replace("armv7", "arm"))
2337 } else if target
.contains("emscripten") {
2338 // Windows use bat files so we have to be a bit more specific
2340 let mut cmd
= self.cmd("cmd");
2341 cmd
.arg("/c").arg("emar.bat");
2342 return Ok((cmd
, "emar.bat".to_string()));
2346 } else if target
.contains("msvc") {
2347 match windows_registry
::find(&target
, "lib.exe") {
2348 Some(t
) => return Ok((t
, "lib.exe".to_string())),
2349 None
=> "lib.exe".to_string(),
2351 } else if target
.contains("illumos") {
2352 // The default 'ar' on illumos uses a non-standard flags,
2353 // but the OS comes bundled with a GNU-compatible variant.
2355 // Use the GNU-variant to match other Unix systems.
2357 } else if self.get_host()?
!= target
{
2358 match self.prefix_for_target(&target
) {
2360 let target_ar
= format
!("{}-ar", p
);
2361 if Command
::new(&target_ar
).output().is_ok() {
2372 Ok((self.cmd(&program
), program
))
2375 fn prefix_for_target(&self, target
: &str) -> Option
<String
> {
2376 // CROSS_COMPILE is of the form: "arm-linux-gnueabi-"
2377 let cc_env
= self.getenv("CROSS_COMPILE");
2378 let cross_compile
= cc_env
2380 .map(|s
| s
.trim_right_matches('
-'
).to_owned());
2381 cross_compile
.or(match &target
[..] {
2382 "aarch64-unknown-linux-gnu" => Some("aarch64-linux-gnu"),
2383 "aarch64-unknown-linux-musl" => Some("aarch64-linux-musl"),
2384 "aarch64-unknown-netbsd" => Some("aarch64--netbsd"),
2385 "arm-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"),
2386 "armv4t-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"),
2387 "armv5te-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"),
2388 "armv5te-unknown-linux-musleabi" => Some("arm-linux-gnueabi"),
2389 "arm-frc-linux-gnueabi" => Some("arm-frc-linux-gnueabi"),
2390 "arm-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"),
2391 "arm-unknown-linux-musleabi" => Some("arm-linux-musleabi"),
2392 "arm-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"),
2393 "arm-unknown-netbsd-eabi" => Some("arm--netbsdelf-eabi"),
2394 "armv6-unknown-netbsd-eabihf" => Some("armv6--netbsdelf-eabihf"),
2395 "armv7-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"),
2396 "armv7-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"),
2397 "armv7-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"),
2398 "armv7neon-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"),
2399 "armv7neon-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"),
2400 "thumbv7-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"),
2401 "thumbv7-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"),
2402 "thumbv7neon-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"),
2403 "thumbv7neon-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"),
2404 "armv7-unknown-netbsd-eabihf" => Some("armv7--netbsdelf-eabihf"),
2405 "hexagon-unknown-linux-musl" => Some("hexagon-linux-musl"),
2406 "i586-unknown-linux-musl" => Some("musl"),
2407 "i686-pc-windows-gnu" => Some("i686-w64-mingw32"),
2408 "i686-uwp-windows-gnu" => Some("i686-w64-mingw32"),
2409 "i686-unknown-linux-musl" => Some("musl"),
2410 "i686-unknown-netbsd" => Some("i486--netbsdelf"),
2411 "mips-unknown-linux-gnu" => Some("mips-linux-gnu"),
2412 "mips-unknown-linux-musl" => Some("mips-linux-musl"),
2413 "mipsel-unknown-linux-gnu" => Some("mipsel-linux-gnu"),
2414 "mipsel-unknown-linux-musl" => Some("mipsel-linux-musl"),
2415 "mips64-unknown-linux-gnuabi64" => Some("mips64-linux-gnuabi64"),
2416 "mips64el-unknown-linux-gnuabi64" => Some("mips64el-linux-gnuabi64"),
2417 "mipsisa32r6-unknown-linux-gnu" => Some("mipsisa32r6-linux-gnu"),
2418 "mipsisa32r6el-unknown-linux-gnu" => Some("mipsisa32r6el-linux-gnu"),
2419 "mipsisa64r6-unknown-linux-gnuabi64" => Some("mipsisa64r6-linux-gnuabi64"),
2420 "mipsisa64r6el-unknown-linux-gnuabi64" => Some("mipsisa64r6el-linux-gnuabi64"),
2421 "powerpc-unknown-linux-gnu" => Some("powerpc-linux-gnu"),
2422 "powerpc-unknown-linux-gnuspe" => Some("powerpc-linux-gnuspe"),
2423 "powerpc-unknown-netbsd" => Some("powerpc--netbsd"),
2424 "powerpc64-unknown-linux-gnu" => Some("powerpc-linux-gnu"),
2425 "powerpc64le-unknown-linux-gnu" => Some("powerpc64le-linux-gnu"),
2426 "riscv32i-unknown-none-elf" => self.find_working_gnu_prefix(&[
2427 "riscv32-unknown-elf",
2428 "riscv64-unknown-elf",
2431 "riscv32imac-unknown-none-elf" => self.find_working_gnu_prefix(&[
2432 "riscv32-unknown-elf",
2433 "riscv64-unknown-elf",
2436 "riscv32imc-unknown-none-elf" => self.find_working_gnu_prefix(&[
2437 "riscv32-unknown-elf",
2438 "riscv64-unknown-elf",
2441 "riscv64gc-unknown-none-elf" => self.find_working_gnu_prefix(&[
2442 "riscv64-unknown-elf",
2443 "riscv32-unknown-elf",
2446 "riscv64imac-unknown-none-elf" => self.find_working_gnu_prefix(&[
2447 "riscv64-unknown-elf",
2448 "riscv32-unknown-elf",
2451 "riscv64gc-unknown-linux-gnu" => Some("riscv64-linux-gnu"),
2452 "riscv32gc-unknown-linux-gnu" => Some("riscv32-linux-gnu"),
2453 "riscv64gc-unknown-linux-musl" => Some("riscv64-linux-musl"),
2454 "riscv32gc-unknown-linux-musl" => Some("riscv32-linux-musl"),
2455 "s390x-unknown-linux-gnu" => Some("s390x-linux-gnu"),
2456 "sparc-unknown-linux-gnu" => Some("sparc-linux-gnu"),
2457 "sparc64-unknown-linux-gnu" => Some("sparc64-linux-gnu"),
2458 "sparc64-unknown-netbsd" => Some("sparc64--netbsd"),
2459 "sparcv9-sun-solaris" => Some("sparcv9-sun-solaris"),
2460 "armv7a-none-eabi" => Some("arm-none-eabi"),
2461 "armv7a-none-eabihf" => Some("arm-none-eabi"),
2462 "armebv7r-none-eabi" => Some("arm-none-eabi"),
2463 "armebv7r-none-eabihf" => Some("arm-none-eabi"),
2464 "armv7r-none-eabi" => Some("arm-none-eabi"),
2465 "armv7r-none-eabihf" => Some("arm-none-eabi"),
2466 "thumbv6m-none-eabi" => Some("arm-none-eabi"),
2467 "thumbv7em-none-eabi" => Some("arm-none-eabi"),
2468 "thumbv7em-none-eabihf" => Some("arm-none-eabi"),
2469 "thumbv7m-none-eabi" => Some("arm-none-eabi"),
2470 "thumbv8m.base-none-eabi" => Some("arm-none-eabi"),
2471 "thumbv8m.main-none-eabi" => Some("arm-none-eabi"),
2472 "thumbv8m.main-none-eabihf" => Some("arm-none-eabi"),
2473 "x86_64-pc-windows-gnu" => Some("x86_64-w64-mingw32"),
2474 "x86_64-uwp-windows-gnu" => Some("x86_64-w64-mingw32"),
2475 "x86_64-rumprun-netbsd" => Some("x86_64-rumprun-netbsd"),
2476 "x86_64-unknown-linux-musl" => Some("musl"),
2477 "x86_64-unknown-netbsd" => Some("x86_64--netbsd"),
2480 .map(|x
| x
.to_owned()))
2483 /// Some platforms have multiple, compatible, canonical prefixes. Look through
2484 /// each possible prefix for a compiler that exists and return it. The prefixes
2485 /// should be ordered from most-likely to least-likely.
2486 fn find_working_gnu_prefix(&self, prefixes
: &[&'
static str]) -> Option
<&'
static str> {
2487 let suffix
= if self.cpp { "-g++" }
else { "-gcc" }
;
2488 let extension
= std
::env
::consts
::EXE_SUFFIX
;
2490 // Loop through PATH entries searching for each toolchain. This ensures that we
2491 // are more likely to discover the toolchain early on, because chances are good
2492 // that the desired toolchain is in one of the higher-priority paths.
2495 .and_then(|path_entries
| {
2496 env
::split_paths(path_entries
).find_map(|path_entry
| {
2497 for prefix
in prefixes
{
2498 let target_compiler
= format
!("{}{}{}", prefix
, suffix
, extension
);
2499 if path_entry
.join(&target_compiler
).exists() {
2500 return Some(prefix
);
2506 .map(|prefix
| *prefix
)
2508 // If no toolchain was found, provide the first toolchain that was passed in.
2509 // This toolchain has been shown not to exist, however it will appear in the
2510 // error that is shown to the user which should make it easier to search for
2511 // where it should be obtained.
2512 prefixes
.first().map(|prefix
| *prefix
))
2515 fn get_target(&self) -> Result
<String
, Error
> {
2516 match self.target
.clone() {
2518 None
=> Ok(self.getenv_unwrap("TARGET")?
),
2522 fn get_host(&self) -> Result
<String
, Error
> {
2523 match self.host
.clone() {
2525 None
=> Ok(self.getenv_unwrap("HOST")?
),
2529 fn get_opt_level(&self) -> Result
<String
, Error
> {
2530 match self.opt_level
.as_ref().cloned() {
2532 None
=> Ok(self.getenv_unwrap("OPT_LEVEL")?
),
2536 fn get_debug(&self) -> bool
{
2537 self.debug
.unwrap_or_else(|| match self.getenv("DEBUG") {
2538 Some(s
) => s
!= "false",
2543 fn get_force_frame_pointer(&self) -> bool
{
2544 self.force_frame_pointer
.unwrap_or_else(|| self.get_debug())
2547 fn get_out_dir(&self) -> Result
<PathBuf
, Error
> {
2548 match self.out_dir
.clone() {
2550 None
=> Ok(env
::var_os("OUT_DIR").map(PathBuf
::from
).ok_or_else(|| {
2552 ErrorKind
::EnvVarNotFound
,
2553 "Environment variable OUT_DIR not defined.",
2559 fn getenv(&self, v
: &str) -> Option
<String
> {
2560 let mut cache
= self.env_cache
.lock().unwrap();
2561 if let Some(val
) = cache
.get(v
) {
2564 let r
= env
::var(v
).ok();
2565 self.print(&format
!("{} = {:?}", v
, r
));
2566 cache
.insert(v
.to_string(), r
.clone());
2570 fn getenv_unwrap(&self, v
: &str) -> Result
<String
, Error
> {
2571 match self.getenv(v
) {
2573 None
=> Err(Error
::new(
2574 ErrorKind
::EnvVarNotFound
,
2575 &format
!("Environment variable {} not defined.", v
.to_string()),
2580 fn print(&self, s
: &str) {
2581 if self.cargo_metadata
{
2586 fn fix_env_for_apple_os(&self, cmd
: &mut Command
) -> Result
<(), Error
> {
2587 let target
= self.get_target()?
;
2588 let host
= self.get_host()?
;
2589 if host
.contains("apple-darwin") && target
.contains("apple-darwin") {
2590 // If, for example, `cargo` runs during the build of an XCode project, then `SDKROOT` environment variable
2591 // would represent the current target, and this is the problem for us, if we want to compile something
2592 // for the host, when host != target.
2593 // We can not just remove `SDKROOT`, because, again, for example, XCode add to PATH
2594 // /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
2595 // and `cc` from this path can not find system include files, like `pthread.h`, if `SDKROOT`
2597 if let Ok(sdkroot
) = env
::var("SDKROOT") {
2598 if !sdkroot
.contains("MacOSX") {
2599 let macos_sdk
= self.apple_sdk_root("macosx")?
;
2600 cmd
.env("SDKROOT", macos_sdk
);
2603 // Additionally, `IPHONEOS_DEPLOYMENT_TARGET` must not be set when using the Xcode linker at
2604 // "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld",
2605 // although this is apparently ignored when using the linker at "/usr/bin/ld".
2606 cmd
.env_remove("IPHONEOS_DEPLOYMENT_TARGET");
2611 fn apple_sdk_root(&self, sdk
: &str) -> Result
<OsString
, Error
> {
2612 let mut cache
= self
2613 .apple_sdk_root_cache
2615 .expect("apple_sdk_root_cache lock failed");
2616 if let Some(ret
) = cache
.get(sdk
) {
2617 return Ok(ret
.clone());
2620 let sdk_path
= run_output(
2622 .arg("--show-sdk-path")
2628 let sdk_path
= match String
::from_utf8(sdk_path
) {
2631 return Err(Error
::new(
2633 "Unable to determine iOS SDK path.",
2637 let ret
: OsString
= sdk_path
.trim().into();
2638 cache
.insert(sdk
.into(), ret
.clone());
2643 impl Default
for Build
{
2644 fn default() -> Build
{
2650 fn new(path
: PathBuf
) -> Self {
2651 Tool
::with_features(path
, None
, false)
2654 fn with_clang_driver(path
: PathBuf
, clang_driver
: Option
<&str>) -> Self {
2655 Self::with_features(path
, clang_driver
, false)
2659 /// Explicitly set the `ToolFamily`, skipping name-based detection.
2660 fn with_family(path
: PathBuf
, family
: ToolFamily
) -> Self {
2663 cc_wrapper_path
: None
,
2664 cc_wrapper_args
: Vec
::new(),
2669 removed_args
: Vec
::new(),
2673 fn with_features(path
: PathBuf
, clang_driver
: Option
<&str>, cuda
: bool
) -> Self {
2674 // Try to detect family of the tool from its name, falling back to Gnu.
2675 let family
= if let Some(fname
) = path
.file_name().and_then(|p
| p
.to_str()) {
2676 if fname
.contains("clang-cl") {
2677 ToolFamily
::Msvc { clang_cl: true }
2678 } else if fname
.ends_with("cl") || fname
== "cl.exe" {
2679 ToolFamily
::Msvc { clang_cl: false }
2680 } else if fname
.contains("clang") {
2681 match clang_driver
{
2682 Some("cl") => ToolFamily
::Msvc { clang_cl: true }
,
2683 _
=> ToolFamily
::Clang
,
2694 cc_wrapper_path
: None
,
2695 cc_wrapper_args
: Vec
::new(),
2700 removed_args
: Vec
::new(),
2704 /// Add an argument to be stripped from the final command arguments.
2705 fn remove_arg(&mut self, flag
: OsString
) {
2706 self.removed_args
.push(flag
);
2709 /// Add a flag, and optionally prepend the NVCC wrapper flag "-Xcompiler".
2711 /// Currently this is only used for compiling CUDA sources, since NVCC only
2712 /// accepts a limited set of GNU-like flags, and the rest must be prefixed
2713 /// with a "-Xcompiler" flag to get passed to the underlying C++ compiler.
2714 fn push_cc_arg(&mut self, flag
: OsString
) {
2716 self.args
.push("-Xcompiler".into());
2718 self.args
.push(flag
);
2721 fn is_duplicate_opt_arg(&self, flag
: &OsString
) -> bool
{
2722 let flag
= flag
.to_str().unwrap();
2723 let mut chars
= flag
.chars();
2725 // Only duplicate check compiler flags
2726 if self.is_like_msvc() {
2727 if chars
.next() != Some('
/'
) {
2730 } else if self.is_like_gnu() || self.is_like_clang() {
2731 if chars
.next() != Some('
-'
) {
2736 // Check for existing optimization flags (-O, /O)
2737 if chars
.next() == Some('O'
) {
2741 .any(|ref a
| a
.to_str().unwrap_or("").chars().nth(1) == Some('O'
));
2744 // TODO Check for existing -m..., -m...=..., /arch:... flags
2748 /// Don't push optimization arg if it conflicts with existing args
2749 fn push_opt_unless_duplicate(&mut self, flag
: OsString
) {
2750 if self.is_duplicate_opt_arg(&flag
) {
2751 println
!("Info: Ignoring duplicate arg {:?}", &flag
);
2753 self.push_cc_arg(flag
);
2757 /// Converts this compiler into a `Command` that's ready to be run.
2759 /// This is useful for when the compiler needs to be executed and the
2760 /// command returned will already have the initial arguments and environment
2761 /// variables configured.
2762 pub fn to_command(&self) -> Command
{
2763 let mut cmd
= match self.cc_wrapper_path
{
2764 Some(ref cc_wrapper_path
) => {
2765 let mut cmd
= Command
::new(&cc_wrapper_path
);
2766 cmd
.arg(&self.path
);
2769 None
=> Command
::new(&self.path
),
2771 cmd
.args(&self.cc_wrapper_args
);
2776 .filter(|a
| !self.removed_args
.contains(a
))
2777 .collect
::<Vec
<_
>>();
2780 for &(ref k
, ref v
) in self.env
.iter() {
2786 /// Returns the path for this compiler.
2788 /// Note that this may not be a path to a file on the filesystem, e.g. "cc",
2789 /// but rather something which will be resolved when a process is spawned.
2790 pub fn path(&self) -> &Path
{
2794 /// Returns the default set of arguments to the compiler needed to produce
2795 /// executables for the target this compiler generates.
2796 pub fn args(&self) -> &[OsString
] {
2800 /// Returns the set of environment variables needed for this compiler to
2803 /// This is typically only used for MSVC compilers currently.
2804 pub fn env(&self) -> &[(OsString
, OsString
)] {
2808 /// Returns the compiler command in format of CC environment variable.
2809 /// Or empty string if CC env was not present
2811 /// This is typically used by configure script
2812 pub fn cc_env(&self) -> OsString
{
2813 match self.cc_wrapper_path
{
2814 Some(ref cc_wrapper_path
) => {
2815 let mut cc_env
= cc_wrapper_path
.as_os_str().to_owned();
2817 cc_env
.push(self.path
.to_path_buf().into_os_string());
2818 for arg
in self.cc_wrapper_args
.iter() {
2824 None
=> OsString
::from(""),
2828 /// Returns the compiler flags in format of CFLAGS environment variable.
2829 /// Important here - this will not be CFLAGS from env, its internal gcc's flags to use as CFLAGS
2830 /// This is typically used by configure script
2831 pub fn cflags_env(&self) -> OsString
{
2832 let mut flags
= OsString
::new();
2833 for (i
, arg
) in self.args
.iter().enumerate() {
2842 /// Whether the tool is GNU Compiler Collection-like.
2843 pub fn is_like_gnu(&self) -> bool
{
2844 self.family
== ToolFamily
::Gnu
2847 /// Whether the tool is Clang-like.
2848 pub fn is_like_clang(&self) -> bool
{
2849 self.family
== ToolFamily
::Clang
2852 /// Whether the tool is MSVC-like.
2853 pub fn is_like_msvc(&self) -> bool
{
2855 ToolFamily
::Msvc { .. }
=> true,
2861 fn run(cmd
: &mut Command
, program
: &str) -> Result
<(), Error
> {
2862 let (mut child
, print
) = spawn(cmd
, program
)?
;
2863 let status
= match child
.wait() {
2866 return Err(Error
::new(
2867 ErrorKind
::ToolExecError
,
2869 "Failed to wait on spawned child process, command {:?} with args {:?}.",
2875 print
.join().unwrap();
2876 println
!("{}", status
);
2878 if status
.success() {
2882 ErrorKind
::ToolExecError
,
2884 "Command {:?} with args {:?} did not execute successfully (status code {}).",
2885 cmd
, program
, status
2891 fn run_output(cmd
: &mut Command
, program
: &str) -> Result
<Vec
<u8>, Error
> {
2892 cmd
.stdout(Stdio
::piped());
2893 let (mut child
, print
) = spawn(cmd
, program
)?
;
2894 let mut stdout
= vec
![];
2899 .read_to_end(&mut stdout
)
2901 let status
= match child
.wait() {
2904 return Err(Error
::new(
2905 ErrorKind
::ToolExecError
,
2907 "Failed to wait on spawned child process, command {:?} with args {:?}.",
2913 print
.join().unwrap();
2914 println
!("{}", status
);
2916 if status
.success() {
2920 ErrorKind
::ToolExecError
,
2922 "Command {:?} with args {:?} did not execute successfully (status code {}).",
2923 cmd
, program
, status
2929 fn spawn(cmd
: &mut Command
, program
: &str) -> Result
<(Child
, JoinHandle
<()>), Error
> {
2930 println
!("running: {:?}", cmd
);
2932 // Capture the standard error coming from these programs, and write it out
2933 // with cargo:warning= prefixes. Note that this is a bit wonky to avoid
2934 // requiring the output to be UTF-8, we instead just ship bytes from one
2935 // location to another.
2936 match cmd
.stderr(Stdio
::piped()).spawn() {
2938 let stderr
= BufReader
::new(child
.stderr
.take().unwrap());
2939 let print
= thread
::spawn(move || {
2940 for line
in stderr
.split(b'
\n'
).filter_map(|l
| l
.ok()) {
2941 print
!("cargo:warning=");
2942 std
::io
::stdout().write_all(&line
).unwrap();
2948 Err(ref e
) if e
.kind() == io
::ErrorKind
::NotFound
=> {
2949 let extra
= if cfg
!(windows
) {
2950 " (see https://github.com/alexcrichton/cc-rs#compile-time-requirements \
2956 ErrorKind
::ToolNotFound
,
2957 &format
!("Failed to find tool. Is `{}` installed?{}", program
, extra
),
2960 Err(_
) => Err(Error
::new(
2961 ErrorKind
::ToolExecError
,
2962 &format
!("Command {:?} with args {:?} failed to start.", cmd
, program
),
2967 fn fail(s
: &str) -> ! {
2968 eprintln
!("\n\nerror occurred: {}\n\n", s
);
2969 std
::process
::exit(1);
2972 fn command_add_output_file(
2981 if msvc
&& !clang
&& !cuda
&& !(is_asm
&& is_arm
) {
2982 let mut s
= OsString
::from("-Fo");
2986 cmd
.arg("-o").arg(&dst
);
2990 // Use by default minimum available API level
2991 // See note about naming here
2992 // https://android.googlesource.com/platform/ndk/+/refs/heads/ndk-release-r21/docs/BuildSystemMaintainers.md#Clang
2993 static NEW_STANDALONE_ANDROID_COMPILERS
: [&str; 4] = [
2994 "aarch64-linux-android21-clang",
2995 "armv7a-linux-androideabi16-clang",
2996 "i686-linux-android16-clang",
2997 "x86_64-linux-android21-clang",
3000 // New "standalone" C/C++ cross-compiler executables from recent Android NDK
3001 // are just shell scripts that call main clang binary (from Android NDK) with
3002 // proper `--target` argument.
3004 // For example, armv7a-linux-androideabi16-clang passes
3005 // `--target=armv7a-linux-androideabi16` to clang.
3006 // So to construct proper command line check if
3007 // `--target` argument would be passed or not to clang
3008 fn android_clang_compiler_uses_target_arg_internally(clang_path
: &Path
) -> bool
{
3009 if let Some(filename
) = clang_path
.file_name() {
3010 if let Some(filename_str
) = filename
.to_str() {
3011 filename_str
.contains("android")
3021 fn test_android_clang_compiler_uses_target_arg_internally() {
3022 for version
in 16..21 {
3023 assert
!(android_clang_compiler_uses_target_arg_internally(
3024 &PathBuf
::from(format
!("armv7a-linux-androideabi{}-clang", version
))
3026 assert
!(android_clang_compiler_uses_target_arg_internally(
3027 &PathBuf
::from(format
!("armv7a-linux-androideabi{}-clang++", version
))
3030 assert
!(!android_clang_compiler_uses_target_arg_internally(
3031 &PathBuf
::from("clang")
3033 assert
!(!android_clang_compiler_uses_target_arg_internally(
3034 &PathBuf
::from("clang++")
3038 fn autodetect_android_compiler(target
: &str, host
: &str, gnu
: &str, clang
: &str) -> String
{
3039 let new_clang_key
= match target
{
3040 "aarch64-linux-android" => Some("aarch64"),
3041 "armv7-linux-androideabi" => Some("armv7a"),
3042 "i686-linux-android" => Some("i686"),
3043 "x86_64-linux-android" => Some("x86_64"),
3047 let new_clang
= new_clang_key
3049 NEW_STANDALONE_ANDROID_COMPILERS
3051 .find(|x
| x
.starts_with(key
))
3055 if let Some(new_clang
) = new_clang
{
3056 if Command
::new(new_clang
).output().is_ok() {
3057 return (*new_clang
).into();
3062 .replace("armv7neon", "arm")
3063 .replace("armv7", "arm")
3064 .replace("thumbv7neon", "arm")
3065 .replace("thumbv7", "arm");
3066 let gnu_compiler
= format
!("{}-{}", target
, gnu
);
3067 let clang_compiler
= format
!("{}-{}", target
, clang
);
3069 // On Windows, the Android clang compiler is provided as a `.cmd` file instead
3070 // of a `.exe` file. `std::process::Command` won't run `.cmd` files unless the
3071 // `.cmd` is explicitly appended to the command name, so we do that here.
3072 let clang_compiler_cmd
= format
!("{}-{}.cmd", target
, clang
);
3074 // Check if gnu compiler is present
3075 // if not, use clang
3076 if Command
::new(&gnu_compiler
).output().is_ok() {
3078 } else if host
.contains("windows") && Command
::new(&clang_compiler_cmd
).output().is_ok() {
3085 // Rust and clang/cc don't agree on how to name the target.
3086 fn map_darwin_target_from_rust_to_compiler_architecture(target
: &str) -> Option
<&'
static str> {
3087 if target
.contains("x86_64") {
3089 } else if target
.contains("arm64e") {
3091 } else if target
.contains("aarch64") {
3093 } else if target
.contains("i686") {
3095 } else if target
.contains("powerpc") {
3097 } else if target
.contains("powerpc64") {