about summary refs log tree commit diff
path: root/src/bootstrap
diff options
context:
space:
mode:
authorThe rustc-josh-sync Cronjob Bot <github-actions@github.com>2025-09-22 04:19:52 +0000
committerThe rustc-josh-sync Cronjob Bot <github-actions@github.com>2025-09-22 04:19:52 +0000
commitc3d30743a4d5cb71c2df9f438787beb207df3078 (patch)
treeee9b4d0fceaff1308f1f7acdfc88be49c6cbced2 /src/bootstrap
parent5658b39397d2667ed8c51ef6b5c52f562af4415e (diff)
parent9f32ccf35fb877270bc44a86a126440f04d676d0 (diff)
Merge ref '9f32ccf35fb8' from rust-lang/rust
Pull recent changes from https://github.com/rust-lang/rust via Josh.

Upstream ref: 9f32ccf35fb877270bc44a86a126440f04d676d0
Filtered ref: 87b13773969f65eec6762cfe4194954e7513f59b
Upstream diff: https://github.com/rust-lang/rust/compare/2f3f27bf79ec147fec9d2e7980605307a74067f4...9f32ccf35fb877270bc44a86a126440f04d676d0

This merge was created using https://github.com/rust-lang/josh-sync.
Diffstat (limited to 'src/bootstrap')
-rw-r--r--src/bootstrap/README.md15
-rw-r--r--src/bootstrap/bootstrap.py12
-rwxr-xr-xsrc/bootstrap/configure.py5
-rw-r--r--src/bootstrap/defaults/bootstrap.dist.toml4
-rw-r--r--src/bootstrap/src/core/build_steps/compile.rs21
-rw-r--r--src/bootstrap/src/core/build_steps/dist.rs25
-rw-r--r--src/bootstrap/src/core/build_steps/gcc.rs14
-rw-r--r--src/bootstrap/src/core/build_steps/llvm.rs4
-rw-r--r--src/bootstrap/src/core/build_steps/test.rs19
-rw-r--r--src/bootstrap/src/core/build_steps/tool.rs19
-rw-r--r--src/bootstrap/src/core/builder/cargo.rs18
-rw-r--r--src/bootstrap/src/core/builder/mod.rs22
-rw-r--r--src/bootstrap/src/core/builder/tests.rs10
-rw-r--r--src/bootstrap/src/core/config/config.rs132
-rw-r--r--src/bootstrap/src/core/config/mod.rs2
-rw-r--r--src/bootstrap/src/core/config/toml/build.rs1
-rw-r--r--src/bootstrap/src/core/config/toml/rust.rs2
-rw-r--r--src/bootstrap/src/core/sanity.rs2
-rw-r--r--src/bootstrap/src/lib.rs6
-rw-r--r--src/bootstrap/src/utils/change_tracker.rs15
-rw-r--r--src/bootstrap/src/utils/proc_macro_deps.rs1
21 files changed, 282 insertions, 67 deletions
diff --git a/src/bootstrap/README.md b/src/bootstrap/README.md
index 2965174b45b..12e09cb07db 100644
--- a/src/bootstrap/README.md
+++ b/src/bootstrap/README.md
@@ -63,6 +63,21 @@ build/
     debug/
     release/
 
+  # Build directory for various tools like `typos` that are only ever
+  # built for the host system, and always with stage0 cargo.
+  misc-tools/
+    bin/
+    target/
+
+  # Directory where js dependencies like tsc and eslint are stored.
+  node_modules/
+    .bin/
+
+  # Copy of package.json and package-lock.json, because npm requires these
+  # to be in the same directory as `node_modules`.
+  package.json
+  package-lock.json
+
   # Output of the dist-related steps like dist-std, dist-rustc, and dist-docs
   dist/
 
diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py
index 2ece53eb0cc..effd33d288f 100644
--- a/src/bootstrap/bootstrap.py
+++ b/src/bootstrap/bootstrap.py
@@ -1039,6 +1039,9 @@ class RustBuild(object):
         # See also: <https://github.com/rust-lang/rust/issues/70208>.
         if "CARGO_BUILD_TARGET" in env:
             del env["CARGO_BUILD_TARGET"]
+        # if in CI, don't use incremental build when building bootstrap.
+        if "GITHUB_ACTIONS" in env:
+            env["CARGO_INCREMENTAL"] = "0"
         env["CARGO_TARGET_DIR"] = build_dir
         env["RUSTC"] = self.rustc()
         env["LD_LIBRARY_PATH"] = (
@@ -1145,7 +1148,8 @@ class RustBuild(object):
             os.path.join(self.rust_root, "src/bootstrap/Cargo.toml"),
             "-Zroot-dir=" + self.rust_root,
         ]
