about summary refs log tree commit diff
path: root/src/bootstrap
diff options
context:
space:
mode:
authorVadim Petrochenkov <vadim.petrochenkov@gmail.com>2017-10-10 23:06:22 +0300
committerVadim Petrochenkov <vadim.petrochenkov@gmail.com>2017-10-15 22:10:07 +0300
commit9e0fc5ccd050201e77483b1efb2e6c76f47496f6 (patch)
treeb11fa90e3a2205b9add4ba0ffc8a3e38725c96e6 /src/bootstrap
parent2689fd2402590961dae32f35369a8685c89022fb (diff)
downloadrust-9e0fc5ccd050201e77483b1efb2e6c76f47496f6.tar.gz
rust-9e0fc5ccd050201e77483b1efb2e6c76f47496f6.zip
rustbuild: Support specifying archiver and linker explicitly
Diffstat (limited to 'src/bootstrap')
-rw-r--r--src/bootstrap/bin/rustc.rs13
-rw-r--r--src/bootstrap/bin/rustdoc.rs3
-rw-r--r--src/bootstrap/builder.rs50
-rw-r--r--src/bootstrap/cc_detect.rs52
-rw-r--r--src/bootstrap/check.rs8
-rw-r--r--src/bootstrap/config.rs8
-rw-r--r--src/bootstrap/lib.rs23
-rw-r--r--src/bootstrap/native.rs7
-rw-r--r--src/bootstrap/tool.rs2
9 files changed, 130 insertions, 36 deletions
diff --git a/src/bootstrap/bin/rustc.rs b/src/bootstrap/bin/rustc.rs
index e91acb21dac..e496ce27f50 100644
--- a/src/bootstrap/bin/rustc.rs
+++ b/src/bootstrap/bin/rustc.rs
@@ -120,12 +120,6 @@ fn main() {
             cmd.arg("-L").arg(&root);
         }
 
-        // Pass down extra flags, commonly used to configure `-Clinker` when
-        // cross compiling.
-        if let Ok(s) = env::var("RUSTC_FLAGS") {
-            cmd.args(&s.split(" ").filter(|s| !s.is_empty()).collect::<Vec<_>>());
-        }
-
         // Pass down incremental directory, if any.
         if let Ok(dir) = env::var("RUSTC_INCREMENTAL") {
             cmd.arg(format!("-Zincremental={}", dir));
@@ -254,6 +248,13 @@ fn main() {
         }
     }
 
