diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index fd8bf473ca921..d33fdf69474d8 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -20,6 +20,7 @@ use serde_derive::Deserialize; use tracing::span; use crate::core::build_steps::gcc::{Gcc, GccOutput, GccTargetPair}; +use crate::core::build_steps::llvm::{LlvmFromCi, prebuilt_llvm_output}; use crate::core::build_steps::tool::{RustcPrivateCompilers, SourceType, copy_lld_artifacts}; use crate::core::build_steps::{dist, llvm}; use crate::core::builder::{ @@ -505,14 +506,16 @@ pub fn std_crates_for_make_run(run: &RunConfig<'_>) -> Vec { /// downloaded copy of CI LLVM, then we try to use the `compiler-rt` sources from /// there instead, which lets us avoid checking out the LLVM submodule. fn compiler_rt_for_profiler(builder: &Builder<'_>) -> PathBuf { - // Try to use `compiler-rt` sources from downloaded CI LLVM, if possible. - if builder.config.llvm_from_ci { - // CI LLVM might not have been downloaded yet, so try to download it now. - builder.config.maybe_download_ci_llvm(); - let ci_llvm_compiler_rt = builder.config.ci_llvm_root().join("compiler-rt"); - if ci_llvm_compiler_rt.exists() { - return ci_llvm_compiler_rt; + // Try to use `compiler-rt` sources from downloaded CI LLVM, if available + if let Some(downloaded_llvm) = builder.ensure(LlvmFromCi { target: builder.host_target }) { + let ci_llvm_compiler_rt = downloaded_llvm.output.root_dir().join("compiler-rt"); + if !builder.config.dry_run() { + assert!( + ci_llvm_compiler_rt.exists(), + "compiler-rt sources not found in LLVM downloaded from CI at {ci_llvm_compiler_rt:?}" + ); } + return ci_llvm_compiler_rt; } // Otherwise, fall back to requiring the LLVM submodule. @@ -1387,9 +1390,7 @@ pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetS // (i.e. it's already built or is downloadable), we prefer to maintain a // consistent environment between check and non-check builds. if builder.config.llvm_enabled(target) { - let building_llvm_is_expensive = - crate::core::build_steps::llvm::prebuilt_llvm_config(builder, target, false) - .should_build(); + let building_llvm_is_expensive = prebuilt_llvm_output(builder, target).is_none(); let skip_llvm = (builder.kind == Kind::Check) && building_llvm_is_expensive; if !skip_llvm { @@ -1419,13 +1420,13 @@ pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetS /// Note that this has the side-effect of _building LLVM_, which is sometimes /// unwanted (e.g. for check builds). fn rustc_llvm_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetSelection) { - if builder.config.is_rust_llvm(target) { + let llvm_output = builder.ensure(llvm::Llvm { target }); + if builder.config.is_rust_llvm(&llvm_output, target) { cargo.env("LLVM_RUSTLLVM", "1"); } if builder.config.llvm_enzyme { cargo.env("LLVM_ENZYME", "1"); } - let llvm_output = builder.ensure(llvm::Llvm { target }); if builder.config.llvm_offload { builder.ensure(llvm::OmpOffload { target }); cargo.env("LLVM_OFFLOAD", "1"); @@ -1484,7 +1485,7 @@ fn rustc_llvm_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetSelect ); cargo.env("LLVM_STATIC_STDCPP", file); } - if builder.llvm_link_shared() { + if llvm_output.link_shared() { cargo.env("LLVM_LINK_SHARED", "1"); } if builder.config.llvm_use_libcxx { @@ -2185,7 +2186,7 @@ impl CommandLineStep for Assemble { let src_path = llvm_bin_dir.join(&tool_exe); // When using `download-ci-llvm`, some of the tools may not exist, so skip trying to copy them. - if !src_path.exists() && builder.config.llvm_from_ci { + if !src_path.exists() && builder.config.llvm_ci_mode.download_from_ci() { eprintln!("{} does not exist; skipping copy", src_path.display()); continue; } diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 43ac42a8158ba..3e42b6a5c725a 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -24,6 +24,9 @@ use crate::core::build_steps::compile::{ }; use crate::core::build_steps::doc::DocumentationFormat; use crate::core::build_steps::gcc::GccTargetPair; +use crate::core::build_steps::llvm::{ + LLVM_CI_LINK_TYPE_PATH, LlvmBuildStatus, get_llvm_build_status, +}; use crate::core::build_steps::tool::{ self, RustcPrivateCompilers, ToolTargetBuildMode, get_tool_target_compiler, }; @@ -2474,6 +2477,7 @@ fn install_llvm_file( )] fn maybe_install_llvm( builder: &Builder<'_>, + llvm: &LlvmBuildStatus, target: TargetSelection, dst_libdir: &Path, install_symlink: bool, @@ -2494,7 +2498,14 @@ fn maybe_install_llvm( // // If the LLVM is coming from ourselves (just from CI) though, we // still want to install it, as it otherwise won't be available. - if builder.config.is_system_llvm(target) { + + // FIXME: this should be simplified once we stop pre-setting LLVM CI llvm-config during + // config parsing. + let is_system_llvm = + builder.config.target_config.get(&target).and_then(|t| t.llvm_config.as_ref()).is_some() + && !(builder.config.llvm_ci_mode.download_from_ci() + && builder.config.is_host_target(target)); + if is_system_llvm { trace!("system LLVM requested, no install"); return false; } @@ -2504,7 +2515,7 @@ fn maybe_install_llvm( // clear why this is the case, though. llvm-config will emit the versioned // paths and we don't want those in the sysroot (as we're expecting // unversioned paths). - if target.contains("apple-darwin") && builder.llvm_link_shared() { + if target.contains("apple-darwin") && llvm.llvm_output().link_shared() { let src_libdir = builder.llvm_out(target).join("lib"); let llvm_dylib_path = src_libdir.join("libLLVM.dylib"); if llvm_dylib_path.exists() { @@ -2532,7 +2543,7 @@ fn maybe_install_llvm( !builder.config.dry_run() } else if let llvm::LlvmBuildStatus::AlreadyBuilt(llvm::LlvmOutput { host_llvm_config, .. - }) = llvm::prebuilt_llvm_config(builder, target, true) + }) = llvm { trace!("LLVM already built, installing LLVM files"); let mut cmd = command(host_llvm_config); @@ -2565,7 +2576,6 @@ fn maybe_install_llvm( name = "maybe_install_llvm_target", skip_all, fields( - llvm_link_shared = ?builder.llvm_link_shared(), target = ?target, sysroot = ?sysroot, ), @@ -2573,11 +2583,16 @@ fn maybe_install_llvm( )] pub fn maybe_install_llvm_target(builder: &Builder<'_>, target: TargetSelection, sysroot: &Path) { let dst_libdir = sysroot.join("lib/rustlib").join(target).join("lib"); + + // We need to figure out the link mode from a LLVM, if it is provided, but without forcing it + // to be built if it isn't. + let config = get_llvm_build_status(builder, target); + // We do not need to copy LLVM files into the sysroot if it is not // dynamically linked; it is already included into librustc_llvm // statically. - if builder.llvm_link_shared() { - maybe_install_llvm(builder, target, &dst_libdir, false); + if config.llvm_output().link_shared() { + maybe_install_llvm(builder, &config, target, &dst_libdir, false); } } @@ -2589,7 +2604,6 @@ pub fn maybe_install_llvm_target(builder: &Builder<'_>, target: TargetSelection, name = "maybe_install_llvm_runtime", skip_all, fields( - llvm_link_shared = ?builder.llvm_link_shared(), target = ?target, sysroot = ?sysroot, ), @@ -2597,18 +2611,23 @@ pub fn maybe_install_llvm_target(builder: &Builder<'_>, target: TargetSelection, )] pub fn maybe_install_llvm_runtime(builder: &Builder<'_>, target: TargetSelection, sysroot: &Path) { let dst_libdir = sysroot.join(builder.libdir_relative(Compiler::new(1, target))); + + // We need to figure out the link mode from a LLVM, if it is provided, but without forcing it + // to be built if it isn't. + let config = get_llvm_build_status(builder, target); + // We do not need to copy LLVM files into the sysroot if it is not // dynamically linked; it is already included into librustc_llvm // statically. - if builder.llvm_link_shared() { - maybe_install_llvm(builder, target, &dst_libdir, false); + if config.llvm_output().link_shared() { + maybe_install_llvm(builder, &config, target, &dst_libdir, false); // To workaround lack of rpath on Windows, we bundle another copy of // the LLVM DLL to make rust-lld and llvm-tools work when `sysroot/bin` // is missing from PATH, i.e. when they not launched by rustc. if target.triple.contains("windows") { let dst_libdir = sysroot.join("lib/rustlib").join(target).join("bin"); - maybe_install_llvm(builder, target, &dst_libdir, false); + maybe_install_llvm(builder, &config, target, &dst_libdir, false); } } } @@ -2670,7 +2689,7 @@ impl CommandLineStep for LlvmTools { // Run only if a custom llvm-config is not used if let Some(config) = builder.config.target_config.get(&target) - && !builder.config.llvm_from_ci + && !builder.config.llvm_ci_mode.download_from_ci() && config.llvm_config.is_some() { builder.info(&format!("Skipping LlvmTools ({target}): external LLVM")); @@ -2694,7 +2713,7 @@ impl CommandLineStep for LlvmTools { for tool in tools_to_install(&builder.paths) { let exe = src_bindir.join(exe(tool, target)); // When using `download-ci-llvm`, some of the tools may not exist, so skip trying to copy them. - if !exe.exists() && builder.config.llvm_from_ci { + if !exe.exists() && builder.config.llvm_ci_mode.download_from_ci() { eprintln!("{} does not exist; skipping copy", exe.display()); continue; } @@ -2960,9 +2979,13 @@ impl CommandLineStep for RustDev { // of `rustc-dev` to support the inherited `-lLLVM` when using the // compiler libraries. let dst_libdir = tarball.image_dir().join("lib"); - maybe_install_llvm(builder, target, &dst_libdir, true); - let link_type = if builder.llvm_link_shared() { "dynamic" } else { "static" }; - t!(std::fs::write(tarball.image_dir().join("link-type.txt"), link_type), dst_libdir); + + let config = get_llvm_build_status(builder, target); + maybe_install_llvm(builder, &config, target, &dst_libdir, true); + + // Store the link type, so that it can be read by bootstrap after the archive is downloaded + let link_type = if llvm_output.link_shared() { "dynamic" } else { "static" }; + t!(std::fs::write(tarball.image_dir().join(LLVM_CI_LINK_TYPE_PATH), link_type), dst_libdir); // Copy the `compiler-rt` source, so that `library/profiler_builtins` // can potentially use it to build the profiler runtime without needing diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 67615353b1d9f..a2cf55a7f700d 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -20,7 +20,7 @@ use crate::core::build_steps::llvm; use crate::core::builder::{ Builder, CommandLineStep, Kind, RunConfig, ShouldRun, Step, StepMetadata, }; -use crate::core::config::{Config, LlvmPgoGenerationMode, TargetSelection}; +use crate::core::config::{Config, LlvmCiMode, LlvmPgoGenerationMode, TargetSelection}; use crate::utils::build_stamp::{BuildStamp, generate_smart_stamp_hash}; use crate::utils::exec::command; use crate::utils::helpers::{ @@ -28,13 +28,28 @@ use crate::utils::helpers::{ }; use crate::{CLang, GitRepo, exit, trace}; +/// Path where a file containing the link type (dynamic or static) is stored in the LLVM CI tarball. +pub const LLVM_CI_LINK_TYPE_PATH: &str = "link-type.txt"; + +#[derive(Copy, Clone)] +pub enum LlvmKind { + /// The LLVM was built from in-tree sources + BuiltLocally, + /// The LLVM was downloaded from the `rust-dev` CI artifact. + DownloadedFromCi, + /// The LLVM was provided externally through a `llvm-config` file. + External, +} + /// Result of building or downloading LLVM artifacts. #[derive(Clone)] pub struct LlvmOutput { /// Path to llvm-config binary. /// NB: This is always the host llvm-config! pub host_llvm_config: PathBuf, + link_shared: bool, llvm_root_dir: PathBuf, + kind: LlvmKind, } impl LlvmOutput { @@ -48,6 +63,16 @@ impl LlvmOutput { pub fn cmake_dir(&self) -> PathBuf { self.llvm_root_dir.join("lib").join("cmake").join("llvm") } + + /// Should we link dynamically to the built LLVM? + pub fn link_shared(&self) -> bool { + self.link_shared + } + + /// How was the LLVM produced? + pub fn kind(&self) -> LlvmKind { + self.kind + } } pub struct LlvmBuildInfo { @@ -61,15 +86,7 @@ pub enum LlvmBuildStatus { } impl LlvmBuildStatus { - pub fn should_build(&self) -> bool { - match self { - LlvmBuildStatus::AlreadyBuilt(_) => false, - LlvmBuildStatus::ShouldBuild(_) => true, - } - } - - #[cfg(test)] - pub fn llvm_result(&self) -> &LlvmOutput { + pub fn llvm_output(&self) -> &LlvmOutput { match self { LlvmBuildStatus::AlreadyBuilt(res) => res, LlvmBuildStatus::ShouldBuild(meta) => &meta.output, @@ -119,25 +136,21 @@ impl LdFlags { } } -/// This returns whether we've already previously built LLVM. +/// Attempt to return prebuilt LLVM output information, either downloaded from CI or through an +/// externally provided LLVM. /// -/// It's used to avoid busting caches during x.py check -- if we've already built +/// It's used e.g. 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: TargetSelection, - // Certain commands (like `x test mir-opt --bless`) may call this function with different targets, - // which could bypass the CI LLVM early-return even if `builder.config.llvm_from_ci` is true. - // This flag should be `true` only if the caller needs the LLVM sources (e.g., if it will build LLVM). - handle_submodule_when_needed: bool, -) -> LlvmBuildStatus { - builder.config.maybe_download_ci_llvm(); - - // If we're using a custom LLVM bail out here, but we can only use a - // custom LLVM for the build triple. +/// Calling this function should never attempt to checkout the LLVM submodule. +pub fn prebuilt_llvm_output(builder: &Builder<'_>, target: TargetSelection) -> Option { + // Try to download LLVM from CI, if possible + let llvm_ci = builder.ensure(LlvmFromCi { target }); + if let Some(llvm) = llvm_ci { + return Some(llvm.output); + } + + // If it is not available, use an externally provided LLVM if let Some(config) = builder.config.target_config.get(&target) && let Some(ref s) = config.llvm_config { @@ -146,14 +159,32 @@ pub fn prebuilt_llvm_config( let mut llvm_root_dir = host_llvm_config.clone(); llvm_root_dir.pop(); llvm_root_dir.pop(); - return LlvmBuildStatus::AlreadyBuilt(LlvmOutput { host_llvm_config, llvm_root_dir }); + + return Some(LlvmOutput { + host_llvm_config, + link_shared: llvm_link_shared(&builder.config), + llvm_root_dir, + kind: LlvmKind::External, + }); } + None +} - if handle_submodule_when_needed { - // If submodules are disabled, this does nothing. - builder.config.update_submodule("src/llvm-project"); +/// This returns whether we've already previously built LLVM. +/// +/// This will return the llvm-config if it can get it (but it will not build it +/// if not). +/// +/// Note that calling this function *might* checkout the LLVM submodule! +pub fn get_llvm_build_status(builder: &Builder<'_>, target: TargetSelection) -> LlvmBuildStatus { + if let Some(prebuilt_output) = prebuilt_llvm_output(builder, target) { + return LlvmBuildStatus::AlreadyBuilt(prebuilt_output); } + // In remaining cases, build it locally + // If submodules are disabled, this does nothing. + builder.config.update_submodule("src/llvm-project"); + let out_dir = builder.llvm_out(target); let build_llvm_config = if let Some(build_llvm_config) = builder @@ -169,7 +200,12 @@ pub fn prebuilt_llvm_config( llvm_config_ret_dir.join(exe("llvm-config", builder.config.host_target)) }; - let res = LlvmOutput { host_llvm_config: build_llvm_config, llvm_root_dir: out_dir.clone() }; + let res = LlvmOutput { + host_llvm_config: build_llvm_config, + link_shared: llvm_link_shared(&builder.config), + llvm_root_dir: out_dir.clone(), + kind: LlvmKind::BuiltLocally, + }; static STAMP_HASH_MEMO: OnceLock = OnceLock::new(); let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| { @@ -199,6 +235,59 @@ pub fn prebuilt_llvm_config( LlvmBuildStatus::ShouldBuild(LlvmBuildInfo { stamp, output: res }) } +fn try_download_ci_llvm(builder: &Builder<'_>, target: TargetSelection) -> Option { + match builder.config.llvm_ci_mode { + LlvmCiMode::BuildLocally => return None, + LlvmCiMode::DownloadFromCi => {} + } + + // FIXME: this should eventually be relaxed + if target != builder.host_target { + crate::debug!("LLVM not available on CI for non-host target {target}"); + return None; + } + + if !is_ci_llvm_available_for_target(&target, builder.config.llvm_assertions) { + crate::debug!( + "LLVM not available on CI for target={target} and assertions={}", + builder.config.llvm_assertions + ); + return None; + } + + let ci_llvm = builder.config.maybe_download_host_ci_llvm()?; + let link_shared = if !builder.config.dry_run() { + let link_type = t!( + std::fs::read_to_string(ci_llvm.join(LLVM_CI_LINK_TYPE_PATH)), + format!("LLVM downloaded from CI is missing the following file: {}", ci_llvm.display()) + ); + link_type == "dynamic" + } else { + false + }; + + Some(DownloadedLlvm { + output: LlvmOutput { + host_llvm_config: ci_llvm.join("bin").join("llvm-config"), + link_shared, + llvm_root_dir: ci_llvm, + kind: LlvmKind::DownloadedFromCi, + }, + }) +} + +/// Determine whether llvm should be linked dynamically. +/// **NOTE**: This only contains the value from the config. +/// If you need to figure out the correct value for a specific LLVM instance, use +/// `prebuilt_llvm_config` instead. +/// +/// This function is not a method on Config to discourage calling it from outside this module. +fn llvm_link_shared(config: &Config) -> bool { + // unclear how thought-through this default is, but it maintains compatibility with + // previous behavior + config.llvm_link_shared.unwrap_or(false) +} + /// Paths whose changes invalidate LLVM downloads. pub const LLVM_INVALIDATION_PATHS: &[&str] = &[ "src/llvm-project", @@ -275,6 +364,36 @@ pub(crate) fn is_ci_llvm_available_for_target( true } +#[derive(Clone)] +pub struct DownloadedLlvm { + pub output: LlvmOutput, +} + +/// This step explicitly represents the output of *downloaded* LLVM. +/// The step will provide an output only if all the following is true: +/// - `llvm.download-ci-llvm` is `true` or `if-unchanged` +/// - If the previous value is `if-unchanged`, the local LLVM inputs are not modified +/// - Artifacts for LLVM for the given target (and debug assertions) are available on CI +/// +/// There are some places in bootstrap that explicitly want to do something special about the +/// downloaded LLVM, this step serves for them to do it in an explicit way. +/// For all other use-cases, the normal `Llvm` step should be used. +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct LlvmFromCi { + pub target: TargetSelection, +} + +impl Step for LlvmFromCi { + type Output = Option; + + fn run(self, builder: &Builder<'_>) -> Self::Output { + let llvm_ci = try_download_ci_llvm(builder, self.target)?; + // Sanity check + check_llvm_version(builder, &llvm_ci.output.host_llvm_config); + Some(llvm_ci) + } +} + #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Llvm { pub target: TargetSelection, @@ -311,12 +430,14 @@ impl CommandLineStep for Llvm { }; // If LLVM has already been built or been downloaded through download-ci-llvm, we avoid building it again. - let LlvmBuildInfo { stamp, output } = match prebuilt_llvm_config(builder, target, true) { + let LlvmBuildInfo { stamp, output } = match get_llvm_build_status(builder, target) { LlvmBuildStatus::AlreadyBuilt(p) => return p, LlvmBuildStatus::ShouldBuild(m) => m, }; - if builder.llvm_link_shared() && target.is_windows() && !target.is_windows_gnullvm() { + let link_shared = llvm_link_shared(&builder.config); + + if link_shared && target.is_windows() && !target.is_windows_gnullvm() { panic!("shared linking to LLVM is not currently supported on {}", target.triple); } @@ -412,7 +533,7 @@ impl CommandLineStep for Llvm { // which saves both memory during parallel links and overall disk space // for the tools. We don't do this on every platform as it doesn't work // equally well everywhere. - if builder.llvm_link_shared() { + if link_shared { cfg.define("LLVM_LINK_LLVM_DYLIB", "ON"); // Keep the pre-LLVM23 behavior for now. cfg.define("LLVM_VERSIONED_DYLIB_NAME_ON_DARWIN", "OFF"); @@ -582,7 +703,7 @@ impl CommandLineStep for Llvm { // libLLVM.dylib will be built. However, llvm-config will still look // for a versioned path like libLLVM-14.dylib. Manually create a symbolic // link to make llvm-config happy. - if builder.llvm_link_shared() && target.contains("apple-darwin") { + if link_shared && target.contains("apple-darwin") { let lib_name = find_llvm_lib_name("dylib"); let lib_llvm = output.root_dir().join("build").join("lib").join(lib_name); if !lib_llvm.exists() { @@ -593,10 +714,7 @@ impl CommandLineStep for Llvm { // When building LLVM as a shared library on linux, it can contain unexpected debuginfo: // some can come from the C++ standard library. Unless we're explicitly requesting LLVM to // be built with debuginfo, strip it away after the fact, to make dist artifacts smaller. - if builder.llvm_link_shared() - && builder.config.llvm_optimize - && !builder.config.llvm_release_debuginfo - { + if link_shared && builder.config.llvm_optimize && !builder.config.llvm_release_debuginfo { // Find the name of the LLVM shared library that we just built. let lib_name = find_llvm_lib_name("so"); @@ -1503,7 +1621,7 @@ impl CommandLineStep for Lld { // if builder.config.rpath_enabled(target) && helpers::use_host_linker(target) - && builder.config.llvm_link_shared() + && llvm_output.link_shared() && target.contains("linux") { // So we inform LLD where it can find LLVM's libraries by adding an rpath entry to the diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index dca3220acda55..6e2ff37d6b2af 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -2707,11 +2707,10 @@ Please disable assertions with `rust.debug-assertions = false`. let mut llvm_components_passed = false; let mut copts_passed = false; if builder.config.llvm_enabled(test_compiler.host) { - let llvm::LlvmOutput { host_llvm_config, .. } = - builder.ensure(llvm::Llvm { target: builder.config.host_target }); + let llvm_output = builder.ensure(llvm::Llvm { target: builder.config.host_target }); if !builder.config.dry_run() { - let llvm_version = get_llvm_version(builder, &host_llvm_config); - let llvm_components = command(&host_llvm_config) + let llvm_version = get_llvm_version(builder, &llvm_output.host_llvm_config); + let llvm_components = command(&llvm_output.host_llvm_config) .cached() .arg("--components") .run_capture_stdout(builder) @@ -2723,7 +2722,7 @@ Please disable assertions with `rust.debug-assertions = false`. .arg(llvm_components.trim()); llvm_components_passed = true; } - if !builder.config.is_rust_llvm(target) { + if !builder.config.is_rust_llvm(&llvm_output, target) { cmd.arg("--system-llvm"); } @@ -2732,7 +2731,7 @@ Please disable assertions with `rust.debug-assertions = false`. // separate compilations. We can add LLVM's library path to the // rustc args as a workaround. if !builder.config.dry_run() && suite.ends_with("fulldeps") { - let llvm_libdir = command(&host_llvm_config) + let llvm_libdir = command(&llvm_output.host_llvm_config) .cached() .arg("--libdir") .run_capture_stdout(builder) @@ -2752,7 +2751,8 @@ Please disable assertions with `rust.debug-assertions = false`. // tools. Pass the path to run-make tests so they can use them. // (The coverage-run tests also need these tools to process // coverage reports.) - let llvm_bin_path = host_llvm_config + let llvm_bin_path = llvm_output + .host_llvm_config .parent() .expect("Expected llvm-config to be contained in directory"); assert!(llvm_bin_path.is_dir()); diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index cd4e40a58ffc3..1c2e724ceae30 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -3,6 +3,7 @@ use std::ffi::{OsStr, OsString}; use std::path::{Path, PathBuf}; use super::{Builder, Kind}; +use crate::core::build_steps::llvm::prebuilt_llvm_output; use crate::core::build_steps::test; use crate::core::build_steps::tool::SourceType; use crate::core::config::flags::Color; @@ -699,9 +700,7 @@ impl Builder<'_> { // rustc_llvm. But if LLVM is stale, that'll be a tiny amount // of work comparatively, and we'd likely need to rebuild it anyway, // so that's okay. - if crate::core::build_steps::llvm::prebuilt_llvm_config(self, target, false) - .should_build() - { + if prebuilt_llvm_output(self, target).is_none() { cargo.env("RUST_CHECK", "1"); } } diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 8d0461f0787ed..d254eb17563b4 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1433,7 +1433,7 @@ Alternatively, you can set `build.local-rebuild=true` and use a stage0 compiler let mut dylib_dirs = vec![self.rustc_libdir(compiler)]; // Ensure that the downloaded LLVM libraries can be found. - if self.config.llvm_from_ci { + if self.config.llvm_ci_mode.download_from_ci() { let ci_llvm_lib = self.out.join(compiler.host).join("ci-llvm").join("lib"); dylib_dirs.push(ci_llvm_lib); } @@ -1585,6 +1585,24 @@ Alternatively, you can set `build.local-rebuild=true` and use a stage0 compiler None } + /// Root output directory of LLVM for `target` + /// + /// Note that if LLVM is configured externally then the directory returned + /// will likely be empty. + pub fn llvm_out(&self, target: TargetSelection) -> PathBuf { + // We don't want to eagerly build LLVM by calling this function, so we only check if it + // was already downloaded from CI. + // The first part of the condition ensures that we don't download LLVM for non-host targets + // from CI eagerly (FIXME: this could be relaxed in the future). + if self.config.is_host_target(target) + && let Some(llvm_ci) = self.ensure(llvm::LlvmFromCi { target }) + { + llvm_ci.output.root_dir().to_path_buf() + } else { + self.out.join(target).join("llvm") + } + } + /// Updates all submodules, and exits with an error if submodule /// management is disabled and the submodule does not exist. pub fn require_and_update_all_submodules(&self) { diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 4ae1ee53537f2..201e3e8f0dada 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -2,7 +2,7 @@ use std::panic; use build_helper::stage0_parser::parse_stage0_file; -use llvm::prebuilt_llvm_config; +use llvm::get_llvm_build_status; use super::*; use crate::core::config::Config; @@ -112,33 +112,6 @@ fn parse_config_download_rustc_at(path: &Path, download_rustc: &str, ci: bool) - .create_config() } -mod dist { - use super::{Config, TEST_TRIPLE_1, TEST_TRIPLE_2}; - use crate::core::builder::tests::host_target; - use crate::core::builder::*; - - fn configure(host: &[&str], target: &[&str]) -> Config { - Config { stage: 2, ..super::configure("dist", host, target) } - } - - #[test] - fn llvm_out_behaviour() { - let mut config = configure(&[], &[TEST_TRIPLE_2]); - config.llvm_from_ci = true; - let build = Build::new(config.clone()); - - let target = TargetSelection::from_user(&host_target()); - assert!(build.llvm_out(target).ends_with("ci-llvm")); - let target = TargetSelection::from_user(TEST_TRIPLE_2); - assert!(build.llvm_out(target).ends_with("llvm")); - - config.llvm_from_ci = false; - let build = Build::new(config.clone()); - let target = TargetSelection::from_user(TEST_TRIPLE_1); - assert!(build.llvm_out(target).ends_with("llvm")); - } -} - mod sysroot_target_dirs { use super::{ Build, Builder, Compiler, TEST_TRIPLE_1, TEST_TRIPLE_2, TargetSelection, configure, @@ -274,19 +247,16 @@ fn test_prebuilt_llvm_config_path_resolution() { let expected = PathBuf::from("/some/path/to/llvm-config"); - let actual = prebuilt_llvm_config( - &builder, - TargetSelection::from_user("arm-unknown-linux-gnueabihf"), - false, - ) - .llvm_result() - .host_llvm_config - .clone(); + let actual = + get_llvm_build_status(&builder, TargetSelection::from_user("arm-unknown-linux-gnueabihf")) + .llvm_output() + .host_llvm_config + .clone(); let actual = drop_win_disk_prefix_if_present(actual); assert_eq!(expected, actual); - let actual = prebuilt_llvm_config(&builder, builder.config.host_target, false) - .llvm_result() + let actual = get_llvm_build_status(&builder, builder.config.host_target) + .llvm_output() .host_llvm_config .clone(); let actual = drop_win_disk_prefix_if_present(actual); @@ -303,8 +273,8 @@ fn test_prebuilt_llvm_config_path_resolution() { let build = Build::new(config.clone()); let builder = Builder::new(&build); - let actual = prebuilt_llvm_config(&builder, builder.config.host_target, false) - .llvm_result() + let actual = get_llvm_build_status(&builder, builder.config.host_target) + .llvm_output() .host_llvm_config .clone(); let expected = builder @@ -322,12 +292,12 @@ fn test_prebuilt_llvm_config_path_resolution() { ); // CI-LLVM isn't always available; check if it's enabled before testing. - if config.llvm_from_ci { + if config.llvm_ci_mode.download_from_ci() { let build = Build::new(config.clone()); let builder = Builder::new(&build); - let actual = prebuilt_llvm_config(&builder, builder.config.host_target, false) - .llvm_result() + let actual = get_llvm_build_status(&builder, builder.config.host_target) + .llvm_output() .host_llvm_config .clone(); let expected = builder diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index fc69eada5eb02..4c5d95ae7ad77 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -13,8 +13,6 @@ //! and the `bootstrap.toml` file—merging them, applying defaults, and performing //! cross-component validation. The main `parse_inner` function and its supporting //! helpers reside here, transforming raw `Toml` data into the structured `Config` type. - -use std::cell::Cell; use std::collections::{BTreeSet, HashMap, HashSet}; use std::io::IsTerminal; use std::path::{Path, PathBuf, absolute}; @@ -29,7 +27,7 @@ use serde::Deserialize; use tracing::{instrument, span}; use crate::core::build_steps::llvm; -use crate::core::build_steps::llvm::LLVM_INVALIDATION_PATHS; +use crate::core::build_steps::llvm::{LLVM_INVALIDATION_PATHS, LlvmKind, LlvmOutput}; use crate::core::build_steps::test::failed_tests::collect_previously_failed_tests; pub use crate::core::config::flags::Subcommand; use crate::core::config::flags::{Color, Flags, Warnings}; @@ -51,8 +49,8 @@ use crate::core::config::toml::target::{ }; use crate::core::config::{ Allocator, CompilerBuiltins, CompressDebuginfo, DebuggerPath, DebuginfoLevel, DryRun, - GccCiMode, LlvmLibunwind, Merge, ReplaceOpt, RustcLto, SplitDebuginfo, StringOrBool, - TargetSelection, threads_from_config, + GccCiMode, LlvmCiMode, LlvmLibunwind, Merge, ReplaceOpt, RustcLto, SplitDebuginfo, + StringOrBool, TargetSelection, threads_from_config, }; use crate::core::download::{DownloadContext, download_beta_toolchain, is_download_ci_available}; use crate::utils::channel::{self, GitInfo}; @@ -167,7 +165,7 @@ pub struct Config { pub llvm_release_debuginfo: bool, pub llvm_static_stdcpp: bool, pub llvm_libzstd: bool, - pub llvm_link_shared: Cell>, + pub llvm_link_shared: Option, pub llvm_clang_cl: Option, pub llvm_targets: Option, pub llvm_experimental_targets: Option, @@ -179,7 +177,7 @@ pub struct Config { pub llvm_polly: bool, pub llvm_clang: bool, pub llvm_enable_warnings: bool, - pub llvm_from_ci: bool, + pub llvm_ci_mode: LlvmCiMode, pub llvm_build_config: HashMap, pub bootstrap_override_lld: BootstrapOverrideLld, @@ -1082,6 +1080,12 @@ impl Config { llvm_download_ci_llvm, llvm_assertions, ); + + // FIXME: llvm_ci_mode should eventually represent what was used in the config, not the + // dynamic value used for determining whether it is actually available. + let llvm_ci_mode = + if llvm_from_ci { LlvmCiMode::DownloadFromCi } else { LlvmCiMode::BuildLocally }; + let is_host_system_llvm = target_config.get(&host_target).and_then(|c| c.llvm_config.as_ref()).is_some(); @@ -1119,14 +1123,10 @@ impl Config { ); } - let triple = &host_target.triple; - let ci_llvm_bin = ci_llvm_root(&dwn_ctx, llvm_from_ci, &out).join("bin"); - let build_target = - target_config.entry(host_target).or_insert_with(|| Target::from_triple(triple)); - check_ci_llvm!(build_target.llvm_config); - check_ci_llvm!(build_target.llvm_filecheck); - // FIXME: Do not overwrite the LLVM config here - build_target.llvm_config = Some(ci_llvm_bin.join(exe("llvm-config", host_target))); + if let Some(target) = target_config.get(&host_target) { + check_ci_llvm!(target.llvm_config); + check_ci_llvm!(target.llvm_filecheck); + } } for (target, linker_override) in default_linux_linker_overrides() { @@ -1398,6 +1398,12 @@ NOTE: Please add `--stage 2` to your command line, or if you're sure you want to paths }; + // If we're building with ThinLTO on, by default we want to link + // to LLVM shared, to avoid re-doing ThinLTO (which happens in + // the link step) with each stage. + let llvm_link_shared = + llvm_link_shared.or((!llvm_from_ci && llvm_thin_lto.unwrap_or(false)).then_some(true)); + Config { // tidy-alphabetical-start allocator: reconcile_jemalloc(rust_jemalloc, build_allocator, "rust", "build"), @@ -1480,6 +1486,7 @@ NOTE: Please add `--stage 2` to your command line, or if you're sure you want to llvm_bitcode_linker_enabled: rust_llvm_bitcode_linker.unwrap_or(false), llvm_build_config: llvm_build_config.clone().unwrap_or(Default::default()), llvm_cflags, + llvm_ci_mode, llvm_clang: llvm_clang.unwrap_or(false), llvm_clang_cl, llvm_clang_dir: llvm_clang_dir.map(PathBuf::from), @@ -1487,19 +1494,12 @@ NOTE: Please add `--stage 2` to your command line, or if you're sure you want to llvm_enable_warnings: llvm_enable_warnings.unwrap_or(false), llvm_enzyme: llvm_enzyme.unwrap_or(false), llvm_experimental_targets, - llvm_from_ci, llvm_ldflags, llvm_libunwind_default: rust_llvm_libunwind .map(|v| v.parse().expect("failed to parse rust.llvm-libunwind")), llvm_libzstd: llvm_libzstd.unwrap_or(false), llvm_link_jobs, - // If we're building with ThinLTO on, by default we want to link - // to LLVM shared, to avoid re-doing ThinLTO (which happens in - // the link step) with each stage. - llvm_link_shared: Cell::new( - llvm_link_shared - .or((!llvm_from_ci && llvm_thin_lto.unwrap_or(false)).then_some(true)), - ), + llvm_link_shared, llvm_offload: llvm_offload.unwrap_or(false), llvm_optimize: llvm_optimize.unwrap_or(true), llvm_pgo: pgo_llvm, @@ -1728,48 +1728,12 @@ NOTE: Please add `--stage 2` to your command line, or if you're sure you want to } } - /// The absolute path to the downloaded LLVM artifacts. - pub(crate) fn ci_llvm_root(&self) -> PathBuf { - let dwn_ctx = DownloadContext::from(self); - ci_llvm_root(dwn_ctx, self.llvm_from_ci, &self.out) - } - /// Directory where the extracted `rustc-dev` component is stored. pub(crate) fn ci_rustc_dir(&self) -> PathBuf { assert!(self.download_rustc()); self.out.join(self.host_target).join("ci-rustc") } - /// Determine whether llvm should be linked dynamically. - /// - /// If `false`, llvm should be linked statically. - /// This is computed on demand since LLVM might have to first be downloaded from CI. - pub(crate) fn llvm_link_shared(&self) -> bool { - let mut opt = self.llvm_link_shared.get(); - if opt.is_none() && self.dry_run() { - // just assume static for now - dynamic linking isn't supported on all platforms - return false; - } - - let llvm_link_shared = *opt.get_or_insert_with(|| { - if self.llvm_from_ci { - self.maybe_download_ci_llvm(); - let ci_llvm = self.ci_llvm_root(); - let link_type = t!( - std::fs::read_to_string(ci_llvm.join("link-type.txt")), - format!("CI llvm missing: {}", ci_llvm.display()) - ); - link_type == "dynamic" - } else { - // unclear how thought-through this default is, but it maintains compatibility with - // previous behavior - false - } - }); - self.llvm_link_shared.set(opt); - llvm_link_shared - } - /// Return whether we will use a downloaded, pre-compiled version of rustc, or just build from source. pub(crate) fn download_rustc(&self) -> bool { self.download_rustc_commit().is_some() @@ -1791,7 +1755,7 @@ NOTE: Please add `--stage 2` to your command line, or if you're sure you want to // CI-rustc can't be used without CI-LLVM. If `self.llvm_from_ci` is false, it means the "if-unchanged" // logic has detected some changes in the LLVM submodule (download-ci-llvm=false can't happen here as // we don't allow it while parsing the configuration). - if !self.llvm_from_ci { + if !self.llvm_ci_mode.download_from_ci() { // This happens when LLVM submodule is updated in CI, we should disable ci-rustc without an error // to not break CI. For non-CI environments, we should return an error. if self.is_running_on_ci() { @@ -2022,18 +1986,10 @@ NOTE: Please add `--stage 2` to your command line, or if you're sure you want to self.host_target == target } - /// Returns `true` if this is an external version of LLVM not managed by bootstrap. - /// In particular, we expect llvm sources to be available when this is false. - /// - /// NOTE: this is not the same as `!is_rust_llvm` when `llvm_has_patches` is set. - pub fn is_system_llvm(&self, target: TargetSelection) -> bool { - is_system_llvm(&self.target_config, self.llvm_from_ci, self.host_target, target) - } - /// Returns `true` if this is our custom, patched, version of LLVM. /// /// This does not necessarily imply that we're managing the `llvm-project` submodule. - pub fn is_rust_llvm(&self, target: TargetSelection) -> bool { + pub fn is_rust_llvm(&self, llvm: &LlvmOutput, target: TargetSelection) -> bool { match self.target_config.get(&target) { // We're using a user-controlled version of LLVM. The user has explicitly told us whether the version has our patches. // (They might be wrong, but that's not a supported use-case.) @@ -2041,7 +1997,10 @@ NOTE: Please add `--stage 2` to your command line, or if you're sure you want to Some(Target { llvm_has_rust_patches: Some(patched), .. }) => *patched, // The user hasn't promised the patches match. // This only has our patches if it's downloaded from CI or built from source. - _ => !self.is_system_llvm(target), + _ => match llvm.kind() { + LlvmKind::BuiltLocally | LlvmKind::DownloadedFromCi => true, + LlvmKind::External => false, + }, } } @@ -2690,41 +2649,6 @@ pub fn submodules_(submodules: &Option, rust_info: &channel::GitInfo) -> b submodules.unwrap_or(rust_info.is_managed_git_subrepository()) } -/// Returns `true` if this is an external version of LLVM not managed by bootstrap. -/// In particular, we expect llvm sources to be available when this is false. -/// -/// NOTE: this is not the same as `!is_rust_llvm` when `llvm_has_patches` is set. -pub fn is_system_llvm( - target_config: &HashMap, - llvm_from_ci: bool, - host_target: TargetSelection, - target: TargetSelection, -) -> bool { - match target_config.get(&target) { - Some(Target { llvm_config: Some(_), .. }) => { - let ci_llvm = llvm_from_ci && is_host_target(&host_target, &target); - !ci_llvm - } - // We're building from the in-tree src/llvm-project sources. - Some(Target { llvm_config: None, .. }) => false, - None => false, - } -} - -pub fn is_host_target(host_target: &TargetSelection, target: &TargetSelection) -> bool { - host_target == target -} - -pub(crate) fn ci_llvm_root<'a>( - dwn_ctx: impl AsRef>, - llvm_from_ci: bool, - out: &Path, -) -> PathBuf { - let dwn_ctx = dwn_ctx.as_ref(); - assert!(llvm_from_ci); - out.join(dwn_ctx.host_target).join("ci-llvm") -} - /// Returns the content of the given file at a specific commit. pub(crate) fn read_file_by_commit<'a>( dwn_ctx: impl AsRef>, diff --git a/src/bootstrap/src/core/config/mod.rs b/src/bootstrap/src/core/config/mod.rs index db901434a47e8..0bac86471eaac 100644 --- a/src/bootstrap/src/core/config/mod.rs +++ b/src/bootstrap/src/core/config/mod.rs @@ -512,6 +512,26 @@ pub enum GccCiMode { DownloadFromCi, } +/// Determines how will LLVM be provided. +#[derive(Default, Debug, Clone, PartialEq)] +pub enum LlvmCiMode { + /// Build LLVM from the local `src/llvm-project` submodule. + BuildLocally, + /// Try to download LLVM from CI. + /// If it is not available on CI, it will be built locally instead. + #[default] + DownloadFromCi, +} + +impl LlvmCiMode { + pub fn download_from_ci(&self) -> bool { + match self { + LlvmCiMode::BuildLocally => false, + LlvmCiMode::DownloadFromCi => true, + } + } +} + #[derive(Clone, Debug, PartialEq)] pub enum DebuggerPath { /// Use a debugger at this path diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index ad0859727c8fc..94d3d358b2a74 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -34,14 +34,15 @@ fn modified(upstream: impl Into, changes: &[&str]) -> PathFreshness { #[test] fn download_ci_llvm() { let config = TestCtx::new().config("check").create_config(); - assert!(!config.llvm_from_ci); + assert!(!config.llvm_ci_mode.download_from_ci()); // this doesn't make sense, as we are overriding it later. let if_unchanged_config = TestCtx::new() .config("check") .with_default_toml_config("llvm.download-ci-llvm = \"if-unchanged\"") .create_config(); - if if_unchanged_config.llvm_from_ci && if_unchanged_config.is_running_on_ci() { + if if_unchanged_config.llvm_ci_mode.download_from_ci() && if_unchanged_config.is_running_on_ci() + { let has_changes = if_unchanged_config.has_changes_from_upstream(LLVM_INVALIDATION_PATHS); assert!( @@ -161,7 +162,7 @@ fn override_toml() { .collect(), "setting dictionary value" ); - assert!(!config.llvm_from_ci); + assert!(!config.llvm_ci_mode.download_from_ci()); assert!(!config.download_rustc()); } diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index 665a976fe8cea..f5d9d452a0101 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -270,17 +270,15 @@ impl Config { download_component(dwn_ctx, &self.out, mode, filename, prefix, key, destination); } - pub(crate) fn maybe_download_ci_llvm(&self) { + /// Attempts to download LLVM from CI for the **host target**. + /// Returns a path to the downloaded and extracted directory. + pub(crate) fn maybe_download_host_ci_llvm(&self) -> Option { // Never try to download CI LLVM during unit tests. if cfg!(test) { - return; - } - - if !self.llvm_from_ci { - return; + return None; } - let llvm_root = self.ci_llvm_root(); + let llvm_root = self.out.join(self.host_target).join("ci-llvm"); let llvm_freshness = detect_llvm_freshness(self, self.rust_info.is_managed_git_subrepository()); self.do_if_verbose(|| { @@ -300,7 +298,7 @@ impl Config { let stamp_key = format!("{}{}", llvm_sha, self.llvm_assertions); let llvm_stamp = BuildStamp::new(&llvm_root).with_prefix("llvm").add_stamp(stamp_key); if !llvm_stamp.is_up_to_date() && !self.dry_run() { - self.download_ci_llvm(&llvm_sha); + self.download_ci_llvm(&llvm_root, &llvm_sha); if self.should_fix_bins_and_dylibs() { for entry in t!(fs::read_dir(llvm_root.join("bin"))) { @@ -354,9 +352,10 @@ impl Config { } }; }; + Some(llvm_root) } - fn download_ci_llvm(&self, llvm_sha: &str) { + fn download_ci_llvm(&self, llvm_root: &Path, llvm_sha: &str) { // For unit tests, downloading should have been blocked by `maybe_download_ci_llvm`. assert!(cfg!(not(test)), "unit tests shouldn't be downloading CI LLVM"); @@ -391,8 +390,7 @@ impl Config { "; self.download_file(&format!("{base}/{llvm_sha}/{filename}"), &tarball, help_on_error); } - let llvm_root = self.ci_llvm_root(); - self.unpack(&tarball, &llvm_root, "rust-dev"); + self.unpack(&tarball, llvm_root, "rust-dev"); } pub fn download_ci_gcc(&self, gcc_sha: &str, root_dir: &Path) { diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index c043dc3944f18..0364c830c4da6 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -110,7 +110,7 @@ pub fn check(build: &mut Build) { if cfg!(not(test)) && !build.config.dry_run() && !build.host_target.is_msvc() - && build.config.llvm_from_ci + && build.config.llvm_ci_mode.download_from_ci() { let builder = Builder::new(build); let libcxx_version = builder.ensure(tool::LibcxxVersionTool { target: build.host_target }); @@ -138,7 +138,7 @@ pub fn check(build: &mut Build) { } // We need cmake, but only if we're actually building LLVM or sanitizers. - let building_llvm = !build.config.llvm_from_ci + let building_llvm = !build.config.llvm_ci_mode.download_from_ci() && !build.config.local_rebuild && build.hosts.iter().any(|host| { build.config.llvm_enabled(*host) diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index c084c12ae4ab8..c486abac7f8ae 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -409,7 +409,6 @@ forward! { create(path: &Path, s: &str), remove(f: &Path), tempdir() -> PathBuf, - llvm_link_shared() -> bool, download_rustc() -> bool, } @@ -962,18 +961,6 @@ impl Build { self.stage_out(build_compiler, mode).join(target).join(self.cargo_dir(mode)) } - /// Root output directory of LLVM for `target` - /// - /// Note that if LLVM is configured externally then the directory returned - /// will likely be empty. - fn llvm_out(&self, target: TargetSelection) -> PathBuf { - if self.config.llvm_from_ci && self.config.is_host_target(target) { - self.config.ci_llvm_root() - } else { - self.out.join(target).join("llvm") - } - } - /// Output directory for all documentation for a target fn doc_out(&self, target: TargetSelection) -> PathBuf { self.out.join(target).join("doc") diff --git a/src/bootstrap/src/utils/tracing.rs b/src/bootstrap/src/utils/tracing.rs index 9ab1ca23ee5a8..4b4b50d68a058 100644 --- a/src/bootstrap/src/utils/tracing.rs +++ b/src/bootstrap/src/utils/tracing.rs @@ -122,7 +122,10 @@ mod inner { pub fn setup_tracing(env_name: &str) -> TracingGuard { let filter = EnvFilter::from_env(env_name); - let registry = tracing_subscriber::registry().with(filter).with(TracingPrinter::default()); + let mut printer = TracingPrinter::default(); + printer.show_time = + !std::env::var("BOOTSTRAP_TRACING_SKIP_TIME").map(|v| v == "1").unwrap_or(false); + let registry = tracing_subscriber::registry().with(filter).with(printer); // When we're creating this layer, we do not yet know the location of the tracing output // directory, because it is stored in the output directory determined after Config is parsed, @@ -233,6 +236,7 @@ mod inner { struct TracingPrinter { indent: std::sync::atomic::AtomicU32, span_values: std::sync::Mutex>, + show_time: bool, } impl TracingPrinter { @@ -242,9 +246,11 @@ mod inner { time: DateTime, level: &Level, ) -> std::io::Result<()> { - // Use a fixed-width timestamp without date, that shouldn't be very important - let timestamp = time.format("%H:%M:%S.%3f"); - write!(writer, "{timestamp} ")?; + if self.show_time { + // Use a fixed-width timestamp without date, that shouldn't be very important + let timestamp = time.format("%H:%M:%S.%3f"); + write!(writer, "{timestamp} ")?; + } // Make sure that levels are aligned to the same number of characters, in order not to // break the layout write!(writer, "{level:>5} ")?; diff --git a/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md b/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md index da7c07cb23c52..5c3ee9df43a58 100644 --- a/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md +++ b/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md @@ -107,6 +107,9 @@ $ BOOTSTRAP_TRACING=CONFIG_HANDLING=trace,STEP=info,COMMAND=trace ./x build libr Note that the level that you specify using `BOOTSTRAP_TRACING` also has an effect on the spans that will be recorded in the Chrome trace file. +You can also pass the `BOOTSTRAP_TRACING_SKIP_TIME=1` environment variable to avoid including the timestamp in the tracing output. +This is useful for comparing/diffing the executed steps between two versions of bootstrap. + ##### FIXME(#96176): specific tracing for `compiler()` vs `compiler_for()` The additional targets `COMPILER` and `COMPILER_FOR` are used to help trace what