]> git.lizzy.rs Git - rust.git/blob - src/bootstrap/cc_detect.rs
Auto merge of #56827 - faern:eliminate-recv-timeout-panic, r=KodrAus
[rust.git] / src / bootstrap / cc_detect.rs
1 //! C-compiler probing and detection.
2 //!
3 //! This module will fill out the `cc` and `cxx` maps of `Build` by looking for
4 //! C and C++ compilers for each target configured. A compiler is found through
5 //! a number of vectors (in order of precedence)
6 //!
7 //! 1. Configuration via `target.$target.cc` in `config.toml`.
8 //! 2. Configuration via `target.$target.android-ndk` in `config.toml`, if
9 //!    applicable
10 //! 3. Special logic to probe on OpenBSD
11 //! 4. The `CC_$target` environment variable.
12 //! 5. The `CC` environment variable.
13 //! 6. "cc"
14 //!
15 //! Some of this logic is implemented here, but much of it is farmed out to the
16 //! `cc` crate itself, so we end up having the same fallbacks as there.
17 //! Similar logic is then used to find a C++ compiler, just some s/cc/c++/ is
18 //! used.
19 //!
20 //! It is intended that after this module has run no C/C++ compiler will
21 //! ever be probed for. Instead the compilers found here will be used for
22 //! everything.
23
24 use std::collections::HashSet;
25 use std::{env, iter};
26 use std::path::{Path, PathBuf};
27 use std::process::Command;
28
29 use build_helper::output;
30 use cc;
31
32 use crate::{Build, GitRepo};
33 use crate::config::Target;
34 use crate::cache::Interned;
35
36 // The `cc` crate doesn't provide a way to obtain a path to the detected archiver,
37 // so use some simplified logic here. First we respect the environment variable `AR`, then
38 // try to infer the archiver path from the C compiler path.
39 // In the future this logic should be replaced by calling into the `cc` crate.
40 fn cc2ar(cc: &Path, target: &str) -> Option<PathBuf> {
41     if let Some(ar) = env::var_os("AR") {
42         Some(PathBuf::from(ar))
43     } else if target.contains("msvc") {
44         None
45     } else if target.contains("musl") {
46         Some(PathBuf::from("ar"))
47     } else if target.contains("openbsd") {
48         Some(PathBuf::from("ar"))
49     } else {
50         let parent = cc.parent().unwrap();
51         let file = cc.file_name().unwrap().to_str().unwrap();
52         for suffix in &["gcc", "cc", "clang"] {
53             if let Some(idx) = file.rfind(suffix) {
54                 let mut file = file[..idx].to_owned();
55                 file.push_str("ar");
56                 return Some(parent.join(&file));
57             }
58         }
59         Some(parent.join(file))
60     }
61 }
62
63 pub fn find(build: &mut Build) {
64     // For all targets we're going to need a C compiler for building some shims
65     // and such as well as for being a linker for Rust code.
66     let targets = build.targets.iter().chain(&build.hosts).cloned().chain(iter::once(build.build))
67                                .collect::<HashSet<_>>();
68     for target in targets.into_iter() {
69         let mut cfg = cc::Build::new();
70         cfg.cargo_metadata(false).opt_level(2).warnings(false).debug(false)
71            .target(&target).host(&build.build);
72         match build.crt_static(target) {
73             Some(a) => { cfg.static_crt(a); }
74             None => {
75                 if target.contains("msvc") {
76                     cfg.static_crt(true);
77                 }
78                 if target.contains("musl") {
79                     cfg.static_flag(true);
80                 }
81             }
82         }
83
84         let config = build.config.target_config.get(&target);
85         if let Some(cc) = config.and_then(|c| c.cc.as_ref()) {
86             cfg.compiler(cc);
87         } else {
88             set_compiler(&mut cfg, Language::C, target, config, build);
89         }
90
91         let compiler = cfg.get_compiler();
92         let ar = if let ar @ Some(..) = config.and_then(|c| c.ar.clone()) {
93             ar
94         } else {
95             cc2ar(compiler.path(), &target)
96         };
97
98         build.cc.insert(target, compiler);
99         build.verbose(&format!("CC_{} = {:?}", &target, build.cc(target)));
100         build.verbose(&format!("CFLAGS_{} = {:?}", &target, build.cflags(target, GitRepo::Rustc)));
101         if let Some(ar) = ar {
102             build.verbose(&format!("AR_{} = {:?}", &target, ar));
103             build.ar.insert(target, ar);
104         }
105     }
106
107     // For all host triples we need to find a C++ compiler as well
108     let hosts = build.hosts.iter().cloned().chain(iter::once(build.build)).collect::<HashSet<_>>();
109     for host in hosts.into_iter() {
110         let mut cfg = cc::Build::new();
111         cfg.cargo_metadata(false).opt_level(2).warnings(false).debug(false).cpp(true)
112            .target(&host).host(&build.build);
113         let config = build.config.target_config.get(&host);
114         if let Some(cxx) = config.and_then(|c| c.cxx.as_ref()) {
115             cfg.compiler(cxx);
116         } else {
117             set_compiler(&mut cfg, Language::CPlusPlus, host, config, build);
118         }
119         let compiler = cfg.get_compiler();
120         build.verbose(&format!("CXX_{} = {:?}", host, compiler.path()));
121         build.cxx.insert(host, compiler);
122     }
123 }
124
125 fn set_compiler(cfg: &mut cc::Build,
126                 compiler: Language,
127                 target: Interned<String>,
128                 config: Option<&Target>,
129                 build: &Build) {
130     match &*target {
131         // When compiling for android we may have the NDK configured in the
132         // config.toml in which case we look there. Otherwise the default
133         // compiler already takes into account the triple in question.
134         t if t.contains("android") => {
135             if let Some(ndk) = config.and_then(|c| c.ndk.as_ref()) {
136                 let target = target.replace("armv7neon", "arm")
137                                    .replace("armv7", "arm")
138                                    .replace("thumbv7neon", "arm")
139                                    .replace("thumbv7", "arm");
140                 let compiler = format!("{}-{}", target, compiler.clang());
141                 cfg.compiler(ndk.join("bin").join(compiler));
142             }
143         }
144
145         // The default gcc version from OpenBSD may be too old, try using egcc,
146         // which is a gcc version from ports, if this is the case.
147         t if t.contains("openbsd") => {
148             let c = cfg.get_compiler();
149             let gnu_compiler = compiler.gcc();
150             if !c.path().ends_with(gnu_compiler) {
151                 return
152             }
153
154             let output = output(c.to_command().arg("--version"));
155             let i = match output.find(" 4.") {
156                 Some(i) => i,
157                 None => return,
158             };
159             match output[i + 3..].chars().next().unwrap() {
160                 '0' ... '6' => {}
161                 _ => return,
162             }
163             let alternative = format!("e{}", gnu_compiler);
164             if Command::new(&alternative).output().is_ok() {
165                 cfg.compiler(alternative);
166             }
167         }
168
169         "mips-unknown-linux-musl" => {
170             if cfg.get_compiler().path().to_str() == Some("gcc") {
171                 cfg.compiler("mips-linux-musl-gcc");
172             }
173         }
174         "mipsel-unknown-linux-musl" => {
175             if cfg.get_compiler().path().to_str() == Some("gcc") {
176                 cfg.compiler("mipsel-linux-musl-gcc");
177             }
178         }
179
180         t if t.contains("musl") => {
181             if let Some(root) = build.musl_root(target) {
182                 let guess = root.join("bin/musl-gcc");
183                 if guess.exists() {
184                     cfg.compiler(guess);
185                 }
186             }
187         }
188
189         _ => {}
190     }
191 }
192
193 /// The target programming language for a native compiler.
194 enum Language {
195     /// The compiler is targeting C.
196     C,
197     /// The compiler is targeting C++.
198     CPlusPlus,
199 }
200
201 impl Language {
202     /// Obtains the name of a compiler in the GCC collection.
203     fn gcc(self) -> &'static str {
204         match self {
205             Language::C => "gcc",
206             Language::CPlusPlus => "g++",
207         }
208     }
209
210     /// Obtains the name of a compiler in the clang suite.
211     fn clang(self) -> &'static str {
212         match self {
213             Language::C => "clang",
214             Language::CPlusPlus => "clang++",
215         }
216     }
217 }