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