about summary refs log tree commit diff
path: root/src/bootstrap/native.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/bootstrap/native.rs')
-rw-r--r--src/bootstrap/native.rs225
1 files changed, 153 insertions, 72 deletions
diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs
index 21dcb1d8aa0..bcd79a49ece 100644
--- a/src/bootstrap/native.rs
+++ b/src/bootstrap/native.rs
@@ -11,6 +11,7 @@
 use std::env;
 use std::ffi::OsString;
 use std::fs::{self, File};
+use std::io;
 use std::path::{Path, PathBuf};
 use std::process::Command;
 
@@ -23,6 +24,72 @@ use crate::util::{self, exe};
 use crate::GitRepo;
 use build_helper::up_to_date;
 
+pub struct Meta {
+    stamp: HashStamp,
+    build_llvm_config: PathBuf,
+    out_dir: PathBuf,
+    root: String,
+}
+
+// This returns whether we've already previously built LLVM.
+//
+// It's used to avoid busting caches during x.py check -- if we've already built
+// LLVM, it's fine for us to not try to avoid doing so.
+//
+// This will return the llvm-config if it can get it (but it will not build it
+// if not).
+pub fn prebuilt_llvm_config(
+    builder: &Builder<'_>,
+    target: Interned<String>,
+) -> Result<PathBuf, Meta> {
+    // If we're using a custom LLVM bail out here, but we can only use a
+    // custom LLVM for the build triple.
+    if let Some(config) = builder.config.target_config.get(&target) {
+        if let Some(ref s) = config.llvm_config {
+            check_llvm_version(builder, s);
+            return Ok(s.to_path_buf());
+        }
+    }
+
+    let root = "src/llvm-project/llvm";
+    let out_dir = builder.llvm_out(target);
+    let mut llvm_config_ret_dir = builder.llvm_out(builder.config.build);
+    if !builder.config.build.contains("msvc") || builder.config.ninja {
+        llvm_config_ret_dir.push("build");
+    }
+    llvm_config_ret_dir.push("bin");
+
+    let build_llvm_config = llvm_config_ret_dir.join(exe("llvm-config", &*builder.config.build));
+
+    let stamp = out_dir.join("llvm-finished-building");
+    let stamp = HashStamp::new(stamp, builder.in_tree_llvm_info.sha());
+
+    if builder.config.llvm_skip_rebuild && stamp.path.exists() {
+        builder.info(
+            "Warning: \
+                Using a potentially stale build of LLVM; \
+                This may not behave well.",
+        );
+        return Ok(build_llvm_config);
+    }
+
+    if stamp.is_done() {
+        if stamp.hash.is_none() {
+            builder.info(
+                "Could not determine the LLVM submodule commit hash. \
+                     Assuming that an LLVM rebuild is not necessary.",
+            );
+            builder.info(&format!(
+                "To force LLVM to rebuild, remove the file `{}`",
+                stamp.path.display()
+            ));
+        }
+        return Ok(build_llvm_config);
+    }
+
+    Err(Meta { stamp, build_llvm_config, out_dir, root: root.into() })
+}
+
 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
 pub struct Llvm {
     pub target: Interned<String>,
@@ -45,60 +112,14 @@ impl Step for Llvm {
     fn run(self, builder: &Builder<'_>) -> PathBuf {
         let target = self.target;
 
-        // If we're using a custom LLVM bail out here, but we can only use a
-        // custom LLVM for the build triple.
-        if let Some(config) = builder.config.target_config.get(&target) {
-            if let Some(ref s) = config.llvm_config {
-                check_llvm_version(builder, s);
-                return s.to_path_buf();
-            }
-        }
-
-        let llvm_info = &builder.in_tree_llvm_info;
-        let root = "src/llvm-project/llvm";
-        let out_dir = builder.llvm_out(target);
-        let mut llvm_config_ret_dir = builder.llvm_out(builder.config.build);
-        if !builder.config.build.contains("msvc") || builder.config.ninja {
-            llvm_config_ret_dir.push("build");
-        }
-        llvm_config_ret_dir.push("bin");
-
-        let build_llvm_config =
-            llvm_config_ret_dir.join(exe("llvm-config", &*builder.config.build));
-        let done_stamp = out_dir.join("llvm-finished-building");
-
-        if done_stamp.exists() {
-            if builder.config.llvm_skip_rebuild {
-                builder.info(
-                    "Warning: \
-                    Using a potentially stale build of LLVM; \
-                    This may not behave well.",
-                );
-                return build_llvm_config;
-            }
-
-            if let Some(llvm_commit) = llvm_info.sha() {
-                let done_contents = t!(fs::read(&done_stamp));
-
-                // If LLVM was already built previously and the submodule's commit didn't change
-                // from the previous build, then no action is required.
-                if done_contents == llvm_commit.as_bytes() {
-                    return build_llvm_config;
-                }
-            } else {
-                builder.info(
-                    "Could not determine the LLVM submodule commit hash. \
-                     Assuming that an LLVM rebuild is not necessary.",
-                );
-                builder.info(&format!(
-                    "To force LLVM to rebuild, remove the file `{}`",
-                    done_stamp.display()
-                ));
-                return build_llvm_config;
-            }
-        }
+        let Meta { stamp, build_llvm_config, out_dir, root } =
+            match prebuilt_llvm_config(builder, target) {
+                Ok(p) => return p,
+                Err(m) => m,
+            };
 
         builder.info(&format!("Building LLVM for {}", target));
+        t!(stamp.remove());
         let _time = util::timeit(&builder);
         t!(fs::create_dir_all(&out_dir));
 
@@ -240,14 +261,14 @@ impl Step for Llvm {
             if !suffix.is_empty() {
                 cfg.define("LLVM_VERSION_SUFFIX", suffix);
             }
+        } else if builder.config.channel == "dev" {
+            // Changes to a version suffix require a complete rebuild of the LLVM.
+            // To avoid rebuilds during a time of version bump, don't include rustc
+            // release number on the dev channel.
+            cfg.define("LLVM_VERSION_SUFFIX", "-rust-dev");
         } else {
-            let mut default_suffix =
-                format!("-rust-{}-{}", channel::CFG_RELEASE_NUM, builder.config.channel,);
-            if let Some(sha) = llvm_info.sha_short() {
-                default_suffix.push_str("-");
-                default_suffix.push_str(sha);
-            }
-            cfg.define("LLVM_VERSION_SUFFIX", default_suffix);
+            let suffix = format!("-rust-{}-{}", channel::CFG_RELEASE_NUM, builder.config.channel);
+            cfg.define("LLVM_VERSION_SUFFIX", suffix);
         }
 
         if let Some(ref linker) = builder.config.llvm_use_linker {
@@ -275,7 +296,7 @@ impl Step for Llvm {
 
         cfg.build();
 
-        t!(fs::write(&done_stamp, llvm_info.sha().unwrap_or("")));
+        t!(stamp.write());
 
         build_llvm_config
     }
@@ -294,11 +315,11 @@ fn check_llvm_version(builder: &Builder<'_>, llvm_config: &Path) {
     let version = output(cmd.arg("--version"));
     let mut parts = version.split('.').take(2).filter_map(|s| s.parse::<u32>().ok());
     if let (Some(major), Some(_minor)) = (parts.next(), parts.next()) {
-        if major >= 7 {
+        if major >= 8 {
             return;
         }
     }
-    panic!("\n\nbad LLVM version: {}, need >=7.0\n\n", version)
+    panic!("\n\nbad LLVM version: {}, need >=8.0\n\n", version)
 }
 
 fn configure_cmake(
@@ -484,10 +505,29 @@ impl Step for Lld {
         let llvm_config_shim = env::current_exe().unwrap().with_file_name("llvm-config-wrapper");
         cfg.out_dir(&out_dir)
             .profile("Release")
-            .env("LLVM_CONFIG_REAL", llvm_config)
+            .env("LLVM_CONFIG_REAL", &llvm_config)
             .define("LLVM_CONFIG_PATH", llvm_config_shim)
             .define("LLVM_INCLUDE_TESTS", "OFF");
 
+        // While we're using this horrible workaround to shim the execution of
+        // llvm-config, let's just pile on more. I can't seem to figure out how
+        // to build LLD as a standalone project and also cross-compile it at the
+        // same time. It wants a natively executable `llvm-config` to learn
+        // about LLVM, but then it learns about all the host configuration of
+        // LLVM and tries to link to host LLVM libraries.
+        //
+        // To work around that we tell our shim to replace anything with the
+        // build target with the actual target instead. This'll break parts of
+        // LLD though which try to execute host tools, such as llvm-tblgen, so
+        // we specifically tell it where to find those. This is likely super
+        // brittle and will break over time. If anyone knows better how to
+        // cross-compile LLD it would be much appreciated to fix this!
+        if target != builder.config.build {
+            cfg.env("LLVM_CONFIG_SHIM_REPLACE", &builder.config.build)
+                .env("LLVM_CONFIG_SHIM_REPLACE_WITH", &target)
+                .define("LLVM_TABLEGEN_EXE", llvm_config.with_file_name("llvm-tblgen"));
+        }
+
         cfg.build();
 
         t!(File::create(&done_stamp));
@@ -588,17 +628,21 @@ impl Step for Sanitizers {
             return runtimes;
         }
 
-        let done_stamp = out_dir.join("sanitizers-finished-building");
-        if done_stamp.exists() {
-            builder.info(&format!(
-                "Assuming that sanitizers rebuild is not necessary. \
-                To force a rebuild, remove the file `{}`",
-                done_stamp.display()
-            ));
+        let stamp = out_dir.join("sanitizers-finished-building");
+        let stamp = HashStamp::new(stamp, builder.in_tree_llvm_info.sha());
+
+        if stamp.is_done() {
+            if stamp.hash.is_none() {
+                builder.info(&format!(
+                    "Rebuild sanitizers by removing the file `{}`",
+                    stamp.path.display()
+                ));
+            }
             return runtimes;
         }
 
         builder.info(&format!("Building sanitizers for {}", self.target));
+        t!(stamp.remove());
         let _time = util::timeit(&builder);
 
         let mut cfg = cmake::Config::new(&compiler_rt_dir);
@@ -627,8 +671,7 @@ impl Step for Sanitizers {
             cfg.build_target(&runtime.cmake_target);
             cfg.build();
         }
-
-        t!(fs::write(&done_stamp, b""));
+        t!(stamp.write());
 
         runtimes
     }
@@ -693,3 +736,41 @@ fn supported_sanitizers(
     }
     result
 }
+
+struct HashStamp {
+    path: PathBuf,
+    hash: Option<Vec<u8>>,
+}
+
+impl HashStamp {
+    fn new(path: PathBuf, hash: Option<&str>) -> Self {
+        HashStamp { path, hash: hash.map(|s| s.as_bytes().to_owned()) }
+    }
+
+    fn is_done(&self) -> bool {
+        match fs::read(&self.path) {
+            Ok(h) => self.hash.as_deref().unwrap_or(b"") == h.as_slice(),
+            Err(e) if e.kind() == io::ErrorKind::NotFound => false,
+            Err(e) => {
+                panic!("failed to read stamp file `{}`: {}", self.path.display(), e);
+            }
+        }
+    }
+
+    fn remove(&self) -> io::Result<()> {
+        match fs::remove_file(&self.path) {
+            Ok(()) => Ok(()),
+            Err(e) => {
+                if e.kind() == io::ErrorKind::NotFound {
+                    Ok(())
+                } else {
+                    Err(e)
+                }
+            }
+        }
+    }
+
+    fn write(&self) -> io::Result<()> {
+        fs::write(&self.path, self.hash.as_deref().unwrap_or(b""))
+    }
+}