about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbit-aloo <sshourya17@gmail.com>2025-08-18 09:37:50 +0530
committerbit-aloo <sshourya17@gmail.com>2025-08-18 09:45:43 +0530
commit77c3d6edfa30cf4c9dc010d96324f6f72579f36b (patch)
tree514f83e02ac0daf418faa7fcb741e897003492fe
parent425a9c0a0e365c0b8c6cfd00c2ded83a73bed9a0 (diff)
downloadrust-77c3d6edfa30cf4c9dc010d96324f6f72579f36b.tar.gz
rust-77c3d6edfa30cf4c9dc010d96324f6f72579f36b.zip
remove default config
-rw-r--r--src/bootstrap/src/core/config/config.rs1068
-rw-r--r--src/bootstrap/src/core/config/mod.rs6
-rw-r--r--src/bootstrap/src/core/download.rs71
3 files changed, 563 insertions, 582 deletions
diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs
index 5eea5436023..692c05456f6 100644
--- a/src/bootstrap/src/core/config/config.rs
+++ b/src/bootstrap/src/core/config/config.rs
@@ -13,7 +13,6 @@
 //! and the `bootstrap.toml` file—merging them, applying defaults, and performing
 //! cross-component validation. The main `parse_inner` function and its supporting
 //! helpers reside here, transforming raw `Toml` data into the structured `Config` type.
-
 use std::cell::Cell;
 use std::collections::{BTreeSet, HashMap, HashSet};
 use std::io::IsTerminal;
