diff options
Diffstat (limited to 'src/bootstrap/native.rs')
| -rw-r--r-- | src/bootstrap/native.rs | 225 |
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"")) + } +} |
