summaryrefslogtreecommitdiffstats
path: root/src/bootstrap
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-19 09:25:56 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-19 09:25:56 +0000
commit018c4950b9406055dec02ef0fb52f132e2bb1e2c (patch)
treea835ebdf2088ef88fa681f8fad45f09922c1ae9a /src/bootstrap
parentAdding debian version 1.75.0+dfsg1-5. (diff)
downloadrustc-018c4950b9406055dec02ef0fb52f132e2bb1e2c.tar.xz
rustc-018c4950b9406055dec02ef0fb52f132e2bb1e2c.zip
Merging upstream version 1.76.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/bootstrap')
-rw-r--r--src/bootstrap/Cargo.lock11
-rw-r--r--src/bootstrap/Cargo.toml3
-rw-r--r--src/bootstrap/README.md2
-rw-r--r--src/bootstrap/bootstrap.py23
-rw-r--r--src/bootstrap/defaults/config.compiler.toml2
-rw-r--r--src/bootstrap/defaults/config.library.toml2
-rw-r--r--src/bootstrap/defaults/config.tools.toml2
-rw-r--r--src/bootstrap/src/bin/main.rs141
-rw-r--r--src/bootstrap/src/bin/rustc.rs60
-rw-r--r--src/bootstrap/src/bin/rustdoc.rs16
-rw-r--r--src/bootstrap/src/core/build_steps/check.rs10
-rw-r--r--src/bootstrap/src/core/build_steps/clean.rs12
-rw-r--r--src/bootstrap/src/core/build_steps/compile.rs39
-rw-r--r--src/bootstrap/src/core/build_steps/dist.rs4
-rw-r--r--src/bootstrap/src/core/build_steps/doc.rs110
-rw-r--r--src/bootstrap/src/core/build_steps/format.rs17
-rw-r--r--src/bootstrap/src/core/build_steps/llvm.rs41
-rw-r--r--src/bootstrap/src/core/build_steps/setup.rs21
-rw-r--r--src/bootstrap/src/core/build_steps/synthetic_targets.rs5
-rw-r--r--src/bootstrap/src/core/build_steps/test.rs75
-rw-r--r--src/bootstrap/src/core/build_steps/tool.rs17
-rw-r--r--src/bootstrap/src/core/builder.rs251
-rw-r--r--src/bootstrap/src/core/config/config.rs619
-rw-r--r--src/bootstrap/src/core/config/flags.rs10
-rw-r--r--src/bootstrap/src/core/download.rs45
-rw-r--r--src/bootstrap/src/core/sanity.rs2
-rw-r--r--src/bootstrap/src/lib.rs145
-rw-r--r--src/bootstrap/src/tests/builder.rs18
-rw-r--r--src/bootstrap/src/tests/config.rs27
-rw-r--r--src/bootstrap/src/tests/helpers.rs59
-rw-r--r--src/bootstrap/src/tests/setup.rs3
-rw-r--r--src/bootstrap/src/utils/bin_helpers.rs31
-rw-r--r--src/bootstrap/src/utils/cc_detect.rs4
-rw-r--r--src/bootstrap/src/utils/change_tracker.rs99
-rw-r--r--src/bootstrap/src/utils/dylib.rs13
-rw-r--r--src/bootstrap/src/utils/helpers.rs122
-rw-r--r--src/bootstrap/src/utils/mod.rs1
37 files changed, 1442 insertions, 620 deletions
diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock
index 57113b0ec..f8e6d629b 100644
--- a/src/bootstrap/Cargo.lock
+++ b/src/bootstrap/Cargo.lock
@@ -55,7 +55,6 @@ dependencies = [
"cmake",
"fd-lock",
"filetime",
- "hex",
"home",
"ignore",
"junction",
@@ -314,12 +313,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
-name = "hex"
-version = "0.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
-
-[[package]]
name = "home"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -370,9 +363,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
-version = "0.2.149"
+version = "0.2.150"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
+checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
[[package]]
name = "linux-raw-sys"
diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml
index e4d359141..077d1954b 100644
--- a/src/bootstrap/Cargo.toml
+++ b/src/bootstrap/Cargo.toml
@@ -39,10 +39,9 @@ clap = { version = "4.4.7", default-features = false, features = ["std", "usage"
clap_complete = "4.4.3"
cmake = "0.1.38"
filetime = "0.2"
-hex = "0.4"
home = "0.5.4"
ignore = "0.4.10"
-libc = "0.2"
+libc = "0.2.150"
object = { version = "0.32.0", default-features = false, features = ["archive", "coff", "read_core", "unaligned"] }
once_cell = "1.7.2"
opener = "0.5"
diff --git a/src/bootstrap/README.md b/src/bootstrap/README.md
index e7998a40a..d0a069f45 100644
--- a/src/bootstrap/README.md
+++ b/src/bootstrap/README.md
@@ -183,7 +183,7 @@ Some general areas that you may be interested in modifying are:
If you make a major change on bootstrap configuration, please remember to:
-+ Update `CONFIG_CHANGE_HISTORY` in `src/bootstrap/lib.rs`.
++ Update `CONFIG_CHANGE_HISTORY` in `src/bootstrap/src/utils/change_tracker.rs`.
* Update `change-id = {pull-request-id}` in `config.example.toml`.
A 'major change' includes
diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py
index 5a84e37f8..fea194a80 100644
--- a/src/bootstrap/bootstrap.py
+++ b/src/bootstrap/bootstrap.py
@@ -616,22 +616,6 @@ class RustBuild(object):
with output(self.rustc_stamp()) as rust_stamp:
rust_stamp.write(key)
- def _download_component_helper(
- self, filename, pattern, tarball_suffix, rustc_cache,
- ):
- key = self.stage0_compiler.date
-
- tarball = os.path.join(rustc_cache, filename)
- if not os.path.exists(tarball):
- get(
- self.download_url,
- "dist/{}/{}".format(key, filename),
- tarball,
- self.checksums_sha256,
- verbose=self.verbose,
- )
- unpack(tarball, tarball_suffix, self.bin_root(), match=pattern, verbose=self.verbose)
-
def should_fix_bins_and_dylibs(self):
"""Whether or not `fix_bin_or_dylib` needs to be run; can only be True
on NixOS or if config.toml has `build.patch-binaries-for-nix` set.
@@ -946,6 +930,8 @@ class RustBuild(object):
target_linker = self.get_toml("linker", build_section)
if target_linker is not None:
env["RUSTFLAGS"] += " -C linker=" + target_linker
+ # When changing this list, also update the corresponding list in `Builder::cargo`
+ # in `src/bootstrap/src/core/builder.rs`.
env["RUSTFLAGS"] += " -Wrust_2018_idioms -Wunused_lifetimes"
if self.warnings == "default":
deny_warnings = self.get_toml("deny-warnings", "rust") != "false"
@@ -1083,6 +1069,11 @@ def bootstrap(args):
include_file = 'config.{}.toml'.format(profile_aliases.get(profile) or profile)
include_dir = os.path.join(rust_root, 'src', 'bootstrap', 'defaults')
include_path = os.path.join(include_dir, include_file)
+
+ if not os.path.exists(include_path):
+ raise Exception("Unrecognized config profile '{}'. Check src/bootstrap/defaults"
+ " for available options.".format(profile))
+
# HACK: This works because `self.get_toml()` returns the first match it finds for a
# specific key, so appending our defaults at the end allows the user to override them
with open(include_path) as included_toml:
diff --git a/src/bootstrap/defaults/config.compiler.toml b/src/bootstrap/defaults/config.compiler.toml
index b98b13119..b27b524b8 100644
--- a/src/bootstrap/defaults/config.compiler.toml
+++ b/src/bootstrap/defaults/config.compiler.toml
@@ -17,4 +17,4 @@ lto = "off"
[llvm]
# Will download LLVM from CI if available on your platform.
-download-ci-llvm = "if-available"
+download-ci-llvm = "if-unchanged"
diff --git a/src/bootstrap/defaults/config.library.toml b/src/bootstrap/defaults/config.library.toml
index f362c4111..087544397 100644
--- a/src/bootstrap/defaults/config.library.toml
+++ b/src/bootstrap/defaults/config.library.toml
@@ -13,4 +13,4 @@ lto = "off"
[llvm]
# Will download LLVM from CI if available on your platform.
-download-ci-llvm = "if-available"
+download-ci-llvm = "if-unchanged"
diff --git a/src/bootstrap/defaults/config.tools.toml b/src/bootstrap/defaults/config.tools.toml
index 79424f28d..6e6c36600 100644
--- a/src/bootstrap/defaults/config.tools.toml
+++ b/src/bootstrap/defaults/config.tools.toml
@@ -21,4 +21,4 @@ compiler-docs = true
[llvm]
# Will download LLVM from CI if available on your platform.
-download-ci-llvm = "if-available"
+download-ci-llvm = "if-unchanged"
diff --git a/src/bootstrap/src/bin/main.rs b/src/bootstrap/src/bin/main.rs
index 0a6072ae1..b1ab8dae5 100644
--- a/src/bootstrap/src/bin/main.rs
+++ b/src/bootstrap/src/bin/main.rs
@@ -5,15 +5,18 @@
//! parent directory, and otherwise documentation can be found throughout the `build`
//! directory in each respective module.
-#[cfg(all(any(unix, windows), not(target_os = "solaris")))]
use std::io::Write;
#[cfg(all(any(unix, windows), not(target_os = "solaris")))]
use std::process;
-use std::{env, fs};
+use std::{
+ env,
+ fs::{self, OpenOptions},
+ io::{self, BufRead, BufReader, IsTerminal},
+};
-#[cfg(all(any(unix, windows), not(target_os = "solaris")))]
-use bootstrap::t;
-use bootstrap::{find_recent_config_change_ids, Build, Config, Subcommand, CONFIG_CHANGE_HISTORY};
+use bootstrap::{
+ find_recent_config_change_ids, t, Build, Config, Subcommand, CONFIG_CHANGE_HISTORY,
+};
fn main() {
let args = env::args().skip(1).collect::<Vec<_>>();
@@ -23,35 +26,40 @@ fn main() {
let mut build_lock;
#[cfg(all(any(unix, windows), not(target_os = "solaris")))]
let _build_lock_guard;
- #[cfg(all(any(unix, windows), not(target_os = "solaris")))]
- // Display PID of process holding the lock
- // PID will be stored in a lock file
- {
- let path = config.out.join("lock");
- let pid = match fs::read_to_string(&path) {
- Ok(contents) => contents,
- Err(_) => String::new(),
- };
-
- build_lock =
- fd_lock::RwLock::new(t!(fs::OpenOptions::new().write(true).create(true).open(&path)));
- _build_lock_guard = match build_lock.try_write() {
- Ok(mut lock) => {
- t!(lock.write(&process::id().to_string().as_ref()));
- lock
- }
- err => {
- drop(err);
- println!("WARNING: build directory locked by process {pid}, waiting for lock");
- let mut lock = t!(build_lock.write());
- t!(lock.write(&process::id().to_string().as_ref()));
- lock
- }
- };
- }
- #[cfg(any(not(any(unix, windows)), target_os = "solaris"))]
- println!("WARNING: file locking not supported for target, not locking build directory");
+ if !config.bypass_bootstrap_lock {
+ // Display PID of process holding the lock
+ // PID will be stored in a lock file
+ #[cfg(all(any(unix, windows), not(target_os = "solaris")))]
+ {
+ let path = config.out.join("lock");
+ let pid = match fs::read_to_string(&path) {
+ Ok(contents) => contents,
+ Err(_) => String::new(),
+ };
+
+ build_lock = fd_lock::RwLock::new(t!(fs::OpenOptions::new()
+ .write(true)
+ .create(true)
+ .open(&path)));
+ _build_lock_guard = match build_lock.try_write() {
+ Ok(mut lock) => {
+ t!(lock.write(&process::id().to_string().as_ref()));
+ lock
+ }
+ err => {
+ drop(err);
+ println!("WARNING: build directory locked by process {pid}, waiting for lock");
+ let mut lock = t!(build_lock.write());
+ t!(lock.write(&process::id().to_string().as_ref()));
+ lock
+ }
+ };
+ }
+
+ #[cfg(any(not(any(unix, windows)), target_os = "solaris"))]
+ println!("WARNING: file locking not supported for target, not locking build directory");
+ }
// check_version warnings are not printed during setup
let changelog_suggestion =
@@ -71,6 +79,9 @@ fn main() {
}
let pre_commit = config.src.join(".git").join("hooks").join("pre-commit");
+ let dump_bootstrap_shims = config.dump_bootstrap_shims;
+ let out_dir = config.out.clone();
+
Build::new(config).build();
if suggest_setup {
@@ -99,6 +110,29 @@ fn main() {
if suggest_setup || changelog_suggestion.is_some() {
println!("NOTE: this message was printed twice to make it more likely to be seen");
}
+
+ if dump_bootstrap_shims {
+ let dump_dir = out_dir.join("bootstrap-shims-dump");
+ assert!(dump_dir.exists());
+
+ for entry in walkdir::WalkDir::new(&dump_dir) {
+ let entry = t!(entry);
+
+ if !entry.file_type().is_file() {
+ continue;
+ }
+
+ let file = t!(fs::File::open(&entry.path()));
+
+ // To ensure deterministic results we must sort the dump lines.
+ // This is necessary because the order of rustc invocations different
+ // almost all the time.
+ let mut lines: Vec<String> = t!(BufReader::new(&file).lines().collect());
+ lines.sort_by_key(|t| t.to_lowercase());
+ let mut file = t!(OpenOptions::new().write(true).truncate(true).open(&entry.path()));
+ t!(file.write_all(lines.join("\n").as_bytes()));
+ }
+ }
}
fn check_version(config: &Config) -> Option<String> {
@@ -108,35 +142,46 @@ fn check_version(config: &Config) -> Option<String> {
msg.push_str("WARNING: The use of `changelog-seen` is deprecated. Please refer to `change-id` option in `config.example.toml` instead.\n");
}
- let latest_config_id = CONFIG_CHANGE_HISTORY.last().unwrap();
+ let latest_change_id = CONFIG_CHANGE_HISTORY.last().unwrap().change_id;
+ let warned_id_path = config.out.join("bootstrap").join(".last-warned-change-id");
+
if let Some(id) = config.change_id {
- if &id == latest_config_id {
+ if id == latest_change_id {
return None;
}
- let change_links: Vec<String> = find_recent_config_change_ids(id)
- .iter()
- .map(|id| format!("https://github.com/rust-lang/rust/pull/{id}"))
- .collect();
- if !change_links.is_empty() {
- msg.push_str("WARNING: there have been changes to x.py since you last updated.\n");
- msg.push_str("To see more detail about these changes, visit the following PRs:\n");
-
- for link in change_links {
- msg.push_str(&format!(" - {link}\n"));
+ if let Ok(last_warned_id) = fs::read_to_string(&warned_id_path) {
+ if latest_change_id.to_string() == last_warned_id {
+ return None;
}
+ }
+
+ let changes = find_recent_config_change_ids(id);
+
+ if !changes.is_empty() {
+ msg.push_str("There have been changes to x.py since you last updated:\n");
- msg.push_str("WARNING: there have been changes to x.py since you last updated.\n");
+ for change in changes {
+ msg.push_str(&format!(" [{}] {}\n", change.severity.to_string(), change.summary));
+ msg.push_str(&format!(
+ " - PR Link https://github.com/rust-lang/rust/pull/{}\n",
+ change.change_id
+ ));
+ }
msg.push_str("NOTE: to silence this warning, ");
msg.push_str(&format!(
- "update `config.toml` to use `change-id = {latest_config_id}` instead"
+ "update `config.toml` to use `change-id = {latest_change_id}` instead"
));
+
+ if io::stdout().is_terminal() && !config.dry_run() {
+ t!(fs::write(warned_id_path, latest_change_id.to_string()));
+ }
}
} else {
msg.push_str("WARNING: The `change-id` is missing in the `config.toml`. This means that you will not be able to track the major changes made to the bootstrap configurations.\n");
msg.push_str("NOTE: to silence this warning, ");
- msg.push_str(&format!("add `change-id = {latest_config_id}` at the top of `config.toml`"));
+ msg.push_str(&format!("add `change-id = {latest_change_id}` at the top of `config.toml`"));
};
Some(msg)
diff --git a/src/bootstrap/src/bin/rustc.rs b/src/bootstrap/src/bin/rustc.rs
index 070a2da6a..38c55b203 100644
--- a/src/bootstrap/src/bin/rustc.rs
+++ b/src/bootstrap/src/bin/rustc.rs
@@ -16,11 +16,11 @@
//! never get replaced.
use std::env;
-use std::path::PathBuf;
+use std::path::{Path, PathBuf};
use std::process::{Child, Command};
use std::time::Instant;
-use dylib_util::{dylib_path, dylib_path_var};
+use dylib_util::{dylib_path, dylib_path_var, exe};
#[path = "../utils/bin_helpers.rs"]
mod bin_helpers;
@@ -29,12 +29,12 @@ mod bin_helpers;
mod dylib_util;
fn main() {
- let args = env::args_os().skip(1).collect::<Vec<_>>();
- let arg = |name| args.windows(2).find(|args| args[0] == name).and_then(|args| args[1].to_str());
+ let orig_args = env::args_os().skip(1).collect::<Vec<_>>();
+ let mut args = orig_args.clone();
+ let arg =
+ |name| orig_args.windows(2).find(|args| args[0] == name).and_then(|args| args[1].to_str());
- // We don't use the stage in this shim, but let's parse it to make sure that we're invoked
- // by bootstrap, or that we provide a helpful error message if not.
- bin_helpers::parse_rustc_stage();
+ let stage = bin_helpers::parse_rustc_stage();
let verbose = bin_helpers::parse_rustc_verbose();
// Detect whether or not we're a build script depending on whether --target
@@ -47,7 +47,8 @@ fn main() {
// determine the version of the compiler, the real compiler needs to be
// used. Currently, these two states are differentiated based on whether
// --target and -vV is/isn't passed.
- let (rustc, libdir) = if target.is_none() && version.is_none() {
+ let is_build_script = target.is_none() && version.is_none();
+ let (rustc, libdir) = if is_build_script {
("RUSTC_SNAPSHOT", "RUSTC_SNAPSHOT_LIBDIR")
} else {
("RUSTC_REAL", "RUSTC_LIBDIR")
@@ -56,12 +57,47 @@ fn main() {
let sysroot = env::var_os("RUSTC_SYSROOT").expect("RUSTC_SYSROOT was not set");
let on_fail = env::var_os("RUSTC_ON_FAIL").map(Command::new);
- let rustc = env::var_os(rustc).unwrap_or_else(|| panic!("{:?} was not set", rustc));
+ let rustc_real = env::var_os(rustc).unwrap_or_else(|| panic!("{:?} was not set", rustc));
let libdir = env::var_os(libdir).unwrap_or_else(|| panic!("{:?} was not set", libdir));
let mut dylib_path = dylib_path();
dylib_path.insert(0, PathBuf::from(&libdir));
- let mut cmd = Command::new(rustc);
+ // if we're running clippy, trust cargo-clippy to set clippy-driver appropriately (and don't override it with rustc).
+ // otherwise, substitute whatever cargo thinks rustc should be with RUSTC_REAL.
+ // NOTE: this means we ignore RUSTC in the environment.
+ // FIXME: We might want to consider removing RUSTC_REAL and setting RUSTC directly?
+ // NOTE: we intentionally pass the name of the host, not the target.
+ let host = env::var("CFG_COMPILER_BUILD_TRIPLE").unwrap();
+ let is_clippy = args[0].to_string_lossy().ends_with(&exe("clippy-driver", &host));
+ let rustc_driver = if is_clippy {
+ if is_build_script {
+ // Don't run clippy on build scripts (for one thing, we may not have libstd built with
+ // the appropriate version yet, e.g. for stage 1 std).
+ // Also remove the `clippy-driver` param in addition to the RUSTC param.
+ args.drain(..2);
+ rustc_real
+ } else {
+ args.remove(0)
+ }
+ } else {
+ // Cargo doesn't respect RUSTC_WRAPPER for version information >:(
+ // don't remove the first arg if we're being run as RUSTC instead of RUSTC_WRAPPER.
+ // Cargo also sometimes doesn't pass the `.exe` suffix on Windows - add it manually.
+ let current_exe = env::current_exe().expect("couldn't get path to rustc shim");
+ let arg0 = exe(args[0].to_str().expect("only utf8 paths are supported"), &host);
+ if Path::new(&arg0) == current_exe {
+ args.remove(0);
+ }
+ rustc_real
+ };
+
+ let mut cmd = if let Some(wrapper) = env::var_os("RUSTC_WRAPPER_REAL") {
+ let mut cmd = Command::new(wrapper);
+ cmd.arg(rustc_driver);
+ cmd
+ } else {
+ Command::new(rustc_driver)
+ };
cmd.args(&args).env(dylib_path_var(), env::join_paths(&dylib_path).unwrap());
// Get the name of the crate we're compiling, if any.
@@ -114,7 +150,7 @@ fn main() {
{
cmd.arg("-Ztls-model=initial-exec");
}
- } else {
+ } else if std::env::var("MIRI").is_err() {
// Find any host flags that were passed by bootstrap.
// The flags are stored in a RUSTC_HOST_FLAGS variable, separated by spaces.
if let Ok(flags) = std::env::var("RUSTC_HOST_FLAGS") {
@@ -214,6 +250,8 @@ fn main() {
}
}
+ bin_helpers::maybe_dump(format!("stage{stage}-rustc"), &cmd);
+
let start = Instant::now();
let (child, status) = {
let errmsg = format!("\nFailed to run:\n{cmd:?}\n-------------");
diff --git a/src/bootstrap/src/bin/rustdoc.rs b/src/bootstrap/src/bin/rustdoc.rs
index dbbce6fe2..b4d141518 100644
--- a/src/bootstrap/src/bin/rustdoc.rs
+++ b/src/bootstrap/src/bin/rustdoc.rs
@@ -3,7 +3,6 @@
//! See comments in `src/bootstrap/rustc.rs` for more information.
use std::env;
-use std::ffi::OsString;
use std::path::PathBuf;
use std::process::Command;
@@ -52,15 +51,6 @@ fn main() {
if env::var_os("RUSTC_FORCE_UNSTABLE").is_some() {
cmd.arg("-Z").arg("force-unstable-if-unmarked");
}
- if let Some(linker) = env::var_os("RUSTDOC_LINKER") {
- let mut arg = OsString::from("-Clinker=");
- arg.push(&linker);
- cmd.arg(arg);
- }
- if let Ok(no_threads) = env::var("RUSTDOC_LLD_NO_THREADS") {
- cmd.arg("-Clink-arg=-fuse-ld=lld");
- cmd.arg(format!("-Clink-arg=-Wl,{no_threads}"));
- }
// Cargo doesn't pass RUSTDOCFLAGS to proc_macros:
// https://github.com/rust-lang/cargo/issues/4423
// Thus, if we are on stage 0, we explicitly set `--cfg=bootstrap`.
@@ -70,9 +60,9 @@ fn main() {
cmd.arg("--cfg=bootstrap");
}
cmd.arg("-Zunstable-options");
- // #[cfg(bootstrap)]
- cmd.arg("--check-cfg=values(bootstrap)");
- // cmd.arg("--check-cfg=cfg(bootstrap)");
+ cmd.arg("--check-cfg=cfg(bootstrap)");
+
+ bin_helpers::maybe_dump(format!("stage{stage}-rustdoc"), &cmd);
if verbose > 1 {
eprintln!(
diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs
index 121925b56..ecaaf91ae 100644
--- a/src/bootstrap/src/core/build_steps/check.rs
+++ b/src/bootstrap/src/core/build_steps/check.rs
@@ -376,12 +376,12 @@ impl Step for RustAnalyzer {
let compiler = builder.compiler(builder.top_stage, builder.config.build);
let target = self.target;
- builder.ensure(Std::new(target));
+ builder.ensure(Rustc::new(target, builder));
let mut cargo = prepare_tool_cargo(
builder,
compiler,
- Mode::ToolStd,
+ Mode::ToolRustc,
target,
cargo_subcommand(builder.kind),
"src/tools/rust-analyzer",
@@ -414,7 +414,7 @@ impl Step for RustAnalyzer {
/// Cargo's output path in a given stage, compiled by a particular
/// compiler for the specified target.
fn stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf {
- builder.cargo_out(compiler, Mode::ToolStd, target).join(".rust-analyzer-check.stamp")
+ builder.cargo_out(compiler, Mode::ToolRustc, target).join(".rust-analyzer-check.stamp")
}
}
}
@@ -463,10 +463,6 @@ macro_rules! tool_check_step {
cargo.arg("--all-targets");
}
- // Enable internal lints for clippy and rustdoc
- // NOTE: this doesn't enable lints for any other tools unless they explicitly add `#![warn(rustc::internal)]`
- // See https://github.com/rust-lang/rust/pull/80573#issuecomment-754010776
- cargo.rustflag("-Zunstable-options");
let _guard = builder.msg_check(&concat!(stringify!($name), " artifacts").to_lowercase(), target);
run_cargo(
builder,
diff --git a/src/bootstrap/src/core/build_steps/clean.rs b/src/bootstrap/src/core/build_steps/clean.rs
index cbb6b5f46..4b993945f 100644
--- a/src/bootstrap/src/core/build_steps/clean.rs
+++ b/src/bootstrap/src/core/build_steps/clean.rs
@@ -145,10 +145,18 @@ fn clean_specific_stage(build: &Build, stage: u32) {
fn clean_default(build: &Build) {
rm_rf(&build.out.join("tmp"));
rm_rf(&build.out.join("dist"));
+ rm_rf(&build.out.join("bootstrap").join(".last-warned-change-id"));
+ rm_rf(&build.out.join("bootstrap-shims-dump"));
rm_rf(&build.out.join("rustfmt.stamp"));
- for host in &build.hosts {
- let entries = match build.out.join(host.triple).read_dir() {
+ let mut hosts: Vec<_> = build.hosts.iter().map(|t| build.out.join(t.triple)).collect();
+ // After cross-compilation, artifacts of the host architecture (which may differ from build.host)
+ // might not get removed.
+ // Adding its path (linked one for easier accessibility) will solve this problem.
+ hosts.push(build.out.join("host"));
+
+ for host in hosts {
+ let entries = match host.read_dir() {
Ok(iter) => iter,
Err(_) => continue,
};
diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs
index 7021a9543..df4d1a43d 100644
--- a/src/bootstrap/src/core/build_steps/compile.rs
+++ b/src/bootstrap/src/core/build_steps/compile.rs
@@ -592,7 +592,9 @@ impl Step for StdLink {
.join("stage0/lib/rustlib")
.join(&host)
.join("codegen-backends");
- builder.cp_r(&stage0_codegen_backends, &sysroot_codegen_backends);
+ if stage0_codegen_backends.exists() {
+ builder.cp_r(&stage0_codegen_backends, &sysroot_codegen_backends);
+ }
}
}
}
@@ -868,7 +870,7 @@ impl Step for Rustc {
// is already on by default in MSVC optimized builds, which is interpreted as --icf=all:
// https://github.com/llvm/llvm-project/blob/3329cec2f79185bafd678f310fafadba2a8c76d2/lld/COFF/Driver.cpp#L1746
// https://github.com/rust-lang/rust/blob/f22819bcce4abaff7d1246a56eec493418f9f4ee/compiler/rustc_codegen_ssa/src/back/linker.rs#L827
- if builder.config.use_lld && !compiler.host.contains("msvc") {
+ if builder.config.lld_mode.is_used() && !compiler.host.is_msvc() {
cargo.rustflag("-Clink-args=-Wl,--icf=all");
}
@@ -885,7 +887,9 @@ impl Step for Rustc {
} else if let Some(path) = &builder.config.rust_profile_use {
if compiler.stage == 1 {
cargo.rustflag(&format!("-Cprofile-use={path}"));
- cargo.rustflag("-Cllvm-args=-pgo-warn-missing-function");
+ if builder.is_verbose() {
+ cargo.rustflag("-Cllvm-args=-pgo-warn-missing-function");
+ }
true
} else {
false
@@ -1001,6 +1005,13 @@ pub fn rustc_cargo_env(
.env("CFG_RELEASE_CHANNEL", &builder.config.channel)
.env("CFG_VERSION", builder.rust_version());
+ // Some tools like Cargo detect their own git information in build scripts. When omit-git-hash
+ // is enabled in config.toml, we pass this environment variable to tell build scripts to avoid
+ // detecting git information on their own.
+ if builder.config.omit_git_hash {
+ cargo.env("CFG_OMIT_GIT_HASH", "1");
+ }
+
if let Some(backend) = builder.config.default_codegen_backend() {
cargo.env("CFG_DEFAULT_CODEGEN_BACKEND", backend);
}
@@ -1078,7 +1089,7 @@ fn rustc_llvm_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetSelect
// found. This is to avoid the linker errors about undefined references to
// `__llvm_profile_instrument_memop` when linking `rustc_driver`.
let mut llvm_linker_flags = String::new();
- if builder.config.llvm_profile_generate && target.contains("msvc") {
+ if builder.config.llvm_profile_generate && target.is_msvc() {
if let Some(ref clang_cl_path) = builder.config.llvm_clang_cl {
// Add clang's runtime library directory to the search path
let clang_rt_dir = get_clang_cl_resource_dir(clang_cl_path);
@@ -1103,7 +1114,7 @@ fn rustc_llvm_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetSelect
// not for MSVC or macOS
if builder.config.llvm_static_stdcpp
&& !target.contains("freebsd")
- && !target.contains("msvc")
+ && !target.is_msvc()
&& !target.contains("apple")
&& !target.contains("solaris")
{
@@ -1711,7 +1722,7 @@ impl Step for Assemble {
let dst_exe = exe("rust-lld", target_compiler.host);
builder.copy(&lld_install.join("bin").join(&src_exe), &libdir_bin.join(&dst_exe));
let self_contained_lld_dir = libdir_bin.join("gcc-ld");
- t!(fs::create_dir(&self_contained_lld_dir));
+ t!(fs::create_dir_all(&self_contained_lld_dir));
let lld_wrapper_exe = builder.ensure(crate::core::build_steps::tool::LldWrapper {
compiler: build_compiler,
target: target_compiler.host,
@@ -1798,10 +1809,6 @@ pub fn run_cargo(
is_check: bool,
rlib_only_metadata: bool,
) -> Vec<PathBuf> {
- if builder.config.dry_run() {
- return Vec::new();
- }
-
// `target_root_dir` looks like $dir/$target/release
let target_root_dir = stamp.parent().unwrap();
// `target_deps_dir` looks like $dir/$target/release/deps
@@ -1908,6 +1915,10 @@ pub fn run_cargo(
crate::exit!(1);
}
+ if builder.config.dry_run() {
+ return Vec::new();
+ }
+
// Ok now we need to actually find all the files listed in `toplevel`. We've
// got a list of prefix/extensions and we basically just need to find the
// most recent file in the `deps` folder corresponding to each one.
@@ -1963,9 +1974,6 @@ pub fn stream_cargo(
cb: &mut dyn FnMut(CargoMessage<'_>),
) -> bool {
let mut cargo = Command::from(cargo);
- if builder.config.dry_run() {
- return true;
- }
// Instruct Cargo to give us json messages on stdout, critically leaving
// stderr as piped so we can get those pretty colors.
let mut message_format = if builder.config.json_output {
@@ -1984,6 +1992,11 @@ pub fn stream_cargo(
}
builder.verbose(&format!("running: {cargo:?}"));
+
+ if builder.config.dry_run() {
+ return true;
+ }
+
let mut child = match cargo.spawn() {
Ok(child) => child,
Err(e) => panic!("failed to execute command: {cargo:?}\nERROR: {e}"),
diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs
index c485481b9..98e267713 100644
--- a/src/bootstrap/src/core/build_steps/dist.rs
+++ b/src/bootstrap/src/core/build_steps/dist.rs
@@ -1319,7 +1319,7 @@ impl Step for CodegenBackend {
return None;
}
- if self.compiler.host.contains("windows") {
+ if self.compiler.host.is_windows() {
builder.info(
"dist currently disabled for windows by rustc_codegen_cranelift. skipping",
);
@@ -1658,7 +1658,7 @@ impl Step for Extended {
builder.run(&mut cmd);
}
- if target.contains("windows") {
+ if target.is_windows() {
let exe = tmp.join("exe");
let _ = fs::remove_dir_all(&exe);
diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs
index e3fd942fc..cf3f5bc98 100644
--- a/src/bootstrap/src/core/build_steps/doc.rs
+++ b/src/bootstrap/src/core/build_steps/doc.rs
@@ -7,8 +7,9 @@
//! Everything here is basically just a shim around calling either `rustbook` or
//! `rustdoc`.
-use std::fs;
+use std::io::{self, Write};
use std::path::{Path, PathBuf};
+use std::{fs, mem};
use crate::core::build_steps::compile;
use crate::core::build_steps::tool::{self, prepare_tool_cargo, SourceType, Tool};
@@ -388,6 +389,104 @@ impl Step for Standalone {
}
}
+#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
+pub struct Releases {
+ compiler: Compiler,
+ target: TargetSelection,
+}
+
+impl Step for Releases {
+ type Output = ();
+ const DEFAULT: bool = true;
+
+ fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+ let builder = run.builder;
+ run.path("RELEASES.md").alias("releases").default_condition(builder.config.docs)
+ }
+
+ fn make_run(run: RunConfig<'_>) {
+ run.builder.ensure(Releases {
+ compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
+ target: run.target,
+ });
+ }
+
+ /// Generates HTML release notes to include in the final docs bundle.
+ ///
+ /// This uses the same stylesheet and other tools as Standalone, but the
+ /// RELEASES.md file is included at the root of the repository and gets
+ /// the headline added. In the end, the conversion is done by Rustdoc.
+ fn run(self, builder: &Builder<'_>) {
+ let target = self.target;
+ let compiler = self.compiler;
+ let _guard = builder.msg_doc(compiler, "releases", target);
+ let out = builder.doc_out(target);
+ t!(fs::create_dir_all(&out));
+
+ builder.ensure(Standalone {
+ compiler: builder.compiler(builder.top_stage, builder.config.build),
+ target,
+ });
+
+ let version_info = builder.ensure(SharedAssets { target: self.target }).version_info;
+
+ let favicon = builder.src.join("src/doc/favicon.inc");
+ let footer = builder.src.join("src/doc/footer.inc");
+ let full_toc = builder.src.join("src/doc/full-toc.inc");
+
+ let html = out.join("releases.html");
+ let tmppath = out.join("releases.md");
+ let inpath = builder.src.join("RELEASES.md");
+ let rustdoc = builder.rustdoc(compiler);
+ if !up_to_date(&inpath, &html)
+ || !up_to_date(&footer, &html)
+ || !up_to_date(&favicon, &html)
+ || !up_to_date(&full_toc, &html)
+ || !(builder.config.dry_run()
+ || up_to_date(&version_info, &html)
+ || up_to_date(&rustdoc, &html))
+ {
+ let mut tmpfile = t!(fs::File::create(&tmppath));
+ t!(tmpfile.write_all(b"% Rust Release Notes\n\n"));
+ t!(io::copy(&mut t!(fs::File::open(&inpath)), &mut tmpfile));
+ mem::drop(tmpfile);
+ let mut cmd = builder.rustdoc_cmd(compiler);
+
+ // Needed for --index-page flag
+ cmd.arg("-Z").arg("unstable-options");
+
+ cmd.arg("--html-after-content")
+ .arg(&footer)
+ .arg("--html-before-content")
+ .arg(&version_info)
+ .arg("--html-in-header")
+ .arg(&favicon)
+ .arg("--markdown-no-toc")
+ .arg("--markdown-css")
+ .arg("rust.css")
+ .arg("--index-page")
+ .arg(&builder.src.join("src/doc/index.md"))
+ .arg("--markdown-playground-url")
+ .arg("https://play.rust-lang.org/")
+ .arg("-o")
+ .arg(&out)
+ .arg(&tmppath);
+
+ if !builder.config.docs_minification {
+ cmd.arg("--disable-minification");
+ }
+
+ builder.run(&mut cmd);
+ }
+
+ // We open doc/RELEASES.html as the default if invoked as `x.py doc --open RELEASES.md`
+ // with no particular explicit doc requested (e.g. library/core).
+ if builder.was_invoked_explicitly::<Self>(Kind::Doc) {
+ builder.open_in_browser(&html);
+ }
+ }
+}
+
#[derive(Debug, Clone)]
pub struct SharedAssetsPaths {
pub version_info: PathBuf,
@@ -897,15 +996,14 @@ tool_doc!(
in_tree = false,
crates = [
"cargo",
+ "cargo-credential",
"cargo-platform",
- "cargo-util",
- "crates-io",
"cargo-test-macro",
"cargo-test-support",
- "cargo-credential",
+ "cargo-util",
+ "crates-io",
"mdman",
- // FIXME: this trips a license check in tidy.
- // "resolver-tests",
+ "rustfix",
]
);
tool_doc!(Tidy, "tidy", "src/tools/tidy", rustc_tool = false, crates = ["tidy"]);
diff --git a/src/bootstrap/src/core/build_steps/format.rs b/src/bootstrap/src/core/build_steps/format.rs
index 86f1d925f..e792d38b7 100644
--- a/src/bootstrap/src/core/build_steps/format.rs
+++ b/src/bootstrap/src/core/build_steps/format.rs
@@ -142,14 +142,17 @@ pub fn format(build: &Builder<'_>, check: bool, paths: &[PathBuf]) {
};
if in_working_tree {
let untracked_paths_output = output(
- build.config.git().arg("status").arg("--porcelain").arg("--untracked-files=normal"),
+ build
+ .config
+ .git()
+ .arg("status")
+ .arg("--porcelain")
+ .arg("-z")
+ .arg("--untracked-files=normal"),
+ );
+ let untracked_paths = untracked_paths_output.split_terminator('\0').filter_map(
+ |entry| entry.strip_prefix("?? "), // returns None if the prefix doesn't match
);
- let untracked_paths = untracked_paths_output
- .lines()
- .filter(|entry| entry.starts_with("??"))
- .map(|entry| {
- entry.split(' ').nth(1).expect("every git status entry should list a path")
- });
let mut untracked_count = 0;
for untracked_path in untracked_paths {
println!("skip untracked path {untracked_path} during rustfmt invocations");
diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs
index a1f6fac8a..4b2d3e9ab 100644
--- a/src/bootstrap/src/core/build_steps/llvm.rs
+++ b/src/bootstrap/src/core/build_steps/llvm.rs
@@ -15,12 +15,13 @@ use std::fs::{self, File};
use std::io;
use std::path::{Path, PathBuf};
use std::process::Command;
+use std::sync::OnceLock;
use crate::core::builder::{Builder, RunConfig, ShouldRun, Step};
use crate::core::config::{Config, TargetSelection};
use crate::utils::channel;
use crate::utils::helpers::{self, exe, get_clang_cl_resource_dir, output, t, up_to_date};
-use crate::{CLang, GitRepo, Kind};
+use crate::{generate_smart_stamp_hash, CLang, GitRepo, Kind};
use build_helper::ci::CiEnv;
use build_helper::git::get_git_merge_base;
@@ -97,7 +98,7 @@ pub fn prebuilt_llvm_config(
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.ninja() {
+ if !builder.config.build.is_msvc() || builder.ninja() {
llvm_config_ret_dir.push("build");
}
llvm_config_ret_dir.push("bin");
@@ -105,8 +106,16 @@ pub fn prebuilt_llvm_config(
let llvm_cmake_dir = out_dir.join("lib/cmake/llvm");
let res = LlvmResult { llvm_config: build_llvm_config, llvm_cmake_dir };
+ static STAMP_HASH_MEMO: OnceLock<String> = OnceLock::new();
+ let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| {
+ generate_smart_stamp_hash(
+ &builder.config.src.join("src/llvm-project"),
+ &builder.in_tree_llvm_info.sha().unwrap_or_default(),
+ )
+ });
+
let stamp = out_dir.join("llvm-finished-building");
- let stamp = HashStamp::new(stamp, builder.in_tree_llvm_info.sha());
+ let stamp = HashStamp::new(stamp, Some(smart_stamp_hash));
if stamp.is_done() {
if stamp.hash.is_none() {
@@ -274,7 +283,7 @@ impl Step for Llvm {
};
builder.update_submodule(&Path::new("src").join("llvm-project"));
- if builder.llvm_link_shared() && target.contains("windows") {
+ if builder.llvm_link_shared() && target.is_windows() {
panic!("shared linking to LLVM is not currently supported on {}", target.triple);
}
@@ -352,7 +361,7 @@ impl Step for Llvm {
// Disable zstd to avoid a dependency on libzstd.so.
cfg.define("LLVM_ENABLE_ZSTD", "OFF");
- if !target.contains("windows") {
+ if !target.is_windows() {
cfg.define("LLVM_ENABLE_ZLIB", "ON");
} else {
cfg.define("LLVM_ENABLE_ZLIB", "OFF");
@@ -402,7 +411,7 @@ impl Step for Llvm {
ldflags.shared.push(" -latomic");
}
- if target.contains("msvc") {
+ if target.is_msvc() {
cfg.define("LLVM_USE_CRT_DEBUG", "MT");
cfg.define("LLVM_USE_CRT_RELEASE", "MT");
cfg.define("LLVM_USE_CRT_RELWITHDEBINFO", "MT");
@@ -560,11 +569,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 >= 15 {
+ if major >= 16 {
return;
}
}
- panic!("\n\nbad LLVM version: {version}, need >=15.0\n\n")
+ panic!("\n\nbad LLVM version: {version}, need >=16.0\n\n")
}
fn configure_cmake(
@@ -598,7 +607,7 @@ fn configure_cmake(
cfg.define("CMAKE_SYSTEM_NAME", "DragonFly");
} else if target.contains("freebsd") {
cfg.define("CMAKE_SYSTEM_NAME", "FreeBSD");
- } else if target.contains("windows") {
+ } else if target.is_windows() {
cfg.define("CMAKE_SYSTEM_NAME", "Windows");
} else if target.contains("haiku") {
cfg.define("CMAKE_SYSTEM_NAME", "Haiku");
@@ -635,7 +644,7 @@ fn configure_cmake(
}
let sanitize_cc = |cc: &Path| {
- if target.contains("msvc") {
+ if target.is_msvc() {
OsString::from(cc.to_str().unwrap().replace("\\", "/"))
} else {
cc.as_os_str().to_owned()
@@ -645,7 +654,7 @@ fn configure_cmake(
// MSVC with CMake uses msbuild by default which doesn't respect these
// vars that we'd otherwise configure. In that case we just skip this
// entirely.
- if target.contains("msvc") && !builder.ninja() {
+ if target.is_msvc() && !builder.ninja() {
return;
}
@@ -655,7 +664,7 @@ fn configure_cmake(
};
// Handle msvc + ninja + ccache specially (this is what the bots use)
- if target.contains("msvc") && builder.ninja() && builder.config.ccache.is_some() {
+ if target.is_msvc() && builder.ninja() && 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");
@@ -759,11 +768,11 @@ fn configure_cmake(
// For distribution we want the LLVM tools to be *statically* linked to libstdc++.
// We also do this if the user explicitly requested static libstdc++.
if builder.config.llvm_static_stdcpp
- && !target.contains("msvc")
+ && !target.is_msvc()
&& !target.contains("netbsd")
&& !target.contains("solaris")
{
- if target.contains("apple") || target.contains("windows") {
+ if target.contains("apple") || target.is_windows() {
ldflags.push_all("-static-libstdc++");
} else {
ldflags.push_all("-Wl,-Bsymbolic -static-libstdc++");
@@ -865,7 +874,7 @@ impl Step for Lld {
// when doing PGO on CI, cmake or clang-cl don't automatically link clang's
// profiler runtime in. In that case, we need to manually ask cmake to do it, to avoid
// linking errors, much like LLVM's cmake setup does in that situation.
- if builder.config.llvm_profile_generate && target.contains("msvc") {
+ if builder.config.llvm_profile_generate && target.is_msvc() {
if let Some(clang_cl_path) = builder.config.llvm_clang_cl.as_ref() {
// Find clang's runtime library directory and push that as a search path to the
// cmake linker flags.
@@ -1286,7 +1295,7 @@ impl Step for Libunwind {
cfg.define("__LIBUNWIND_IS_NATIVE_ONLY", None);
cfg.define("NDEBUG", None);
}
- if self.target.contains("windows") {
+ if self.target.is_windows() {
cfg.define("_LIBUNWIND_HIDE_SYMBOLS", "1");
cfg.define("_LIBUNWIND_IS_NATIVE_ONLY", "1");
}
diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs
index 486a1e20f..9c897ae1b 100644
--- a/src/bootstrap/src/core/build_steps/setup.rs
+++ b/src/bootstrap/src/core/build_steps/setup.rs
@@ -1,6 +1,8 @@
use crate::core::builder::{Builder, RunConfig, ShouldRun, Step};
+use crate::t;
+use crate::utils::change_tracker::CONFIG_CHANGE_HISTORY;
+use crate::utils::helpers::hex_encode;
use crate::Config;
-use crate::{t, CONFIG_CHANGE_HISTORY};
use sha2::Digest;
use std::env::consts::EXE_SUFFIX;
use std::fmt::Write as _;
@@ -226,7 +228,7 @@ fn setup_config_toml(path: &PathBuf, profile: Profile, config: &Config) {
return;
}
- let latest_change_id = CONFIG_CHANGE_HISTORY.last().unwrap();
+ let latest_change_id = CONFIG_CHANGE_HISTORY.last().unwrap().change_id;
let settings = format!(
"# Includes one of the default files in src/bootstrap/defaults\n\
profile = \"{profile}\"\n\
@@ -548,12 +550,13 @@ impl Step for Vscode {
if config.dry_run() {
return;
}
- t!(create_vscode_settings_maybe(&config));
+ while !t!(create_vscode_settings_maybe(&config)) {}
}
}
/// Create a `.vscode/settings.json` file for rustc development, or just print it
-fn create_vscode_settings_maybe(config: &Config) -> io::Result<()> {
+/// If this method should be re-called, it returns `false`.
+fn create_vscode_settings_maybe(config: &Config) -> io::Result<bool> {
let (current_hash, historical_hashes) = SETTINGS_HASHES.split_last().unwrap();
let vscode_settings = config.src.join(".vscode").join("settings.json");
// If None, no settings.json exists
@@ -564,9 +567,9 @@ fn create_vscode_settings_maybe(config: &Config) -> io::Result<()> {
if let Ok(current) = fs::read_to_string(&vscode_settings) {
let mut hasher = sha2::Sha256::new();
hasher.update(&current);
- let hash = hex::encode(hasher.finalize().as_slice());
+ let hash = hex_encode(hasher.finalize().as_slice());
if hash == *current_hash {
- return Ok(());
+ return Ok(true);
} else if historical_hashes.contains(&hash.as_str()) {
mismatched_settings = Some(true);
} else {
@@ -586,13 +589,13 @@ fn create_vscode_settings_maybe(config: &Config) -> io::Result<()> {
_ => (),
}
let should_create = match prompt_user(
- "Would you like to create/update `settings.json`, or only print suggested settings?: [y/p/N]",
+ "Would you like to create/update settings.json? (Press 'p' to preview values): [y/N]",
)? {
Some(PromptResult::Yes) => true,
Some(PromptResult::Print) => false,
_ => {
println!("Ok, skipping settings!");
- return Ok(());
+ return Ok(true);
}
};
if should_create {
@@ -619,5 +622,5 @@ fn create_vscode_settings_maybe(config: &Config) -> io::Result<()> {
} else {
println!("\n{RUST_ANALYZER_SETTINGS}");
}
- Ok(())
+ Ok(should_create)
}
diff --git a/src/bootstrap/src/core/build_steps/synthetic_targets.rs b/src/bootstrap/src/core/build_steps/synthetic_targets.rs
index d2c65b740..9acdcaeb5 100644
--- a/src/bootstrap/src/core/build_steps/synthetic_targets.rs
+++ b/src/bootstrap/src/core/build_steps/synthetic_targets.rs
@@ -59,6 +59,11 @@ fn create_synthetic_target(
let mut cmd = Command::new(builder.rustc(compiler));
cmd.arg("--target").arg(base.rustc_target_arg());
cmd.args(["-Zunstable-options", "--print", "target-spec-json"]);
+
+ // If `rust.channel` is set to either beta or stable, rustc will complain that
+ // we cannot use nightly features. So `RUSTC_BOOTSTRAP` is needed here.
+ cmd.env("RUSTC_BOOTSTRAP", "1");
+
cmd.stdout(Stdio::piped());
let output = cmd.spawn().unwrap().wait_with_output().unwrap();
diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs
index d2aa89dee..4eb776625 100644
--- a/src/bootstrap/src/core/build_steps/test.rs
+++ b/src/bootstrap/src/core/build_steps/test.rs
@@ -29,8 +29,9 @@ use crate::utils;
use crate::utils::cache::{Interned, INTERNER};
use crate::utils::exec::BootstrapCommand;
use crate::utils::helpers::{
- self, add_link_lib_path, dylib_path, dylib_path_var, output, t,
- target_supports_cranelift_backend, up_to_date,
+ self, add_link_lib_path, add_rustdoc_cargo_linker_args, dylib_path, dylib_path_var,
+ linker_args, linker_flags, output, t, target_supports_cranelift_backend, up_to_date,
+ LldThreads,
};
use crate::utils::render_tests::{add_flags_and_try_run_tests, try_run_tests};
use crate::{envify, CLang, DocTests, GitRepo, Mode};
@@ -49,9 +50,9 @@ const MIR_OPT_BLESS_TARGET_MAPPING: &[(&str, &str)] = &[
("i686-unknown-linux-musl", "x86_64-unknown-linux-musl"),
("i686-pc-windows-msvc", "x86_64-pc-windows-msvc"),
("i686-pc-windows-gnu", "x86_64-pc-windows-gnu"),
- ("i686-apple-darwin", "x86_64-apple-darwin"),
// ARM Macs don't have a corresponding 32-bit target that they can (easily)
// build for, so there is no entry for "aarch64-apple-darwin" here.
+ // Likewise, i686 for macOS is no longer possible to build.
];
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
@@ -271,13 +272,14 @@ impl Step for Cargotest {
let _time = helpers::timeit(&builder);
let mut cmd = builder.tool_cmd(Tool::CargoTest);
- builder.run_delaying_failure(
- cmd.arg(&cargo)
- .arg(&out_dir)
- .args(builder.config.test_args())
- .env("RUSTC", builder.rustc(compiler))
- .env("RUSTDOC", builder.rustdoc(compiler)),
- );
+ let mut cmd = cmd
+ .arg(&cargo)
+ .arg(&out_dir)
+ .args(builder.config.test_args())
+ .env("RUSTC", builder.rustc(compiler))
+ .env("RUSTDOC", builder.rustdoc(compiler));
+ add_rustdoc_cargo_linker_args(&mut cmd, builder, compiler.host, LldThreads::No);
+ builder.run_delaying_failure(cmd);
}
}
@@ -369,7 +371,7 @@ impl Step for RustAnalyzer {
// We don't need to build the whole Rust Analyzer for the proc-macro-srv test suite,
// but we do need the standard library to be present.
- builder.ensure(compile::Std::new(compiler, host));
+ builder.ensure(compile::Rustc::new(compiler, host));
let workspace_path = "src/tools/rust-analyzer";
// until the whole RA test suite runs on `i686`, we only run
@@ -378,7 +380,7 @@ impl Step for RustAnalyzer {
let mut cargo = tool::prepare_tool_cargo(
builder,
compiler,
- Mode::ToolStd,
+ Mode::ToolRustc,
host,
"test",
crate_path,
@@ -862,15 +864,8 @@ impl Step for RustdocTheme {
.env("CFG_RELEASE_CHANNEL", &builder.config.channel)
.env("RUSTDOC_REAL", builder.rustdoc(self.compiler))
.env("RUSTC_BOOTSTRAP", "1");
- if let Some(linker) = builder.linker(self.compiler.host) {
- cmd.env("RUSTDOC_LINKER", linker);
- }
- if builder.is_fuse_ld_lld(self.compiler.host) {
- cmd.env(
- "RUSTDOC_LLD_NO_THREADS",
- helpers::lld_flag_no_threads(self.compiler.host.contains("windows")),
- );
- }
+ cmd.args(linker_args(builder, self.compiler.host, LldThreads::No));
+
builder.run_delaying_failure(&mut cmd);
}
}
@@ -1005,6 +1000,7 @@ impl Step for RustdocGUI {
let run = run.suite_path("tests/rustdoc-gui");
run.lazy_default_condition(Box::new(move || {
builder.config.nodejs.is_some()
+ && builder.doc_tests != DocTests::Only
&& builder
.config
.npm
@@ -1044,6 +1040,8 @@ impl Step for RustdocGUI {
cmd.env("RUSTDOC", builder.rustdoc(self.compiler))
.env("RUSTC", builder.rustc(self.compiler));
+ add_rustdoc_cargo_linker_args(&mut cmd, builder, self.compiler.host, LldThreads::No);
+
for path in &builder.paths {
if let Some(p) = helpers::is_valid_test_suite_arg(path, "tests/rustdoc-gui", builder) {
if !p.ends_with(".goml") {
@@ -1162,7 +1160,8 @@ HELP: to skip test's attempt to check tidiness, pass `--skip src/tools/tidy` to
}
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
- run.path("src/tools/tidy")
+ let default = run.builder.doc_tests != DocTests::Only;
+ run.path("src/tools/tidy").default_condition(default)
}
fn make_run(run: RunConfig<'_>) {
@@ -1564,6 +1563,10 @@ impl Step for Compiletest {
/// compiletest `mode` and `suite` arguments. For example `mode` can be
/// "run-pass" or `suite` can be something like `debuginfo`.
fn run(self, builder: &Builder<'_>) {
+ if builder.doc_tests == DocTests::Only {
+ return;
+ }
+
if builder.top_stage == 0 && env::var("COMPILETEST_FORCE_STAGE0").is_err() {
eprintln!("\
ERROR: `--stage 0` runs compiletest on the beta compiler, not your local changes, and will almost always cause tests to fail
@@ -1594,8 +1597,13 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
// NOTE: Only stage 1 is special cased because we need the rustc_private artifacts to match the
// running compiler in stage 2 when plugins run.
let stage_id = if suite == "ui-fulldeps" && compiler.stage == 1 {
- compiler = builder.compiler(compiler.stage - 1, target);
- format!("stage{}-{}", compiler.stage + 1, target)
+ // At stage 0 (stage - 1) we are using the beta compiler. Using `self.target` can lead finding
+ // an incorrect compiler path on cross-targets, as the stage 0 beta compiler is always equal
+ // to `build.build` in the configuration.
+ let build = builder.build.build;
+
+ compiler = builder.compiler(compiler.stage - 1, build);
+ format!("stage{}-{}", compiler.stage + 1, build)
} else {
format!("stage{}-{}", compiler.stage, target)
};
@@ -1744,14 +1752,14 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
let mut hostflags = flags.clone();
hostflags.push(format!("-Lnative={}", builder.test_helpers_out(compiler.host).display()));
- hostflags.extend(builder.lld_flags(compiler.host));
+ hostflags.extend(linker_flags(builder, compiler.host, LldThreads::No));
for flag in hostflags {
cmd.arg("--host-rustcflags").arg(flag);
}
let mut targetflags = flags;
targetflags.push(format!("-Lnative={}", builder.test_helpers_out(target).display()));
- targetflags.extend(builder.lld_flags(target));
+ targetflags.extend(linker_flags(builder, compiler.host, LldThreads::No));
for flag in targetflags {
cmd.arg("--target-rustcflags").arg(flag);
}
@@ -1825,6 +1833,10 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
cmd.arg("--json");
+ if builder.config.rust_debug_assertions_std {
+ cmd.arg("--with-debug-assertions");
+ };
+
let mut llvm_components_passed = false;
let mut copts_passed = false;
if builder.config.llvm_enabled() {
@@ -1925,13 +1937,18 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
//
// Note that if we encounter `PATH` we make sure to append to our own `PATH`
// rather than stomp over it.
- if !builder.config.dry_run() && target.contains("msvc") {
+ if !builder.config.dry_run() && target.is_msvc() {
for &(ref k, ref v) in builder.cc.borrow()[&target].env() {
if k != "PATH" {
cmd.env(k, v);
}
}
}
+
+ // Some UI tests trigger behavior in rustc where it reads $CARGO and changes behavior if it exists.
+ // To make the tests work that rely on it not being set, make sure it is not set.
+ cmd.env_remove("CARGO");
+
cmd.env("RUSTC_BOOTSTRAP", "1");
// Override the rustc version used in symbol hashes to reduce the amount of normalization
// needed when diffing test output.
@@ -2322,6 +2339,8 @@ impl Step for CrateLibrustc {
}
fn run(self, builder: &Builder<'_>) {
+ builder.ensure(compile::Std::new(self.compiler, self.target));
+
builder.ensure(Crate {
compiler: self.compiler,
target: self.target,
@@ -3057,7 +3076,7 @@ impl Step for TestHelpers {
// We may have found various cross-compilers a little differently due to our
// extra configuration, so inform cc of these compilers. Note, though, that
// on MSVC we still need cc's detection of env vars (ugh).
- if !target.contains("msvc") {
+ if !target.is_msvc() {
if let Some(ar) = builder.ar(target) {
cfg.archiver(ar);
}
diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs
index d1bc05e51..9942f00a0 100644
--- a/src/bootstrap/src/core/build_steps/tool.rs
+++ b/src/bootstrap/src/core/build_steps/tool.rs
@@ -203,6 +203,16 @@ pub fn prepare_tool_cargo(
if !features.is_empty() {
cargo.arg("--features").arg(&features.join(", "));
}
+
+ // Enable internal lints for clippy and rustdoc
+ // NOTE: this doesn't enable lints for any other tools unless they explicitly add `#![warn(rustc::internal)]`
+ // See https://github.com/rust-lang/rust/pull/80573#issuecomment-754010776
+ //
+ // NOTE: We unconditionally set this here to avoid recompiling tools between `x check $tool`
+ // and `x test $tool` executions.
+ // See https://github.com/rust-lang/rust/issues/116538
+ cargo.rustflag("-Zunstable-options");
+
cargo
}
@@ -603,8 +613,7 @@ pub struct RustAnalyzer {
}
impl RustAnalyzer {
- pub const ALLOW_FEATURES: &'static str =
- "proc_macro_internals,proc_macro_diagnostic,proc_macro_span,proc_macro_span_shrink";
+ pub const ALLOW_FEATURES: &'static str = "rustc_private,proc_macro_internals,proc_macro_diagnostic,proc_macro_span,proc_macro_span_shrink";
}
impl Step for RustAnalyzer {
@@ -636,7 +645,7 @@ impl Step for RustAnalyzer {
compiler: self.compiler,
target: self.target,
tool: "rust-analyzer",
- mode: Mode::ToolStd,
+ mode: Mode::ToolRustc,
path: "src/tools/rust-analyzer",
extra_features: vec!["rust-analyzer/in-rust-tree".to_owned()],
is_optional_tool: false,
@@ -824,7 +833,7 @@ impl<'a> Builder<'a> {
// On MSVC a tool may invoke a C compiler (e.g., compiletest in run-make
// mode) and that C compiler may need some extra PATH modification. Do
// so here.
- if compiler.host.contains("msvc") {
+ if compiler.host.is_msvc() {
let curpaths = env::var_os("PATH").unwrap_or_default();
let curpaths = env::split_paths(&curpaths).collect::<Vec<_>>();
for &(ref k, ref v) in self.cc.borrow()[&compiler.host].env() {
diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs
index cd276674d..e18096443 100644
--- a/src/bootstrap/src/core/builder.rs
+++ b/src/bootstrap/src/core/builder.rs
@@ -10,6 +10,7 @@ use std::io::{BufRead, BufReader};
use std::ops::Deref;
use std::path::{Path, PathBuf};
use std::process::Command;
+use std::sync::OnceLock;
use std::time::{Duration, Instant};
use crate::core::build_steps::llvm;
@@ -17,19 +18,18 @@ use crate::core::build_steps::tool::{self, SourceType};
use crate::core::build_steps::{check, clean, compile, dist, doc, install, run, setup, test};
use crate::core::config::flags::{Color, Subcommand};
use crate::core::config::{DryRun, SplitDebuginfo, TargetSelection};
+use crate::prepare_behaviour_dump_dir;
use crate::utils::cache::{Cache, Interned, INTERNER};
-use crate::utils::helpers::{self, add_dylib_path, add_link_lib_path, exe, libdir, output, t};
-use crate::Crate;
+use crate::utils::helpers::{self, add_dylib_path, add_link_lib_path, exe, linker_args};
+use crate::utils::helpers::{libdir, linker_flags, output, t, LldThreads};
use crate::EXTRA_CHECK_CFGS;
-use crate::{Build, CLang, DocTests, GitRepo, Mode};
+use crate::{Build, CLang, Crate, DocTests, GitRepo, Mode};
pub use crate::Compiler;
-// FIXME:
-// - use std::lazy for `Lazy`
-// - use std::cell for `OnceCell`
-// Once they get stabilized and reach beta.
+
use clap::ValueEnum;
-use once_cell::sync::{Lazy, OnceCell};
+// FIXME: replace with std::lazy after it gets stabilized and reaches beta
+use once_cell::sync::Lazy;
#[cfg(test)]
#[path = "../tests/builder.rs"]
@@ -495,7 +495,7 @@ impl<'a> ShouldRun<'a> {
///
/// [`path`]: ShouldRun::path
pub fn paths(mut self, paths: &[&str]) -> Self {
- static SUBMODULES_PATHS: OnceCell<Vec<String>> = OnceCell::new();
+ static SUBMODULES_PATHS: OnceLock<Vec<String>> = OnceLock::new();
let init_submodules_paths = |src: &PathBuf| {
let file = File::open(src.join(".gitmodules")).unwrap();
@@ -810,6 +810,7 @@ impl<'a> Builder<'a> {
doc::StyleGuide,
doc::Tidy,
doc::Bootstrap,
+ doc::Releases,
),
Kind::Dist => describe!(
dist::Docs,
@@ -1151,6 +1152,44 @@ impl<'a> Builder<'a> {
self.ensure(tool::Rustdoc { compiler })
}
+ pub fn cargo_clippy_cmd(&self, run_compiler: Compiler) -> Command {
+ let initial_sysroot_bin = self.initial_rustc.parent().unwrap();
+ // Set PATH to include the sysroot bin dir so clippy can find cargo.
+ // FIXME: once rust-clippy#11944 lands on beta, set `CARGO` directly instead.
+ let path = t!(env::join_paths(
+ // The sysroot comes first in PATH to avoid using rustup's cargo.
+ std::iter::once(PathBuf::from(initial_sysroot_bin))
+ .chain(env::split_paths(&t!(env::var("PATH"))))
+ ));
+
+ if run_compiler.stage == 0 {
+ // `ensure(Clippy { stage: 0 })` *builds* clippy with stage0, it doesn't use the beta clippy.
+ let cargo_clippy = self.build.config.download_clippy();
+ let mut cmd = Command::new(cargo_clippy);
+ cmd.env("PATH", &path);
+ return cmd;
+ }
+
+ let build_compiler = self.compiler(run_compiler.stage - 1, self.build.build);
+ self.ensure(tool::Clippy {
+ compiler: build_compiler,
+ target: self.build.build,
+ extra_features: vec![],
+ });
+ let cargo_clippy = self.ensure(tool::CargoClippy {
+ compiler: build_compiler,
+ target: self.build.build,
+ extra_features: vec![],
+ });
+ let mut dylib_path = helpers::dylib_path();
+ dylib_path.insert(0, self.sysroot(run_compiler).join("lib"));
+
+ let mut cmd = Command::new(cargo_clippy.unwrap());
+ cmd.env(helpers::dylib_path_var(), env::join_paths(&dylib_path).unwrap());
+ cmd.env("PATH", path);
+ cmd
+ }
+
pub fn rustdoc_cmd(&self, compiler: Compiler) -> Command {
let mut cmd = Command::new(&self.bootstrap_out.join("rustdoc"));
cmd.env("RUSTC_STAGE", compiler.stage.to_string())
@@ -1173,9 +1212,7 @@ impl<'a> Builder<'a> {
cmd.env_remove("MAKEFLAGS");
cmd.env_remove("MFLAGS");
- if let Some(linker) = self.linker(compiler.host) {
- cmd.env("RUSTDOC_LINKER", linker);
- }
+ cmd.args(linker_args(self, compiler.host, LldThreads::Yes));
cmd
}
@@ -1201,7 +1238,12 @@ impl<'a> Builder<'a> {
target: TargetSelection,
cmd: &str,
) -> Command {
- let mut cargo = Command::new(&self.initial_cargo);
+ let mut cargo = if cmd == "clippy" {
+ self.cargo_clippy_cmd(compiler)
+ } else {
+ Command::new(&self.initial_cargo)
+ };
+
// Run cargo from the source root so it can find .cargo/config.
// This matters when using vendoring and the working directory is outside the repository.
cargo.current_dir(&self.src);
@@ -1325,6 +1367,23 @@ impl<'a> Builder<'a> {
compiler.stage
};
+ // We synthetically interpret a stage0 compiler used to build tools as a
+ // "raw" compiler in that it's the exact snapshot we download. Normally
+ // the stage0 build means it uses libraries build by the stage0
+ // compiler, but for tools we just use the precompiled libraries that
+ // we've downloaded
+ let use_snapshot = mode == Mode::ToolBootstrap;
+ assert!(!use_snapshot || stage == 0 || self.local_rebuild);
+
+ let maybe_sysroot = self.sysroot(compiler);
+ let sysroot = if use_snapshot { self.rustc_snapshot_sysroot() } else { &maybe_sysroot };
+ let libdir = self.rustc_libdir(compiler);
+
+ let sysroot_str = sysroot.as_os_str().to_str().expect("sysroot should be UTF-8");
+ if !matches!(self.config.dry_run, DryRun::SelfCheck) {
+ self.verbose_than(0, &format!("using sysroot {sysroot_str}"));
+ }
+
let mut rustflags = Rustflags::new(target);
if stage != 0 {
if let Ok(s) = env::var("CARGOFLAGS_NOT_BOOTSTRAP") {
@@ -1336,41 +1395,16 @@ impl<'a> Builder<'a> {
cargo.args(s.split_whitespace());
}
rustflags.env("RUSTFLAGS_BOOTSTRAP");
- if cmd == "clippy" {
- // clippy overwrites sysroot if we pass it to cargo.
- // Pass it directly to clippy instead.
- // NOTE: this can't be fixed in clippy because we explicitly don't set `RUSTC`,
- // so it has no way of knowing the sysroot.
- rustflags.arg("--sysroot");
- rustflags.arg(
- self.sysroot(compiler)
- .as_os_str()
- .to_str()
- .expect("sysroot must be valid UTF-8"),
- );
- // Only run clippy on a very limited subset of crates (in particular, not build scripts).
- cargo.arg("-Zunstable-options");
- // Explicitly does *not* set `--cfg=bootstrap`, since we're using a nightly clippy.
- let host_version = Command::new("rustc").arg("--version").output().map_err(|_| ());
- let output = host_version.and_then(|output| {
- if output.status.success() {
- Ok(output)
- } else {
- Err(())
- }
- }).unwrap_or_else(|_| {
- eprintln!(
- "ERROR: `x.py clippy` requires a host `rustc` toolchain with the `clippy` component"
- );
- eprintln!("HELP: try `rustup component add clippy`");
- crate::exit!(1);
- });
- if !t!(std::str::from_utf8(&output.stdout)).contains("nightly") {
- rustflags.arg("--cfg=bootstrap");
- }
- } else {
- rustflags.arg("--cfg=bootstrap");
- }
+ rustflags.arg("--cfg=bootstrap");
+ }
+
+ if cmd == "clippy" {
+ // clippy overwrites sysroot if we pass it to cargo.
+ // Pass it directly to clippy instead.
+ // NOTE: this can't be fixed in clippy because we explicitly don't set `RUSTC`,
+ // so it has no way of knowing the sysroot.
+ rustflags.arg("--sysroot");
+ rustflags.arg(sysroot_str);
}
let use_new_symbol_mangling = match self.config.rust_new_symbol_mangling {
@@ -1404,9 +1438,6 @@ impl<'a> Builder<'a> {
rustflags.arg("-Zunstable-options");
}
- // #[cfg(bootstrap)]
- let use_new_check_cfg_syntax = self.local_rebuild;
-
// Enable compile-time checking of `cfg` names, values and Cargo `features`.
//
// Note: `std`, `alloc` and `core` imports some dependencies by #[path] (like
@@ -1414,17 +1445,9 @@ impl<'a> Builder<'a> {
// features but cargo isn't involved in the #[path] process and so cannot pass the
// complete list of features, so for that reason we don't enable checking of
// features for std crates.
- if use_new_check_cfg_syntax {
- cargo.arg("-Zcheck-cfg");
- if mode == Mode::Std {
- rustflags.arg("--check-cfg=cfg(feature,values(any()))");
- }
- } else {
- cargo.arg(if mode != Mode::Std {
- "-Zcheck-cfg=names,values,output,features"
- } else {
- "-Zcheck-cfg=names,values,output"
- });
+ cargo.arg("-Zcheck-cfg");
+ if mode == Mode::Std {
+ rustflags.arg("--check-cfg=cfg(feature,values(any()))");
}
// Add extra cfg not defined in/by rustc
@@ -1445,12 +1468,8 @@ impl<'a> Builder<'a> {
.collect::<String>(),
None => String::new(),
};
- if use_new_check_cfg_syntax {
- let values = values.strip_prefix(",").unwrap_or(&values); // remove the first `,`
- rustflags.arg(&format!("--check-cfg=cfg({name},values({values}))"));
- } else {
- rustflags.arg(&format!("--check-cfg=values({name}{values})"));
- }
+ let values = values.strip_prefix(",").unwrap_or(&values); // remove the first `,`
+ rustflags.arg(&format!("--check-cfg=cfg({name},values({values}))"));
}
}
@@ -1466,11 +1485,7 @@ impl<'a> Builder<'a> {
// We also declare that the flag is expected, which we need to do to not
// get warnings about it being unexpected.
hostflags.arg("-Zunstable-options");
- if use_new_check_cfg_syntax {
- hostflags.arg("--check-cfg=cfg(bootstrap)");
- } else {
- hostflags.arg("--check-cfg=values(bootstrap)");
- }
+ hostflags.arg("--check-cfg=cfg(bootstrap)");
// FIXME: It might be better to use the same value for both `RUSTFLAGS` and `RUSTDOCFLAGS`,
// but this breaks CI. At the very least, stage0 `rustdoc` needs `--cfg bootstrap`. See
@@ -1584,18 +1599,6 @@ impl<'a> Builder<'a> {
let want_rustdoc = self.doc_tests != DocTests::No;
- // We synthetically interpret a stage0 compiler used to build tools as a
- // "raw" compiler in that it's the exact snapshot we download. Normally
- // the stage0 build means it uses libraries build by the stage0
- // compiler, but for tools we just use the precompiled libraries that
- // we've downloaded
- let use_snapshot = mode == Mode::ToolBootstrap;
- assert!(!use_snapshot || stage == 0 || self.local_rebuild);
-
- let maybe_sysroot = self.sysroot(compiler);
- let sysroot = if use_snapshot { self.rustc_snapshot_sysroot() } else { &maybe_sysroot };
- let libdir = self.rustc_libdir(compiler);
-
// Clear the output directory if the real rustc we're using has changed;
// Cargo cannot detect this as it thinks rustc is bootstrap/debug/rustc.
//
@@ -1631,10 +1634,19 @@ impl<'a> Builder<'a> {
)
.env("RUSTC_ERROR_METADATA_DST", self.extended_error_dir())
.env("RUSTC_BREAK_ON_ICE", "1");
- // Clippy support is a hack and uses the default `cargo-clippy` in path.
- // Don't override RUSTC so that the `cargo-clippy` in path will be run.
- if cmd != "clippy" {
- cargo.env("RUSTC", self.bootstrap_out.join("rustc"));
+
+ // Set RUSTC_WRAPPER to the bootstrap shim, which switches between beta and in-tree
+ // sysroot depending on whether we're building build scripts.
+ // NOTE: we intentionally use RUSTC_WRAPPER so that we can support clippy - RUSTC is not
+ // respected by clippy-driver; RUSTC_WRAPPER happens earlier, before clippy runs.
+ cargo.env("RUSTC_WRAPPER", self.bootstrap_out.join("rustc"));
+ // NOTE: we also need to set RUSTC so cargo can run `rustc -vV`; apparently that ignores RUSTC_WRAPPER >:(
+ cargo.env("RUSTC", self.bootstrap_out.join("rustc"));
+
+ // Someone might have set some previous rustc wrapper (e.g.
+ // sccache) before bootstrap overrode it. Respect that variable.
+ if let Some(existing_wrapper) = env::var_os("RUSTC_WRAPPER") {
+ cargo.env("RUSTC_WRAPPER_REAL", existing_wrapper);
}
// Dealing with rpath here is a little special, so let's go into some
@@ -1673,10 +1685,7 @@ impl<'a> Builder<'a> {
// flesh out rpath support more fully in the future.
rustflags.arg("-Zosx-rpath-install-name");
Some(format!("-Wl,-rpath,@loader_path/../{libdir}"))
- } else if !target.contains("windows")
- && !target.contains("aix")
- && !target.contains("xous")
- {
+ } else if !target.is_windows() && !target.contains("aix") && !target.contains("xous") {
rustflags.arg("-Clink-args=-Wl,-z,origin");
Some(format!("-Wl,-rpath,$ORIGIN/../{libdir}"))
} else {
@@ -1687,23 +1696,28 @@ impl<'a> Builder<'a> {
}
}
- if let Some(host_linker) = self.linker(compiler.host) {
- hostflags.arg(format!("-Clinker={}", host_linker.display()));
+ cargo.env(profile_var("STRIP"), self.config.rust_strip.to_string());
+
+ if let Some(stack_protector) = &self.config.rust_stack_protector {
+ rustflags.arg(&format!("-Zstack-protector={stack_protector}"));
}
- if self.is_fuse_ld_lld(compiler.host) {
- hostflags.arg("-Clink-args=-fuse-ld=lld");
+
+ for arg in linker_args(self, compiler.host, LldThreads::Yes) {
+ hostflags.arg(&arg);
}
if let Some(target_linker) = self.linker(target) {
let target = crate::envify(&target.triple);
cargo.env(&format!("CARGO_TARGET_{target}_LINKER"), target_linker);
}
- if self.is_fuse_ld_lld(target) {
- rustflags.arg("-Clink-args=-fuse-ld=lld");
+ // We want to set -Clinker using Cargo, therefore we only call `linker_flags` and not
+ // `linker_args` here.
+ for flag in linker_flags(self, target, LldThreads::Yes) {
+ rustflags.arg(&flag);
+ }
+ for arg in linker_args(self, target, LldThreads::Yes) {
+ rustdocflags.arg(&arg);
}
- self.lld_flags(target).for_each(|flag| {
- rustdocflags.arg(&flag);
- });
if !(["build", "check", "clippy", "fix", "rustc"].contains(&cmd)) && want_rustdoc {
cargo.env("RUSTDOC_LIBDIR", self.rustc_libdir(compiler));
@@ -1743,10 +1757,8 @@ impl<'a> Builder<'a> {
let split_debuginfo_is_stable = target.contains("linux")
|| target.contains("apple")
- || (target.contains("msvc")
- && self.config.rust_split_debuginfo == SplitDebuginfo::Packed)
- || (target.contains("windows")
- && self.config.rust_split_debuginfo == SplitDebuginfo::Off);
+ || (target.is_msvc() && self.config.rust_split_debuginfo == SplitDebuginfo::Packed)
+ || (target.is_windows() && self.config.rust_split_debuginfo == SplitDebuginfo::Off);
if !split_debuginfo_is_stable {
rustflags.arg("-Zunstable-options");
@@ -1804,6 +1816,16 @@ impl<'a> Builder<'a> {
// Enable usage of unstable features
cargo.env("RUSTC_BOOTSTRAP", "1");
+
+ if self.config.dump_bootstrap_shims {
+ prepare_behaviour_dump_dir(&self.build);
+
+ cargo
+ .env("DUMP_BOOTSTRAP_SHIMS", self.build.out.join("bootstrap-shims-dump"))
+ .env("BUILD_OUT", &self.build.out)
+ .env("CARGO_HOME", t!(home::cargo_home()));
+ };
+
self.add_rust_test_threads(&mut cargo);
// Almost all of the crates that we compile as part of the bootstrap may
@@ -1892,7 +1914,6 @@ impl<'a> Builder<'a> {
// some code doesn't go through this `rustc` wrapper.
lint_flags.push("-Wrust_2018_idioms");
lint_flags.push("-Wunused_lifetimes");
- lint_flags.push("-Wsemicolon_in_expressions_from_macros");
if self.config.deny_warnings {
lint_flags.push("-Dwarnings");
@@ -1924,7 +1945,7 @@ 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.is_msvc() {
if let Some(ref cl) = self.config.llvm_clang_cl {
cargo.env("CC", cl).env("CXX", cl);
}
@@ -1982,6 +2003,16 @@ impl<'a> Builder<'a> {
rustflags.arg("-Ccontrol-flow-guard");
}
+ // If EHCont Guard is enabled, pass the `-Zehcont-guard` flag to rustc when compiling the
+ // standard library, since this might be linked into the final outputs produced by rustc.
+ // Since this mitigation is only available on Windows, only enable it for the standard
+ // library in case the compiler is run on a non-Windows platform.
+ // This is not needed for stage 0 artifacts because these will only be used for building
+ // the stage 1 compiler.
+ if cfg!(windows) && mode == Mode::Std && self.config.ehcont_guard && compiler.stage >= 1 {
+ rustflags.arg("-Zehcont-guard");
+ }
+
// For `cargo doc` invocations, make rustdoc print the Rust version into the docs
// This replaces spaces with tabs because RUSTDOCFLAGS does not
// support arguments with regular spaces. Hopefully someday Cargo will
@@ -1992,7 +2023,11 @@ impl<'a> Builder<'a> {
// Environment variables *required* throughout the build
//
// FIXME: should update code to not require this env var
+
+ // The host this new compiler will *run* on.
cargo.env("CFG_COMPILER_HOST_TRIPLE", target.triple);
+ // The host this new compiler is being *built* on.
+ cargo.env("CFG_COMPILER_BUILD_TRIPLE", compiler.host.triple);
// Set this for all builds to make sure doc builds also get it.
cargo.env("CFG_RELEASE_CHANNEL", &self.config.channel);
diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs
index 0a9175aa3..f1e1b89d9 100644
--- a/src/bootstrap/src/core/config/config.rs
+++ b/src/bootstrap/src/core/config/config.rs
@@ -17,6 +17,7 @@ use std::io::IsTerminal;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::str::FromStr;
+use std::sync::OnceLock;
use crate::core::build_steps::compile::CODEGEN_BACKEND_PREFIX;
use crate::core::build_steps::llvm;
@@ -25,7 +26,6 @@ use crate::utils::cache::{Interned, INTERNER};
use crate::utils::channel::{self, GitInfo};
use crate::utils::helpers::{exe, output, t};
use build_helper::exit;
-use once_cell::sync::OnceCell;
use semver::Version;
use serde::{Deserialize, Deserializer};
use serde_derive::Deserialize;
@@ -105,6 +105,39 @@ impl Display for DebuginfoLevel {
}
}
+/// LLD in bootstrap works like this:
+/// - Self-contained lld: use `rust-lld` from the compiler's sysroot
+/// - External: use an external `lld` binary
+///
+/// It is configured depending on the target:
+/// 1) Everything except MSVC
+/// - Self-contained: `-Clinker-flavor=gnu-lld-cc -Clink-self-contained=+linker`
+/// - External: `-Clinker-flavor=gnu-lld-cc`
+/// 2) MSVC
+/// - Self-contained: `-Clinker=<path to rust-lld>`
+/// - External: `-Clinker=lld`
+#[derive(Default, Copy, Clone)]
+pub enum LldMode {
+ /// Do not use LLD
+ #[default]
+ Unused,
+ /// Use `rust-lld` from the compiler's sysroot
+ SelfContained,
+ /// Use an externally provided `lld` binary.
+ /// Note that the linker name cannot be overridden, the binary has to be named `lld` and it has
+ /// to be in $PATH.
+ External,
+}
+
+impl LldMode {
+ pub fn is_used(&self) -> bool {
+ match self {
+ LldMode::SelfContained | LldMode::External => true,
+ LldMode::Unused => false,
+ }
+ }
+}
+
/// Global configuration for the entire build and/or bootstrap.
///
/// This structure is parsed from `config.toml`, and some of the fields are inferred from `git` or build-time parameters.
@@ -117,6 +150,7 @@ impl Display for DebuginfoLevel {
pub struct Config {
pub changelog_seen: Option<usize>, // FIXME: Deprecated field. Remove it at 2024.
pub change_id: Option<usize>,
+ pub bypass_bootstrap_lock: bool,
pub ccache: Option<String>,
/// Call Build::ninja() instead of this.
pub ninja_in_file: bool,
@@ -159,6 +193,7 @@ pub struct Config {
pub cmd: Subcommand,
pub incremental: bool,
pub dry_run: DryRun,
+ pub dump_bootstrap_shims: bool,
/// Arguments appearing after `--` to be forwarded to tools,
/// e.g. `--fix-broken` or test arguments.
pub free_args: Vec<String>,
@@ -198,7 +233,7 @@ pub struct Config {
pub llvm_from_ci: bool,
pub llvm_build_config: HashMap<String, String>,
- pub use_lld: bool,
+ pub lld_mode: LldMode,
pub lld_enabled: bool,
pub llvm_tools_enabled: bool,
@@ -222,6 +257,8 @@ pub struct Config {
pub rust_debuginfo_level_tests: DebuginfoLevel,
pub rust_split_debuginfo: SplitDebuginfo,
pub rust_rpath: bool,
+ pub rust_strip: bool,
+ pub rust_stack_protector: Option<String>,
pub rustc_parallel: bool,
pub rustc_default_linker: Option<String>,
pub rust_optimize_tests: bool,
@@ -248,6 +285,7 @@ pub struct Config {
pub local_rebuild: bool,
pub jemalloc: bool,
pub control_flow_guard: bool,
+ pub ehcont_guard: bool,
// dist misc
pub dist_sign_folder: Option<PathBuf>,
@@ -383,10 +421,10 @@ impl std::str::FromStr for SplitDebuginfo {
impl SplitDebuginfo {
/// Returns the default `-Csplit-debuginfo` value for the current target. See the comment for
/// `rust.split-debuginfo` in `config.example.toml`.
- fn default_for_platform(target: &str) -> Self {
+ fn default_for_platform(target: TargetSelection) -> Self {
if target.contains("apple") {
SplitDebuginfo::Unpacked
- } else if target.contains("windows") {
+ } else if target.is_windows() {
SplitDebuginfo::Packed
} else {
SplitDebuginfo::Off
@@ -485,6 +523,14 @@ impl TargetSelection {
pub fn is_synthetic(&self) -> bool {
self.synthetic
}
+
+ pub fn is_msvc(&self) -> bool {
+ self.contains("msvc")
+ }
+
+ pub fn is_windows(&self) -> bool {
+ self.contains("windows")
+ }
}
impl fmt::Display for TargetSelection {
@@ -853,7 +899,6 @@ define_config! {
define_config! {
struct Dist {
sign_folder: Option<String> = "sign-folder",
- gpg_password_file: Option<String> = "gpg-password-file",
upload_addr: Option<String> = "upload-addr",
src_tarball: Option<bool> = "src-tarball",
missing_tools: Option<bool> = "missing-tools",
@@ -973,6 +1018,44 @@ enum StringOrInt<'a> {
String(&'a str),
Int(i64),
}
+
+impl<'de> Deserialize<'de> for LldMode {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ struct LldModeVisitor;
+
+ impl<'de> serde::de::Visitor<'de> for LldModeVisitor {
+ type Value = LldMode;
+
+ fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+ formatter.write_str("one of true, 'self-contained' or 'external'")
+ }
+
+ fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ Ok(if v { LldMode::External } else { LldMode::Unused })
+ }
+
+ fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ match v {
+ "external" => Ok(LldMode::External),
+ "self-contained" => Ok(LldMode::SelfContained),
+ _ => Err(E::custom("unknown mode {v}")),
+ }
+ }
+ }
+
+ deserializer.deserialize_any(LldModeVisitor)
+ }
+}
+
define_config! {
/// TOML representation of how the Rust build is configured.
struct Rust {
@@ -991,7 +1074,6 @@ define_config! {
debuginfo_level_tools: Option<DebuginfoLevel> = "debuginfo-level-tools",
debuginfo_level_tests: Option<DebuginfoLevel> = "debuginfo-level-tests",
split_debuginfo: Option<String> = "split-debuginfo",
- run_dsymutil: Option<bool> = "run-dsymutil",
backtrace: Option<bool> = "backtrace",
incremental: Option<bool> = "incremental",
parallel_compiler: Option<bool> = "parallel-compiler",
@@ -1000,6 +1082,8 @@ define_config! {
description: Option<String> = "description",
musl_root: Option<String> = "musl-root",
rpath: Option<bool> = "rpath",
+ strip: Option<bool> = "strip",
+ stack_protector: Option<String> = "stack-protector",
verbose_tests: Option<bool> = "verbose-tests",
optimize_tests: Option<bool> = "optimize-tests",
codegen_tests: Option<bool> = "codegen-tests",
@@ -1008,7 +1092,7 @@ define_config! {
save_toolstates: Option<String> = "save-toolstates",
codegen_backends: Option<Vec<String>> = "codegen-backends",
lld: Option<bool> = "lld",
- use_lld: Option<bool> = "use-lld",
+ lld_mode: Option<LldMode> = "use-lld",
llvm_tools: Option<bool> = "llvm-tools",
deny_warnings: Option<bool> = "deny-warnings",
backtrace_on_ice: Option<bool> = "backtrace-on-ice",
@@ -1019,6 +1103,7 @@ define_config! {
test_compare_mode: Option<bool> = "test-compare-mode",
llvm_libunwind: Option<String> = "llvm-libunwind",
control_flow_guard: Option<bool> = "control-flow-guard",
+ ehcont_guard: Option<bool> = "ehcont-guard",
new_symbol_mangling: Option<bool> = "new-symbol-mangling",
profile_generate: Option<String> = "profile-generate",
profile_use: Option<String> = "profile-use",
@@ -1057,6 +1142,7 @@ define_config! {
impl Config {
pub fn default_opts() -> Config {
let mut config = Config::default();
+ config.bypass_bootstrap_lock = false;
config.llvm_optimize = true;
config.ninja_in_file = true;
config.llvm_static_stdcpp = false;
@@ -1067,6 +1153,7 @@ impl Config {
config.docs = true;
config.docs_minification = true;
config.rust_rpath = true;
+ config.rust_strip = false;
config.channel = "dev".to_string();
config.codegen_tests = true;
config.rust_dist_src = true;
@@ -1128,6 +1215,7 @@ impl Config {
config.cmd = flags.cmd;
config.incremental = flags.incremental;
config.dry_run = if flags.dry_run { DryRun::UserSelected } else { DryRun::Disabled };
+ config.dump_bootstrap_shims = flags.dump_bootstrap_shims;
config.keep_stage = flags.keep_stage;
config.keep_stage_std = flags.keep_stage_std;
config.color = flags.color;
@@ -1135,6 +1223,7 @@ impl Config {
config.llvm_profile_use = flags.llvm_profile_use;
config.llvm_profile_generate = flags.llvm_profile_generate;
config.enable_bolt_settings = flags.enable_bolt_settings;
+ config.bypass_bootstrap_lock = flags.bypass_bootstrap_lock;
// Infer the rest of the configuration.
@@ -1264,12 +1353,56 @@ impl Config {
config.changelog_seen = toml.changelog_seen;
config.change_id = toml.change_id;
- let build = toml.build.unwrap_or_default();
- if let Some(file_build) = build.build {
+ let Build {
+ build,
+ host,
+ target,
+ build_dir,
+ cargo,
+ rustc,
+ rustfmt,
+ docs,
+ compiler_docs,
+ library_docs_private_items,
+ docs_minification,
+ submodules,
+ gdb,
+ nodejs,
+ npm,
+ python,
+ reuse,
+ locked_deps,
+ vendor,
+ full_bootstrap,
+ extended,
+ tools,
+ verbose,
+ sanitizers,
+ profiler,
+ cargo_native_static,
+ low_priority,
+ configure_args,
+ local_rebuild,
+ print_step_timings,
+ print_step_rusage,
+ check_stage,
+ doc_stage,
+ build_stage,
+ test_stage,
+ install_stage,
+ dist_stage,
+ bench_stage,
+ patch_binaries_for_nix,
+ // This field is only used by bootstrap.py
+ metrics: _,
+ android_ndk,
+ } = toml.build.unwrap_or_default();
+
+ if let Some(file_build) = build {
config.build = TargetSelection::from_user(&file_build);
};
- set(&mut config.out, flags.build_dir.or_else(|| build.build_dir.map(PathBuf::from)));
+ set(&mut config.out, flags.build_dir.or_else(|| build_dir.map(PathBuf::from)));
// NOTE: Bootstrap spawns various commands with different working directories.
// To avoid writing to random places on the file system, `config.out` needs to be an absolute path.
if !config.out.is_absolute() {
@@ -1277,7 +1410,7 @@ impl Config {
config.out = crate::utils::helpers::absolute(&config.out);
}
- config.initial_rustc = if let Some(rustc) = build.rustc {
+ config.initial_rustc = if let Some(rustc) = rustc {
if !flags.skip_stage0_validation {
config.check_build_rustc_version(&rustc);
}
@@ -1287,8 +1420,7 @@ impl Config {
config.out.join(config.build.triple).join("stage0/bin/rustc")
};
- config.initial_cargo = build
- .cargo
+ config.initial_cargo = cargo
.map(|cargo| {
t!(PathBuf::from(cargo).canonicalize(), "`initial_cargo` not found on disk")
})
@@ -1303,14 +1435,14 @@ impl Config {
config.hosts = if let Some(TargetSelectionList(arg_host)) = flags.host {
arg_host
- } else if let Some(file_host) = build.host {
+ } else if let Some(file_host) = host {
file_host.iter().map(|h| TargetSelection::from_user(h)).collect()
} else {
vec![config.build]
};
config.targets = if let Some(TargetSelectionList(arg_target)) = flags.target {
arg_target
- } else if let Some(file_target) = build.target {
+ } else if let Some(file_target) = target {
file_target.iter().map(|h| TargetSelection::from_user(h)).collect()
} else {
// If target is *not* configured, then default to the host
@@ -1318,43 +1450,44 @@ impl Config {
config.hosts.clone()
};
- config.nodejs = build.nodejs.map(PathBuf::from);
- config.npm = build.npm.map(PathBuf::from);
- config.gdb = build.gdb.map(PathBuf::from);
- config.python = build.python.map(PathBuf::from);
- config.reuse = build.reuse.map(PathBuf::from);
- config.submodules = build.submodules;
- config.android_ndk = build.android_ndk;
- set(&mut config.low_priority, build.low_priority);
- set(&mut config.compiler_docs, build.compiler_docs);
- set(&mut config.library_docs_private_items, build.library_docs_private_items);
- set(&mut config.docs_minification, build.docs_minification);
- set(&mut config.docs, build.docs);
- set(&mut config.locked_deps, build.locked_deps);
- set(&mut config.vendor, build.vendor);
- set(&mut config.full_bootstrap, build.full_bootstrap);
- set(&mut config.extended, build.extended);
- config.tools = build.tools;
- set(&mut config.verbose, build.verbose);
- set(&mut config.sanitizers, build.sanitizers);
- set(&mut config.profiler, build.profiler);
- set(&mut config.cargo_native_static, build.cargo_native_static);
- set(&mut config.configure_args, build.configure_args);
- 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);
- config.patch_binaries_for_nix = build.patch_binaries_for_nix;
+ config.nodejs = nodejs.map(PathBuf::from);
+ config.npm = npm.map(PathBuf::from);
+ config.gdb = gdb.map(PathBuf::from);
+ config.python = python.map(PathBuf::from);
+ config.reuse = reuse.map(PathBuf::from);
+ config.submodules = submodules;
+ config.android_ndk = android_ndk;
+ set(&mut config.low_priority, low_priority);
+ set(&mut config.compiler_docs, compiler_docs);
+ set(&mut config.library_docs_private_items, library_docs_private_items);
+ set(&mut config.docs_minification, docs_minification);
+ set(&mut config.docs, docs);
+ set(&mut config.locked_deps, locked_deps);
+ set(&mut config.vendor, vendor);
+ set(&mut config.full_bootstrap, full_bootstrap);
+ set(&mut config.extended, extended);
+ config.tools = tools;
+ set(&mut config.verbose, verbose);
+ set(&mut config.sanitizers, sanitizers);
+ set(&mut config.profiler, profiler);
+ set(&mut config.cargo_native_static, cargo_native_static);
+ set(&mut config.configure_args, configure_args);
+ set(&mut config.local_rebuild, local_rebuild);
+ set(&mut config.print_step_timings, print_step_timings);
+ set(&mut config.print_step_rusage, print_step_rusage);
+ config.patch_binaries_for_nix = patch_binaries_for_nix;
config.verbose = cmp::max(config.verbose, flags.verbose as usize);
if let Some(install) = toml.install {
- config.prefix = install.prefix.map(PathBuf::from);
- config.sysconfdir = install.sysconfdir.map(PathBuf::from);
- config.datadir = install.datadir.map(PathBuf::from);
- config.docdir = install.docdir.map(PathBuf::from);
- set(&mut config.bindir, install.bindir.map(PathBuf::from));
- config.libdir = install.libdir.map(PathBuf::from);
- config.mandir = install.mandir.map(PathBuf::from);
+ let Install { prefix, sysconfdir, docdir, bindir, libdir, mandir, datadir } = install;
+ config.prefix = prefix.map(PathBuf::from);
+ config.sysconfdir = sysconfdir.map(PathBuf::from);
+ config.datadir = datadir.map(PathBuf::from);
+ config.docdir = docdir.map(PathBuf::from);
+ set(&mut config.bindir, bindir.map(PathBuf::from));
+ config.libdir = libdir.map(PathBuf::from);
+ config.mandir = mandir.map(PathBuf::from);
}
// Store off these values as options because if they're not provided
@@ -1377,9 +1510,63 @@ impl Config {
let mut omit_git_hash = None;
if let Some(rust) = toml.rust {
- set(&mut config.channel, rust.channel);
-
- config.download_rustc_commit = config.download_ci_rustc_commit(rust.download_rustc);
+ let Rust {
+ optimize: optimize_toml,
+ debug: debug_toml,
+ codegen_units,
+ codegen_units_std,
+ debug_assertions: debug_assertions_toml,
+ debug_assertions_std: debug_assertions_std_toml,
+ overflow_checks: overflow_checks_toml,
+ overflow_checks_std: overflow_checks_std_toml,
+ debug_logging: debug_logging_toml,
+ debuginfo_level: debuginfo_level_toml,
+ debuginfo_level_rustc: debuginfo_level_rustc_toml,
+ debuginfo_level_std: debuginfo_level_std_toml,
+ debuginfo_level_tools: debuginfo_level_tools_toml,
+ debuginfo_level_tests: debuginfo_level_tests_toml,
+ split_debuginfo,
+ backtrace,
+ incremental,
+ parallel_compiler,
+ default_linker,
+ channel,
+ description,
+ musl_root,
+ rpath,
+ verbose_tests,
+ optimize_tests,
+ codegen_tests,
+ omit_git_hash: omit_git_hash_toml,
+ dist_src,
+ save_toolstates,
+ codegen_backends,
+ lld,
+ llvm_tools,
+ deny_warnings,
+ backtrace_on_ice,
+ verify_llvm_ir,
+ thin_lto_import_instr_limit,
+ remap_debuginfo,
+ jemalloc,
+ test_compare_mode,
+ llvm_libunwind,
+ control_flow_guard,
+ ehcont_guard,
+ new_symbol_mangling,
+ profile_generate,
+ profile_use,
+ download_rustc,
+ lto,
+ validate_mir_opts,
+ stack_protector,
+ strip,
+ lld_mode,
+ } = rust;
+
+ set(&mut config.channel, channel);
+
+ config.download_rustc_commit = config.download_ci_rustc_commit(download_rustc);
// This list is incomplete, please help by expanding it!
if config.download_rustc_commit.is_some() {
// We need the channel used by the downloaded compiler to match the one we set for rustdoc;
@@ -1396,67 +1583,77 @@ impl Config {
}
}
- debug = rust.debug;
- debug_assertions = rust.debug_assertions;
- debug_assertions_std = rust.debug_assertions_std;
- overflow_checks = rust.overflow_checks;
- overflow_checks_std = rust.overflow_checks_std;
- debug_logging = rust.debug_logging;
- debuginfo_level = rust.debuginfo_level;
- debuginfo_level_rustc = rust.debuginfo_level_rustc;
- debuginfo_level_std = rust.debuginfo_level_std;
- debuginfo_level_tools = rust.debuginfo_level_tools;
- debuginfo_level_tests = rust.debuginfo_level_tests;
-
- config.rust_split_debuginfo = rust
- .split_debuginfo
+ debug = debug_toml;
+ debug_assertions = debug_assertions_toml;
+ debug_assertions_std = debug_assertions_std_toml;
+ overflow_checks = overflow_checks_toml;
+ overflow_checks_std = overflow_checks_std_toml;
+ debug_logging = debug_logging_toml;
+ debuginfo_level = debuginfo_level_toml;
+ debuginfo_level_rustc = debuginfo_level_rustc_toml;
+ debuginfo_level_std = debuginfo_level_std_toml;
+ debuginfo_level_tools = debuginfo_level_tools_toml;
+ debuginfo_level_tests = debuginfo_level_tests_toml;
+
+ config.rust_split_debuginfo = 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;
- omit_git_hash = rust.omit_git_hash;
- config.rust_new_symbol_mangling = rust.new_symbol_mangling;
- set(&mut config.rust_optimize_tests, rust.optimize_tests);
- set(&mut config.codegen_tests, rust.codegen_tests);
- set(&mut config.rust_rpath, rust.rpath);
- set(&mut config.jemalloc, rust.jemalloc);
- set(&mut config.test_compare_mode, rust.test_compare_mode);
- set(&mut config.backtrace, rust.backtrace);
- config.description = rust.description;
- set(&mut config.rust_dist_src, rust.dist_src);
- set(&mut config.verbose_tests, rust.verbose_tests);
+ .unwrap_or(SplitDebuginfo::default_for_platform(config.build));
+ optimize = optimize_toml;
+ omit_git_hash = omit_git_hash_toml;
+ config.rust_new_symbol_mangling = new_symbol_mangling;
+ set(&mut config.rust_optimize_tests, optimize_tests);
+ set(&mut config.codegen_tests, codegen_tests);
+ set(&mut config.rust_rpath, rpath);
+ set(&mut config.rust_strip, strip);
+ config.rust_stack_protector = stack_protector;
+ set(&mut config.jemalloc, jemalloc);
+ set(&mut config.test_compare_mode, test_compare_mode);
+ set(&mut config.backtrace, backtrace);
+ config.description = description;
+ set(&mut config.rust_dist_src, dist_src);
+ set(&mut config.verbose_tests, verbose_tests);
// in the case "false" is set explicitly, do not overwrite the command line args
- if let Some(true) = rust.incremental {
+ if let Some(true) = incremental {
config.incremental = true;
}
- set(&mut config.use_lld, rust.use_lld);
- set(&mut config.lld_enabled, rust.lld);
- set(&mut config.llvm_tools_enabled, rust.llvm_tools);
- config.rustc_parallel = rust
- .parallel_compiler
- .unwrap_or(config.channel == "dev" || config.channel == "nightly");
- config.rustc_default_linker = rust.default_linker;
- config.musl_root = rust.musl_root.map(PathBuf::from);
- config.save_toolstates = rust.save_toolstates.map(PathBuf::from);
+ set(&mut config.lld_mode, lld_mode);
+ set(&mut config.lld_enabled, lld);
+
+ if matches!(config.lld_mode, LldMode::SelfContained)
+ && !config.lld_enabled
+ && flags.stage.unwrap_or(0) > 0
+ {
+ panic!(
+ "Trying to use self-contained lld as a linker, but LLD is not being added to the sysroot. Enable it with rust.lld = true."
+ );
+ }
+
+ set(&mut config.llvm_tools_enabled, llvm_tools);
+ config.rustc_parallel =
+ parallel_compiler.unwrap_or(config.channel == "dev" || config.channel == "nightly");
+ config.rustc_default_linker = default_linker;
+ config.musl_root = musl_root.map(PathBuf::from);
+ config.save_toolstates = save_toolstates.map(PathBuf::from);
set(
&mut config.deny_warnings,
match flags.warnings {
Warnings::Deny => Some(true),
Warnings::Warn => Some(false),
- Warnings::Default => rust.deny_warnings,
+ Warnings::Default => deny_warnings,
},
);
- set(&mut config.backtrace_on_ice, rust.backtrace_on_ice);
- set(&mut config.rust_verify_llvm_ir, rust.verify_llvm_ir);
- config.rust_thin_lto_import_instr_limit = rust.thin_lto_import_instr_limit;
- set(&mut config.rust_remap_debuginfo, rust.remap_debuginfo);
- set(&mut config.control_flow_guard, rust.control_flow_guard);
- config.llvm_libunwind_default = rust
- .llvm_libunwind
- .map(|v| v.parse().expect("failed to parse rust.llvm-libunwind"));
-
- if let Some(ref backends) = rust.codegen_backends {
+ set(&mut config.backtrace_on_ice, backtrace_on_ice);
+ set(&mut config.rust_verify_llvm_ir, verify_llvm_ir);
+ config.rust_thin_lto_import_instr_limit = thin_lto_import_instr_limit;
+ set(&mut config.rust_remap_debuginfo, remap_debuginfo);
+ set(&mut config.control_flow_guard, control_flow_guard);
+ set(&mut config.ehcont_guard, ehcont_guard);
+ config.llvm_libunwind_default =
+ llvm_libunwind.map(|v| v.parse().expect("failed to parse rust.llvm-libunwind"));
+
+ if let Some(ref backends) = codegen_backends {
let available_backends = vec!["llvm", "cranelift", "gcc"];
config.rust_codegen_backends = backends.iter().map(|s| {
@@ -1474,16 +1671,13 @@ impl Config {
}).collect();
}
- config.rust_codegen_units = rust.codegen_units.map(threads_from_config);
- config.rust_codegen_units_std = rust.codegen_units_std.map(threads_from_config);
- config.rust_profile_use = flags.rust_profile_use.or(rust.profile_use);
- config.rust_profile_generate = flags.rust_profile_generate.or(rust.profile_generate);
- config.rust_lto = rust
- .lto
- .as_deref()
- .map(|value| RustcLto::from_str(value).unwrap())
- .unwrap_or_default();
- config.rust_validate_mir_opts = rust.validate_mir_opts;
+ config.rust_codegen_units = codegen_units.map(threads_from_config);
+ config.rust_codegen_units_std = codegen_units_std.map(threads_from_config);
+ config.rust_profile_use = flags.rust_profile_use.or(profile_use);
+ config.rust_profile_generate = flags.rust_profile_generate.or(profile_generate);
+ config.rust_lto =
+ lto.as_deref().map(|value| RustcLto::from_str(value).unwrap()).unwrap_or_default();
+ config.rust_validate_mir_opts = validate_mir_opts;
} else {
config.rust_profile_use = flags.rust_profile_use;
config.rust_profile_generate = flags.rust_profile_generate;
@@ -1497,43 +1691,71 @@ impl Config {
config.rust_info = GitInfo::new(config.omit_git_hash, &config.src);
if let Some(llvm) = toml.llvm {
- match llvm.ccache {
+ let Llvm {
+ optimize: optimize_toml,
+ thin_lto,
+ release_debuginfo,
+ assertions,
+ tests,
+ plugins,
+ ccache,
+ static_libstdcpp,
+ ninja,
+ targets,
+ experimental_targets,
+ link_jobs,
+ link_shared,
+ version_suffix,
+ clang_cl,
+ cflags,
+ cxxflags,
+ ldflags,
+ use_libcxx,
+ use_linker,
+ allow_old_toolchain,
+ polly,
+ clang,
+ enable_warnings,
+ download_ci_llvm,
+ build_config,
+ } = llvm;
+ match ccache {
Some(StringOrBool::String(ref s)) => config.ccache = Some(s.to_string()),
Some(StringOrBool::Bool(true)) => {
config.ccache = Some("ccache".to_string());
}
Some(StringOrBool::Bool(false)) | None => {}
}
- set(&mut config.ninja_in_file, llvm.ninja);
- llvm_assertions = llvm.assertions;
- llvm_tests = llvm.tests;
- llvm_plugins = llvm.plugins;
- set(&mut config.llvm_optimize, llvm.optimize);
- set(&mut config.llvm_thin_lto, llvm.thin_lto);
- set(&mut config.llvm_release_debuginfo, llvm.release_debuginfo);
- set(&mut config.llvm_static_stdcpp, llvm.static_libstdcpp);
- if let Some(v) = llvm.link_shared {
+ set(&mut config.ninja_in_file, ninja);
+ llvm_assertions = assertions;
+ llvm_tests = tests;
+ llvm_plugins = plugins;
+ set(&mut config.llvm_optimize, optimize_toml);
+ set(&mut config.llvm_thin_lto, thin_lto);
+ set(&mut config.llvm_release_debuginfo, release_debuginfo);
+ set(&mut config.llvm_static_stdcpp, static_libstdcpp);
+ if let Some(v) = 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;
- config.llvm_version_suffix = llvm.version_suffix.clone();
- config.llvm_clang_cl = llvm.clang_cl.clone();
-
- config.llvm_cflags = llvm.cflags.clone();
- config.llvm_cxxflags = llvm.cxxflags.clone();
- config.llvm_ldflags = llvm.ldflags.clone();
- set(&mut config.llvm_use_libcxx, llvm.use_libcxx);
- config.llvm_use_linker = llvm.use_linker.clone();
- config.llvm_allow_old_toolchain = llvm.allow_old_toolchain.unwrap_or(false);
- config.llvm_polly = llvm.polly.unwrap_or(false);
- config.llvm_clang = llvm.clang.unwrap_or(false);
- config.llvm_enable_warnings = llvm.enable_warnings.unwrap_or(false);
- config.llvm_build_config = llvm.build_config.clone().unwrap_or(Default::default());
+ config.llvm_targets = targets.clone();
+ config.llvm_experimental_targets = experimental_targets.clone();
+ config.llvm_link_jobs = link_jobs;
+ config.llvm_version_suffix = version_suffix.clone();
+ config.llvm_clang_cl = clang_cl.clone();
+
+ config.llvm_cflags = cflags.clone();
+ config.llvm_cxxflags = cxxflags.clone();
+ config.llvm_ldflags = ldflags.clone();
+ set(&mut config.llvm_use_libcxx, use_libcxx);
+ config.llvm_use_linker = use_linker.clone();
+ config.llvm_allow_old_toolchain = allow_old_toolchain.unwrap_or(false);
+ config.llvm_polly = polly.unwrap_or(false);
+ config.llvm_clang = clang.unwrap_or(false);
+ config.llvm_enable_warnings = enable_warnings.unwrap_or(false);
+ config.llvm_build_config = build_config.clone().unwrap_or(Default::default());
let asserts = llvm_assertions.unwrap_or(false);
- config.llvm_from_ci = config.parse_download_ci_llvm(llvm.download_ci_llvm, asserts);
+ config.llvm_from_ci = config.parse_download_ci_llvm(download_ci_llvm, asserts);
if config.llvm_from_ci {
// None of the LLVM options, except assertions, are supported
@@ -1542,39 +1764,38 @@ impl Config {
// explicitly set. The defaults and CI defaults don't
// necessarily match but forcing people to match (somewhat
// arbitrary) CI configuration locally seems bad/hard.
- check_ci_llvm!(llvm.optimize);
- check_ci_llvm!(llvm.thin_lto);
- check_ci_llvm!(llvm.release_debuginfo);
+ check_ci_llvm!(optimize_toml);
+ check_ci_llvm!(thin_lto);
+ check_ci_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.experimental_targets);
- check_ci_llvm!(llvm.link_jobs);
- check_ci_llvm!(llvm.clang_cl);
- check_ci_llvm!(llvm.version_suffix);
- check_ci_llvm!(llvm.cflags);
- check_ci_llvm!(llvm.cxxflags);
- check_ci_llvm!(llvm.ldflags);
- check_ci_llvm!(llvm.use_libcxx);
- check_ci_llvm!(llvm.use_linker);
- check_ci_llvm!(llvm.allow_old_toolchain);
- check_ci_llvm!(llvm.polly);
- check_ci_llvm!(llvm.clang);
- check_ci_llvm!(llvm.build_config);
- check_ci_llvm!(llvm.plugins);
+ check_ci_llvm!(link_shared);
+ check_ci_llvm!(static_libstdcpp);
+ check_ci_llvm!(targets);
+ check_ci_llvm!(experimental_targets);
+ check_ci_llvm!(link_jobs);
+ check_ci_llvm!(clang_cl);
+ check_ci_llvm!(version_suffix);
+ check_ci_llvm!(cflags);
+ check_ci_llvm!(cxxflags);
+ check_ci_llvm!(ldflags);
+ check_ci_llvm!(use_libcxx);
+ check_ci_llvm!(use_linker);
+ check_ci_llvm!(allow_old_toolchain);
+ check_ci_llvm!(polly);
+ check_ci_llvm!(clang);
+ check_ci_llvm!(build_config);
+ check_ci_llvm!(plugins);
}
// 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 config.llvm_thin_lto && 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.set(Some(true));
}
} else {
- config.llvm_from_ci = config.channel == "dev"
- && crate::core::build_steps::llvm::is_ci_llvm_available(&config, false);
+ config.llvm_from_ci = config.parse_download_ci_llvm(None, false);
}
if let Some(t) = toml.target {
@@ -1632,17 +1853,26 @@ impl Config {
build_target.llvm_filecheck = Some(ci_llvm_bin.join(exe("FileCheck", config.build)));
}
- if let Some(t) = toml.dist {
- config.dist_sign_folder = t.sign_folder.map(PathBuf::from);
- config.dist_upload_addr = t.upload_addr;
- config.dist_compression_formats = t.compression_formats;
- set(&mut config.dist_compression_profile, t.compression_profile);
- set(&mut config.rust_dist_src, t.src_tarball);
- set(&mut config.missing_tools, t.missing_tools);
- set(&mut config.dist_include_mingw_linker, t.include_mingw_linker)
+ if let Some(dist) = toml.dist {
+ let Dist {
+ sign_folder,
+ upload_addr,
+ src_tarball,
+ missing_tools,
+ compression_formats,
+ compression_profile,
+ include_mingw_linker,
+ } = dist;
+ config.dist_sign_folder = sign_folder.map(PathBuf::from);
+ config.dist_upload_addr = upload_addr;
+ config.dist_compression_formats = compression_formats;
+ set(&mut config.dist_compression_profile, compression_profile);
+ set(&mut config.rust_dist_src, src_tarball);
+ set(&mut config.missing_tools, missing_tools);
+ set(&mut config.dist_include_mingw_linker, include_mingw_linker)
}
- if let Some(r) = build.rustfmt {
+ if let Some(r) = rustfmt {
*config.initial_rustfmt.borrow_mut() = if r.exists() {
RustfmtState::SystemToolchain(r)
} else {
@@ -1683,20 +1913,20 @@ impl Config {
let download_rustc = config.download_rustc_commit.is_some();
// See https://github.com/rust-lang/compiler-team/issues/326
config.stage = match config.cmd {
- Subcommand::Check { .. } => flags.stage.or(build.check_stage).unwrap_or(0),
+ Subcommand::Check { .. } => flags.stage.or(check_stage).unwrap_or(0),
// `download-rustc` only has a speed-up for stage2 builds. Default to stage2 unless explicitly overridden.
Subcommand::Doc { .. } => {
- flags.stage.or(build.doc_stage).unwrap_or(if download_rustc { 2 } else { 0 })
+ flags.stage.or(doc_stage).unwrap_or(if download_rustc { 2 } else { 0 })
}
Subcommand::Build { .. } => {
- flags.stage.or(build.build_stage).unwrap_or(if download_rustc { 2 } else { 1 })
+ flags.stage.or(build_stage).unwrap_or(if download_rustc { 2 } else { 1 })
}
Subcommand::Test { .. } => {
- flags.stage.or(build.test_stage).unwrap_or(if download_rustc { 2 } else { 1 })
+ flags.stage.or(test_stage).unwrap_or(if download_rustc { 2 } else { 1 })
}
- Subcommand::Bench { .. } => flags.stage.or(build.bench_stage).unwrap_or(2),
- Subcommand::Dist { .. } => flags.stage.or(build.dist_stage).unwrap_or(2),
- Subcommand::Install { .. } => flags.stage.or(build.install_stage).unwrap_or(2),
+ Subcommand::Bench { .. } => flags.stage.or(bench_stage).unwrap_or(2),
+ Subcommand::Dist { .. } => flags.stage.or(dist_stage).unwrap_or(2),
+ Subcommand::Install { .. } => flags.stage.or(install_stage).unwrap_or(2),
// These are all bootstrap tools, which don't depend on the compiler.
// The stage we pass shouldn't matter, but use 0 just in case.
Subcommand::Clean { .. }
@@ -1738,7 +1968,7 @@ impl Config {
config
}
- pub(crate) fn dry_run(&self) -> bool {
+ pub fn dry_run(&self) -> bool {
match self.dry_run {
DryRun::Disabled => false,
DryRun::SelfCheck | DryRun::UserSelected => true,
@@ -1904,7 +2134,7 @@ impl Config {
}
pub(crate) fn download_rustc_commit(&self) -> Option<&str> {
- static DOWNLOAD_RUSTC: OnceCell<Option<String>> = OnceCell::new();
+ static DOWNLOAD_RUSTC: OnceLock<Option<String>> = OnceLock::new();
if self.dry_run() && DOWNLOAD_RUSTC.get().is_none() {
// avoid trying to actually download the commit
return self.download_rustc_commit.as_deref();
@@ -2110,27 +2340,30 @@ impl Config {
download_ci_llvm: Option<StringOrBool>,
asserts: bool,
) -> bool {
+ let if_unchanged = || {
+ // Git is needed to track modifications here, but tarball source is not available.
+ // If not modified here or built through tarball source, we maintain consistency
+ // with '"if available"'.
+ if !self.rust_info.is_from_tarball()
+ && self
+ .last_modified_commit(&["src/llvm-project"], "download-ci-llvm", true)
+ .is_none()
+ {
+ // there are some untracked changes in the the given paths.
+ false
+ } else {
+ llvm::is_ci_llvm_available(&self, asserts)
+ }
+ };
match download_ci_llvm {
- None => self.channel == "dev" && llvm::is_ci_llvm_available(&self, asserts),
+ None => self.channel == "dev" && if_unchanged(),
Some(StringOrBool::Bool(b)) => b,
+ // FIXME: "if-available" is deprecated. Remove this block later (around mid 2024)
+ // to not break builds between the recent-to-old checkouts.
Some(StringOrBool::String(s)) if s == "if-available" => {
llvm::is_ci_llvm_available(&self, asserts)
}
- Some(StringOrBool::String(s)) if s == "if-unchanged" => {
- // Git is needed to track modifications here, but tarball source is not available.
- // If not modified here or built through tarball source, we maintain consistency
- // with '"if available"'.
- if !self.rust_info.is_from_tarball()
- && self
- .last_modified_commit(&["src/llvm-project"], "download-ci-llvm", true)
- .is_none()
- {
- // there are some untracked changes in the the given paths.
- false
- } else {
- llvm::is_ci_llvm_available(&self, asserts)
- }
- }
+ Some(StringOrBool::String(s)) if s == "if-unchanged" => if_unchanged(),
Some(StringOrBool::String(other)) => {
panic!("unrecognized option for download-ci-llvm: {:?}", other)
}
diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs
index 2a301007a..0b1372608 100644
--- a/src/bootstrap/src/core/config/flags.rs
+++ b/src/bootstrap/src/core/config/flags.rs
@@ -85,6 +85,9 @@ pub struct Flags {
#[arg(global(true), long)]
/// dry run; don't build anything
pub dry_run: bool,
+ /// Indicates whether to dump the work done from bootstrap shims
+ #[arg(global(true), long)]
+ pub dump_bootstrap_shims: bool,
#[arg(global(true), value_hint = clap::ValueHint::Other, long, value_name = "N")]
/// stage to build (indicates compiler to use/test, e.g., stage 0 uses the
/// bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)
@@ -133,6 +136,13 @@ pub struct Flags {
/// whether to use color in cargo and rustc output
pub color: Color,
+ #[arg(global(true), long)]
+ /// Bootstrap uses this value to decide whether it should bypass locking the build process.
+ /// This is rarely needed (e.g., compiling the std library for different targets in parallel).
+ ///
+ /// Unless you know exactly what you are doing, you probably don't need this.
+ pub bypass_bootstrap_lock: bool,
+
/// whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml
#[arg(global(true), long, value_name = "VALUE")]
pub llvm_skip_rebuild: Option<bool>,
diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs
index 3327aed96..ec404ab85 100644
--- a/src/bootstrap/src/core/download.rs
+++ b/src/bootstrap/src/core/download.rs
@@ -5,18 +5,18 @@ use std::{
io::{BufRead, BufReader, BufWriter, ErrorKind, Write},
path::{Path, PathBuf},
process::{Command, Stdio},
+ sync::OnceLock,
};
use build_helper::ci::CiEnv;
-use once_cell::sync::OnceCell;
use xz2::bufread::XzDecoder;
-use crate::core::build_steps::llvm::detect_llvm_sha;
use crate::core::config::RustfmtMetadata;
use crate::utils::helpers::{check_run, exe, program_out_of_date};
+use crate::{core::build_steps::llvm::detect_llvm_sha, utils::helpers::hex_encode};
use crate::{t, Config};
-static SHOULD_FIX_BINS_AND_DYLIBS: OnceCell<bool> = OnceCell::new();
+static SHOULD_FIX_BINS_AND_DYLIBS: OnceLock<bool> = OnceLock::new();
/// `Config::try_run` wrapper for this module to avoid warnings on `try_run`, since we don't have access to a `builder` yet.
fn try_run(config: &Config, cmd: &mut Command) -> Result<(), ()> {
@@ -131,7 +131,7 @@ impl Config {
println!("attempting to patch {}", fname.display());
// Only build `.nix-deps` once.
- static NIX_DEPS_DIR: OnceCell<PathBuf> = OnceCell::new();
+ static NIX_DEPS_DIR: OnceLock<PathBuf> = OnceLock::new();
let mut nix_build_succeeded = true;
let nix_deps_dir = NIX_DEPS_DIR.get_or_init(|| {
// Run `nix-build` to "build" each dependency (which will likely reuse
@@ -208,7 +208,10 @@ impl Config {
Some(other) => panic!("unsupported protocol {other} in {url}"),
None => panic!("no protocol in {url}"),
}
- t!(std::fs::rename(&tempfile, dest_path));
+ t!(
+ std::fs::rename(&tempfile, dest_path),
+ format!("failed to rename {tempfile:?} to {dest_path:?}")
+ );
}
fn download_http_with_retries(&self, tempfile: &Path, url: &str, help_on_error: &str) {
@@ -342,7 +345,7 @@ impl Config {
reader.consume(l);
}
- let checksum = hex::encode(hasher.finalize().as_slice());
+ let checksum = hex_encode(hasher.finalize().as_slice());
let verified = checksum == expected;
if !verified {
@@ -375,6 +378,32 @@ enum DownloadSource {
/// Functions that are only ever called once, but named for clarify and to avoid thousand-line functions.
impl Config {
+ pub(crate) fn download_clippy(&self) -> PathBuf {
+ self.verbose("downloading stage0 clippy artifacts");
+
+ let date = &self.stage0_metadata.compiler.date;
+ let version = &self.stage0_metadata.compiler.version;
+ let host = self.build;
+
+ let bin_root = self.out.join(host.triple).join("stage0");
+ let clippy_stamp = bin_root.join(".clippy-stamp");
+ let cargo_clippy = bin_root.join("bin").join(exe("cargo-clippy", host));
+ if cargo_clippy.exists() && !program_out_of_date(&clippy_stamp, &date) {
+ return cargo_clippy;
+ }
+
+ let filename = format!("clippy-{version}-{host}.tar.xz");
+ self.download_component(DownloadSource::Dist, filename, "clippy-preview", date, "stage0");
+ if self.should_fix_bins_and_dylibs() {
+ self.fix_bin_or_dylib(&cargo_clippy);
+ self.fix_bin_or_dylib(&cargo_clippy.with_file_name(exe("clippy-driver", host)));
+ }
+
+ cargo_clippy
+ }
+
+ /// NOTE: rustfmt is a completely different toolchain than the bootstrap compiler, so it can't
+ /// reuse target directories or artifacts
pub(crate) fn maybe_download_rustfmt(&self) -> Option<PathBuf> {
let RustfmtMetadata { date, version } = self.stage0_metadata.rustfmt.as_ref()?;
let channel = format!("{version}-{date}");
@@ -544,6 +573,10 @@ impl Config {
key: &str,
destination: &str,
) {
+ if self.dry_run() {
+ return;
+ }
+
let cache_dst = self.out.join("cache");
let cache_dir = cache_dst.join(key);
if !cache_dir.exists() {
diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs
index eec3be66a..9101d94ea 100644
--- a/src/bootstrap/src/core/sanity.rs
+++ b/src/bootstrap/src/core/sanity.rs
@@ -237,7 +237,7 @@ than building it.
}
}
- if need_cmake && target.contains("msvc") {
+ if need_cmake && target.is_msvc() {
// There are three builds of cmake on windows: MSVC, MinGW, and
// Cygwin. The Cygwin build does not have generators for Visual
// Studio, so detect that here and error.
diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs
index 33b8f1a7c..871318de5 100644
--- a/src/bootstrap/src/lib.rs
+++ b/src/bootstrap/src/lib.rs
@@ -25,18 +25,20 @@ use std::io;
use std::path::{Path, PathBuf};
use std::process::{Command, Output, Stdio};
use std::str;
+use std::sync::OnceLock;
use build_helper::ci::{gha, CiEnv};
use build_helper::exit;
use build_helper::util::fail;
use filetime::FileTime;
-use once_cell::sync::OnceCell;
+use sha2::digest::Digest;
use termcolor::{ColorChoice, StandardStream, WriteColor};
use utils::channel::GitInfo;
+use utils::helpers::hex_encode;
use crate::core::builder;
use crate::core::builder::Kind;
-use crate::core::config::flags;
+use crate::core::config::{flags, LldMode};
use crate::core::config::{DryRun, Target};
use crate::core::config::{LlvmLibunwind, TargetSelection};
use crate::utils::cache::{Interned, INTERNER};
@@ -46,9 +48,10 @@ use crate::utils::helpers::{self, dir_is_empty, exe, libdir, mtime, output, syml
mod core;
mod utils;
-pub use crate::core::builder::PathSet;
-pub use crate::core::config::flags::Subcommand;
-pub use crate::core::config::Config;
+pub use core::builder::PathSet;
+pub use core::config::flags::Subcommand;
+pub use core::config::Config;
+pub use utils::change_tracker::{find_recent_config_change_ids, CONFIG_CHANGE_HISTORY};
const LLVM_TOOLS: &[&str] = &[
"llvm-cov", // used to generate coverage report
@@ -69,36 +72,25 @@ const LLVM_TOOLS: &[&str] = &[
/// LLD file names for all flavors.
const LLD_FILE_NAMES: &[&str] = &["ld.lld", "ld64.lld", "lld-link", "wasm-ld"];
-/// Keeps track of major changes made to the bootstrap configuration.
-///
-/// These values also represent the IDs of the PRs that caused major changes.
-/// You can visit `https://github.com/rust-lang/rust/pull/{any-id-from-the-list}` to
-/// check for more details regarding each change.
-///
-/// If you make any major changes (such as adding new values or changing default values),
-/// please ensure that the associated PR ID is added to the end of this list.
-/// This is necessary because the list must be sorted by the merge date.
-pub const CONFIG_CHANGE_HISTORY: &[usize] = &[115898, 116998, 117435, 116881];
-
/// Extra --check-cfg to add when building
/// (Mode restriction, config name, config values (if any))
const EXTRA_CHECK_CFGS: &[(Option<Mode>, &str, Option<&[&'static str]>)] = &[
(None, "bootstrap", None),
(Some(Mode::Rustc), "parallel_compiler", None),
(Some(Mode::ToolRustc), "parallel_compiler", None),
+ (Some(Mode::ToolRustc), "rust_analyzer", None),
+ (Some(Mode::ToolStd), "rust_analyzer", None),
(Some(Mode::Codegen), "parallel_compiler", None),
(Some(Mode::Std), "stdarch_intel_sde", None),
(Some(Mode::Std), "no_fp_fmt_parse", None),
(Some(Mode::Std), "no_global_oom_handling", None),
(Some(Mode::Std), "no_rc", None),
(Some(Mode::Std), "no_sync", None),
- (Some(Mode::Std), "freebsd12", None),
- (Some(Mode::Std), "freebsd13", None),
(Some(Mode::Std), "backtrace_in_libstd", None),
/* Extra values not defined in the built-in targets yet, but used in std */
(Some(Mode::Std), "target_env", Some(&["libnx"])),
// (Some(Mode::Std), "target_os", Some(&[])),
- (Some(Mode::Std), "target_arch", Some(&["asmjs", "spirv", "nvptx", "xtensa"])),
+ (Some(Mode::Std), "target_arch", Some(&["spirv", "nvptx", "xtensa"])),
/* Extra names used by dependencies */
// FIXME: Used by serde_json, but we should not be triggering on external dependencies.
(Some(Mode::Rustc), "no_btreemap_remove_entry", None),
@@ -873,7 +865,7 @@ impl Build {
}
} else {
let base = self.llvm_out(target).join("build");
- let base = if !self.ninja() && target.contains("msvc") {
+ let base = if !self.ninja() && target.is_msvc() {
if self.config.llvm_optimize {
if self.config.llvm_release_debuginfo {
base.join("RelWithDebInfo")
@@ -915,7 +907,7 @@ impl Build {
/// Returns the sysroot of the snapshot compiler.
fn rustc_snapshot_sysroot(&self) -> &Path {
- static SYSROOT_CACHE: OnceCell<PathBuf> = once_cell::sync::OnceCell::new();
+ static SYSROOT_CACHE: OnceLock<PathBuf> = OnceLock::new();
SYSROOT_CACHE.get_or_init(|| {
let mut rustc = Command::new(&self.initial_rustc);
rustc.args(&["--print", "sysroot"]);
@@ -1266,35 +1258,27 @@ impl Build {
Some(self.cxx.borrow()[&target].path().into())
} else if target != self.config.build
&& helpers::use_host_linker(target)
- && !target.contains("msvc")
+ && !target.is_msvc()
{
Some(self.cc(target))
- } else if self.config.use_lld && !self.is_fuse_ld_lld(target) && self.build == target {
- Some(self.initial_lld.clone())
+ } else if self.config.lld_mode.is_used()
+ && self.is_lld_direct_linker(target)
+ && self.build == target
+ {
+ match self.config.lld_mode {
+ LldMode::SelfContained => Some(self.initial_lld.clone()),
+ LldMode::External => Some("lld".into()),
+ LldMode::Unused => None,
+ }
} else {
None
}
}
- // LLD is used through `-fuse-ld=lld` rather than directly.
+ // Is LLD configured directly through `-Clinker`?
// Only MSVC targets use LLD directly at the moment.
- fn is_fuse_ld_lld(&self, target: TargetSelection) -> bool {
- self.config.use_lld && !target.contains("msvc")
- }
-
- fn lld_flags(&self, target: TargetSelection) -> impl Iterator<Item = String> {
- let mut options = [None, None];
-
- if self.config.use_lld {
- if self.is_fuse_ld_lld(target) {
- options[0] = Some("-Clink-arg=-fuse-ld=lld".to_string());
- }
-
- let no_threads = helpers::lld_flag_no_threads(target.contains("windows"));
- options[1] = Some(format!("-Clink-arg=-Wl,{no_threads}"));
- }
-
- IntoIterator::into_iter(options).flatten()
+ fn is_lld_direct_linker(&self, target: TargetSelection) -> bool {
+ target.is_msvc()
}
/// Returns if this target should statically link the C runtime, if specified
@@ -1775,7 +1759,7 @@ to download LLVM rather than building it.
// In these cases we automatically enable Ninja if we find it in the
// environment.
if !self.config.ninja_in_file
- && self.config.build.contains("msvc")
+ && self.config.build.is_msvc()
&& cmd_finder.maybe_have("ninja").is_some()
{
return true;
@@ -1849,26 +1833,63 @@ fn envify(s: &str) -> String {
.collect()
}
-pub fn find_recent_config_change_ids(current_id: usize) -> Vec<usize> {
- if !CONFIG_CHANGE_HISTORY.contains(&current_id) {
- // If the current change-id is greater than the most recent one, return
- // an empty list (it may be due to switching from a recent branch to an
- // older one); otherwise, return the full list (assuming the user provided
- // the incorrect change-id by accident).
- if let Some(max_id) = CONFIG_CHANGE_HISTORY.iter().max() {
- if &current_id > max_id {
- return Vec::new();
- }
- }
+/// Computes a hash representing the state of a repository/submodule and additional input.
+///
+/// It uses `git diff` for the actual changes, and `git status` for including the untracked
+/// files in the specified directory. The additional input is also incorporated into the
+/// computation of the hash.
+///
+/// # Parameters
+///
+/// - `dir`: A reference to the directory path of the target repository/submodule.
+/// - `additional_input`: An additional input to be included in the hash.
+///
+/// # Panics
+///
+/// In case of errors during `git` command execution (e.g., in tarball sources), default values
+/// are used to prevent panics.
+pub fn generate_smart_stamp_hash(dir: &Path, additional_input: &str) -> String {
+ let diff = Command::new("git")
+ .current_dir(dir)
+ .arg("diff")
+ .output()
+ .map(|o| String::from_utf8(o.stdout).unwrap_or_default())
+ .unwrap_or_default();
+
+ let status = Command::new("git")
+ .current_dir(dir)
+ .arg("status")
+ .arg("--porcelain")
+ .arg("-z")
+ .arg("--untracked-files=normal")
+ .output()
+ .map(|o| String::from_utf8(o.stdout).unwrap_or_default())
+ .unwrap_or_default();
+
+ let mut hasher = sha2::Sha256::new();
+
+ hasher.update(diff);
+ hasher.update(status);
+ hasher.update(additional_input);
+
+ hex_encode(hasher.finalize().as_slice())
+}
- return CONFIG_CHANGE_HISTORY.to_vec();
- }
+/// Ensures that the behavior dump directory is properly initialized.
+pub fn prepare_behaviour_dump_dir(build: &Build) {
+ static INITIALIZED: OnceLock<bool> = OnceLock::new();
- let index = CONFIG_CHANGE_HISTORY.iter().position(|&id| id == current_id).unwrap();
+ let dump_path = build.out.join("bootstrap-shims-dump");
- CONFIG_CHANGE_HISTORY
- .iter()
- .skip(index + 1) // Skip the current_id and IDs before it
- .cloned()
- .collect()
+ let initialized = INITIALIZED.get().unwrap_or_else(|| &false);
+ if !initialized {
+ // clear old dumps
+ if dump_path.exists() {
+ t!(fs::remove_dir_all(&dump_path));
+ }
+
+ t!(fs::create_dir_all(&dump_path));
+
+ t!(INITIALIZED.set(true));
+ }
}
diff --git a/src/bootstrap/src/tests/builder.rs b/src/bootstrap/src/tests/builder.rs
index 96139f7b0..700ebcf5e 100644
--- a/src/bootstrap/src/tests/builder.rs
+++ b/src/bootstrap/src/tests/builder.rs
@@ -1,6 +1,6 @@
use super::*;
-use crate::core::config::{Config, DryRun, TargetSelection};
use crate::core::build_steps::doc::DocumentationFormat;
+use crate::core::config::{Config, DryRun, TargetSelection};
use std::thread;
fn configure(cmd: &str, host: &[&str], target: &[&str]) -> Config {
@@ -156,22 +156,6 @@ fn alias_and_path_for_library() {
assert_eq!(first(cache.all::<doc::Std>()), &[doc_std!(A => A, stage = 0)]);
}
-#[test]
-fn test_beta_rev_parsing() {
- use crate::utils::helpers::extract_beta_rev;
-
- // single digit revision
- assert_eq!(extract_beta_rev("1.99.9-beta.7 (xxxxxx)"), Some("7".to_string()));
- // multiple digits
- assert_eq!(extract_beta_rev("1.99.9-beta.777 (xxxxxx)"), Some("777".to_string()));
- // nightly channel (no beta revision)
- assert_eq!(extract_beta_rev("1.99.9-nightly (xxxxxx)"), None);
- // stable channel (no beta revision)
- assert_eq!(extract_beta_rev("1.99.9 (xxxxxxx)"), None);
- // invalid string
- assert_eq!(extract_beta_rev("invalid"), None);
-}
-
mod defaults {
use super::{configure, first, run_build};
use crate::core::builder::*;
diff --git a/src/bootstrap/src/tests/config.rs b/src/bootstrap/src/tests/config.rs
index 59bd52a94..6f4323438 100644
--- a/src/bootstrap/src/tests/config.rs
+++ b/src/bootstrap/src/tests/config.rs
@@ -1,5 +1,5 @@
-use crate::core::config::TomlConfig;
use super::{Config, Flags};
+use crate::core::config::{LldMode, TomlConfig};
use clap::CommandFactory;
use serde::Deserialize;
@@ -24,19 +24,19 @@ fn download_ci_llvm() {
}
let parse_llvm = |s| parse(s).llvm_from_ci;
- let if_available = parse_llvm("llvm.download-ci-llvm = \"if-available\"");
+ let if_unchanged = parse_llvm("llvm.download-ci-llvm = \"if-unchanged\"");
assert!(parse_llvm("llvm.download-ci-llvm = true"));
assert!(!parse_llvm("llvm.download-ci-llvm = false"));
- assert_eq!(parse_llvm(""), if_available);
- assert_eq!(parse_llvm("rust.channel = \"dev\""), if_available);
+ assert_eq!(parse_llvm(""), if_unchanged);
+ assert_eq!(parse_llvm("rust.channel = \"dev\""), if_unchanged);
assert!(!parse_llvm("rust.channel = \"stable\""));
- assert!(parse_llvm("build.build = \"x86_64-unknown-linux-gnu\""));
- assert!(parse_llvm(
- "llvm.assertions = true \r\n build.build = \"x86_64-unknown-linux-gnu\" \r\n llvm.download-ci-llvm = \"if-available\""
- ));
+ assert_eq!(parse_llvm("build.build = \"x86_64-unknown-linux-gnu\""), if_unchanged);
+ assert_eq!(parse_llvm(
+ "llvm.assertions = true \r\n build.build = \"x86_64-unknown-linux-gnu\" \r\n llvm.download-ci-llvm = \"if-unchanged\""
+ ), if_unchanged);
assert!(!parse_llvm(
- "llvm.assertions = true \r\n build.build = \"aarch64-apple-darwin\" \r\n llvm.download-ci-llvm = \"if-available\""
+ "llvm.assertions = true \r\n build.build = \"aarch64-apple-darwin\" \r\n llvm.download-ci-llvm = \"if-unchanged\""
));
}
@@ -217,3 +217,12 @@ fn verify_file_integrity() {
remove_file(tempfile).unwrap();
}
+
+#[test]
+fn rust_lld() {
+ assert!(matches!(parse("").lld_mode, LldMode::Unused));
+ assert!(matches!(parse("rust.use-lld = \"self-contained\"").lld_mode, LldMode::SelfContained));
+ assert!(matches!(parse("rust.use-lld = \"external\"").lld_mode, LldMode::External));
+ assert!(matches!(parse("rust.use-lld = true").lld_mode, LldMode::External));
+ assert!(matches!(parse("rust.use-lld = false").lld_mode, LldMode::Unused));
+}
diff --git a/src/bootstrap/src/tests/helpers.rs b/src/bootstrap/src/tests/helpers.rs
new file mode 100644
index 000000000..afe18aeba
--- /dev/null
+++ b/src/bootstrap/src/tests/helpers.rs
@@ -0,0 +1,59 @@
+use crate::utils::helpers::{extract_beta_rev, hex_encode, make};
+use std::path::PathBuf;
+
+#[test]
+fn test_make() {
+ for (host, make_path) in vec![
+ ("dragonfly", PathBuf::from("gmake")),
+ ("netbsd", PathBuf::from("gmake")),
+ ("freebsd", PathBuf::from("gmake")),
+ ("openbsd", PathBuf::from("gmake")),
+ ("linux", PathBuf::from("make")),
+ // for checking the default
+ ("_", PathBuf::from("make")),
+ ] {
+ assert_eq!(make(host), make_path);
+ }
+}
+
+#[cfg(unix)]
+#[test]
+fn test_absolute_unix() {
+ use crate::utils::helpers::absolute_unix;
+
+ // Test an absolute path
+ let path = PathBuf::from("/home/user/file.txt");
+ assert_eq!(absolute_unix(&path).unwrap(), PathBuf::from("/home/user/file.txt"));
+
+ // Test an absolute path with double leading slashes
+ let path = PathBuf::from("//root//file.txt");
+ assert_eq!(absolute_unix(&path).unwrap(), PathBuf::from("//root/file.txt"));
+
+ // Test a relative path
+ let path = PathBuf::from("relative/path");
+ assert_eq!(
+ absolute_unix(&path).unwrap(),
+ std::env::current_dir().unwrap().join("relative/path")
+ );
+}
+
+#[test]
+fn test_beta_rev_parsing() {
+ // single digit revision
+ assert_eq!(extract_beta_rev("1.99.9-beta.7 (xxxxxx)"), Some("7".to_string()));
+ // multiple digits
+ assert_eq!(extract_beta_rev("1.99.9-beta.777 (xxxxxx)"), Some("777".to_string()));
+ // nightly channel (no beta revision)
+ assert_eq!(extract_beta_rev("1.99.9-nightly (xxxxxx)"), None);
+ // stable channel (no beta revision)
+ assert_eq!(extract_beta_rev("1.99.9 (xxxxxxx)"), None);
+ // invalid string
+ assert_eq!(extract_beta_rev("invalid"), None);
+}
+
+#[test]
+fn test_string_to_hex_encode() {
+ let input_string = "Hello, World!";
+ let hex_string = hex_encode(input_string);
+ assert_eq!(hex_string, "48656c6c6f2c20576f726c6421");
+}
diff --git a/src/bootstrap/src/tests/setup.rs b/src/bootstrap/src/tests/setup.rs
index 0fe6e4a46..3e4d66c74 100644
--- a/src/bootstrap/src/tests/setup.rs
+++ b/src/bootstrap/src/tests/setup.rs
@@ -1,11 +1,12 @@
use super::{RUST_ANALYZER_SETTINGS, SETTINGS_HASHES};
+use crate::utils::helpers::hex_encode;
use sha2::Digest;
#[test]
fn check_matching_settings_hash() {
let mut hasher = sha2::Sha256::new();
hasher.update(&RUST_ANALYZER_SETTINGS);
- let hash = hex::encode(hasher.finalize().as_slice());
+ let hash = hex_encode(hasher.finalize().as_slice());
assert_eq!(
&hash,
SETTINGS_HASHES.last().unwrap(),
diff --git a/src/bootstrap/src/utils/bin_helpers.rs b/src/bootstrap/src/utils/bin_helpers.rs
index c90fd2805..9c4e039ea 100644
--- a/src/bootstrap/src/utils/bin_helpers.rs
+++ b/src/bootstrap/src/utils/bin_helpers.rs
@@ -2,14 +2,18 @@
//! dependency on the bootstrap library. This reduces the binary size and
//! improves compilation time by reducing the linking time.
+use std::env;
+use std::fs::OpenOptions;
+use std::io::Write;
+use std::process::Command;
+use std::str::FromStr;
+
/// Parses the value of the "RUSTC_VERBOSE" environment variable and returns it as a `usize`.
/// If it was not defined, returns 0 by default.
///
/// Panics if "RUSTC_VERBOSE" is defined with the value that is not an unsigned integer.
pub(crate) fn parse_rustc_verbose() -> usize {
- use std::str::FromStr;
-
- match std::env::var("RUSTC_VERBOSE") {
+ match env::var("RUSTC_VERBOSE") {
Ok(s) => usize::from_str(&s).expect("RUSTC_VERBOSE should be an integer"),
Err(_) => 0,
}
@@ -19,10 +23,29 @@ pub(crate) fn parse_rustc_verbose() -> usize {
///
/// If "RUSTC_STAGE" was not set, the program will be terminated with 101.
pub(crate) fn parse_rustc_stage() -> String {
- std::env::var("RUSTC_STAGE").unwrap_or_else(|_| {
+ env::var("RUSTC_STAGE").unwrap_or_else(|_| {
// Don't panic here; it's reasonable to try and run these shims directly. Give a helpful error instead.
eprintln!("rustc shim: FATAL: RUSTC_STAGE was not set");
eprintln!("rustc shim: NOTE: use `x.py build -vvv` to see all environment variables set by bootstrap");
std::process::exit(101);
})
}
+
+/// Writes the command invocation to a file if `DUMP_BOOTSTRAP_SHIMS` is set during bootstrap.
+///
+/// Before writing it, replaces user-specific values to create generic dumps for cross-environment
+/// comparisons.
+pub(crate) fn maybe_dump(dump_name: String, cmd: &Command) {
+ if let Ok(dump_dir) = env::var("DUMP_BOOTSTRAP_SHIMS") {
+ let dump_file = format!("{dump_dir}/{dump_name}");
+
+ let mut file =
+ OpenOptions::new().create(true).write(true).append(true).open(&dump_file).unwrap();
+
+ let cmd_dump = format!("{:?}\n", cmd);
+ let cmd_dump = cmd_dump.replace(&env::var("BUILD_OUT").unwrap(), "${BUILD_OUT}");
+ let cmd_dump = cmd_dump.replace(&env::var("CARGO_HOME").unwrap(), "${CARGO_HOME}");
+
+ file.write_all(cmd_dump.as_bytes()).expect("Unable to write file");
+ }
+}
diff --git a/src/bootstrap/src/utils/cc_detect.rs b/src/bootstrap/src/utils/cc_detect.rs
index 52b36ce75..fb5b9d8c8 100644
--- a/src/bootstrap/src/utils/cc_detect.rs
+++ b/src/bootstrap/src/utils/cc_detect.rs
@@ -39,7 +39,7 @@ fn cc2ar(cc: &Path, target: TargetSelection) -> Option<PathBuf> {
Some(PathBuf::from(ar))
} else if let Some(ar) = env::var_os("AR") {
Some(PathBuf::from(ar))
- } else if target.contains("msvc") {
+ } else if target.is_msvc() {
None
} else if target.contains("musl") {
Some(PathBuf::from("ar"))
@@ -78,7 +78,7 @@ fn new_cc_build(build: &Build, target: TargetSelection) -> cc::Build {
cfg.static_crt(a);
}
None => {
- if target.contains("msvc") {
+ if target.is_msvc() {
cfg.static_crt(true);
}
if target.contains("musl") {
diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs
new file mode 100644
index 000000000..8b53a6154
--- /dev/null
+++ b/src/bootstrap/src/utils/change_tracker.rs
@@ -0,0 +1,99 @@
+//! This module facilitates the tracking system for major changes made to the bootstrap,
+//! with the goal of keeping developers synchronized with important modifications in
+//! the bootstrap.
+
+#[derive(Clone, Debug)]
+pub struct ChangeInfo {
+ /// Represents the ID of PR caused major change on bootstrap.
+ pub change_id: usize,
+ pub severity: ChangeSeverity,
+ /// Provides a short summary of the change that will guide developers
+ /// on "how to handle/behave" in response to the changes.
+ pub summary: &'static str,
+}
+
+#[derive(Clone, Debug)]
+pub enum ChangeSeverity {
+ /// Used when build configurations continue working as before.
+ Info,
+ /// Used when the default value of an option changes, or support for an option is removed entirely,
+ /// potentially requiring developers to update their build configurations.
+ Warning,
+}
+
+impl ToString for ChangeSeverity {
+ fn to_string(&self) -> String {
+ match self {
+ ChangeSeverity::Info => "INFO".to_string(),
+ ChangeSeverity::Warning => "WARNING".to_string(),
+ }
+ }
+}
+
+pub fn find_recent_config_change_ids(current_id: usize) -> Vec<ChangeInfo> {
+ if !CONFIG_CHANGE_HISTORY.iter().any(|config| config.change_id == current_id) {
+ // If the current change-id is greater than the most recent one, return
+ // an empty list (it may be due to switching from a recent branch to an
+ // older one); otherwise, return the full list (assuming the user provided
+ // the incorrect change-id by accident).
+ if let Some(config) = CONFIG_CHANGE_HISTORY.iter().max_by_key(|config| config.change_id) {
+ if &current_id > &config.change_id {
+ return Vec::new();
+ }
+ }
+
+ return CONFIG_CHANGE_HISTORY.to_vec();
+ }
+
+ let index =
+ CONFIG_CHANGE_HISTORY.iter().position(|config| config.change_id == current_id).unwrap();
+
+ CONFIG_CHANGE_HISTORY
+ .iter()
+ .skip(index + 1) // Skip the current_id and IDs before it
+ .cloned()
+ .collect()
+}
+
+/// Keeps track of major changes made to the bootstrap configuration.
+///
+/// If you make any major changes (such as adding new values or changing default values),
+/// please ensure adding `ChangeInfo` to the end(because the list must be sorted by the merge date)
+/// of this list.
+pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[
+ ChangeInfo {
+ change_id: 115898,
+ severity: ChangeSeverity::Info,
+ summary: "Implementation of this change-tracking system. Ignore this.",
+ },
+ ChangeInfo {
+ change_id: 116998,
+ severity: ChangeSeverity::Info,
+ summary: "Removed android-ndk r15 support in favor of android-ndk r25b.",
+ },
+ ChangeInfo {
+ change_id: 117435,
+ severity: ChangeSeverity::Info,
+ summary: "New option `rust.parallel-compiler` added to config.toml.",
+ },
+ ChangeInfo {
+ change_id: 116881,
+ severity: ChangeSeverity::Warning,
+ summary: "Default value of `download-ci-llvm` was changed for `codegen` profile.",
+ },
+ ChangeInfo {
+ change_id: 117813,
+ severity: ChangeSeverity::Info,
+ summary: "Use of the `if-available` value for `download-ci-llvm` is deprecated; prefer using the new `if-unchanged` value.",
+ },
+ ChangeInfo {
+ change_id: 116278,
+ severity: ChangeSeverity::Info,
+ summary: "The `rust.use-lld` configuration now has different options ('external'/true or 'self-contained'), and its behaviour has changed.",
+ },
+ ChangeInfo {
+ change_id: 118703,
+ severity: ChangeSeverity::Info,
+ summary: "Removed rust.run_dsymutil and dist.gpg_password_file config options, as they were unused.",
+ },
+];
diff --git a/src/bootstrap/src/utils/dylib.rs b/src/bootstrap/src/utils/dylib.rs
index 279a6a010..b6e7aec17 100644
--- a/src/bootstrap/src/utils/dylib.rs
+++ b/src/bootstrap/src/utils/dylib.rs
@@ -25,3 +25,16 @@ pub fn dylib_path() -> Vec<std::path::PathBuf> {
};
std::env::split_paths(&var).collect()
}
+
+/// Given an executable called `name`, return the filename for the
+/// executable for a particular target.
+#[allow(dead_code)]
+pub fn exe(name: &str, target: &str) -> String {
+ if target.contains("windows") {
+ format!("{name}.exe")
+ } else if target.contains("uefi") {
+ format!("{name}.efi")
+ } else {
+ name.to_string()
+ }
+}
diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs
index 5bc81f2d9..0c4297db6 100644
--- a/src/bootstrap/src/utils/helpers.rs
+++ b/src/bootstrap/src/utils/helpers.rs
@@ -5,19 +5,25 @@
use build_helper::util::fail;
use std::env;
+use std::ffi::OsStr;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::str;
+use std::sync::OnceLock;
use std::time::{Instant, SystemTime, UNIX_EPOCH};
use crate::core::builder::Builder;
use crate::core::config::{Config, TargetSelection};
-use crate::OnceCell;
+use crate::LldMode;
pub use crate::utils::dylib::{dylib_path, dylib_path_var};
+#[cfg(test)]
+#[path = "../tests/helpers.rs"]
+mod tests;
+
/// A helper macro to `unwrap` a result except also print out details like:
///
/// * The file/line of the panic
@@ -44,16 +50,8 @@ macro_rules! t {
}
pub use t;
-/// Given an executable called `name`, return the filename for the
-/// executable for a particular target.
pub fn exe(name: &str, target: TargetSelection) -> String {
- if target.contains("windows") {
- format!("{name}.exe")
- } else if target.contains("uefi") {
- format!("{name}.efi")
- } else {
- name.to_string()
- }
+ crate::utils::dylib::exe(name, &target.triple)
}
/// Returns `true` if the file name given looks like a dynamic library.
@@ -70,7 +68,7 @@ pub fn is_debug_info(name: &str) -> bool {
/// Returns the corresponding relative library directory that the compiler's
/// dylibs will be found in.
pub fn libdir(target: TargetSelection) -> &'static str {
- if target.contains("windows") { "bin" } else { "lib" }
+ if target.is_windows() { "bin" } else { "lib" }
}
/// Adds a list of lookup paths to `cmd`'s dynamic library lookup path.
@@ -189,7 +187,7 @@ pub fn target_supports_cranelift_backend(target: TargetSelection) -> bool {
|| target.contains("aarch64")
|| target.contains("s390x")
|| target.contains("riscv64gc")
- } else if target.contains("darwin") || target.contains("windows") {
+ } else if target.contains("darwin") || target.is_windows() {
target.contains("x86_64")
} else {
false
@@ -443,17 +441,29 @@ pub fn get_clang_cl_resource_dir(clang_cl_path: &str) -> PathBuf {
clang_rt_dir.to_path_buf()
}
-pub fn lld_flag_no_threads(is_windows: bool) -> &'static str {
- static LLD_NO_THREADS: OnceCell<(&'static str, &'static str)> = OnceCell::new();
- let (windows, other) = LLD_NO_THREADS.get_or_init(|| {
- let out = output(Command::new("lld").arg("-flavor").arg("ld").arg("--version"));
- let newer = match (out.find(char::is_numeric), out.find('.')) {
- (Some(b), Some(e)) => out.as_str()[b..e].parse::<i32>().ok().unwrap_or(14) > 10,
+/// Returns a flag that configures LLD to use only a single thread.
+/// If we use an external LLD, we need to find out which version is it to know which flag should we
+/// pass to it (LLD older than version 10 had a different flag).
+fn lld_flag_no_threads(lld_mode: LldMode, is_windows: bool) -> &'static str {
+ static LLD_NO_THREADS: OnceLock<(&'static str, &'static str)> = OnceLock::new();
+
+ let new_flags = ("/threads:1", "--threads=1");
+ let old_flags = ("/no-threads", "--no-threads");
+
+ let (windows_flag, other_flag) = LLD_NO_THREADS.get_or_init(|| {
+ let newer_version = match lld_mode {
+ LldMode::External => {
+ let out = output(Command::new("lld").arg("-flavor").arg("ld").arg("--version"));
+ match (out.find(char::is_numeric), out.find('.')) {
+ (Some(b), Some(e)) => out.as_str()[b..e].parse::<i32>().ok().unwrap_or(14) > 10,
+ _ => true,
+ }
+ }
_ => true,
};
- if newer { ("/threads:1", "--threads=1") } else { ("/no-threads", "--no-threads") }
+ if newer_version { new_flags } else { old_flags }
});
- if is_windows { windows } else { other }
+ if is_windows { windows_flag } else { other_flag }
}
pub fn dir_is_empty(dir: &Path) -> bool {
@@ -470,3 +480,75 @@ pub fn extract_beta_rev(version: &str) -> Option<String> {
count
}
+
+pub enum LldThreads {
+ Yes,
+ No,
+}
+
+/// Returns the linker arguments for rustc/rustdoc for the given builder and target.
+pub fn linker_args(
+ builder: &Builder<'_>,
+ target: TargetSelection,
+ lld_threads: LldThreads,
+) -> Vec<String> {
+ let mut args = linker_flags(builder, target, lld_threads);
+
+ if let Some(linker) = builder.linker(target) {
+ args.push(format!("-Clinker={}", linker.display()));
+ }
+
+ args
+}
+
+/// Returns the linker arguments for rustc/rustdoc for the given builder and target, without the
+/// -Clinker flag.
+pub fn linker_flags(
+ builder: &Builder<'_>,
+ target: TargetSelection,
+ lld_threads: LldThreads,
+) -> Vec<String> {
+ let mut args = vec![];
+ if !builder.is_lld_direct_linker(target) && builder.config.lld_mode.is_used() {
+ args.push(String::from("-Clink-arg=-fuse-ld=lld"));
+
+ if matches!(lld_threads, LldThreads::No) {
+ args.push(format!(
+ "-Clink-arg=-Wl,{}",
+ lld_flag_no_threads(builder.config.lld_mode, target.is_windows())
+ ));
+ }
+ }
+ args
+}
+
+pub fn add_rustdoc_cargo_linker_args(
+ cmd: &mut Command,
+ builder: &Builder<'_>,
+ target: TargetSelection,
+ lld_threads: LldThreads,
+) {
+ let args = linker_args(builder, target, lld_threads);
+ let mut flags = cmd
+ .get_envs()
+ .find_map(|(k, v)| if k == OsStr::new("RUSTDOCFLAGS") { v } else { None })
+ .unwrap_or_default()
+ .to_os_string();
+ for arg in args {
+ if !flags.is_empty() {
+ flags.push(" ");
+ }
+ flags.push(arg);
+ }
+ if !flags.is_empty() {
+ cmd.env("RUSTDOCFLAGS", flags);
+ }
+}
+
+/// Converts `T` into a hexadecimal `String`.
+pub fn hex_encode<T>(input: T) -> String
+where
+ T: AsRef<[u8]>,
+{
+ input.as_ref().iter().map(|x| format!("{:02x}", x)).collect()
+}
diff --git a/src/bootstrap/src/utils/mod.rs b/src/bootstrap/src/utils/mod.rs
index 8ca22d008..cb535f0e1 100644
--- a/src/bootstrap/src/utils/mod.rs
+++ b/src/bootstrap/src/utils/mod.rs
@@ -4,6 +4,7 @@
pub(crate) mod cache;
pub(crate) mod cc_detect;
+pub(crate) mod change_tracker;
pub(crate) mod channel;
pub(crate) mod dylib;
pub(crate) mod exec;