@@ -48,7 +47,7 @@ use crate::core::config::toml::rust::{
 use crate::core::config::toml::target::Target;
 use crate::core::config::{
     DebuginfoLevel, DryRun, GccCiMode, LlvmLibunwind, Merge, ReplaceOpt, RustcLto, SplitDebuginfo,
-    StringOrBool, set, threads_from_config,
+    StringOrBool, threads_from_config,
 };
 use crate::core::download::{
     DownloadContext, download_beta_toolchain, is_download_ci_available, maybe_download_rustfmt,
@@ -463,35 +462,32 @@ impl Config {
             "flags.exclude" = ?flags_exclude
         );
 
-        // First initialize the bare minimum that we need for further operation - source directory
-        // and execution context.
-        let mut config = Config::default_opts();
-        let exec_ctx = ExecutionContext::new(flags_verbose, flags_cmd.fail_fast());
+        // Set config values based on flags.
 
-        config.exec_ctx = exec_ctx;
+        let mut exec_ctx = ExecutionContext::new(flags_verbose, flags_cmd.fail_fast());
+        exec_ctx.set_dry_run(if flags_dry_run { DryRun::UserSelected } else { DryRun::Disabled });
+        let mut src = {
+            let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
+            // Undo `src/bootstrap`
+            manifest_dir.parent().unwrap().parent().unwrap().to_owned()
+        };
 
-        if let Some(src) = compute_src_directory(flags_src, &config.exec_ctx) {
-            config.src = src;
+        if let Some(src_) = compute_src_directory(flags_src, &exec_ctx) {
+            src = src_;
         }
 
         // Now load the TOML config, as soon as possible
-        let (mut toml, toml_path) = load_toml_config(&config.src, flags_config, &get_toml);
-        config.config = toml_path.clone();
-
-        postprocess_toml(
-            &mut toml,
-            &config.src,
-            toml_path,
-            config.exec_ctx(),
-            &flags_set,
-            &get_toml,
-        );
+        let (mut toml, toml_path) = load_toml_config(&src, flags_config, &get_toml);
+
+        let is_running_on_ci = flags_ci.unwrap_or(CiEnv::is_ci());
+
+        postprocess_toml(&mut toml, &src, toml_path.clone(), &exec_ctx, &flags_set, &get_toml);
 
         // Now override TOML values with flags, to make sure that we won't later override flags with
         // TOML values by accident instead, because flags have higher priority.
         let Build {
             description: build_description,
-            build: mut build_build,
+            build: build_build,
             host: build_host,
             target: build_target,
             build_dir: build_build_dir,
@@ -538,7 +534,7 @@ impl Config {
             metrics: _,
             android_ndk: build_android_ndk,
             optimized_compiler_builtins: build_optimized_compiler_builtins,
-            jobs: mut build_jobs,
+            jobs: build_jobs,
             compiletest_diff_tool: build_compiletest_diff_tool,
             compiletest_use_stage0_libtest: build_compiletest_use_stage0_libtest,
             tidy_extra_checks: build_tidy_extra_checks,
@@ -558,226 +554,179 @@ impl Config {
         } = toml.install.unwrap_or_default();
 
         let Rust {
-            optimize: rust_optimize,
+            optimize: rust_optimize_,
             debug: rust_debug,
-            codegen_units: rust_codegen_units,
-            codegen_units_std: rust_codegen_units_std,
+            codegen_units: rust_codegen_units_,
+            codegen_units_std: rust_codegen_units_std_,
             rustc_debug_assertions: rust_rustc_debug_assertions,
             std_debug_assertions: rust_std_debug_assertions,
             tools_debug_assertions: rust_tools_debug_assertions,
-            overflow_checks: rust_overflow_checks,
-            overflow_checks_std: rust_overflow_checks_std,
-            debug_logging: rust_debug_logging,
+            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,
+            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_,
             backtrace: rust_backtrace,
             incremental: rust_incremental,
-            randomize_layout: rust_randomize_layout,
+            randomize_layout: rust_randomize_layout_,
             default_linker: rust_default_linker,
             channel: rust_channel,
             musl_root: rust_musl_root,
-            rpath: rust_rpath,
+            rpath: rust_rpath_,
             verbose_tests: rust_verbose_tests,
-            optimize_tests: rust_optimize_tests,
+            optimize_tests: rust_optimize_tests_,
             codegen_tests: rust_codegen_tests,
             omit_git_hash: rust_omit_git_hash,
-            dist_src: rust_dist_src,
+            dist_src: rust_dist_src_,
             save_toolstates: rust_save_toolstates,
-            codegen_backends: rust_codegen_backends,
+            codegen_backends: rust_codegen_backends_,
             lld: rust_lld_enabled,
             llvm_tools: rust_llvm_tools,
             llvm_bitcode_linker: rust_llvm_bitcode_linker,
             deny_warnings: rust_deny_warnings,
             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,
-            remap_debuginfo: rust_remap_debuginfo,
+            verify_llvm_ir: rust_verify_llvm_ir_,
+            thin_lto_import_instr_limit: rust_thin_lto_import_instr_limit_,
+            remap_debuginfo: rust_remap_debuginfo_,
             jemalloc: rust_jemalloc,
             test_compare_mode: rust_test_compare_mode,
             llvm_libunwind: rust_llvm_libunwind,
             control_flow_guard: rust_control_flow_guard,
             ehcont_guard: rust_ehcont_guard,
-            new_symbol_mangling: rust_new_symbol_mangling,
-            profile_generate: rust_profile_generate,
-            profile_use: rust_profile_use,
+            new_symbol_mangling: rust_new_symbol_mangling_,
+            profile_generate: rust_profile_generate_,
+            profile_use: rust_profile_use_,
             download_rustc: rust_download_rustc,
-            lto: rust_lto,
-            validate_mir_opts: rust_validate_mir_opts,
-            frame_pointers: rust_frame_pointers,
-            stack_protector: rust_stack_protector,
-            strip: rust_strip,
+            lto: rust_lto_,
+            validate_mir_opts: rust_validate_mir_opts_,
+            frame_pointers: rust_frame_pointers_,
+            stack_protector: rust_stack_protector_,
+            strip: rust_strip_,
             lld_mode: rust_lld_mode,
-            std_features: rust_std_features,
+            std_features: rust_std_features_,
         } = toml.rust.unwrap_or_default();
 
         let Llvm {
-            optimize: llvm_optimize,
-            thin_lto: llvm_thin_lto,
-            release_debuginfo: llvm_release_debuginfo,
-            assertions: llvm_assertions,
-            tests: llvm_tests,
-            enzyme: llvm_enzyme,
+            optimize: llvm_optimize_,
+            thin_lto: llvm_thin_lto_,
+            release_debuginfo: llvm_release_debuginfo_,
+            assertions: llvm_assertions_,
+            tests: llvm_tests_,
+            enzyme: llvm_enzyme_,
             plugins: llvm_plugin,
             static_libstdcpp: llvm_static_libstdcpp,
-            libzstd: llvm_libzstd,
+            libzstd: llvm_libzstd_,
             ninja: llvm_ninja,
-            targets: llvm_targets,
-            experimental_targets: llvm_experimental_targets,
-            link_jobs: llvm_link_jobs,
-            link_shared: llvm_link_shared,
-            version_suffix: llvm_version_suffix,
-            clang_cl: llvm_clang_cl,
-            cflags: llvm_cflags,
-            cxxflags: llvm_cxxflags,
-            ldflags: llvm_ldflags,
-            use_libcxx: llvm_use_libcxx,
-            use_linker: llvm_use_linker,
-            allow_old_toolchain: llvm_allow_old_toolchain,
-            offload: llvm_offload,
-            polly: llvm_polly,
-            clang: llvm_clang,
-            enable_warnings: llvm_enable_warnings,
+            targets: llvm_targets_,
+            experimental_targets: llvm_experimental_targets_,
+            link_jobs: llvm_link_jobs_,
+            link_shared: llvm_link_shared_,
+            version_suffix: llvm_version_suffix_,
+            clang_cl: llvm_clang_cl_,
+            cflags: llvm_cflags_,
+            cxxflags: llvm_cxxflags_,
+            ldflags: llvm_ldflags_,
+            use_libcxx: llvm_use_libcxx_,
+            use_linker: llvm_use_linker_,
+            allow_old_toolchain: llvm_allow_old_toolchain_,
+            offload: llvm_offload_,
+            polly: llvm_polly_,
+            clang: llvm_clang_,
+            enable_warnings: llvm_enable_warnings_,
             download_ci_llvm: llvm_download_ci_llvm,
-            build_config: llvm_build_config,
+            build_config: llvm_build_config_,
         } = toml.llvm.unwrap_or_default();
 
         let Dist {
-            sign_folder: dist_sign_folder,
-            upload_addr: dist_upload_addr,
-            src_tarball: dist_src_tarball,
-            compression_formats: dist_compression_formats,
-            compression_profile: dist_compression_profile,
-            include_mingw_linker: dist_include_mingw_linker,
-            vendor: dist_vendor,
+            sign_folder: dist_sign_folder_,
+            upload_addr: dist_upload_addr_,
+            src_tarball: dist_src_tarball_,
+            compression_formats: dist_compression_formats_,
+            compression_profile: dist_compression_profile_,
+            include_mingw_linker: dist_include_mingw_linker_,
+            vendor: dist_vendor_,
         } = toml.dist.unwrap_or_default();
 
         let Gcc { download_ci_gcc: gcc_download_ci_gcc } = toml.gcc.unwrap_or_default();
 
-        if cfg!(test) {
-            // When configuring bootstrap for tests, make sure to set the rustc and Cargo to the
-            // same ones used to call the tests (if custom ones are not defined in the toml). If we
-            // don't do that, bootstrap will use its own detection logic to find a suitable rustc
-            // and Cargo, which doesn't work when the caller is specìfying a custom local rustc or
-            // Cargo in their bootstrap.toml.
-            build_rustc = build_rustc.take().or(std::env::var_os("RUSTC").map(|p| p.into()));
-            build_cargo = build_cargo.take().or(std::env::var_os("CARGO").map(|p| p.into()));
-        }
-
-        build_jobs = flags_jobs.or(build_jobs);
-        build_build = flags_build.or(build_build);
-
-        let build_dir = flags_build_dir.or(build_build_dir.map(PathBuf::from));
-        let host = if let Some(TargetSelectionList(hosts)) = flags_host {
-            Some(hosts)
-        } else {
-            build_host
-                .map(|file_host| file_host.iter().map(|h| TargetSelection::from_user(h)).collect())
-        };
-        let target = if let Some(TargetSelectionList(targets)) = flags_target {
-            Some(targets)
-        } else {
-            build_target.map(|file_target| {
-                file_target.iter().map(|h| TargetSelection::from_user(h)).collect()
-            })
-        };
-
-        if let Some(rustc) = &build_rustc
-            && !flags_skip_stage0_validation
-        {
-            check_stage0_version(rustc, "rustc", &config.src, config.exec_ctx());
-        }
-        if let Some(cargo) = &build_cargo
-            && !flags_skip_stage0_validation
-        {
-            check_stage0_version(cargo, "cargo", &config.src, config.exec_ctx());
+        if rust_optimize_.as_ref().is_some_and(|v| matches!(v, RustOptimize::Bool(false))) {
+            eprintln!(
+                "WARNING: setting `optimize` to `false` is known to cause errors and \
+                should be considered unsupported. Refer to `bootstrap.example.toml` \
+                for more details."
+            );
         }
 
         // Prefer CLI verbosity flags if set (`flags_verbose` > 0), otherwise take the value from
         // TOML.
-        config
-            .exec_ctx
-            .set_verbosity(cmp::max(build_verbose.unwrap_or_default() as u8, flags_verbose));
+        exec_ctx.set_verbosity(cmp::max(build_verbose.unwrap_or_default() as u8, flags_verbose));
+
+        let stage0_metadata = build_helper::stage0_parser::parse_stage0_file();
+        let bootstrap_cache_path = build_bootstrap_cache_path;
+        let patch_binaries_for_nix = build_patch_binaries_for_nix;
+
+        let path_modification_cache = Arc::new(Mutex::new(HashMap::new()));
+
+        let host_target = flags_build
+            .or(build_build)
+            .map(|build| TargetSelection::from_user(&build))
+            .unwrap_or_else(get_host_target);
+        let hosts = flags_host
+            .map(|TargetSelectionList(hosts)| hosts)
+            .or_else(|| {
+                build_host.map(|h| h.iter().map(|t| TargetSelection::from_user(t)).collect())
+            })
+            .unwrap_or_else(|| vec![host_target]);
 
-        let mut paths: Vec<PathBuf> = flags_skip.into_iter().chain(flags_exclude).collect();
-        if let Some(exclude) = build_exclude {
-            paths.extend(exclude);
-        }
+        let submodules = build_submodules;
+        let llvm_assertions = llvm_assertions_.unwrap_or(false);
 
-        // Set config values based on flags.
-        config.paths = flags_paths;
-        config.include_default_paths = flags_include_default_paths;
-        config.rustc_error_format = flags_rustc_error_format;
-        config.json_output = flags_json_output;
-        config.compile_time_deps = flags_compile_time_deps;
-        config.on_fail = flags_on_fail;
-        config.cmd = flags_cmd;
-        config.incremental = flags_incremental;
-        config.set_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;
-        config.free_args = flags_free_args;
-        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;
-        config.is_running_on_ci = flags_ci.unwrap_or(CiEnv::is_ci());
-        config.skip_std_check_if_no_download_rustc = flags_skip_std_check_if_no_download_rustc;
-
-        // Infer the rest of the configuration.
+        let mut target_config = HashMap::new();
+        let mut download_rustc_commit = None;
+        let llvm_link_shared = Cell::default();
+        let mut llvm_from_ci = false;
+        let mut channel = "dev".to_string();
+        let mut out = flags_build_dir
+            .or(build_build_dir.map(PathBuf::from))
+            .unwrap_or_else(|| PathBuf::from("build"));
+        let mut rust_info = GitInfo::Absent;
 
         if cfg!(test) {
             // Use the build directory of the original x.py invocation, so that we can set `initial_rustc` properly.
-            config.out = Path::new(
-                &env::var_os("CARGO_TARGET_DIR").expect("cargo test directly is not supported"),
-            )
-            .parent()
-            .unwrap()
-            .to_path_buf();
-        }
-
-        config.compiletest_allow_stage0 = build_compiletest_allow_stage0.unwrap_or(false);
-        config.stage0_metadata = build_helper::stage0_parser::parse_stage0_file();
-
-        config.change_id = toml.change_id.inner;
-
-        config.skip = paths
-            .into_iter()
-            .map(|p| {
-                // Never return top-level path here as it would break `--skip`
-                // logic on rustc's internal test framework which is utilized
-                // by compiletest.
-                if cfg!(windows) {
-                    PathBuf::from(p.to_str().unwrap().replace('/', "\\"))
-                } else {
-                    p
-                }
-            })
-            .collect();
-
-        #[cfg(feature = "tracing")]
-        span!(
-            target: "CONFIG_HANDLING",
-            tracing::Level::TRACE,
-            "normalizing and combining `flag.skip`/`flag.exclude` paths",
-            "config.skip" = ?config.skip,
-        );
-
-        config.jobs = Some(threads_from_config(build_jobs.unwrap_or(0)));
-        if let Some(build) = build_build {
-            config.host_target = TargetSelection::from_user(&build);
+            if out == PathBuf::from("build") {
+                out = Path::new(
+                    &env::var_os("CARGO_TARGET_DIR").expect("cargo test directly is not supported"),
+                )
+                .parent()
+                .unwrap()
+                .to_path_buf();
+            }
+            // When configuring bootstrap for tests, make sure to set the rustc and Cargo to the
+            // same ones used to call the tests (if custom ones are not defined in the toml). If we
+            // don't do that, bootstrap will use its own detection logic to find a suitable rustc
+            // and Cargo, which doesn't work when the caller is specìfying a custom local rustc or
+            // Cargo in their bootstrap.toml.
+            build_rustc = build_rustc.take().or(std::env::var_os("RUSTC").map(|p| p.into()));
+            build_cargo = build_cargo.take().or(std::env::var_os("CARGO").map(|p| p.into()));
         }
 
-        set(&mut config.out, build_dir);
         // 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() {
+        if !out.is_absolute() {
             // `canonicalize` requires the path to already exist. Use our vendored copy of `absolute` instead.
-            config.out = absolute(&config.out).expect("can't make empty path absolute");
+            out = absolute(&out).expect("can't make empty path absolute");
+        }
+
+        if !flags_skip_stage0_validation {
+            if let Some(rustc) = &build_rustc {
+                check_stage0_version(rustc, "rustc", &src, &exec_ctx);
+            }
+            if let Some(cargo) = &build_cargo {
+                check_stage0_version(cargo, "cargo", &src, &exec_ctx);
+            }
         }
 
         if build_cargo_clippy.is_some() && build_rustc.is_none() {
@@ -786,146 +735,74 @@ impl Config {
             );
         }
 
-        config.initial_rustc = if let Some(rustc) = build_rustc {
-            rustc
-        } else {
-            let dwn_ctx = DownloadContext::from(&config);
-            download_beta_toolchain(dwn_ctx);
-            config
-                .out
-                .join(config.host_target)
-                .join("stage0")
-                .join("bin")
-                .join(exe("rustc", config.host_target))
-        };
+        let mut dwn_ctx = DownloadContext::new(
+            path_modification_cache.clone(),
+            &src,
+            rust_info.clone(),
+            &submodules,
+            download_rustc_commit.clone(),
+            host_target,
+            llvm_from_ci,
+            target_config.clone(),
+            out.clone(),
+            patch_binaries_for_nix,
+            &exec_ctx,
+            &stage0_metadata,
+            llvm_assertions,
+            &bootstrap_cache_path,
+            is_running_on_ci,
+        );
+
+        let initial_rustc = build_rustc.unwrap_or_else(|| {
+            download_beta_toolchain(&dwn_ctx);
+            out.join(host_target).join("stage0").join("bin").join(exe("rustc", host_target))
+        });
 
-        config.initial_sysroot = t!(PathBuf::from_str(
-            command(&config.initial_rustc)
+        let initial_sysroot = t!(PathBuf::from_str(
+            command(&initial_rustc)
                 .args(["--print", "sysroot"])
                 .run_in_dry_run()
-                .run_capture_stdout(&config)
+                .run_capture_stdout(&exec_ctx)
                 .stdout()
                 .trim()
         ));
 
-        config.initial_cargo_clippy = build_cargo_clippy;
-
-        config.initial_cargo = if let Some(cargo) = build_cargo {
-            cargo
-        } else {
-            let dwn_ctx = DownloadContext::from(&config);
-            download_beta_toolchain(dwn_ctx);
-            config.initial_sysroot.join("bin").join(exe("cargo", config.host_target))
-        };
+        let initial_cargo = build_cargo.unwrap_or_else(|| {
+            download_beta_toolchain(&mut dwn_ctx);
+            initial_sysroot.join("bin").join(exe("cargo", host_target))
+        });
 
         // NOTE: it's important this comes *after* we set `initial_rustc` just above.
-        if config.dry_run() {
-            let dir = config.out.join("tmp-dry-run");
-            t!(fs::create_dir_all(&dir));
-            config.out = dir;
+        if exec_ctx.dry_run() {
+            out = out.join("tmp-dry-run");
+            fs::create_dir_all(&out).expect("Failed to create dry-run directory");
+            dwn_ctx.out = out.clone();
         }
 
-        config.hosts = if let Some(hosts) = host { hosts } else { vec![config.host_target] };
-        config.targets = if let Some(targets) = target {
-            targets
-        } else {
-            // If target is *not* configured, then default to the host
-            // toolchains.
-            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.lldb = build_lldb.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;
-        config.bootstrap_cache_path = build_bootstrap_cache_path;
-        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.full_bootstrap, build_full_bootstrap);
-        set(&mut config.extended, build_extended);
-        config.tools = build_tools;
-        set(&mut config.tool, build_tool);
-        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;
-
-        // Verbose flag is a good default for `rust.verbose-tests`.
-        config.verbose_tests = config.is_verbose();
-
-        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);
-
-        config.llvm_assertions = llvm_assertions.unwrap_or(false);
-
-        let file_content = t!(fs::read_to_string(config.src.join("src/ci/channel")));
+        let file_content = t!(fs::read_to_string(src.join("src/ci/channel")));
         let ci_channel = file_content.trim_end();
 
         let is_user_configured_rust_channel = match rust_channel {
-            Some(channel) if channel == "auto-detect" => {
-                config.channel = ci_channel.into();
+            Some(channel_) if channel_ == "auto-detect" => {
+                channel = ci_channel.into();
                 true
             }
-            Some(channel) => {
-                config.channel = channel;
+            Some(channel_) => {
+                channel = channel_;
                 true
             }
             None => false,
         };
 
-        let default = config.channel == "dev";
-        config.omit_git_hash = rust_omit_git_hash.unwrap_or(default);
+        let omit_git_hash = rust_omit_git_hash.unwrap_or(channel == "dev");
 
-        config.rust_info = git_info(&config.exec_ctx, config.omit_git_hash, &config.src);
-        config.cargo_info =
-            git_info(&config.exec_ctx, config.omit_git_hash, &config.src.join("src/tools/cargo"));
-        config.rust_analyzer_info = git_info(
-            &config.exec_ctx,
-            config.omit_git_hash,
-            &config.src.join("src/tools/rust-analyzer"),
-        );
-        config.clippy_info =
-            git_info(&config.exec_ctx, config.omit_git_hash, &config.src.join("src/tools/clippy"));
-        config.miri_info =
-            git_info(&config.exec_ctx, config.omit_git_hash, &config.src.join("src/tools/miri"));
-        config.rustfmt_info =
-            git_info(&config.exec_ctx, config.omit_git_hash, &config.src.join("src/tools/rustfmt"));
-        config.enzyme_info =
-            git_info(&config.exec_ctx, config.omit_git_hash, &config.src.join("src/tools/enzyme"));
-        config.in_tree_llvm_info =
-            git_info(&config.exec_ctx, false, &config.src.join("src/llvm-project"));
-        config.in_tree_gcc_info = git_info(&config.exec_ctx, false, &config.src.join("src/gcc"));
-
-        config.vendor = build_vendor.unwrap_or(
-            config.rust_info.is_from_tarball()
-                && config.src.join("vendor").exists()
-                && config.src.join(".cargo/config.toml").exists(),
-        );
+        rust_info = git_info(&exec_ctx, omit_git_hash, &src);
+        dwn_ctx.rust_info = rust_info.clone();
 
-        if !is_user_configured_rust_channel && config.rust_info.is_from_tarball() {
-            config.channel = ci_channel.into();
+        if !is_user_configured_rust_channel && rust_info.is_from_tarball() {
+            channel = ci_channel.into();
         }
 
-        config.rust_profile_use = flags_rust_profile_use;
-        config.rust_profile_generate = flags_rust_profile_generate;
-
         // FIXME(#133381): alt rustc builds currently do *not* have rustc debug assertions
         // enabled. We should not download a CI alt rustc if we need rustc to have debug
         // assertions (e.g. for crashes test suite). This can be changed once something like
@@ -951,17 +828,35 @@ impl Config {
             );
         }
 
-        let dwn_ctx = DownloadContext::from(&config);
-        config.download_rustc_commit =
-            download_ci_rustc_commit(dwn_ctx, rust_download_rustc, config.llvm_assertions);
+        download_rustc_commit =
+            download_ci_rustc_commit(&dwn_ctx, rust_download_rustc, llvm_assertions);
+        dwn_ctx.download_rustc_commit = download_rustc_commit.clone();
 
-        if debug_assertions_requested && config.download_rustc_commit.is_some() {
+        if debug_assertions_requested && download_rustc_commit.is_some() {
             eprintln!(
                 "WARN: `rust.debug-assertions = true` will prevent downloading CI rustc as alt CI \
                 rustc is not currently built with debug assertions."
             );
             // We need to put this later down_ci_rustc_commit.
-            config.download_rustc_commit = None;
+            download_rustc_commit = None;
+            dwn_ctx.download_rustc_commit = None;
+        }
+
+        // We need to override `rust.channel` if it's manually specified when using the CI rustc.
+        // This is because if the compiler uses a different channel than the one specified in bootstrap.toml,
+        // tests may fail due to using a different channel than the one used by the compiler during tests.
+        if let Some(commit) = &download_rustc_commit
+            && is_user_configured_rust_channel
+        {
+            println!(
+                "WARNING: `rust.download-rustc` is enabled. The `rust.channel` option will be overridden by the CI rustc's channel."
+            );
+
+            let channel_ = read_file_by_commit(&dwn_ctx, Path::new("src/ci/channel"), commit)
+                .trim()
+                .to_owned();
+
+            channel = channel_;
         }
 
         if let Some(t) = toml.target {
@@ -969,24 +864,22 @@ impl Config {
                 let mut target = Target::from_triple(&triple);
 
                 if let Some(ref s) = cfg.llvm_config {
-                    if config.download_rustc_commit.is_some()
-                        && triple == *config.host_target.triple
-                    {
+                    if download_rustc_commit.is_some() && triple == *host_target.triple {
                         panic!(
                             "setting llvm_config for the host is incompatible with download-rustc"
                         );
                     }
-                    target.llvm_config = Some(config.src.join(s));
+                    target.llvm_config = Some(src.join(s));
                 }
                 if let Some(patches) = cfg.llvm_has_rust_patches {
                     assert!(
-                        config.submodules == Some(false) || cfg.llvm_config.is_some(),
+                        submodules == Some(false) || cfg.llvm_config.is_some(),
                         "use of `llvm-has-rust-patches` is restricted to cases where either submodules are disabled or llvm-config been provided"
                     );
                     target.llvm_has_rust_patches = Some(patches);
                 }
                 if let Some(ref s) = cfg.llvm_filecheck {
-                    target.llvm_filecheck = Some(config.src.join(s));
+                    target.llvm_filecheck = Some(src.join(s));
                 }
                 target.llvm_libunwind = cfg.llvm_libunwind.as_ref().map(|v| {
                     v.parse().unwrap_or_else(|_| {
@@ -1023,82 +916,11 @@ impl Config {
                     })
                 });
 
-                config.target_config.insert(TargetSelection::from_user(&triple), target);
+                target_config.insert(TargetSelection::from_user(&triple), target);
             }
+            dwn_ctx.target_config = target_config.clone();
         }
 
-        if rust_optimize.as_ref().is_some_and(|v| matches!(v, RustOptimize::Bool(false))) {
-            eprintln!(
-                "WARNING: setting `optimize` to `false` is known to cause errors and \
-                should be considered unsupported. Refer to `bootstrap.example.toml` \
-                for more details."
-            );
-        }
-
-        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.rust_strip, rust_strip);
-        set(&mut config.rust_frame_pointers, rust_frame_pointers);
-        config.rust_stack_protector = rust_stack_protector;
-        set(&mut config.jemalloc, rust_jemalloc);
-        set(&mut config.test_compare_mode, rust_test_compare_mode);
-        set(&mut config.backtrace, rust_backtrace);
-        set(&mut config.rust_dist_src, rust_dist_src);
-        set(&mut config.verbose_tests, rust_verbose_tests);
-        // in the case "false" is set explicitly, do not overwrite the command line args
-        if let Some(true) = rust_incremental {
-            config.incremental = true;
-        }
-        set(&mut config.lld_mode, rust_lld_mode);
-        set(&mut config.llvm_bitcode_linker_enabled, rust_llvm_bitcode_linker);
-
-        config.rust_randomize_layout = rust_randomize_layout.unwrap_or_default();
-        config.llvm_tools_enabled = rust_llvm_tools.unwrap_or(true);
-
-        config.llvm_enzyme = 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.deny_warnings,
-            match flags_warnings {
-                Warnings::Deny => Some(true),
-                Warnings::Warn => Some(false),
-                Warnings::Default => rust_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);
-        set(&mut config.ehcont_guard, rust_ehcont_guard);
-        config.llvm_libunwind_default =
-            rust_llvm_libunwind.map(|v| v.parse().expect("failed to parse rust.llvm-libunwind"));
-        set(
-            &mut config.rust_codegen_backends,
-            rust_codegen_backends.map(|backends| parse_codegen_backends(backends, "rust")),
-        );
-
-        config.rust_codegen_units = rust_codegen_units.map(threads_from_config);
-        config.rust_codegen_units_std = rust_codegen_units_std.map(threads_from_config);
-
-        if config.rust_profile_use.is_none() {
-            config.rust_profile_use = rust_profile_use;
-        }
-
-        if config.rust_profile_generate.is_none() {
-            config.rust_profile_generate = 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_optimize = rust_optimize.unwrap_or(RustOptimize::Bool(true));
-
         // We make `x86_64-unknown-linux-gnu` use the self-contained linker by default, so we will
         // build our internal lld and use it as the default linker, by setting the `rust.lld` config
         // to true by default:
@@ -1111,105 +933,24 @@ impl Config {
         //   thus, disabled
         // - similarly, lld will not be built nor used by default when explicitly asked not to, e.g.
         //   when the config sets `rust.lld = false`
-        if default_lld_opt_in_targets().contains(&config.host_target.triple.to_string())
-            && config.hosts == [config.host_target]
+        let lld_enabled = if default_lld_opt_in_targets().contains(&host_target.triple.to_string())
+            && hosts == [host_target]
         {
-            let no_llvm_config = config
-                .target_config
-                .get(&config.host_target)
-                .is_none_or(|target_config| target_config.llvm_config.is_none());
-            let enable_lld = config.llvm_from_ci || no_llvm_config;
-            // Prefer the config setting in case an explicit opt-out is needed.
-            config.lld_enabled = rust_lld_enabled.unwrap_or(enable_lld);
+            let no_llvm_config =
+                target_config.get(&host_target).map_or(true, |config| config.llvm_config.is_none());
+            rust_lld_enabled.unwrap_or(llvm_from_ci || no_llvm_config)
         } else {
-            set(&mut config.lld_enabled, rust_lld_enabled);
-        }
-
-        let default_std_features = BTreeSet::from([String::from("panic-unwind")]);
-        config.rust_std_features = rust_std_features.unwrap_or(default_std_features);
-
-        let default = rust_debug == Some(true);
-        config.rustc_debug_assertions = rust_rustc_debug_assertions.unwrap_or(default);
-        config.std_debug_assertions =
-            rust_std_debug_assertions.unwrap_or(config.rustc_debug_assertions);
-        config.tools_debug_assertions =
-            rust_tools_debug_assertions.unwrap_or(config.rustc_debug_assertions);
-        config.rust_overflow_checks = rust_overflow_checks.unwrap_or(default);
-        config.rust_overflow_checks_std =
-            rust_overflow_checks_std.unwrap_or(config.rust_overflow_checks);
-
-        config.rust_debug_logging = rust_debug_logging.unwrap_or(config.rustc_debug_assertions);
-
-        let with_defaults = |debuginfo_level_specific: Option<_>| {
-            debuginfo_level_specific.or(rust_debuginfo_level).unwrap_or(
-                if rust_debug == Some(true) {
-                    DebuginfoLevel::Limited
-                } else {
-                    DebuginfoLevel::None
-                },
-            )
+            rust_lld_enabled.unwrap_or(false)
         };
-        config.rust_debuginfo_level_rustc = with_defaults(rust_debuginfo_level_rustc);
-        config.rust_debuginfo_level_std = with_defaults(rust_debuginfo_level_std);
-        config.rust_debuginfo_level_tools = with_defaults(rust_debuginfo_level_tools);
-        config.rust_debuginfo_level_tests =
-            rust_debuginfo_level_tests.unwrap_or(DebuginfoLevel::None);
-
-        config.reproducible_artifacts = flags_reproducible_artifact;
-        config.description = build_description;
-
-        // We need to override `rust.channel` if it's manually specified when using the CI rustc.
-        // This is because if the compiler uses a different channel than the one specified in bootstrap.toml,
-        // tests may fail due to using a different channel than the one used by the compiler during tests.
-        if let Some(commit) = &config.download_rustc_commit
-            && is_user_configured_rust_channel
-        {
-            println!(
-                "WARNING: `rust.download-rustc` is enabled. The `rust.channel` option will be overridden by the CI rustc's channel."
-            );
 
-            let dwn_ctx = DownloadContext::from(&config);
-            let channel =
-                read_file_by_commit(dwn_ctx, Path::new("src/ci/channel"), commit).trim().to_owned();
-
-            config.channel = channel;
+        if let Some(v) = llvm_link_shared_ {
+            llvm_link_shared.set(Some(v));
         }
 
-        set(&mut config.ninja_in_file, llvm_ninja);
-        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);
-        set(&mut config.llvm_libzstd, llvm_libzstd);
-        if let Some(v) = llvm_link_shared {
-            config.llvm_link_shared.set(Some(v));
-        }
-        config.llvm_targets.clone_from(&llvm_targets);
-        config.llvm_experimental_targets.clone_from(&llvm_experimental_targets);
-        config.llvm_link_jobs = llvm_link_jobs;
-        config.llvm_version_suffix.clone_from(&llvm_version_suffix);
-        config.llvm_clang_cl.clone_from(&llvm_clang_cl);
-        config.llvm_tests = llvm_tests.unwrap_or_default();
-        config.llvm_enzyme = llvm_enzyme.unwrap_or_default();
-        config.llvm_plugins = llvm_plugin.unwrap_or_default();
-
-        config.llvm_cflags.clone_from(&llvm_cflags);
-        config.llvm_cxxflags.clone_from(&llvm_cxxflags);
-        config.llvm_ldflags.clone_from(&llvm_ldflags);
-        set(&mut config.llvm_use_libcxx, llvm_use_libcxx);
-        config.llvm_use_linker.clone_from(&llvm_use_linker);
-        config.llvm_allow_old_toolchain = llvm_allow_old_toolchain.unwrap_or(false);
-        config.llvm_offload = llvm_offload.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());
-
-        let dwn_ctx = DownloadContext::from(&config);
-        config.llvm_from_ci =
-            parse_download_ci_llvm(dwn_ctx, llvm_download_ci_llvm, config.llvm_assertions);
-
-        if config.llvm_from_ci {
+        llvm_from_ci = parse_download_ci_llvm(&dwn_ctx, llvm_download_ci_llvm, llvm_assertions);
+        dwn_ctx.llvm_from_ci = llvm_from_ci;
+
+        if llvm_from_ci {
             let warn = |option: &str| {
                 println!(
                     "WARNING: `{option}` will only be used on `compiler/rustc_llvm` build, not for the LLVM build."
@@ -1223,7 +964,7 @@ impl Config {
                 warn("static-libstdcpp");
             }
 
-            if llvm_link_shared.is_some() {
+            if llvm_link_shared_.is_some() {
                 warn("link-shared");
             }
 
@@ -1232,7 +973,7 @@ impl Config {
             // config to the ones used to build the LLVM artifacts on CI, and only notify users
             // if they've chosen a different value.
 
-            if llvm_libzstd.is_some() {
+            if llvm_libzstd_.is_some() {
                 println!(
                     "WARNING: when using `download-ci-llvm`, the local `llvm.libzstd` option, \
                     like almost all `llvm.*` options, will be ignored and set by the LLVM CI \
@@ -1244,66 +985,30 @@ impl Config {
             }
         }
 
-        if !config.llvm_from_ci && config.llvm_thin_lto && llvm_link_shared.is_none() {
+        if !llvm_from_ci && llvm_thin_lto_.unwrap_or(false) && llvm_link_shared_.is_none() {
             // If we're building with ThinLTO on, by default we want to link
             // to LLVM shared, to avoid re-doing ThinLTO (which happens in
             // the link step) with each stage.
-            config.llvm_link_shared.set(Some(true));
-        }
-
-        config.gcc_ci_mode = match gcc_download_ci_gcc {
-            Some(value) => match value {
-                true => GccCiMode::DownloadFromCi,
-                false => GccCiMode::BuildLocally,
-            },
-            None => GccCiMode::default(),
-        };
-
-        match build_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 => {}
+            llvm_link_shared.set(Some(true));
         }
 
-        if config.llvm_from_ci {
-            let triple = &config.host_target.triple;
-            let dwn_ctx = DownloadContext::from(&config);
-            let ci_llvm_bin = ci_llvm_root(dwn_ctx).join("bin");
-            let build_target = config
-                .target_config
-                .entry(config.host_target)
-                .or_insert_with(|| Target::from_triple(triple));
+        if llvm_from_ci {
+            let triple = &host_target.triple;
+            let ci_llvm_bin = ci_llvm_root(&dwn_ctx).join("bin");
+            let build_target =
+                target_config.entry(host_target).or_insert_with(|| Target::from_triple(triple));
+            dwn_ctx.target_config.entry(host_target).or_insert_with(|| Target::from_triple(triple));
 
             check_ci_llvm!(build_target.llvm_config);
             check_ci_llvm!(build_target.llvm_filecheck);
-            build_target.llvm_config =
-                Some(ci_llvm_bin.join(exe("llvm-config", config.host_target)));
-            build_target.llvm_filecheck =
-                Some(ci_llvm_bin.join(exe("FileCheck", config.host_target)));
+            build_target.llvm_config = Some(ci_llvm_bin.join(exe("llvm-config", host_target)));
+            build_target.llvm_filecheck = Some(ci_llvm_bin.join(exe("FileCheck", host_target)));
         }
 
-        config.dist_sign_folder = dist_sign_folder.map(PathBuf::from);
-        config.dist_upload_addr = dist_upload_addr;
-        config.dist_compression_formats = dist_compression_formats;
-        set(&mut config.dist_compression_profile, dist_compression_profile);
-        set(&mut config.rust_dist_src, dist_src_tarball);
-        set(&mut config.dist_include_mingw_linker, dist_include_mingw_linker);
-        config.dist_vendor = dist_vendor.unwrap_or_else(|| {
-            // If we're building from git or tarball sources, enable it by default.
-            config.rust_info.is_managed_git_subrepository() || config.rust_info.is_from_tarball()
-        });
-
-        config.initial_rustfmt = if let Some(r) = build_rustfmt {
-            Some(r)
-        } else {
-            let dwn_ctx = DownloadContext::from(&config);
-            maybe_download_rustfmt(dwn_ctx)
-        };
+        let initial_rustfmt = build_rustfmt.or_else(|| maybe_download_rustfmt(&dwn_ctx));
 
-        if matches!(config.lld_mode, LldMode::SelfContained)
-            && !config.lld_enabled
+        if matches!(rust_lld_mode.unwrap_or_default(), LldMode::SelfContained)
+            && !lld_enabled
             && flags_stage.unwrap_or(0) > 0
         {
             panic!(
@@ -1311,29 +1016,13 @@ impl Config {
             );
         }
 
-        let dwn_ctx = DownloadContext::from(&config);
-        if config.lld_enabled && is_system_llvm(dwn_ctx, config.host_target) {
+        if lld_enabled && is_system_llvm(&dwn_ctx, host_target) {
             panic!("Cannot enable LLD with `rust.lld = true` when using external llvm-config.");
         }
 
-        config.optimized_compiler_builtins =
-            build_optimized_compiler_builtins.unwrap_or(config.channel != "dev");
-        config.compiletest_diff_tool = build_compiletest_diff_tool;
-        config.compiletest_use_stage0_libtest =
-            build_compiletest_use_stage0_libtest.unwrap_or(true);
-        config.tidy_extra_checks = build_tidy_extra_checks;
-
-        let download_rustc = config.download_rustc_commit.is_some();
-        config.explicit_stage_from_cli = flags_stage.is_some();
-        config.explicit_stage_from_config = build_test_stage.is_some()
-            || build_build_stage.is_some()
-            || build_doc_stage.is_some()
-            || build_dist_stage.is_some()
-            || build_install_stage.is_some()
-            || build_check_stage.is_some()
-            || build_bench_stage.is_some();
-
-        config.stage = match config.cmd {
+        let download_rustc = download_rustc_commit.is_some();
+
+        let stage = match flags_cmd {
             Subcommand::Check { .. } => flags_stage.or(build_check_stage).unwrap_or(1),
             Subcommand::Clippy { .. } | Subcommand::Fix => {
                 flags_stage.or(build_check_stage).unwrap_or(1)
@@ -1362,7 +1051,7 @@ impl Config {
         };
 
         // Now check that the selected stage makes sense, and if not, print a warning and end
-        match (config.stage, &config.cmd) {
+        match (stage, &flags_cmd) {
             (0, Subcommand::Build { .. }) => {
                 eprintln!("ERROR: cannot build anything on stage 0. Use at least stage 1.");
                 exit!(1);
@@ -1382,7 +1071,7 @@ impl Config {
             _ => {}
         }
 
-        if config.compile_time_deps && !matches!(config.cmd, Subcommand::Check { .. }) {
+        if flags_compile_time_deps && !matches!(flags_cmd, Subcommand::Check { .. }) {
             eprintln!(
                 "WARNING: Can't use --compile-time-deps with any subcommand other than check."
             );
@@ -1391,8 +1080,8 @@ impl Config {
 
         // CI should always run stage 2 builds, unless it specifically states otherwise
         #[cfg(not(test))]
-        if flags_stage.is_none() && config.is_running_on_ci {
-            match config.cmd {
+        if flags_stage.is_none() && is_running_on_ci {
+            match flags_cmd {
                 Subcommand::Test { .. }
                 | Subcommand::Miri { .. }
                 | Subcommand::Doc { .. }
@@ -1401,9 +1090,8 @@ impl Config {
                 | Subcommand::Dist
                 | Subcommand::Install => {
                     assert_eq!(
-                        config.stage, 2,
-                        "x.py should be run with `--stage 2` on CI, but was run with `--stage {}`",
-                        config.stage,
+                        stage, 2,
+                        "x.py should be run with `--stage 2` on CI, but was run with `--stage {stage}`",
                     );
                 }
                 Subcommand::Clean { .. }
@@ -1418,7 +1106,267 @@ impl Config {
             }
         }
 
-        config
+        let with_defaults = |debuginfo_level_specific: Option<_>| {
+            debuginfo_level_specific.or(rust_debuginfo_level).unwrap_or(
+                if rust_debug == Some(true) {
+                    DebuginfoLevel::Limited
+                } else {
+                    DebuginfoLevel::None
+                },
+            )
+        };
+
+        Config {
+            change_id: toml.change_id.inner,
+            bypass_bootstrap_lock: flags_bypass_bootstrap_lock,
+            ccache: match build_ccache {
+                Some(StringOrBool::String(s)) => Some(s),
+                Some(StringOrBool::Bool(true)) => Some("ccache".to_string()),
+                _ => None,
+            },
+            ninja_in_file: llvm_ninja.unwrap_or(true),
+            compiler_docs: build_compiler_docs.unwrap_or(false),
+            library_docs_private_items: build_library_docs_private_items.unwrap_or(false),
+            docs_minification: build_docs_minification.unwrap_or(true),
+            docs: build_docs.unwrap_or(true),
+            locked_deps: build_locked_deps.unwrap_or(false),
+            full_bootstrap: build_full_bootstrap.unwrap_or(false),
+            bootstrap_cache_path,
+            extended: build_extended.unwrap_or(false),
+            tools: build_tools,
+            tool: build_tool.unwrap_or_default(),
+            sanitizers: build_sanitizers.unwrap_or(false),
+            profiler: build_profiler.unwrap_or(false),
+            include_default_paths: flags_include_default_paths,
+            rustc_error_format: flags_rustc_error_format,
+            json_output: flags_json_output,
+            compile_time_deps: flags_compile_time_deps,
+            test_compare_mode: rust_test_compare_mode.unwrap_or(false),
+            color: flags_color,
+            android_ndk: build_android_ndk,
+            optimized_compiler_builtins: build_optimized_compiler_builtins
+                .unwrap_or(channel != "dev"),
+            stdout_is_tty: std::io::stdout().is_terminal(),
+            stderr_is_tty: std::io::stderr().is_terminal(),
+            on_fail: flags_on_fail,
+            explicit_stage_from_cli: flags_stage.is_some(),
+            explicit_stage_from_config: build_test_stage.is_some()
+                || build_build_stage.is_some()
+                || build_doc_stage.is_some()
+                || build_dist_stage.is_some()
+                || build_install_stage.is_some()
+                || build_check_stage.is_some()
+                || build_bench_stage.is_some(),
+
+            keep_stage: flags_keep_stage,
+            keep_stage_std: flags_keep_stage_std,
+            jobs: Some(threads_from_config(flags_jobs.or(build_jobs).unwrap_or(0))),
+            incremental: flags_incremental || rust_incremental == Some(true),
+            dump_bootstrap_shims: flags_dump_bootstrap_shims,
+            free_args: flags_free_args,
+            deny_warnings: match flags_warnings {
+                Warnings::Deny => true,
+                Warnings::Warn => false,
+                Warnings::Default => rust_deny_warnings.unwrap_or(true),
+            },
+            backtrace_on_ice: rust_backtrace_on_ice.unwrap_or(false),
+            llvm_tests: llvm_tests_.unwrap_or_default(),
+            llvm_enzyme: llvm_enzyme_.unwrap_or_default(),
+            llvm_offload: llvm_offload_.unwrap_or(false),
+            llvm_plugins: llvm_plugin.unwrap_or_default(),
+            llvm_optimize: llvm_optimize_.unwrap_or(true),
+            llvm_release_debuginfo: llvm_release_debuginfo_.unwrap_or(false),
+            llvm_static_stdcpp: llvm_static_libstdcpp.unwrap_or(false),
+            llvm_libzstd: llvm_libzstd_.unwrap_or(false),
+            llvm_clang_cl: llvm_clang_cl_,
+            llvm_targets: llvm_targets_,
+            llvm_experimental_targets: llvm_experimental_targets_,
+            llvm_link_jobs: llvm_link_jobs_,
+            llvm_version_suffix: llvm_version_suffix_,
+            llvm_use_linker: llvm_use_linker_,
+            llvm_allow_old_toolchain: llvm_allow_old_toolchain_.unwrap_or(false),
+            llvm_polly: llvm_polly_.unwrap_or(false),
+            llvm_clang: llvm_clang_.unwrap_or(false),
+            llvm_enable_warnings: llvm_enable_warnings_.unwrap_or(false),
+            llvm_build_config: llvm_build_config_.clone().unwrap_or(Default::default()),
+            llvm_tools_enabled: rust_llvm_tools.unwrap_or(true),
+            llvm_bitcode_linker_enabled: rust_llvm_bitcode_linker.unwrap_or(false),
+            llvm_cflags: llvm_cflags_,
+            llvm_cxxflags: llvm_cxxflags_,
+            llvm_ldflags: llvm_ldflags_,
+            llvm_use_libcxx: llvm_use_libcxx_.unwrap_or(false),
+            gcc_ci_mode: match gcc_download_ci_gcc {
+                Some(value) => match value {
+                    true => GccCiMode::DownloadFromCi,
+                    false => GccCiMode::BuildLocally,
+                },
+                None => GccCiMode::default(),
+            },
+            rust_optimize: rust_optimize_.unwrap_or(RustOptimize::Bool(true)),
+            rust_codegen_units: rust_codegen_units_.map(threads_from_config),
+            rust_codegen_units_std: rust_codegen_units_std_.map(threads_from_config),
+            std_debug_assertions: rust_std_debug_assertions
+                .or(rust_rustc_debug_assertions)
+                .unwrap_or(rust_debug == Some(true)),
+            tools_debug_assertions: rust_tools_debug_assertions
+                .or(rust_rustc_debug_assertions)
+                .unwrap_or(rust_debug == Some(true)),
+            rust_overflow_checks_std: rust_overflow_checks_std_
+                .or(rust_overflow_checks_)
+                .unwrap_or(rust_debug == Some(true)),
+            rust_overflow_checks: rust_overflow_checks_.unwrap_or(rust_debug == Some(true)),
+            rust_debug_logging: rust_debug_logging_
+                .or(rust_rustc_debug_assertions)
+                .unwrap_or(rust_debug == Some(true)),
+            rust_debuginfo_level_rustc: with_defaults(rust_debuginfo_level_rustc_),
+            rust_debuginfo_level_std: with_defaults(rust_debuginfo_level_std_),
+            rust_debuginfo_level_tools: with_defaults(rust_debuginfo_level_tools_),
+            rust_debuginfo_level_tests: rust_debuginfo_level_tests_.unwrap_or(DebuginfoLevel::None),
+            rust_rpath: rust_rpath_.unwrap_or(true),
+            rust_strip: rust_strip_.unwrap_or(false),
+            rust_frame_pointers: rust_frame_pointers_.unwrap_or(false),
+            rust_stack_protector: rust_stack_protector_,
+            rustc_default_linker: rust_default_linker,
+            rust_optimize_tests: rust_optimize_tests_.unwrap_or(true),
+            rust_dist_src: dist_src_tarball_.unwrap_or_else(|| rust_dist_src_.unwrap_or(true)),
+            rust_codegen_backends: rust_codegen_backends_
+                .map(|backends| parse_codegen_backends(backends, "rust"))
+                .unwrap_or(vec![CodegenBackendKind::Llvm]),
+            rust_verify_llvm_ir: rust_verify_llvm_ir_.unwrap_or(false),
+            rust_thin_lto_import_instr_limit: rust_thin_lto_import_instr_limit_,
+            rust_randomize_layout: rust_randomize_layout_.unwrap_or_default(),
+            rust_remap_debuginfo: rust_remap_debuginfo_.unwrap_or(false),
+            rust_new_symbol_mangling: rust_new_symbol_mangling_,
+            rust_profile_use: flags_rust_profile_use.or(rust_profile_use_),
+            rust_profile_generate: flags_rust_profile_generate.or(rust_profile_generate_),
+            rust_lto: rust_lto_
+                .as_deref()
+                .map(|value| RustcLto::from_str(value).unwrap())
+                .unwrap_or_default(),
+            rust_validate_mir_opts: rust_validate_mir_opts_,
+            rust_std_features: rust_std_features_
+                .unwrap_or(BTreeSet::from([String::from("panic-unwind")])),
+            llvm_profile_use: flags_llvm_profile_use,
+            llvm_profile_generate: flags_llvm_profile_generate,
+            llvm_libunwind_default: rust_llvm_libunwind
+                .map(|v| v.parse().expect("failed to parse rust.llvm-libunwind")),
+            enable_bolt_settings: flags_enable_bolt_settings,
+            reproducible_artifacts: flags_reproducible_artifact,
+            local_rebuild: build_local_rebuild.unwrap_or(false),
+            jemalloc: rust_jemalloc.unwrap_or(false),
+            control_flow_guard: rust_control_flow_guard.unwrap_or(false),
+            ehcont_guard: rust_ehcont_guard.unwrap_or(false),
+            dist_sign_folder: dist_sign_folder_.map(PathBuf::from),
+            dist_upload_addr: dist_upload_addr_,
+            dist_compression_formats: dist_compression_formats_,
+            dist_compression_profile: dist_compression_profile_.unwrap_or("fast".into()),
+            dist_include_mingw_linker: dist_include_mingw_linker_.unwrap_or(true),
+            backtrace: rust_backtrace.unwrap_or(true),
+            low_priority: build_low_priority.unwrap_or(false),
+            description: build_description,
+            verbose_tests: rust_verbose_tests.unwrap_or(exec_ctx.is_verbose()),
+            save_toolstates: rust_save_toolstates.map(PathBuf::from),
+            print_step_timings: build_print_step_timings.unwrap_or(false),
+            print_step_rusage: build_print_step_rusage.unwrap_or(false),
+            musl_root: rust_musl_root.map(PathBuf::from),
+            prefix: install_prefix.map(PathBuf::from),
+            sysconfdir: install_sysconfdir.map(PathBuf::from),
+            datadir: install_datadir.map(PathBuf::from),
+            docdir: install_docdir.map(PathBuf::from),
+            bindir: install_bindir.map(PathBuf::from).unwrap_or("bin".into()),
+            libdir: install_libdir.map(PathBuf::from),
+            mandir: install_mandir.map(PathBuf::from),
+            codegen_tests: rust_codegen_tests.unwrap_or(true),
+            nodejs: build_nodejs.map(PathBuf::from),
+            npm: build_npm.map(PathBuf::from),
+            gdb: build_gdb.map(PathBuf::from),
+            lldb: build_lldb.map(PathBuf::from),
+            python: build_python.map(PathBuf::from),
+            reuse: build_reuse.map(PathBuf::from),
+            cargo_native_static: build_cargo_native_static.unwrap_or(false),
+            configure_args: build_configure_args.unwrap_or_default(),
+            compiletest_diff_tool: build_compiletest_diff_tool,
+            compiletest_allow_stage0: build_compiletest_allow_stage0.unwrap_or(false),
+            compiletest_use_stage0_libtest: build_compiletest_use_stage0_libtest.unwrap_or(true),
+            tidy_extra_checks: build_tidy_extra_checks,
+            skip_std_check_if_no_download_rustc: flags_skip_std_check_if_no_download_rustc,
+            cargo_info: git_info(&exec_ctx, omit_git_hash, &src.join("src/tools/cargo")),
+            rust_analyzer_info: git_info(
+                &exec_ctx,
+                omit_git_hash,
+                &src.join("src/tools/rust-analyzer"),
+            ),
+            clippy_info: git_info(&exec_ctx, omit_git_hash, &src.join("src/tools/clippy")),
+            miri_info: git_info(&exec_ctx, omit_git_hash, &src.join("src/tools/miri")),
+            rustfmt_info: git_info(&exec_ctx, omit_git_hash, &src.join("src/tools/rustfmt")),
+            enzyme_info: git_info(&exec_ctx, omit_git_hash, &src.join("src/tools/enzyme")),
+            in_tree_llvm_info: git_info(&exec_ctx, false, &src.join("src/llvm-project")),
+            in_tree_gcc_info: git_info(&exec_ctx, false, &src.join("src/gcc")),
+            dist_vendor: dist_vendor_.unwrap_or_else(|| {
+                // If we're building from git or tarball sources, enable it by default.
+                rust_info.is_managed_git_subrepository() || rust_info.is_from_tarball()
+            }),
+            targets: flags_target
+                .map(|TargetSelectionList(targets)| targets)
+                .or_else(|| {
+                    build_target.map(|t| t.iter().map(|t| TargetSelection::from_user(t)).collect())
+                })
+                .unwrap_or_else(|| hosts.clone()),
+            #[allow(clippy::map_identity)]
+            skip: flags_skip
+                .into_iter()
+                .chain(flags_exclude)
+                .chain(build_exclude.unwrap_or_default())
+                .map(|p| {
+                    // Never return top-level path here as it would break `--skip`
+                    // logic on rustc's internal test framework which is utilized by compiletest.
+                    #[cfg(windows)]
+                    {
+                        PathBuf::from(p.to_string_lossy().replace('/', "\\"))
+                    }
+                    #[cfg(not(windows))]
+                    {
+                        p
+                    }
+                })
+                .collect(),
+            paths: flags_paths,
+            config: toml_path,
+            llvm_thin_lto: llvm_thin_lto_.unwrap_or(false),
+            rustc_debug_assertions: rust_rustc_debug_assertions.unwrap_or(rust_debug == Some(true)),
+            lld_mode: rust_lld_mode.unwrap_or_default(),
+            initial_cargo_clippy: build_cargo_clippy,
+            vendor: build_vendor.unwrap_or(
+                rust_info.is_from_tarball()
+                    && src.join("vendor").exists()
+                    && src.join(".cargo/config.toml").exists(),
+            ),
+            cmd: flags_cmd,
+            exec_ctx,
+            out,
+            rust_info,
+            initial_cargo,
+            initial_rustc,
+            initial_sysroot,
+            initial_rustfmt,
+            submodules,
+            target_config,
+            omit_git_hash,
+            stage,
+            src,
+            llvm_from_ci,
+            llvm_assertions,
+            lld_enabled,
+            host_target,
+            hosts,
+            channel,
+            is_running_on_ci,
+            path_modification_cache,
+            patch_binaries_for_nix,
+            stage0_metadata,
+            download_rustc_commit,
+            llvm_link_shared,
+        }
     }
 
     pub fn dry_run(&self) -> bool {
@@ -2292,7 +2240,7 @@ pub fn has_changes_from_upstream<'a>(
 )]
 pub(crate) fn update_submodule<'a>(dwn_ctx: impl AsRef<DownloadContext<'a>>, relative_path: &str) {
     let dwn_ctx = dwn_ctx.as_ref();
-    if dwn_ctx.rust_info.is_from_tarball() || !submodules_(dwn_ctx.submodules, dwn_ctx.rust_info) {
+    if dwn_ctx.rust_info.is_from_tarball() || !submodules_(dwn_ctx.submodules, &dwn_ctx.rust_info) {
         return;
     }
 
diff --git a/src/bootstrap/src/core/config/mod.rs b/src/bootstrap/src/core/config/mod.rs
index 285d20917e7..02f44d41e9e 100644
--- a/src/bootstrap/src/core/config/mod.rs
+++ b/src/bootstrap/src/core/config/mod.rs
@@ -402,12 +402,6 @@ pub enum GccCiMode {
     DownloadFromCi,
 }
 
-pub fn set<T>(field: &mut T, val: Option<T>) {
-    if let Some(v) = val {
-        *field = v;
-    }
-}
-
 pub fn threads_from_config(v: u32) -> u32 {
     match v {
         0 => std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get) as u32,
diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs
index 5ded44cef14..29a244a6c40 100644
--- a/src/bootstrap/src/core/download.rs
+++ b/src/bootstrap/src/core/download.rs
@@ -403,13 +403,13 @@ impl Config {
 pub(crate) struct DownloadContext<'a> {
     pub path_modification_cache: Arc<Mutex<HashMap<Vec<&'static str>, PathFreshness>>>,
     pub src: &'a Path,
-    pub rust_info: &'a channel::GitInfo,
+    pub rust_info: channel::GitInfo,
     pub submodules: &'a Option<bool>,
-    pub download_rustc_commit: &'a Option<String>,
+    pub download_rustc_commit: Option<String>,
     pub host_target: TargetSelection,
     pub llvm_from_ci: bool,
-    pub target_config: &'a HashMap<TargetSelection, Target>,
-    pub out: &'a Path,
+    pub target_config: HashMap<TargetSelection, Target>,
+    pub out: PathBuf,
     pub patch_binaries_for_nix: Option<bool>,
     pub exec_ctx: &'a ExecutionContext,
     pub stage0_metadata: &'a build_helper::stage0_parser::Stage0,
@@ -418,6 +418,45 @@ pub(crate) struct DownloadContext<'a> {
     pub is_running_on_ci: bool,
 }
 
+impl<'a> DownloadContext<'a> {
+    #[allow(clippy::too_many_arguments)]
+    pub fn new(
+        path_modification_cache: Arc<Mutex<HashMap<Vec<&'static str>, PathFreshness>>>,
+        src: &'a Path,
+        rust_info: channel::GitInfo,
+        submodules: &'a Option<bool>,
+        download_rustc_commit: Option<String>,
+        host_target: TargetSelection,
+        llvm_from_ci: bool,
+        target_config: HashMap<TargetSelection, Target>,
+        out: PathBuf,
+        patch_binaries_for_nix: Option<bool>,
+        exec_ctx: &'a ExecutionContext,
+        stage0_metadata: &'a build_helper::stage0_parser::Stage0,
+        llvm_assertions: bool,
+        bootstrap_cache_path: &'a Option<PathBuf>,
+        is_running_on_ci: bool,
+    ) -> Self {
+        Self {
+            path_modification_cache,
+            src,
+            rust_info,
+            submodules,
+            download_rustc_commit,
+            host_target,
+            llvm_from_ci,
+            target_config,
+            out,
+            patch_binaries_for_nix,
+            exec_ctx,
+            stage0_metadata,
+            llvm_assertions,
+            bootstrap_cache_path,
+            is_running_on_ci,
+        }
+    }
+}
+
 impl<'a> AsRef<DownloadContext<'a>> for DownloadContext<'a> {
     fn as_ref(&self) -> &DownloadContext<'a> {
         self
@@ -430,12 +469,12 @@ impl<'a> From<&'a Config> for DownloadContext<'a> {
             path_modification_cache: value.path_modification_cache.clone(),
             src: &value.src,
             host_target: value.host_target,
-            rust_info: &value.rust_info,
-            download_rustc_commit: &value.download_rustc_commit,
+            rust_info: value.rust_info.clone(),
+            download_rustc_commit: value.download_rustc_commit.clone(),
             submodules: &value.submodules,
             llvm_from_ci: value.llvm_from_ci,
-            target_config: &value.target_config,
-            out: &value.out,
+            target_config: value.target_config.clone(),
+            out: value.out.clone(),
             patch_binaries_for_nix: value.patch_binaries_for_nix,
             exec_ctx: &value.exec_ctx,
             stage0_metadata: &value.stage0_metadata,
@@ -543,13 +582,13 @@ pub(crate) fn maybe_download_rustfmt<'a>(
     );
 
     if should_fix_bins_and_dylibs(dwn_ctx.patch_binaries_for_nix, dwn_ctx.exec_ctx) {
-        fix_bin_or_dylib(dwn_ctx.out, &bin_root.join("bin").join("rustfmt"), dwn_ctx.exec_ctx);
-        fix_bin_or_dylib(dwn_ctx.out, &bin_root.join("bin").join("cargo-fmt"), dwn_ctx.exec_ctx);
+        fix_bin_or_dylib(&dwn_ctx.out, &bin_root.join("bin").join("rustfmt"), dwn_ctx.exec_ctx);
+        fix_bin_or_dylib(&dwn_ctx.out, &bin_root.join("bin").join("cargo-fmt"), dwn_ctx.exec_ctx);
         let lib_dir = bin_root.join("lib");
         for lib in t!(fs::read_dir(&lib_dir), lib_dir.display().to_string()) {
             let lib = t!(lib);
             if path_is_dylib(&lib.path()) {
-                fix_bin_or_dylib(dwn_ctx.out, &lib.path(), dwn_ctx.exec_ctx);
+                fix_bin_or_dylib(&dwn_ctx.out, &lib.path(), dwn_ctx.exec_ctx);
             }
         }
     }
@@ -615,10 +654,10 @@ fn download_toolchain<'a>(
         }
 
         if should_fix_bins_and_dylibs(dwn_ctx.patch_binaries_for_nix, dwn_ctx.exec_ctx) {
-            fix_bin_or_dylib(dwn_ctx.out, &bin_root.join("bin").join("rustc"), dwn_ctx.exec_ctx);
-            fix_bin_or_dylib(dwn_ctx.out, &bin_root.join("bin").join("rustdoc"), dwn_ctx.exec_ctx);
+            fix_bin_or_dylib(&dwn_ctx.out, &bin_root.join("bin").join("rustc"), dwn_ctx.exec_ctx);
+            fix_bin_or_dylib(&dwn_ctx.out, &bin_root.join("bin").join("rustdoc"), dwn_ctx.exec_ctx);
             fix_bin_or_dylib(
-                dwn_ctx.out,
+                &dwn_ctx.out,
                 &bin_root.join("libexec").join("rust-analyzer-proc-macro-srv"),
                 dwn_ctx.exec_ctx,
             );
@@ -626,7 +665,7 @@ fn download_toolchain<'a>(
             for lib in t!(fs::read_dir(&lib_dir), lib_dir.display().to_string()) {
                 let lib = t!(lib);
                 if path_is_dylib(&lib.path()) {
-                    fix_bin_or_dylib(dwn_ctx.out, &lib.path(), dwn_ctx.exec_ctx);
+                    fix_bin_or_dylib(&dwn_ctx.out, &lib.path(), dwn_ctx.exec_ctx);
                 }
             }
         }
@@ -963,7 +1002,7 @@ fn download_file<'a>(
         println!("download {url}");
     });
     // Use a temporary file in case we crash while downloading, to avoid a corrupt download in cache/.
-    let tempfile = tempdir(dwn_ctx.out).join(dest_path.file_name().unwrap());
+    let tempfile = tempdir(&dwn_ctx.out).join(dest_path.file_name().unwrap());
     // While bootstrap itself only supports http and https downloads, downstream forks might
     // need to download components from other protocols. The match allows them adding more
     // protocols without worrying about merge conflicts if we change the HTTP implementation.