+    // Pass down extra flags, commonly used to configure `-Clinker`.
+    // Linker options should be set for build scripts as well,
+    // can't link a build script executable without a linker!
+    if let Ok(s) = env::var("RUSTC_FLAGS") {
+        cmd.args(&s.split(" ").filter(|s| !s.is_empty()).collect::<Vec<_>>());
+    }
+
     let color = match env::var("RUSTC_COLOR") {
         Ok(s) => usize::from_str(&s).expect("RUSTC_COLOR should be an integer"),
         Err(_) => 0,
diff --git a/src/bootstrap/bin/rustdoc.rs b/src/bootstrap/bin/rustdoc.rs
index d18512b257d..2105797a830 100644
--- a/src/bootstrap/bin/rustdoc.rs
+++ b/src/bootstrap/bin/rustdoc.rs
@@ -47,6 +47,9 @@ fn main() {
     if env::var_os("RUSTC_FORCE_UNSTABLE").is_some() {
         cmd.arg("-Z").arg("force-unstable-if-unmarked");
     }
+    if let Some(linker) = env::var_os("RUSTDOC_LINKER") {
+        cmd.arg("--linker").arg(linker).arg("-Z").arg("unstable-options");
+    }
 
     // Bootstrap's Cargo-command builder sets this variable to the current Rust version; let's pick
     // it up so we can make rustdoc print this into the docs
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs
index 1d63e112ca6..ef4f8f78b0d 100644
--- a/src/bootstrap/builder.rs
+++ b/src/bootstrap/builder.rs
@@ -413,13 +413,15 @@ impl<'a> Builder<'a> {
     pub fn rustdoc_cmd(&self, host: Interned<String>) -> Command {
         let mut cmd = Command::new(&self.out.join("bootstrap/debug/rustdoc"));
         let compiler = self.compiler(self.top_stage, host);
-        cmd
-            .env("RUSTC_STAGE", compiler.stage.to_string())
-            .env("RUSTC_SYSROOT", self.sysroot(compiler))
-            .env("RUSTC_LIBDIR", self.sysroot_libdir(compiler, self.build.build))
-            .env("CFG_RELEASE_CHANNEL", &self.build.config.channel)
-            .env("RUSTDOC_REAL", self.rustdoc(host))
-            .env("RUSTDOC_CRATE_VERSION", self.build.rust_version());
+        cmd.env("RUSTC_STAGE", compiler.stage.to_string())
+           .env("RUSTC_SYSROOT", self.sysroot(compiler))
+           .env("RUSTC_LIBDIR", self.sysroot_libdir(compiler, self.build.build))
+           .env("CFG_RELEASE_CHANNEL", &self.build.config.channel)
+           .env("RUSTDOC_REAL", self.rustdoc(host))
+           .env("RUSTDOC_CRATE_VERSION", self.build.rust_version());
+        if let Some(linker) = self.build.linker(host) {
+            cmd.env("RUSTDOC_LINKER", linker);
+        }
         cmd
     }
 
@@ -485,6 +487,10 @@ impl<'a> Builder<'a> {
              .env("TEST_MIRI", self.config.test_miri.to_string())
              .env("RUSTC_FLAGS", self.rustc_flags(target).join(" "));
 
+        if let Some(linker) = self.build.linker(target) {
+            cargo.env("RUSTDOC_LINKER", linker);
+        }
+
         if mode != Mode::Tool {
             // Tools don't get debuginfo right now, e.g. cargo and rls don't
             // get compiled with debuginfo.
@@ -557,17 +563,35 @@ impl<'a> Builder<'a> {
 
         cargo.env("RUSTC_VERBOSE", format!("{}", self.verbosity));
 
-        // Specify some various options for build scripts used throughout
-        // the build.
+        // Throughout the build Cargo can execute a number of build scripts
+        // compiling C/C++ code and we need to pass compilers, archivers, flags, etc
+        // obtained previously to those build scripts.
+        // Build scripts use either the `cc` crate or `configure/make` so we pass
+        // the options through environment variables that are fetched and understood by both.
         //
         // FIXME: the guard against msvc shouldn't need to be here
         if !target.contains("msvc") {
-            cargo.env(format!("CC_{}", target), self.cc(target))
-                 .env(format!("AR_{}", target), self.ar(target).unwrap()) // only msvc is None
-                 .env(format!("CFLAGS_{}", target), self.cflags(target).join(" "));
+            let cc = self.cc(target);
+            cargo.env(format!("CC_{}", target), cc)
+                 .env("CC", cc);
+
+            let cflags = self.cflags(target).join(" ");
+            cargo.env(format!("CFLAGS_{}", target), cflags.clone())
+                 .env("CFLAGS", cflags.clone());
+
+            if let Some(ar) = self.ar(target) {
+                let ranlib = format!("{} s", ar.display());
+                cargo.env(format!("AR_{}", target), ar)
+                     .env("AR", ar)
+                     .env(format!("RANLIB_{}", target), ranlib.clone())
+                     .env("RANLIB", ranlib);
+            }
 
             if let Ok(cxx) = self.cxx(target) {
-                 cargo.env(format!("CXX_{}", target), cxx);
+                cargo.env(format!("CXX_{}", target), cxx)
+                     .env("CXX", cxx)
+                     .env(format!("CXXFLAGS_{}", target), cflags.clone())
+                     .env("CXXFLAGS", cflags);
             }
         }
 
diff --git a/src/bootstrap/cc_detect.rs b/src/bootstrap/cc_detect.rs
index 08df65c7611..6e3e3c92029 100644
--- a/src/bootstrap/cc_detect.rs
+++ b/src/bootstrap/cc_detect.rs
@@ -31,20 +31,51 @@
 //! ever be probed for. Instead the compilers found here will be used for
 //! everything.
 
+use std::collections::HashSet;
+use std::{env, iter};
+use std::path::{Path, PathBuf};
 use std::process::Command;
-use std::iter;
 
-use build_helper::{cc2ar, output};
+use build_helper::output;
 use cc;
 
 use Build;
 use config::Target;
 use cache::Interned;
 
+// The `cc` crate doesn't provide a way to obtain a path to the detected archiver,
+// so use some simplified logic here. First we respect the environment variable `AR`, then
+// try to infer the archiver path from the C compiler path.
+// In the future this logic should be replaced by calling into the `cc` crate.
+fn cc2ar(cc: &Path, target: &str) -> Option<PathBuf> {
+    if let Some(ar) = env::var_os("AR") {
+        Some(PathBuf::from(ar))
+    } else if target.contains("msvc") {
+        None
+    } else if target.contains("musl") {
+        Some(PathBuf::from("ar"))
+    } else if target.contains("openbsd") {
+        Some(PathBuf::from("ar"))
+    } else {
+        let parent = cc.parent().unwrap();
+        let file = cc.file_name().unwrap().to_str().unwrap();
+        for suffix in &["gcc", "cc", "clang"] {
+            if let Some(idx) = file.rfind(suffix) {
+                let mut file = file[..idx].to_owned();
+                file.push_str("ar");
+                return Some(parent.join(&file));
+            }
+        }
+        Some(parent.join(file))
+    }
+}
+
 pub fn find(build: &mut Build) {
     // For all targets we're going to need a C compiler for building some shims
     // and such as well as for being a linker for Rust code.
-    for target in build.targets.iter().chain(&build.hosts).cloned().chain(iter::once(build.build)) {
+    let targets = build.targets.iter().chain(&build.hosts).cloned().chain(iter::once(build.build))
+                               .collect::<HashSet<_>>();
+    for target in targets.into_iter() {
         let mut cfg = cc::Build::new();
         cfg.cargo_metadata(false).opt_level(0).warnings(false).debug(false)
            .target(&target).host(&build.build);
@@ -57,16 +88,23 @@ pub fn find(build: &mut Build) {
         }
 
         let compiler = cfg.get_compiler();
-        let ar = cc2ar(compiler.path(), &target);
+        let ar = if let ar @ Some(..) = config.and_then(|c| c.ar.clone()) {
+            ar
+        } else {
+            cc2ar(compiler.path(), &target)
+        };
+
         build.verbose(&format!("CC_{} = {:?}", &target, compiler.path()));
-        if let Some(ref ar) = ar {
+        build.cc.insert(target, compiler);
+        if let Some(ar) = ar {
             build.verbose(&format!("AR_{} = {:?}", &target, ar));
+            build.ar.insert(target, ar);
         }
-        build.cc.insert(target, (compiler, ar));
     }
 
     // For all host triples we need to find a C++ compiler as well
-    for host in build.hosts.iter().cloned().chain(iter::once(build.build)) {
+    let hosts = build.hosts.iter().cloned().chain(iter::once(build.build)).collect::<HashSet<_>>();
+    for host in hosts.into_iter() {
         let mut cfg = cc::Build::new();
         cfg.cargo_metadata(false).opt_level(0).warnings(false).debug(false).cpp(true)
            .target(&host).host(&build.build);
diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs
index fb32ab0cb86..d9b0ff4c7a4 100644
--- a/src/bootstrap/check.rs
+++ b/src/bootstrap/check.rs
@@ -725,6 +725,9 @@ impl Step for Compiletest {
         // Avoid depending on rustdoc when we don't need it.
         if mode == "rustdoc" || mode == "run-make" {
             cmd.arg("--rustdoc-path").arg(builder.rustdoc(compiler.host));
+            if let Some(linker) = build.linker(target) {
+                cmd.arg("--linker").arg(linker);
+            }
         }
 
         cmd.arg("--src-base").arg(build.src.join("src/test").join(suite));
@@ -806,6 +809,9 @@ impl Step for Compiletest {
                 .arg("--cflags").arg(build.cflags(target).join(" "))
                 .arg("--llvm-components").arg(llvm_components.trim())
                 .arg("--llvm-cxxflags").arg(llvm_cxxflags.trim());
+                if let Some(ar) = build.ar(target) {
+                    cmd.arg("--ar").arg(ar);
+                }
             }
         }
         if suite == "run-make" && !build.config.llvm_enabled {
@@ -831,7 +837,7 @@ impl Step for Compiletest {
         // Note that if we encounter `PATH` we make sure to append to our own `PATH`
         // rather than stomp over it.
         if target.contains("msvc") {
-            for &(ref k, ref v) in build.cc[&target].0.env() {
+            for &(ref k, ref v) in build.cc[&target].env() {
                 if k != "PATH" {
                     cmd.env(k, v);
                 }
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index 5fb5eb3b7f1..69e0f58f1cd 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -143,6 +143,8 @@ pub struct Target {
     pub jemalloc: Option<PathBuf>,
     pub cc: Option<PathBuf>,
     pub cxx: Option<PathBuf>,
+    pub ar: Option<PathBuf>,
+    pub linker: Option<PathBuf>,
     pub ndk: Option<PathBuf>,
     pub crt_static: Option<bool>,
     pub musl_root: Option<PathBuf>,
@@ -282,6 +284,8 @@ struct TomlTarget {
     jemalloc: Option<String>,
     cc: Option<String>,
     cxx: Option<String>,
+    ar: Option<String>,
+    linker: Option<String>,
     android_ndk: Option<String>,
     crt_static: Option<bool>,
     musl_root: Option<String>,
@@ -484,8 +488,10 @@ impl Config {
                 if let Some(ref s) = cfg.android_ndk {
                     target.ndk = Some(env::current_dir().unwrap().join(s));
                 }
-                target.cxx = cfg.cxx.clone().map(PathBuf::from);
                 target.cc = cfg.cc.clone().map(PathBuf::from);
+                target.cxx = cfg.cxx.clone().map(PathBuf::from);
+                target.ar = cfg.ar.clone().map(PathBuf::from);
+                target.linker = cfg.linker.clone().map(PathBuf::from);
                 target.crt_static = cfg.crt_static.clone();
                 target.musl_root = cfg.musl_root.clone().map(PathBuf::from);
                 target.qemu_rootfs = cfg.qemu_rootfs.clone().map(PathBuf::from);
diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs
index 2d721f45578..7c599f91838 100644
--- a/src/bootstrap/lib.rs
+++ b/src/bootstrap/lib.rs
@@ -240,10 +240,11 @@ pub struct Build {
     lldb_python_dir: Option<String>,
 
     // Runtime state filled in later on
-    // target -> (cc, ar)
-    cc: HashMap<Interned<String>, (cc::Tool, Option<PathBuf>)>,
-    // host -> (cc, ar)
+    // C/C++ compilers and archiver for all targets
+    cc: HashMap<Interned<String>, cc::Tool>,
     cxx: HashMap<Interned<String>, cc::Tool>,
+    ar: HashMap<Interned<String>, PathBuf>,
+    // Misc
     crates: HashMap<Interned<String>, Crate>,
     is_sudo: bool,
     ci_env: CiEnv,
@@ -324,6 +325,7 @@ impl Build {
             rls_info,
             cc: HashMap::new(),
             cxx: HashMap::new(),
+            ar: HashMap::new(),
             crates: HashMap::new(),
             lldb_version: None,
             lldb_python_dir: None,
@@ -612,7 +614,7 @@ impl Build {
 
     /// Returns the path to the C compiler for the target specified.
     fn cc(&self, target: Interned<String>) -> &Path {
-        self.cc[&target].0.path()
+        self.cc[&target].path()
     }
 
     /// Returns a list of flags to pass to the C compiler for the target
@@ -620,7 +622,7 @@ impl Build {
     fn cflags(&self, target: Interned<String>) -> Vec<String> {
         // Filter out -O and /O (the optimization flags) that we picked up from
         // cc-rs because the build scripts will determine that for themselves.
-        let mut base = self.cc[&target].0.args().iter()
+        let mut base = self.cc[&target].args().iter()
                            .map(|s| s.to_string_lossy().into_owned())
                            .filter(|s| !s.starts_with("-O") && !s.starts_with("/O"))
                            .collect::<Vec<_>>();
@@ -644,7 +646,11 @@ impl Build {
 
     /// Returns the path to the `ar` archive utility for the target specified.
     fn ar(&self, target: Interned<String>) -> Option<&Path> {
-        self.cc[&target].1.as_ref().map(|p| &**p)
+        self.ar.get(&target).map(|p| &**p)
+    }
+
+    fn linker(&self, target: Interned<String>) -> Option<&Path> {
+        self.config.target_config.get(&target).and_then(|c| c.linker.as_ref().map(|p| &**p))
     }
 
     /// Returns the path to the C++ compiler for the target specified.
@@ -667,7 +673,10 @@ impl Build {
         // than an entry here.
 
         let mut base = Vec::new();
-        if target != self.config.build && !target.contains("msvc") &&
+        if let Some(linker) = self.linker(target) {
+            // If linker was explictly provided, force it on all the compiled Rust code.
+            base.push(format!("-Clinker={}", linker.display()));
+        } else if target != self.config.build && !target.contains("msvc") &&
             !target.contains("emscripten") {
             base.push(format!("-Clinker={}", self.cc(target).display()));
         }
diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs
index 2c8e5004041..941ea96bbec 100644
--- a/src/bootstrap/native.rs
+++ b/src/bootstrap/native.rs
@@ -227,6 +227,13 @@ impl Step for Llvm {
             cfg.build_arg("-j").build_arg(build.jobs().to_string());
             cfg.define("CMAKE_C_FLAGS", build.cflags(target).join(" "));
             cfg.define("CMAKE_CXX_FLAGS", build.cflags(target).join(" "));
+            if let Some(ar) = build.ar(target) {
+                if ar.is_absolute() {
+                    // LLVM build breaks if `CMAKE_AR` is a relative path, for some reason it
+                    // tries to resolve this path in the LLVM build directory.
+                    cfg.define("CMAKE_AR", sanitize_cc(ar));
+                }
+            }
         };
 
         configure_compilers(&mut cfg);
diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs
index e95a5e07436..662c56d728d 100644
--- a/src/bootstrap/tool.rs
+++ b/src/bootstrap/tool.rs
@@ -561,7 +561,7 @@ impl<'a> Builder<'a> {
         if compiler.host.contains("msvc") {
             let curpaths = env::var_os("PATH").unwrap_or_default();
             let curpaths = env::split_paths(&curpaths).collect::<Vec<_>>();
-            for &(ref k, ref v) in self.cc[&compiler.host].0.env() {
+            for &(ref k, ref v) in self.cc[&compiler.host].env() {
                 if k != "PATH" {
                     continue
                 }