about summary refs log tree commit diff
path: root/src/bootstrap
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2018-04-24 08:34:14 -0700
committerAlex Crichton <alex@alexcrichton.com>2018-05-09 14:45:34 -0700
commit7e5b9ac41e36b29d5208f4e5455aade07d4d107d (patch)
tree089e6f1ef7eac8c624658ed2328d2cfbdc6cb41b /src/bootstrap
parentac287ed167f07619409928008e4a50eecac4a285 (diff)
downloadrust-7e5b9ac41e36b29d5208f4e5455aade07d4d107d.tar.gz
rust-7e5b9ac41e36b29d5208f4e5455aade07d4d107d.zip
ci: Compile LLVM with Clang 6.0.0
Currently on CI we predominately compile LLVM with the default system compiler
which means gcc on Linux, some version of Clang on OSX, MSVC on Windows, and
gcc on MinGW. This commit switches Linux, OSX, and Windows to all use Clang
6.0.0 to build LLVM (aka the C/C++ compiler as part of the bootstrap). This
looks to generate faster code according to #49879 which translates to a faster
rustc (as LLVM internally is faster)

The major changes here were to the containers that build Linux releases,
basically adding a new step that uses the previous gcc 4.8 compiler to compile
the next Clang 6.0.0 compiler. Otherwise the OSX and Windows scripts have been
updated to download precompiled versions of Clang 6 and configure the build to
use them.

Note that `cc` was updated here to fix using `clang-cl` with `cc-rs` on MSVC, as
well as an update to `sccache` on Windows which was needed to correctly work
with `clang-cl`. Finally the MinGW compiler is entirely left out here
intentionally as it's currently thought that Clang can't generate C++ code for
MinGW and we need to use gcc, but this should be verified eventually.
Diffstat (limited to 'src/bootstrap')
-rw-r--r--src/bootstrap/Cargo.toml5
-rw-r--r--src/bootstrap/bin/llvm-config-wrapper.rs27
-rw-r--r--src/bootstrap/bin/sccache-plus-cl.rs10
-rw-r--r--src/bootstrap/builder.rs6
-rw-r--r--src/bootstrap/config.rs3
-rw-r--r--src/bootstrap/native.rs68
6 files changed, 107 insertions, 12 deletions
diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml
index 2f9c4e148a6..af33ebf3c42 100644
--- a/src/bootstrap/Cargo.toml
+++ b/src/bootstrap/Cargo.toml
@@ -28,6 +28,11 @@ name = "sccache-plus-cl"
 path = "bin/sccache-plus-cl.rs"
 test = false
 
+[[bin]]
+name = "llvm-config-wrapper"
+path = "bin/llvm-config-wrapper.rs"
+test = false
+
 [dependencies]
 build_helper = { path = "../build_helper" }
 cmake = "0.1.23"
