//! This module implements parsing `config.toml` configuration files to tweak
//! how the build runs.
+use std::cell::Cell;
use std::cmp;
use std::collections::{HashMap, HashSet};
use std::env;
use std::path::{Path, PathBuf};
use std::str::FromStr;
-use crate::builder::TaskPath;
+use crate::builder::{Builder, TaskPath};
use crate::cache::{Interned, INTERNER};
use crate::channel::GitInfo;
pub use crate::flags::Subcommand;
/// each field, see the corresponding fields in
/// `config.toml.example`.
#[derive(Default)]
+#[cfg_attr(test, derive(Clone))]
pub struct Config {
pub changelog_seen: Option<usize>,
pub ccache: Option<String>,
pub test_compare_mode: bool,
pub llvm_libunwind: LlvmLibunwind,
pub color: Color,
+ pub patch_binaries_for_nix: bool,
pub on_fail: Option<String>,
pub stage: u32,
pub keep_stage: Vec<u32>,
pub keep_stage_std: Vec<u32>,
pub src: PathBuf,
- // defaults to `config.toml`
+ /// defaults to `config.toml`
pub config: PathBuf,
pub jobs: Option<u32>,
pub cmd: Subcommand,
pub llvm_release_debuginfo: bool,
pub llvm_version_check: bool,
pub llvm_static_stdcpp: bool,
- pub llvm_link_shared: bool,
+ /// `None` if `llvm_from_ci` is true and we haven't yet downloaded llvm.
+ #[cfg(not(test))]
+ llvm_link_shared: Cell<Option<bool>>,
+ #[cfg(test)]
+ pub llvm_link_shared: Cell<Option<bool>>,
pub llvm_clang_cl: Option<String>,
pub llvm_targets: Option<String>,
pub llvm_experimental_targets: Option<String>,
pub rust_debuginfo_level_std: u32,
pub rust_debuginfo_level_tools: u32,
pub rust_debuginfo_level_tests: u32,
- pub rust_run_dsymutil: bool,
+ pub rust_split_debuginfo: SplitDebuginfo,
pub rust_rpath: bool,
pub rustc_parallel: bool,
pub rustc_default_linker: Option<String>,
}
}
-#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum SplitDebuginfo {
+ Packed,
+ Unpacked,
+ Off,
+}
+
+impl Default for SplitDebuginfo {
+ fn default() -> Self {
+ SplitDebuginfo::Off
+ }
+}
+
+impl std::str::FromStr for SplitDebuginfo {
+ type Err = ();
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ match s {
+ "packed" => Ok(SplitDebuginfo::Packed),
+ "unpacked" => Ok(SplitDebuginfo::Unpacked),
+ "off" => Ok(SplitDebuginfo::Off),
+ _ => Err(()),
+ }
+ }
+}
+
+impl SplitDebuginfo {
+ /// Returns the default `-Csplit-debuginfo` value for the current target. See the comment for
+ /// `rust.split-debuginfo` in `config.toml.example`.
+ fn default_for_platform(target: &str) -> Self {
+ if target.contains("apple") {
+ SplitDebuginfo::Unpacked
+ } else if target.contains("windows") {
+ SplitDebuginfo::Packed
+ } else {
+ SplitDebuginfo::Off
+ }
+ }
+}
+
+#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct TargetSelection {
pub triple: Interned<String>,
file: Option<Interned<String>>,
}
}
+impl fmt::Debug for TargetSelection {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", self)
+ }
+}
+
impl PartialEq<&str> for TargetSelection {
fn eq(&self, other: &&str) -> bool {
self.triple == *other
/// Per-target configuration stored in the global configuration structure.
#[derive(Default)]
+#[cfg_attr(test, derive(Clone))]
pub struct Target {
/// Some(path to llvm-config) if using an external LLVM.
pub llvm_config: Option<PathBuf>,
debuginfo_level_std: Option<u32> = "debuginfo-level-std",
debuginfo_level_tools: Option<u32> = "debuginfo-level-tools",
debuginfo_level_tests: Option<u32> = "debuginfo-level-tests",
+ split_debuginfo: Option<String> = "split-debuginfo",
run_dsymutil: Option<bool> = "run-dsymutil",
backtrace: Option<bool> = "backtrace",
incremental: Option<bool> = "incremental",
set(&mut config.local_rebuild, build.local_rebuild);
set(&mut config.print_step_timings, build.print_step_timings);
set(&mut config.print_step_rusage, build.print_step_rusage);
+ set(&mut config.patch_binaries_for_nix, build.patch_binaries_for_nix);
config.verbose = cmp::max(config.verbose, flags.verbose);
set(&mut config.llvm_release_debuginfo, llvm.release_debuginfo);
set(&mut config.llvm_version_check, llvm.version_check);
set(&mut config.llvm_static_stdcpp, llvm.static_libstdcpp);
- set(&mut config.llvm_link_shared, llvm.link_shared);
+ if let Some(v) = llvm.link_shared {
+ config.llvm_link_shared.set(Some(v));
+ }
config.llvm_targets = llvm.targets.clone();
config.llvm_experimental_targets = llvm.experimental_targets.clone();
config.llvm_link_jobs = llvm.link_jobs;
check_ci_llvm!(llvm.optimize);
check_ci_llvm!(llvm.thin_lto);
check_ci_llvm!(llvm.release_debuginfo);
+ // CI-built LLVM can be either dynamic or static. We won't know until we download it.
check_ci_llvm!(llvm.link_shared);
check_ci_llvm!(llvm.static_libstdcpp);
check_ci_llvm!(llvm.targets);
check_ci_llvm!(llvm.clang);
check_ci_llvm!(llvm.build_config);
check_ci_llvm!(llvm.plugins);
-
- // CI-built LLVM can be either dynamic or static.
- let ci_llvm = config.out.join(&*config.build.triple).join("ci-llvm");
- config.llvm_link_shared = if config.dry_run {
- // just assume dynamic for now
- true
- } else {
- 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"
- };
}
+ // NOTE: can never be hit when downloading from CI, since we call `check_ci_llvm!(thin_lto)` above.
if config.llvm_thin_lto && llvm.link_shared.is_none() {
// 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.
- config.llvm_link_shared = true;
+ config.llvm_link_shared.set(Some(true));
}
}
debuginfo_level_std = rust.debuginfo_level_std;
debuginfo_level_tools = rust.debuginfo_level_tools;
debuginfo_level_tests = rust.debuginfo_level_tests;
- config.rust_run_dsymutil = rust.run_dsymutil.unwrap_or(false);
+ config.rust_split_debuginfo = rust
+ .split_debuginfo
+ .as_deref()
+ .map(SplitDebuginfo::from_str)
+ .map(|v| v.expect("invalid value for rust.split_debuginfo"))
+ .unwrap_or(SplitDebuginfo::default_for_platform(&config.build.triple));
optimize = rust.optimize;
ignore_git = rust.ignore_git;
config.rust_new_symbol_mangling = rust.new_symbol_mangling;
}
}
+ /// The absolute path to the downloaded LLVM artifacts.
+ pub(crate) fn ci_llvm_root(&self) -> PathBuf {
+ assert!(self.llvm_from_ci);
+ self.out.join(&*self.build.triple).join("ci-llvm")
+ }
+
+ /// 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(builder: &Builder<'_>) -> bool {
+ let mut opt = builder.config.llvm_link_shared.get();
+ if opt.is_none() && builder.config.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 builder.config.llvm_from_ci {
+ crate::native::maybe_download_ci_llvm(builder);
+ let ci_llvm = builder.config.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
+ }
+ });
+ builder.config.llvm_link_shared.set(opt);
+ llvm_link_shared
+ }
+
pub fn verbose(&self) -> bool {
self.verbose > 0
}
fn threads_from_config(v: u32) -> u32 {
match v {
- 0 => std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get) as u32,
+ 0 => num_cpus::get() as u32,
n => n,
}
}