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