diff --git a/src/bootstrap/bin/llvm-config-wrapper.rs b/src/bootstrap/bin/llvm-config-wrapper.rs
new file mode 100644
index 00000000000..b1703f8c728
--- /dev/null
+++ b/src/bootstrap/bin/llvm-config-wrapper.rs
@@ -0,0 +1,27 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// The sheer existence of this file is an awful hack. See the comments in
+// `src/bootstrap/native.rs` for why this is needed when compiling LLD.
+
+use std::env;
+use std::process::{self, Stdio, Command};
+use std::io::{self, Write};
+
+fn main() {
+    let real_llvm_config = env::var_os("LLVM_CONFIG_REAL").unwrap();
+    let mut cmd = Command::new(real_llvm_config);
+    cmd.args(env::args().skip(1)).stderr(Stdio::piped());
+    let output = cmd.output().expect("failed to spawn llvm-config");
+    let stdout = String::from_utf8_lossy(&output.stdout);
+    print!("{}", stdout.replace("\\", "/"));
+    io::stdout().flush().unwrap();
+    process::exit(output.status.code().unwrap_or(1));
+}
diff --git a/src/bootstrap/bin/sccache-plus-cl.rs b/src/bootstrap/bin/sccache-plus-cl.rs
index 8584014d48d..0a20ac7e492 100644
--- a/src/bootstrap/bin/sccache-plus-cl.rs
+++ b/src/bootstrap/bin/sccache-plus-cl.rs
@@ -16,8 +16,8 @@ use std::process::{self, Command};
 fn main() {
     let target = env::var("SCCACHE_TARGET").unwrap();
     // Locate the actual compiler that we're invoking
-    env::remove_var("CC");
-    env::remove_var("CXX");
+    env::set_var("CC", env::var_os("SCCACHE_CC").unwrap());
+    env::set_var("CXX", env::var_os("SCCACHE_CXX").unwrap());
     let mut cfg = cc::Build::new();
     cfg.cargo_metadata(false)
        .out_dir("/")
@@ -39,6 +39,12 @@ fn main() {
         cmd.arg(arg);
     }
 
+    if let Ok(s) = env::var("SCCACHE_EXTRA_ARGS") {
+        for s in s.split_whitespace() {
+            cmd.arg(s);
+        }
+    }
+
     let status = cmd.status().expect("failed to spawn");
     process::exit(status.code().unwrap_or(2))
 }
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs
index 9c35cb7f506..465f7045ded 100644
--- a/src/bootstrap/builder.rs
+++ b/src/bootstrap/builder.rs
@@ -726,7 +726,11 @@ impl<'a> Builder<'a> {
         // 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") {
+        if target.contains("msvc") {
+            if let Some(ref cl) = self.config.llvm_clang_cl {
+                cargo.env("CC", cl).env("CXX", cl);
+            }
+        } else {
             let ccache = self.config.ccache.as_ref();
             let ccacheify = |s: &Path| {
                 let ccache = match ccache {
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index 6dd6291be23..9840682d137 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -82,6 +82,7 @@ pub struct Config {
     pub llvm_version_check: bool,
     pub llvm_static_stdcpp: bool,
     pub llvm_link_shared: bool,
+    pub llvm_clang_cl: Option<String>,
     pub llvm_targets: Option<String>,
     pub llvm_experimental_targets: String,
     pub llvm_link_jobs: Option<u32>,
@@ -250,6 +251,7 @@ struct Llvm {
     experimental_targets: Option<String>,
     link_jobs: Option<u32>,
     link_shared: Option<bool>,
+    clang_cl: Option<String>
 }
 
 #[derive(Deserialize, Default, Clone)]
@@ -504,6 +506,7 @@ impl Config {
             config.llvm_experimental_targets = llvm.experimental_targets.clone()
                 .unwrap_or("WebAssembly".to_string());
             config.llvm_link_jobs = llvm.link_jobs;
+            config.llvm_clang_cl = llvm.clang_cl.clone();
         }
 
         if let Some(ref rust) = toml.rust {
diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs
index d952cb5bfc4..002044050f3 100644
--- a/src/bootstrap/native.rs
+++ b/src/bootstrap/native.rs
@@ -275,21 +275,53 @@ fn configure_cmake(builder: &Builder,
         return
     }
 
-    let cc = builder.cc(target);
-    let cxx = builder.cxx(target).unwrap();
+    let (cc, cxx) = match builder.config.llvm_clang_cl {
+        Some(ref cl) => (cl.as_ref(), cl.as_ref()),
+        None => (builder.cc(target), builder.cxx(target).unwrap()),
+    };
 
     // Handle msvc + ninja + ccache specially (this is what the bots use)
     if target.contains("msvc") &&
        builder.config.ninja &&
-       builder.config.ccache.is_some() {
-        let mut cc = env::current_exe().expect("failed to get cwd");
-        cc.set_file_name("sccache-plus-cl.exe");
+       builder.config.ccache.is_some()
+    {
+       let mut wrap_cc = env::current_exe().expect("failed to get cwd");
+       wrap_cc.set_file_name("sccache-plus-cl.exe");
 
-       cfg.define("CMAKE_C_COMPILER", sanitize_cc(&cc))
-          .define("CMAKE_CXX_COMPILER", sanitize_cc(&cc));
+       cfg.define("CMAKE_C_COMPILER", sanitize_cc(&wrap_cc))
+          .define("CMAKE_CXX_COMPILER", sanitize_cc(&wrap_cc));
        cfg.env("SCCACHE_PATH",
                builder.config.ccache.as_ref().unwrap())
-          .env("SCCACHE_TARGET", target);
+          .env("SCCACHE_TARGET", target)
+          .env("SCCACHE_CC", &cc)
+          .env("SCCACHE_CXX", &cxx);
+
+       // Building LLVM on MSVC can be a little ludicrous at times. We're so far
+       // off the beaten path here that I'm not really sure this is even half
+       // supported any more. Here we're trying to:
+       //
+       // * Build LLVM on MSVC
+       // * Build LLVM with `clang-cl` instead of `cl.exe`
+       // * Build a project with `sccache`
+       // * Build for 32-bit as well
+       // * Build with Ninja
+       //
+       // For `cl.exe` there are different binaries to compile 32/64 bit which
+       // we use but for `clang-cl` there's only one which internally
+       // multiplexes via flags. As a result it appears that CMake's detection
+       // of a compiler's architecture and such on MSVC **doesn't** pass any
+       // custom flags we pass in CMAKE_CXX_FLAGS below. This means that if we
+       // use `clang-cl.exe` it's always diagnosed as a 64-bit compiler which
+       // definitely causes problems since all the env vars are pointing to
+       // 32-bit libraries.
+       //
+       // To hack aroudn this... again... we pass an argument that's
+       // unconditionally passed in the sccache shim. This'll get CMake to
+       // correctly diagnose it's doing a 32-bit compilation and LLVM will
+       // internally configure itself appropriately.
+       if builder.config.llvm_clang_cl.is_some() && target.contains("i686") {
+           cfg.env("SCCACHE_EXTRA_ARGS", "-m32");
+       }
 
     // If ccache is configured we inform the build a little differently hwo
     // to invoke ccache while also invoking our compilers.
@@ -368,9 +400,27 @@ impl Step for Lld {
         let mut cfg = cmake::Config::new(builder.src.join("src/tools/lld"));
         configure_cmake(builder, target, &mut cfg, true);
 
+        // This is an awful, awful hack. Discovered when we migrated to using
+        // clang-cl to compile LLVM/LLD it turns out that LLD, when built out of
+        // tree, will execute `llvm-config --cmakedir` and then tell CMake about
+        // that directory for later processing. Unfortunately if this path has
+        // forward slashes in it (which it basically always does on Windows)
+        // then CMake will hit a syntax error later on as... something isn't
+        // escaped it seems?
+        //
+        // Instead of attempting to fix this problem in upstream CMake and/or
+        // LLVM/LLD we just hack around it here. This thin wrapper will take the
+        // output from llvm-config and replace all instances of `\` with `/` to
+        // ensure we don't hit the same bugs with escaping. It means that you
+        // can't build on a system where your paths require `\` on Windows, but
+        // there's probably a lot of reasons you can't do that other than this.
+        let llvm_config_shim = env::current_exe()
+            .unwrap()
+            .with_file_name("llvm-config-wrapper");
         cfg.out_dir(&out_dir)
            .profile("Release")
-           .define("LLVM_CONFIG_PATH", llvm_config)
+           .env("LLVM_CONFIG_REAL", llvm_config)
+           .define("LLVM_CONFIG_PATH", llvm_config_shim)
            .define("LLVM_INCLUDE_TESTS", "OFF");
 
         cfg.build();