From 75b7d24a985ef9f6bab00414752dd74ceb47d114 Mon Sep 17 00:00:00 2001 From: MarcoIeni <11428655+MarcoIeni@users.noreply.github.com> Date: Thu, 14 Aug 2025 12:38:35 +0200 Subject: [PATCH 01/16] ci: clean windows disk space in background --- .github/workflows/ci.yml | 5 ++ .../scripts/free-disk-space-windows-start.py | 72 +++++++++++++++++ .../scripts/free-disk-space-windows-wait.py | 77 +++++++++++++++++++ src/ci/scripts/free-disk-space.sh | 2 +- .../scripts/free_disk_space_windows_util.py | 29 +++++++ 5 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 src/ci/scripts/free-disk-space-windows-start.py create mode 100644 src/ci/scripts/free-disk-space-windows-wait.py create mode 100644 src/ci/scripts/free_disk_space_windows_util.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6ce543071d841..cee987dbf12ef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -223,6 +223,11 @@ jobs: cd src/ci/citool CARGO_INCREMENTAL=0 CARGO_TARGET_DIR=../../../build/citool cargo build + - name: wait for Windows disk cleanup to finish + if: ${{ matrix.free_disk && startsWith(matrix.os, 'windows-') }} + run: | + python3 src/ci/scripts/free-disk-space-windows-wait.py + - name: run the build run: | set +e diff --git a/src/ci/scripts/free-disk-space-windows-start.py b/src/ci/scripts/free-disk-space-windows-start.py new file mode 100644 index 0000000000000..fbaad722bffdb --- /dev/null +++ b/src/ci/scripts/free-disk-space-windows-start.py @@ -0,0 +1,72 @@ +""" +Start freeing disk space on Windows in the background by launching +the PowerShell cleanup script, and recording the PID in a file, +so later steps can wait for completion. +""" + +import subprocess +from pathlib import Path +from free_disk_space_windows_util import get_pid_file, get_log_file, run_main + + +def get_cleanup_script() -> Path: + script_dir = Path(__file__).resolve().parent + cleanup_script = script_dir / "free-disk-space-windows.ps1" + if not cleanup_script.exists(): + raise Exception(f"Cleanup script '{cleanup_script}' not found") + return cleanup_script + + +def write_pid(pid: int): + pid_file = get_pid_file() + if pid_file.exists(): + raise Exception(f"Pid file '{pid_file}' already exists") + pid_file.write_text(str(pid)) + print(f"wrote pid {pid} in file {pid_file}") + + +def launch_cleanup_process(): + cleanup_script = get_cleanup_script() + log_file_path = get_log_file() + # Launch the PowerShell cleanup in the background and redirect logs. + try: + with open(log_file_path, "w", encoding="utf-8") as log_file: + proc = subprocess.Popen( + [ + "pwsh", + # Suppress PowerShell startup banner/logo for cleaner logs. + "-NoLogo", + # Don't load user/system profiles. Ensures a clean, predictable environment. + "-NoProfile", + # Disable interactive prompts. Required for CI to avoid hangs. + "-NonInteractive", + # Execute the specified script file (next argument). + "-File", + str(cleanup_script), + ], + # Write child stdout to the log file. + stdout=log_file, + # Merge stderr into stdout for a single, ordered log stream. + stderr=subprocess.STDOUT, + ) + print( + f"Started free-disk-space cleanup in background. " + f"pid={proc.pid}; log_file={log_file_path}" + ) + return proc + except FileNotFoundError as e: + raise Exception("pwsh not found on PATH; cannot start disk cleanup.") from e + + +def main() -> int: + proc = launch_cleanup_process() + + # Write pid of the process to a file, so that later steps can read it and wait + # until the process completes. + write_pid(proc.pid) + + return 0 + + +if __name__ == "__main__": + run_main(main) diff --git a/src/ci/scripts/free-disk-space-windows-wait.py b/src/ci/scripts/free-disk-space-windows-wait.py new file mode 100644 index 0000000000000..b8612bb71c2c2 --- /dev/null +++ b/src/ci/scripts/free-disk-space-windows-wait.py @@ -0,0 +1,77 @@ +""" +Wait for the background Windows disk cleanup process. +""" + +import ctypes +import time +from free_disk_space_windows_util import get_pid_file, get_log_file, run_main + + +def is_process_running(pid: int) -> bool: + PROCESS_QUERY_LIMITED_INFORMATION = 0x1000 + processHandle = ctypes.windll.kernel32.OpenProcess( + PROCESS_QUERY_LIMITED_INFORMATION, 0, pid + ) + if processHandle == 0: + # The process is not running. + # If you don't have the sufficient rights to check if a process is running, + # zero is also returned. But in GitHub Actions we have these rights. + return False + else: + ctypes.windll.kernel32.CloseHandle(processHandle) + return True + + +def print_logs(): + """Print the logs from the cleanup script.""" + log_file = get_log_file() + if log_file.exists(): + print("free-disk-space logs:") + # Print entire log; replace undecodable bytes to avoid exceptions. + try: + with open(log_file, "r", encoding="utf-8", errors="replace") as f: + print(f.read()) + except Exception as e: + raise Exception(f"Failed to read log file '{log_file}'") from e + else: + print(f"::warning::Log file '{log_file}' not found") + + +def read_pid_from_file() -> int: + """Read the PID from the pid file.""" + + pid_file = get_pid_file() + if not pid_file.exists(): + raise Exception( + f"No background free-disk-space process to wait for: pid file {pid_file} not found" + ) + + pid_file_content = pid_file.read_text().strip() + + # Delete the file if it exists + pid_file.unlink(missing_ok=True) + + try: + # Read the first line and convert to int. + pid = int(pid_file_content.splitlines()[0]) + return pid + except Exception as e: + raise Exception( + f"Error while parsing the pid file with content '{pid_file_content!r}'" + ) from e + + +def main() -> int: + pid = read_pid_from_file() + + # Poll until process exits + while is_process_running(pid): + time.sleep(3) + + print_logs() + + return 0 + + +if __name__ == "__main__": + run_main(main) diff --git a/src/ci/scripts/free-disk-space.sh b/src/ci/scripts/free-disk-space.sh index 062ad801cd8d2..9264fe4de6d8e 100755 --- a/src/ci/scripts/free-disk-space.sh +++ b/src/ci/scripts/free-disk-space.sh @@ -4,7 +4,7 @@ set -euo pipefail script_dir=$(dirname "$0") if [[ "${RUNNER_OS:-}" == "Windows" ]]; then - pwsh $script_dir/free-disk-space-windows.ps1 + python3 "$script_dir/free-disk-space-windows-start.py" else $script_dir/free-disk-space-linux.sh fi diff --git a/src/ci/scripts/free_disk_space_windows_util.py b/src/ci/scripts/free_disk_space_windows_util.py new file mode 100644 index 0000000000000..488187864c2f8 --- /dev/null +++ b/src/ci/scripts/free_disk_space_windows_util.py @@ -0,0 +1,29 @@ +""" +Utilities for Windows disk space cleanup scripts. +""" + +import os +from pathlib import Path +import sys + + +def get_temp_dir() -> Path: + """Get the temporary directory set by GitHub Actions.""" + return Path(os.environ.get("RUNNER_TEMP")) + + +def get_pid_file() -> Path: + return get_temp_dir() / "free-disk-space.pid" + + +def get_log_file() -> Path: + return get_temp_dir() / "free-disk-space.log" + + +def run_main(main_fn): + exit_code = 1 + try: + exit_code = main_fn() + except Exception as e: + print(f"::error::{e}") + sys.exit(exit_code) From 5d01d90ad617349109db79cb9980267fd294d735 Mon Sep 17 00:00:00 2001 From: Jens Reidel Date: Sun, 20 Jul 2025 07:04:58 +0200 Subject: [PATCH 02/16] std: thread: Return error if setting thread stack size fails Currently, when setting the thread stack size fails, it would be rounded up to the nearest multiple of the page size and the code asserts that the next call to pthread_attr_setstacksize succeeds. This may be true for glibc, but it isn't true for musl, which not only enforces a minimum stack size, but also a maximum stack size of usize::MAX / 4 - PTHREAD_STACK_MIN [1], triggering the assert rather than erroring gracefully. There isn't any way to handle this properly other than bailing out and letting the user know it didn't succeed. [1]: https://git.musl-libc.org/cgit/musl/tree/src/thread/pthread_attr_setstacksize.c#n5 Signed-off-by: Jens Reidel --- library/std/src/sys/pal/unix/thread.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index 36e53e7cadca3..24b65c11fd2ab 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -77,7 +77,18 @@ impl Thread { let page_size = os::page_size(); let stack_size = (stack_size + page_size - 1) & (-(page_size as isize - 1) as usize - 1); - assert_eq!(libc::pthread_attr_setstacksize(attr.as_mut_ptr(), stack_size), 0); + + // Some libc implementations, e.g. musl, place an upper bound + // on the stack size, in which case we can only gracefully return + // an error here. + if libc::pthread_attr_setstacksize(attr.as_mut_ptr(), stack_size) != 0 { + assert_eq!(libc::pthread_attr_destroy(attr.as_mut_ptr()), 0); + drop(Box::from_raw(data)); + return Err(io::const_error!( + io::ErrorKind::InvalidInput, + "invalid stack size" + )); + } } }; } From 8b387d84fe6ba00561ca00f6553c46598e3f2d68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 14 Aug 2025 22:24:37 +0200 Subject: [PATCH 03/16] Deduplicate `-L` paths passed to rustc --- compiler/rustc_session/src/config.rs | 31 +++++++++++++++++++--------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index c665c85d1fea4..a0b214d4a2536 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -2828,16 +2828,27 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M // This is the location used by the `rustc-dev` `rustup` component. real_source_base_dir("lib/rustlib/rustc-src/rust", "compiler/rustc/src/main.rs"); - let mut search_paths = vec![]; - for s in &matches.opt_strs("L") { - search_paths.push(SearchPath::from_cli_opt( - sysroot.path(), - &target_triple, - early_dcx, - s, - unstable_opts.unstable_options, - )); - } + // We eagerly scan all files in each passed -L path. If the same directory is passed multiple + // times, and the directory contains a lot of files, this can take a lot of time. + // So we remove -L paths that were passed multiple times, and keep only the first occurrence. + // We still have to keep the original order of the -L arguments. + let search_paths: Vec = { + let mut seen_search_paths = FxHashSet::default(); + let search_path_matches: Vec = matches.opt_strs("L"); + search_path_matches + .iter() + .filter(|p| seen_search_paths.insert(*p)) + .map(|path| { + SearchPath::from_cli_opt( + sysroot.path(), + &target_triple, + early_dcx, + &path, + unstable_opts.unstable_options, + ) + }) + .collect() + }; let working_dir = std::env::current_dir().unwrap_or_else(|e| { early_dcx.early_fatal(format!("Current directory is invalid: {e}")); From fd5b7373db2093be7e1c906d8f92627689cc1eed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 14 Aug 2025 22:30:10 +0200 Subject: [PATCH 04/16] Do not pass duplicated `-L` arguments to merged doctests --- src/librustdoc/doctest.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 73ce62cdcde6a..95bd31729de18 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -16,7 +16,7 @@ use std::{fmt, panic, str}; pub(crate) use make::{BuildDocTestBuilder, DocTestBuilder}; pub(crate) use markdown::test as test_markdown; -use rustc_data_structures::fx::{FxHashMap, FxHasher, FxIndexMap, FxIndexSet}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxHasher, FxIndexMap, FxIndexSet}; use rustc_errors::emitter::HumanReadableErrorType; use rustc_errors::{ColorConfig, DiagCtxtHandle}; use rustc_hir as hir; @@ -689,6 +689,10 @@ fn run_test( "--extern=doctest_bundle_{edition}=", edition = doctest.edition )); + + // Deduplicate passed -L directory paths, since usually all dependencies will be in the + // same directory (e.g. target/debug/deps from Cargo). + let mut seen_search_dirs = FxHashSet::default(); for extern_str in &rustdoc_options.extern_strs { if let Some((_cratename, path)) = extern_str.split_once('=') { // Direct dependencies of the tests themselves are @@ -698,7 +702,9 @@ fn run_test( .parent() .filter(|x| x.components().count() > 0) .unwrap_or(Path::new(".")); - runner_compiler.arg("-L").arg(dir); + if seen_search_dirs.insert(dir) { + runner_compiler.arg("-L").arg(dir); + } } } let output_bundle_file = doctest From 2355563e0ad51e2285b57ddb1d3d00b90a7cef02 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Thu, 14 Aug 2025 19:40:07 -0500 Subject: [PATCH 05/16] Windows: Replace `GetThreadId`+`GetCurrentThread` with `GetCurrentThreadId` Reference: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentthreadid --- library/std/src/sys/pal/windows/c/bindings.txt | 2 +- library/std/src/sys/pal/windows/c/windows_sys.rs | 2 +- library/std/src/sys/pal/windows/thread.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library/std/src/sys/pal/windows/c/bindings.txt b/library/std/src/sys/pal/windows/c/bindings.txt index c8e4dca478162..abc1c19827fe7 100644 --- a/library/std/src/sys/pal/windows/c/bindings.txt +++ b/library/std/src/sys/pal/windows/c/bindings.txt @@ -2158,6 +2158,7 @@ GetCurrentDirectoryW GetCurrentProcess GetCurrentProcessId GetCurrentThread +GetCurrentThreadId GetEnvironmentStringsW GetEnvironmentVariableW GetExitCodeProcess @@ -2185,7 +2186,6 @@ GetSystemInfo GetSystemTimeAsFileTime GetSystemTimePreciseAsFileTime GetTempPathW -GetThreadId GetUserProfileDirectoryW GetWindowsDirectoryW HANDLE diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs index 45a273d241a98..989a1246650cd 100644 --- a/library/std/src/sys/pal/windows/c/windows_sys.rs +++ b/library/std/src/sys/pal/windows/c/windows_sys.rs @@ -38,6 +38,7 @@ windows_targets::link!("kernel32.dll" "system" fn GetCurrentDirectoryW(nbufferle windows_targets::link!("kernel32.dll" "system" fn GetCurrentProcess() -> HANDLE); windows_targets::link!("kernel32.dll" "system" fn GetCurrentProcessId() -> u32); windows_targets::link!("kernel32.dll" "system" fn GetCurrentThread() -> HANDLE); +windows_targets::link!("kernel32.dll" "system" fn GetCurrentThreadId() -> u32); windows_targets::link!("kernel32.dll" "system" fn GetEnvironmentStringsW() -> PWSTR); windows_targets::link!("kernel32.dll" "system" fn GetEnvironmentVariableW(lpname : PCWSTR, lpbuffer : PWSTR, nsize : u32) -> u32); windows_targets::link!("kernel32.dll" "system" fn GetExitCodeProcess(hprocess : HANDLE, lpexitcode : *mut u32) -> BOOL); @@ -61,7 +62,6 @@ windows_targets::link!("kernel32.dll" "system" fn GetSystemInfo(lpsysteminfo : * windows_targets::link!("kernel32.dll" "system" fn GetSystemTimeAsFileTime(lpsystemtimeasfiletime : *mut FILETIME)); windows_targets::link!("kernel32.dll" "system" fn GetSystemTimePreciseAsFileTime(lpsystemtimeasfiletime : *mut FILETIME)); windows_targets::link!("kernel32.dll" "system" fn GetTempPathW(nbufferlength : u32, lpbuffer : PWSTR) -> u32); -windows_targets::link!("kernel32.dll" "system" fn GetThreadId(thread : HANDLE) -> u32); windows_targets::link!("userenv.dll" "system" fn GetUserProfileDirectoryW(htoken : HANDLE, lpprofiledir : PWSTR, lpcchsize : *mut u32) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn GetWindowsDirectoryW(lpbuffer : PWSTR, usize : u32) -> u32); windows_targets::link!("kernel32.dll" "system" fn InitOnceBeginInitialize(lpinitonce : *mut INIT_ONCE, dwflags : u32, fpending : *mut BOOL, lpcontext : *mut *mut core::ffi::c_void) -> BOOL); diff --git a/library/std/src/sys/pal/windows/thread.rs b/library/std/src/sys/pal/windows/thread.rs index c708da5af12ac..b0e38220a2d3b 100644 --- a/library/std/src/sys/pal/windows/thread.rs +++ b/library/std/src/sys/pal/windows/thread.rs @@ -129,7 +129,7 @@ impl Thread { pub(crate) fn current_os_id() -> Option { // SAFETY: FFI call with no preconditions. - let id: u32 = unsafe { c::GetThreadId(c::GetCurrentThread()) }; + let id: u32 = unsafe { c::GetCurrentThreadId() }; // A return value of 0 indicates failed lookup. if id == 0 { None } else { Some(id.into()) } From ffdc40f30ab3ed1b4b1b7e5b2c877f21fe4e38ef Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 14 Aug 2025 17:51:48 -0700 Subject: [PATCH 06/16] bootstrap: Switch from fd-lock to native locking in std In the process, fix a race condition, by never truncating or writing to the file unless we currently hold the lock. --- src/bootstrap/Cargo.lock | 12 --------- src/bootstrap/Cargo.toml | 1 - src/bootstrap/src/bin/main.rs | 46 ++++++++++++++++------------------- 3 files changed, 21 insertions(+), 38 deletions(-) diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index 537f4b6184f1b..e5f95b8727317 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -48,7 +48,6 @@ dependencies = [ "clap", "clap_complete", "cmake", - "fd-lock", "home", "ignore", "insta", @@ -268,17 +267,6 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" -[[package]] -name = "fd-lock" -version = "4.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" -dependencies = [ - "cfg-if", - "rustix", - "windows-sys 0.59.0", -] - [[package]] name = "filetime" version = "0.2.25" diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index bdf0b42255ee5..60c3548e16de4 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -38,7 +38,6 @@ cmake = "=0.1.54" build_helper = { path = "../build_helper" } clap = { version = "4.4", default-features = false, features = ["std", "usage", "help", "derive", "error-context"] } clap_complete = "4.4" -fd-lock = "4.0" home = "0.5" ignore = "0.4" libc = "0.2" diff --git a/src/bootstrap/src/bin/main.rs b/src/bootstrap/src/bin/main.rs index 8b2d67266a77b..93c7faf4f0159 100644 --- a/src/bootstrap/src/bin/main.rs +++ b/src/bootstrap/src/bin/main.rs @@ -5,8 +5,8 @@ //! parent directory, and otherwise documentation can be found throughout the `build` //! directory in each respective module. -use std::fs::{self, OpenOptions}; -use std::io::{self, BufRead, BufReader, IsTerminal, Write}; +use std::fs::{self, OpenOptions, TryLockError}; +use std::io::{self, BufRead, BufReader, IsTerminal, Read, Write}; use std::path::Path; use std::str::FromStr; use std::time::Instant; @@ -39,38 +39,34 @@ fn main() { let config = Config::parse(flags); let mut build_lock; - let _build_lock_guard; if !config.bypass_bootstrap_lock { // Display PID of process holding the lock // PID will be stored in a lock file let lock_path = config.out.join("lock"); - let pid = fs::read_to_string(&lock_path); - - build_lock = fd_lock::RwLock::new(t!(fs::OpenOptions::new() + build_lock = t!(fs::OpenOptions::new() + .read(true) .write(true) - .truncate(true) .create(true) - .open(&lock_path))); - _build_lock_guard = match build_lock.try_write() { - Ok(mut lock) => { - t!(lock.write(process::id().to_string().as_ref())); - lock + .truncate(false) + .open(&lock_path)); + t!(build_lock.try_lock().or_else(|e| { + if let TryLockError::Error(e) = e { + return Err(e); } - err => { - drop(err); - // #135972: We can reach this point when the lock has been taken, - // but the locker has not yet written its PID to the file - if let Some(pid) = pid.ok().filter(|pid| !pid.is_empty()) { - println!("WARNING: build directory locked by process {pid}, waiting for lock"); - } else { - println!("WARNING: build directory locked, waiting for lock"); - } - let mut lock = t!(build_lock.write()); - t!(lock.write(process::id().to_string().as_ref())); - lock + let mut pid = String::new(); + t!(build_lock.read_to_string(&mut pid)); + // #135972: We can reach this point when the lock has been taken, + // but the locker has not yet written its PID to the file + if !pid.is_empty() { + println!("WARNING: build directory locked by process {pid}, waiting for lock"); + } else { + println!("WARNING: build directory locked, waiting for lock"); } - }; + build_lock.lock() + })); + t!(build_lock.set_len(0)); + t!(build_lock.write_all(process::id().to_string().as_bytes())); } // check_version warnings are not printed during setup, or during CI From 56b33cd5fa0eb2814f94f5fc636775ac70d10b0e Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 14 Aug 2025 18:02:39 -0700 Subject: [PATCH 07/16] bootstrap: Remove dependency on xattr Extracting the Rust tarballs doesn't require this. --- src/bootstrap/Cargo.lock | 15 ++------------- src/bootstrap/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index e5f95b8727317..be29e77e572af 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -747,13 +747,12 @@ dependencies = [ [[package]] name = "tar" -version = "0.4.43" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c65998313f8e17d0d553d28f91a0df93e4dbbbf770279c7bc21ca0f09ea1a1f6" +checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" dependencies = [ "filetime", "libc", - "xattr", ] [[package]] @@ -1135,16 +1134,6 @@ dependencies = [ "bitflags", ] -[[package]] -name = "xattr" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d65cbf2f12c15564212d48f4e3dfb87923d25d611f2aed18f4cb23f0413d89e" -dependencies = [ - "libc", - "rustix", -] - [[package]] name = "xz2" version = "0.1.7" diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index 60c3548e16de4..cd5a60187e51b 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -50,7 +50,7 @@ serde = "1.0" serde_derive = "1.0" serde_json = "1.0" sha2 = "0.10" -tar = "0.4" +tar = { version = "0.4.44", default-features = false } termcolor = "1.4" toml = "0.5" walkdir = "2.4" From 2a2903c49eeb63211abf1ad50d01ddd710582efe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 13 Aug 2025 09:55:01 +0200 Subject: [PATCH 08/16] Split codegen backend check step into two and don't run it with `x check compiler` --- src/bootstrap/src/core/build_steps/check.rs | 109 +++++++++++--- src/bootstrap/src/core/builder/mod.rs | 3 +- src/bootstrap/src/core/builder/tests.rs | 149 +------------------- 3 files changed, 91 insertions(+), 170 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 1e08e8547dc72..4a110b733e14d 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -314,41 +314,31 @@ pub fn prepare_compiler_for_check( } } -/// Checks a single codegen backend. +/// Check the Cranelift codegen backend. #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct CodegenBackend { - pub build_compiler: Compiler, - pub target: TargetSelection, - pub backend: CodegenBackendKind, +pub struct CraneliftCodegenBackend { + build_compiler: Compiler, + target: TargetSelection, } -impl Step for CodegenBackend { +impl Step for CraneliftCodegenBackend { type Output = (); + const IS_HOST: bool = true; const DEFAULT: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.paths(&["compiler/rustc_codegen_cranelift", "compiler/rustc_codegen_gcc"]) + run.alias("rustc_codegen_cranelift").alias("cg_clif") } fn make_run(run: RunConfig<'_>) { - // FIXME: only check the backend(s) that were actually selected in run.paths let build_compiler = prepare_compiler_for_check(run.builder, run.target, Mode::Codegen); - for backend in [CodegenBackendKind::Cranelift, CodegenBackendKind::Gcc] { - run.builder.ensure(CodegenBackend { build_compiler, target: run.target, backend }); - } + run.builder.ensure(CraneliftCodegenBackend { build_compiler, target: run.target }); } fn run(self, builder: &Builder<'_>) { - // FIXME: remove once https://github.com/rust-lang/rust/issues/112393 is resolved - if builder.build.config.vendor && self.backend.is_gcc() { - println!("Skipping checking of `rustc_codegen_gcc` with vendoring enabled."); - return; - } - let build_compiler = self.build_compiler; let target = self.target; - let backend = self.backend; let mut cargo = builder::Cargo::new( builder, @@ -361,31 +351,104 @@ impl Step for CodegenBackend { cargo .arg("--manifest-path") - .arg(builder.src.join(format!("compiler/{}/Cargo.toml", backend.crate_name()))); + .arg(builder.src.join("compiler/rustc_codegen_cranelift/Cargo.toml")); rustc_cargo_env(builder, &mut cargo, target); let _guard = builder.msg( Kind::Check, - backend.crate_name(), + "rustc_codegen_cranelift", Mode::Codegen, self.build_compiler, target, ); - let stamp = build_stamp::codegen_backend_stamp(builder, build_compiler, target, &backend) - .with_prefix("check"); + let stamp = build_stamp::codegen_backend_stamp( + builder, + build_compiler, + target, + &CodegenBackendKind::Cranelift, + ) + .with_prefix("check"); run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); } fn metadata(&self) -> Option { Some( - StepMetadata::check(&self.backend.crate_name(), self.target) + StepMetadata::check("rustc_codegen_cranelift", self.target) .built_by(self.build_compiler), ) } } +/// Check the GCC codegen backend. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct GccCodegenBackend { + build_compiler: Compiler, + target: TargetSelection, +} + +impl Step for GccCodegenBackend { + type Output = (); + + const IS_HOST: bool = true; + const DEFAULT: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.alias("rustc_codegen_gcc").alias("cg_gcc") + } + + fn make_run(run: RunConfig<'_>) { + let build_compiler = prepare_compiler_for_check(run.builder, run.target, Mode::Codegen); + run.builder.ensure(GccCodegenBackend { build_compiler, target: run.target }); + } + + fn run(self, builder: &Builder<'_>) { + // FIXME: remove once https://github.com/rust-lang/rust/issues/112393 is resolved + if builder.build.config.vendor { + println!("Skipping checking of `rustc_codegen_gcc` with vendoring enabled."); + return; + } + + let build_compiler = self.build_compiler; + let target = self.target; + + let mut cargo = builder::Cargo::new( + builder, + build_compiler, + Mode::Codegen, + SourceType::InTree, + target, + builder.kind, + ); + + cargo.arg("--manifest-path").arg(builder.src.join("compiler/rustc_codegen_gcc/Cargo.toml")); + rustc_cargo_env(builder, &mut cargo, target); + + let _guard = builder.msg( + Kind::Check, + "rustc_codegen_gcc", + Mode::Codegen, + self.build_compiler, + target, + ); + + let stamp = build_stamp::codegen_backend_stamp( + builder, + build_compiler, + target, + &CodegenBackendKind::Gcc, + ) + .with_prefix("check"); + + run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); + } + + fn metadata(&self) -> Option { + Some(StepMetadata::check("rustc_codegen_gcc", self.target).built_by(self.build_compiler)) + } +} + macro_rules! tool_check_step { ( $name:ident { diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 6226c81a3fdb7..2b521debd84d1 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1042,7 +1042,8 @@ impl<'a> Builder<'a> { Kind::Check | Kind::Fix => describe!( check::Rustc, check::Rustdoc, - check::CodegenBackend, + check::CraneliftCodegenBackend, + check::GccCodegenBackend, check::Clippy, check::Miri, check::CargoMiri, diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 9ba57542549b5..a9398a654e956 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1514,12 +1514,7 @@ mod snapshot { insta::assert_snapshot!( ctx.config("check") .path("compiler") - .render_steps(), @r" - [check] rustc 0 -> rustc 1 (73 crates) - [check] rustc 0 -> rustc 1 - [check] rustc 0 -> rustc_codegen_cranelift 1 - [check] rustc 0 -> rustc_codegen_gcc 1 - "); + .render_steps(), @"[check] rustc 0 -> rustc 1 (73 crates)"); } #[test] @@ -1545,12 +1540,7 @@ mod snapshot { ctx.config("check") .path("compiler") .stage(1) - .render_steps(), @r" - [check] rustc 0 -> rustc 1 (73 crates) - [check] rustc 0 -> rustc 1 - [check] rustc 0 -> rustc_codegen_cranelift 1 - [check] rustc 0 -> rustc_codegen_gcc 1 - "); + .render_steps(), @"[check] rustc 0 -> rustc 1 (73 crates)"); } #[test] @@ -1565,9 +1555,6 @@ mod snapshot { [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 [check] rustc 1 -> rustc 2 (73 crates) - [check] rustc 1 -> rustc 2 - [check] rustc 1 -> rustc_codegen_cranelift 2 - [check] rustc 1 -> rustc_codegen_gcc 2 "); } @@ -1679,12 +1666,7 @@ mod snapshot { ctx.config("check") .paths(&["library", "compiler"]) .args(&args) - .render_steps(), @r" - [check] rustc 0 -> rustc 1 (73 crates) - [check] rustc 0 -> rustc 1 - [check] rustc 0 -> rustc_codegen_cranelift 1 - [check] rustc 0 -> rustc_codegen_gcc 1 - "); + .render_steps(), @"[check] rustc 0 -> rustc 1 (73 crates)"); } #[test] @@ -1768,7 +1750,6 @@ mod snapshot { .render_steps(), @r" [check] rustc 0 -> rustc 1 [check] rustc 0 -> rustc_codegen_cranelift 1 - [check] rustc 0 -> rustc_codegen_gcc 1 "); } @@ -2068,130 +2049,6 @@ mod snapshot { [doc] rustc 1 -> reference (book) 2 "); } - - #[test] - fn clippy_ci() { - let ctx = TestCtx::new(); - insta::assert_snapshot!( - ctx.config("clippy") - .path("ci") - .stage(2) - .render_steps(), @r" - [build] llvm - [build] rustc 0 -> rustc 1 - [build] rustc 1 -> std 1 - [build] rustc 0 -> clippy-driver 1 - [build] rustc 0 -> cargo-clippy 1 - [clippy] rustc 1 -> bootstrap 2 - [clippy] rustc 1 -> std 1 - [clippy] rustc 1 -> rustc 2 - [check] rustc 1 -> rustc 2 - [clippy] rustc 1 -> rustc_codegen_gcc 2 - "); - } - - #[test] - fn clippy_compiler_stage1() { - let ctx = TestCtx::new(); - insta::assert_snapshot!( - ctx.config("clippy") - .path("compiler") - .render_steps(), @r" - [build] llvm - [clippy] rustc 0 -> rustc 1 - "); - } - - #[test] - fn clippy_compiler_stage2() { - let ctx = TestCtx::new(); - insta::assert_snapshot!( - ctx.config("clippy") - .path("compiler") - .stage(2) - .render_steps(), @r" - [build] llvm - [build] rustc 0 -> rustc 1 - [build] rustc 1 -> std 1 - [build] rustc 0 -> clippy-driver 1 - [build] rustc 0 -> cargo-clippy 1 - [clippy] rustc 1 -> rustc 2 - "); - } - - #[test] - fn clippy_std_stage1() { - let ctx = TestCtx::new(); - insta::assert_snapshot!( - ctx.config("clippy") - .path("std") - .render_steps(), @r" - [build] llvm - [build] rustc 0 -> rustc 1 - [build] rustc 0 -> clippy-driver 1 - [build] rustc 0 -> cargo-clippy 1 - [clippy] rustc 1 -> std 1 - "); - } - - #[test] - fn clippy_std_stage2() { - let ctx = TestCtx::new(); - insta::assert_snapshot!( - ctx.config("clippy") - .path("std") - .stage(2) - .render_steps(), @r" - [build] llvm - [build] rustc 0 -> rustc 1 - [build] rustc 1 -> std 1 - [build] rustc 1 -> rustc 2 - [build] rustc 1 -> clippy-driver 2 - [build] rustc 1 -> cargo-clippy 2 - [clippy] rustc 2 -> std 2 - "); - } - - #[test] - fn clippy_miri_stage1() { - let ctx = TestCtx::new(); - insta::assert_snapshot!( - ctx.config("clippy") - .path("miri") - .stage(1) - .render_steps(), @r" - [build] llvm - [check] rustc 0 -> rustc 1 - [clippy] rustc 0 -> miri 1 - "); - } - - #[test] - fn clippy_miri_stage2() { - let ctx = TestCtx::new(); - insta::assert_snapshot!( - ctx.config("clippy") - .path("miri") - .stage(2) - .render_steps(), @r" - [build] llvm - [build] rustc 0 -> rustc 1 - [build] rustc 1 -> std 1 - [check] rustc 1 -> rustc 2 - [build] rustc 0 -> clippy-driver 1 - [build] rustc 0 -> cargo-clippy 1 - [clippy] rustc 1 -> miri 2 - "); - } - - #[test] - fn clippy_bootstrap() { - let ctx = TestCtx::new(); - insta::assert_snapshot!( - ctx.config("clippy") - .path("bootstrap") - .render_steps(), @"[clippy] rustc 0 -> bootstrap 1 "); - } } struct ExecutedSteps { From 8a7be4ab5e2f31e3ad571e00ed0800369a88124c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 12 Aug 2025 16:45:35 +0200 Subject: [PATCH 09/16] Rename `compiler` to `build_compiler` in the `compile::Std` step --- src/bootstrap/src/core/build_steps/compile.rs | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index d860cafa1c0fd..5a44abc6c603e 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -37,11 +37,12 @@ use crate::{ debug, trace, }; -/// Build a standard library for the given `target` using the given `compiler`. +/// Build a standard library for the given `target` using the given `build_compiler`. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Std { pub target: TargetSelection, - pub compiler: Compiler, + /// Compiler that builds the standard library. + pub build_compiler: Compiler, /// Whether to build only a subset of crates in the standard library. /// /// This shouldn't be used from other steps; see the comment on [`Rustc`]. @@ -54,10 +55,10 @@ pub struct Std { } impl Std { - pub fn new(compiler: Compiler, target: TargetSelection) -> Self { + pub fn new(build_compiler: Compiler, target: TargetSelection) -> Self { Self { target, - compiler, + build_compiler, crates: Default::default(), force_recompile: false, extra_rust_args: &[], @@ -120,7 +121,7 @@ impl Step for Std { trace!(force_recompile); run.builder.ensure(Std { - compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()), + build_compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()), target: run.target, crates, force_recompile, @@ -138,8 +139,8 @@ impl Step for Std { let target = self.target; // We already have std ready to be used for stage 0. - if self.compiler.stage == 0 { - let compiler = self.compiler; + if self.build_compiler.stage == 0 { + let compiler = self.build_compiler; builder.ensure(StdLink::from_std(self, compiler)); return; @@ -148,9 +149,10 @@ impl Step for Std { let build_compiler = if builder.download_rustc() && self.force_recompile { // When there are changes in the library tree with CI-rustc, we want to build // the stageN library and that requires using stageN-1 compiler. - builder.compiler(self.compiler.stage.saturating_sub(1), builder.config.host_target) + builder + .compiler(self.build_compiler.stage.saturating_sub(1), builder.config.host_target) } else { - self.compiler + self.build_compiler }; // When using `download-rustc`, we already have artifacts for the host available. Don't @@ -299,7 +301,7 @@ impl Step for Std { } fn metadata(&self) -> Option { - Some(StepMetadata::build("std", self.target).built_by(self.compiler)) + Some(StepMetadata::build("std", self.target).built_by(self.build_compiler)) } } @@ -680,7 +682,7 @@ impl StdLink { pub fn from_std(std: Std, host_compiler: Compiler) -> Self { Self { compiler: host_compiler, - target_compiler: std.compiler, + target_compiler: std.build_compiler, target: std.target, crates: std.crates, force_recompile: std.force_recompile, From d8a51f58b3677437a574265e73a9363ac3f3d396 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 13 Aug 2025 07:31:41 +0200 Subject: [PATCH 10/16] Remove usage of `compiler_for` from the `compile::Std` step --- src/bootstrap/src/core/build_steps/compile.rs | 55 +++++++++---------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 5a44abc6c603e..ba9f006e715cc 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -189,51 +189,50 @@ impl Step for Std { let mut target_deps = builder.ensure(StartupObjects { compiler: build_compiler, target }); - let compiler_to_use = - builder.compiler_for(build_compiler.stage, build_compiler.host, target); - trace!(?compiler_to_use); - - if compiler_to_use != build_compiler - // Never uplift std unless we have compiled stage 1; if stage 1 is compiled, - // uplift it from there. - // - // FIXME: improve `fn compiler_for` to avoid adding stage condition here. - && build_compiler.stage > 1 + // Stage of the stdlib that we're building + let stage = build_compiler.stage; + + // If we're building a stage2+ libstd, full bootstrap is + // disabled and we have a stage1 libstd already compiled for the given target, + // then simply uplift a previously built stage1 library. + if build_compiler.stage > 1 + && !builder.config.full_bootstrap + // This estimates if a stage1 libstd exists for the given target. If we're not + // cross-compiling, it should definitely exist by the time we're building a stage2 + // libstd. + // Or if we are cross-compiling, and we are building a cross-compiled rustc, then that + // rustc needs to link to a cross-compiled libstd, so again we should have a stage1 + // libstd for the given target prepared. + // Even if we guess wrong in the cross-compiled case, the worst that should happen is + // that we build a fresh stage1 libstd below, and then we immediately uplift it, so we + // don't pay the libstd build cost twice. + && (target == builder.host_target || builder.config.hosts.contains(&target)) { - trace!( - ?compiler_to_use, - ?build_compiler, - "build_compiler != compiler_to_use, uplifting library" - ); + let build_compiler_for_std_to_uplift = builder.compiler(1, builder.host_target); + builder.std(build_compiler_for_std_to_uplift, target); - builder.std(compiler_to_use, target); - let msg = if compiler_to_use.host == target { + let msg = if build_compiler_for_std_to_uplift.host == target { format!( - "Uplifting library (stage{} -> stage{})", - compiler_to_use.stage, build_compiler.stage + "Uplifting library (stage{} -> stage{stage})", + build_compiler_for_std_to_uplift.stage ) } else { format!( - "Uplifting library (stage{}:{} -> stage{}:{})", - compiler_to_use.stage, compiler_to_use.host, build_compiler.stage, target + "Uplifting library (stage{}:{} -> stage{stage}:{target})", + build_compiler_for_std_to_uplift.stage, build_compiler_for_std_to_uplift.host, ) }; + builder.info(&msg); // Even if we're not building std this stage, the new sysroot must // still contain the third party objects needed by various targets. self.copy_extra_objects(builder, &build_compiler, target); - builder.ensure(StdLink::from_std(self, compiler_to_use)); + builder.ensure(StdLink::from_std(self, build_compiler_for_std_to_uplift)); return; } - trace!( - ?compiler_to_use, - ?build_compiler, - "compiler == compiler_to_use, handling not-cross-compile scenario" - ); - target_deps.extend(self.copy_extra_objects(builder, &build_compiler, target)); // We build a sysroot for mir-opt tests using the same trick that Miri does: A check build From 36dfed6435a48841e1befff580bbc26a796df79a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 13 Aug 2025 07:34:59 +0200 Subject: [PATCH 11/16] Remove usage of `compiler_for` from the `compile::Rustc` step --- src/bootstrap/src/core/build_steps/compile.rs | 94 +++++++++---------- 1 file changed, 43 insertions(+), 51 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index ba9f006e715cc..da828937861b8 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -666,6 +666,14 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, cargo: &mut Car cargo.rustdocflag("-Zcrate-attr=warn(rust_2018_idioms)"); } +/// Link all libstd rlibs/dylibs into a sysroot of `target_compiler`. +/// +/// Links those artifacts generated by `compiler` to the `stage` compiler's +/// sysroot for the specified `host` and `target`. +/// +/// Note that this assumes that `compiler` has already generated the libstd +/// libraries for `target`, and this method will find them in the relevant +/// output directory. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct StdLink { pub compiler: Compiler, @@ -952,14 +960,8 @@ impl Rustc { } impl Step for Rustc { - /// We return the stage of the "actual" compiler (not the uplifted one). - /// - /// By "actual" we refer to the uplifting logic where we may not compile the requested stage; - /// instead, we uplift it from the previous stages. Which can lead to bootstrap failures in - /// specific situations where we request stage X from other steps. However we may end up - /// uplifting it from stage Y, causing the other stage to fail when attempting to link with - /// stage X which was never actually built. - type Output = u32; + type Output = (); + const IS_HOST: bool = true; const DEFAULT: bool = false; @@ -998,7 +1000,7 @@ impl Step for Rustc { /// This will build the compiler for a particular stage of the build using /// the `build_compiler` targeting the `target` architecture. The artifacts /// created will also be linked into the sysroot directory. - fn run(self, builder: &Builder<'_>) -> u32 { + fn run(self, builder: &Builder<'_>) { let build_compiler = self.build_compiler; let target = self.target; @@ -1014,7 +1016,7 @@ impl Step for Rustc { &sysroot, builder.config.ci_rustc_dev_contents(), ); - return build_compiler.stage; + return; } // Build a standard library for `target` using the `build_compiler`. @@ -1028,31 +1030,33 @@ impl Step for Rustc { builder.info("WARNING: Use `--keep-stage-std` if you want to rebuild the compiler when it changes"); builder.ensure(RustcLink::from_rustc(self, build_compiler)); - return build_compiler.stage; + return; } - let compiler_to_use = - builder.compiler_for(build_compiler.stage, build_compiler.host, target); - if compiler_to_use != build_compiler { - builder.ensure(Rustc::new(compiler_to_use, target)); - let msg = if compiler_to_use.host == target { - format!( - "Uplifting rustc (stage{} -> stage{})", - compiler_to_use.stage, - build_compiler.stage + 1 - ) + // The stage of the compiler that we're building + let stage = build_compiler.stage + 1; + + // If we are building a stage3+ compiler, and full bootstrap is disabled, and we have a + // previous rustc available, we will uplift a compiler from a previous stage. + if build_compiler.stage >= 2 + && !builder.config.full_bootstrap + && (target == builder.host_target || builder.hosts.contains(&target)) + { + // If we're cross-compiling, the earliest rustc that we could have is stage 2. + // If we're not cross-compiling, then we should have rustc stage 1. + let stage_to_uplift = if target == builder.host_target { 1 } else { 2 }; + let rustc_to_uplift = builder.compiler(stage_to_uplift, target); + let msg = if rustc_to_uplift.host == target { + format!("Uplifting rustc (stage{} -> stage{stage})", rustc_to_uplift.stage,) } else { format!( - "Uplifting rustc (stage{}:{} -> stage{}:{})", - compiler_to_use.stage, - compiler_to_use.host, - build_compiler.stage + 1, - target + "Uplifting rustc (stage{}:{} -> stage{stage}:{target})", + rustc_to_uplift.stage, rustc_to_uplift.host, ) }; builder.info(&msg); - builder.ensure(RustcLink::from_rustc(self, compiler_to_use)); - return compiler_to_use.stage; + builder.ensure(RustcLink::from_rustc(self, rustc_to_uplift)); + return; } // Build a standard library for the current host target using the `build_compiler`. @@ -1129,8 +1133,6 @@ impl Step for Rustc { self, builder.compiler(build_compiler.stage, builder.config.host_target), )); - - build_compiler.stage } fn metadata(&self) -> Option { @@ -1910,12 +1912,18 @@ impl Step for Sysroot { } } +/// Prepare a compiler sysroot. +/// +/// The sysroot may contain various things useful for running the compiler, like linkers and +/// linker wrappers (LLD, LLVM bitcode linker, etc.). +/// +/// This will assemble a compiler in `build/$target/stage$stage`. #[derive(Debug, PartialOrd, Ord, Clone, PartialEq, Eq, Hash)] pub struct Assemble { /// The compiler which we will produce in this step. Assemble itself will /// take care of ensuring that the necessary prerequisites to do so exist, - /// that is, this target can be a stage2 compiler and Assemble will build - /// previous stages for you. + /// that is, this can be e.g. a stage2 compiler and Assemble will build + /// the previous stages for you. pub target_compiler: Compiler, } @@ -1933,11 +1941,6 @@ impl Step for Assemble { }); } - /// Prepare a new compiler from the artifacts in `stage` - /// - /// This will assemble a compiler in `build/$host/stage$stage`. The compiler - /// must have been previously produced by the `stage - 1` builder.build - /// compiler. fn run(self, builder: &Builder<'_>) -> Compiler { let target_compiler = self.target_compiler; @@ -2066,7 +2069,7 @@ impl Step for Assemble { target_compiler.stage - 1, builder.config.host_target, ); - let mut build_compiler = + let build_compiler = builder.compiler(target_compiler.stage - 1, builder.config.host_target); // Build enzyme @@ -2090,24 +2093,13 @@ impl Step for Assemble { } // Build the libraries for this compiler to link to (i.e., the libraries - // it uses at runtime). NOTE: Crates the target compiler compiles don't - // link to these. (FIXME: Is that correct? It seems to be correct most - // of the time but I think we do link to these for stage2/bin compilers - // when not performing a full bootstrap). + // it uses at runtime). debug!( ?build_compiler, "target_compiler.host" = ?target_compiler.host, "building compiler libraries to link to" ); - let actual_stage = builder.ensure(Rustc::new(build_compiler, target_compiler.host)); - // Current build_compiler.stage might be uplifted instead of being built; so update it - // to not fail while linking the artifacts. - debug!( - "(old) build_compiler.stage" = build_compiler.stage, - "(adjusted) build_compiler.stage" = actual_stage, - "temporarily adjusting `build_compiler.stage` to account for uplifted libraries" - ); - build_compiler.stage = actual_stage; + builder.ensure(Rustc::new(build_compiler, target_compiler.host)); let stage = target_compiler.stage; let host = target_compiler.host; From f2c2d3ebc329865a1a5c2f41420711c2b1032c45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 12 Aug 2025 17:19:45 +0200 Subject: [PATCH 12/16] Clarify that `build.full-bootstrap` is only used to affect uplifting, not stage selection --- bootstrap.example.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bootstrap.example.toml b/bootstrap.example.toml index 89da6eeb53153..41649fd796c1b 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -345,9 +345,9 @@ # want to use vendoring. See https://forge.rust-lang.org/infra/other-installation-methods.html#source-code. #build.vendor = if "is a tarball source" && "vendor" dir exists && ".cargo/config.toml" file exists { true } else { false } -# Typically the build system will build the Rust compiler twice. The second -# compiler, however, will simply use its own libraries to link against. If you -# would rather to perform a full bootstrap, compiling the compiler three times, +# If you build the compiler more than twice (stage3+) or the standard library more than once +# (stage 2+), the third compiler and second library will get uplifted from stage2 and stage1, +# respectively. If you would like to disable this uplifting, and rather perform a full bootstrap, # then you can set this option to true. # # This is only useful for verifying that rustc generates reproducible builds. From cda2114c655a1eab26c8c8678eb23525733353dc Mon Sep 17 00:00:00 2001 From: AMS21 Date: Fri, 15 Aug 2025 09:57:24 +0200 Subject: [PATCH 13/16] Fix typos in bootstrap.example.toml equivelent -> equivalent recommeded -> recommended --- bootstrap.example.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bootstrap.example.toml b/bootstrap.example.toml index 89da6eeb53153..2e448f5df92c2 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -9,7 +9,7 @@ # a custom configuration file can also be specified with `--config` to the build # system. # -# Note that the following are equivelent, for more details see . +# Note that the following are equivalent, for more details see . # # build.verbose = 1 # @@ -482,7 +482,7 @@ # Use `--extra-checks=''` to temporarily disable all extra checks. # # Automatically enabled in the "tools" profile. -# Set to the empty string to force disable (recommeded for hdd systems). +# Set to the empty string to force disable (recommended for hdd systems). #build.tidy-extra-checks = "" # Indicates whether ccache is used when building certain artifacts (e.g. LLVM). From ae4eeb9299b5938cca183ad7a90e70a1bb4b1174 Mon Sep 17 00:00:00 2001 From: Urgau Date: Fri, 15 Aug 2025 11:59:23 +0200 Subject: [PATCH 14/16] Fix wrong spans with external macros in the `dropping_copy_types` lint --- compiler/rustc_lint/src/drop_forget_useless.rs | 2 +- tests/ui/lint/dropping_copy_types-macros.fixed | 3 +++ tests/ui/lint/dropping_copy_types-macros.rs | 3 +++ tests/ui/lint/dropping_copy_types-macros.stderr | 16 +++++++++++++++- 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_lint/src/drop_forget_useless.rs b/compiler/rustc_lint/src/drop_forget_useless.rs index 7f098893f7de5..c2d137986ce4d 100644 --- a/compiler/rustc_lint/src/drop_forget_useless.rs +++ b/compiler/rustc_lint/src/drop_forget_useless.rs @@ -151,7 +151,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetUseless { && let Node::Stmt(stmt) = node && let StmtKind::Semi(e) = stmt.kind && e.hir_id == expr.hir_id - && let Some(arg_span) = arg.span.find_ancestor_inside(expr.span) + && let Some(arg_span) = arg.span.find_ancestor_inside_same_ctxt(expr.span) { UseLetUnderscoreIgnoreSuggestion::Suggestion { start_span: expr.span.shrink_to_lo().until(arg_span), diff --git a/tests/ui/lint/dropping_copy_types-macros.fixed b/tests/ui/lint/dropping_copy_types-macros.fixed index a8ceedadc8063..2a0c398815755 100644 --- a/tests/ui/lint/dropping_copy_types-macros.fixed +++ b/tests/ui/lint/dropping_copy_types-macros.fixed @@ -9,4 +9,7 @@ fn main() { let mut msg = String::new(); let _ = writeln!(&mut msg, "test"); //~^ ERROR calls to `std::mem::drop` + + let _ = format_args!("a"); + //~^ ERROR calls to `std::mem::drop` } diff --git a/tests/ui/lint/dropping_copy_types-macros.rs b/tests/ui/lint/dropping_copy_types-macros.rs index b249b0c868f37..2272e0e8560d1 100644 --- a/tests/ui/lint/dropping_copy_types-macros.rs +++ b/tests/ui/lint/dropping_copy_types-macros.rs @@ -9,4 +9,7 @@ fn main() { let mut msg = String::new(); drop(writeln!(&mut msg, "test")); //~^ ERROR calls to `std::mem::drop` + + drop(format_args!("a")); + //~^ ERROR calls to `std::mem::drop` } diff --git a/tests/ui/lint/dropping_copy_types-macros.stderr b/tests/ui/lint/dropping_copy_types-macros.stderr index 117e9f4fe099c..5048f6e3f18bf 100644 --- a/tests/ui/lint/dropping_copy_types-macros.stderr +++ b/tests/ui/lint/dropping_copy_types-macros.stderr @@ -17,5 +17,19 @@ LL - drop(writeln!(&mut msg, "test")); LL + let _ = writeln!(&mut msg, "test"); | -error: aborting due to 1 previous error +error: calls to `std::mem::drop` with a value that implements `Copy` does nothing + --> $DIR/dropping_copy_types-macros.rs:13:5 + | +LL | drop(format_args!("a")); + | ^^^^^-----------------^ + | | + | argument has type `Arguments<'_>` + | +help: use `let _ = ...` to ignore the expression or result + | +LL - drop(format_args!("a")); +LL + let _ = format_args!("a"); + | + +error: aborting due to 2 previous errors From dbd5addf884158385782609405abb1161b827b15 Mon Sep 17 00:00:00 2001 From: AMS21 Date: Fri, 15 Aug 2025 11:32:22 +0200 Subject: [PATCH 15/16] Enhance UI test output handling for runtime errors When a UI test runs a compiled binary and an error/forbid pattern check fails, the failure message previously only showed compiler output, hiding the executed programs stdout/stderr. This makes it harder to see near-miss or unexpected runtime lines. --- src/tools/compiletest/src/runtest/ui.rs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/tools/compiletest/src/runtest/ui.rs b/src/tools/compiletest/src/runtest/ui.rs index 0507c2600aed3..40b0ee0a399d4 100644 --- a/src/tools/compiletest/src/runtest/ui.rs +++ b/src/tools/compiletest/src/runtest/ui.rs @@ -10,6 +10,7 @@ use super::{ TestCx, TestOutput, Truncated, UI_FIXED, WillExecute, }; use crate::json; +use crate::runtest::ProcRes; impl TestCx<'_> { pub(super) fn run_ui_test(&self) { @@ -127,6 +128,9 @@ impl TestCx<'_> { ); } + // If the test is executed, capture its ProcRes separately so that + // pattern/forbid checks can report the *runtime* stdout/stderr when they fail. + let mut run_proc_res: Option = None; let output_to_check = if let WillExecute::Yes = should_run { let proc_res = self.exec_compiled_test(); let run_output_errors = if self.props.check_run_results { @@ -189,7 +193,10 @@ impl TestCx<'_> { unreachable!("run_ui_test() must not be called if the test should not run"); } - self.get_output(&proc_res) + let output = self.get_output(&proc_res); + // Move the proc_res into our option after we've extracted output. + run_proc_res = Some(proc_res); + output } else { self.get_output(&proc_res) }; @@ -200,9 +207,14 @@ impl TestCx<'_> { explicit, self.config.compare_mode, proc_res.status, self.props.error_patterns ); + // Compiler diagnostics (expected errors) are always tied to the compile-time ProcRes. self.check_expected_errors(&proc_res); - self.check_all_error_patterns(&output_to_check, &proc_res); - self.check_forbid_output(&output_to_check, &proc_res); + + // For runtime pattern/forbid checks prefer the executed program's ProcRes if available + // so that missing pattern failures include the program's stdout/stderr. + let pattern_proc_res = run_proc_res.as_ref().unwrap_or(&proc_res); + self.check_all_error_patterns(&output_to_check, pattern_proc_res); + self.check_forbid_output(&output_to_check, pattern_proc_res); if self.props.run_rustfix && self.config.compare_mode.is_none() { // And finally, compile the fixed code and make sure it both From 26817eec0520f30b8228990614ff2caa04811fcc Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Fri, 15 Aug 2025 21:34:58 +0800 Subject: [PATCH 16/16] Autolabel `src/tools/{rustfmt,rust-analyzer}` changes with `T-{rustfmt,rust-analyzer}` --- triagebot.toml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/triagebot.toml b/triagebot.toml index 6f6e95c5b50e9..497c4d020cd62 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -628,6 +628,16 @@ trigger_files = [ "src/ci", ] +[autolabel."T-rust-analyzer"] +trigger_files = [ + "src/tools/rust-analyzer", +] + +[autolabel."T-rustfmt"] +trigger_files = [ + "src/tools/rustfmt", +] + # ------------------------------------------------------------------------------ # Prioritization and team nominations # ------------------------------------------------------------------------------