1#![doc(html_root_url = "https://docs.rs/gcc/0.3")]
41#![cfg_attr(test, deny(warnings))]
42#![deny(missing_docs)]
43
44#[cfg(feature = "parallel")]
45extern crate rayon;
46
47use std::env;
48use std::ffi::{OsString, OsStr};
49use std::fs;
50use std::path::{PathBuf, Path};
51use std::process::{Command, Stdio, Child};
52use std::io::{self, BufReader, BufRead, Read, Write};
53use std::thread::{self, JoinHandle};
54
55#[doc(hidden)]
56#[deprecated(since="0.3.51", note="gcc::Config has been renamed to gcc::Build")]
57pub type Config = Build;
58
59#[cfg(feature = "parallel")]
60use std::sync::Mutex;
61
62#[cfg(windows)]
65mod registry;
66#[cfg(windows)]
67#[macro_use]
68mod winapi;
69#[cfg(windows)]
70mod com;
71#[cfg(windows)]
72mod setup_config;
73
74pub mod windows_registry;
75
76#[derive(Clone, Debug)]
78#[deprecated(note = "crate has been renamed to `cc`, the `gcc` name is not maintained")]
79pub struct Build {
80 include_directories: Vec<PathBuf>,
81 definitions: Vec<(String, Option<String>)>,
82 objects: Vec<PathBuf>,
83 flags: Vec<String>,
84 flags_supported: Vec<String>,
85 files: Vec<PathBuf>,
86 cpp: bool,
87 cpp_link_stdlib: Option<Option<String>>,
88 cpp_set_stdlib: Option<String>,
89 target: Option<String>,
90 host: Option<String>,
91 out_dir: Option<PathBuf>,
92 opt_level: Option<String>,
93 debug: Option<bool>,
94 env: Vec<(OsString, OsString)>,
95 compiler: Option<PathBuf>,
96 archiver: Option<PathBuf>,
97 cargo_metadata: bool,
98 pic: Option<bool>,
99 static_crt: Option<bool>,
100 shared_flag: Option<bool>,
101 static_flag: Option<bool>,
102 warnings_into_errors: bool,
103 warnings: bool,
104}
105
106#[derive(Clone, Debug)]
108enum ErrorKind {
109 IOError,
111 ArchitectureInvalid,
113 EnvVarNotFound,
115 ToolExecError,
117 ToolNotFound,
119}
120
121#[derive(Clone, Debug)]
123pub struct Error {
124 kind: ErrorKind,
126 message: String,
128}
129
130impl Error {
131 fn new(kind: ErrorKind, message: &str) -> Error {
132 Error { kind: kind, message: message.to_owned() }
133 }
134}
135
136impl From<io::Error> for Error {
137 fn from(e: io::Error) -> Error {
138 Error::new(ErrorKind::IOError, &format!("{}", e))
139 }
140}
141
142#[derive(Clone, Debug)]
150pub struct Tool {
151 path: PathBuf,
152 args: Vec<OsString>,
153 env: Vec<(OsString, OsString)>,
154 family: ToolFamily
155}
156
157#[derive(Copy, Clone, Debug, PartialEq)]
163enum ToolFamily {
164 Gnu,
166 Clang,
169 Msvc,
171}
172
173impl ToolFamily {
174 fn debug_flag(&self) -> &'static str {
176 match *self {
177 ToolFamily::Msvc => "/Z7",
178 ToolFamily::Gnu |
179 ToolFamily::Clang => "-g",
180 }
181 }
182
183 fn include_flag(&self) -> &'static str {
185 match *self {
186 ToolFamily::Msvc => "/I",
187 ToolFamily::Gnu |
188 ToolFamily::Clang => "-I",
189 }
190 }
191
192 fn expand_flag(&self) -> &'static str {
194 match *self {
195 ToolFamily::Msvc => "/E",
196 ToolFamily::Gnu |
197 ToolFamily::Clang => "-E",
198 }
199 }
200
201 fn warnings_flags(&self) -> &'static [&'static str] {
203 static MSVC_FLAGS: &'static [&'static str] = &["/W4"];
204 static GNU_CLANG_FLAGS: &'static [&'static str] = &["-Wall", "-Wextra"];
205
206 match *self {
207 ToolFamily::Msvc => &MSVC_FLAGS,
208 ToolFamily::Gnu |
209 ToolFamily::Clang => &GNU_CLANG_FLAGS,
210 }
211 }
212
213 fn warnings_to_errors_flag(&self) -> &'static str {
215 match *self {
216 ToolFamily::Msvc => "/WX",
217 ToolFamily::Gnu |
218 ToolFamily::Clang => "-Werror"
219 }
220 }
221}
222
223#[doc(hidden)]
237#[deprecated(note = "crate has been renamed to `cc`, the `gcc` name is not maintained")]
238pub fn compile_library(output: &str, files: &[&str]) {
239 let mut c = Build::new();
240 for f in files.iter() {
241 c.file(*f);
242 }
243 c.compile(output);
244}
245
246impl Build {
247 #[deprecated(note = "crate has been renamed to `cc`, the `gcc` name is not maintained")]
253 pub fn new() -> Build {
254 Build {
255 include_directories: Vec::new(),
256 definitions: Vec::new(),
257 objects: Vec::new(),
258 flags: Vec::new(),
259 flags_supported: Vec::new(),
260 files: Vec::new(),
261 shared_flag: None,
262 static_flag: None,
263 cpp: false,
264 cpp_link_stdlib: None,
265 cpp_set_stdlib: None,
266 target: None,
267 host: None,
268 out_dir: None,
269 opt_level: None,
270 debug: None,
271 env: Vec::new(),
272 compiler: None,
273 archiver: None,
274 cargo_metadata: true,
275 pic: None,
276 static_crt: None,
277 warnings: true,
278 warnings_into_errors: false,
279 }
280 }
281
282 pub fn include<P: AsRef<Path>>(&mut self, dir: P) -> &mut Build {
298 self.include_directories.push(dir.as_ref().to_path_buf());
299 self
300 }
301
302 pub fn define<'a, V: Into<Option<&'a str>>>(&mut self, var: &str, val: V) -> &mut Build {
314 self.definitions.push((var.to_string(), val.into().map(|s| s.to_string())));
315 self
316 }
317
318 pub fn object<P: AsRef<Path>>(&mut self, obj: P) -> &mut Build {
320 self.objects.push(obj.as_ref().to_path_buf());
321 self
322 }
323
324 pub fn flag(&mut self, flag: &str) -> &mut Build {
335 self.flags.push(flag.to_string());
336 self
337 }
338
339 fn ensure_check_file(&self) -> Result<PathBuf, Error> {
340 let out_dir = self.get_out_dir()?;
341 let src = if self.cpp {
342 out_dir.join("flag_check.cpp")
343 } else {
344 out_dir.join("flag_check.c")
345 };
346
347 if !src.exists() {
348 let mut f = fs::File::create(&src)?;
349 write!(f, "int main(void) {{ return 0; }}")?;
350 }
351
352 Ok(src)
353 }
354
355 fn is_flag_supported(&self, flag: &str) -> Result<bool, Error> {
356 let out_dir = self.get_out_dir()?;
357 let src = self.ensure_check_file()?;
358 let obj = out_dir.join("flag_check");
359 let target = self.get_target()?;
360 let mut cfg = Build::new();
361 cfg.flag(flag)
362 .target(&target)
363 .opt_level(0)
364 .host(&target)
365 .debug(false)
366 .cpp(self.cpp);
367 let compiler = cfg.try_get_compiler()?;
368 let mut cmd = compiler.to_command();
369 command_add_output_file(&mut cmd, &obj, target.contains("msvc"), false);
370 cmd.arg(&src);
371
372 let output = cmd.output()?;
373 Ok(output.stderr.is_empty())
374 }
375
376 pub fn flag_if_supported(&mut self, flag: &str) -> &mut Build {
388 self.flags_supported.push(flag.to_string());
389 self
390 }
391
392 pub fn shared_flag(&mut self, shared_flag: bool) -> &mut Build {
407 self.shared_flag = Some(shared_flag);
408 self
409 }
410
411 pub fn static_flag(&mut self, static_flag: bool) -> &mut Build {
426 self.static_flag = Some(static_flag);
427 self
428 }
429
430 pub fn file<P: AsRef<Path>>(&mut self, p: P) -> &mut Build {
432 self.files.push(p.as_ref().to_path_buf());
433 self
434 }
435
436 pub fn files<P>(&mut self, p: P) -> &mut Build
438 where P: IntoIterator,
439 P::Item: AsRef<Path> {
440 for file in p.into_iter() {
441 self.file(file);
442 }
443 self
444 }
445
446 pub fn cpp(&mut self, cpp: bool) -> &mut Build {
451 self.cpp = cpp;
452 self
453 }
454
455 pub fn warnings_into_errors(&mut self, warnings_into_errors: bool) -> &mut Build {
475 self.warnings_into_errors = warnings_into_errors;
476 self
477 }
478
479 pub fn warnings(&mut self, warnings: bool) -> &mut Build {
496 self.warnings = warnings;
497 self
498 }
499
500 pub fn cpp_link_stdlib<'a, V: Into<Option<&'a str>>>(&mut self, cpp_link_stdlib: V) -> &mut Build {
526 self.cpp_link_stdlib = Some(cpp_link_stdlib.into().map(|s| s.into()));
527 self
528 }
529
530 pub fn cpp_set_stdlib<'a, V: Into<Option<&'a str>>>(&mut self, cpp_set_stdlib: V) -> &mut Build {
564 let cpp_set_stdlib = cpp_set_stdlib.into();
565 self.cpp_set_stdlib = cpp_set_stdlib.map(|s| s.into());
566 self.cpp_link_stdlib(cpp_set_stdlib);
567 self
568 }
569
570 pub fn target(&mut self, target: &str) -> &mut Build {
584 self.target = Some(target.to_string());
585 self
586 }
587
588 pub fn host(&mut self, host: &str) -> &mut Build {
602 self.host = Some(host.to_string());
603 self
604 }
605
606 pub fn opt_level(&mut self, opt_level: u32) -> &mut Build {
611 self.opt_level = Some(opt_level.to_string());
612 self
613 }
614
615 pub fn opt_level_str(&mut self, opt_level: &str) -> &mut Build {
620 self.opt_level = Some(opt_level.to_string());
621 self
622 }
623
624 pub fn debug(&mut self, debug: bool) -> &mut Build {
631 self.debug = Some(debug);
632 self
633 }
634
635 pub fn out_dir<P: AsRef<Path>>(&mut self, out_dir: P) -> &mut Build {
641 self.out_dir = Some(out_dir.as_ref().to_owned());
642 self
643 }
644
645 pub fn compiler<P: AsRef<Path>>(&mut self, compiler: P) -> &mut Build {
651 self.compiler = Some(compiler.as_ref().to_owned());
652 self
653 }
654
655 pub fn archiver<P: AsRef<Path>>(&mut self, archiver: P) -> &mut Build {
661 self.archiver = Some(archiver.as_ref().to_owned());
662 self
663 }
664 pub fn cargo_metadata(&mut self, cargo_metadata: bool) -> &mut Build {
675 self.cargo_metadata = cargo_metadata;
676 self
677 }
678
679 pub fn pic(&mut self, pic: bool) -> &mut Build {
684 self.pic = Some(pic);
685 self
686 }
687
688 pub fn static_crt(&mut self, static_crt: bool) -> &mut Build {
692 self.static_crt = Some(static_crt);
693 self
694 }
695
696 #[doc(hidden)]
697 pub fn __set_env<A, B>(&mut self, a: A, b: B) -> &mut Build
698 where A: AsRef<OsStr>,
699 B: AsRef<OsStr>
700 {
701 self.env.push((a.as_ref().to_owned(), b.as_ref().to_owned()));
702 self
703 }
704
705 pub fn try_compile(&self, output: &str) -> Result<(), Error> {
709 let (lib_name, gnu_lib_name) = if output.starts_with("lib") && output.ends_with(".a") {
710 (&output[3..output.len() - 2], output.to_owned())
711 } else {
712 let mut gnu = String::with_capacity(5 + output.len());
713 gnu.push_str("lib");
714 gnu.push_str(&output);
715 gnu.push_str(".a");
716 (output, gnu)
717 };
718 let dst = self.get_out_dir()?;
719
720 let mut objects = Vec::new();
721 let mut src_dst = Vec::new();
722 for file in self.files.iter() {
723 let obj = dst.join(file).with_extension("o");
724 let obj = if !obj.starts_with(&dst) {
725 dst.join(obj.file_name().ok_or_else(|| Error::new(ErrorKind::IOError, "Getting object file details failed."))?)
726 } else {
727 obj
728 };
729
730 match obj.parent() {
731 Some(s) => fs::create_dir_all(s)?,
732 None => return Err(Error::new(ErrorKind::IOError, "Getting object file details failed.")),
733 };
734
735 src_dst.push((file.to_path_buf(), obj.clone()));
736 objects.push(obj);
737 }
738 self.compile_objects(&src_dst)?;
739 self.assemble(lib_name, &dst.join(gnu_lib_name), &objects)?;
740
741 if self.get_target()?.contains("msvc") {
742 let compiler = self.get_base_compiler()?;
743 let atlmfc_lib = compiler.env()
744 .iter()
745 .find(|&&(ref var, _)| var.as_os_str() == OsStr::new("LIB"))
746 .and_then(|&(_, ref lib_paths)| {
747 env::split_paths(lib_paths).find(|path| {
748 let sub = Path::new("atlmfc/lib");
749 path.ends_with(sub) || path.parent().map_or(false, |p| p.ends_with(sub))
750 })
751 });
752
753 if let Some(atlmfc_lib) = atlmfc_lib {
754 self.print(&format!("cargo:rustc-link-search=native={}", atlmfc_lib.display()));
755 }
756 }
757
758 self.print(&format!("cargo:rustc-link-lib=static={}", lib_name));
759 self.print(&format!("cargo:rustc-link-search=native={}", dst.display()));
760
761 if self.cpp {
763 if let Some(stdlib) = self.get_cpp_link_stdlib()? {
764 self.print(&format!("cargo:rustc-link-lib={}", stdlib));
765 }
766 }
767
768 Ok(())
769 }
770
771 pub fn compile(&self, output: &str) {
784 if let Err(e) = self.try_compile(output) {
785 fail(&e.message);
786 }
787 }
788
789 #[cfg(feature = "parallel")]
790 fn compile_objects(&self, objs: &[(PathBuf, PathBuf)]) -> Result<(), Error> {
791 use self::rayon::prelude::*;
792
793 let mut cfg = rayon::Configuration::new();
794 if let Ok(amt) = env::var("NUM_JOBS") {
795 if let Ok(amt) = amt.parse() {
796 cfg = cfg.num_threads(amt);
797 }
798 }
799 drop(rayon::initialize(cfg));
800
801 let results: Mutex<Vec<Result<(), Error>>> = Mutex::new(Vec::new());
802
803 objs.par_iter().with_max_len(1)
804 .for_each(|&(ref src, ref dst)| results.lock().unwrap().push(self.compile_object(src, dst)));
805
806 for result in results.into_inner().unwrap().iter() {
808 if result.is_err() {
809 return result.clone();
810 }
811 }
812
813 Ok(())
814 }
815
816 #[cfg(not(feature = "parallel"))]
817 fn compile_objects(&self, objs: &[(PathBuf, PathBuf)]) -> Result<(), Error> {
818 for &(ref src, ref dst) in objs {
819 self.compile_object(src, dst)?;
820 }
821 Ok(())
822 }
823
824 fn compile_object(&self, file: &Path, dst: &Path) -> Result<(), Error> {
825 let is_asm = file.extension().and_then(|s| s.to_str()) == Some("asm");
826 let msvc = self.get_target()?.contains("msvc");
827 let (mut cmd, name) = if msvc && is_asm {
828 self.msvc_macro_assembler()?
829 } else {
830 let compiler = self.try_get_compiler()?;
831 let mut cmd = compiler.to_command();
832 for &(ref a, ref b) in self.env.iter() {
833 cmd.env(a, b);
834 }
835 (cmd,
836 compiler.path
837 .file_name()
838 .ok_or_else(|| Error::new(ErrorKind::IOError, "Failed to get compiler path."))?
839 .to_string_lossy()
840 .into_owned())
841 };
842 command_add_output_file(&mut cmd, dst, msvc, is_asm);
843 cmd.arg(if msvc { "/c" } else { "-c" });
844 cmd.arg(file);
845
846 run(&mut cmd, &name)?;
847 Ok(())
848 }
849
850 pub fn try_expand(&self) -> Result<Vec<u8>, Error> {
852 let compiler = self.try_get_compiler()?;
853 let mut cmd = compiler.to_command();
854 for &(ref a, ref b) in self.env.iter() {
855 cmd.env(a, b);
856 }
857 cmd.arg(compiler.family.expand_flag());
858
859 assert!(self.files.len() <= 1,
860 "Expand may only be called for a single file");
861
862 for file in self.files.iter() {
863 cmd.arg(file);
864 }
865
866 let name = compiler.path
867 .file_name()
868 .ok_or_else(|| Error::new(ErrorKind::IOError, "Failed to get compiler path."))?
869 .to_string_lossy()
870 .into_owned();
871
872 Ok(run_output(&mut cmd, &name)?)
873 }
874
875 pub fn expand(&self) -> Vec<u8> {
890 match self.try_expand() {
891 Err(e) => fail(&e.message),
892 Ok(v) => v,
893 }
894 }
895
896 pub fn get_compiler(&self) -> Tool {
915 match self.try_get_compiler() {
916 Ok(tool) => tool,
917 Err(e) => fail(&e.message),
918 }
919 }
920
921 pub fn try_get_compiler(&self) -> Result<Tool, Error> {
925 let opt_level = self.get_opt_level()?;
926 let target = self.get_target()?;
927
928 let mut cmd = self.get_base_compiler()?;
929 let nvcc = cmd.path.file_name()
930 .and_then(|p| p.to_str()).map(|p| p.contains("nvcc"))
931 .unwrap_or(false);
932
933 match cmd.family {
936 ToolFamily::Msvc => {
937 cmd.args.push("/nologo".into());
938
939 let crt_flag = match self.static_crt {
940 Some(true) => "/MT",
941 Some(false) => "/MD",
942 None => {
943 let features = env::var("CARGO_CFG_TARGET_FEATURE")
944 .unwrap_or(String::new());
945 if features.contains("crt-static") {
946 "/MT"
947 } else {
948 "/MD"
949 }
950 },
951 };
952 cmd.args.push(crt_flag.into());
953
954 match &opt_level[..] {
955 "z" | "s" => cmd.args.push("/Os".into()),
956 "1" => cmd.args.push("/O1".into()),
957 "2" | "3" => cmd.args.push("/O2".into()),
959 _ => {}
960 }
961 }
962 ToolFamily::Gnu |
963 ToolFamily::Clang => {
964 if opt_level == "z" && cmd.family != ToolFamily::Clang {
967 cmd.args.push("-Os".into());
968 } else {
969 cmd.args.push(format!("-O{}", opt_level).into());
970 }
971
972 if !nvcc {
973 cmd.args.push("-ffunction-sections".into());
974 cmd.args.push("-fdata-sections".into());
975 if self.pic.unwrap_or(!target.contains("windows-gnu")) {
976 cmd.args.push("-fPIC".into());
977 }
978 } else if self.pic.unwrap_or(false) {
979 cmd.args.push("-Xcompiler".into());
980 cmd.args.push("\'-fPIC\'".into());
981 }
982 }
983 }
984 for arg in self.envflags(if self.cpp {"CXXFLAGS"} else {"CFLAGS"}) {
985 cmd.args.push(arg.into());
986 }
987
988 if self.get_debug() {
989 cmd.args.push(cmd.family.debug_flag().into());
990 }
991
992 match cmd.family {
994 ToolFamily::Clang => {
995 cmd.args.push(format!("--target={}", target).into());
996 }
997 ToolFamily::Msvc => {
998 if target.contains("i586") {
999 cmd.args.push("/ARCH:IA32".into());
1000 }
1001 }
1002 ToolFamily::Gnu => {
1003 if target.contains("i686") || target.contains("i586") {
1004 cmd.args.push("-m32".into());
1005 } else if target.contains("x86_64") || target.contains("powerpc64") {
1006 cmd.args.push("-m64".into());
1007 }
1008
1009 if self.static_flag.is_none() && target.contains("musl") {
1010 cmd.args.push("-static".into());
1011 }
1012
1013 if target.starts_with("armv7-") && target.contains("-linux-") {
1015 cmd.args.push("-march=armv7-a".into());
1016 }
1017
1018 if target.starts_with("armv7-linux-androideabi") {
1021 cmd.args.push("-march=armv7-a".into());
1022 cmd.args.push("-mfpu=vfpv3-d16".into());
1023 cmd.args.push("-mfloat-abi=softfp".into());
1024 }
1025
1026 if target.starts_with("arm-unknown-linux-") {
1028 cmd.args.push("-march=armv6".into());
1029 cmd.args.push("-marm".into());
1030 }
1031
1032 if target.starts_with("arm-frc-") {
1034 cmd.args.push("-march=armv7-a".into());
1035 cmd.args.push("-mcpu=cortex-a9".into());
1036 cmd.args.push("-mfpu=vfpv3".into());
1037 cmd.args.push("-mfloat-abi=softfp".into());
1038 cmd.args.push("-marm".into());
1039 }
1040
1041 if target.starts_with("i586-unknown-linux-") {
1043 cmd.args.push("-march=pentium".into());
1044 }
1045
1046 if target.starts_with("i686-unknown-linux-") {
1048 cmd.args.push("-march=i686".into());
1049 }
1050
1051 if target == "i686-unknown-linux-musl" {
1057 cmd.args.push("-Wl,-melf_i386".into());
1058 }
1059
1060 if target.starts_with("thumb") {
1061 cmd.args.push("-mthumb".into());
1062
1063 if target.ends_with("eabihf") {
1064 cmd.args.push("-mfloat-abi=hard".into())
1065 }
1066 }
1067 if target.starts_with("thumbv6m") {
1068 cmd.args.push("-march=armv6s-m".into());
1069 }
1070 if target.starts_with("thumbv7em") {
1071 cmd.args.push("-march=armv7e-m".into());
1072
1073 if target.ends_with("eabihf") {
1074 cmd.args.push("-mfpu=fpv4-sp-d16".into())
1075 }
1076 }
1077 if target.starts_with("thumbv7m") {
1078 cmd.args.push("-march=armv7-m".into());
1079 }
1080 }
1081 }
1082
1083 if target.contains("-ios") {
1084 self.ios_flags(&mut cmd)?;
1087 }
1088
1089 if self.static_flag.unwrap_or(false) {
1090 cmd.args.push("-static".into());
1091 }
1092 if self.shared_flag.unwrap_or(false) {
1093 cmd.args.push("-shared".into());
1094 }
1095
1096 if self.cpp {
1097 match (self.cpp_set_stdlib.as_ref(), cmd.family) {
1098 (None, _) => { }
1099 (Some(stdlib), ToolFamily::Gnu) |
1100 (Some(stdlib), ToolFamily::Clang) => {
1101 cmd.args.push(format!("-stdlib=lib{}", stdlib).into());
1102 }
1103 _ => {
1104 println!("cargo:warning=cpp_set_stdlib is specified, but the {:?} compiler \
1105 does not support this option, ignored", cmd.family);
1106 }
1107 }
1108 }
1109
1110 for directory in self.include_directories.iter() {
1111 cmd.args.push(cmd.family.include_flag().into());
1112 cmd.args.push(directory.into());
1113 }
1114
1115 for flag in self.flags.iter() {
1116 cmd.args.push(flag.into());
1117 }
1118
1119 for flag in self.flags_supported.iter() {
1120 if self.is_flag_supported(flag).unwrap_or(false) {
1121 cmd.args.push(flag.into());
1122 }
1123 }
1124
1125 for &(ref key, ref value) in self.definitions.iter() {
1126 let lead = if let ToolFamily::Msvc = cmd.family {"/"} else {"-"};
1127 if let Some(ref value) = *value {
1128 cmd.args.push(format!("{}D{}={}", lead, key, value).into());
1129 } else {
1130 cmd.args.push(format!("{}D{}", lead, key).into());
1131 }
1132 }
1133
1134 if self.warnings {
1135 for flag in cmd.family.warnings_flags().iter() {
1136 cmd.args.push(flag.into());
1137 }
1138 }
1139
1140 if self.warnings_into_errors {
1141 cmd.args.push(cmd.family.warnings_to_errors_flag().into());
1142 }
1143
1144 Ok(cmd)
1145 }
1146
1147 fn msvc_macro_assembler(&self) -> Result<(Command, String), Error> {
1148 let target = self.get_target()?;
1149 let tool = if target.contains("x86_64") {
1150 "ml64.exe"
1151 } else {
1152 "ml.exe"
1153 };
1154 let mut cmd = windows_registry::find(&target, tool).unwrap_or_else(|| self.cmd(tool));
1155 for directory in self.include_directories.iter() {
1156 cmd.arg("/I").arg(directory);
1157 }
1158 for &(ref key, ref value) in self.definitions.iter() {
1159 if let Some(ref value) = *value {
1160 cmd.arg(&format!("/D{}={}", key, value));
1161 } else {
1162 cmd.arg(&format!("/D{}", key));
1163 }
1164 }
1165
1166 if target.contains("i686") || target.contains("i586") {
1167 cmd.arg("/safeseh");
1168 }
1169 for flag in self.flags.iter() {
1170 cmd.arg(flag);
1171 }
1172
1173 Ok((cmd, tool.to_string()))
1174 }
1175
1176 fn assemble(&self, lib_name: &str, dst: &Path, objects: &[PathBuf]) -> Result<(), Error> {
1177 let _ = fs::remove_file(&dst);
1180
1181 let target = self.get_target()?;
1182 if target.contains("msvc") {
1183 let mut cmd = match self.archiver {
1184 Some(ref s) => self.cmd(s),
1185 None => windows_registry::find(&target, "lib.exe").unwrap_or_else(|| self.cmd("lib.exe")),
1186 };
1187 let mut out = OsString::from("/OUT:");
1188 out.push(dst);
1189 run(cmd.arg(out)
1190 .arg("/nologo")
1191 .args(objects)
1192 .args(&self.objects),
1193 "lib.exe")?;
1194
1195 let lib_dst = dst.with_file_name(format!("{}.lib", lib_name));
1199 let _ = fs::remove_file(&lib_dst);
1200 match fs::hard_link(&dst, &lib_dst)
1201 .or_else(|_| {
1202 fs::copy(&dst, &lib_dst).map(|_| ())
1204 }) {
1205 Ok(_) => (),
1206 Err(_) => return Err(Error::new(ErrorKind::IOError, "Could not copy or create a hard-link to the generated lib file.")),
1207 };
1208 } else {
1209 let ar = self.get_ar()?;
1210 let cmd = ar.file_name()
1211 .ok_or_else(|| Error::new(ErrorKind::IOError, "Failed to get archiver (ar) path."))?
1212 .to_string_lossy();
1213 run(self.cmd(&ar)
1214 .arg("crs")
1215 .arg(dst)
1216 .args(objects)
1217 .args(&self.objects),
1218 &cmd)?;
1219 }
1220
1221 Ok(())
1222 }
1223
1224 fn ios_flags(&self, cmd: &mut Tool) -> Result<(), Error> {
1225 enum ArchSpec {
1226 Device(&'static str),
1227 Simulator(&'static str),
1228 }
1229
1230 let target = self.get_target()?;
1231 let arch = target.split('-').nth(0).ok_or_else(|| Error::new(ErrorKind::ArchitectureInvalid, "Unknown architecture for iOS target."))?;
1232 let arch = match arch {
1233 "arm" | "armv7" | "thumbv7" => ArchSpec::Device("armv7"),
1234 "armv7s" | "thumbv7s" => ArchSpec::Device("armv7s"),
1235 "arm64" | "aarch64" => ArchSpec::Device("arm64"),
1236 "i386" | "i686" => ArchSpec::Simulator("-m32"),
1237 "x86_64" => ArchSpec::Simulator("-m64"),
1238 _ => return Err(Error::new(ErrorKind::ArchitectureInvalid, "Unknown architecture for iOS target.")),
1239 };
1240
1241 let sdk = match arch {
1242 ArchSpec::Device(arch) => {
1243 cmd.args.push("-arch".into());
1244 cmd.args.push(arch.into());
1245 cmd.args.push("-miphoneos-version-min=7.0".into());
1246 "iphoneos"
1247 }
1248 ArchSpec::Simulator(arch) => {
1249 cmd.args.push(arch.into());
1250 cmd.args.push("-mios-simulator-version-min=7.0".into());
1251 "iphonesimulator"
1252 }
1253 };
1254
1255 self.print(&format!("Detecting iOS SDK path for {}", sdk));
1256 let sdk_path = self.cmd("xcrun")
1257 .arg("--show-sdk-path")
1258 .arg("--sdk")
1259 .arg(sdk)
1260 .stderr(Stdio::inherit())
1261 .output()?
1262 .stdout;
1263
1264 let sdk_path = match String::from_utf8(sdk_path) {
1265 Ok(p) => p,
1266 Err(_) => return Err(Error::new(ErrorKind::IOError, "Unable to determine iOS SDK path.")),
1267 };
1268
1269 cmd.args.push("-isysroot".into());
1270 cmd.args.push(sdk_path.trim().into());
1271
1272 Ok(())
1273 }
1274
1275 fn cmd<P: AsRef<OsStr>>(&self, prog: P) -> Command {
1276 let mut cmd = Command::new(prog);
1277 for &(ref a, ref b) in self.env.iter() {
1278 cmd.env(a, b);
1279 }
1280 cmd
1281 }
1282
1283 fn get_base_compiler(&self) -> Result<Tool, Error> {
1284 if let Some(ref c) = self.compiler {
1285 return Ok(Tool::new(c.clone()));
1286 }
1287 let host = self.get_host()?;
1288 let target = self.get_target()?;
1289 let (env, msvc, gnu) = if self.cpp {
1290 ("CXX", "cl.exe", "g++")
1291 } else {
1292 ("CC", "cl.exe", "gcc")
1293 };
1294
1295 let default = if host.contains("solaris") {
1296 gnu
1298 } else if self.cpp {
1299 "c++"
1300 } else {
1301 "cc"
1302 };
1303
1304 let tool_opt: Option<Tool> = self.env_tool(env)
1305 .map(|(tool, args)| {
1306 let mut t = Tool::new(PathBuf::from(tool));
1307 for arg in args {
1308 t.args.push(arg.into());
1309 }
1310 t
1311 })
1312 .or_else(|| {
1313 if target.contains("emscripten") {
1314 let tool = if self.cpp {
1315 "em++"
1316 } else {
1317 "emcc"
1318 };
1319 if cfg!(windows) {
1321 let mut t = Tool::new(PathBuf::from("cmd"));
1322 t.args.push("/c".into());
1323 t.args.push(format!("{}.bat", tool).into());
1324 Some(t)
1325 } else {
1326 Some(Tool::new(PathBuf::from(tool)))
1327 }
1328 } else {
1329 None
1330 }
1331 })
1332 .or_else(|| windows_registry::find_tool(&target, "cl.exe"));
1333
1334 let tool = match tool_opt {
1335 Some(t) => t,
1336 None => {
1337 let compiler = if host.contains("windows") && target.contains("windows") {
1338 if target.contains("msvc") {
1339 msvc.to_string()
1340 } else {
1341 format!("{}.exe", gnu)
1342 }
1343 } else if target.contains("android") {
1344 format!("{}-{}", target.replace("armv7", "arm"), gnu)
1345 } else if self.get_host()? != target {
1346 let cc_env = self.getenv("CROSS_COMPILE");
1348 let cross_compile = cc_env.as_ref().map(|s| s.trim_right_matches('-'));
1349 let prefix = cross_compile.or(match &target[..] {
1350 "aarch64-unknown-linux-gnu" => Some("aarch64-linux-gnu"),
1351 "arm-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"),
1352 "arm-frc-linux-gnueabi" => Some("arm-frc-linux-gnueabi"),
1353 "arm-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"),
1354 "arm-unknown-linux-musleabi" => Some("arm-linux-musleabi"),
1355 "arm-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"),
1356 "arm-unknown-netbsd-eabi" => Some("arm--netbsdelf-eabi"),
1357 "armv6-unknown-netbsd-eabihf" => Some("armv6--netbsdelf-eabihf"),
1358 "armv7-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"),
1359 "armv7-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"),
1360 "armv7-unknown-netbsd-eabihf" => Some("armv7--netbsdelf-eabihf"),
1361 "i686-pc-windows-gnu" => Some("i686-w64-mingw32"),
1362 "i686-unknown-linux-musl" => Some("musl"),
1363 "i686-unknown-netbsd" => Some("i486--netbsdelf"),
1364 "mips-unknown-linux-gnu" => Some("mips-linux-gnu"),
1365 "mipsel-unknown-linux-gnu" => Some("mipsel-linux-gnu"),
1366 "mips64-unknown-linux-gnuabi64" => Some("mips64-linux-gnuabi64"),
1367 "mips64el-unknown-linux-gnuabi64" => Some("mips64el-linux-gnuabi64"),
1368 "powerpc-unknown-linux-gnu" => Some("powerpc-linux-gnu"),
1369 "powerpc-unknown-netbsd" => Some("powerpc--netbsd"),
1370 "powerpc64-unknown-linux-gnu" => Some("powerpc-linux-gnu"),
1371 "powerpc64le-unknown-linux-gnu" => Some("powerpc64le-linux-gnu"),
1372 "s390x-unknown-linux-gnu" => Some("s390x-linux-gnu"),
1373 "sparc64-unknown-netbsd" => Some("sparc64--netbsd"),
1374 "sparcv9-sun-solaris" => Some("sparcv9-sun-solaris"),
1375 "thumbv6m-none-eabi" => Some("arm-none-eabi"),
1376 "thumbv7em-none-eabi" => Some("arm-none-eabi"),
1377 "thumbv7em-none-eabihf" => Some("arm-none-eabi"),
1378 "thumbv7m-none-eabi" => Some("arm-none-eabi"),
1379 "x86_64-pc-windows-gnu" => Some("x86_64-w64-mingw32"),
1380 "x86_64-rumprun-netbsd" => Some("x86_64-rumprun-netbsd"),
1381 "x86_64-unknown-linux-musl" => Some("musl"),
1382 "x86_64-unknown-netbsd" => Some("x86_64--netbsd"),
1383 _ => None,
1384 });
1385 match prefix {
1386 Some(prefix) => format!("{}-{}", prefix, gnu),
1387 None => default.to_string(),
1388 }
1389 } else {
1390 default.to_string()
1391 };
1392 Tool::new(PathBuf::from(compiler))
1393 }
1394 };
1395
1396 Ok(tool)
1397 }
1398
1399 fn get_var(&self, var_base: &str) -> Result<String, Error> {
1400 let target = self.get_target()?;
1401 let host = self.get_host()?;
1402 let kind = if host == target { "HOST" } else { "TARGET" };
1403 let target_u = target.replace("-", "_");
1404 let res = self.getenv(&format!("{}_{}", var_base, target))
1405 .or_else(|| self.getenv(&format!("{}_{}", var_base, target_u)))
1406 .or_else(|| self.getenv(&format!("{}_{}", kind, var_base)))
1407 .or_else(|| self.getenv(var_base));
1408
1409 match res {
1410 Some(res) => Ok(res),
1411 None => Err(Error::new(ErrorKind::EnvVarNotFound, &format!("Could not find environment variable {}.", var_base))),
1412 }
1413 }
1414
1415 fn envflags(&self, name: &str) -> Vec<String> {
1416 self.get_var(name)
1417 .unwrap_or(String::new())
1418 .split(|c: char| c.is_whitespace())
1419 .filter(|s| !s.is_empty())
1420 .map(|s| s.to_string())
1421 .collect()
1422 }
1423
1424 fn env_tool(&self, name: &str) -> Option<(String, Vec<String>)> {
1425 self.get_var(name).ok().map(|tool| {
1426 let whitelist = ["ccache", "distcc", "sccache"];
1427 for t in whitelist.iter() {
1428 if tool.starts_with(t) && tool[t.len()..].starts_with(' ') {
1429 return (t.to_string(), vec![tool[t.len()..].trim_left().to_string()]);
1430 }
1431 }
1432 (tool, Vec::new())
1433 })
1434 }
1435
1436 fn get_cpp_link_stdlib(&self) -> Result<Option<String>, Error> {
1439 match self.cpp_link_stdlib.clone() {
1440 Some(s) => Ok(s),
1441 None => {
1442 let target = self.get_target()?;
1443 if target.contains("msvc") {
1444 Ok(None)
1445 } else if target.contains("darwin") {
1446 Ok(Some("c++".to_string()))
1447 } else if target.contains("freebsd") {
1448 Ok(Some("c++".to_string()))
1449 } else {
1450 Ok(Some("stdc++".to_string()))
1451 }
1452 },
1453 }
1454 }
1455
1456 fn get_ar(&self) -> Result<PathBuf, Error> {
1457 match self.archiver
1458 .clone()
1459 .or_else(|| self.get_var("AR").map(PathBuf::from).ok()) {
1460 Some(p) => Ok(p),
1461 None => {
1462 if self.get_target()?.contains("android") {
1463 Ok(PathBuf::from(format!("{}-ar", self.get_target()?.replace("armv7", "arm"))))
1464 } else if self.get_target()?.contains("emscripten") {
1465 let tool = if cfg!(windows) {
1467 "emar.bat"
1468 } else {
1469 "emar"
1470 };
1471
1472 Ok(PathBuf::from(tool))
1473 } else {
1474 Ok(PathBuf::from("ar"))
1475 }
1476 }
1477 }
1478 }
1479
1480 fn get_target(&self) -> Result<String, Error> {
1481 match self.target.clone() {
1482 Some(t) => Ok(t),
1483 None => Ok(self.getenv_unwrap("TARGET")?),
1484 }
1485 }
1486
1487 fn get_host(&self) -> Result<String, Error> {
1488 match self.host.clone() {
1489 Some(h) => Ok(h),
1490 None => Ok(self.getenv_unwrap("HOST")?),
1491 }
1492 }
1493
1494 fn get_opt_level(&self) -> Result<String, Error> {
1495 match self.opt_level.as_ref().cloned() {
1496 Some(ol) => Ok(ol),
1497 None => Ok(self.getenv_unwrap("OPT_LEVEL")?),
1498 }
1499 }
1500
1501 fn get_debug(&self) -> bool {
1502 self.debug.unwrap_or_else(|| {
1503 match self.getenv("DEBUG") {
1504 Some(s) => s != "false",
1505 None => false,
1506 }
1507 })
1508 }
1509
1510 fn get_out_dir(&self) -> Result<PathBuf, Error> {
1511 match self.out_dir.clone() {
1512 Some(p) => Ok(p),
1513 None => Ok(env::var_os("OUT_DIR")
1514 .map(PathBuf::from)
1515 .ok_or_else(|| Error::new(ErrorKind::EnvVarNotFound, "Environment variable OUT_DIR not defined."))?),
1516 }
1517 }
1518
1519 fn getenv(&self, v: &str) -> Option<String> {
1520 let r = env::var(v).ok();
1521 self.print(&format!("{} = {:?}", v, r));
1522 r
1523 }
1524
1525 fn getenv_unwrap(&self, v: &str) -> Result<String, Error> {
1526 match self.getenv(v) {
1527 Some(s) => Ok(s),
1528 None => Err(Error::new(ErrorKind::EnvVarNotFound, &format!("Environment variable {} not defined.", v.to_string()))),
1529 }
1530 }
1531
1532 fn print(&self, s: &str) {
1533 if self.cargo_metadata {
1534 println!("{}", s);
1535 }
1536 }
1537}
1538
1539impl Default for Build {
1540 fn default() -> Build {
1541 Build::new()
1542 }
1543}
1544
1545impl Tool {
1546 fn new(path: PathBuf) -> Tool {
1547 let family = if let Some(fname) = path.file_name().and_then(|p| p.to_str()) {
1549 if fname.contains("clang") {
1550 ToolFamily::Clang
1551 } else if fname.contains("cl") && !fname.contains("uclibc") {
1552 ToolFamily::Msvc
1553 } else {
1554 ToolFamily::Gnu
1555 }
1556 } else {
1557 ToolFamily::Gnu
1558 };
1559 Tool {
1560 path: path,
1561 args: Vec::new(),
1562 env: Vec::new(),
1563 family: family
1564 }
1565 }
1566
1567 pub fn to_command(&self) -> Command {
1573 let mut cmd = Command::new(&self.path);
1574 cmd.args(&self.args);
1575 for &(ref k, ref v) in self.env.iter() {
1576 cmd.env(k, v);
1577 }
1578 cmd
1579 }
1580
1581 pub fn path(&self) -> &Path {
1586 &self.path
1587 }
1588
1589 pub fn args(&self) -> &[OsString] {
1592 &self.args
1593 }
1594
1595 pub fn env(&self) -> &[(OsString, OsString)] {
1600 &self.env
1601 }
1602}
1603
1604fn run(cmd: &mut Command, program: &str) -> Result<(), Error> {
1605 let (mut child, print) = spawn(cmd, program)?;
1606 let status = match child.wait() {
1607 Ok(s) => s,
1608 Err(_) => return Err(Error::new(ErrorKind::ToolExecError, &format!("Failed to wait on spawned child process, command {:?} with args {:?}.", cmd, program))),
1609 };
1610 print.join().unwrap();
1611 println!("{}", status);
1612
1613 if status.success() {
1614 Ok(())
1615 } else {
1616 Err(Error::new(ErrorKind::ToolExecError, &format!("Command {:?} with args {:?} did not execute successfully (status code {}).", cmd, program, status)))
1617 }
1618}
1619
1620fn run_output(cmd: &mut Command, program: &str) -> Result<Vec<u8>, Error> {
1621 cmd.stdout(Stdio::piped());
1622 let (mut child, print) = spawn(cmd, program)?;
1623 let mut stdout = vec![];
1624 child.stdout.take().unwrap().read_to_end(&mut stdout).unwrap();
1625 let status = match child.wait() {
1626 Ok(s) => s,
1627 Err(_) => return Err(Error::new(ErrorKind::ToolExecError, &format!("Failed to wait on spawned child process, command {:?} with args {:?}.", cmd, program))),
1628 };
1629 print.join().unwrap();
1630 println!("{}", status);
1631
1632 if status.success() {
1633 Ok(stdout)
1634 } else {
1635 Err(Error::new(ErrorKind::ToolExecError, &format!("Command {:?} with args {:?} did not execute successfully (status code {}).", cmd, program, status)))
1636 }
1637}
1638
1639fn spawn(cmd: &mut Command, program: &str) -> Result<(Child, JoinHandle<()>), Error> {
1640 println!("running: {:?}", cmd);
1641
1642 match cmd.stderr(Stdio::piped()).spawn() {
1647 Ok(mut child) => {
1648 let stderr = BufReader::new(child.stderr.take().unwrap());
1649 let print = thread::spawn(move || {
1650 for line in stderr.split(b'\n').filter_map(|l| l.ok()) {
1651 print!("cargo:warning=");
1652 std::io::stdout().write_all(&line).unwrap();
1653 println!("");
1654 }
1655 });
1656 Ok((child, print))
1657 }
1658 Err(ref e) if e.kind() == io::ErrorKind::NotFound => {
1659 let extra = if cfg!(windows) {
1660 " (see https://github.com/alexcrichton/gcc-rs#compile-time-requirements \
1661 for help)"
1662 } else {
1663 ""
1664 };
1665 Err(Error::new(ErrorKind::ToolNotFound, &format!("Failed to find tool. Is `{}` installed?{}", program, extra)))
1666 }
1667 Err(_) => Err(Error::new(ErrorKind::ToolExecError, &format!("Command {:?} with args {:?} failed to start.", cmd, program))),
1668 }
1669}
1670
1671fn fail(s: &str) -> ! {
1672 panic!("\n\nInternal error occurred: {}\n\n", s)
1673}
1674
1675
1676fn command_add_output_file(cmd: &mut Command, dst: &Path, msvc: bool, is_asm: bool) {
1677 if msvc && is_asm {
1678 cmd.arg("/Fo").arg(dst);
1679 } else if msvc {
1680 let mut s = OsString::from("/Fo");
1681 s.push(&dst);
1682 cmd.arg(s);
1683 } else {
1684 cmd.arg("-o").arg(&dst);
1685 }
1686}