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;
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 if target.contains("msvc") {
86 let config = build.config.target_config.get(&target);
87 if let Some(cc) = config.and_then(|c| c.cc.as_ref()) {
90 set_compiler(&mut cfg, Language::C, target, config, build);
93 let compiler = cfg.get_compiler();
94 let ar = if let ar @ Some(..) = config.and_then(|c| c.ar.clone()) {
97 cc2ar(compiler.path(), &target)
100 build.verbose(&format!("CC_{} = {:?}", &target, compiler.path()));
101 build.cc.insert(target, compiler);
102 if let Some(ar) = ar {
103 build.verbose(&format!("AR_{} = {:?}", &target, ar));
104 build.ar.insert(target, ar);
108 // For all host triples we need to find a C++ compiler as well
109 let hosts = build.hosts.iter().cloned().chain(iter::once(build.build)).collect::<HashSet<_>>();
110 for host in hosts.into_iter() {
111 let mut cfg = cc::Build::new();
112 cfg.cargo_metadata(false).opt_level(2).warnings(false).debug(false).cpp(true)
113 .target(&host).host(&build.build);
114 let config = build.config.target_config.get(&host);
115 if let Some(cxx) = config.and_then(|c| c.cxx.as_ref()) {
118 set_compiler(&mut cfg, Language::CPlusPlus, host, config, build);
120 let compiler = cfg.get_compiler();
121 build.verbose(&format!("CXX_{} = {:?}", host, compiler.path()));
122 build.cxx.insert(host, compiler);
126 fn set_compiler(cfg: &mut cc::Build,
128 target: Interned<String>,
129 config: Option<&Target>,
132 // When compiling for android we may have the NDK configured in the
133 // config.toml in which case we look there. Otherwise the default
134 // compiler already takes into account the triple in question.
135 t if t.contains("android") => {
136 if let Some(ndk) = config.and_then(|c| c.ndk.as_ref()) {
137 let target = target.replace("armv7", "arm");
138 let compiler = format!("{}-{}", target, compiler.clang());
139 cfg.compiler(ndk.join("bin").join(compiler));
143 // The default gcc version from OpenBSD may be too old, try using egcc,
144 // which is a gcc version from ports, if this is the case.
145 t if t.contains("openbsd") => {
146 let c = cfg.get_compiler();
147 let gnu_compiler = compiler.gcc();
148 if !c.path().ends_with(gnu_compiler) {
152 let output = output(c.to_command().arg("--version"));
153 let i = match output.find(" 4.") {
157 match output[i + 3..].chars().next().unwrap() {
161 let alternative = format!("e{}", gnu_compiler);
162 if Command::new(&alternative).output().is_ok() {
163 cfg.compiler(alternative);
167 "mips-unknown-linux-musl" => {
168 if cfg.get_compiler().path().to_str() == Some("gcc") {
169 cfg.compiler("mips-linux-musl-gcc");
172 "mipsel-unknown-linux-musl" => {
173 if cfg.get_compiler().path().to_str() == Some("gcc") {
174 cfg.compiler("mipsel-linux-musl-gcc");
178 t if t.contains("musl") => {
179 if let Some(root) = build.musl_root(target) {
180 let guess = root.join("bin/musl-gcc");
191 /// The target programming language for a native compiler.
193 /// The compiler is targeting C.
195 /// The compiler is targeting C++.
200 /// Obtains the name of a compiler in the GCC collection.
201 fn gcc(self) -> &'static str {
203 Language::C => "gcc",
204 Language::CPlusPlus => "g++",
208 /// Obtains the name of a compiler in the clang suite.
209 fn clang(self) -> &'static str {
211 Language::C => "clang",
212 Language::CPlusPlus => "clang++",