1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! C-compiler probing and detection.
13 //! This module will fill out the `cc` and `cxx` maps of `Build` by looking for
14 //! C and C++ compilers for each target configured. A compiler is found through
15 //! a number of vectors (in order of precedence)
17 //! 1. Configuration via `target.$target.cc` in `config.toml`.
18 //! 2. Configuration via `target.$target.android-ndk` in `config.toml`, if
20 //! 3. Special logic to probe on OpenBSD
21 //! 4. The `CC_$target` environment variable.
22 //! 5. The `CC` environment variable.
25 //! Some of this logic is implemented here, but much of it is farmed out to the
26 //! `cc` crate itself, so we end up having the same fallbacks as there.
27 //! Similar logic is then used to find a C++ compiler, just some s/cc/c++/ is
30 //! It is intended that after this module has run no C/C++ compiler will
31 //! ever be probed for. Instead the compilers found here will be used for
34 use std::collections::HashSet;
36 use std::path::{Path, PathBuf};
37 use std::process::Command;
39 use build_helper::output;
42 use crate::{Build, GitRepo};
43 use crate::config::Target;
44 use crate::cache::Interned;
46 // The `cc` crate doesn't provide a way to obtain a path to the detected archiver,
47 // so use some simplified logic here. First we respect the environment variable `AR`, then
48 // try to infer the archiver path from the C compiler path.
49 // In the future this logic should be replaced by calling into the `cc` crate.
50 fn cc2ar(cc: &Path, target: &str) -> Option<PathBuf> {
51 if let Some(ar) = env::var_os("AR") {
52 Some(PathBuf::from(ar))
53 } else if target.contains("msvc") {
55 } else if target.contains("musl") {
56 Some(PathBuf::from("ar"))
57 } else if target.contains("openbsd") {
58 Some(PathBuf::from("ar"))
60 let parent = cc.parent().unwrap();
61 let file = cc.file_name().unwrap().to_str().unwrap();
62 for suffix in &["gcc", "cc", "clang"] {
63 if let Some(idx) = file.rfind(suffix) {
64 let mut file = file[..idx].to_owned();
66 return Some(parent.join(&file));
69 Some(parent.join(file))
73 pub fn find(build: &mut Build) {
74 // For all targets we're going to need a C compiler for building some shims
75 // and such as well as for being a linker for Rust code.
76 let targets = build.targets.iter().chain(&build.hosts).cloned().chain(iter::once(build.build))
77 .collect::<HashSet<_>>();
78 for target in targets.into_iter() {
79 let mut cfg = cc::Build::new();
80 cfg.cargo_metadata(false).opt_level(2).warnings(false).debug(false)
81 .target(&target).host(&build.build);
82 match build.crt_static(target) {
83 Some(a) => { cfg.static_crt(a); }
85 if target.contains("msvc") {
88 if target.contains("musl") {
89 cfg.static_flag(true);
94 let config = build.config.target_config.get(&target);
95 if let Some(cc) = config.and_then(|c| c.cc.as_ref()) {
98 set_compiler(&mut cfg, Language::C, target, config, build);
101 let compiler = cfg.get_compiler();
102 let ar = if let ar @ Some(..) = config.and_then(|c| c.ar.clone()) {
105 cc2ar(compiler.path(), &target)
108 build.cc.insert(target, compiler);
109 build.verbose(&format!("CC_{} = {:?}", &target, build.cc(target)));
110 build.verbose(&format!("CFLAGS_{} = {:?}", &target, build.cflags(target, GitRepo::Rustc)));
111 if let Some(ar) = ar {
112 build.verbose(&format!("AR_{} = {:?}", &target, ar));
113 build.ar.insert(target, ar);
117 // For all host triples we need to find a C++ compiler as well
118 let hosts = build.hosts.iter().cloned().chain(iter::once(build.build)).collect::<HashSet<_>>();
119 for host in hosts.into_iter() {
120 let mut cfg = cc::Build::new();
121 cfg.cargo_metadata(false).opt_level(2).warnings(false).debug(false).cpp(true)
122 .target(&host).host(&build.build);
123 let config = build.config.target_config.get(&host);
124 if let Some(cxx) = config.and_then(|c| c.cxx.as_ref()) {
127 set_compiler(&mut cfg, Language::CPlusPlus, host, config, build);
129 let compiler = cfg.get_compiler();
130 build.verbose(&format!("CXX_{} = {:?}", host, compiler.path()));
131 build.cxx.insert(host, compiler);
135 fn set_compiler(cfg: &mut cc::Build,
137 target: Interned<String>,
138 config: Option<&Target>,
141 // When compiling for android we may have the NDK configured in the
142 // config.toml in which case we look there. Otherwise the default
143 // compiler already takes into account the triple in question.
144 t if t.contains("android") => {
145 if let Some(ndk) = config.and_then(|c| c.ndk.as_ref()) {
146 let target = target.replace("armv7neon", "arm")
147 .replace("armv7", "arm")
148 .replace("thumbv7neon", "arm")
149 .replace("thumbv7", "arm");
150 let compiler = format!("{}-{}", target, compiler.clang());
151 cfg.compiler(ndk.join("bin").join(compiler));
155 // The default gcc version from OpenBSD may be too old, try using egcc,
156 // which is a gcc version from ports, if this is the case.
157 t if t.contains("openbsd") => {
158 let c = cfg.get_compiler();
159 let gnu_compiler = compiler.gcc();
160 if !c.path().ends_with(gnu_compiler) {
164 let output = output(c.to_command().arg("--version"));
165 let i = match output.find(" 4.") {
169 match output[i + 3..].chars().next().unwrap() {
173 let alternative = format!("e{}", gnu_compiler);
174 if Command::new(&alternative).output().is_ok() {
175 cfg.compiler(alternative);
179 "mips-unknown-linux-musl" => {
180 if cfg.get_compiler().path().to_str() == Some("gcc") {
181 cfg.compiler("mips-linux-musl-gcc");
184 "mipsel-unknown-linux-musl" => {
185 if cfg.get_compiler().path().to_str() == Some("gcc") {
186 cfg.compiler("mipsel-linux-musl-gcc");
190 t if t.contains("musl") => {
191 if let Some(root) = build.musl_root(target) {
192 let guess = root.join("bin/musl-gcc");
203 /// The target programming language for a native compiler.
205 /// The compiler is targeting C.
207 /// The compiler is targeting C++.
212 /// Obtains the name of a compiler in the GCC collection.
213 fn gcc(self) -> &'static str {
215 Language::C => "gcc",
216 Language::CPlusPlus => "g++",
220 /// Obtains the name of a compiler in the clang suite.
221 fn clang(self) -> &'static str {
223 Language::C => "clang",
224 Language::CPlusPlus => "clang++",