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