]> git.proxmox.com Git - rustc.git/blame - src/vendor/gcc/src/lib.rs
New upstream version 1.19.0+dfsg3
[rustc.git] / src / vendor / gcc / src / lib.rs
CommitLineData
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]
8//! gcc = "0.3"
9//! ```
10//!
11//! The purpose of this crate is to provide the utility functions necessary to
12//! compile C code into a static archive which is then linked into a Rust crate.
13//! The top-level `compile_library` function serves as a convenience and more
14//! advanced configuration is available through the `Config` builder.
15//!
16//! This crate will automatically detect situations such as cross compilation or
17//! other environment variables set by Cargo and will build code appropriately.
18//!
19//! # Examples
20//!
21//! Use the default configuration:
22//!
23//! ```no_run
24//! extern crate gcc;
25//!
26//! fn main() {
27//! gcc::compile_library("libfoo.a", &["src/foo.c"]);
28//! }
29//! ```
30//!
31//! Use more advanced configuration:
32//!
33//! ```no_run
34//! extern crate gcc;
35//!
36//! fn main() {
37//! gcc::Config::new()
38//! .file("src/foo.c")
39//! .define("FOO", Some("bar"))
40//! .include("src")
41//! .compile("libfoo.a");
42//! }
43//! ```
44
8bb4bdeb 45#![doc(html_root_url = "https://docs.rs/gcc/0.3")]
476ff2be
SL
46#![cfg_attr(test, deny(warnings))]
47#![deny(missing_docs)]
48
49#[cfg(feature = "parallel")]
50extern crate rayon;
51
52use std::env;
53use std::ffi::{OsString, OsStr};
54use std::fs;
476ff2be 55use std::path::{PathBuf, Path};
cc61c64b 56use std::process::{Command, Stdio, Child};
8bb4bdeb 57use std::io::{self, BufReader, BufRead, Read, Write};
cc61c64b 58use std::thread::{self, JoinHandle};
476ff2be 59
7cac9316
XL
60// These modules are all glue to support reading the MSVC version from
61// the registry and from COM interfaces
476ff2be
SL
62#[cfg(windows)]
63mod registry;
7cac9316
XL
64#[cfg(windows)]
65#[macro_use]
66mod winapi;
67#[cfg(windows)]
68mod com;
69#[cfg(windows)]
70mod setup_config;
71
476ff2be
SL
72pub mod windows_registry;
73
74/// Extra configuration to pass to gcc.
75pub struct Config {
76 include_directories: Vec<PathBuf>,
77 definitions: Vec<(String, Option<String>)>,
78 objects: Vec<PathBuf>,
79 flags: Vec<String>,
80 files: Vec<PathBuf>,
81 cpp: bool,
82 cpp_link_stdlib: Option<Option<String>>,
83 cpp_set_stdlib: Option<String>,
84 target: Option<String>,
85 host: Option<String>,
86 out_dir: Option<PathBuf>,
87 opt_level: Option<String>,
88 debug: Option<bool>,
89 env: Vec<(OsString, OsString)>,
90 compiler: Option<PathBuf>,
91 archiver: Option<PathBuf>,
92 cargo_metadata: bool,
93 pic: Option<bool>,
7cac9316 94 static_crt: Option<bool>,
476ff2be
SL
95}
96
97/// Configuration used to represent an invocation of a C compiler.
98///
99/// This can be used to figure out what compiler is in use, what the arguments
100/// to it are, and what the environment variables look like for the compiler.
101/// This can be used to further configure other build systems (e.g. forward
102/// along CC and/or CFLAGS) or the `to_command` method can be used to run the
103/// compiler itself.
104pub struct Tool {
105 path: PathBuf,
106 args: Vec<OsString>,
107 env: Vec<(OsString, OsString)>,
8bb4bdeb
XL
108 family: ToolFamily
109}
110
111/// Represents the family of tools this tool belongs to.
112///
113/// Each family of tools differs in how and what arguments they accept.
114///
115/// Detection of a family is done on best-effort basis and may not accurately reflect the tool.
116#[derive(Copy, Clone, Debug)]
117enum ToolFamily {
118 /// Tool is GNU Compiler Collection-like.
119 Gnu,
120 /// Tool is Clang-like. It differs from the GCC in a sense that it accepts superset of flags
121 /// and its cross-compilation approach is different.
122 Clang,
123 /// Tool is the MSVC cl.exe.
124 Msvc,
125}
126
127impl ToolFamily {
128 /// What the flag to request debug info for this family of tools look like
129 fn debug_flag(&self) -> &'static str {
130 match *self {
131 ToolFamily::Msvc => "/Z7",
132 ToolFamily::Gnu |
133 ToolFamily::Clang => "-g",
134 }
135 }
136
137 /// What the flag to include directories into header search path looks like
138 fn include_flag(&self) -> &'static str {
139 match *self {
140 ToolFamily::Msvc => "/I",
141 ToolFamily::Gnu |
142 ToolFamily::Clang => "-I",
143 }
144 }
145
146 /// What the flag to request macro-expanded source output looks like
147 fn expand_flag(&self) -> &'static str {
148 match *self {
149 ToolFamily::Msvc => "/E",
150 ToolFamily::Gnu |
151 ToolFamily::Clang => "-E",
152 }
153 }
476ff2be
SL
154}
155
156/// Compile a library from the given set of input C files.
157///
158/// This will simply compile all files into object files and then assemble them
159/// into the output. This will read the standard environment variables to detect
160/// cross compilations and such.
161///
162/// This function will also print all metadata on standard output for Cargo.
163///
164/// # Example
165///
166/// ```no_run
167/// gcc::compile_library("libfoo.a", &["foo.c", "bar.c"]);
168/// ```
169pub fn compile_library(output: &str, files: &[&str]) {
170 let mut c = Config::new();
171 for f in files.iter() {
172 c.file(*f);
173 }
8bb4bdeb 174 c.compile(output);
476ff2be
SL
175}
176
177impl Config {
178 /// Construct a new instance of a blank set of configuration.
179 ///
180 /// This builder is finished with the `compile` function.
181 pub fn new() -> Config {
182 Config {
183 include_directories: Vec::new(),
184 definitions: Vec::new(),
185 objects: Vec::new(),
186 flags: Vec::new(),
187 files: Vec::new(),
188 cpp: false,
189 cpp_link_stdlib: None,
190 cpp_set_stdlib: None,
191 target: None,
192 host: None,
193 out_dir: None,
194 opt_level: None,
195 debug: None,
196 env: Vec::new(),
197 compiler: None,
198 archiver: None,
199 cargo_metadata: true,
200 pic: None,
7cac9316 201 static_crt: None,
476ff2be
SL
202 }
203 }
204
205 /// Add a directory to the `-I` or include path for headers
206 pub fn include<P: AsRef<Path>>(&mut self, dir: P) -> &mut Config {
207 self.include_directories.push(dir.as_ref().to_path_buf());
208 self
209 }
210
211 /// Specify a `-D` variable with an optional value.
212 pub fn define(&mut self, var: &str, val: Option<&str>) -> &mut Config {
213 self.definitions.push((var.to_string(), val.map(|s| s.to_string())));
214 self
215 }
216
217 /// Add an arbitrary object file to link in
218 pub fn object<P: AsRef<Path>>(&mut self, obj: P) -> &mut Config {
219 self.objects.push(obj.as_ref().to_path_buf());
220 self
221 }
222
223 /// Add an arbitrary flag to the invocation of the compiler
224 pub fn flag(&mut self, flag: &str) -> &mut Config {
225 self.flags.push(flag.to_string());
226 self
227 }
228
229 /// Add a file which will be compiled
230 pub fn file<P: AsRef<Path>>(&mut self, p: P) -> &mut Config {
231 self.files.push(p.as_ref().to_path_buf());
232 self
233 }
234
235 /// Set C++ support.
236 ///
237 /// The other `cpp_*` options will only become active if this is set to
238 /// `true`.
239 pub fn cpp(&mut self, cpp: bool) -> &mut Config {
240 self.cpp = cpp;
241 self
242 }
243
244 /// Set the standard library to link against when compiling with C++
245 /// support.
246 ///
247 /// The default value of this property depends on the current target: On
248 /// OS X `Some("c++")` is used, when compiling for a Visual Studio based
249 /// target `None` is used and for other targets `Some("stdc++")` is used.
250 ///
251 /// A value of `None` indicates that no automatic linking should happen,
252 /// otherwise cargo will link against the specified library.
253 ///
254 /// The given library name must not contain the `lib` prefix.
8bb4bdeb 255 pub fn cpp_link_stdlib(&mut self, cpp_link_stdlib: Option<&str>) -> &mut Config {
476ff2be
SL
256 self.cpp_link_stdlib = Some(cpp_link_stdlib.map(|s| s.into()));
257 self
258 }
259
260 /// Force the C++ compiler to use the specified standard library.
261 ///
262 /// Setting this option will automatically set `cpp_link_stdlib` to the same
263 /// value.
264 ///
265 /// The default value of this option is always `None`.
266 ///
267 /// This option has no effect when compiling for a Visual Studio based
268 /// target.
269 ///
270 /// This option sets the `-stdlib` flag, which is only supported by some
271 /// compilers (clang, icc) but not by others (gcc). The library will not
272 /// detect which compiler is used, as such it is the responsibility of the
273 /// caller to ensure that this option is only used in conjuction with a
274 /// compiler which supports the `-stdlib` flag.
275 ///
276 /// A value of `None` indicates that no specific C++ standard library should
277 /// be used, otherwise `-stdlib` is added to the compile invocation.
278 ///
279 /// The given library name must not contain the `lib` prefix.
8bb4bdeb 280 pub fn cpp_set_stdlib(&mut self, cpp_set_stdlib: Option<&str>) -> &mut Config {
476ff2be
SL
281 self.cpp_set_stdlib = cpp_set_stdlib.map(|s| s.into());
282 self.cpp_link_stdlib(cpp_set_stdlib);
283 self
284 }
285
286 /// Configures the target this configuration will be compiling for.
287 ///
288 /// This option is automatically scraped from the `TARGET` environment
289 /// variable by build scripts, so it's not required to call this function.
290 pub fn target(&mut self, target: &str) -> &mut Config {
291 self.target = Some(target.to_string());
292 self
293 }
294
295 /// Configures the host assumed by this configuration.
296 ///
297 /// This option is automatically scraped from the `HOST` environment
298 /// variable by build scripts, so it's not required to call this function.
299 pub fn host(&mut self, host: &str) -> &mut Config {
300 self.host = Some(host.to_string());
301 self
302 }
303
304 /// Configures the optimization level of the generated object files.
305 ///
306 /// This option is automatically scraped from the `OPT_LEVEL` environment
307 /// variable by build scripts, so it's not required to call this function.
308 pub fn opt_level(&mut self, opt_level: u32) -> &mut Config {
309 self.opt_level = Some(opt_level.to_string());
310 self
311 }
312
313 /// Configures the optimization level of the generated object files.
314 ///
315 /// This option is automatically scraped from the `OPT_LEVEL` environment
316 /// variable by build scripts, so it's not required to call this function.
317 pub fn opt_level_str(&mut self, opt_level: &str) -> &mut Config {
318 self.opt_level = Some(opt_level.to_string());
319 self
320 }
321
322 /// Configures whether the compiler will emit debug information when
323 /// generating object files.
324 ///
325 /// This option is automatically scraped from the `PROFILE` environment
326 /// variable by build scripts (only enabled when the profile is "debug"), so
327 /// it's not required to call this function.
328 pub fn debug(&mut self, debug: bool) -> &mut Config {
329 self.debug = Some(debug);
330 self
331 }
332
333 /// Configures the output directory where all object files and static
334 /// libraries will be located.
335 ///
336 /// This option is automatically scraped from the `OUT_DIR` environment
337 /// variable by build scripts, so it's not required to call this function.
338 pub fn out_dir<P: AsRef<Path>>(&mut self, out_dir: P) -> &mut Config {
339 self.out_dir = Some(out_dir.as_ref().to_owned());
340 self
341 }
342
343 /// Configures the compiler to be used to produce output.
344 ///
345 /// This option is automatically determined from the target platform or a
346 /// number of environment variables, so it's not required to call this
347 /// function.
348 pub fn compiler<P: AsRef<Path>>(&mut self, compiler: P) -> &mut Config {
349 self.compiler = Some(compiler.as_ref().to_owned());
350 self
351 }
352
353 /// Configures the tool used to assemble archives.
354 ///
355 /// This option is automatically determined from the target platform or a
356 /// number of environment variables, so it's not required to call this
357 /// function.
358 pub fn archiver<P: AsRef<Path>>(&mut self, archiver: P) -> &mut Config {
359 self.archiver = Some(archiver.as_ref().to_owned());
360 self
361 }
362 /// Define whether metadata should be emitted for cargo allowing it to
363 /// automatically link the binary. Defaults to `true`.
364 pub fn cargo_metadata(&mut self, cargo_metadata: bool) -> &mut Config {
365 self.cargo_metadata = cargo_metadata;
366 self
367 }
368
369 /// Configures whether the compiler will emit position independent code.
370 ///
cc61c64b
XL
371 /// This option defaults to `false` for `windows-gnu` targets and
372 /// to `true` for all other targets.
476ff2be
SL
373 pub fn pic(&mut self, pic: bool) -> &mut Config {
374 self.pic = Some(pic);
375 self
376 }
377
7cac9316
XL
378 /// Configures whether the /MT flag or the /MD flag will be passed to msvc build tools.
379 ///
380 /// This option defaults to `false`, and affect only msvc targets.
381 pub fn static_crt(&mut self, static_crt: bool) -> &mut Config {
382 self.static_crt = Some(static_crt);
383 self
384 }
385
476ff2be
SL
386
387 #[doc(hidden)]
388 pub fn __set_env<A, B>(&mut self, a: A, b: B) -> &mut Config
8bb4bdeb
XL
389 where A: AsRef<OsStr>,
390 B: AsRef<OsStr>
476ff2be
SL
391 {
392 self.env.push((a.as_ref().to_owned(), b.as_ref().to_owned()));
393 self
394 }
395
396 /// Run the compiler, generating the file `output`
397 ///
398 /// The name `output` must begin with `lib` and end with `.a`
399 pub fn compile(&self, output: &str) {
400 assert!(output.starts_with("lib"));
401 assert!(output.ends_with(".a"));
402 let lib_name = &output[3..output.len() - 2];
403 let dst = self.get_out_dir();
404
405 let mut objects = Vec::new();
406 let mut src_dst = Vec::new();
407 for file in self.files.iter() {
408 let obj = dst.join(file).with_extension("o");
409 let obj = if !obj.starts_with(&dst) {
410 dst.join(obj.file_name().unwrap())
411 } else {
412 obj
413 };
414 fs::create_dir_all(&obj.parent().unwrap()).unwrap();
415 src_dst.push((file.to_path_buf(), obj.clone()));
416 objects.push(obj);
417 }
418 self.compile_objects(&src_dst);
419 self.assemble(lib_name, &dst.join(output), &objects);
420
421 if self.get_target().contains("msvc") {
422 let compiler = self.get_base_compiler();
8bb4bdeb
XL
423 let atlmfc_lib = compiler.env()
424 .iter()
425 .find(|&&(ref var, _)| var.as_os_str() == OsStr::new("LIB"))
426 .and_then(|&(_, ref lib_paths)| {
427 env::split_paths(lib_paths).find(|path| {
428 let sub = Path::new("atlmfc/lib");
429 path.ends_with(sub) || path.parent().map_or(false, |p| p.ends_with(sub))
430 })
431 });
476ff2be
SL
432
433 if let Some(atlmfc_lib) = atlmfc_lib {
8bb4bdeb 434 self.print(&format!("cargo:rustc-link-search=native={}", atlmfc_lib.display()));
476ff2be
SL
435 }
436 }
437
438 self.print(&format!("cargo:rustc-link-lib=static={}",
439 &output[3..output.len() - 2]));
440 self.print(&format!("cargo:rustc-link-search=native={}", dst.display()));
441
442 // Add specific C++ libraries, if enabled.
443 if self.cpp {
444 if let Some(stdlib) = self.get_cpp_link_stdlib() {
445 self.print(&format!("cargo:rustc-link-lib={}", stdlib));
446 }
447 }
448 }
449
450 #[cfg(feature = "parallel")]
451 fn compile_objects(&self, objs: &[(PathBuf, PathBuf)]) {
452 use self::rayon::prelude::*;
453
454 let mut cfg = rayon::Configuration::new();
455 if let Ok(amt) = env::var("NUM_JOBS") {
456 if let Ok(amt) = amt.parse() {
7cac9316 457 cfg = cfg.num_threads(amt);
476ff2be
SL
458 }
459 }
460 drop(rayon::initialize(cfg));
461
7cac9316
XL
462 objs.par_iter().with_max_len(1)
463 .for_each(|&(ref src, ref dst)| self.compile_object(src, dst));
476ff2be
SL
464 }
465
466 #[cfg(not(feature = "parallel"))]
467 fn compile_objects(&self, objs: &[(PathBuf, PathBuf)]) {
468 for &(ref src, ref dst) in objs {
469 self.compile_object(src, dst);
470 }
471 }
472
473 fn compile_object(&self, file: &Path, dst: &Path) {
474 let is_asm = file.extension().and_then(|s| s.to_str()) == Some("asm");
475 let msvc = self.get_target().contains("msvc");
476 let (mut cmd, name) = if msvc && is_asm {
477 self.msvc_macro_assembler()
478 } else {
479 let compiler = self.get_compiler();
480 let mut cmd = compiler.to_command();
481 for &(ref a, ref b) in self.env.iter() {
482 cmd.env(a, b);
483 }
8bb4bdeb
XL
484 (cmd,
485 compiler.path
486 .file_name()
487 .unwrap()
488 .to_string_lossy()
489 .into_owned())
476ff2be
SL
490 };
491 if msvc && is_asm {
492 cmd.arg("/Fo").arg(dst);
493 } else if msvc {
494 let mut s = OsString::from("/Fo");
495 s.push(&dst);
496 cmd.arg(s);
497 } else {
498 cmd.arg("-o").arg(&dst);
499 }
8bb4bdeb 500 cmd.arg(if msvc { "/c" } else { "-c" });
476ff2be
SL
501 cmd.arg(file);
502
503 run(&mut cmd, &name);
504 }
505
8bb4bdeb
XL
506 /// Run the compiler, returning the macro-expanded version of the input files.
507 ///
508 /// This is only relevant for C and C++ files.
509 pub fn expand(&self) -> Vec<u8> {
510 let compiler = self.get_compiler();
511 let mut cmd = compiler.to_command();
512 for &(ref a, ref b) in self.env.iter() {
513 cmd.env(a, b);
514 }
515 cmd.arg(compiler.family.expand_flag());
516 for file in self.files.iter() {
517 cmd.arg(file);
518 }
519
520 let name = compiler.path
521 .file_name()
522 .unwrap()
523 .to_string_lossy()
524 .into_owned();
525
cc61c64b 526 run_output(&mut cmd, &name)
8bb4bdeb
XL
527 }
528
476ff2be
SL
529 /// Get the compiler that's in use for this configuration.
530 ///
531 /// This function will return a `Tool` which represents the culmination
532 /// of this configuration at a snapshot in time. The returned compiler can
533 /// be inspected (e.g. the path, arguments, environment) to forward along to
534 /// other tools, or the `to_command` method can be used to invoke the
535 /// compiler itself.
536 ///
537 /// This method will take into account all configuration such as debug
538 /// information, optimization level, include directories, defines, etc.
539 /// Additionally, the compiler binary in use follows the standard
540 /// conventions for this path, e.g. looking at the explicitly set compiler,
541 /// environment variables (a number of which are inspected here), and then
542 /// falling back to the default configuration.
543 pub fn get_compiler(&self) -> Tool {
544 let opt_level = self.get_opt_level();
476ff2be 545 let target = self.get_target();
476ff2be
SL
546
547 let mut cmd = self.get_base_compiler();
8bb4bdeb
XL
548 let nvcc = cmd.path.file_name()
549 .and_then(|p| p.to_str()).map(|p| p.contains("nvcc"))
476ff2be
SL
550 .unwrap_or(false);
551
8bb4bdeb
XL
552 // Non-target flags
553 // If the flag is not conditioned on target variable, it belongs here :)
554 match cmd.family {
555 ToolFamily::Msvc => {
556 cmd.args.push("/nologo".into());
7cac9316
XL
557
558 let crt_flag = match self.static_crt {
559 Some(true) => "/MT",
560 Some(false) => "/MD",
561 None => {
562 let features = env::var("CARGO_CFG_TARGET_FEATURE")
8bb4bdeb 563 .unwrap_or(String::new());
7cac9316
XL
564 if features.contains("crt-static") {
565 "/MT"
566 } else {
567 "/MD"
568 }
569 },
570 };
571 cmd.args.push(crt_flag.into());
572
8bb4bdeb
XL
573 match &opt_level[..] {
574 "z" | "s" => cmd.args.push("/Os".into()),
575 "1" => cmd.args.push("/O1".into()),
576 // -O3 is a valid value for gcc and clang compilers, but not msvc. Cap to /O2.
577 "2" | "3" => cmd.args.push("/O2".into()),
578 _ => {}
579 }
476ff2be 580 }
8bb4bdeb
XL
581 ToolFamily::Gnu |
582 ToolFamily::Clang => {
583 cmd.args.push(format!("-O{}", opt_level).into());
584 if !nvcc {
585 cmd.args.push("-ffunction-sections".into());
586 cmd.args.push("-fdata-sections".into());
587 if self.pic.unwrap_or(!target.contains("windows-gnu")) {
588 cmd.args.push("-fPIC".into());
589 }
590 } else if self.pic.unwrap_or(false) {
591 cmd.args.push("-Xcompiler".into());
592 cmd.args.push("\'-fPIC\'".into());
593 }
476ff2be 594 }
476ff2be
SL
595 }
596 for arg in self.envflags(if self.cpp {"CXXFLAGS"} else {"CFLAGS"}) {
597 cmd.args.push(arg.into());
598 }
599
8bb4bdeb
XL
600 if self.get_debug() {
601 cmd.args.push(cmd.family.debug_flag().into());
476ff2be
SL
602 }
603
8bb4bdeb
XL
604 // Target flags
605 match cmd.family {
606 ToolFamily::Clang => {
607 cmd.args.push(format!("--target={}", target).into());
476ff2be 608 }
8bb4bdeb
XL
609 ToolFamily::Msvc => {
610 if target.contains("i586") {
611 cmd.args.push("/ARCH:IA32".into());
612 }
476ff2be 613 }
8bb4bdeb
XL
614 ToolFamily::Gnu => {
615 if target.contains("i686") || target.contains("i586") {
616 cmd.args.push("-m32".into());
617 } else if target.contains("x86_64") || target.contains("powerpc64") {
618 cmd.args.push("-m64".into());
619 }
476ff2be 620
8bb4bdeb
XL
621 if target.contains("musl") {
622 cmd.args.push("-static".into());
623 }
476ff2be 624
8bb4bdeb 625 // armv7 targets get to use armv7 instructions
7cac9316 626 if target.starts_with("armv7-") && target.contains("-linux-") {
8bb4bdeb
XL
627 cmd.args.push("-march=armv7-a".into());
628 }
476ff2be 629
8bb4bdeb
XL
630 // On android we can guarantee some extra float instructions
631 // (specified in the android spec online)
632 if target.starts_with("armv7-linux-androideabi") {
633 cmd.args.push("-march=armv7-a".into());
634 cmd.args.push("-mfpu=vfpv3-d16".into());
7cac9316 635 cmd.args.push("-mfloat-abi=softfp".into());
8bb4bdeb 636 }
476ff2be 637
8bb4bdeb
XL
638 // For us arm == armv6 by default
639 if target.starts_with("arm-unknown-linux-") {
640 cmd.args.push("-march=armv6".into());
641 cmd.args.push("-marm".into());
642 }
476ff2be 643
7cac9316
XL
644 // We can guarantee some settings for FRC
645 if target.starts_with("arm-frc-") {
646 cmd.args.push("-march=armv7-a".into());
647 cmd.args.push("-mcpu=cortex-a9".into());
648 cmd.args.push("-mfpu=vfpv3".into());
649 cmd.args.push("-mfloat-abi=softfp".into());
650 cmd.args.push("-marm".into());
651 }
652
8bb4bdeb
XL
653 // Turn codegen down on i586 to avoid some instructions.
654 if target.starts_with("i586-unknown-linux-") {
655 cmd.args.push("-march=pentium".into());
656 }
476ff2be 657
8bb4bdeb
XL
658 // Set codegen level for i686 correctly
659 if target.starts_with("i686-unknown-linux-") {
660 cmd.args.push("-march=i686".into());
661 }
476ff2be 662
8bb4bdeb
XL
663 // Looks like `musl-gcc` makes is hard for `-m32` to make its way
664 // all the way to the linker, so we need to actually instruct the
665 // linker that we're generating 32-bit executables as well. This'll
666 // typically only be used for build scripts which transitively use
667 // these flags that try to compile executables.
668 if target == "i686-unknown-linux-musl" {
669 cmd.args.push("-Wl,-melf_i386".into());
670 }
476ff2be 671
8bb4bdeb
XL
672 if target.starts_with("thumb") {
673 cmd.args.push("-mthumb".into());
476ff2be 674
8bb4bdeb
XL
675 if target.ends_with("eabihf") {
676 cmd.args.push("-mfloat-abi=hard".into())
677 }
476ff2be 678 }
8bb4bdeb
XL
679 if target.starts_with("thumbv6m") {
680 cmd.args.push("-march=armv6s-m".into());
681 }
682 if target.starts_with("thumbv7em") {
683 cmd.args.push("-march=armv7e-m".into());
476ff2be 684
8bb4bdeb
XL
685 if target.ends_with("eabihf") {
686 cmd.args.push("-mfpu=fpv4-sp-d16".into())
687 }
688 }
689 if target.starts_with("thumbv7m") {
690 cmd.args.push("-march=armv7-m".into());
476ff2be
SL
691 }
692 }
476ff2be
SL
693 }
694
8bb4bdeb
XL
695 if target.contains("-ios") {
696 // FIXME: potential bug. iOS is always compiled with Clang, but Gcc compiler may be
697 // detected instead.
698 self.ios_flags(&mut cmd);
699 }
700
701 if self.cpp {
702 match (self.cpp_set_stdlib.as_ref(), cmd.family) {
703 (None, _) => { }
704 (Some(stdlib), ToolFamily::Gnu) |
705 (Some(stdlib), ToolFamily::Clang) => {
706 cmd.args.push(format!("-stdlib=lib{}", stdlib).into());
707 }
708 _ => {
709 println!("cargo:warning=cpp_set_stdlib is specified, but the {:?} compiler \
710 does not support this option, ignored", cmd.family);
711 }
476ff2be
SL
712 }
713 }
714
715 for directory in self.include_directories.iter() {
8bb4bdeb 716 cmd.args.push(cmd.family.include_flag().into());
476ff2be
SL
717 cmd.args.push(directory.into());
718 }
719
720 for flag in self.flags.iter() {
721 cmd.args.push(flag.into());
722 }
723
724 for &(ref key, ref value) in self.definitions.iter() {
8bb4bdeb 725 let lead = if let ToolFamily::Msvc = cmd.family {"/"} else {"-"};
7cac9316 726 if let Some(ref value) = *value {
476ff2be
SL
727 cmd.args.push(format!("{}D{}={}", lead, key, value).into());
728 } else {
729 cmd.args.push(format!("{}D{}", lead, key).into());
730 }
731 }
732 cmd
733 }
734
735 fn msvc_macro_assembler(&self) -> (Command, String) {
736 let target = self.get_target();
8bb4bdeb
XL
737 let tool = if target.contains("x86_64") {
738 "ml64.exe"
739 } else {
740 "ml.exe"
741 };
742 let mut cmd = windows_registry::find(&target, tool).unwrap_or_else(|| self.cmd(tool));
476ff2be
SL
743 for directory in self.include_directories.iter() {
744 cmd.arg("/I").arg(directory);
745 }
746 for &(ref key, ref value) in self.definitions.iter() {
7cac9316 747 if let Some(ref value) = *value {
476ff2be
SL
748 cmd.arg(&format!("/D{}={}", key, value));
749 } else {
750 cmd.arg(&format!("/D{}", key));
751 }
752 }
753
754 if target.contains("i686") || target.contains("i586") {
755 cmd.arg("/safeseh");
756 }
757 for flag in self.flags.iter() {
758 cmd.arg(flag);
759 }
760
761 (cmd, tool.to_string())
762 }
763
764 fn assemble(&self, lib_name: &str, dst: &Path, objects: &[PathBuf]) {
765 // Delete the destination if it exists as the `ar` tool at least on Unix
766 // appends to it, which we don't want.
767 let _ = fs::remove_file(&dst);
768
769 let target = self.get_target();
770 if target.contains("msvc") {
771 let mut cmd = match self.archiver {
772 Some(ref s) => self.cmd(s),
7cac9316 773 None => windows_registry::find(&target, "lib.exe").unwrap_or_else(|| self.cmd("lib.exe")),
476ff2be
SL
774 };
775 let mut out = OsString::from("/OUT:");
776 out.push(dst);
8bb4bdeb
XL
777 run(cmd.arg(out)
778 .arg("/nologo")
779 .args(objects)
780 .args(&self.objects),
781 "lib.exe");
476ff2be
SL
782
783 // The Rust compiler will look for libfoo.a and foo.lib, but the
784 // MSVC linker will also be passed foo.lib, so be sure that both
785 // exist for now.
786 let lib_dst = dst.with_file_name(format!("{}.lib", lib_name));
787 let _ = fs::remove_file(&lib_dst);
8bb4bdeb
XL
788 fs::hard_link(&dst, &lib_dst)
789 .or_else(|_| {
790 // if hard-link fails, just copy (ignoring the number of bytes written)
791 fs::copy(&dst, &lib_dst).map(|_| ())
792 })
8bb4bdeb 793 .expect("Copying from {:?} to {:?} failed.");;
476ff2be
SL
794 } else {
795 let ar = self.get_ar();
796 let cmd = ar.file_name().unwrap().to_string_lossy();
8bb4bdeb
XL
797 run(self.cmd(&ar)
798 .arg("crs")
799 .arg(dst)
800 .args(objects)
801 .args(&self.objects),
802 &cmd);
476ff2be
SL
803 }
804 }
805
806 fn ios_flags(&self, cmd: &mut Tool) {
807 enum ArchSpec {
808 Device(&'static str),
809 Simulator(&'static str),
810 }
811
812 let target = self.get_target();
813 let arch = target.split('-').nth(0).unwrap();
814 let arch = match arch {
815 "arm" | "armv7" | "thumbv7" => ArchSpec::Device("armv7"),
816 "armv7s" | "thumbv7s" => ArchSpec::Device("armv7s"),
817 "arm64" | "aarch64" => ArchSpec::Device("arm64"),
818 "i386" | "i686" => ArchSpec::Simulator("-m32"),
819 "x86_64" => ArchSpec::Simulator("-m64"),
8bb4bdeb 820 _ => fail("Unknown arch for iOS target"),
476ff2be
SL
821 };
822
823 let sdk = match arch {
824 ArchSpec::Device(arch) => {
825 cmd.args.push("-arch".into());
826 cmd.args.push(arch.into());
827 cmd.args.push("-miphoneos-version-min=7.0".into());
828 "iphoneos"
8bb4bdeb 829 }
476ff2be
SL
830 ArchSpec::Simulator(arch) => {
831 cmd.args.push(arch.into());
832 cmd.args.push("-mios-simulator-version-min=7.0".into());
833 "iphonesimulator"
834 }
835 };
836
837 self.print(&format!("Detecting iOS SDK path for {}", sdk));
838 let sdk_path = self.cmd("xcrun")
839 .arg("--show-sdk-path")
840 .arg("--sdk")
841 .arg(sdk)
842 .stderr(Stdio::inherit())
843 .output()
844 .unwrap()
845 .stdout;
846
847 let sdk_path = String::from_utf8(sdk_path).unwrap();
848
849 cmd.args.push("-isysroot".into());
850 cmd.args.push(sdk_path.trim().into());
851 }
852
853 fn cmd<P: AsRef<OsStr>>(&self, prog: P) -> Command {
854 let mut cmd = Command::new(prog);
855 for &(ref a, ref b) in self.env.iter() {
856 cmd.env(a, b);
857 }
7cac9316 858 cmd
476ff2be
SL
859 }
860
861 fn get_base_compiler(&self) -> Tool {
862 if let Some(ref c) = self.compiler {
8bb4bdeb 863 return Tool::new(c.clone());
476ff2be
SL
864 }
865 let host = self.get_host();
866 let target = self.get_target();
cc61c64b
XL
867 let (env, msvc, gnu) = if self.cpp {
868 ("CXX", "cl.exe", "g++")
476ff2be 869 } else {
cc61c64b 870 ("CC", "cl.exe", "gcc")
476ff2be 871 };
cc61c64b
XL
872
873 let default = if host.contains("solaris") {
874 // In this case, c++/cc unlikely to exist or be correct.
875 gnu
876 } else if self.cpp {
877 "c++"
878 } else {
879 "cc"
880 };
881
8bb4bdeb
XL
882 self.env_tool(env)
883 .map(|(tool, args)| {
884 let mut t = Tool::new(PathBuf::from(tool));
885 for arg in args {
886 t.args.push(arg.into());
476ff2be 887 }
7cac9316 888 t
8bb4bdeb
XL
889 })
890 .or_else(|| {
891 if target.contains("emscripten") {
7cac9316
XL
892 //Windows uses bat file so we have to be a bit more specific
893 let tool = if self.cpp {
894 if cfg!(windows) {
895 "em++.bat"
896 } else {
897 "em++"
898 }
8bb4bdeb 899 } else {
7cac9316
XL
900 if cfg!(windows) {
901 "emcc.bat"
902 } else {
903 "emcc"
904 }
905 };
906
907 Some(Tool::new(PathBuf::from(tool)))
476ff2be 908 } else {
8bb4bdeb 909 None
476ff2be 910 }
8bb4bdeb
XL
911 })
912 .or_else(|| windows_registry::find_tool(&target, "cl.exe"))
913 .unwrap_or_else(|| {
914 let compiler = if host.contains("windows") && target.contains("windows") {
915 if target.contains("msvc") {
916 msvc.to_string()
917 } else {
918 format!("{}.exe", gnu)
919 }
920 } else if target.contains("android") {
921 format!("{}-{}", target.replace("armv7", "arm"), gnu)
922 } else if self.get_host() != target {
923 // CROSS_COMPILE is of the form: "arm-linux-gnueabi-"
924 let cc_env = self.getenv("CROSS_COMPILE");
925 let cross_compile = cc_env.as_ref().map(|s| s.trim_right_matches('-'));
926 let prefix = cross_compile.or(match &target[..] {
927 "aarch64-unknown-linux-gnu" => Some("aarch64-linux-gnu"),
928 "arm-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"),
7cac9316 929 "arm-frc-linux-gnueabi" => Some("arm-frc-linux-gnueabi"),
8bb4bdeb
XL
930 "arm-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"),
931 "arm-unknown-linux-musleabi" => Some("arm-linux-musleabi"),
932 "arm-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"),
933 "arm-unknown-netbsdelf-eabi" => Some("arm--netbsdelf-eabi"),
934 "armv6-unknown-netbsdelf-eabihf" => Some("armv6--netbsdelf-eabihf"),
935 "armv7-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"),
936 "armv7-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"),
937 "armv7-unknown-netbsdelf-eabihf" => Some("armv7--netbsdelf-eabihf"),
938 "i686-pc-windows-gnu" => Some("i686-w64-mingw32"),
939 "i686-unknown-linux-musl" => Some("musl"),
940 "i686-unknown-netbsdelf" => Some("i486--netbsdelf"),
941 "mips-unknown-linux-gnu" => Some("mips-linux-gnu"),
942 "mipsel-unknown-linux-gnu" => Some("mipsel-linux-gnu"),
943 "mips64-unknown-linux-gnuabi64" => Some("mips64-linux-gnuabi64"),
944 "mips64el-unknown-linux-gnuabi64" => Some("mips64el-linux-gnuabi64"),
945 "powerpc-unknown-linux-gnu" => Some("powerpc-linux-gnu"),
946 "powerpc-unknown-netbsd" => Some("powerpc--netbsd"),
947 "powerpc64-unknown-linux-gnu" => Some("powerpc-linux-gnu"),
948 "powerpc64le-unknown-linux-gnu" => Some("powerpc64le-linux-gnu"),
949 "s390x-unknown-linux-gnu" => Some("s390x-linux-gnu"),
950 "sparc64-unknown-netbsd" => Some("sparc64--netbsd"),
cc61c64b 951 "sparcv9-sun-solaris" => Some("sparcv9-sun-solaris"),
8bb4bdeb
XL
952 "thumbv6m-none-eabi" => Some("arm-none-eabi"),
953 "thumbv7em-none-eabi" => Some("arm-none-eabi"),
954 "thumbv7em-none-eabihf" => Some("arm-none-eabi"),
955 "thumbv7m-none-eabi" => Some("arm-none-eabi"),
956 "x86_64-pc-windows-gnu" => Some("x86_64-w64-mingw32"),
957 "x86_64-rumprun-netbsd" => Some("x86_64-rumprun-netbsd"),
958 "x86_64-unknown-linux-musl" => Some("musl"),
959 "x86_64-unknown-netbsd" => Some("x86_64--netbsd"),
960 _ => None,
961 });
962 match prefix {
963 Some(prefix) => format!("{}-{}", prefix, gnu),
964 None => default.to_string(),
965 }
966 } else {
967 default.to_string()
968 };
969 Tool::new(PathBuf::from(compiler))
970 })
476ff2be
SL
971 }
972
973 fn get_var(&self, var_base: &str) -> Result<String, String> {
974 let target = self.get_target();
975 let host = self.get_host();
8bb4bdeb 976 let kind = if host == target { "HOST" } else { "TARGET" };
476ff2be
SL
977 let target_u = target.replace("-", "_");
978 let res = self.getenv(&format!("{}_{}", var_base, target))
979 .or_else(|| self.getenv(&format!("{}_{}", var_base, target_u)))
980 .or_else(|| self.getenv(&format!("{}_{}", kind, var_base)))
981 .or_else(|| self.getenv(var_base));
982
983 match res {
984 Some(res) => Ok(res),
985 None => Err("could not get environment variable".to_string()),
986 }
987 }
988
989 fn envflags(&self, name: &str) -> Vec<String> {
8bb4bdeb
XL
990 self.get_var(name)
991 .unwrap_or(String::new())
992 .split(|c: char| c.is_whitespace())
993 .filter(|s| !s.is_empty())
476ff2be
SL
994 .map(|s| s.to_string())
995 .collect()
996 }
997
998 fn env_tool(&self, name: &str) -> Option<(String, Vec<String>)> {
999 self.get_var(name).ok().map(|tool| {
1000 let whitelist = ["ccache", "distcc"];
1001 for t in whitelist.iter() {
7cac9316 1002 if tool.starts_with(t) && tool[t.len()..].starts_with(' ') {
8bb4bdeb 1003 return (t.to_string(), vec![tool[t.len()..].trim_left().to_string()]);
476ff2be
SL
1004 }
1005 }
1006 (tool, Vec::new())
1007 })
1008 }
1009
1010 /// Returns the default C++ standard library for the current target: `libc++`
1011 /// for OS X and `libstdc++` for anything else.
1012 fn get_cpp_link_stdlib(&self) -> Option<String> {
1013 self.cpp_link_stdlib.clone().unwrap_or_else(|| {
1014 let target = self.get_target();
1015 if target.contains("msvc") {
1016 None
1017 } else if target.contains("darwin") {
1018 Some("c++".to_string())
1019 } else if target.contains("freebsd") {
1020 Some("c++".to_string())
1021 } else {
1022 Some("stdc++".to_string())
1023 }
1024 })
1025 }
1026
1027 fn get_ar(&self) -> PathBuf {
8bb4bdeb
XL
1028 self.archiver
1029 .clone()
1030 .or_else(|| self.get_var("AR").map(PathBuf::from).ok())
1031 .unwrap_or_else(|| {
1032 if self.get_target().contains("android") {
1033 PathBuf::from(format!("{}-ar", self.get_target().replace("armv7", "arm")))
1034 } else if self.get_target().contains("emscripten") {
7cac9316
XL
1035 //Windows use bat files so we have to be a bit more specific
1036 let tool = if cfg!(windows) {
1037 "emar.bat"
1038 } else {
1039 "emar"
1040 };
1041
1042 PathBuf::from(tool)
8bb4bdeb
XL
1043 } else {
1044 PathBuf::from("ar")
1045 }
1046 })
476ff2be
SL
1047 }
1048
1049 fn get_target(&self) -> String {
1050 self.target.clone().unwrap_or_else(|| self.getenv_unwrap("TARGET"))
1051 }
1052
1053 fn get_host(&self) -> String {
1054 self.host.clone().unwrap_or_else(|| self.getenv_unwrap("HOST"))
1055 }
1056
1057 fn get_opt_level(&self) -> String {
8bb4bdeb 1058 self.opt_level.as_ref().cloned().unwrap_or_else(|| self.getenv_unwrap("OPT_LEVEL"))
476ff2be
SL
1059 }
1060
1061 fn get_debug(&self) -> bool {
1062 self.debug.unwrap_or_else(|| self.getenv_unwrap("PROFILE") == "debug")
1063 }
1064
1065 fn get_out_dir(&self) -> PathBuf {
8bb4bdeb 1066 self.out_dir.clone().unwrap_or_else(|| env::var_os("OUT_DIR").map(PathBuf::from).unwrap())
476ff2be
SL
1067 }
1068
1069 fn getenv(&self, v: &str) -> Option<String> {
1070 let r = env::var(v).ok();
1071 self.print(&format!("{} = {:?}", v, r));
1072 r
1073 }
1074
1075 fn getenv_unwrap(&self, v: &str) -> String {
1076 match self.getenv(v) {
1077 Some(s) => s,
1078 None => fail(&format!("environment variable `{}` not defined", v)),
1079 }
1080 }
1081
1082 fn print(&self, s: &str) {
1083 if self.cargo_metadata {
1084 println!("{}", s);
1085 }
1086 }
1087}
1088
1089impl Tool {
1090 fn new(path: PathBuf) -> Tool {
8bb4bdeb
XL
1091 // Try to detect family of the tool from its name, falling back to Gnu.
1092 let family = if let Some(fname) = path.file_name().and_then(|p| p.to_str()) {
1093 if fname.contains("clang") {
1094 ToolFamily::Clang
7cac9316 1095 } else if fname.contains("cl") && !fname.contains("uclibc") {
8bb4bdeb
XL
1096 ToolFamily::Msvc
1097 } else {
1098 ToolFamily::Gnu
1099 }
1100 } else {
1101 ToolFamily::Gnu
1102 };
476ff2be
SL
1103 Tool {
1104 path: path,
1105 args: Vec::new(),
1106 env: Vec::new(),
8bb4bdeb 1107 family: family
476ff2be
SL
1108 }
1109 }
1110
1111 /// Converts this compiler into a `Command` that's ready to be run.
1112 ///
1113 /// This is useful for when the compiler needs to be executed and the
1114 /// command returned will already have the initial arguments and environment
1115 /// variables configured.
1116 pub fn to_command(&self) -> Command {
1117 let mut cmd = Command::new(&self.path);
1118 cmd.args(&self.args);
1119 for &(ref k, ref v) in self.env.iter() {
1120 cmd.env(k, v);
1121 }
8bb4bdeb 1122 cmd
476ff2be
SL
1123 }
1124
1125 /// Returns the path for this compiler.
1126 ///
1127 /// Note that this may not be a path to a file on the filesystem, e.g. "cc",
1128 /// but rather something which will be resolved when a process is spawned.
1129 pub fn path(&self) -> &Path {
1130 &self.path
1131 }
1132
1133 /// Returns the default set of arguments to the compiler needed to produce
1134 /// executables for the target this compiler generates.
1135 pub fn args(&self) -> &[OsString] {
1136 &self.args
1137 }
1138
1139 /// Returns the set of environment variables needed for this compiler to
1140 /// operate.
1141 ///
1142 /// This is typically only used for MSVC compilers currently.
1143 pub fn env(&self) -> &[(OsString, OsString)] {
1144 &self.env
1145 }
1146}
1147
cc61c64b
XL
1148fn run(cmd: &mut Command, program: &str) {
1149 let (mut child, print) = spawn(cmd, program);
1150 let status = child.wait().expect("failed to wait on child process");
1151 print.join().unwrap();
7cac9316 1152 println!("{}", status);
cc61c64b
XL
1153 if !status.success() {
1154 fail(&format!("command did not execute successfully, got: {}", status));
1155 }
1156}
1157
1158fn run_output(cmd: &mut Command, program: &str) -> Vec<u8> {
1159 cmd.stdout(Stdio::piped());
1160 let (mut child, print) = spawn(cmd, program);
1161 let mut stdout = vec![];
1162 child.stdout.take().unwrap().read_to_end(&mut stdout).unwrap();
1163 let status = child.wait().expect("failed to wait on child process");
1164 print.join().unwrap();
7cac9316 1165 println!("{}", status);
cc61c64b
XL
1166 if !status.success() {
1167 fail(&format!("command did not execute successfully, got: {}", status));
1168 }
7cac9316 1169 stdout
cc61c64b
XL
1170}
1171
1172fn spawn(cmd: &mut Command, program: &str) -> (Child, JoinHandle<()>) {
476ff2be 1173 println!("running: {:?}", cmd);
cc61c64b 1174
476ff2be
SL
1175 // Capture the standard error coming from these programs, and write it out
1176 // with cargo:warning= prefixes. Note that this is a bit wonky to avoid
1177 // requiring the output to be UTF-8, we instead just ship bytes from one
1178 // location to another.
cc61c64b 1179 match cmd.stderr(Stdio::piped()).spawn() {
476ff2be
SL
1180 Ok(mut child) => {
1181 let stderr = BufReader::new(child.stderr.take().unwrap());
cc61c64b 1182 let print = thread::spawn(move || {
8bb4bdeb
XL
1183 for line in stderr.split(b'\n').filter_map(|l| l.ok()) {
1184 print!("cargo:warning=");
1185 std::io::stdout().write_all(&line).unwrap();
1186 println!("");
1187 }
1188 });
cc61c64b 1189 (child, print)
476ff2be 1190 }
476ff2be
SL
1191 Err(ref e) if e.kind() == io::ErrorKind::NotFound => {
1192 let extra = if cfg!(windows) {
1193 " (see https://github.com/alexcrichton/gcc-rs#compile-time-requirements \
1194 for help)"
1195 } else {
1196 ""
1197 };
1198 fail(&format!("failed to execute command: {}\nIs `{}` \
8bb4bdeb
XL
1199 not installed?{}",
1200 e,
1201 program,
1202 extra));
476ff2be
SL
1203 }
1204 Err(e) => fail(&format!("failed to execute command: {}", e)),
476ff2be
SL
1205 }
1206}
1207
1208fn fail(s: &str) -> ! {
1209 println!("\n\n{}\n\n", s);
1210 panic!()
1211}