diff options
| author | The Miri Cronjob Bot <miri@cron.bot> | 2025-08-27 05:00:43 +0000 |
|---|---|---|
| committer | The Miri Cronjob Bot <miri@cron.bot> | 2025-08-27 05:00:43 +0000 |
| commit | aa583798abbead86c01450106088efba8dc0d54e (patch) | |
| tree | d3bde15db472993fc1d801f93f3c86ab8f669971 /src | |
| parent | a10bdf93b153211e5daf1b1c147b955e690e30ef (diff) | |
| parent | b8160e9f38329c5c17f642f3e7e8ac702375dad5 (diff) | |
| download | rust-aa583798abbead86c01450106088efba8dc0d54e.tar.gz rust-aa583798abbead86c01450106088efba8dc0d54e.zip | |
Merge ref '269d5b56bcfd' from rust-lang/rust
Pull recent changes from https://github.com/rust-lang/rust via Josh. Upstream ref: 269d5b56bcfdf2be82213e72ef9a2e4c592a8c6b Filtered ref: a221b1d3ebb78ec8a01dcb1fe6bb165378e2f5c9 This merge was created using https://github.com/rust-lang/josh-sync.
Diffstat (limited to 'src')
85 files changed, 1762 insertions, 727 deletions
diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 40e08361a0f..2ece53eb0cc 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -312,6 +312,12 @@ def default_build_triple(verbose): kernel, cputype, processor = uname.decode(default_encoding).split(maxsplit=2) + # ON NetBSD, use `uname -p` to set the CPU type + if kernel == "NetBSD": + cputype = ( + subprocess.check_output(["uname", "-p"]).strip().decode(default_encoding) + ) + # The goal here is to come up with the same triple as LLVM would, # at least for the subset of platforms we're willing to target. kerneltype_mapper = { @@ -433,10 +439,16 @@ def default_build_triple(verbose): kernel = "linux-androideabi" else: kernel += "eabihf" - elif cputype in {"armv7l", "armv8l"}: + elif cputype in {"armv6hf", "earmv6hf"}: + cputype = "armv6" + if kernel == "unknown-netbsd": + kernel += "-eabihf" + elif cputype in {"armv7l", "earmv7hf", "armv8l"}: cputype = "armv7" if kernel == "linux-android": kernel = "linux-androideabi" + elif kernel == "unknown-netbsd": + kernel += "-eabihf" else: kernel += "eabihf" elif cputype == "mips": diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 6ca32aca345..d30005c8d51 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -26,7 +26,9 @@ use crate::core::builder; use crate::core::builder::{ Builder, Cargo, Kind, RunConfig, ShouldRun, Step, StepMetadata, crate_description, }; -use crate::core::config::{DebuginfoLevel, LlvmLibunwind, RustcLto, TargetSelection}; +use crate::core::config::{ + CompilerBuiltins, DebuginfoLevel, LlvmLibunwind, RustcLto, TargetSelection, +}; use crate::utils::build_stamp; use crate::utils::build_stamp::BuildStamp; use crate::utils::exec::command; @@ -96,10 +98,36 @@ impl Std { } deps } + + /// Returns true if the standard library will be uplifted from stage 1 for the given + /// `build_compiler` (which determines the stdlib stage) and `target`. + /// + /// Uplifting is enabled if we're building a stage2+ libstd, full bootstrap is + /// disabled and we have a stage1 libstd already compiled for the given target. + pub fn should_be_uplifted_from_stage_1( + builder: &Builder<'_>, + stage: u32, + target: TargetSelection, + ) -> bool { + 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)) + } } impl Step for Std { - type Output = (); + /// Build stamp of std, if it was indeed built or uplifted. + type Output = Option<BuildStamp>; + const DEFAULT: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -136,15 +164,20 @@ impl Step for Std { /// This will build the standard library for a particular stage of the build /// using the `compiler` targeting the `target` architecture. The artifacts /// created will also be linked into the sysroot directory. - fn run(self, builder: &Builder<'_>) { + fn run(self, builder: &Builder<'_>) -> Self::Output { let target = self.target; - // We already have std ready to be used for stage 0. - if self.build_compiler.stage == 0 { + // In most cases, we already have the std ready to be used for stage 0. + // However, if we are doing a local rebuild (so the build compiler can compile the standard + // library even on stage 0), and we're cross-compiling (so the stage0 standard library for + // *target* is not available), we still allow the stdlib to be built here. + if self.build_compiler.stage == 0 + && !(builder.local_rebuild && target != builder.host_target) + { let compiler = self.build_compiler; builder.ensure(StdLink::from_std(self, compiler)); - return; + return None; } let build_compiler = if builder.download_rustc() && self.force_recompile { @@ -169,7 +202,7 @@ impl Step for Std { &sysroot, builder.config.ci_rust_std_contents(), ); - return; + return None; } if builder.config.keep_stage.contains(&build_compiler.stage) @@ -185,7 +218,7 @@ impl Step for Std { self.copy_extra_objects(builder, &build_compiler, target); builder.ensure(StdLink::from_std(self, build_compiler)); - return; + return Some(build_stamp::libstd_stamp(builder, build_compiler, target)); } let mut target_deps = builder.ensure(StartupObjects { compiler: build_compiler, target }); @@ -193,24 +226,9 @@ impl Step for Std { // 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)) - { + if Self::should_be_uplifted_from_stage_1(builder, build_compiler.stage, target) { let build_compiler_for_std_to_uplift = builder.compiler(1, builder.host_target); - builder.std(build_compiler_for_std_to_uplift, target); + let stage_1_stamp = builder.std(build_compiler_for_std_to_uplift, target); let msg = if build_compiler_for_std_to_uplift.host == target { format!( @@ -231,7 +249,7 @@ impl Step for Std { self.copy_extra_objects(builder, &build_compiler, target); builder.ensure(StdLink::from_std(self, build_compiler_for_std_to_uplift)); - return; + return stage_1_stamp; } target_deps.extend(self.copy_extra_objects(builder, &build_compiler, target)); @@ -284,11 +302,13 @@ impl Step for Std { build_compiler, target, ); + + let stamp = build_stamp::libstd_stamp(builder, build_compiler, target); run_cargo( builder, cargo, vec![], - &build_stamp::libstd_stamp(builder, build_compiler, target), + &stamp, target_deps, self.is_for_mir_opt_tests, // is_check false, @@ -298,6 +318,7 @@ impl Step for Std { self, builder.compiler(build_compiler.stage, builder.config.host_target), )); + Some(stamp) } fn metadata(&self) -> Option<StepMetadata> { @@ -560,29 +581,36 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, cargo: &mut Car // If `compiler-rt` is available ensure that the `c` feature of the // `compiler-builtins` crate is enabled and it's configured to learn where // `compiler-rt` is located. - let compiler_builtins_c_feature = if builder.config.optimized_compiler_builtins(target) { - // NOTE: this interacts strangely with `llvm-has-rust-patches`. In that case, we enforce `submodules = false`, so this is a no-op. - // But, the user could still decide to manually use an in-tree submodule. - // - // NOTE: if we're using system llvm, we'll end up building a version of `compiler-rt` that doesn't match the LLVM we're linking to. - // That's probably ok? At least, the difference wasn't enforced before. There's a comment in - // the compiler_builtins build script that makes me nervous, though: - // https://github.com/rust-lang/compiler-builtins/blob/31ee4544dbe47903ce771270d6e3bea8654e9e50/build.rs#L575-L579 - builder.require_submodule( - "src/llvm-project", - Some( - "The `build.optimized-compiler-builtins` config option \ - requires `compiler-rt` sources from LLVM.", - ), - ); - let compiler_builtins_root = builder.src.join("src/llvm-project/compiler-rt"); - assert!(compiler_builtins_root.exists()); - // The path to `compiler-rt` is also used by `profiler_builtins` (above), - // so if you're changing something here please also change that as appropriate. - cargo.env("RUST_COMPILER_RT_ROOT", &compiler_builtins_root); - " compiler-builtins-c" - } else { - "" + let compiler_builtins_c_feature = match builder.config.optimized_compiler_builtins(target) { + CompilerBuiltins::LinkLLVMBuiltinsLib(path) => { + cargo.env("LLVM_COMPILER_RT_LIB", path); + " compiler-builtins-c" + } + CompilerBuiltins::BuildLLVMFuncs => { + // NOTE: this interacts strangely with `llvm-has-rust-patches`. In that case, we enforce + // `submodules = false`, so this is a no-op. But, the user could still decide to + // manually use an in-tree submodule. + // + // NOTE: if we're using system llvm, we'll end up building a version of `compiler-rt` + // that doesn't match the LLVM we're linking to. That's probably ok? At least, the + // difference wasn't enforced before. There's a comment in the compiler_builtins build + // script that makes me nervous, though: + // https://github.com/rust-lang/compiler-builtins/blob/31ee4544dbe47903ce771270d6e3bea8654e9e50/build.rs#L575-L579 + builder.require_submodule( + "src/llvm-project", + Some( + "The `build.optimized-compiler-builtins` config option \ + requires `compiler-rt` sources from LLVM.", + ), + ); + let compiler_builtins_root = builder.src.join("src/llvm-project/compiler-rt"); + assert!(compiler_builtins_root.exists()); + // The path to `compiler-rt` is also used by `profiler_builtins` (above), + // so if you're changing something here please also change that as appropriate. + cargo.env("RUST_COMPILER_RT_ROOT", &compiler_builtins_root); + " compiler-builtins-c" + } + CompilerBuiltins::BuildRustOnly => "", }; // `libtest` uses this to know whether or not to support @@ -1309,9 +1337,7 @@ pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetS cargo.env("CFG_OMIT_GIT_HASH", "1"); } - if let Some(backend) = builder.config.default_codegen_backend(target) { - cargo.env("CFG_DEFAULT_CODEGEN_BACKEND", backend.name()); - } + cargo.env("CFG_DEFAULT_CODEGEN_BACKEND", builder.config.default_codegen_backend(target).name()); let libdir_relative = builder.config.libdir_relative().unwrap_or_else(|| Path::new("lib")); let target_config = builder.config.target_config.get(&target); @@ -2008,6 +2034,7 @@ impl Step for Assemble { let host_llvm_bin_dir = command(&host_llvm_config) .arg("--bindir") + .cached() .run_capture_stdout(builder) .stdout() .trim() diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index daac75c03e2..778c3beb50f 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -21,7 +21,9 @@ use tracing::instrument; use crate::core::build_steps::compile::{get_codegen_backend_file, normalize_codegen_backend_name}; use crate::core::build_steps::doc::DocumentationFormat; -use crate::core::build_steps::tool::{self, RustcPrivateCompilers, Tool}; +use crate::core::build_steps::tool::{ + self, RustcPrivateCompilers, Tool, ToolTargetBuildMode, get_tool_target_compiler, +}; use crate::core::build_steps::vendor::{VENDOR_DIR, Vendor}; use crate::core::build_steps::{compile, llvm}; use crate::core::builder::{Builder, Kind, RunConfig, ShouldRun, Step, StepMetadata}; @@ -75,7 +77,10 @@ impl Step for Docs { /// Builds the `rust-docs` installer component. fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> { let host = self.host; - builder.default_doc(&[]); + // FIXME: explicitly enumerate the steps that should be executed here, and gather their + // documentation, rather than running all default steps and then read their output + // from a shared directory. + builder.run_default_doc_steps(); let dest = "share/doc/rust/html"; @@ -132,13 +137,20 @@ impl Step for JsonDocs { } } +/// Builds the `rustc-docs` installer component. +/// Apart from the documentation of the `rustc_*` crates, it also includes the documentation of +/// various in-tree helper tools (bootstrap, build_helper, tidy), +/// and also rustc_private tools like rustdoc, clippy, miri or rustfmt. +/// +/// It is currently hosted at <https://doc.rust-lang.org/nightly/nightly-rustc>. #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct RustcDocs { - pub host: TargetSelection, + target: TargetSelection, } impl Step for RustcDocs { - type Output = Option<GeneratedTarball>; + type Output = GeneratedTarball; + const DEFAULT: bool = true; const IS_HOST: bool = true; @@ -148,18 +160,17 @@ impl Step for RustcDocs { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(RustcDocs { host: run.target }); + run.builder.ensure(RustcDocs { target: run.target }); } - /// Builds the `rustc-docs` installer component. - fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> { - let host = self.host; - builder.default_doc(&[]); + fn run(self, builder: &Builder<'_>) -> Self::Output { + let target = self.target; + builder.run_default_doc_steps(); - let mut tarball = Tarball::new(builder, "rustc-docs", &host.triple); + let mut tarball = Tarball::new(builder, "rustc-docs", &target.triple); tarball.set_product_name("Rustc Documentation"); - tarball.add_bulk_dir(builder.compiler_doc_out(host), "share/doc/rust/html/rustc"); - Some(tarball.generate()) + tarball.add_bulk_dir(builder.compiler_doc_out(target), "share/doc/rust/html/rustc"); + tarball.generate() } } @@ -354,9 +365,13 @@ fn get_cc_search_dirs( (bin_path, lib_path) } +/// Builds the `rust-mingw` installer component. +/// +/// This contains all the bits and pieces to run the MinGW Windows targets +/// without any extra installed software (e.g., we bundle gcc, libraries, etc.). #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Mingw { - pub host: TargetSelection, + target: TargetSelection, } impl Step for Mingw { @@ -368,39 +383,46 @@ impl Step for Mingw { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(Mingw { host: run.target }); + run.builder.ensure(Mingw { target: run.target }); } - /// Builds the `rust-mingw` installer component. - /// - /// This contains all the bits and pieces to run the MinGW Windows targets - /// without any extra installed software (e.g., we bundle gcc, libraries, etc). fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> { - let host = self.host; - if !host.ends_with("pc-windows-gnu") || !builder.config.dist_include_mingw_linker { + let target = self.target; + if !target.ends_with("pc-windows-gnu") || !builder.config.dist_include_mingw_linker { return None; } - let mut tarball = Tarball::new(builder, "rust-mingw", &host.triple); + let mut tarball = Tarball::new(builder, "rust-mingw", &target.triple); tarball.set_product_name("Rust MinGW"); - make_win_dist(tarball.image_dir(), host, builder); + make_win_dist(tarball.image_dir(), target, builder); Some(tarball.generate()) } fn metadata(&self) -> Option<StepMetadata> { - Some(StepMetadata::dist("mingw", self.host)) + Some(StepMetadata::dist("mingw", self.target)) } } +/// Creates the `rustc` installer component. +/// +/// This includes: +/// - The compiler and LLVM. +/// - Debugger scripts. +/// - Various helper tools, e.g. LLD or Rust Analyzer proc-macro server (if enabled). +/// - The licenses of all code used by the compiler. +/// +/// It does not include any standard library. #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Rustc { - pub compiler: Compiler, + /// This is the compiler that we will *ship* in this dist step. + pub target_compiler: Compiler, } impl Step for Rustc { type Output = GeneratedTarball; + const DEFAULT: bool = true; const IS_HOST: bool = true; @@ -409,19 +431,19 @@ impl Step for Rustc { } fn make_run(run: RunConfig<'_>) { - run.builder - .ensure(Rustc { compiler: run.builder.compiler(run.builder.top_stage, run.target) }); + run.builder.ensure(Rustc { + target_compiler: run.builder.compiler(run.builder.top_stage, run.target), + }); } - /// Creates the `rustc` installer component. fn run(self, builder: &Builder<'_>) -> GeneratedTarball { - let compiler = self.compiler; - let host = self.compiler.host; + let target_compiler = self.target_compiler; + let target = self.target_compiler.host; - let tarball = Tarball::new(builder, "rustc", &host.triple); + let tarball = Tarball::new(builder, "rustc", &target.triple); // Prepare the rustc "image", what will actually end up getting installed - prepare_image(builder, compiler, tarball.image_dir()); + prepare_image(builder, target_compiler, tarball.image_dir()); // On MinGW we've got a few runtime DLL dependencies that we need to // include. @@ -430,16 +452,16 @@ impl Step for Rustc { // anything requiring us to distribute a license, but it's likely the // install will *also* include the rust-mingw package, which also needs // licenses, so to be safe we just include it here in all MinGW packages. - if host.contains("pc-windows-gnu") && builder.config.dist_include_mingw_linker { - runtime_dll_dist(tarball.image_dir(), host, builder); + if target.contains("pc-windows-gnu") && builder.config.dist_include_mingw_linker { + runtime_dll_dist(tarball.image_dir(), target, builder); tarball.add_dir(builder.src.join("src/etc/third-party"), "share/doc"); } return tarball.generate(); - fn prepare_image(builder: &Builder<'_>, compiler: Compiler, image: &Path) { - let host = compiler.host; - let src = builder.sysroot(compiler); + fn prepare_image(builder: &Builder<'_>, target_compiler: Compiler, image: &Path) { + let target = target_compiler.host; + let src = builder.sysroot(target_compiler); // Copy rustc binary t!(fs::create_dir_all(image.join("bin"))); @@ -452,17 +474,11 @@ impl Step for Rustc { .as_ref() .is_none_or(|tools| tools.iter().any(|tool| tool == "rustdoc")) { - let rustdoc = builder.rustdoc_for_compiler(compiler); + let rustdoc = builder.rustdoc_for_compiler(target_compiler); builder.install(&rustdoc, &image.join("bin"), FileType::Executable); } - let ra_proc_macro_srv_compiler = - builder.compiler_for(compiler.stage, builder.config.host_target, compiler.host); - let compilers = RustcPrivateCompilers::from_build_compiler( - builder, - ra_proc_macro_srv_compiler, - compiler.host, - ); + let compilers = RustcPrivateCompilers::from_target_compiler(builder, target_compiler); if let Some(ra_proc_macro_srv) = builder.ensure_if_default( tool::RustAnalyzerProcMacroSrv::from_compilers(compilers), @@ -472,11 +488,11 @@ impl Step for Rustc { builder.install(&ra_proc_macro_srv.tool_path, &dst, FileType::Executable); } - let libdir_relative = builder.libdir_relative(compiler); + let libdir_relative = builder.libdir_relative(target_compiler); // Copy runtime DLLs needed by the compiler if libdir_relative.to_str() != Some("bin") { - let libdir = builder.rustc_libdir(compiler); + let libdir = builder.rustc_libdir(target_compiler); for entry in builder.read_dir(&libdir) { // A safeguard that we will not ship libgccjit.so from the libdir, in case the // GCC codegen backend is enabled by default. @@ -503,15 +519,15 @@ impl Step for Rustc { // components like the llvm tools and LLD. LLD is included below and // tools/LLDB come later, so let's just throw it in the rustc // component for now. - maybe_install_llvm_runtime(builder, host, image); + maybe_install_llvm_runtime(builder, target, image); - let dst_dir = image.join("lib/rustlib").join(host).join("bin"); + let dst_dir = image.join("lib/rustlib").join(target).join("bin"); t!(fs::create_dir_all(&dst_dir)); // Copy over lld if it's there if builder.config.lld_enabled { - let src_dir = builder.sysroot_target_bindir(compiler, host); - let rust_lld = exe("rust-lld", compiler.host); + let src_dir = builder.sysroot_target_bindir(target_compiler, target); + let rust_lld = exe("rust-lld", target_compiler.host); builder.copy_link( &src_dir.join(&rust_lld), &dst_dir.join(&rust_lld), @@ -521,7 +537,7 @@ impl Step for Rustc { let self_contained_lld_dst_dir = dst_dir.join("gcc-ld"); t!(fs::create_dir(&self_contained_lld_dst_dir)); for name in crate::LLD_FILE_NAMES { - let exe_name = exe(name, compiler.host); + let exe_name = exe(name, target_compiler.host); builder.copy_link( &self_contained_lld_src_dir.join(&exe_name), &self_contained_lld_dst_dir.join(&exe_name), @@ -530,10 +546,12 @@ impl Step for Rustc { } } - if builder.config.llvm_enabled(compiler.host) && builder.config.llvm_tools_enabled { - let src_dir = builder.sysroot_target_bindir(compiler, host); - let llvm_objcopy = exe("llvm-objcopy", compiler.host); - let rust_objcopy = exe("rust-objcopy", compiler.host); + if builder.config.llvm_enabled(target_compiler.host) + && builder.config.llvm_tools_enabled + { + let src_dir = builder.sysroot_target_bindir(target_compiler, target); + let llvm_objcopy = exe("llvm-objcopy", target_compiler.host); + let rust_objcopy = exe("rust-objcopy", target_compiler.host); builder.copy_link( &src_dir.join(&llvm_objcopy), &dst_dir.join(&rust_objcopy), @@ -542,8 +560,8 @@ impl Step for Rustc { } if builder.tool_enabled("wasm-component-ld") { - let src_dir = builder.sysroot_target_bindir(compiler, host); - let ld = exe("wasm-component-ld", compiler.host); + let src_dir = builder.sysroot_target_bindir(target_compiler, target); + let ld = exe("wasm-component-ld", target_compiler.host); builder.copy_link(&src_dir.join(&ld), &dst_dir.join(&ld), FileType::Executable); } @@ -564,7 +582,7 @@ impl Step for Rustc { } // Debugger scripts - builder.ensure(DebuggerScripts { sysroot: image.to_owned(), host }); + builder.ensure(DebuggerScripts { sysroot: image.to_owned(), target }); // HTML copyright files let file_list = builder.ensure(super::run::GenerateCopyright); @@ -590,14 +608,16 @@ impl Step for Rustc { } fn metadata(&self) -> Option<StepMetadata> { - Some(StepMetadata::dist("rustc", self.compiler.host)) + Some(StepMetadata::dist("rustc", self.target_compiler.host)) } } +/// Copies debugger scripts for `target` into the given compiler `sysroot`. #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct DebuggerScripts { + /// Sysroot of a compiler into which will the debugger scripts be copied to. pub sysroot: PathBuf, - pub host: TargetSelection, + pub target: TargetSelection, } impl Step for DebuggerScripts { @@ -607,16 +627,15 @@ impl Step for DebuggerScripts { run.never() } - /// Copies debugger scripts for `target` into the `sysroot` specified. fn run(self, builder: &Builder<'_>) { - let host = self.host; + let target = self.target; let sysroot = self.sysroot; let dst = sysroot.join("lib/rustlib/etc"); t!(fs::create_dir_all(&dst)); let cp_debugger_script = |file: &str| { builder.install(&builder.src.join("src/etc/").join(file), &dst, FileType::Regular); }; - if host.contains("windows-msvc") { + if target.contains("windows-msvc") { // windbg debugger scripts builder.install( &builder.src.join("src/etc/rust-windbg.cmd"), @@ -730,12 +749,40 @@ fn copy_target_libs( } } +/// Builds the standard library (`rust-std`) dist component for a given `target`. +/// This includes the standard library dynamic library file (e.g. .so/.dll), along with stdlib +/// .rlibs. +/// +/// Note that due to uplifting, we actually ship the stage 1 library +/// (built using the stage1 compiler) even with a stage 2 dist, unless `full-bootstrap` is enabled. #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Std { - pub compiler: Compiler, + /// Compiler that will build the standard library. + pub build_compiler: Compiler, pub target: TargetSelection, } +impl Std { + pub fn new(builder: &Builder<'_>, target: TargetSelection) -> Self { + // This is an important optimization mainly for CI. + // Normally, to build stage N libstd, we need stage N rustc. + // However, if we know that we will uplift libstd from stage 1 anyway, building the stage N + // rustc can be wasteful. + // In particular, if we do a cross-compiling dist stage 2 build from T1 to T2, we need: + // - stage 2 libstd for T2 (uplifted from stage 1, where it was built by T1 rustc) + // - stage 2 rustc for T2 + // However, without this optimization, we would also build stage 2 rustc for **T1**, which + // is completely wasteful. + let build_compiler = + if compile::Std::should_be_uplifted_from_stage_1(builder, builder.top_stage, target) { + builder.compiler(1, builder.host_target) + } else { + builder.compiler(builder.top_stage, builder.host_target) + }; + Std { build_compiler, target } + } +} + impl Step for Std { type Output = Option<GeneratedTarball>; const DEFAULT: bool = true; @@ -745,31 +792,25 @@ impl Step for Std { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(Std { - compiler: run.builder.compiler_for( - run.builder.top_stage, - run.builder.config.host_target, - run.target, - ), - target: run.target, - }); + run.builder.ensure(Std::new(run.builder, run.target)); } fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> { - let compiler = self.compiler; + let build_compiler = self.build_compiler; let target = self.target; - if skip_host_target_lib(builder, compiler) { + if skip_host_target_lib(builder, build_compiler) { return None; } - builder.std(compiler, target); + // It's possible that std was uplifted and thus built with a different build compiler + // So we need to read the stamp that was actually generated when std was built + let stamp = + builder.std(build_compiler, target).expect("Standard library has to be built for dist"); let mut tarball = Tarball::new(builder, "rust-std", &target.triple); tarball.include_target_in_component_name(true); - let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target); - let stamp = build_stamp::libstd_stamp(builder, compiler_to_use, target); verify_uefi_rlib_format(builder, target, &stamp); copy_target_libs(builder, target, tarball.image_dir(), &stamp); @@ -777,7 +818,7 @@ impl Step for Std { } fn metadata(&self) -> Option<StepMetadata> { - Some(StepMetadata::dist("std", self.target).built_by(self.compiler)) + Some(StepMetadata::dist("std", self.target).built_by(self.build_compiler)) } } @@ -787,8 +828,9 @@ impl Step for Std { /// (Don't confuse this with [`RustDev`], without the `c`!) #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct RustcDev { - pub compiler: Compiler, - pub target: TargetSelection, + /// The compiler that will build rustc which will be shipped in this component. + build_compiler: Compiler, + target: TargetSelection, } impl Step for RustcDev { @@ -802,28 +844,27 @@ impl Step for RustcDev { fn make_run(run: RunConfig<'_>) { run.builder.ensure(RustcDev { - compiler: run.builder.compiler_for( - run.builder.top_stage, - run.builder.config.host_target, - run.target, - ), + // We currently always ship a stage 2 rustc-dev component, so we build it with the + // stage 1 compiler. This might change in the future. + // The precise stage used here is important, so we hard-code it. + build_compiler: run.builder.compiler(1, run.builder.config.host_target), target: run.target, }); } fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> { - let compiler = self.compiler; + let build_compiler = self.build_compiler; let target = self.target; - if skip_host_target_lib(builder, compiler) { + if skip_host_target_lib(builder, build_compiler) { return None; } - builder.ensure(compile::Rustc::new(compiler, target)); + // Build the compiler that we will ship + builder.ensure(compile::Rustc::new(build_compiler, target)); let tarball = Tarball::new(builder, "rustc-dev", &target.triple); - let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target); - let stamp = build_stamp::librustc_stamp(builder, compiler_to_use, target); + let stamp = build_stamp::librustc_stamp(builder, build_compiler, target); copy_target_libs(builder, target, tarball.image_dir(), &stamp); let src_files = &["Cargo.lock"]; @@ -847,16 +888,25 @@ impl Step for RustcDev { Some(tarball.generate()) } + + fn metadata(&self) -> Option<StepMetadata> { + Some(StepMetadata::dist("rustc-dev", self.target).built_by(self.build_compiler)) + } } +/// The `rust-analysis` component used to create a tarball of save-analysis metadata. +/// +/// This component has been deprecated and its contents now only include a warning about +/// its non-availability. #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Analysis { - pub compiler: Compiler, - pub target: TargetSelection, + build_compiler: Compiler, + target: TargetSelection, } impl Step for Analysis { type Output = Option<GeneratedTarball>; + const DEFAULT: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -865,24 +915,17 @@ impl Step for Analysis { } fn make_run(run: RunConfig<'_>) { + // The step just produces a deprecation notice, so we just hardcode stage 1 run.builder.ensure(Analysis { - // Find the actual compiler (handling the full bootstrap option) which - // produced the save-analysis data because that data isn't copied - // through the sysroot uplifting. - compiler: run.builder.compiler_for( - run.builder.top_stage, - run.builder.config.host_target, - run.target, - ), + build_compiler: run.builder.compiler(1, run.builder.config.host_target), target: run.target, }); } - /// Creates a tarball of (degenerate) save-analysis metadata, if available. fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> { - let compiler = self.compiler; + let compiler = self.build_compiler; let target = self.target; - if !builder.config.is_host_target(compiler.host) { + if skip_host_target_lib(builder, compiler) { return None; } @@ -905,6 +948,10 @@ impl Step for Analysis { tarball.add_dir(src, format!("lib/rustlib/{}/analysis", target.triple)); Some(tarball.generate()) } + + fn metadata(&self) -> Option<StepMetadata> { + Some(StepMetadata::dist("analysis", self.target).built_by(self.build_compiler)) + } } /// Use the `builder` to make a filtered copy of `base`/X for X in (`src_dirs` - `exclude_dirs`) to @@ -1251,10 +1298,9 @@ impl Step for Cargo { fn make_run(run: RunConfig<'_>) { run.builder.ensure(Cargo { - build_compiler: run.builder.compiler_for( - run.builder.top_stage, - run.builder.config.host_target, - run.target, + build_compiler: get_tool_target_compiler( + run.builder, + ToolTargetBuildMode::Build(run.target), ), target: run.target, }); @@ -1285,11 +1331,16 @@ impl Step for Cargo { Some(tarball.generate()) } + + fn metadata(&self) -> Option<StepMetadata> { + Some(StepMetadata::dist("cargo", self.target).built_by(self.build_compiler)) + } } +/// Distribute the rust-analyzer component, which is used as a LSP by various IDEs. #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct RustAnalyzer { - pub build_compiler: Compiler, + pub compilers: RustcPrivateCompilers, pub target: TargetSelection, } @@ -1305,21 +1356,14 @@ impl Step for RustAnalyzer { fn make_run(run: RunConfig<'_>) { run.builder.ensure(RustAnalyzer { - build_compiler: run.builder.compiler_for( - run.builder.top_stage, - run.builder.config.host_target, - run.target, - ), + compilers: RustcPrivateCompilers::new(run.builder, run.builder.top_stage, run.target), target: run.target, }); } fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> { let target = self.target; - let compilers = - RustcPrivateCompilers::from_build_compiler(builder, self.build_compiler, self.target); - - let rust_analyzer = builder.ensure(tool::RustAnalyzer::from_compilers(compilers)); + let rust_analyzer = builder.ensure(tool::RustAnalyzer::from_compilers(self.compilers)); let mut tarball = Tarball::new(builder, "rust-analyzer", &target.triple); tarball.set_overlay(OverlayKind::RustAnalyzer); @@ -1328,11 +1372,18 @@ impl Step for RustAnalyzer { tarball.add_legal_and_readme_to("share/doc/rust-analyzer"); Some(tarball.generate()) } + + fn metadata(&self) -> Option<StepMetadata> { + Some( + StepMetadata::dist("rust-analyzer", self.target) + .built_by(self.compilers.build_compiler()), + ) + } } #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Clippy { - pub build_compiler: Compiler, + pub compilers: RustcPrivateCompilers, pub target: TargetSelection, } @@ -1348,25 +1399,19 @@ impl Step for Clippy { fn make_run(run: RunConfig<'_>) { run.builder.ensure(Clippy { - build_compiler: run.builder.compiler_for( - run.builder.top_stage, - run.builder.config.host_target, - run.target, - ), + compilers: RustcPrivateCompilers::new(run.builder, run.builder.top_stage, run.target), target: run.target, }); } fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> { let target = self.target; - let compilers = - RustcPrivateCompilers::from_build_compiler(builder, self.build_compiler, target); // Prepare the image directory // We expect clippy to build, because we've exited this step above if tool // state for clippy isn't testing. - let clippy = builder.ensure(tool::Clippy::from_compilers(compilers)); - let cargoclippy = builder.ensure(tool::CargoClippy::from_compilers(compilers)); + let clippy = builder.ensure(tool::Clippy::from_compilers(self.compilers)); + let cargoclippy = builder.ensure(tool::CargoClippy::from_compilers(self.compilers)); let mut tarball = Tarball::new(builder, "clippy", &target.triple); tarball.set_overlay(OverlayKind::Clippy); @@ -1376,11 +1421,15 @@ impl Step for Clippy { tarball.add_legal_and_readme_to("share/doc/clippy"); Some(tarball.generate()) } + + fn metadata(&self) -> Option<StepMetadata> { + Some(StepMetadata::dist("clippy", self.target).built_by(self.compilers.build_compiler())) + } } #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Miri { - pub build_compiler: Compiler, + pub compilers: RustcPrivateCompilers, pub target: TargetSelection, } @@ -1396,11 +1445,7 @@ impl Step for Miri { fn make_run(run: RunConfig<'_>) { run.builder.ensure(Miri { - build_compiler: run.builder.compiler_for( - run.builder.top_stage, - run.builder.config.host_target, - run.target, - ), + compilers: RustcPrivateCompilers::new(run.builder, run.builder.top_stage, run.target), target: run.target, }); } @@ -1413,10 +1458,8 @@ impl Step for Miri { return None; } - let compilers = - RustcPrivateCompilers::from_build_compiler(builder, self.build_compiler, self.target); - let miri = builder.ensure(tool::Miri::from_compilers(compilers)); - let cargomiri = builder.ensure(tool::CargoMiri::from_compilers(compilers)); + let miri = builder.ensure(tool::Miri::from_compilers(self.compilers)); + let cargomiri = builder.ensure(tool::CargoMiri::from_compilers(self.compilers)); let mut tarball = Tarball::new(builder, "miri", &self.target.triple); tarball.set_overlay(OverlayKind::Miri); @@ -1426,11 +1469,15 @@ impl Step for Miri { tarball.add_legal_and_readme_to("share/doc/miri"); Some(tarball.generate()) } + + fn metadata(&self) -> Option<StepMetadata> { + Some(StepMetadata::dist("miri", self.target).built_by(self.compilers.build_compiler())) + } } #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct CraneliftCodegenBackend { - pub build_compiler: Compiler, + pub compilers: RustcPrivateCompilers, pub target: TargetSelection, } @@ -1454,11 +1501,7 @@ impl Step for CraneliftCodegenBackend { fn make_run(run: RunConfig<'_>) { run.builder.ensure(CraneliftCodegenBackend { - build_compiler: run.builder.compiler_for( - run.builder.top_stage, - run.builder.config.host_target, - run.target, - ), + compilers: RustcPrivateCompilers::new(run.builder, run.builder.top_stage, run.target), target: run.target, }); } @@ -1472,8 +1515,6 @@ impl Step for CraneliftCodegenBackend { } let target = self.target; - let compilers = - RustcPrivateCompilers::from_build_compiler(builder, self.build_compiler, target); if !target_supports_cranelift_backend(target) { builder.info("target not supported by rustc_codegen_cranelift. skipping"); return None; @@ -1484,6 +1525,7 @@ impl Step for CraneliftCodegenBackend { tarball.is_preview(true); tarball.add_legal_and_readme_to("share/doc/rustc_codegen_cranelift"); + let compilers = self.compilers; let stamp = builder.ensure(compile::CraneliftCodegenBackend { compilers }); if builder.config.dry_run() { @@ -1513,15 +1555,15 @@ impl Step for CraneliftCodegenBackend { fn metadata(&self) -> Option<StepMetadata> { Some( - StepMetadata::dist("rustc_codegen_cranelift", self.build_compiler.host) - .built_by(self.build_compiler), + StepMetadata::dist("rustc_codegen_cranelift", self.target) + .built_by(self.compilers.build_compiler()), ) } } #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Rustfmt { - pub build_compiler: Compiler, + pub compilers: RustcPrivateCompilers, pub target: TargetSelection, } @@ -1537,21 +1579,14 @@ impl Step for Rustfmt { fn make_run(run: RunConfig<'_>) { run.builder.ensure(Rustfmt { - build_compiler: run.builder.compiler_for( - run.builder.top_stage, - run.builder.config.host_target, - run.target, - ), + compilers: RustcPrivateCompilers::new(run.builder, run.builder.top_stage, run.target), target: run.target, }); } fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> { - let compilers = - RustcPrivateCompilers::from_build_compiler(builder, self.build_compiler, self.target); - - let rustfmt = builder.ensure(tool::Rustfmt::from_compilers(compilers)); - let cargofmt = builder.ensure(tool::Cargofmt::from_compilers(compilers)); + let rustfmt = builder.ensure(tool::Rustfmt::from_compilers(self.compilers)); + let cargofmt = builder.ensure(tool::Cargofmt::from_compilers(self.compilers)); let mut tarball = Tarball::new(builder, "rustfmt", &self.target.triple); tarball.set_overlay(OverlayKind::Rustfmt); @@ -1561,12 +1596,16 @@ impl Step for Rustfmt { tarball.add_legal_and_readme_to("share/doc/rustfmt"); Some(tarball.generate()) } + + fn metadata(&self) -> Option<StepMetadata> { + Some(StepMetadata::dist("rustfmt", self.target).built_by(self.compilers.build_compiler())) + } } +/// Extended archive that contains the compiler, standard library and a bunch of tools. #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Extended { - stage: u32, - host: TargetSelection, + build_compiler: Compiler, target: TargetSelection, } @@ -1582,8 +1621,9 @@ impl Step for Extended { fn make_run(run: RunConfig<'_>) { run.builder.ensure(Extended { - stage: run.builder.top_stage, - host: run.builder.config.host_target, + build_compiler: run + .builder + .compiler(run.builder.top_stage - 1, run.builder.host_target), target: run.target, }); } @@ -1591,10 +1631,7 @@ impl Step for Extended { /// Creates a combined installer for the specified target in the provided stage. fn run(self, builder: &Builder<'_>) { let target = self.target; - let stage = self.stage; - let compiler = builder.compiler_for(self.stage, self.host, self.target); - - builder.info(&format!("Dist extended stage{} ({})", compiler.stage, target)); + builder.info(&format!("Dist extended stage{} ({target})", builder.top_stage)); let mut tarballs = Vec::new(); let mut built_tools = HashSet::new(); @@ -1607,34 +1644,38 @@ impl Step for Extended { }; } - let target_compiler = builder.compiler(stage, target); + let rustc_private_compilers = + RustcPrivateCompilers::from_build_compiler(builder, self.build_compiler, target); + let build_compiler = rustc_private_compilers.build_compiler(); + let target_compiler = rustc_private_compilers.target_compiler(); + // When rust-std package split from rustc, we needed to ensure that during // upgrades rustc was upgraded before rust-std. To avoid rustc clobbering // the std files during uninstall. To do this ensure that rustc comes // before rust-std in the list below. - tarballs.push(builder.ensure(Rustc { compiler: target_compiler })); - tarballs.push(builder.ensure(Std { compiler, target }).expect("missing std")); + tarballs.push(builder.ensure(Rustc { target_compiler })); + tarballs.push(builder.ensure(Std { build_compiler, target }).expect("missing std")); if target.is_windows_gnu() { - tarballs.push(builder.ensure(Mingw { host: target }).expect("missing mingw")); + tarballs.push(builder.ensure(Mingw { target }).expect("missing mingw")); } add_component!("rust-docs" => Docs { host: target }); // Std stage N is documented with compiler stage N add_component!("rust-json-docs" => JsonDocs { build_compiler: target_compiler, target }); - add_component!("cargo" => Cargo { build_compiler: compiler, target }); - add_component!("rustfmt" => Rustfmt { build_compiler: compiler, target }); - add_component!("rust-analyzer" => RustAnalyzer { build_compiler: compiler, target }); + add_component!("cargo" => Cargo { build_compiler, target }); + add_component!("rustfmt" => Rustfmt { compilers: rustc_private_compilers, target }); + add_component!("rust-analyzer" => RustAnalyzer { compilers: rustc_private_compilers, target }); add_component!("llvm-components" => LlvmTools { target }); - add_component!("clippy" => Clippy { build_compiler: compiler, target }); - add_component!("miri" => Miri { build_compiler: compiler, target }); - add_component!("analysis" => Analysis { compiler, target }); + add_component!("clippy" => Clippy { compilers: rustc_private_compilers, target }); + add_component!("miri" => Miri { compilers: rustc_private_compilers, target }); + add_component!("analysis" => Analysis { build_compiler, target }); add_component!("rustc-codegen-cranelift" => CraneliftCodegenBackend { - build_compiler: compiler, + compilers: rustc_private_compilers, target }); add_component!("llvm-bitcode-linker" => LlvmBitcodeLinker { - build_compiler: compiler, + build_compiler, target }); @@ -2100,6 +2141,10 @@ impl Step for Extended { } } } + + fn metadata(&self) -> Option<StepMetadata> { + Some(StepMetadata::dist("extended", self.target).built_by(self.build_compiler)) + } } fn add_env( @@ -2236,6 +2281,7 @@ fn maybe_install_llvm( { trace!("LLVM already built, installing LLVM files"); let mut cmd = command(host_llvm_config); + cmd.cached(); cmd.arg("--libfiles"); builder.verbose(|| println!("running {cmd:?}")); let files = cmd.run_capture_stdout(builder).stdout(); @@ -2562,15 +2608,17 @@ impl Step for RustDev { /// Tarball intended for internal consumption to ease rustc/std development. /// +/// It only packages the binaries that were already compiled when bootstrap itself was built. +/// /// Should not be considered stable by end users. #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct Bootstrap { - pub target: TargetSelection, + target: TargetSelection, } impl Step for Bootstrap { type Output = Option<GeneratedTarball>; - const DEFAULT: bool = false; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -2597,6 +2645,10 @@ impl Step for Bootstrap { Some(tarball.generate()) } + + fn metadata(&self) -> Option<StepMetadata> { + Some(StepMetadata::dist("bootstrap", self.target)) + } } /// Tarball containing a prebuilt version of the build-manifest tool, intended to be used by the @@ -2605,12 +2657,12 @@ impl Step for Bootstrap { /// Should not be considered stable by end users. #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct BuildManifest { - pub target: TargetSelection, + target: TargetSelection, } impl Step for BuildManifest { type Output = GeneratedTarball; - const DEFAULT: bool = false; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -2628,16 +2680,20 @@ impl Step for BuildManifest { tarball.add_file(&build_manifest, "bin", FileType::Executable); tarball.generate() } + + fn metadata(&self) -> Option<StepMetadata> { + Some(StepMetadata::dist("build-manifest", self.target)) + } } /// Tarball containing artifacts necessary to reproduce the build of rustc. /// -/// Currently this is the PGO profile data. +/// Currently this is the PGO (and possibly BOLT) profile data. /// /// Should not be considered stable by end users. #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct ReproducibleArtifacts { - pub target: TargetSelection, + target: TargetSelection, } impl Step for ReproducibleArtifacts { @@ -2670,6 +2726,10 @@ impl Step for ReproducibleArtifacts { } if added_anything { Some(tarball.generate()) } else { None } } + + fn metadata(&self) -> Option<StepMetadata> { + Some(StepMetadata::dist("reproducible-artifacts", self.target)) + } } /// Tarball containing a prebuilt version of the libgccjit library, @@ -2677,7 +2737,7 @@ impl Step for ReproducibleArtifacts { /// backend needing a prebuilt libLLVM). #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct Gcc { - pub target: TargetSelection, + target: TargetSelection, } impl Step for Gcc { @@ -2697,4 +2757,8 @@ impl Step for Gcc { tarball.add_file(&output.libgccjit, "lib", FileType::NativeLibrary); tarball.generate() } + + fn metadata(&self) -> Option<StepMetadata> { + Some(StepMetadata::dist("gcc", self.target)) + } } diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 7fe19c00ef5..8c20c8c479a 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -784,7 +784,7 @@ fn doc_std( let description = format!("library{} in {} format", crate_description(requested_crates), format.as_str()); - let _guard = builder.msg(Kind::Doc, description, None, build_compiler, target); + let _guard = builder.msg(Kind::Doc, description, Mode::Std, build_compiler, target); cargo.into_cmd().run(builder); builder.cp_link_r(&out_dir, out); @@ -994,7 +994,7 @@ macro_rules! tool_doc { (compilers.build_compiler(), Mode::ToolRustc) } else { // bootstrap/host tools have to be documented with the stage 0 compiler - (prepare_doc_compiler(run.builder, target, 1), Mode::ToolBootstrap) + (prepare_doc_compiler(run.builder, run.builder.host_target, 1), Mode::ToolBootstrap) }; run.builder.ensure($tool { build_compiler, mode, target }); diff --git a/src/bootstrap/src/core/build_steps/install.rs b/src/bootstrap/src/core/build_steps/install.rs index 4457258e9cd..ce68dbf5a20 100644 --- a/src/bootstrap/src/core/build_steps/install.rs +++ b/src/bootstrap/src/core/build_steps/install.rs @@ -7,6 +7,7 @@ use std::path::{Component, Path, PathBuf}; use std::{env, fs}; use crate::core::build_steps::dist; +use crate::core::build_steps::tool::RustcPrivateCompilers; use crate::core::builder::{Builder, RunConfig, ShouldRun, Step}; use crate::core::config::{Config, TargetSelection}; use crate::utils::exec::command; @@ -64,17 +65,14 @@ fn is_dir_writable_for_user(dir: &Path) -> bool { fn install_sh( builder: &Builder<'_>, package: &str, - stage: u32, - host: Option<TargetSelection>, + build_compiler: impl Into<Option<Compiler>>, + target: Option<TargetSelection>, tarball: &GeneratedTarball, ) { - let _guard = builder.msg( - Kind::Install, - package, - None, - (host.unwrap_or(builder.host_target), stage), - host, - ); + let _guard = match build_compiler.into() { + Some(build_compiler) => builder.msg(Kind::Install, package, None, build_compiler, target), + None => builder.msg_unstaged(Kind::Install, package, target.unwrap_or(builder.host_target)), + }; let prefix = default_path(&builder.config.prefix, "/usr/local"); let sysconfdir = prefix.join(default_path(&builder.config.sysconfdir, "/etc")); @@ -166,10 +164,10 @@ macro_rules! install { IS_HOST: $IS_HOST:expr, $run_item:block $(, $c:ident)*;)+) => { $( - #[derive(Debug, Clone, Hash, PartialEq, Eq)] + #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct $name { - pub compiler: Compiler, - pub target: TargetSelection, + build_compiler: Compiler, + target: TargetSelection, } impl $name { @@ -193,7 +191,7 @@ macro_rules! install { fn make_run(run: RunConfig<'_>) { run.builder.ensure($name { - compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.host_target), + build_compiler: run.builder.compiler(run.builder.top_stage - 1, run.builder.config.host_target), target: run.target, }); } @@ -208,96 +206,95 @@ macro_rules! install { install!((self, builder, _config), Docs, path = "src/doc", _config.docs, IS_HOST: false, { let tarball = builder.ensure(dist::Docs { host: self.target }).expect("missing docs"); - install_sh(builder, "docs", self.compiler.stage, Some(self.target), &tarball); + install_sh(builder, "docs", self.build_compiler, Some(self.target), &tarball); }; Std, path = "library/std", true, IS_HOST: false, { // `expect` should be safe, only None when host != build, but this // only runs when host == build - let tarball = builder.ensure(dist::Std { - compiler: self.compiler, - target: self.target - }).expect("missing std"); - install_sh(builder, "std", self.compiler.stage, Some(self.target), &tarball); + let std = dist::Std::new(builder, self.target); + let build_compiler = std.build_compiler; + let tarball = builder.ensure(std).expect("missing std"); + install_sh(builder, "std", build_compiler, Some(self.target), &tarball); }; Cargo, alias = "cargo", Self::should_build(_config), IS_HOST: true, { let tarball = builder - .ensure(dist::Cargo { build_compiler: self.compiler, target: self.target }) + .ensure(dist::Cargo { build_compiler: self.build_compiler, target: self.target }) .expect("missing cargo"); - install_sh(builder, "cargo", self.compiler.stage, Some(self.target), &tarball); + install_sh(builder, "cargo", self.build_compiler, Some(self.target), &tarball); }; RustAnalyzer, alias = "rust-analyzer", Self::should_build(_config), IS_HOST: true, { if let Some(tarball) = - builder.ensure(dist::RustAnalyzer { build_compiler: self.compiler, target: self.target }) + builder.ensure(dist::RustAnalyzer { compilers: RustcPrivateCompilers::from_build_compiler(builder, self.build_compiler, self.target), target: self.target }) { - install_sh(builder, "rust-analyzer", self.compiler.stage, Some(self.target), &tarball); + install_sh(builder, "rust-analyzer", self.build_compiler, Some(self.target), &tarball); } else { builder.info( - &format!("skipping Install rust-analyzer stage{} ({})", self.compiler.stage, self.target), + &format!("skipping Install rust-analyzer stage{} ({})", self.build_compiler.stage + 1, self.target), ); } }; Clippy, alias = "clippy", Self::should_build(_config), IS_HOST: true, { let tarball = builder - .ensure(dist::Clippy { build_compiler: self.compiler, target: self.target }) + .ensure(dist::Clippy { compilers: RustcPrivateCompilers::from_build_compiler(builder, self.build_compiler, self.target), target: self.target }) .expect("missing clippy"); - install_sh(builder, "clippy", self.compiler.stage, Some(self.target), &tarball); + install_sh(builder, "clippy", self.build_compiler, Some(self.target), &tarball); }; Miri, alias = "miri", Self::should_build(_config), IS_HOST: true, { - if let Some(tarball) = builder.ensure(dist::Miri { build_compiler: self.compiler, target: self.target }) { - install_sh(builder, "miri", self.compiler.stage, Some(self.target), &tarball); + if let Some(tarball) = builder.ensure(dist::Miri { compilers: RustcPrivateCompilers::from_build_compiler(builder, self.build_compiler, self.target) , target: self.target }) { + install_sh(builder, "miri", self.build_compiler, Some(self.target), &tarball); } else { // Miri is only available on nightly builder.info( - &format!("skipping Install miri stage{} ({})", self.compiler.stage, self.target), + &format!("skipping Install miri stage{} ({})", self.build_compiler.stage + 1, self.target), ); } }; LlvmTools, alias = "llvm-tools", _config.llvm_tools_enabled && _config.llvm_enabled(_config.host_target), IS_HOST: true, { if let Some(tarball) = builder.ensure(dist::LlvmTools { target: self.target }) { - install_sh(builder, "llvm-tools", self.compiler.stage, Some(self.target), &tarball); + install_sh(builder, "llvm-tools", None, Some(self.target), &tarball); } else { builder.info( - &format!("skipping llvm-tools stage{} ({}): external LLVM", self.compiler.stage, self.target), + &format!("skipping llvm-tools ({}): external LLVM", self.target), ); } }; Rustfmt, alias = "rustfmt", Self::should_build(_config), IS_HOST: true, { if let Some(tarball) = builder.ensure(dist::Rustfmt { - build_compiler: self.compiler, + compilers: RustcPrivateCompilers::from_build_compiler(builder, self.build_compiler, self.target), target: self.target }) { - install_sh(builder, "rustfmt", self.compiler.stage, Some(self.target), &tarball); + install_sh(builder, "rustfmt", self.build_compiler, Some(self.target), &tarball); } else { builder.info( - &format!("skipping Install Rustfmt stage{} ({})", self.compiler.stage, self.target), + &format!("skipping Install Rustfmt stage{} ({})", self.build_compiler.stage + 1, self.target), ); } }; Rustc, path = "compiler/rustc", true, IS_HOST: true, { let tarball = builder.ensure(dist::Rustc { - compiler: builder.compiler(builder.top_stage, self.target), + target_compiler: builder.compiler(self.build_compiler.stage + 1, self.target), }); - install_sh(builder, "rustc", self.compiler.stage, Some(self.target), &tarball); + install_sh(builder, "rustc", self.build_compiler, Some(self.target), &tarball); }; RustcCodegenCranelift, alias = "rustc-codegen-cranelift", Self::should_build(_config), IS_HOST: true, { if let Some(tarball) = builder.ensure(dist::CraneliftCodegenBackend { - build_compiler: self.compiler, + compilers: RustcPrivateCompilers::from_build_compiler(builder, self.build_compiler, self.target), target: self.target }) { - install_sh(builder, "rustc-codegen-cranelift", self.compiler.stage, Some(self.target), &tarball); + install_sh(builder, "rustc-codegen-cranelift", self.build_compiler, Some(self.target), &tarball); } else { builder.info( &format!("skipping Install CodegenBackend(\"cranelift\") stage{} ({})", - self.compiler.stage, self.target), + self.build_compiler.stage + 1, self.target), ); } }; LlvmBitcodeLinker, alias = "llvm-bitcode-linker", Self::should_build(_config), IS_HOST: true, { - if let Some(tarball) = builder.ensure(dist::LlvmBitcodeLinker { build_compiler: self.compiler, target: self.target }) { - install_sh(builder, "llvm-bitcode-linker", self.compiler.stage, Some(self.target), &tarball); + if let Some(tarball) = builder.ensure(dist::LlvmBitcodeLinker { build_compiler: self.build_compiler, target: self.target }) { + install_sh(builder, "llvm-bitcode-linker", self.build_compiler, Some(self.target), &tarball); } else { builder.info( - &format!("skipping llvm-bitcode-linker stage{} ({})", self.compiler.stage, self.target), + &format!("skipping llvm-bitcode-linker stage{} ({})", self.build_compiler.stage + 1, self.target), ); } }; @@ -305,7 +302,7 @@ install!((self, builder, _config), #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Src { - pub stage: u32, + stage: u32, } impl Step for Src { @@ -325,6 +322,6 @@ impl Step for Src { fn run(self, builder: &Builder<'_>) { let tarball = builder.ensure(dist::Src); - install_sh(builder, "src", self.stage, None, &tarball); + install_sh(builder, "src", None, None, &tarball); } } diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 70259f0d1d7..d47c1495838 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -486,8 +486,11 @@ impl Step for Llvm { let LlvmResult { host_llvm_config, .. } = builder.ensure(Llvm { target: builder.config.host_target }); if !builder.config.dry_run() { - let llvm_bindir = - command(&host_llvm_config).arg("--bindir").run_capture_stdout(builder).stdout(); + let llvm_bindir = command(&host_llvm_config) + .arg("--bindir") + .cached() + .run_capture_stdout(builder) + .stdout(); let host_bin = Path::new(llvm_bindir.trim()); cfg.define( "LLVM_TABLEGEN", @@ -593,7 +596,13 @@ impl Step for Llvm { } pub fn get_llvm_version(builder: &Builder<'_>, llvm_config: &Path) -> String { - command(llvm_config).arg("--version").run_capture_stdout(builder).stdout().trim().to_owned() + command(llvm_config) + .arg("--version") + .cached() + .run_capture_stdout(builder) + .stdout() + .trim() + .to_owned() } pub fn get_llvm_version_major(builder: &Builder<'_>, llvm_config: &Path) -> u8 { diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs index c6288f63847..d9de6b7ef96 100644 --- a/src/bootstrap/src/core/build_steps/run.rs +++ b/src/bootstrap/src/core/build_steps/run.rs @@ -5,13 +5,14 @@ use std::path::PathBuf; +use build_helper::exit; use clap_complete::{Generator, shells}; use crate::core::build_steps::dist::distdir; use crate::core::build_steps::test; use crate::core::build_steps::tool::{self, RustcPrivateCompilers, SourceType, Tool}; use crate::core::build_steps::vendor::{Vendor, default_paths_to_vendor}; -use crate::core::builder::{Builder, Kind, RunConfig, ShouldRun, Step}; +use crate::core::builder::{Builder, Kind, RunConfig, ShouldRun, Step, StepMetadata}; use crate::core::config::TargetSelection; use crate::core::config::flags::get_completion; use crate::utils::exec::command; @@ -100,8 +101,17 @@ impl Step for ReplaceVersionPlaceholder { } } +/// Invoke the Miri tool on a specified file. +/// +/// Note that Miri always executed on the host, as it is an interpreter. +/// That means that `x run miri --target FOO` will build miri for the host, +/// prepare a miri sysroot for the target `FOO` and then execute miri with +/// the target `FOO`. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Miri { + /// The build compiler that will build miri and the target compiler to which miri links. + compilers: RustcPrivateCompilers, + /// The target which will miri interpret. target: TargetSelection, } @@ -113,14 +123,9 @@ impl Step for Miri { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(Miri { target: run.target }); - } - - fn run(self, builder: &Builder<'_>) { - let host = builder.build.host_target; - let target = self.target; + let builder = run.builder; - // `x run` uses stage 0 by default but miri does not work well with stage 0. + // `x run` uses stage 0 by default, but miri does not work well with stage 0. // Change the stage to 1 if it's not set explicitly. let stage = if builder.config.is_explicit_stage() || builder.top_stage >= 1 { builder.top_stage @@ -129,14 +134,22 @@ impl Step for Miri { }; if stage == 0 { - eprintln!("miri cannot be run at stage 0"); - std::process::exit(1); + eprintln!("ERROR: miri cannot be run at stage 0"); + exit!(1); } - // This compiler runs on the host, we'll just use it for the target. - let compilers = RustcPrivateCompilers::new(builder, stage, target); - let miri_build = builder.ensure(tool::Miri::from_compilers(compilers)); - let host_compiler = miri_build.build_compiler; + // Miri always runs on the host, because it can interpret code for any target + let compilers = RustcPrivateCompilers::new(builder, stage, builder.host_target); + + run.builder.ensure(Miri { compilers, target: run.target }); + } + + fn run(self, builder: &Builder<'_>) { + let host = builder.build.host_target; + let compilers = self.compilers; + let target = self.target; + + builder.ensure(tool::Miri::from_compilers(compilers)); // Get a target sysroot for Miri. let miri_sysroot = @@ -147,7 +160,7 @@ impl Step for Miri { // add_rustc_lib_path does not add the path that contains librustc_driver-<...>.so. let mut miri = tool::prepare_tool_cargo( builder, - host_compiler, + compilers.build_compiler(), Mode::ToolRustc, host, Kind::Run, @@ -167,6 +180,10 @@ impl Step for Miri { miri.into_cmd().run(builder); } + + fn metadata(&self) -> Option<StepMetadata> { + Some(StepMetadata::run("miri", self.target).built_by(self.compilers.build_compiler())) + } } #[derive(Debug, Clone, Hash, PartialEq, Eq)] diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 269e7da8d7b..abe79405984 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -153,7 +153,7 @@ You can skip linkcheck with --skip src/tools/linkchecker" } // Build all the default documentation. - builder.default_doc(&[]); + builder.run_default_doc_steps(); // Build the linkchecker before calling `msg`, since GHA doesn't support nested groups. let linkchecker = builder.tool_cmd(Tool::Linkchecker); @@ -208,7 +208,7 @@ impl Step for HtmlCheck { panic!("Cannot run html-check tests"); } // Ensure that a few different kinds of documentation are available. - builder.default_doc(&[]); + builder.run_default_doc_steps(); builder.ensure(crate::core::build_steps::doc::Rustc::for_stage( builder, builder.top_stage, @@ -1719,7 +1719,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the if suite == "debuginfo" { builder.ensure(dist::DebuggerScripts { sysroot: builder.sysroot(compiler).to_path_buf(), - host: target, + target, }); } if suite == "run-make" { @@ -1850,7 +1850,7 @@ HELP: You can add it into `bootstrap.toml` in `rust.codegen-backends = [{name:?} // Tells compiletest which codegen backend to use. // It is used to e.g. ignore tests that don't support that codegen backend. cmd.arg("--default-codegen-backend") - .arg(builder.config.default_codegen_backend(compiler.host).unwrap().name()); + .arg(builder.config.default_codegen_backend(compiler.host).name()); } if builder.build.config.llvm_enzyme { @@ -2043,6 +2043,7 @@ HELP: You can add it into `bootstrap.toml` in `rust.codegen-backends = [{name:?} if !builder.config.dry_run() { let llvm_version = get_llvm_version(builder, &host_llvm_config); let llvm_components = command(&host_llvm_config) + .cached() .arg("--components") .run_capture_stdout(builder) .stdout(); @@ -2062,8 +2063,11 @@ HELP: You can add it into `bootstrap.toml` in `rust.codegen-backends = [{name:?} // separate compilations. We can add LLVM's library path to the // rustc args as a workaround. if !builder.config.dry_run() && suite.ends_with("fulldeps") { - let llvm_libdir = - command(&host_llvm_config).arg("--libdir").run_capture_stdout(builder).stdout(); + let llvm_libdir = command(&host_llvm_config) + .cached() + .arg("--libdir") + .run_capture_stdout(builder) + .stdout(); let link_llvm = if target.is_msvc() { format!("-Clink-arg=-LIBPATH:{llvm_libdir}") } else { @@ -3117,45 +3121,55 @@ impl Step for Distcheck { /// /// FIXME(#136822): dist components are under-tested. fn run(self, builder: &Builder<'_>) { - builder.info("Distcheck"); - let dir = builder.tempdir().join("distcheck"); - let _ = fs::remove_dir_all(&dir); - t!(fs::create_dir_all(&dir)); - - // Guarantee that these are built before we begin running. - builder.ensure(dist::PlainSourceTarball); - builder.ensure(dist::Src); + // Use a temporary directory completely outside the current checkout, to avoid reusing any + // local source code, built artifacts or configuration by accident + let root_dir = std::env::temp_dir().join("distcheck"); + + // Check that we can build some basic things from the plain source tarball + builder.info("Distcheck plain source tarball"); + let plain_src_tarball = builder.ensure(dist::PlainSourceTarball); + let plain_src_dir = root_dir.join("distcheck-plain-src"); + builder.clear_dir(&plain_src_dir); + + let configure_args: Vec<String> = std::env::var("DISTCHECK_CONFIGURE_ARGS") + .map(|args| args.split(" ").map(|s| s.to_string()).collect::<Vec<String>>()) + .unwrap_or_default(); command("tar") .arg("-xf") - .arg(builder.ensure(dist::PlainSourceTarball).tarball()) + .arg(plain_src_tarball.tarball()) .arg("--strip-components=1") - .current_dir(&dir) + .current_dir(&plain_src_dir) .run(builder); command("./configure") - .args(&builder.config.configure_args) + .arg("--set") + .arg("rust.omit-git-hash=false") + .args(&configure_args) .arg("--enable-vendor") - .current_dir(&dir) + .current_dir(&plain_src_dir) .run(builder); command(helpers::make(&builder.config.host_target.triple)) .arg("check") - .current_dir(&dir) + // Do not run the build as if we were in CI, otherwise git would be assumed to be + // present, but we build from a tarball here + .env("GITHUB_ACTIONS", "0") + .current_dir(&plain_src_dir) .run(builder); // Now make sure that rust-src has all of libstd's dependencies builder.info("Distcheck rust-src"); - let dir = builder.tempdir().join("distcheck-src"); - let _ = fs::remove_dir_all(&dir); - t!(fs::create_dir_all(&dir)); + let src_tarball = builder.ensure(dist::Src); + let src_dir = root_dir.join("distcheck-src"); + builder.clear_dir(&src_dir); command("tar") .arg("-xf") - .arg(builder.ensure(dist::Src).tarball()) + .arg(src_tarball.tarball()) .arg("--strip-components=1") - .current_dir(&dir) + .current_dir(&src_dir) .run(builder); - let toml = dir.join("rust-src/lib/rustlib/src/rust/library/std/Cargo.toml"); + let toml = src_dir.join("rust-src/lib/rustlib/src/rust/library/std/Cargo.toml"); command(&builder.initial_cargo) // Will read the libstd Cargo.toml // which uses the unstable `public-dependency` feature. @@ -3163,7 +3177,7 @@ impl Step for Distcheck { .arg("generate-lockfile") .arg("--manifest-path") .arg(&toml) - .current_dir(&dir) + .current_dir(&src_dir) .run(builder); } } diff --git a/src/bootstrap/src/core/build_steps/vendor.rs b/src/bootstrap/src/core/build_steps/vendor.rs index 7b860ceb943..0e9d4e7e32b 100644 --- a/src/bootstrap/src/core/build_steps/vendor.rs +++ b/src/bootstrap/src/core/build_steps/vendor.rs @@ -19,6 +19,7 @@ pub const VENDOR_DIR: &str = "vendor"; pub fn default_paths_to_vendor(builder: &Builder<'_>) -> Vec<(PathBuf, Vec<&'static str>)> { [ ("src/tools/cargo/Cargo.toml", vec!["src/tools/cargo"]), + ("src/tools/clippy/clippy_test_deps/Cargo.toml", vec![]), ("src/tools/rust-analyzer/Cargo.toml", vec![]), ("compiler/rustc_codegen_cranelift/Cargo.toml", vec![]), ("compiler/rustc_codegen_gcc/Cargo.toml", vec![]), diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index 72192403412..cdf6fe573e5 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -132,10 +132,7 @@ impl Cargo { } pub fn into_cmd(self) -> BootstrapCommand { - let mut cmd: BootstrapCommand = self.into(); - // Disable caching for commands originating from Cargo-related operations. - cmd.do_not_cache(); - cmd + self.into() } /// Same as [`Cargo::new`] except this one doesn't configure the linker with @@ -1085,7 +1082,7 @@ impl Builder<'_> { && let Some(llvm_config) = self.llvm_config(target) { let llvm_libdir = - command(llvm_config).arg("--libdir").run_capture_stdout(self).stdout(); + command(llvm_config).cached().arg("--libdir").run_capture_stdout(self).stdout(); if target.is_msvc() { rustflags.arg(&format!("-Clink-arg=-LIBPATH:{llvm_libdir}")); } else { @@ -1326,12 +1323,7 @@ impl Builder<'_> { if let Some(limit) = limit && (build_compiler_stage == 0 - || self - .config - .default_codegen_backend(target) - .cloned() - .unwrap_or_default() - .is_llvm()) + || self.config.default_codegen_backend(target).is_llvm()) { rustflags.arg(&format!("-Cllvm-args=-import-instr-limit={limit}")); } diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 40460bf168d..f794f4e079a 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -22,6 +22,7 @@ use crate::core::build_steps::{ }; use crate::core::config::flags::Subcommand; use crate::core::config::{DryRun, TargetSelection}; +use crate::utils::build_stamp::BuildStamp; use crate::utils::cache::Cache; use crate::utils::exec::{BootstrapCommand, ExecutionContext, command}; use crate::utils::helpers::{self, LldThreads, add_dylib_path, exe, libdir, linker_args, t}; @@ -144,8 +145,7 @@ pub trait Step: 'static + Clone + Debug + PartialEq + Eq + Hash { } /// Metadata that describes an executed step, mostly for testing and tracing. -#[allow(unused)] -#[derive(Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct StepMetadata { name: String, kind: Kind, @@ -181,6 +181,10 @@ impl StepMetadata { Self::new(name, target, Kind::Test) } + pub fn run(name: &str, target: TargetSelection) -> Self { + Self::new(name, target, Kind::Run) + } + fn new(name: &str, target: TargetSelection, kind: Kind) -> Self { Self { name: name.to_string(), kind, target, built_by: None, stage: None, metadata: None } } @@ -1308,8 +1312,9 @@ impl<'a> Builder<'a> { self.run_step_descriptions(&Builder::get_step_descriptions(self.kind), &self.paths); } - pub fn default_doc(&self, paths: &[PathBuf]) { - self.run_step_descriptions(&Builder::get_step_descriptions(Kind::Doc), paths); + /// Run all default documentation steps to build documentation. + pub fn run_default_doc_steps(&self) { + self.run_step_descriptions(&Builder::get_step_descriptions(Kind::Doc), &[]); } pub fn doc_rust_lang_org_channel(&self) -> String { @@ -1411,6 +1416,8 @@ impl<'a> Builder<'a> { /// The standard library will be linked to the sysroot of the passed compiler. /// /// Prefer using this method rather than manually invoking `Std::new`. + /// + /// Returns an optional build stamp, if libstd was indeed built. #[cfg_attr( feature = "tracing", instrument( @@ -1424,29 +1431,38 @@ impl<'a> Builder<'a> { ), ), )] - pub fn std(&self, compiler: Compiler, target: TargetSelection) { + pub fn std(&self, compiler: Compiler, target: TargetSelection) -> Option<BuildStamp> { // FIXME: make the `Std` step return some type-level "proof" that std was indeed built, // and then require passing that to all Cargo invocations that we do. - // The "stage 0" std is always precompiled and comes with the stage0 compiler, so we have - // special logic for it, to avoid creating needless and confusing Std steps that don't + // The "stage 0" std is almost always precompiled and comes with the stage0 compiler, so we + // have special logic for it, to avoid creating needless and confusing Std steps that don't // actually build anything. + // We only allow building the stage0 stdlib if we do a local rebuild, so the stage0 compiler + // actually comes from in-tree sources, and we're cross-compiling, so the stage0 for the + // given `target` is not available. if compiler.stage == 0 { if target != compiler.host { - panic!( - r"It is not possible to build the standard library for `{target}` using the stage0 compiler. + if self.local_rebuild { + self.ensure(Std::new(compiler, target)) + } else { + panic!( + r"It is not possible to build the standard library for `{target}` using the stage0 compiler. You have to build a stage1 compiler for `{}` first, and then use it to build a standard library for `{target}`. +Alternatively, you can set `build.local-rebuild=true` and use a stage0 compiler built from in-tree sources. ", - compiler.host - ) + compiler.host + ) + } + } else { + // We still need to link the prebuilt standard library into the ephemeral stage0 sysroot + self.ensure(StdLink::from_std(Std::new(compiler, target), compiler)); + None } - - // We still need to link the prebuilt standard library into the ephemeral stage0 sysroot - self.ensure(StdLink::from_std(Std::new(compiler, target), compiler)); } else { // This step both compiles the std and links it into the compiler's sysroot. // Yes, it's quite magical and side-effecty.. would be nice to refactor later. - self.ensure(Std::new(compiler, target)); + self.ensure(Std::new(compiler, target)) } } diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 2afba25ae59..3c2cb782850 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -854,6 +854,18 @@ mod snapshot { } #[test] + fn build_library_stage_0_local_rebuild() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("build") + .path("library") + .stage(0) + .targets(&[TEST_TRIPLE_1]) + .args(&["--set", "build.local-rebuild=true"]) + .render_steps(), @"[build] rustc 0 <host> -> std 0 <target1>"); + } + + #[test] fn build_library_stage_1() { let ctx = TestCtx::new(); insta::assert_snapshot!( @@ -1134,7 +1146,9 @@ mod snapshot { [build] rustc 0 <host> -> GenerateCopyright 1 <host> [dist] rustc <host> [dist] rustc 1 <host> -> std 1 <host> + [dist] rustc 1 <host> -> rustc-dev 2 <host> [dist] src <> + [dist] reproducible-artifacts <host> " ); } @@ -1196,15 +1210,24 @@ mod snapshot { [build] rustc 0 <host> -> GenerateCopyright 1 <host> [dist] rustc <host> [dist] rustc 1 <host> -> std 1 <host> + [dist] rustc 1 <host> -> rustc-dev 2 <host> + [dist] rustc 1 <host> -> analysis 2 <host> [dist] src <> [build] rustc 1 <host> -> cargo 2 <host> + [dist] rustc 1 <host> -> cargo 2 <host> [build] rustc 1 <host> -> rust-analyzer 2 <host> + [dist] rustc 1 <host> -> rust-analyzer 2 <host> [build] rustc 1 <host> -> rustfmt 2 <host> [build] rustc 1 <host> -> cargo-fmt 2 <host> + [dist] rustc 1 <host> -> rustfmt 2 <host> [build] rustc 1 <host> -> clippy-driver 2 <host> [build] rustc 1 <host> -> cargo-clippy 2 <host> + [dist] rustc 1 <host> -> clippy 2 <host> [build] rustc 1 <host> -> miri 2 <host> [build] rustc 1 <host> -> cargo-miri 2 <host> + [dist] rustc 1 <host> -> miri 2 <host> + [dist] rustc 1 <host> -> extended 2 <host> + [dist] reproducible-artifacts <host> "); } @@ -1276,7 +1299,9 @@ mod snapshot { [dist] rustc 1 <host> -> std 1 <host> [build] rustc 2 <host> -> std 2 <target1> [dist] rustc 2 <host> -> std 2 <target1> + [dist] rustc 1 <host> -> rustc-dev 2 <host> [dist] src <> + [dist] reproducible-artifacts <host> " ); } @@ -1334,7 +1359,11 @@ mod snapshot { [build] rustdoc 2 <target1> [dist] rustc <target1> [dist] rustc 1 <host> -> std 1 <host> + [dist] rustc 1 <host> -> rustc-dev 2 <host> + [dist] rustc 1 <host> -> rustc-dev 2 <target1> [dist] src <> + [dist] reproducible-artifacts <host> + [dist] reproducible-artifacts <target1> " ); } @@ -1413,7 +1442,11 @@ mod snapshot { [dist] rustc <target1> [dist] rustc 1 <host> -> std 1 <host> [dist] rustc 1 <host> -> std 1 <target1> + [dist] rustc 1 <host> -> rustc-dev 2 <host> + [dist] rustc 1 <host> -> rustc-dev 2 <target1> [dist] src <> + [dist] reproducible-artifacts <host> + [dist] reproducible-artifacts <target1> " ); } @@ -1462,10 +1495,8 @@ mod snapshot { "); } - /// This also serves as an important regression test for <https://github.com/rust-lang/rust/issues/138123> - /// and <https://github.com/rust-lang/rust/issues/138004>. #[test] - fn dist_all_cross() { + fn dist_all_cross_extended() { let ctx = TestCtx::new(); insta::assert_snapshot!( ctx @@ -1518,22 +1549,99 @@ mod snapshot { [build] rustc 0 <host> -> GenerateCopyright 1 <host> [dist] rustc <target1> [dist] rustc 1 <host> -> std 1 <target1> + [dist] rustc 1 <host> -> rustc-dev 2 <target1> + [dist] rustc 1 <host> -> analysis 2 <target1> [dist] src <> [build] rustc 1 <host> -> cargo 2 <target1> + [dist] rustc 1 <host> -> cargo 2 <target1> [build] rustc 1 <host> -> rust-analyzer 2 <target1> + [dist] rustc 1 <host> -> rust-analyzer 2 <target1> [build] rustc 1 <host> -> rustfmt 2 <target1> [build] rustc 1 <host> -> cargo-fmt 2 <target1> + [dist] rustc 1 <host> -> rustfmt 2 <target1> [build] rustc 1 <host> -> clippy-driver 2 <target1> [build] rustc 1 <host> -> cargo-clippy 2 <target1> + [dist] rustc 1 <host> -> clippy 2 <target1> [build] rustc 1 <host> -> miri 2 <target1> [build] rustc 1 <host> -> cargo-miri 2 <target1> + [dist] rustc 1 <host> -> miri 2 <target1> [build] rustc 1 <host> -> LlvmBitcodeLinker 2 <target1> [doc] rustc 2 <target1> -> std 2 <target1> crates=[] + [dist] rustc 1 <host> -> extended 2 <target1> + [dist] reproducible-artifacts <target1> "); } - // Enable dist cranelift tarball by default with `x dist` if cranelift is enabled in - // `rust.codegen-backends`. + /// Simulates e.g. the powerpc64 builder, which is fully cross-compiled from x64, but it does + /// not build docs. Crutically, it shouldn't build host stage 2 rustc. + /// + /// This is a regression test for <https://github.com/rust-lang/rust/issues/138123> + /// and <https://github.com/rust-lang/rust/issues/138004>. + #[test] + fn dist_all_cross_extended_no_docs() { + let ctx = TestCtx::new(); + let steps = ctx + .config("dist") + .hosts(&[TEST_TRIPLE_1]) + .targets(&[TEST_TRIPLE_1]) + .args(&[ + "--set", + "rust.channel=nightly", + "--set", + "build.extended=true", + "--set", + "build.docs=false", + ]) + .get_steps(); + + // Make sure that we don't build stage2 host rustc + steps.assert_no_match(|m| { + m.name == "rustc" + && m.built_by.map(|b| b.stage) == Some(1) + && *m.target.triple == host_target() + }); + + insta::assert_snapshot!( + steps.render(), @r" + [dist] mingw <target1> + [build] llvm <host> + [build] llvm <target1> + [build] rustc 0 <host> -> rustc 1 <host> + [build] rustc 0 <host> -> WasmComponentLd 1 <host> + [build] rustc 1 <host> -> std 1 <target1> + [build] rustc 1 <host> -> std 1 <host> + [build] rustc 1 <host> -> rustc 2 <target1> + [build] rustc 1 <host> -> WasmComponentLd 2 <target1> + [build] rustdoc 2 <target1> + [build] rustc 1 <host> -> rust-analyzer-proc-macro-srv 2 <target1> + [build] rustc 0 <host> -> GenerateCopyright 1 <host> + [build] rustc 0 <host> -> RustInstaller 1 <host> + [dist] rustc <target1> + [dist] rustc 1 <host> -> std 1 <target1> + [dist] rustc 1 <host> -> rustc-dev 2 <target1> + [dist] rustc 1 <host> -> analysis 2 <target1> + [dist] src <> + [build] rustc 1 <host> -> cargo 2 <target1> + [dist] rustc 1 <host> -> cargo 2 <target1> + [build] rustc 1 <host> -> rust-analyzer 2 <target1> + [dist] rustc 1 <host> -> rust-analyzer 2 <target1> + [build] rustc 1 <host> -> rustfmt 2 <target1> + [build] rustc 1 <host> -> cargo-fmt 2 <target1> + [dist] rustc 1 <host> -> rustfmt 2 <target1> + [build] rustc 1 <host> -> clippy-driver 2 <target1> + [build] rustc 1 <host> -> cargo-clippy 2 <target1> + [dist] rustc 1 <host> -> clippy 2 <target1> + [build] rustc 1 <host> -> miri 2 <target1> + [build] rustc 1 <host> -> cargo-miri 2 <target1> + [dist] rustc 1 <host> -> miri 2 <target1> + [build] rustc 1 <host> -> LlvmBitcodeLinker 2 <target1> + [dist] rustc 1 <host> -> extended 2 <target1> + [dist] reproducible-artifacts <target1> + "); + } + + /// Enable dist cranelift tarball by default with `x dist` if cranelift is enabled in + /// `rust.codegen-backends`. #[test] fn dist_cranelift_by_default() { let ctx = TestCtx::new(); @@ -1581,7 +1689,38 @@ mod snapshot { [dist] rustc <host> [dist] rustc 1 <host> -> rustc_codegen_cranelift 2 <host> [dist] rustc 1 <host> -> std 1 <host> + [dist] rustc 1 <host> -> rustc-dev 2 <host> [dist] src <> + [dist] reproducible-artifacts <host> + "); + } + + #[test] + fn dist_bootstrap() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx + .config("dist") + .path("bootstrap") + .render_steps(), @r" + [build] rustc 0 <host> -> RustInstaller 1 <host> + [dist] bootstrap <host> + "); + } + + #[test] + fn dist_library_stage_0_local_rebuild() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("dist") + .path("rust-std") + .stage(0) + .targets(&[TEST_TRIPLE_1]) + .args(&["--set", "build.local-rebuild=true"]) + .render_steps(), @r" + [build] rustc 0 <host> -> std 0 <target1> + [build] rustc 0 <host> -> RustInstaller 1 <host> + [dist] rustc 0 <host> -> std 0 <target1> "); } @@ -2126,6 +2265,214 @@ mod snapshot { [doc] rustc 1 <host> -> reference (book) 2 <host> "); } + + #[test] + fn clippy_ci() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("clippy") + .path("ci") + .stage(2) + .render_steps(), @r" + [build] llvm <host> + [build] rustc 0 <host> -> rustc 1 <host> + [build] rustc 1 <host> -> std 1 <host> + [build] rustc 0 <host> -> clippy-driver 1 <host> + [build] rustc 0 <host> -> cargo-clippy 1 <host> + [clippy] rustc 1 <host> -> bootstrap 2 <host> + [clippy] rustc 1 <host> -> std 1 <host> + [clippy] rustc 1 <host> -> rustc 2 <host> + [check] rustc 1 <host> -> rustc 2 <host> + [clippy] rustc 1 <host> -> rustc_codegen_gcc 2 <host> + "); + } + + #[test] + fn clippy_compiler_stage1() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("clippy") + .path("compiler") + .render_steps(), @r" + [build] llvm <host> + [clippy] rustc 0 <host> -> rustc 1 <host> + "); + } + + #[test] + fn clippy_compiler_stage2() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("clippy") + .path("compiler") + .stage(2) + .render_steps(), @r" + [build] llvm <host> + [build] rustc 0 <host> -> rustc 1 <host> + [build] rustc 1 <host> -> std 1 <host> + [build] rustc 0 <host> -> clippy-driver 1 <host> + [build] rustc 0 <host> -> cargo-clippy 1 <host> + [clippy] rustc 1 <host> -> rustc 2 <host> + "); + } + + #[test] + fn clippy_std_stage1() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("clippy") + .path("std") + .render_steps(), @r" + [build] llvm <host> + [build] rustc 0 <host> -> rustc 1 <host> + [build] rustc 0 <host> -> clippy-driver 1 <host> + [build] rustc 0 <host> -> cargo-clippy 1 <host> + [clippy] rustc 1 <host> -> std 1 <host> + "); + } + + #[test] + fn clippy_std_stage2() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("clippy") + .path("std") + .stage(2) + .render_steps(), @r" + [build] llvm <host> + [build] rustc 0 <host> -> rustc 1 <host> + [build] rustc 1 <host> -> std 1 <host> + [build] rustc 1 <host> -> rustc 2 <host> + [build] rustc 1 <host> -> clippy-driver 2 <host> + [build] rustc 1 <host> -> cargo-clippy 2 <host> + [clippy] rustc 2 <host> -> std 2 <host> + "); + } + + #[test] + fn clippy_miri_stage1() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("clippy") + .path("miri") + .stage(1) + .render_steps(), @r" + [build] llvm <host> + [check] rustc 0 <host> -> rustc 1 <host> + [clippy] rustc 0 <host> -> miri 1 <host> + "); + } + + #[test] + fn clippy_miri_stage2() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("clippy") + .path("miri") + .stage(2) + .render_steps(), @r" + [build] llvm <host> + [build] rustc 0 <host> -> rustc 1 <host> + [build] rustc 1 <host> -> std 1 <host> + [check] rustc 1 <host> -> rustc 2 <host> + [build] rustc 0 <host> -> clippy-driver 1 <host> + [build] rustc 0 <host> -> cargo-clippy 1 <host> + [clippy] rustc 1 <host> -> miri 2 <host> + "); + } + + #[test] + fn clippy_bootstrap() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("clippy") + .path("bootstrap") + .render_steps(), @"[clippy] rustc 0 <host> -> bootstrap 1 <host>"); + } + + #[test] + fn install_extended() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("install") + .args(&[ + // Using backslashes fails with `--set` + "--set", &format!("install.prefix={}", ctx.dir().display()).replace("\\", "/"), + "--set", &format!("install.sysconfdir={}", ctx.dir().display()).replace("\\", "/"), + "--set", "build.extended=true" + ]) + .render_steps(), @r" + [build] llvm <host> + [build] rustc 0 <host> -> rustc 1 <host> + [build] rustc 0 <host> -> WasmComponentLd 1 <host> + [build] rustc 0 <host> -> UnstableBookGen 1 <host> + [build] rustc 0 <host> -> Rustbook 1 <host> + [doc] unstable-book (book) <host> + [build] rustc 1 <host> -> std 1 <host> + [doc] book (book) <host> + [doc] book/first-edition (book) <host> + [doc] book/second-edition (book) <host> + [doc] book/2018-edition (book) <host> + [build] rustdoc 1 <host> + [doc] rustc 1 <host> -> standalone 2 <host> + [build] rustc 1 <host> -> rustc 2 <host> + [build] rustc 1 <host> -> WasmComponentLd 2 <host> + [build] rustdoc 2 <host> + [doc] rustc 2 <host> -> std 2 <host> crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] + [build] rustc 1 <host> -> error-index 2 <host> + [doc] rustc 1 <host> -> error-index 2 <host> + [doc] nomicon (book) <host> + [doc] rustc 1 <host> -> reference (book) 2 <host> + [doc] rustdoc (book) <host> + [doc] rust-by-example (book) <host> + [build] rustc 0 <host> -> LintDocs 1 <host> + [doc] rustc (book) <host> + [doc] cargo (book) <host> + [doc] clippy (book) <host> + [doc] embedded-book (book) <host> + [doc] edition-guide (book) <host> + [doc] style-guide (book) <host> + [doc] rustc 1 <host> -> releases 2 <host> + [build] rustc 0 <host> -> RustInstaller 1 <host> + [dist] docs <host> + [dist] rustc 1 <host> -> std 1 <host> + [build] rustc 1 <host> -> rust-analyzer-proc-macro-srv 2 <host> + [build] rustc 0 <host> -> GenerateCopyright 1 <host> + [dist] rustc <host> + [build] rustc 1 <host> -> cargo 2 <host> + [dist] rustc 1 <host> -> cargo 2 <host> + [build] rustc 1 <host> -> rust-analyzer 2 <host> + [dist] rustc 1 <host> -> rust-analyzer 2 <host> + [build] rustc 1 <host> -> rustfmt 2 <host> + [build] rustc 1 <host> -> cargo-fmt 2 <host> + [dist] rustc 1 <host> -> rustfmt 2 <host> + [build] rustc 1 <host> -> clippy-driver 2 <host> + [build] rustc 1 <host> -> cargo-clippy 2 <host> + [dist] rustc 1 <host> -> clippy 2 <host> + [build] rustc 1 <host> -> miri 2 <host> + [build] rustc 1 <host> -> cargo-miri 2 <host> + [dist] rustc 1 <host> -> miri 2 <host> + [dist] src <> + "); + } + + // Check that `x run miri --target FOO` actually builds miri for the host. + #[test] + fn run_miri() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("run") + .path("miri") + .stage(1) + .targets(&[TEST_TRIPLE_1]) + .render_steps(), @r" + [build] llvm <host> + [build] rustc 0 <host> -> rustc 1 <host> + [build] rustc 0 <host> -> miri 1 <host> + [build] rustc 0 <host> -> cargo-miri 1 <host> + [run] rustc 0 <host> -> miri 1 <target1> + "); + } } struct ExecutedSteps { @@ -2180,6 +2527,21 @@ impl ExecutedSteps { } } + /// Make sure that no metadata matches the given `func`. + #[track_caller] + fn assert_no_match<F>(&self, func: F) + where + F: Fn(StepMetadata) -> bool, + { + for metadata in self.steps.iter().filter_map(|s| s.metadata.clone()) { + if func(metadata.clone()) { + panic!( + "Metadata {metadata:?} was found, even though it should have not been present" + ); + } + } + } + fn contains(&self, metadata: &StepMetadata) -> bool { self.steps .iter() diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 84c9f9cc4d2..a8eb563015f 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -46,8 +46,8 @@ 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, threads_from_config, + CompilerBuiltins, DebuginfoLevel, DryRun, GccCiMode, LlvmLibunwind, Merge, ReplaceOpt, + RustcLto, SplitDebuginfo, StringOrBool, threads_from_config, }; use crate::core::download::{ DownloadContext, download_beta_toolchain, is_download_ci_available, maybe_download_rustfmt, @@ -121,8 +121,7 @@ pub struct Config { pub patch_binaries_for_nix: Option<bool>, pub stage0_metadata: build_helper::stage0_parser::Stage0, pub android_ndk: Option<PathBuf>, - /// Whether to use the `c` feature of the `compiler_builtins` crate. - pub optimized_compiler_builtins: bool, + pub optimized_compiler_builtins: CompilerBuiltins, pub stdout_is_tty: bool, pub stderr_is_tty: bool, @@ -961,8 +960,8 @@ impl Config { Subcommand::Dist => flags_stage.or(build_dist_stage).unwrap_or(2), Subcommand::Install => flags_stage.or(build_install_stage).unwrap_or(2), Subcommand::Perf { .. } => flags_stage.unwrap_or(1), - // These are all bootstrap tools, which don't depend on the compiler. - // The stage we pass shouldn't matter, but use 0 just in case. + // Most of the run commands execute bootstrap tools, which don't depend on the compiler. + // Other commands listed here should always use bootstrap tools. Subcommand::Clean { .. } | Subcommand::Run { .. } | Subcommand::Setup { .. } @@ -970,23 +969,38 @@ impl Config { | Subcommand::Vendor { .. } => flags_stage.unwrap_or(0), }; - // Now check that the selected stage makes sense, and if not, print a warning and end + let local_rebuild = build_local_rebuild.unwrap_or(false); + + let check_stage0 = |kind: &str| { + if local_rebuild { + eprintln!("WARNING: running {kind} in stage 0. This might not work as expected."); + } else { + eprintln!( + "ERROR: cannot {kind} anything on stage 0. Use at least stage 1 or set build.local-rebuild=true and use a stage0 compiler built from in-tree sources." + ); + exit!(1); + } + }; + + // Now check that the selected stage makes sense, and if not, print an error and end match (stage, &flags_cmd) { (0, Subcommand::Build { .. }) => { - eprintln!("ERROR: cannot build anything on stage 0. Use at least stage 1."); - exit!(1); + check_stage0("build"); } (0, Subcommand::Check { .. }) => { - eprintln!("ERROR: cannot check anything on stage 0. Use at least stage 1."); - exit!(1); + check_stage0("check"); } (0, Subcommand::Doc { .. }) => { - eprintln!("ERROR: cannot document anything on stage 0. Use at least stage 1."); - exit!(1); + check_stage0("doc"); } (0, Subcommand::Clippy { .. }) => { - eprintln!("ERROR: cannot run clippy on stage 0. Use at least stage 1."); - exit!(1); + check_stage0("clippy"); + } + (0, Subcommand::Dist) => { + check_stage0("dist"); + } + (0, Subcommand::Install) => { + check_stage0("install"); } _ => {} } @@ -1101,7 +1115,11 @@ impl Config { let rustfmt_info = git_info(&exec_ctx, omit_git_hash, &src.join("src/tools/rustfmt")); let optimized_compiler_builtins = - build_optimized_compiler_builtins.unwrap_or(channel != "dev"); + build_optimized_compiler_builtins.unwrap_or(if channel == "dev" { + CompilerBuiltins::BuildRustOnly + } else { + CompilerBuiltins::BuildLLVMFuncs + }); let vendor = build_vendor.unwrap_or( rust_info.is_from_tarball() && src.join("vendor").exists() @@ -1223,7 +1241,7 @@ impl Config { llvm_use_libcxx: llvm_use_libcxx.unwrap_or(false), llvm_use_linker, llvm_version_suffix, - local_rebuild: build_local_rebuild.unwrap_or(false), + local_rebuild, locked_deps: build_locked_deps.unwrap_or(false), low_priority: build_low_priority.unwrap_or(false), mandir: install_mandir.map(PathBuf::from), @@ -1652,8 +1670,9 @@ impl Config { /// Returns the codegen backend that should be configured as the *default* codegen backend /// for a rustc compiled by bootstrap. - pub fn default_codegen_backend(&self, target: TargetSelection) -> Option<&CodegenBackendKind> { - self.enabled_codegen_backends(target).first() + pub fn default_codegen_backend(&self, target: TargetSelection) -> &CodegenBackendKind { + // We're guaranteed to have always at least one codegen backend listed. + self.enabled_codegen_backends(target).first().unwrap() } pub fn jemalloc(&self, target: TargetSelection) -> bool { @@ -1664,11 +1683,11 @@ impl Config { self.target_config.get(&target).and_then(|t| t.rpath).unwrap_or(self.rust_rpath) } - pub fn optimized_compiler_builtins(&self, target: TargetSelection) -> bool { + pub fn optimized_compiler_builtins(&self, target: TargetSelection) -> &CompilerBuiltins { self.target_config .get(&target) - .and_then(|t| t.optimized_compiler_builtins) - .unwrap_or(self.optimized_compiler_builtins) + .and_then(|t| t.optimized_compiler_builtins.as_ref()) + .unwrap_or(&self.optimized_compiler_builtins) } pub fn llvm_enabled(&self, target: TargetSelection) -> bool { diff --git a/src/bootstrap/src/core/config/mod.rs b/src/bootstrap/src/core/config/mod.rs index dbd05fd2519..5999348a7fe 100644 --- a/src/bootstrap/src/core/config/mod.rs +++ b/src/bootstrap/src/core/config/mod.rs @@ -218,6 +218,33 @@ impl<T> Merge for Option<T> { } } +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub enum CompilerBuiltins { + #[default] + // Only build native rust intrinsic compiler functions. + BuildRustOnly, + // Some intrinsic functions have a C implementation provided by LLVM's + // compiler-rt builtins library. Build them from the LLVM source included + // with Rust. + BuildLLVMFuncs, + // Similar to BuildLLVMFuncs, but specify a path to an existing library + // containing LLVM's compiler-rt builtins instead of compiling them. + LinkLLVMBuiltinsLib(String), +} + +impl<'de> Deserialize<'de> for CompilerBuiltins { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: Deserializer<'de>, + { + Ok(match Deserialize::deserialize(deserializer)? { + StringOrBool::Bool(false) => Self::BuildRustOnly, + StringOrBool::Bool(true) => Self::BuildLLVMFuncs, + StringOrBool::String(path) => Self::LinkLLVMBuiltinsLib(path), + }) + } +} + #[derive(Copy, Clone, Default, Debug, Eq, PartialEq)] pub enum DebuginfoLevel { #[default] diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index 50eba12aba7..e93525fbd09 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -17,7 +17,7 @@ use crate::core::build_steps::clippy::{LintConfig, get_clippy_rules_in_order}; use crate::core::build_steps::llvm; use crate::core::build_steps::llvm::LLVM_INVALIDATION_PATHS; use crate::core::config::toml::TomlConfig; -use crate::core::config::{LldMode, Target, TargetSelection}; +use crate::core::config::{CompilerBuiltins, LldMode, StringOrBool, Target, TargetSelection}; use crate::utils::tests::git::git_test; pub(crate) fn parse(config: &str) -> Config { @@ -183,7 +183,11 @@ runner = "x86_64-runner" ); assert_eq!(config.gdb, Some("bar".into()), "setting string value with quotes"); assert!(!config.deny_warnings, "setting boolean value"); - assert!(config.optimized_compiler_builtins, "setting boolean value"); + assert_eq!( + config.optimized_compiler_builtins, + CompilerBuiltins::BuildLLVMFuncs, + "setting boolean value" + ); assert_eq!( config.tools, Some(["cargo".to_string()].into_iter().collect()), @@ -212,7 +216,7 @@ runner = "x86_64-runner" let darwin = TargetSelection::from_user("aarch64-apple-darwin"); let darwin_values = Target { runner: Some("apple".into()), - optimized_compiler_builtins: Some(false), + optimized_compiler_builtins: Some(CompilerBuiltins::BuildRustOnly), ..Default::default() }; assert_eq!( diff --git a/src/bootstrap/src/core/config/toml/build.rs b/src/bootstrap/src/core/config/toml/build.rs index 728367b3972..25c19f1070a 100644 --- a/src/bootstrap/src/core/config/toml/build.rs +++ b/src/bootstrap/src/core/config/toml/build.rs @@ -11,7 +11,7 @@ use std::collections::HashMap; use serde::{Deserialize, Deserializer}; use crate::core::config::toml::ReplaceOpt; -use crate::core::config::{Merge, StringOrBool}; +use crate::core::config::{CompilerBuiltins, Merge, StringOrBool}; use crate::{HashSet, PathBuf, define_config, exit}; define_config! { @@ -65,7 +65,7 @@ define_config! { // NOTE: only parsed by bootstrap.py, `--feature build-metrics` enables metrics unconditionally metrics: Option<bool> = "metrics", android_ndk: Option<PathBuf> = "android-ndk", - optimized_compiler_builtins: Option<bool> = "optimized-compiler-builtins", + optimized_compiler_builtins: Option<CompilerBuiltins> = "optimized-compiler-builtins", jobs: Option<u32> = "jobs", compiletest_diff_tool: Option<String> = "compiletest-diff-tool", compiletest_allow_stage0: Option<bool> = "compiletest-allow-stage0", diff --git a/src/bootstrap/src/core/config/toml/rust.rs b/src/bootstrap/src/core/config/toml/rust.rs index 3dab8d1d96d..c54df456d52 100644 --- a/src/bootstrap/src/core/config/toml/rust.rs +++ b/src/bootstrap/src/core/config/toml/rust.rs @@ -269,9 +269,9 @@ pub fn check_incompatible_options_for_ci_rustc( err!(current_profiler, profiler, "build"); let current_optimized_compiler_builtins = - current_config_toml.build.as_ref().and_then(|b| b.optimized_compiler_builtins); + current_config_toml.build.as_ref().and_then(|b| b.optimized_compiler_builtins.clone()); let optimized_compiler_builtins = - ci_config_toml.build.as_ref().and_then(|b| b.optimized_compiler_builtins); + ci_config_toml.build.as_ref().and_then(|b| b.optimized_compiler_builtins.clone()); err!(current_optimized_compiler_builtins, optimized_compiler_builtins, "build"); // We always build the in-tree compiler on cross targets, so we only care @@ -415,6 +415,10 @@ pub(crate) fn parse_codegen_backends( }; found_backends.push(backend); } + if found_backends.is_empty() { + eprintln!("ERROR: `{section}.codegen-backends` should not be set to `[]`"); + exit!(1); + } found_backends } diff --git a/src/bootstrap/src/core/config/toml/target.rs b/src/bootstrap/src/core/config/toml/target.rs index 2c06fd083a8..020602e6a19 100644 --- a/src/bootstrap/src/core/config/toml/target.rs +++ b/src/bootstrap/src/core/config/toml/target.rs @@ -11,7 +11,9 @@ use serde::{Deserialize, Deserializer}; -use crate::core::config::{LlvmLibunwind, Merge, ReplaceOpt, SplitDebuginfo, StringOrBool}; +use crate::core::config::{ + CompilerBuiltins, LlvmLibunwind, Merge, ReplaceOpt, SplitDebuginfo, StringOrBool, +}; use crate::{CodegenBackendKind, HashSet, PathBuf, define_config, exit}; define_config! { @@ -39,7 +41,7 @@ define_config! { no_std: Option<bool> = "no-std", codegen_backends: Option<Vec<String>> = "codegen-backends", runner: Option<String> = "runner", - optimized_compiler_builtins: Option<bool> = "optimized-compiler-builtins", + optimized_compiler_builtins: Option<CompilerBuiltins> = "optimized-compiler-builtins", jemalloc: Option<bool> = "jemalloc", } } @@ -71,7 +73,7 @@ pub struct Target { pub runner: Option<String>, pub no_std: bool, pub codegen_backends: Option<Vec<CodegenBackendKind>>, - pub optimized_compiler_builtins: Option<bool>, + pub optimized_compiler_builtins: Option<CompilerBuiltins>, pub jemalloc: Option<bool>, } diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index de7cada93f2..099ec488397 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -18,7 +18,7 @@ use crate::builder::Builder; use crate::builder::Kind; #[cfg(not(test))] use crate::core::build_steps::tool; -use crate::core::config::Target; +use crate::core::config::{CompilerBuiltins, Target}; use crate::utils::exec::command; use crate::{Build, Subcommand}; @@ -34,6 +34,7 @@ pub struct Finder { // Targets can be removed from this list once they are present in the stage0 compiler (usually by updating the beta compiler of the bootstrap). const STAGE0_MISSING_TARGETS: &[&str] = &[ "armv7a-vex-v5", + "riscv64a23-unknown-linux-gnu", // just a dummy comment so the list doesn't get onelined "aarch64_be-unknown-hermit", "aarch64_be-unknown-none-softfloat", @@ -330,7 +331,8 @@ than building it. // compiler-rt c fallbacks for wasm cannot be built with gcc if target.contains("wasm") - && (build.config.optimized_compiler_builtins(*target) + && (*build.config.optimized_compiler_builtins(*target) + != CompilerBuiltins::BuildRustOnly || build.config.rust_std_features.contains("compiler-builtins-c")) { let cc_tool = build.cc_tool(*target); diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index ec7edbf7531..b8ee83b20e4 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -1950,6 +1950,20 @@ impl Build { t!(fs::remove_dir_all(dir)) } + /// Make sure that `dir` will be an empty existing directory after this function ends. + /// If it existed before, it will be first deleted. + fn clear_dir(&self, dir: &Path) { + if self.config.dry_run() { + return; + } + + #[cfg(feature = "tracing")] + let _span = trace_io!("dir-clear", ?dir); + + let _ = std::fs::remove_dir_all(dir); + self.create_dir(dir); + } + fn read_dir(&self, dir: &Path) -> impl Iterator<Item = fs::DirEntry> { let iter = match fs::read_dir(dir) { Ok(v) => v, diff --git a/src/bootstrap/src/utils/build_stamp.rs b/src/bootstrap/src/utils/build_stamp.rs index 6c79385190e..4c35388a181 100644 --- a/src/bootstrap/src/utils/build_stamp.rs +++ b/src/bootstrap/src/utils/build_stamp.rs @@ -136,13 +136,13 @@ pub fn codegen_backend_stamp( } /// Cargo's output path for the standard library in a given stage, compiled -/// by a particular compiler for the specified target. +/// by a particular `build_compiler` for the specified `target`. pub fn libstd_stamp( builder: &Builder<'_>, - compiler: Compiler, + build_compiler: Compiler, target: TargetSelection, ) -> BuildStamp { - BuildStamp::new(&builder.cargo_out(compiler, Mode::Std, target)).with_prefix("libstd") + BuildStamp::new(&builder.cargo_out(build_compiler, Mode::Std, target)).with_prefix("libstd") } /// Cargo's output path for librustc in a given stage, compiled by a particular diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 4fb5891ed18..606d88d3db4 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -516,4 +516,19 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "Build/check now supports forwarding `--timings` flag to cargo.", }, + ChangeInfo { + change_id: 145472, + severity: ChangeSeverity::Warning, + summary: "It is no longer possible to `x dist` or `x install` with stage 0. All dist and install commands have to be on stage 1+.", + }, + ChangeInfo { + change_id: 143689, + severity: ChangeSeverity::Info, + summary: "The `optimized-compiler-builtins` option now accepts a path to an existing compiler-rt builtins library.", + }, + ChangeInfo { + change_id: 145876, + severity: ChangeSeverity::Info, + summary: "It is now possible to `check/build/dist` the standard stage 0 library if you use a stage0 rustc built from in-tree sources. This is useful for quickly cross-compiling the standard library. You have to enable build.local-rebuild for this to work.", + }, ]; diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs index 9a536f75ab7..e09f3086b77 100644 --- a/src/bootstrap/src/utils/exec.rs +++ b/src/bootstrap/src/utils/exec.rs @@ -264,8 +264,11 @@ impl<'a> BootstrapCommand { self } - pub fn do_not_cache(&mut self) -> &mut Self { - self.should_cache = false; + /// Cache the command. If it will be executed multiple times with the exact same arguments + /// and environment variables in the same bootstrap invocation, the previous result will be + /// loaded from memory. + pub fn cached(&mut self) -> &mut Self { + self.should_cache = true; self } @@ -425,7 +428,7 @@ impl From<Command> for BootstrapCommand { fn from(command: Command) -> Self { let program = command.get_program().to_owned(); Self { - should_cache: true, + should_cache: false, command, failure_behavior: BehaviorOnFailure::Exit, run_in_dry_run: false, diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index 451482717b6..e802c0214dd 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -510,6 +510,8 @@ pub fn check_cfg_arg(name: &str, values: Option<&[&str]>) -> String { #[track_caller] pub fn git(source_dir: Option<&Path>) -> BootstrapCommand { let mut git = command("git"); + // git commands are almost always read-only, so cache them by default + git.cached(); if let Some(source_dir) = source_dir { git.current_dir(source_dir); diff --git a/src/bootstrap/src/utils/tests/mod.rs b/src/bootstrap/src/utils/tests/mod.rs index 983680b0385..3332187e2a8 100644 --- a/src/bootstrap/src/utils/tests/mod.rs +++ b/src/bootstrap/src/utils/tests/mod.rs @@ -31,6 +31,10 @@ impl TestCtx { Self { directory } } + pub fn dir(&self) -> &Path { + self.directory.path() + } + /// Starts a new invocation of bootstrap that executes `kind` as its top level command /// (i.e. `x <kind>`). Returns a builder that configures the created config through CLI flags. pub fn config(&self, kind: &str) -> ConfigBuilder { diff --git a/src/build_helper/src/npm.rs b/src/build_helper/src/npm.rs index 86cf6183bd0..5a7df0999bd 100644 --- a/src/build_helper/src/npm.rs +++ b/src/build_helper/src/npm.rs @@ -27,7 +27,7 @@ pub fn install(src_root_path: &Path, out_dir: &Path, npm: &Path) -> Result<PathB } // disable a bunch of things we don't want. // this makes tidy output less noisy, and also significantly improves runtime - // of repeated tidy invokations. + // of repeated tidy invocations. cmd.args(&["--audit=false", "--save=false", "--fund=false"]); cmd.current_dir(out_dir); let exit_status = cmd.spawn()?.wait()?; diff --git a/src/ci/docker/host-x86_64/dist-aarch64-windows-gnullvm/Dockerfile b/src/ci/docker/host-x86_64/dist-aarch64-windows-gnullvm/Dockerfile index cdbc1cda025..0bb51af817a 100644 --- a/src/ci/docker/host-x86_64/dist-aarch64-windows-gnullvm/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-aarch64-windows-gnullvm/Dockerfile @@ -26,23 +26,10 @@ ENV CC_aarch64_pc_windows_gnullvm=aarch64-w64-mingw32-clang \ ENV HOST=aarch64-pc-windows-gnullvm -# We are bootstrapping this target and cannot use previously built artifacts. -# Without this option Clang is given `"-I/checkout/obj/build/aarch64-pc-windows-gnullvm/ci-llvm/include"` -# despite no such directory existing: -# $ ls obj/dist-windows-gnullvm/build/aarch64-pc-windows-gnullvm/ -1 -# llvm -# stage2 -ENV NO_DOWNLOAD_CI_LLVM 1 - ENV RUST_CONFIGURE_ARGS \ - --enable-extended \ + --enable-full-tools \ --enable-profiler \ --enable-sanitizers \ - --disable-docs \ - --set llvm.download-ci-llvm=false \ - --set rust.llvm-tools=false -# LLVM cross tools are not installed into expected location so copying fails. -# Probably will solve itself once this target can host itself on Windows. -# --enable-full-tools \ + --disable-docs ENV SCRIPT python3 ../x.py dist --host $HOST --target $HOST diff --git a/src/ci/docker/host-x86_64/dist-x86_64-windows-gnullvm/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-windows-gnullvm/Dockerfile index 1ee3951beb5..da0c065c854 100644 --- a/src/ci/docker/host-x86_64/dist-x86_64-windows-gnullvm/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-x86_64-windows-gnullvm/Dockerfile @@ -28,23 +28,10 @@ ENV CC_i686_pc_windows_gnullvm=i686-w64-mingw32-clang \ ENV HOST=x86_64-pc-windows-gnullvm ENV TARGETS=i686-pc-windows-gnullvm,x86_64-pc-windows-gnullvm -# We are bootstrapping this target and cannot use previously built artifacts. -# Without this option Clang is given `"-I/checkout/obj/build/aarch64-pc-windows-gnullvm/ci-llvm/include"` -# despite no such directory existing: -# $ ls obj/dist-windows-gnullvm/build/aarch64-pc-windows-gnullvm/ -1 -# llvm -# stage2 -ENV NO_DOWNLOAD_CI_LLVM 1 - ENV RUST_CONFIGURE_ARGS \ - --enable-extended \ + --enable-full-tools \ --enable-profiler \ --enable-sanitizers \ - --disable-docs \ - --set llvm.download-ci-llvm=false \ - --set rust.llvm-tools=false -# LLVM cross tools are not installed into expected location so copying fails. -# Probably will solve itself once these targets can host themselves on Windows. -# --enable-full-tools \ + --disable-docs ENV SCRIPT python3 ../x.py dist --host $HOST --target $TARGETS diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile index b97568b0819..5052d86f0ac 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile @@ -38,11 +38,15 @@ ENV RUST_CONFIGURE_ARGS \ --build=x86_64-unknown-linux-gnu \ --enable-debug \ --enable-lld \ + --set rust.debuginfo-level-tests=1 \ --set llvm.use-linker=lld \ --set target.x86_64-unknown-linux-gnu.linker=clang \ --set target.x86_64-unknown-linux-gnu.cc=clang \ --set target.x86_64-unknown-linux-gnu.cxx=clang++ +# This job checks: +# - That ui tests can be built with `-Cdebuginfo=1` + # This job appears to be checking two separate things: # - That we can build the compiler with `--enable-debug` # (without necessarily testing the result). @@ -51,4 +55,5 @@ ENV RUST_CONFIGURE_ARGS \ ENV SCRIPT \ python3 ../x.py --stage 2 build && \ + python3 ../x.py --stage 2 test tests/ui && \ python3 ../x.py --stage 2 test tests/run-make diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile index 98fd31a22e9..5bafd89cfd9 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile @@ -33,9 +33,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh -# We are disabling CI LLVM since distcheck is an offline build. -ENV NO_DOWNLOAD_CI_LLVM 1 +# Make distcheck builds faster +ENV DISTCHECK_CONFIGURE_ARGS "--enable-sccache" -ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu --set rust.omit-git-hash=false -ENV SCRIPT python3 ../x.py --stage 2 test distcheck -ENV DIST_SRC 1 +ENV SCRIPT python3 ../x.py test distcheck diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 409d2cba821..35b9456d37d 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -104,10 +104,10 @@ jobs: # These jobs automatically inherit envs.pr, to avoid repeating it in each job # definition. # -# PR CI jobs will be automatically registered as Auto CI jobs or overriden. When +# PR CI jobs will be automatically registered as Auto CI jobs or overridden. When # automatically registered, the PR CI job configuration will be copied as an -# Auto CI job but with `continue_on_error` overriden to `false` (to fail-fast). -# When overriden, `citool` will check for equivalence between the PR and CI job +# Auto CI job but with `continue_on_error` overridden to `false` (to fail-fast). +# When overridden, `citool` will check for equivalence between the PR and CI job # of the same name modulo `continue_on_error` and `env`. pr: - name: pr-check-1 @@ -166,7 +166,7 @@ optional: # # Auto jobs may not specify `continue_on_error: true`, and thus will fail-fast. # -# Unless explicitly overriden, PR CI jobs will be automatically registered as +# Unless explicitly overridden, PR CI jobs will be automatically registered as # Auto CI jobs. auto: ############################# diff --git a/src/ci/scripts/free-disk-space-linux.sh b/src/ci/scripts/free-disk-space-linux.sh index 32649fe0d9b..ac3c9cfb28b 100755 --- a/src/ci/scripts/free-disk-space-linux.sh +++ b/src/ci/scripts/free-disk-space-linux.sh @@ -221,10 +221,13 @@ cleanPackages() { ) fi - sudo apt-get -qq remove -y --fix-missing "${packages[@]}" + WAIT_DPKG_LOCK="-o DPkg::Lock::Timeout=60" + sudo apt-get ${WAIT_DPKG_LOCK} -qq remove -y --fix-missing "${packages[@]}" - sudo apt-get autoremove -y || echo "::warning::The command [sudo apt-get autoremove -y] failed" - sudo apt-get clean || echo "::warning::The command [sudo apt-get clean] failed failed" + sudo apt-get ${WAIT_DPKG_LOCK} autoremove -y \ + || echo "::warning::The command [sudo apt-get autoremove -y] failed" + sudo apt-get ${WAIT_DPKG_LOCK} clean \ + || echo "::warning::The command [sudo apt-get clean] failed" } # Remove Docker images. diff --git a/src/doc/nomicon b/src/doc/nomicon -Subproject 3ff384320598bbe8d8cfe5cb8f18f78a3a3e6b1 +Subproject 57ed4473660565d9357fcae176b358d7e8724eb diff --git a/src/doc/reference b/src/doc/reference -Subproject 59b8af811886313577615c2cf0e045f01faed88 +Subproject 89f67b3c1b904cbcd9ed55e443d6fc67c8ca276 diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example -Subproject adc1f3b9012ad3255eea2054ca30596a953d053 +Subproject ad27f82c18464525c761a4a8db2e01785da59e1 diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index a399b5cd77e..f412399cc8c 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -425a9c0a0e365c0b8c6cfd00c2ded83a73bed9a0 +a1dbb443527bd126452875eb5d5860c1d001d761 diff --git a/src/doc/rustc-dev-guide/src/SUMMARY.md b/src/doc/rustc-dev-guide/src/SUMMARY.md index 025a078ae5b..a1612738537 100644 --- a/src/doc/rustc-dev-guide/src/SUMMARY.md +++ b/src/doc/rustc-dev-guide/src/SUMMARY.md @@ -103,6 +103,7 @@ - [The `rustdoc-json` test suite](./rustdoc-internals/rustdoc-json-test-suite.md) - [GPU offload internals](./offload/internals.md) - [Installation](./offload/installation.md) + - [Usage](./offload/usage.md) - [Autodiff internals](./autodiff/internals.md) - [Installation](./autodiff/installation.md) - [How to debug](./autodiff/debugging.md) diff --git a/src/doc/rustc-dev-guide/src/building/bootstrapping/what-bootstrapping-does.md b/src/doc/rustc-dev-guide/src/building/bootstrapping/what-bootstrapping-does.md index da425d8d39b..bfd75ebda40 100644 --- a/src/doc/rustc-dev-guide/src/building/bootstrapping/what-bootstrapping-does.md +++ b/src/doc/rustc-dev-guide/src/building/bootstrapping/what-bootstrapping-does.md @@ -23,7 +23,7 @@ Note that this documentation mostly covers user-facing information. See ### Overview -- Stage 0: the pre-compiled compiler +- Stage 0: the pre-compiled compiler and standard library - Stage 1: from current code, by an earlier compiler - Stage 2: the truly current compiler - Stage 3: the same-result test @@ -192,7 +192,7 @@ include, but are not limited to: artifacts'). If you're working on the standard library, this is normally the test command you want. - `./x build --stage 0` means to build with the stage0 `rustc`. -- `./x doc --stage 0` means to document using the stage0 `rustdoc`. +- `./x doc --stage 1` means to document using the stage0 `rustdoc`. #### Examples of what *not* to do @@ -211,7 +211,7 @@ include, but are not limited to: In short, _stage 0 uses the `stage0` compiler to create `stage0` artifacts which will later be uplifted to be the stage1 compiler_. -In each stage, two major steps are performed: +In each stage besides 0, two major steps are performed: 1. `std` is compiled by the stage N compiler. 2. That `std` is linked to programs built by the stage N compiler, including the diff --git a/src/doc/rustc-dev-guide/src/img/coverage-branch-counting-01.png b/src/doc/rustc-dev-guide/src/img/coverage-branch-counting-01.png index c445f3552a6..7c6c845f2cb 100644 --- a/src/doc/rustc-dev-guide/src/img/coverage-branch-counting-01.png +++ b/src/doc/rustc-dev-guide/src/img/coverage-branch-counting-01.png Binary files differdiff --git a/src/doc/rustc-dev-guide/src/img/dataflow-graphviz-example.png b/src/doc/rustc-dev-guide/src/img/dataflow-graphviz-example.png index 718411a8c42..7baa37e4323 100644 --- a/src/doc/rustc-dev-guide/src/img/dataflow-graphviz-example.png +++ b/src/doc/rustc-dev-guide/src/img/dataflow-graphviz-example.png Binary files differdiff --git a/src/doc/rustc-dev-guide/src/img/github-cli.png b/src/doc/rustc-dev-guide/src/img/github-cli.png index c3b0e7707eb..88ba95f90a8 100644 --- a/src/doc/rustc-dev-guide/src/img/github-cli.png +++ b/src/doc/rustc-dev-guide/src/img/github-cli.png Binary files differdiff --git a/src/doc/rustc-dev-guide/src/img/github-whitespace-changes.png b/src/doc/rustc-dev-guide/src/img/github-whitespace-changes.png index 9a19a10aace..e235a30b33e 100644 --- a/src/doc/rustc-dev-guide/src/img/github-whitespace-changes.png +++ b/src/doc/rustc-dev-guide/src/img/github-whitespace-changes.png Binary files differdiff --git a/src/doc/rustc-dev-guide/src/img/llvm-cov-show-01.png b/src/doc/rustc-dev-guide/src/img/llvm-cov-show-01.png index 35f04594347..ce4dec128b6 100644 --- a/src/doc/rustc-dev-guide/src/img/llvm-cov-show-01.png +++ b/src/doc/rustc-dev-guide/src/img/llvm-cov-show-01.png Binary files differdiff --git a/src/doc/rustc-dev-guide/src/img/other-peoples-commits.png b/src/doc/rustc-dev-guide/src/img/other-peoples-commits.png index e4fc2c7972e..0c949d8844d 100644 --- a/src/doc/rustc-dev-guide/src/img/other-peoples-commits.png +++ b/src/doc/rustc-dev-guide/src/img/other-peoples-commits.png Binary files differdiff --git a/src/doc/rustc-dev-guide/src/img/rustbot-submodules.png b/src/doc/rustc-dev-guide/src/img/rustbot-submodules.png index c2e6937cbeb..c099fdfcb46 100644 --- a/src/doc/rustc-dev-guide/src/img/rustbot-submodules.png +++ b/src/doc/rustc-dev-guide/src/img/rustbot-submodules.png Binary files differdiff --git a/src/doc/rustc-dev-guide/src/img/submodule-conflicts.png b/src/doc/rustc-dev-guide/src/img/submodule-conflicts.png index e90a6bbe8fd..5d4caf0b142 100644 --- a/src/doc/rustc-dev-guide/src/img/submodule-conflicts.png +++ b/src/doc/rustc-dev-guide/src/img/submodule-conflicts.png Binary files differdiff --git a/src/doc/rustc-dev-guide/src/img/wpa-initial-memory.png b/src/doc/rustc-dev-guide/src/img/wpa-initial-memory.png index b6020667ef0..177d92c794c 100644 --- a/src/doc/rustc-dev-guide/src/img/wpa-initial-memory.png +++ b/src/doc/rustc-dev-guide/src/img/wpa-initial-memory.png Binary files differdiff --git a/src/doc/rustc-dev-guide/src/img/wpa-stack.png b/src/doc/rustc-dev-guide/src/img/wpa-stack.png index 29eb5a54b5d..a4a71358ac1 100644 --- a/src/doc/rustc-dev-guide/src/img/wpa-stack.png +++ b/src/doc/rustc-dev-guide/src/img/wpa-stack.png Binary files differdiff --git a/src/doc/rustc-dev-guide/src/macro-expansion.md b/src/doc/rustc-dev-guide/src/macro-expansion.md index 54d6d2b4e81..96f12b76416 100644 --- a/src/doc/rustc-dev-guide/src/macro-expansion.md +++ b/src/doc/rustc-dev-guide/src/macro-expansion.md @@ -517,8 +517,9 @@ We use these items in macro parser: are about to ask the MBE parser to parse. We will consume the raw stream of tokens and output a binding of metavariables to corresponding token trees. The parsing session can be used to report parser errors. -- a `matcher` variable is a sequence of [`MatcherLoc`]s that we want to match - the token stream against. They're converted from token trees before matching. +- a `matcher` variable is a sequence of [`MatcherLoc`]s that we want to match the token stream + against. They're converted from the original token trees in the macro's definition before + matching. [`MatcherLoc`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/mbe/macro_parser/enum.MatcherLoc.html @@ -544,41 +545,26 @@ The full interface is defined [here][code_parse_int]. The macro parser does pretty much exactly the same as a normal regex parser with one exception: in order to parse different types of metavariables, such as `ident`, `block`, `expr`, etc., the macro parser must call back to the normal -Rust parser. Both the definition and invocation of macros are parsed using -the parser in a process which is non-intuitively self-referential. - -The code to parse macro _definitions_ is in -[`compiler/rustc_expand/src/mbe/macro_rules.rs`][code_mr]. It defines the -pattern for matching a macro definition as `$( $lhs:tt => $rhs:tt );+`. In -other words, a `macro_rules` definition should have in its body at least one -occurrence of a token tree followed by `=>` followed by another token tree. -When the compiler comes to a `macro_rules` definition, it uses this pattern to -match the two token trees per the rules of the definition of the macro, _thereby -utilizing the macro parser itself_. In our example definition, the -metavariable `$lhs` would match the patterns of both arms: `(print -$mvar:ident)` and `(print twice $mvar:ident)`. And `$rhs` would match the -bodies of both arms: `{ println!("{}", $mvar); }` and `{ println!("{}", $mvar); -println!("{}", $mvar); }`. The parser keeps this knowledge around for when it -needs to expand a macro invocation. - -When the compiler comes to a macro invocation, it parses that invocation using -a NFA-based macro parser described above. However, the matcher variable -used is the first token tree (`$lhs`) extracted from the arms of the macro -_definition_. Using our example, we would try to match the token stream `print -foo` from the invocation against the matchers `print $mvar:ident` and `print -twice $mvar:ident` that we previously extracted from the definition. The -algorithm is exactly the same, but when the macro parser comes to a place in the -current matcher where it needs to match a _non-terminal_ (e.g. `$mvar:ident`), -it calls back to the normal Rust parser to get the contents of that -non-terminal. In this case, the Rust parser would look for an `ident` token, -which it finds (`foo`) and returns to the macro parser. Then, the macro parser -proceeds in parsing as normal. Also, note that exactly one of the matchers from -the various arms should match the invocation; if there is more than one match, -the parse is ambiguous, while if there are no matches at all, there is a syntax +Rust parser. + +The code to parse macro definitions is in [`compiler/rustc_expand/src/mbe/macro_rules.rs`][code_mr]. +For more information about the macro parser's implementation, see the comments in +[`compiler/rustc_expand/src/mbe/macro_parser.rs`][code_mp]. + +Using our example, we would try to match the token stream `print foo` from the invocation against +the matchers `print $mvar:ident` and `print twice $mvar:ident` that we previously extracted from the +rules in the macro definition. When the macro parser comes to a place in the current matcher where +it needs to match a _non-terminal_ (e.g. `$mvar:ident`), it calls back to the normal Rust parser to +get the contents of that non-terminal. In this case, the Rust parser would look for an `ident` +token, which it finds (`foo`) and returns to the macro parser. Then, the macro parser continues +parsing. + +Note that exactly one of the matchers from the various rules should match the invocation; if there is +more than one match, the parse is ambiguous, while if there are no matches at all, there is a syntax error. -For more information about the macro parser's implementation, see the comments -in [`compiler/rustc_expand/src/mbe/macro_parser.rs`][code_mp]. +Assuming exactly one rule matches, macro expansion will then *transcribe* the right-hand side of the +rule, substituting the values of any matches it captured when matching against the left-hand side. ## Procedural Macros diff --git a/src/doc/rustc-dev-guide/src/offload/installation.md b/src/doc/rustc-dev-guide/src/offload/installation.md index b376e962ff6..d1ebf33ac17 100644 --- a/src/doc/rustc-dev-guide/src/offload/installation.md +++ b/src/doc/rustc-dev-guide/src/offload/installation.md @@ -1,6 +1,6 @@ # Installation -In the future, `std::offload` should become available in nightly builds for users. For now, everyone still needs to build rustc from source. +`std::offload` is partly available in nightly builds for users. For now, everyone however still needs to build rustc from source to use all features of it. ## Build instructions @@ -42,30 +42,3 @@ run ``` ./x test --stage 1 tests/codegen-llvm/gpu_offload ``` - -## Usage -It is important to use a clang compiler build on the same llvm as rustc. Just calling clang without the full path will likely use your system clang, which probably will be incompatible. -``` -/absolute/path/to/rust/build/x86_64-unknown-linux-gnu/stage1/bin/rustc --edition=2024 --crate-type cdylib src/main.rs --emit=llvm-ir -O -C lto=fat -Cpanic=abort -Zoffload=Enable -/absolute/path/to/rust/build/x86_64-unknown-linux-gnu/llvm/bin/clang++ -fopenmp --offload-arch=native -g -O3 main.ll -o main -save-temps -LIBOMPTARGET_INFO=-1 ./main -``` -The first step will generate a `main.ll` file, which has enough instructions to cause the offload runtime to move data to and from a gpu. -The second step will use clang as the compilation driver to compile our IR file down to a working binary. Only a very small Rust subset will work out of the box here, unless -you use features like build-std, which are not covered by this guide. Look at the codegen test to get a feeling for how to write a working example. -In the last step you can run your binary, if all went well you will see a data transfer being reported: -``` -omptarget device 0 info: Entering OpenMP data region with being_mapper at unknown:0:0 with 1 arguments: -omptarget device 0 info: tofrom(unknown)[1024] -omptarget device 0 info: Creating new map entry with HstPtrBase=0x00007fffffff9540, HstPtrBegin=0x00007fffffff9540, TgtAllocBegin=0x0000155547200000, TgtPtrBegin=0x0000155547200000, Size=1024, DynRefCount=1, HoldRefCount=0, Name=unknown -omptarget device 0 info: Copying data from host to device, HstPtr=0x00007fffffff9540, TgtPtr=0x0000155547200000, Size=1024, Name=unknown -omptarget device 0 info: OpenMP Host-Device pointer mappings after block at unknown:0:0: -omptarget device 0 info: Host Ptr Target Ptr Size (B) DynRefCount HoldRefCount Declaration -omptarget device 0 info: 0x00007fffffff9540 0x0000155547200000 1024 1 0 unknown at unknown:0:0 -// some other output -omptarget device 0 info: Exiting OpenMP data region with end_mapper at unknown:0:0 with 1 arguments: -omptarget device 0 info: tofrom(unknown)[1024] -omptarget device 0 info: Mapping exists with HstPtrBegin=0x00007fffffff9540, TgtPtrBegin=0x0000155547200000, Size=1024, DynRefCount=0 (decremented, delayed deletion), HoldRefCount=0 -omptarget device 0 info: Copying data from device to host, TgtPtr=0x0000155547200000, HstPtr=0x00007fffffff9540, Size=1024, Name=unknown -omptarget device 0 info: Removing map entry with HstPtrBegin=0x00007fffffff9540, TgtPtrBegin=0x0000155547200000, Size=1024, Name=unknown -``` diff --git a/src/doc/rustc-dev-guide/src/offload/usage.md b/src/doc/rustc-dev-guide/src/offload/usage.md new file mode 100644 index 00000000000..9f519984d9b --- /dev/null +++ b/src/doc/rustc-dev-guide/src/offload/usage.md @@ -0,0 +1,112 @@ +# Usage + +This feature is work-in-progress, and not ready for usage. The instructions here are for contributors, or people interested in following the latest progress. +We currently work on launching the following Rust kernel on the GPU. To follow along, copy it to a `src/lib.rs` file. + +```rust +#![feature(abi_gpu_kernel)] +#![no_std] + +#[cfg(target_os = "linux")] +extern crate libc; +#[cfg(target_os = "linux")] +use libc::c_char; + +use core::mem; + +#[panic_handler] +fn panic(_: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[cfg(target_os = "linux")] +#[unsafe(no_mangle)] +#[inline(never)] +fn main() { + let array_c: *mut [f64; 256] = + unsafe { libc::calloc(256, (mem::size_of::<f64>()) as libc::size_t) as *mut [f64; 256] }; + let output = c"The first element is zero %f\n"; + let output2 = c"The first element is NOT zero %f\n"; + let output3 = c"The second element is %f\n"; + unsafe { + let val: *const c_char = if (*array_c)[0] < 0.1 { + output.as_ptr() + } else { + output2.as_ptr() + }; + libc::printf(val, (*array_c)[0]); + } + + unsafe { + kernel_1(array_c); + } + core::hint::black_box(&array_c); + unsafe { + let val: *const c_char = if (*array_c)[0] < 0.1 { + output.as_ptr() + } else { + output2.as_ptr() + }; + libc::printf(val, (*array_c)[0]); + libc::printf(output3.as_ptr(), (*array_c)[1]); + } +} + +#[cfg(target_os = "linux")] +unsafe extern "C" { + pub fn kernel_1(array_b: *mut [f64; 256]); +} +``` + +## Compile instructions +It is important to use a clang compiler build on the same llvm as rustc. Just calling clang without the full path will likely use your system clang, which probably will be incompatible. So either substitute clang/lld invocations below with absolute path, or set your `PATH` accordingly. + +First we generate the host (cpu) code. The first build is just to compile libc, take note of the hashed path. Then we call rustc directly to build our host code, while providing the libc artifact to rustc. +``` +cargo +offload build -r -v +rustc +offload --edition 2024 src/lib.rs -g --crate-type cdylib -C opt-level=3 -C panic=abort -C lto=fat -L dependency=/absolute_path_to/target/release/deps --extern libc=/absolute_path_to/target/release/deps/liblibc-<HASH>.rlib --emit=llvm-bc,llvm-ir -Zoffload=Enable -Zunstable-options +``` + +Now we generate the device code. Replace the target-cpu with the right code for your gpu. +``` +RUSTFLAGS="-Ctarget-cpu=gfx90a --emit=llvm-bc,llvm-ir" cargo +offload build -Zunstable-options -r -v --target amdgcn-amd-amdhsa -Zbuild-std=core +``` + +Now find the <libname>.ll under target/amdgcn-amd-amdhsa folder and copy it to a device.ll file (or adjust the file names below). +If you work on an NVIDIA or Intel gpu, please adjust the names acordingly and open an issue to share your results (either if you succeed or fail). +First we compile our .ll files (good for manual inspections) to .bc files and clean up leftover artifacts. The cleanup is important, otherwise caching might interfere on following runs. +``` +opt lib.ll -o lib.bc +opt device.ll -o device.bc +rm *.o +rm bare.amdgcn.gfx90a.img* +``` + +``` +clang-offload-packager" "-o" "host.out" "--image=file=device.bc,triple=amdgcn-amd-amdhsa,arch=gfx90a,kind=openmp" + +clang-21" "-cc1" "-triple" "x86_64-unknown-linux-gnu" "-S" "-save-temps=cwd" "-disable-free" "-clear-ast-before-backend" "-main-file-name" "lib.rs" "-mrelocation-model" "pic" "-pic-level" "2" "-pic-is-pie" "-mframe-pointer=all" "-fmath-errno" "-ffp-contract=on" "-fno-rounding-math" "-mconstructor-aliases" "-funwind-tables=2" "-target-cpu" "x86-64" "-tune-cpu" "generic" "-resource-dir" "/<ABSOLUTE_PATH_TO>/rust/build/x86_64-unknown-linux-gnu/llvm/lib/clang/21" "-ferror-limit" "19" "-fopenmp" "-fopenmp-offload-mandatory" "-fgnuc-version=4.2.1" "-fskip-odr-check-in-gmf" "-fembed-offload-object=host.out" "-fopenmp-targets=amdgcn-amd-amdhsa" "-faddrsig" "-D__GCC_HAVE_DWARF2_CFI_ASM=1" "-o" "host.s" "-x" "ir" "lib.bc" + +clang-21" "-cc1as" "-triple" "x86_64-unknown-linux-gnu" "-filetype" "obj" "-main-file-name" "lib.rs" "-target-cpu" "x86-64" "-mrelocation-model" "pic" "-o" "host.o" "host.s" + +clang-linker-wrapper" "--should-extract=gfx90a" "--device-compiler=amdgcn-amd-amdhsa=-g" "--device-compiler=amdgcn-amd-amdhsa=-save-temps=cwd" "--device-linker=amdgcn-amd-amdhsa=-lompdevice" "--host-triple=x86_64-unknown-linux-gnu" "--save-temps" "--linker-path=/ABSOlUTE_PATH_TO/rust/build/x86_64-unknown-linux-gnu/lld/bin/ld.lld" "--hash-style=gnu" "--eh-frame-hdr" "-m" "elf_x86_64" "-pie" "-dynamic-linker" "/lib64/ld-linux-x86-64.so.2" "-o" "bare" "/lib/../lib64/Scrt1.o" "/lib/../lib64/crti.o" "/ABSOLUTE_PATH_TO/crtbeginS.o" "-L/ABSOLUTE_PATH_TO/rust/build/x86_64-unknown-linux-gnu/llvm/bin/../lib/x86_64-unknown-linux-gnu" "-L/ABSOLUTE_PATH_TO/rust/build/x86_64-unknown-linux-gnu/llvm/lib/clang/21/lib/x86_64-unknown-linux-gnu" "-L/lib/../lib64" "-L/usr/lib64" "-L/lib" "-L/usr/lib" "host.o" "-lstdc++" "-lm" "-lomp" "-lomptarget" "-L/ABSOLUTE_PATH_TO/rust/build/x86_64-unknown-linux-gnu/llvm/lib" "-lgcc_s" "-lgcc" "-lpthread" "-lc" "-lgcc_s" "-lgcc" "/ABSOLUTE_PATH_TO/crtendS.o" "/lib/../lib64/crtn.o" +``` + +Especially for the last command I recommend to not fix the paths, but rather just re-generate them by copying a bare-mode openmp example and compiling it with your clang. By adding `-###` to your clang invocation, you can see the invidual steps. +``` +myclang++ -fuse-ld=lld -O3 -fopenmp -fopenmp-offload-mandatory --offload-arch=gfx90a omp_bare.cpp -o main -### +``` + +In the final step, you can now run your binary + +``` +./main +The first element is zero 0.000000 +The first element is NOT zero 21.000000 +The second element is 0.000000 +``` + +To receive more information about the memory transfer, you can enable info printing with +``` +LIBOMPTARGET_INFO=-1 ./main +``` diff --git a/src/doc/rustc-dev-guide/src/queries/example-0.png b/src/doc/rustc-dev-guide/src/queries/example-0.png index 14b46c44f7d..dd67d5f2ef1 100644 --- a/src/doc/rustc-dev-guide/src/queries/example-0.png +++ b/src/doc/rustc-dev-guide/src/queries/example-0.png Binary files differdiff --git a/src/doc/rustc-dev-guide/src/tests/ci.md b/src/doc/rustc-dev-guide/src/tests/ci.md index 750e4fa1a0f..a8cc959124f 100644 --- a/src/doc/rustc-dev-guide/src/tests/ci.md +++ b/src/doc/rustc-dev-guide/src/tests/ci.md @@ -84,16 +84,15 @@ resources to run the full test suite for each commit on every PR. > Thus, it is a good idea to run `./x doc xxx` locally for any doc comment > changes to help catch these early. -PR jobs are defined in the `pr` section of [`jobs.yml`]. They run under the -`rust-lang/rust` repository, and their results can be observed directly on the -PR, in the "CI checks" section at the bottom of the PR page. +PR jobs are defined in the `pr` section of [`jobs.yml`]. Their results can be observed +directly on the PR, in the "CI checks" section at the bottom of the PR page. ### Auto builds Before a commit can be merged into the `master` branch, it needs to pass our complete test suite. We call this an `auto` build. This build runs tens of CI jobs that exercise various tests across operating systems and targets. The full -test suite is quite slow; it can take two hours or more until all the `auto` CI +test suite is quite slow; it can take several hours until all the `auto` CI jobs finish. Most platforms only run the build steps, some run a restricted set of tests, @@ -136,14 +135,21 @@ By default, if you send a comment with `@bors try`, the jobs defined in the `try [`jobs.yml`] will be executed. We call this mode a "fast try build". Such a try build will not execute any tests, and it will allow compilation warnings. It is useful when you want to get an optimized toolchain as fast as possible, for a crater run or performance benchmarks, -even if it might not be working fully correctly. - -If you want to run a custom CI job in a try build and make sure that it passes all tests and does -not produce any compilation warnings, you can select CI jobs to be executed by adding lines -containing `try-job: <job pattern>` to the PR description. All such specified jobs will be executed -in the try build once the `@bors try` command is used on the PR. - -Each pattern can either be an exact name of a job or a glob pattern that matches multiple jobs, +even if it might not be working fully correctly. If you want to do a full build for the default try job, +specify its job name in a job pattern (explained below). + +If you want to run custom CI job(s) in a try build and make sure that they pass all tests and do +not produce any compilation warnings, you can select CI jobs to be executed by specifying a *job pattern*, +which can be used in one of two ways: +- You can add a set of `try-job: <job pattern>` directives to the PR description (described below) and then + simply run `@bors try`. CI will read these directives and run the jobs that you have specified. This is + useful if you want to rerun the same set of try jobs multiple times, after incrementally modifying a PR. +- You can specify the job pattern using the `jobs` parameter of the try command: `@bors try jobs=<job pattern>`. + This is useful for one-off try builds with specific jobs. Note that the `jobs` parameter has a higher priority + than the PR description directives. + - There can also be multiple patterns specified, e.g. `@bors try jobs=job1,job2,job3`. + +Each job pattern can either be an exact name of a job or a glob pattern that matches multiple jobs, for example `*msvc*` or `*-alt`. You can start at most 20 jobs in a single try build. When using glob patterns, you might want to wrap them in backticks (`` ` ``) to avoid GitHub rendering the pattern as Markdown. @@ -182,26 +188,19 @@ of [`jobs.yml`]: > However, it can be less flexible because you cannot adjust the set of tests > that are exercised this way. -Try jobs are defined in the `try` section of [`jobs.yml`]. They are executed on -the `try` branch under the `rust-lang/rust` repository and +Try builds are executed on the `try` branch under the `rust-lang/rust` repository and their results can be seen [here](https://github.com/rust-lang/rust/actions), although usually you will be notified of the result by a comment made by bors on the corresponding PR. -Note that if you start the default try job using `@bors try`, it will skip building several `dist` components and running post-optimization tests, to make the build duration shorter. If you want to execute the full build as it would happen before a merge, add an explicit `try-job` pattern with the name of the default try job (currently `dist-x86_64-linux`). +Multiple try builds can execute concurrently across different PRs, but there can be at most +a single try build running on a single PR at any given time. -Multiple try builds can execute concurrently across different PRs. - -<div class="warning"> - -Bors identifies try jobs by commit hash. This means that if you have two PRs -containing the same (latest) commits, running `@bors try` will result in the -*same* try job and it really confuses `bors`. Please refrain from doing so. - -</div> +Note that try builds are handled using the new [bors][new-bors] implementation. [rustc-perf]: https://github.com/rust-lang/rustc-perf [crater]: https://github.com/rust-lang/crater +[new-bors]: https://github.com/rust-lang/bors ### Modifying CI jobs diff --git a/src/doc/rustc-dev-guide/src/tests/ecosystem-test-jobs/fuchsia.md b/src/doc/rustc-dev-guide/src/tests/ecosystem-test-jobs/fuchsia.md index b19d94d6ff7..75cf782a770 100644 --- a/src/doc/rustc-dev-guide/src/tests/ecosystem-test-jobs/fuchsia.md +++ b/src/doc/rustc-dev-guide/src/tests/ecosystem-test-jobs/fuchsia.md @@ -18,12 +18,8 @@ Fuchsia builds as part of the suite of bors tests that run before a pull request is merged. If you are worried that a pull request might break the Fuchsia builder and want -to test it out before submitting it to the bors queue, simply add this line to -your PR description: - -> try-job: x86_64-fuchsia - -Then when you `@bors try` it will pick the job that builds Fuchsia. +to test it out before submitting it to the bors queue, simply ask bors to run +the try job that builds the Fuchsia integration: `@bors try jobs=x86_64-fuchsia`. ## Building Fuchsia locally diff --git a/src/doc/rustc-dev-guide/src/tests/ecosystem-test-jobs/rust-for-linux.md b/src/doc/rustc-dev-guide/src/tests/ecosystem-test-jobs/rust-for-linux.md index d549ec6fca5..a6a7374b811 100644 --- a/src/doc/rustc-dev-guide/src/tests/ecosystem-test-jobs/rust-for-linux.md +++ b/src/doc/rustc-dev-guide/src/tests/ecosystem-test-jobs/rust-for-linux.md @@ -40,12 +40,8 @@ this sysroot. RfL uses several unstable compiler/language features, therefore this workflow notifies us if a given compiler change would break it. If you are worried that a pull request might break the Rust for Linux builder -and want to test it out before submitting it to the bors queue, simply add this -line to your PR description: - -> try-job: x86_64-rust-for-linux - -Then when you `@bors try` it will pick the job that builds the Rust for Linux -integration. +and want to test it out before submitting it to the bors queue, simply ask +bors to run the try job that builds the Rust for Linux integration: +`@bors try jobs=x86_64-rust-for-linux`. [rfl-ping]: ../../notification-groups/rust-for-linux.md diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index b53494ed98d..e0d637a2a67 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -49,6 +49,7 @@ - [aarch64-nintendo-switch-freestanding](platform-support/aarch64-nintendo-switch-freestanding.md) - [aarch64-unknown-linux-musl](platform-support/aarch64-unknown-linux-musl.md) - [aarch64_be-unknown-none-softfloat](platform-support/aarch64_be-unknown-none-softfloat.md) + - [aarch64_be-unknown-linux-musl](platform-support/aarch64_be-unknown-linux-musl.md) - [amdgcn-amd-amdhsa](platform-support/amdgcn-amd-amdhsa.md) - [armeb-unknown-linux-gnueabi](platform-support/armeb-unknown-linux-gnueabi.md) - [arm-none-eabi](platform-support/arm-none-eabi.md) @@ -106,6 +107,7 @@ - [riscv32imac-unknown-xous-elf](platform-support/riscv32imac-unknown-xous-elf.md) - [riscv64gc-unknown-linux-gnu](platform-support/riscv64gc-unknown-linux-gnu.md) - [riscv64gc-unknown-linux-musl](platform-support/riscv64gc-unknown-linux-musl.md) + - [riscv64a23-unknown-linux-gnu](platform-support/riscv64a23-unknown-linux-gnu.md) - [s390x-unknown-linux-gnu](platform-support/s390x-unknown-linux-gnu.md) - [s390x-unknown-linux-musl](platform-support/s390x-unknown-linux-musl.md) - [sparc-unknown-none-elf](./platform-support/sparc-unknown-none-elf.md) diff --git a/src/doc/rustc/src/command-line-arguments/print-options.md b/src/doc/rustc/src/command-line-arguments/print-options.md index 1f33e91e5d1..fed19d6b667 100644 --- a/src/doc/rustc/src/command-line-arguments/print-options.md +++ b/src/doc/rustc/src/command-line-arguments/print-options.md @@ -32,7 +32,7 @@ The names of the files created by the `link` emit kind. ## `sysroot` -Abosulte path to the sysroot. +Absolute path to the sysroot. Example (with rustup and the stable toolchain): diff --git a/src/doc/rustc/src/images/image1.png b/src/doc/rustc/src/images/image1.png index 0da45e56620..3aad6359389 100644 --- a/src/doc/rustc/src/images/image1.png +++ b/src/doc/rustc/src/images/image1.png Binary files differdiff --git a/src/doc/rustc/src/images/image2.png b/src/doc/rustc/src/images/image2.png index a9cf23f8737..085b1c490b8 100644 --- a/src/doc/rustc/src/images/image2.png +++ b/src/doc/rustc/src/images/image2.png Binary files differdiff --git a/src/doc/rustc/src/images/image3.png b/src/doc/rustc/src/images/image3.png index 844a2fe6747..ee332f51055 100644 --- a/src/doc/rustc/src/images/image3.png +++ b/src/doc/rustc/src/images/image3.png Binary files differdiff --git a/src/doc/rustc/src/images/llvm-cov-show-01.png b/src/doc/rustc/src/images/llvm-cov-show-01.png index 35f04594347..ce4dec128b6 100644 --- a/src/doc/rustc/src/images/llvm-cov-show-01.png +++ b/src/doc/rustc/src/images/llvm-cov-show-01.png Binary files differdiff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 3bf87994297..2f815bcd141 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -273,6 +273,7 @@ target | std | host | notes [`aarch64_be-unknown-hermit`](platform-support/hermit.md) | ✓ | | ARM64 Hermit (big-endian) `aarch64_be-unknown-linux-gnu` | ✓ | ✓ | ARM64 Linux (big-endian) `aarch64_be-unknown-linux-gnu_ilp32` | ✓ | ✓ | ARM64 Linux (big-endian, ILP32 ABI) +[`aarch64_be-unknown-linux-musl`](platform-support/aarch64_be-unknown-linux-musl.md) | ✓ | ✓ | ARM64 Linux (big-endian) with musl-libc 1.2.5 [`aarch64_be-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | ARM64 NetBSD (big-endian) [`aarch64_be-unknown-none-softfloat`](platform-support/aarch64_be-unknown-none-softfloat.md) | * | | Bare big-endian ARM64, softfloat [`amdgcn-amd-amdhsa`](platform-support/amdgcn-amd-amdhsa.md) | * | | `-Ctarget-cpu=gfx...` to specify [the AMD GPU] to compile for @@ -327,8 +328,8 @@ target | std | host | notes [`i686-win7-windows-msvc`](platform-support/win7-windows-msvc.md) | ✓ | | 32-bit Windows 7 support [^x86_32-floats-return-ABI] [^win32-msvc-alignment] [`i686-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | [^x86_32-floats-return-ABI] [`loongarch64-unknown-linux-ohos`](platform-support/openharmony.md) | ✓ | | LoongArch64 OpenHarmony -[`loongarch32-unknown-none`](platform-support/loongarch-none.md) | * | LoongArch32 Bare-metal (ILP32D ABI) -[`loongarch32-unknown-none-softfloat`](platform-support/loongarch-none.md) | * | LoongArch32 Bare-metal (ILP32S ABI) +[`loongarch32-unknown-none`](platform-support/loongarch-none.md) | * | | LoongArch32 Bare-metal (ILP32D ABI) +[`loongarch32-unknown-none-softfloat`](platform-support/loongarch-none.md) | * | | LoongArch32 Bare-metal (ILP32S ABI) [`m68k-unknown-linux-gnu`](platform-support/m68k-unknown-linux-gnu.md) | ? | | Motorola 680x0 Linux [`m68k-unknown-none-elf`](platform-support/m68k-unknown-none-elf.md) | | | Motorola 680x0 `mips-unknown-linux-gnu` | ✓ | ✓ | MIPS Linux (kernel 4.4, glibc 2.23) @@ -391,6 +392,7 @@ target | std | host | notes [`riscv64gc-unknown-nuttx-elf`](platform-support/nuttx.md) | ✓ | | RISC-V 64bit with NuttX [`riscv64gc-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/riscv64 [`riscv64imac-unknown-nuttx-elf`](platform-support/nuttx.md) | ✓ | | RISC-V 64bit with NuttX +[`riscv64a23-unknown-linux-gnu`](platform-support/riscv64a23-unknown-linux-gnu.md) | ✓ | ✓ | RISC-V Linux (kernel 6.8.0+, glibc 2.39) [`s390x-unknown-linux-musl`](platform-support/s390x-unknown-linux-musl.md) | ✓ | | S390x Linux (kernel 3.2, musl 1.2.3) `sparc-unknown-linux-gnu` | ✓ | | 32-bit SPARC Linux [`sparc-unknown-none-elf`](./platform-support/sparc-unknown-none-elf.md) | * | | Bare 32-bit SPARC V7+ diff --git a/src/doc/rustc/src/platform-support/aarch64_be-unknown-linux-musl.md b/src/doc/rustc/src/platform-support/aarch64_be-unknown-linux-musl.md new file mode 100644 index 00000000000..3e816dc8bfb --- /dev/null +++ b/src/doc/rustc/src/platform-support/aarch64_be-unknown-linux-musl.md @@ -0,0 +1,49 @@ +# aarch64_be-unknown-linux-musl + +**Tier: 3** + +ARM64 Linux (big-endian) with musl-libc. + +## Target maintainers + +[@neuschaefer](https://github.com/neuschaefer) +[@Gelbpunkt](https://github.com/Gelbpunkt) + +## Requirements + +The target requires a `aarch64_be-*-linux-musl` toolchain, which likely has to +be built from source because this is a rare combination. [Buildroot] provides +a way of doing so: + +- select _Target options_ → _Target Architecture_ → _AArch64 (big endian)_ +- select _Toolchain_ → _C library_ → _musl_ +- select _Toolchain_ → _Enable C++ support_ + +Host tools are supported. + +[Buildroot]: https://buildroot.org/ + + +## Building the target + +The target can be enabled in bootstrap.toml: + +```toml +[build] +target = ["aarch64_be-unknown-linux-musl"] + +[target.aarch64_be-unknown-linux-musl] +cc = "/path/to/buildroot/host/bin/aarch64_be-buildroot-linux-musl-cc" +cxx = "/path/to/buildroot/host/bin/aarch64_be-buildroot-linux-musl-c++" +linker = "/path/to/buildroot/host/bin/aarch64_be-buildroot-linux-musl-cc" +ar = "/path/to/buildroot/host/bin/aarch64_be-buildroot-linux-musl-ar" +ranlib = "/path/to/buildroot/host/bin/aarch64_be-buildroot-linux-musl-ranlib" +musl-root = "/path/to/buildroot/staging" +runner = "qemu-aarch64_be -L /path/to/buildroot/target" +crt-static = "/path/to/buildroot/target" +``` + + +## Testing + +Binaries can be run under `qemu-aarch64_be` or under a big-endian Linux kernel. diff --git a/src/doc/rustc/src/platform-support/riscv64a23-unknown-linux-gnu.md b/src/doc/rustc/src/platform-support/riscv64a23-unknown-linux-gnu.md new file mode 100644 index 00000000000..2cbaaa86654 --- /dev/null +++ b/src/doc/rustc/src/platform-support/riscv64a23-unknown-linux-gnu.md @@ -0,0 +1,41 @@ +# `riscv64a23-unknown-linux-gnu` + +**Tier: 3** + +RISC-V target using the ratified [RVA23 Profile](https://github.com/riscv/riscv-profiles/blob/main/src/rva23-profile.adoc). +This target will enable all mandary features of rva23u64 by default. + +## Target maintainers + +[@ZhongyaoChen](https://github.com/ZhongyaoChen) +[@CaiWeiran](https://github.com/CaiWeiran) + +## Requirements + +This target can be sucessfully build on the following platform: ubuntu 24.04 (Linux Kernel version 6.8.0, glibc 2.39). + +Other platforms may work, but are not tested. Please contanct if you encounter any issues. + +## Building the target + +Tier-3 target is not distributed through `rustup`. + +You need to build your own Rust, the target can be build with: + +```bash +./x build --target riscv64a23-unknown-linux-gnu +``` + +## Building Rust programs + +Add the toolchain: + +```bash +rustup toolchain link rva23-toolchain {path-to-rust}/build/host/stage2 +``` + +Then cross compile crates with: + +```bash +RUSTFLAGS="-C linker=riscv64-linux-gnu-gcc" cargo +rva23-toolchain build --target=riscv64a23-unknown-linux-gnu +``` diff --git a/src/doc/rustdoc/src/images/collapsed-long-item.png b/src/doc/rustdoc/src/images/collapsed-long-item.png index c382870c64a..6de759fbeb9 100644 --- a/src/doc/rustdoc/src/images/collapsed-long-item.png +++ b/src/doc/rustdoc/src/images/collapsed-long-item.png Binary files differdiff --git a/src/doc/rustdoc/src/images/collapsed-trait-impls.png b/src/doc/rustdoc/src/images/collapsed-trait-impls.png index f685656e09a..96cc7db6798 100644 --- a/src/doc/rustdoc/src/images/collapsed-trait-impls.png +++ b/src/doc/rustdoc/src/images/collapsed-trait-impls.png Binary files differdiff --git a/src/etc/installer/gfx/rust-logo.png b/src/etc/installer/gfx/rust-logo.png index 99ee7507fa2..49d8d0d9485 100644 --- a/src/etc/installer/gfx/rust-logo.png +++ b/src/etc/installer/gfx/rust-logo.png Binary files differdiff --git a/src/gcc b/src/gcc -Subproject 04ce66d8c918de9273bd7101638ad8724edf5e2 +Subproject 4e995bd73c4490edfe5080ec6014d63aa9abed5 diff --git a/src/librustdoc/html/static/images/favicon-32x32.png b/src/librustdoc/html/static/images/favicon-32x32.png index 69b8613ce15..0670c4dabb0 100644 --- a/src/librustdoc/html/static/images/favicon-32x32.png +++ b/src/librustdoc/html/static/images/favicon-32x32.png Binary files differdiff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index d891d1fba25..e2682045ab4 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -11,6 +11,7 @@ #![feature(file_buffered)] #![feature(format_args_nl)] #![feature(if_let_guard)] +#![feature(iter_advance_by)] #![feature(iter_intersperse)] #![feature(round_char_boundary)] #![feature(rustc_private)] diff --git a/src/librustdoc/passes/lint/html_tags.rs b/src/librustdoc/passes/lint/html_tags.rs index 19cf15d40a3..da09117b1bb 100644 --- a/src/librustdoc/passes/lint/html_tags.rs +++ b/src/librustdoc/passes/lint/html_tags.rs @@ -1,9 +1,11 @@ //! Detects invalid HTML (like an unclosed `<span>`) in doc comments. +use std::borrow::Cow; use std::iter::Peekable; use std::ops::Range; use std::str::CharIndices; +use itertools::Itertools as _; use pulldown_cmark::{BrokenLink, Event, LinkType, Parser, Tag, TagEnd}; use rustc_hir::HirId; use rustc_resolve::rustdoc::source_span_for_markdown_range; @@ -101,7 +103,7 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: & }); }; - let mut tags = Vec::new(); + let mut tagp = TagParser::new(); let mut is_in_comment = None; let mut in_code_block = false; @@ -126,70 +128,65 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: & }; let p = Parser::new_with_broken_link_callback(dox, main_body_opts(), Some(&mut replacer)) - .into_offset_iter(); + .into_offset_iter() + .coalesce(|a, b| { + // for some reason, pulldown-cmark splits html blocks into separate events for each line. + // we undo this, in order to handle multi-line tags. + match (a, b) { + ((Event::Html(_), ra), (Event::Html(_), rb)) if ra.end == rb.start => { + let merged = ra.start..rb.end; + Ok((Event::Html(Cow::Borrowed(&dox[merged.clone()]).into()), merged)) + } + x => Err(x), + } + }); for (event, range) in p { match event { Event::Start(Tag::CodeBlock(_)) => in_code_block = true, Event::Html(text) | Event::InlineHtml(text) if !in_code_block => { - extract_tags(&mut tags, &text, range, &mut is_in_comment, &report_diag) + tagp.extract_tags(&text, range, &mut is_in_comment, &report_diag) } Event::End(TagEnd::CodeBlock) => in_code_block = false, _ => {} } } - for (tag, range) in tags.iter().filter(|(t, _)| { - let t = t.to_lowercase(); - !ALLOWED_UNCLOSED.contains(&t.as_str()) - }) { - report_diag(format!("unclosed HTML tag `{tag}`"), range, true); - } - if let Some(range) = is_in_comment { report_diag("Unclosed HTML comment".to_string(), &range, false); + } else if let &Some(quote_pos) = &tagp.quote_pos { + let qr = Range { start: quote_pos, end: quote_pos }; + report_diag( + format!("unclosed quoted HTML attribute on tag `{}`", &tagp.tag_name), + &qr, + false, + ); + } else { + if !tagp.tag_name.is_empty() { + report_diag( + format!("incomplete HTML tag `{}`", &tagp.tag_name), + &(tagp.tag_start_pos..dox.len()), + false, + ); + } + for (tag, range) in tagp.tags.iter().filter(|(t, _)| { + let t = t.to_lowercase(); + !is_implicitly_self_closing(&t) + }) { + report_diag(format!("unclosed HTML tag `{tag}`"), range, true); + } } } +/// These tags are interpreted as self-closing if they lack an explicit closing tag. const ALLOWED_UNCLOSED: &[&str] = &[ "area", "base", "br", "col", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", "source", "track", "wbr", ]; -fn drop_tag( - tags: &mut Vec<(String, Range<usize>)>, - tag_name: String, - range: Range<usize>, - f: &impl Fn(String, &Range<usize>, bool), -) { - let tag_name_low = tag_name.to_lowercase(); - if let Some(pos) = tags.iter().rposition(|(t, _)| t.to_lowercase() == tag_name_low) { - // If the tag is nested inside a "<script>" or a "<style>" tag, no warning should - // be emitted. - let should_not_warn = tags.iter().take(pos + 1).any(|(at, _)| { - let at = at.to_lowercase(); - at == "script" || at == "style" - }); - for (last_tag_name, last_tag_span) in tags.drain(pos + 1..) { - if should_not_warn { - continue; - } - let last_tag_name_low = last_tag_name.to_lowercase(); - if ALLOWED_UNCLOSED.contains(&last_tag_name_low.as_str()) { - continue; - } - // `tags` is used as a queue, meaning that everything after `pos` is included inside it. - // So `<h2><h3></h2>` will look like `["h2", "h3"]`. So when closing `h2`, we will still - // have `h3`, meaning the tag wasn't closed as it should have. - f(format!("unclosed HTML tag `{last_tag_name}`"), &last_tag_span, true); - } - // Remove the `tag_name` that was originally closed - tags.pop(); - } else { - // It can happen for example in this case: `<h2></script></h2>` (the `h2` tag isn't required - // but it helps for the visualization). - f(format!("unopened HTML tag `{tag_name}`"), &range, false); - } +/// Allows constructs like `<img>`, but not `<img`. +fn is_implicitly_self_closing(tag_name: &str) -> bool { + ALLOWED_UNCLOSED.contains(&tag_name) } fn extract_path_backwards(text: &str, end_pos: usize) -> Option<usize> { @@ -252,151 +249,292 @@ fn is_valid_for_html_tag_name(c: char, is_empty: bool) -> bool { c.is_ascii_alphabetic() || !is_empty && (c == '-' || c.is_ascii_digit()) } -fn extract_html_tag( - tags: &mut Vec<(String, Range<usize>)>, - text: &str, - range: &Range<usize>, - start_pos: usize, - iter: &mut Peekable<CharIndices<'_>>, - f: &impl Fn(String, &Range<usize>, bool), -) { - let mut tag_name = String::new(); - let mut is_closing = false; - let mut prev_pos = start_pos; +/// Parse html tags to ensure they are well-formed +#[derive(Debug, Clone)] +struct TagParser { + tags: Vec<(String, Range<usize>)>, + /// Name of the tag that is being parsed, if we are within a tag. + /// + /// Since the `<` and name of a tag must appear on the same line with no whitespace, + /// if this is the empty string, we are not in a tag. + tag_name: String, + tag_start_pos: usize, + is_closing: bool, + /// `true` if we are within a tag, but not within its name. + in_attrs: bool, + /// If we are in a quoted attribute, what quote char does it use? + /// + /// This needs to be stored in the struct since HTML5 allows newlines in quoted attrs. + quote: Option<char>, + quote_pos: Option<usize>, + after_eq: bool, +} - loop { - let (pos, c) = match iter.peek() { - Some((pos, c)) => (*pos, *c), - // In case we reached the of the doc comment, we want to check that it's an - // unclosed HTML tag. For example "/// <h3". - None => (prev_pos, '\0'), - }; - prev_pos = pos; - // Checking if this is a closing tag (like `</a>` for `<a>`). - if c == '/' && tag_name.is_empty() { - is_closing = true; - } else if is_valid_for_html_tag_name(c, tag_name.is_empty()) { - tag_name.push(c); - } else { - if !tag_name.is_empty() { - let mut r = Range { start: range.start + start_pos, end: range.start + pos }; - if c == '>' { - // In case we have a tag without attribute, we can consider the span to - // refer to it fully. - r.end += 1; +impl TagParser { + fn new() -> Self { + Self { + tags: Vec::new(), + tag_name: String::with_capacity(8), + tag_start_pos: 0, + is_closing: false, + in_attrs: false, + quote: None, + quote_pos: None, + after_eq: false, + } + } + + fn drop_tag(&mut self, range: Range<usize>, f: &impl Fn(String, &Range<usize>, bool)) { + let tag_name_low = self.tag_name.to_lowercase(); + if let Some(pos) = self.tags.iter().rposition(|(t, _)| t.to_lowercase() == tag_name_low) { + // If the tag is nested inside a "<script>" or a "<style>" tag, no warning should + // be emitted. + let should_not_warn = self.tags.iter().take(pos + 1).any(|(at, _)| { + let at = at.to_lowercase(); + at == "script" || at == "style" + }); + for (last_tag_name, last_tag_span) in self.tags.drain(pos + 1..) { + if should_not_warn { + continue; } - if is_closing { - // In case we have "</div >" or even "</div >". - if c != '>' { - if !c.is_whitespace() { - // It seems like it's not a valid HTML tag. - break; - } - let mut found = false; - for (new_pos, c) in text[pos..].char_indices() { + let last_tag_name_low = last_tag_name.to_lowercase(); + if is_implicitly_self_closing(&last_tag_name_low) { + continue; + } + // `tags` is used as a queue, meaning that everything after `pos` is included inside it. + // So `<h2><h3></h2>` will look like `["h2", "h3"]`. So when closing `h2`, we will still + // have `h3`, meaning the tag wasn't closed as it should have. + f(format!("unclosed HTML tag `{last_tag_name}`"), &last_tag_span, true); + } + // Remove the `tag_name` that was originally closed + self.tags.pop(); + } else { + // It can happen for example in this case: `<h2></script></h2>` (the `h2` tag isn't required + // but it helps for the visualization). + f(format!("unopened HTML tag `{}`", &self.tag_name), &range, false); + } + } + + /// Handle a `<` that appeared while parsing a tag. + fn handle_lt_in_tag( + &mut self, + range: Range<usize>, + lt_pos: usize, + f: &impl Fn(String, &Range<usize>, bool), + ) { + let global_pos = range.start + lt_pos; + // is this check needed? + if global_pos == self.tag_start_pos { + // `<` is in the tag because it is the start. + return; + } + // tried to start a new tag while in a tag + f( + format!("incomplete HTML tag `{}`", &self.tag_name), + &(self.tag_start_pos..global_pos), + false, + ); + self.tag_parsed(); + } + + fn extract_html_tag( + &mut self, + text: &str, + range: &Range<usize>, + start_pos: usize, + iter: &mut Peekable<CharIndices<'_>>, + f: &impl Fn(String, &Range<usize>, bool), + ) { + let mut prev_pos = start_pos; + + 'outer_loop: loop { + let (pos, c) = match iter.peek() { + Some((pos, c)) => (*pos, *c), + // In case we reached the of the doc comment, we want to check that it's an + // unclosed HTML tag. For example "/// <h3". + None if self.tag_name.is_empty() => (prev_pos, '\0'), + None => break, + }; + prev_pos = pos; + if c == '/' && self.tag_name.is_empty() { + // Checking if this is a closing tag (like `</a>` for `<a>`). + self.is_closing = true; + } else if !self.in_attrs && is_valid_for_html_tag_name(c, self.tag_name.is_empty()) { + self.tag_name.push(c); + } else { + if !self.tag_name.is_empty() { + self.in_attrs = true; + let mut r = Range { start: range.start + start_pos, end: range.start + pos }; + if c == '>' { + // In case we have a tag without attribute, we can consider the span to + // refer to it fully. + r.end += 1; + } + if self.is_closing { + // In case we have "</div >" or even "</div >". + if c != '>' { if !c.is_whitespace() { - if c == '>' { - r.end = range.start + new_pos + 1; - found = true; - } + // It seems like it's not a valid HTML tag. break; } - } - if !found { - break; - } - } - drop_tag(tags, tag_name, r, f); - } else { - let mut is_self_closing = false; - let mut quote_pos = None; - if c != '>' { - let mut quote = None; - let mut after_eq = false; - for (i, c) in text[pos..].char_indices() { - if !c.is_whitespace() { - if let Some(q) = quote { - if c == q { - quote = None; - quote_pos = None; - after_eq = false; + let mut found = false; + for (new_pos, c) in text[pos..].char_indices() { + if !c.is_whitespace() { + if c == '>' { + r.end = range.start + new_pos + 1; + found = true; + } else if c == '<' { + self.handle_lt_in_tag(range.clone(), pos + new_pos, f); } - } else if c == '>' { break; - } else if c == '/' && !after_eq { - is_self_closing = true; - } else { - if is_self_closing { - is_self_closing = false; - } - if (c == '"' || c == '\'') && after_eq { - quote = Some(c); - quote_pos = Some(pos + i); - } else if c == '=' { - after_eq = true; - } } - } else if quote.is_none() { - after_eq = false; + } + if !found { + break 'outer_loop; } } - } - if let Some(quote_pos) = quote_pos { - let qr = Range { start: quote_pos, end: quote_pos }; - f( - format!("unclosed quoted HTML attribute on tag `{tag_name}`"), - &qr, - false, - ); - } - if is_self_closing { - // https://html.spec.whatwg.org/#parse-error-non-void-html-element-start-tag-with-trailing-solidus - let valid = ALLOWED_UNCLOSED.contains(&&tag_name[..]) - || tags.iter().take(pos + 1).any(|(at, _)| { - let at = at.to_lowercase(); - at == "svg" || at == "math" - }); - if !valid { - f(format!("invalid self-closing HTML tag `{tag_name}`"), &r, false); - } + self.drop_tag(r, f); + self.tag_parsed(); } else { - tags.push((tag_name, r)); + self.extract_opening_tag(text, range, r, pos, c, iter, f) } } + break; } - break; + iter.next(); } - iter.next(); } -} - -fn extract_tags( - tags: &mut Vec<(String, Range<usize>)>, - text: &str, - range: Range<usize>, - is_in_comment: &mut Option<Range<usize>>, - f: &impl Fn(String, &Range<usize>, bool), -) { - let mut iter = text.char_indices().peekable(); - while let Some((start_pos, c)) = iter.next() { - if is_in_comment.is_some() { - if text[start_pos..].starts_with("-->") { - *is_in_comment = None; + fn extract_opening_tag( + &mut self, + text: &str, + range: &Range<usize>, + r: Range<usize>, + pos: usize, + c: char, + iter: &mut Peekable<CharIndices<'_>>, + f: &impl Fn(String, &Range<usize>, bool), + ) { + // we can store this as a local, since html5 does require the `/` and `>` + // to not be separated by whitespace. + let mut is_self_closing = false; + if c != '>' { + 'parse_til_gt: { + for (i, c) in text[pos..].char_indices() { + if !c.is_whitespace() { + debug_assert_eq!(self.quote_pos.is_some(), self.quote.is_some()); + if let Some(q) = self.quote { + if c == q { + self.quote = None; + self.quote_pos = None; + self.after_eq = false; + } + } else if c == '>' { + break 'parse_til_gt; + } else if c == '<' { + self.handle_lt_in_tag(range.clone(), pos + i, f); + } else if c == '/' && !self.after_eq { + is_self_closing = true; + } else { + if is_self_closing { + is_self_closing = false; + } + if (c == '"' || c == '\'') && self.after_eq { + self.quote = Some(c); + self.quote_pos = Some(pos + i); + } else if c == '=' { + self.after_eq = true; + } + } + } else if self.quote.is_none() { + self.after_eq = false; + } + if !is_self_closing && !self.tag_name.is_empty() { + iter.next(); + } + } + // if we've run out of text but still haven't found a `>`, + // return early without calling `tag_parsed` or emitting lints. + // this allows us to either find the `>` in a later event + // or emit a lint about it being missing. + return; } - } else if c == '<' { - if text[start_pos..].starts_with("<!--") { - // We skip the "!--" part. (Once `advance_by` is stable, might be nice to use it!) - iter.next(); - iter.next(); - iter.next(); - *is_in_comment = Some(Range { - start: range.start + start_pos, - end: range.start + start_pos + 3, + } + if is_self_closing { + // https://html.spec.whatwg.org/#parse-error-non-void-html-element-start-tag-with-trailing-solidus + let valid = ALLOWED_UNCLOSED.contains(&&self.tag_name[..]) + || self.tags.iter().take(pos + 1).any(|(at, _)| { + let at = at.to_lowercase(); + at == "svg" || at == "math" }); - } else { - extract_html_tag(tags, text, &range, start_pos, &mut iter, f); + if !valid { + f(format!("invalid self-closing HTML tag `{}`", self.tag_name), &r, false); + } + } else if !self.tag_name.is_empty() { + self.tags.push((std::mem::take(&mut self.tag_name), r)); + } + self.tag_parsed(); + } + /// Finished parsing a tag, reset related data. + fn tag_parsed(&mut self) { + self.tag_name.clear(); + self.is_closing = false; + self.in_attrs = false; + } + + fn extract_tags( + &mut self, + text: &str, + range: Range<usize>, + is_in_comment: &mut Option<Range<usize>>, + f: &impl Fn(String, &Range<usize>, bool), + ) { + let mut iter = text.char_indices().peekable(); + let mut prev_pos = 0; + loop { + if self.quote.is_some() { + debug_assert!(self.in_attrs && self.quote_pos.is_some()); + } + if self.in_attrs + && let Some(&(start_pos, _)) = iter.peek() + { + self.extract_html_tag(text, &range, start_pos, &mut iter, f); + // if no progress is being made, move forward forcefully. + if prev_pos == start_pos { + iter.next(); + } + prev_pos = start_pos; + continue; + } + let Some((start_pos, c)) = iter.next() else { break }; + if is_in_comment.is_some() { + if text[start_pos..].starts_with("-->") { + *is_in_comment = None; + } + } else if c == '<' { + // "<!--" is a valid attribute name under html5, so don't treat it as a comment if we're in a tag. + if self.tag_name.is_empty() && text[start_pos..].starts_with("<!--") { + // We skip the "!--" part. (Once `advance_by` is stable, might be nice to use it!) + iter.next(); + iter.next(); + iter.next(); + *is_in_comment = Some(Range { + start: range.start + start_pos, + end: range.start + start_pos + 4, + }); + } else { + if self.tag_name.is_empty() { + self.tag_start_pos = range.start + start_pos; + } + self.extract_html_tag(text, &range, start_pos, &mut iter, f); + } + } else if !self.tag_name.is_empty() { + // partially inside html tag that spans across events + self.extract_html_tag(text, &range, start_pos, &mut iter, f); } } } } + +#[cfg(test)] +mod tests; diff --git a/src/librustdoc/passes/lint/html_tags/tests.rs b/src/librustdoc/passes/lint/html_tags/tests.rs new file mode 100644 index 00000000000..81c1d21a55d --- /dev/null +++ b/src/librustdoc/passes/lint/html_tags/tests.rs @@ -0,0 +1,73 @@ +use std::cell::RefCell; + +use super::*; + +#[test] +fn test_extract_tags_nested_unclosed() { + let mut tagp = TagParser::new(); + let diags = RefCell::new(Vec::new()); + let dox = "<div>\n<br</div>"; + tagp.extract_tags(dox, 0..dox.len(), &mut None, &|s, r, b| { + diags.borrow_mut().push((s, r.clone(), b)); + }); + assert_eq!(diags.borrow().len(), 1, "did not get expected diagnostics: {diags:?}"); + assert_eq!(diags.borrow()[0].1, 6..9) +} + +#[test] +fn test_extract_tags_taglike_in_attr() { + let mut tagp = TagParser::new(); + let diags = RefCell::new(Vec::new()); + let dox = "<img src='<div>'>"; + tagp.extract_tags(dox, 0..dox.len(), &mut None, &|s, r, b| { + diags.borrow_mut().push((s, r.clone(), b)); + }); + assert_eq!(diags.borrow().len(), 0, "unexpected diagnostics: {diags:?}"); +} + +#[test] +fn test_extract_tags_taglike_in_multiline_attr() { + let mut tagp = TagParser::new(); + let diags = RefCell::new(Vec::new()); + let dox = "<img src=\"\nasd\n<div>\n\">"; + tagp.extract_tags(dox, 0..dox.len(), &mut None, &|s, r, b| { + diags.borrow_mut().push((s, r.clone(), b)); + }); + assert_eq!(diags.borrow().len(), 0, "unexpected diagnostics: {diags:?}"); +} + +#[test] +fn test_extract_tags_taglike_in_multievent_attr() { + let mut tagp = TagParser::new(); + let diags = RefCell::new(Vec::new()); + let dox = "<img src='<div>'>"; + let split_point = 10; + let mut p = |range: Range<usize>| { + tagp.extract_tags(&dox[range.clone()], range, &mut None, &|s, r, b| { + diags.borrow_mut().push((s, r.clone(), b)); + }) + }; + p(0..split_point); + p(split_point..dox.len()); + assert_eq!(diags.borrow().len(), 0, "unexpected diagnostics: {diags:?}"); +} + +#[test] +fn test_extract_tags_taglike_in_multiline_multievent_attr() { + let mut tagp = TagParser::new(); + let diags = RefCell::new(Vec::new()); + let dox = "<img src='\n foo:\n </div>\n <p/>\n <div>\n'>"; + let mut p = |range: Range<usize>| { + tagp.extract_tags(&dox[range.clone()], range, &mut None, &|s, r, b| { + diags.borrow_mut().push((s, r.clone(), b)); + }) + }; + let mut offset = 0; + for ln in dox.split_inclusive('\n') { + let new_offset = offset + ln.len(); + p(offset..new_offset); + offset = new_offset; + } + assert_eq!(diags.borrow().len(), 0, "unexpected diagnostics: {diags:?}"); + assert_eq!(tagp.tags.len(), 1); +} diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs index b16924babd1..d02952eb487 100644 --- a/src/tools/clippy/clippy_lints/src/missing_inline.rs +++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs @@ -190,5 +190,5 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { /// and a rustc warning would be triggered, see #15301 fn fn_is_externally_exported(cx: &LateContext<'_>, def_id: DefId) -> bool { let attrs = cx.tcx.codegen_fn_attrs(def_id); - attrs.contains_extern_indicator(cx.tcx, def_id) + attrs.contains_extern_indicator() } diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 739db8fa095..c533293e791 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -412,7 +412,7 @@ impl<'test> TestCx<'test> { cmdline: format!("{cmd:?}"), }; self.dump_output( - self.config.verbose, + self.config.verbose || !proc_res.status.success(), &cmd.get_program().to_string_lossy(), &proc_res.stdout, &proc_res.stderr, @@ -1486,7 +1486,7 @@ impl<'test> TestCx<'test> { }; self.dump_output( - self.config.verbose, + self.config.verbose || (!result.status.success() && self.config.mode != TestMode::Ui), &command.get_program().to_string_lossy(), &result.stdout, &result.stderr, @@ -2987,6 +2987,7 @@ struct ProcArgs { args: Vec<OsString>, } +#[derive(Debug)] pub struct ProcRes { status: ExitStatus, stdout: String, diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index d9e374c414c..ae1b25f8857 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -279,7 +279,7 @@ impl rustc_driver::Callbacks for MiriBeRustCompilerCalls { return None; } let codegen_fn_attrs = tcx.codegen_fn_attrs(local_def_id); - if codegen_fn_attrs.contains_extern_indicator(tcx, local_def_id.into()) + if codegen_fn_attrs.contains_extern_indicator() || codegen_fn_attrs .flags .contains(CodegenFnAttrFlags::USED_COMPILER) diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 1b5d9d50996..e0c077e9931 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -134,8 +134,7 @@ pub fn iter_exported_symbols<'tcx>( for def_id in crate_items.definitions() { let exported = tcx.def_kind(def_id).has_codegen_attrs() && { let codegen_attrs = tcx.codegen_fn_attrs(def_id); - codegen_attrs.contains_extern_indicator(tcx, def_id.into()) - || codegen_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) + codegen_attrs.contains_extern_indicator() || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER) || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) }; diff --git a/src/tools/rustfmt/tests/source/frontmatter_compact.rs b/src/tools/rustfmt/tests/source/frontmatter_compact.rs new file mode 100644 index 00000000000..21d4c6f4b61 --- /dev/null +++ b/src/tools/rustfmt/tests/source/frontmatter_compact.rs @@ -0,0 +1,8 @@ +#!/usr/bin/env cargo +---identifier +[dependencies] +regex = "1" +--- +#![feature(frontmatter)] + +fn main() {} diff --git a/src/tools/rustfmt/tests/source/frontmatter_escaped.rs b/src/tools/rustfmt/tests/source/frontmatter_escaped.rs new file mode 100644 index 00000000000..0d026377566 --- /dev/null +++ b/src/tools/rustfmt/tests/source/frontmatter_escaped.rs @@ -0,0 +1,13 @@ +#!/usr/bin/env cargo +------------ +package.description = """ +Header +----- + +Body +""" +------------ + +#![feature(frontmatter)] + +fn main() {} diff --git a/src/tools/rustfmt/tests/source/frontmatter_spaced.rs b/src/tools/rustfmt/tests/source/frontmatter_spaced.rs new file mode 100644 index 00000000000..ee0bb81705c --- /dev/null +++ b/src/tools/rustfmt/tests/source/frontmatter_spaced.rs @@ -0,0 +1,16 @@ +#!/usr/bin/env cargo + + +--- identifier +[dependencies] +regex = "1" + +--- + + + + + +#![feature(frontmatter)] + +fn main() {} diff --git a/src/tools/rustfmt/tests/target/frontmatter_compact.rs b/src/tools/rustfmt/tests/target/frontmatter_compact.rs new file mode 100644 index 00000000000..21d4c6f4b61 --- /dev/null +++ b/src/tools/rustfmt/tests/target/frontmatter_compact.rs @@ -0,0 +1,8 @@ +#!/usr/bin/env cargo +---identifier +[dependencies] +regex = "1" +--- +#![feature(frontmatter)] + +fn main() {} diff --git a/src/tools/rustfmt/tests/target/frontmatter_escaped.rs b/src/tools/rustfmt/tests/target/frontmatter_escaped.rs new file mode 100644 index 00000000000..0d026377566 --- /dev/null +++ b/src/tools/rustfmt/tests/target/frontmatter_escaped.rs @@ -0,0 +1,13 @@ +#!/usr/bin/env cargo +------------ +package.description = """ +Header +----- + +Body +""" +------------ + +#![feature(frontmatter)] + +fn main() {} diff --git a/src/tools/rustfmt/tests/target/frontmatter_spaced.rs b/src/tools/rustfmt/tests/target/frontmatter_spaced.rs new file mode 100644 index 00000000000..ee0bb81705c --- /dev/null +++ b/src/tools/rustfmt/tests/target/frontmatter_spaced.rs @@ -0,0 +1,16 @@ +#!/usr/bin/env cargo + + +--- identifier +[dependencies] +regex = "1" + +--- + + + + + +#![feature(frontmatter)] + +fn main() {} diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 80b6d54ce1c..18d97a748ba 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -361,7 +361,6 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "scoped-tls", "scopeguard", "self_cell", - "semver", "serde", "serde_derive", "serde_json", diff --git a/src/tools/tidy/src/gcc_submodule.rs b/src/tools/tidy/src/gcc_submodule.rs index 5d726c3ea48..217eaf1758c 100644 --- a/src/tools/tidy/src/gcc_submodule.rs +++ b/src/tools/tidy/src/gcc_submodule.rs @@ -24,6 +24,12 @@ pub fn check(root_path: &Path, compiler_path: &Path, bad: &mut bool) { .output() .expect("Cannot determine git SHA of the src/gcc checkout"); + // Git is not available or we are in a tarball + if !git_output.status.success() { + eprintln!("Cannot figure out the SHA of the GCC submodule"); + return; + } + // This can return e.g. // -e607be166673a8de9fc07f6f02c60426e556c5f2 src/gcc // e607be166673a8de9fc07f6f02c60426e556c5f2 src/gcc (master-e607be166673a8de9fc07f6f02c60426e556c5f2.e607be) diff --git a/src/tools/wasm-component-ld/Cargo.toml b/src/tools/wasm-component-ld/Cargo.toml index ce718902b29..23dc86998e8 100644 --- a/src/tools/wasm-component-ld/Cargo.toml +++ b/src/tools/wasm-component-ld/Cargo.toml @@ -10,4 +10,4 @@ name = "wasm-component-ld" path = "src/main.rs" [dependencies] -wasm-component-ld = "0.5.14" +wasm-component-ld = "0.5.16" |