-        args.extend("--verbose" for _ in range(self.verbose))
+        # verbose cargo output is very noisy, so only enable it with -vv
+        args.extend("--verbose" for _ in range(self.verbose - 1))
 
         if "BOOTSTRAP_TRACING" in env:
             args.append("--features=tracing")
@@ -1189,8 +1193,6 @@ class RustBuild(object):
             return "<commit>"
         cmd = [
             "git",
-            "-C",
-            repo_path,
             "rev-list",
             "--author",
             author_email,
@@ -1198,7 +1200,9 @@ class RustBuild(object):
             "HEAD",
         ]
         try:
-            commit = subprocess.check_output(cmd, universal_newlines=True).strip()
+            commit = subprocess.check_output(
+                cmd, universal_newlines=True, cwd=repo_path
+            ).strip()
             return commit or "<commit>"
         except subprocess.CalledProcessError:
             return "<commit>"
diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py
index b05a5cc8b81..1915986be28 100755
--- a/src/bootstrap/configure.py
+++ b/src/bootstrap/configure.py
@@ -340,6 +340,11 @@ o(
     "don't truncate options when printing them in this configure script",
 )
 v("set", None, "set arbitrary key/value pairs in TOML configuration")
+v(
+    "parallel-frontend-threads",
+    "rust.parallel-frontend-threads",
+    "number of parallel threads for rustc compilation",
+)
 
 
 def p(msg):
diff --git a/src/bootstrap/defaults/bootstrap.dist.toml b/src/bootstrap/defaults/bootstrap.dist.toml
index 9daf9faac14..b111a20f8d8 100644
--- a/src/bootstrap/defaults/bootstrap.dist.toml
+++ b/src/bootstrap/defaults/bootstrap.dist.toml
@@ -14,6 +14,10 @@ compiletest-use-stage0-libtest = false
 [llvm]
 download-ci-llvm = false
 
+# Most users installing from source want to build all parts of the project from source.
+[gcc]
+download-ci-gcc = false
+
 [rust]
 # We have several defaults in bootstrap that depend on whether the channel is `dev` (e.g. `omit-git-hash` and `download-ci-llvm`).
 # Make sure they don't get set when installing from source.
diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs
index 0b75e85772f..14104d7d1d7 100644
--- a/src/bootstrap/src/core/build_steps/compile.rs
+++ b/src/bootstrap/src/core/build_steps/compile.rs
@@ -1357,10 +1357,6 @@ pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetS
         cargo.env("RUSTC_VERIFY_LLVM_IR", "1");
     }
 
-    if builder.config.llvm_enzyme {
-        cargo.rustflag("--cfg=llvm_enzyme");
-    }
-
     // These conditionals represent a tension between three forces:
     // - For non-check builds, we need to define some LLVM-related environment
     //   variables, requiring LLVM to have been built.
@@ -1383,14 +1379,17 @@ pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetS
         }
     }
 
-    // Build jemalloc on AArch64 with support for page sizes up to 64K
-    // See: https://github.com/rust-lang/rust/pull/135081
     // See also the "JEMALLOC_SYS_WITH_LG_PAGE" setting in the tool build step.
-    if builder.config.jemalloc(target)
-        && target.starts_with("aarch64")
-        && env::var_os("JEMALLOC_SYS_WITH_LG_PAGE").is_none()
-    {
-        cargo.env("JEMALLOC_SYS_WITH_LG_PAGE", "16");
+    if builder.config.jemalloc(target) && env::var_os("JEMALLOC_SYS_WITH_LG_PAGE").is_none() {
+        // Build jemalloc on AArch64 with support for page sizes up to 64K
+        // See: https://github.com/rust-lang/rust/pull/135081
+        if target.starts_with("aarch64") {
+            cargo.env("JEMALLOC_SYS_WITH_LG_PAGE", "16");
+        }
+        // Build jemalloc on LoongArch with support for page sizes up to 16K
+        else if target.starts_with("loongarch") {
+            cargo.env("JEMALLOC_SYS_WITH_LG_PAGE", "14");
+        }
     }
 }
 
diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs
index 820dda5a652..99a1062109a 100644
--- a/src/bootstrap/src/core/build_steps/dist.rs
+++ b/src/bootstrap/src/core/build_steps/dist.rs
@@ -88,6 +88,7 @@ impl Step for Docs {
         tarball.set_product_name("Rust Documentation");
         tarball.add_bulk_dir(builder.doc_out(host), dest);
         tarball.add_file(builder.src.join("src/doc/robots.txt"), dest, FileType::Regular);
+        tarball.add_file(builder.src.join("src/doc/sitemap.txt"), dest, FileType::Regular);
         Some(tarball.generate())
     }
 
@@ -589,6 +590,8 @@ impl Step for Rustc {
             // Debugger scripts
             builder.ensure(DebuggerScripts { sysroot: image.to_owned(), target });
 
+            generate_target_spec_json_schema(builder, image);
+
             // HTML copyright files
             let file_list = builder.ensure(super::run::GenerateCopyright);
             for file in file_list {
@@ -617,6 +620,28 @@ impl Step for Rustc {
     }
 }
 
+fn generate_target_spec_json_schema(builder: &Builder<'_>, sysroot: &Path) {
+    // Since we run rustc in bootstrap, we need to ensure that we use the host compiler.
+    // We do this by using the stage 1 compiler, which is always compiled for the host,
+    // even in a cross build.
+    let stage1_host = builder.compiler(1, builder.host_target);
+    let mut rustc = command(builder.rustc(stage1_host)).fail_fast();
+    rustc
+        .env("RUSTC_BOOTSTRAP", "1")
+        .args(["--print=target-spec-json-schema", "-Zunstable-options"]);
+    let schema = rustc.run_capture(builder).stdout();
+
+    let schema_dir = tmpdir(builder);
+    t!(fs::create_dir_all(&schema_dir));
+    let schema_file = schema_dir.join("target-spec-json-schema.json");
+    t!(std::fs::write(&schema_file, schema));
+
+    let dst = sysroot.join("etc");
+    t!(fs::create_dir_all(&dst));
+
+    builder.install(&schema_file, &dst, FileType::Regular);
+}
+
 /// Copies debugger scripts for `target` into the given compiler `sysroot`.
 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct DebuggerScripts {
diff --git a/src/bootstrap/src/core/build_steps/gcc.rs b/src/bootstrap/src/core/build_steps/gcc.rs
index 77c9622a9bf..717dea37e9e 100644
--- a/src/bootstrap/src/core/build_steps/gcc.rs
+++ b/src/bootstrap/src/core/build_steps/gcc.rs
@@ -32,6 +32,10 @@ pub struct GccOutput {
 impl GccOutput {
     /// Install the required libgccjit library file(s) to the specified `path`.
     pub fn install_to(&self, builder: &Builder<'_>, directory: &Path) {
+        if builder.config.dry_run() {
+            return;
+        }
+
         // At build time, cg_gcc has to link to libgccjit.so (the unversioned symbol).
         // However, at runtime, it will by default look for libgccjit.so.0.
         // So when we install the built libgccjit.so file to the target `directory`, we add it there
@@ -39,8 +43,16 @@ impl GccOutput {
         let mut target_filename = self.libgccjit.file_name().unwrap().to_str().unwrap().to_string();
         target_filename.push_str(".0");
 
+        // If we build libgccjit ourselves, then `self.libgccjit` can actually be a symlink.
+        // In that case, we have to resolve it first, otherwise we'd create a symlink to a symlink,
+        // which wouldn't work.
+        let actual_libgccjit_path = t!(
+            self.libgccjit.canonicalize(),
+            format!("Cannot find libgccjit at {}", self.libgccjit.display())
+        );
+
         let dst = directory.join(target_filename);
-        builder.copy_link(&self.libgccjit, &dst, FileType::NativeLibrary);
+        builder.copy_link(&actual_libgccjit_path, &dst, FileType::NativeLibrary);
     }
 }
 
diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs
index 83ed7430c39..d43d261ad6c 100644
--- a/src/bootstrap/src/core/build_steps/llvm.rs
+++ b/src/bootstrap/src/core/build_steps/llvm.rs
@@ -619,11 +619,11 @@ fn check_llvm_version(builder: &Builder<'_>, llvm_config: &Path) {
     let version = get_llvm_version(builder, llvm_config);
     let mut parts = version.split('.').take(2).filter_map(|s| s.parse::<u32>().ok());
     if let (Some(major), Some(_minor)) = (parts.next(), parts.next())
-        && major >= 19
+        && major >= 20
     {
         return;
     }
-    panic!("\n\nbad LLVM version: {version}, need >=19\n\n")
+    panic!("\n\nbad LLVM version: {version}, need >=20\n\n")
 }
 
 fn configure_cmake(
diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs
index 4f839bdf7b8..e7f5879b5f5 100644
--- a/src/bootstrap/src/core/build_steps/test.rs
+++ b/src/bootstrap/src/core/build_steps/test.rs
@@ -2086,11 +2086,25 @@ HELP: You can add it into `bootstrap.toml` in `rust.codegen-backends = [{name:?}
         }
 
         // Get paths from cmd args
-        let paths = match &builder.config.cmd {
+        let mut paths = match &builder.config.cmd {
             Subcommand::Test { .. } => &builder.config.paths[..],
             _ => &[],
         };
 
+        // in rustdoc-js mode, allow filters to be rs files or js files.
+        // use a late-initialized Vec to avoid cloning for other modes.
+        let mut paths_v;
+        if mode == "rustdoc-js" {
+            paths_v = paths.to_vec();
+            for p in &mut paths_v {
+                if let Some(ext) = p.extension()
+                    && ext == "js"
+                {
+                    p.set_extension("rs");
+                }
+            }
+            paths = &paths_v;
+        }
         // Get test-args by striping suite path
         let mut test_args: Vec<&str> = paths
             .iter()
@@ -3404,9 +3418,6 @@ impl Step for TierCheck {
         );
         cargo.arg(builder.src.join("src/doc/rustc/src/platform-support.md"));
         cargo.arg(builder.rustc(self.test_compiler));
-        if builder.is_verbose() {
-            cargo.arg("--verbose");
-        }
 
         let _guard = builder.msg_test(
             "platform support check",
diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs
index 6870bf3eddc..4f096d50ea5 100644
--- a/src/bootstrap/src/core/build_steps/tool.rs
+++ b/src/bootstrap/src/core/build_steps/tool.rs
@@ -228,12 +228,18 @@ pub fn prepare_tool_cargo(
     // own copy
     cargo.env("LZMA_API_STATIC", "1");
 
-    // Build jemalloc on AArch64 with support for page sizes up to 64K
-    // See: https://github.com/rust-lang/rust/pull/135081
     // Note that `miri` always uses jemalloc. As such, there is no checking of the jemalloc build flag.
     // See also the "JEMALLOC_SYS_WITH_LG_PAGE" setting in the compile build step.
-    if target.starts_with("aarch64") && env::var_os("JEMALLOC_SYS_WITH_LG_PAGE").is_none() {
-        cargo.env("JEMALLOC_SYS_WITH_LG_PAGE", "16");
+    if env::var_os("JEMALLOC_SYS_WITH_LG_PAGE").is_none() {
+        // Build jemalloc on AArch64 with support for page sizes up to 64K
+        // See: https://github.com/rust-lang/rust/pull/135081
+        if target.starts_with("aarch64") {
+            cargo.env("JEMALLOC_SYS_WITH_LG_PAGE", "16");
+        }
+        // Build jemalloc on LoongArch with support for page sizes up to 16K
+        else if target.starts_with("loongarch") {
+            cargo.env("JEMALLOC_SYS_WITH_LG_PAGE", "14");
+        }
     }
 
     // CFG_RELEASE is needed by rustfmt (and possibly other tools) which
@@ -388,6 +394,9 @@ macro_rules! bootstrap_tool {
         }
 
         impl<'a> Builder<'a> {
+            /// Ensure a tool is built, then get the path to its executable.
+            ///
+            /// The actual building, if any, will be handled via [`ToolBuild`].
             pub fn tool_exe(&self, tool: Tool) -> PathBuf {
                 match tool {
                     $(Tool::$name =>
@@ -1546,6 +1555,8 @@ pub const TEST_FLOAT_PARSE_ALLOW_FEATURES: &str = "f16,cfg_target_has_reliable_f
 impl Builder<'_> {
     /// Gets a `BootstrapCommand` which is ready to run `tool` in `stage` built for
     /// `host`.
+    ///
+    /// This also ensures that the given tool is built (using [`ToolBuild`]).
     pub fn tool_cmd(&self, tool: Tool) -> BootstrapCommand {
         let mut cmd = command(self.tool_exe(tool));
         let compiler = self.compiler(0, self.config.host_target);
diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs
index 924bb4adb42..ee2bb710674 100644
--- a/src/bootstrap/src/core/builder/cargo.rs
+++ b/src/bootstrap/src/core/builder/cargo.rs
@@ -679,6 +679,12 @@ impl Builder<'_> {
         // cargo would implicitly add it, it was discover that sometimes bootstrap only use
         // `rustflags` without `cargo` making it required.
         rustflags.arg("-Zunstable-options");
+
+        // Add parallel frontend threads configuration
+        if let Some(threads) = self.config.rust_parallel_frontend_threads {
+            rustflags.arg(&format!("-Zthreads={threads}"));
+        }
+
         for (restricted_mode, name, values) in EXTRA_CHECK_CFGS {
             if restricted_mode.is_none() || *restricted_mode == Some(mode) {
                 rustflags.arg(&check_cfg_arg(name, *values));
@@ -1133,7 +1139,7 @@ impl Builder<'_> {
             cargo.env("RUSTC_BACKTRACE_ON_ICE", "1");
         }
 
-        if self.is_verbose() {
+        if self.is_verbose_than(1) {
             // This provides very useful logs especially when debugging build cache-related stuff.
             cargo.env("CARGO_LOG", "cargo::core::compiler::fingerprint=info");
         }
@@ -1221,6 +1227,11 @@ impl Builder<'_> {
             rustflags.arg("-Zehcont-guard");
         }
 
+        // Optionally override the rc.exe when compiling rustc on Windows.
+        if let Some(windows_rc) = &self.config.windows_rc {
+            cargo.env("RUSTC_WINDOWS_RC", windows_rc);
+        }
+
         // 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
@@ -1275,8 +1286,9 @@ impl Builder<'_> {
             cargo.env("WINAPI_NO_BUNDLED_LIBRARIES", "1");
         }
 
-        for _ in 0..self.verbosity {
-            cargo.arg("-v");
+        // verbose cargo output is very noisy, so only enable it with -vv
+        for _ in 0..self.verbosity.saturating_sub(1) {
+            cargo.arg("--verbose");
         }
 
         match (mode, self.config.rust_codegen_units_std, self.config.rust_codegen_units) {
diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs
index 75c8ee36528..8226b4325b6 100644
--- a/src/bootstrap/src/core/builder/mod.rs
+++ b/src/bootstrap/src/core/builder/mod.rs
@@ -590,18 +590,30 @@ impl StepDescription {
         // Attempt to resolve paths to be relative to the builder source directory.
         let mut paths: Vec<PathBuf> = paths
             .iter()
-            .map(|p| {
+            .map(|original_path| {
+                let mut path = original_path.clone();
+
+                // Someone could run `x <cmd> <path>` from a different repository than the source
+                // directory.
+                // In that case, we should not try to resolve the paths relative to the working
+                // directory, but rather relative to the source directory.
+                // So we forcefully "relocate" the path to the source directory here.
+                if !path.is_absolute() {
+                    path = builder.src.join(path);
+                }
+
                 // If the path does not exist, it may represent the name of a Step, such as `tidy` in `x test tidy`
-                if !p.exists() {
-                    return p.clone();
+                if !path.exists() {
+                    // Use the original path here
+                    return original_path.clone();
                 }
 
                 // Make the path absolute, strip the prefix, and convert to a PathBuf.
-                match std::path::absolute(p) {
+                match std::path::absolute(&path) {
                     Ok(p) => p.strip_prefix(&builder.src).unwrap_or(&p).to_path_buf(),
                     Err(e) => {
                         eprintln!("ERROR: {e:?}");
-                        panic!("Due to the above error, failed to resolve path: {p:?}");
+                        panic!("Due to the above error, failed to resolve path: {path:?}");
                     }
                 }
             })
diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs
index 89a0ab7711e..229adf71459 100644
--- a/src/bootstrap/src/core/builder/tests.rs
+++ b/src/bootstrap/src/core/builder/tests.rs
@@ -1805,7 +1805,7 @@ mod snapshot {
         insta::assert_snapshot!(
             ctx.config("check")
                 .path("compiler")
-                .render_steps(), @"[check] rustc 0 <host> -> rustc 1 <host> (74 crates)");
+                .render_steps(), @"[check] rustc 0 <host> -> rustc 1 <host> (75 crates)");
     }
 
     #[test]
@@ -1831,7 +1831,7 @@ mod snapshot {
             ctx.config("check")
                 .path("compiler")
                 .stage(1)
-                .render_steps(), @"[check] rustc 0 <host> -> rustc 1 <host> (74 crates)");
+                .render_steps(), @"[check] rustc 0 <host> -> rustc 1 <host> (75 crates)");
     }
 
     #[test]
@@ -1845,7 +1845,7 @@ mod snapshot {
         [build] llvm <host>
         [build] rustc 0 <host> -> rustc 1 <host>
         [build] rustc 1 <host> -> std 1 <host>
-        [check] rustc 1 <host> -> rustc 2 <host> (74 crates)
+        [check] rustc 1 <host> -> rustc 2 <host> (75 crates)
         ");
     }
 
@@ -1861,7 +1861,7 @@ mod snapshot {
         [build] rustc 0 <host> -> rustc 1 <host>
         [build] rustc 1 <host> -> std 1 <host>
         [check] rustc 1 <host> -> std 1 <target1>
-        [check] rustc 1 <host> -> rustc 2 <target1> (74 crates)
+        [check] rustc 1 <host> -> rustc 2 <target1> (75 crates)
         [check] rustc 1 <host> -> rustc 2 <target1>
         [check] rustc 1 <host> -> Rustdoc 2 <target1>
         [check] rustc 1 <host> -> rustc_codegen_cranelift 2 <target1>
@@ -1957,7 +1957,7 @@ mod snapshot {
             ctx.config("check")
                 .paths(&["library", "compiler"])
                 .args(&args)
-                .render_steps(), @"[check] rustc 0 <host> -> rustc 1 <host> (74 crates)");
+                .render_steps(), @"[check] rustc 0 <host> -> rustc 1 <host> (75 crates)");
     }
 
     #[test]
diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs
index 678a9b63952..dd2d5a1fd53 100644
--- a/src/bootstrap/src/core/config/config.rs
+++ b/src/bootstrap/src/core/config/config.rs
@@ -191,7 +191,6 @@ pub struct Config {
     pub rust_optimize: RustOptimize,
     pub rust_codegen_units: Option<u32>,
     pub rust_codegen_units_std: Option<u32>,
-
     pub rustc_debug_assertions: bool,
     pub std_debug_assertions: bool,
     pub tools_debug_assertions: bool,
@@ -222,6 +221,8 @@ pub struct Config {
     pub rust_validate_mir_opts: Option<u32>,
     pub rust_std_features: BTreeSet<String>,
     pub rust_break_on_ice: bool,
+    pub rust_parallel_frontend_threads: Option<u32>,
+
     pub llvm_profile_use: Option<String>,
     pub llvm_profile_generate: bool,
     pub llvm_libunwind_default: Option<LlvmLibunwind>,
@@ -272,6 +273,7 @@ pub struct Config {
     pub gdb: Option<PathBuf>,
     pub lldb: Option<PathBuf>,
     pub python: Option<PathBuf>,
+    pub windows_rc: Option<PathBuf>,
     pub reuse: Option<PathBuf>,
     pub cargo_native_static: bool,
     pub configure_args: Vec<String>,
@@ -449,6 +451,7 @@ impl Config {
             nodejs: build_nodejs,
             npm: build_npm,
             python: build_python,
+            windows_rc: build_windows_rc,
             reuse: build_reuse,
             locked_deps: build_locked_deps,
             vendor: build_vendor,
@@ -534,6 +537,7 @@ impl Config {
             backtrace_on_ice: rust_backtrace_on_ice,
             verify_llvm_ir: rust_verify_llvm_ir,
             thin_lto_import_instr_limit: rust_thin_lto_import_instr_limit,
+            parallel_frontend_threads: rust_parallel_frontend_threads,
             remap_debuginfo: rust_remap_debuginfo,
             jemalloc: rust_jemalloc,
             test_compare_mode: rust_test_compare_mode,
@@ -1298,6 +1302,7 @@ impl Config {
             rust_overflow_checks_std: rust_overflow_checks_std
                 .or(rust_overflow_checks)
                 .unwrap_or(rust_debug == Some(true)),
+            rust_parallel_frontend_threads: rust_parallel_frontend_threads.map(threads_from_config),
             rust_profile_generate: flags_rust_profile_generate.or(rust_profile_generate),
             rust_profile_use: flags_rust_profile_use.or(rust_profile_use),
             rust_randomize_layout: rust_randomize_layout.unwrap_or(false),
@@ -1339,6 +1344,7 @@ impl Config {
                 .unwrap_or(rust_debug == Some(true)),
             vendor,
             verbose_tests,
+            windows_rc: build_windows_rc.map(PathBuf::from),
             // tidy-alphabetical-end
         }
     }
@@ -1853,13 +1859,7 @@ fn load_toml_config(
         } else {
             toml_path.clone()
         });
-        (
-            get_toml(&toml_path).unwrap_or_else(|e| {
-                eprintln!("ERROR: Failed to parse '{}': {e}", toml_path.display());
-                exit!(2);
-            }),
-            path,
-        )
+        (get_toml(&toml_path).unwrap_or_else(|e| bad_config(&toml_path, e)), path)
     } else {
         (TomlConfig::default(), None)
     }
@@ -1892,10 +1892,8 @@ fn postprocess_toml(
             .unwrap()
             .join(include_path);
 
-        let included_toml = get_toml(&include_path).unwrap_or_else(|e| {
-            eprintln!("ERROR: Failed to parse '{}': {e}", include_path.display());
-            exit!(2);
-        });
+        let included_toml =
+            get_toml(&include_path).unwrap_or_else(|e| bad_config(&include_path, e));
         toml.merge(
             Some(include_path),
             &mut Default::default(),
@@ -2139,15 +2137,7 @@ pub fn parse_download_ci_llvm<'a>(
     asserts: bool,
 ) -> bool {
     let dwn_ctx = dwn_ctx.as_ref();
-
-    // We don't ever want to use `true` on CI, as we should not
-    // download upstream artifacts if there are any local modifications.
-    let default = if dwn_ctx.is_running_on_ci {
-        StringOrBool::String("if-unchanged".to_string())
-    } else {
-        StringOrBool::Bool(true)
-    };
-    let download_ci_llvm = download_ci_llvm.unwrap_or(default);
+    let download_ci_llvm = download_ci_llvm.unwrap_or(StringOrBool::Bool(true));
 
     let if_unchanged = || {
         if rust_info.is_from_tarball() {
@@ -2179,8 +2169,9 @@ pub fn parse_download_ci_llvm<'a>(
                 );
             }
 
-            if b && dwn_ctx.is_running_on_ci {
-                // On CI, we must always rebuild LLVM if there were any modifications to it
+            #[cfg(not(test))]
+            if b && dwn_ctx.is_running_on_ci && CiEnv::is_rust_lang_managed_ci_job() {
+                // On rust-lang CI, we must always rebuild LLVM if there were any modifications to it
                 panic!(
                     "`llvm.download-ci-llvm` cannot be set to `true` on CI. Use `if-unchanged` instead."
                 );
@@ -2398,3 +2389,98 @@ pub(crate) fn read_file_by_commit<'a>(
     git.arg("show").arg(format!("{commit}:{}", file.to_str().unwrap()));
     git.run_capture_stdout(dwn_ctx.exec_ctx).stdout()
 }
+
+fn bad_config(toml_path: &Path, e: toml::de::Error) -> ! {
+    eprintln!("ERROR: Failed to parse '{}': {e}", toml_path.display());
+    let e_s = e.to_string();
+    if e_s.contains("unknown field")
+        && let Some(field_name) = e_s.split("`").nth(1)
+        && let sections = find_correct_section_for_field(field_name)
+        && !sections.is_empty()
+    {
+        if sections.len() == 1 {
+            match sections[0] {
+                WouldBeValidFor::TopLevel { is_section } => {
+                    if is_section {
+                        eprintln!(
+                            "hint: section name `{field_name}` used as a key within a section"
+                        );
+                    } else {
+                        eprintln!("hint: try using `{field_name}` as a top level key");
+                    }
+                }
+                WouldBeValidFor::Section(section) => {
+                    eprintln!("hint: try moving `{field_name}` to the `{section}` section")
+                }
+            }
+        } else {
+            eprintln!(
+                "hint: `{field_name}` would be valid {}",
+                join_oxford_comma(sections.iter(), "or"),
+            );
+        }
+    }
+
+    exit!(2);
+}
+
+#[derive(Copy, Clone, Debug)]
+enum WouldBeValidFor {
+    TopLevel { is_section: bool },
+    Section(&'static str),
+}
+
+fn join_oxford_comma(
+    mut parts: impl ExactSizeIterator<Item = impl std::fmt::Display>,
+    conj: &str,
+) -> String {
+    use std::fmt::Write;
+    let mut out = String::new();
+
+    assert!(parts.len() > 1);
+    while let Some(part) = parts.next() {
+        if parts.len() == 0 {
+            write!(&mut out, "{conj} {part}")
+        } else {
+            write!(&mut out, "{part}, ")
+        }
+        .unwrap();
+    }
+    out
+}
+
+impl std::fmt::Display for WouldBeValidFor {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Self::TopLevel { .. } => write!(f, "at top level"),
+            Self::Section(section_name) => write!(f, "in section `{section_name}`"),
+        }
+    }
+}
+
+fn find_correct_section_for_field(field_name: &str) -> Vec<WouldBeValidFor> {
+    let sections = ["build", "install", "llvm", "gcc", "rust", "dist"];
+    sections
+        .iter()
+        .map(Some)
+        .chain([None])
+        .filter_map(|section_name| {
+            let dummy_config_str = if let Some(section_name) = section_name {
+                format!("{section_name}.{field_name} = 0\n")
+            } else {
+                format!("{field_name} = 0\n")
+            };
+            let is_unknown_field = toml::from_str::<toml::Value>(&dummy_config_str)
+                .and_then(TomlConfig::deserialize)
+                .err()
+                .is_some_and(|e| e.to_string().contains("unknown field"));
+            if is_unknown_field {
+                None
+            } else {
+                Some(section_name.copied().map(WouldBeValidFor::Section).unwrap_or_else(|| {
+                    WouldBeValidFor::TopLevel { is_section: sections.contains(&field_name) }
+                }))
+            }
+        })
+        .collect()
+}
diff --git a/src/bootstrap/src/core/config/mod.rs b/src/bootstrap/src/core/config/mod.rs
index 5999348a7fe..05a5dfc0bc5 100644
--- a/src/bootstrap/src/core/config/mod.rs
+++ b/src/bootstrap/src/core/config/mod.rs
@@ -422,10 +422,10 @@ impl std::str::FromStr for RustcLto {
 #[derive(Default, Clone)]
 pub enum GccCiMode {
     /// Build GCC from the local `src/gcc` submodule.
-    #[default]
     BuildLocally,
     /// Try to download GCC from CI.
     /// If it is not available on CI, it will be built locally instead.
+    #[default]
     DownloadFromCi,
 }
 
diff --git a/src/bootstrap/src/core/config/toml/build.rs b/src/bootstrap/src/core/config/toml/build.rs
index 25c19f1070a..a9d4d3961c9 100644
--- a/src/bootstrap/src/core/config/toml/build.rs
+++ b/src/bootstrap/src/core/config/toml/build.rs
@@ -37,6 +37,7 @@ define_config! {
         nodejs: Option<String> = "nodejs",
         npm: Option<String> = "npm",
         python: Option<String> = "python",
+        windows_rc: Option<String> = "windows-rc",
         reuse: Option<String> = "reuse",
         locked_deps: Option<bool> = "locked-deps",
         vendor: Option<bool> = "vendor",
diff --git a/src/bootstrap/src/core/config/toml/rust.rs b/src/bootstrap/src/core/config/toml/rust.rs
index 4832a1d37b7..e5987d7040a 100644
--- a/src/bootstrap/src/core/config/toml/rust.rs
+++ b/src/bootstrap/src/core/config/toml/rust.rs
@@ -66,6 +66,7 @@ define_config! {
         validate_mir_opts: Option<u32> = "validate-mir-opts",
         std_features: Option<BTreeSet<String>> = "std-features",
         break_on_ice: Option<bool> = "break-on-ice",
+        parallel_frontend_threads: Option<u32> = "parallel-frontend-threads",
     }
 }
 
@@ -357,6 +358,7 @@ pub fn check_incompatible_options_for_ci_rustc(
         validate_mir_opts: _,
         frame_pointers: _,
         break_on_ice: _,
+        parallel_frontend_threads: _,
     } = ci_rust_config;
 
     // There are two kinds of checks for CI rustc incompatible options:
diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs
index 04cf63f1c6d..68202500d97 100644
--- a/src/bootstrap/src/core/sanity.rs
+++ b/src/bootstrap/src/core/sanity.rs
@@ -287,7 +287,7 @@ than building it.
 
             if !has_target {
                 panic!(
-                    "No such target exists in the target list,\n\
+                    "{target_str}: No such target exists in the target list,\n\
                      make sure to correctly specify the location \
                      of the JSON specification file \
                      for custom targets!\n\
diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs
index a2aeed20948..e953fe2945e 100644
--- a/src/bootstrap/src/lib.rs
+++ b/src/bootstrap/src/lib.rs
@@ -87,9 +87,6 @@ const EXTRA_CHECK_CFGS: &[(Option<Mode>, &str, Option<&[&'static str]>)] = &[
     (Some(Mode::Codegen), "bootstrap", None),
     (Some(Mode::ToolRustcPrivate), "bootstrap", None),
     (Some(Mode::ToolStd), "bootstrap", None),
-    (Some(Mode::Rustc), "llvm_enzyme", None),
-    (Some(Mode::Codegen), "llvm_enzyme", None),
-    (Some(Mode::ToolRustcPrivate), "llvm_enzyme", None),
     (Some(Mode::ToolRustcPrivate), "rust_analyzer", None),
     (Some(Mode::ToolStd), "rust_analyzer", None),
     // Any library specific cfgs like `target_os`, `target_arch` should be put in
@@ -869,6 +866,9 @@ impl Build {
         if (self.config.llvm_enabled(target) || kind == Kind::Check) && check("llvm") {
             features.push("llvm");
         }
+        if self.config.llvm_enzyme {
+            features.push("llvm_enzyme");
+        }
         // keep in sync with `bootstrap/compile.rs:rustc_cargo_env`
         if self.config.rust_randomize_layout && check("rustc_randomized_layouts") {
             features.push("rustc_randomized_layouts");
diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs
index 01309072927..6b187578c31 100644
--- a/src/bootstrap/src/utils/change_tracker.rs
+++ b/src/bootstrap/src/utils/change_tracker.rs
@@ -541,4 +541,19 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[
         severity: ChangeSeverity::Info,
         summary: "Added a new option `rust.break-on-ice` to control if internal compiler errors cause a debug break on Windows.",
     },
+    ChangeInfo {
+        change_id: 146435,
+        severity: ChangeSeverity::Info,
+        summary: "The default value of the `gcc.download-ci-gcc` option has been changed to `true`.",
+    },
+    ChangeInfo {
+        change_id: 146458,
+        severity: ChangeSeverity::Info,
+        summary: "There is now a bootstrap option called `rust.parallel-frontend-threads`, which can be used to set the number of threads for the compiler frontend used during compilation of Rust code.",
+    },
+    ChangeInfo {
+        change_id: 146663,
+        severity: ChangeSeverity::Info,
+        summary: "New option `build.windows-rc` that will override which resource compiler on Windows will be used to compile Rust.",
+    },
 ];
diff --git a/src/bootstrap/src/utils/proc_macro_deps.rs b/src/bootstrap/src/utils/proc_macro_deps.rs
index 777c8601aa1..db2369097d6 100644
--- a/src/bootstrap/src/utils/proc_macro_deps.rs
+++ b/src/bootstrap/src/utils/proc_macro_deps.rs
@@ -43,6 +43,7 @@ pub static CRATES: &[&str] = &[
     "rustc-hash",
     "self_cell",
     "serde",
+    "serde_derive_internals",
     "sha2",
     "smallvec",
     "stable_deref_trait",