From b33d15bfb0d4f90d882cca8d4fe3cc278ae4a9ca Mon Sep 17 00:00:00 2001 From: Noratrieb <48135649+Noratrieb@users.noreply.github.com> Date: Tue, 19 Mar 2024 19:03:07 +0100 Subject: [PATCH 01/11] Introduce infrastructure for generating target docs Note: this commit was rebased from c3b05c6e5b5 onto ae0fe511b15 by @qb42 in 2026, the following changes were made: - Remove the TargetMetadata additions from compiler/rustc_target/src/spec/targets (more recent info had been added to main separately for all of them) - Removed changes to src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile (file no longer exists) - Assorted merge conflicts, with the only non-trivial ones being in src/bootstrap/src/lib.rs from the added methods on Builder. The contents of lib.rs have also been moved to core/session.rs since the original commit. --- Cargo.lock | 30 ++ Cargo.toml | 1 + src/bootstrap/src/core/build_steps/doc.rs | 40 ++- src/bootstrap/src/core/build_steps/tool.rs | 1 + src/bootstrap/src/core/session.rs | 49 +++- src/doc/rustc/src/SUMMARY.md | 3 + src/doc/rustc/src/platform-support.md | 12 + src/doc/rustc/src/platform-support/aix.md | 10 + .../rustc/src/platform-support/apple-tvos.md | 10 + .../src/platform-support/loongarch-linux.md | 9 + src/doc/rustc/src/platform-support/targets.md | 7 + src/doc/rustc/target_infos/_-apple-tvos.md | 79 ++++++ .../rustc/target_infos/i686-pc-windows-gnu.md | 11 + .../loongarch_-unknown-linux-_.md | 94 +++++++ .../rustc/target_infos/powerpc64-ibm-aix.md | 19 ++ src/tools/target-docs/Cargo.toml | 14 + src/tools/target-docs/README.md | 40 +++ src/tools/target-docs/src/main.rs | 239 ++++++++++++++++ src/tools/target-docs/src/parse.rs | 138 ++++++++++ src/tools/target-docs/src/parse/tests.rs | 137 ++++++++++ src/tools/target-docs/src/render.rs | 257 ++++++++++++++++++ 21 files changed, 1189 insertions(+), 11 deletions(-) create mode 100644 src/doc/rustc/src/platform-support/targets.md create mode 100644 src/doc/rustc/target_infos/_-apple-tvos.md create mode 100644 src/doc/rustc/target_infos/i686-pc-windows-gnu.md create mode 100644 src/doc/rustc/target_infos/loongarch_-unknown-linux-_.md create mode 100644 src/doc/rustc/target_infos/powerpc64-ibm-aix.md create mode 100644 src/tools/target-docs/Cargo.toml create mode 100644 src/tools/target-docs/README.md create mode 100644 src/tools/target-docs/src/main.rs create mode 100644 src/tools/target-docs/src/parse.rs create mode 100644 src/tools/target-docs/src/parse/tests.rs create mode 100644 src/tools/target-docs/src/render.rs diff --git a/Cargo.lock b/Cargo.lock index e11fd2189569c..2084c9828f318 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5394,6 +5394,19 @@ dependencies = [ "serde_core", ] +[[package]] +name = "serde_yaml" +version = "0.9.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adf8a49373e98a4c5f0ceb5d05aa7c648d75f63774981ed95b7c7443bbd50c6e" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "sha1" version = "0.10.6" @@ -5634,6 +5647,17 @@ dependencies = [ "xattr", ] +[[package]] +name = "target-docs" +version = "0.1.0" +dependencies = [ + "eyre", + "glob", + "serde", + "serde_json", + "serde_yaml", +] + [[package]] name = "tempfile" version = "3.23.0" @@ -6250,6 +6274,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3" +[[package]] +name = "unsafe-libyaml" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" + [[package]] name = "unstable-book-gen" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 63d96ff2a62ac..543fa411a711a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,6 +40,7 @@ members = [ "src/tools/rustdoc-gui-test", "src/tools/rustdoc-themes", "src/tools/rustfmt", + "src/tools/target-docs", "src/tools/test-float-parse", "src/tools/tidy", "src/tools/tier-check", diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 34e9b354bddb2..363cf7c7bbedc 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -1664,14 +1664,24 @@ impl CommandLineStep for RustcBook { /// Builds the rustc book. /// - /// The lints are auto-generated by a tool, and then merged into the book + /// The lints and target docs are auto-generated by a tool, and then merged into the book /// in the "md-doc" directory in the build output directory. Then /// "rustbook" is used to convert it to HTML. fn run(self, builder: &Builder<'_>) { - let out_base = builder.out.join(self.target).join("md-doc").join("rustc"); + // The rustc book invokes rustc in its build process and relies on it being the in-tree rustc. + // (by requiring that the targets match and also that nightly flags can be used) + // Using the bootstrap compiler for this doesn't work. + if builder.top_stage < 1 { + crate::fail("building the rustc book with stage 0 is not supported, use --stage 1"); + } + + let out_base = builder.md_doc_out(self.target).join("rustc"); t!(fs::create_dir_all(&out_base)); - let out_listing = out_base.join("src/lints"); - builder.cp_link_r(&builder.src.join("src/doc/rustc"), &out_base); + let out_lints_listing = out_base.join("src/lints"); + let out_src_listing = out_base.join("src"); + + // target-docs will be modifying the files in-place, so we need an actual copy. + builder.cp_r(&builder.src.join("src/doc/rustc"), &out_base); builder.info(&format!("Generating lint docs ({})", self.target)); let rustc = builder.rustc(self.build_compiler); @@ -1684,7 +1694,7 @@ impl CommandLineStep for RustcBook { cmd.arg("--src"); cmd.arg(builder.src.join("compiler")); cmd.arg("--out"); - cmd.arg(&out_listing); + cmd.arg(&out_lints_listing); cmd.arg("--rustc"); cmd.arg(&rustc); cmd.arg("--rustc-target").arg(self.target.rustc_target_arg()); @@ -1711,6 +1721,26 @@ impl CommandLineStep for RustcBook { cmd.run(builder); drop(doc_generator_guard); + // Run target-docs generator + let mut cmd = builder.tool_cmd(Tool::TargetDocs); + cmd.arg(builder.src.join("src/doc/rustc/target_infos")); + cmd.arg(&out_src_listing); + cmd.env("RUSTC", &rustc); + // For now, we just check that the files are correct but do not generate output. + // Let the user override it to TARGET_CHECK_ONLY=0 for testing, but use 1 by default. + // See https://github.com/rust-lang/rust/issues/120745 for more info. + cmd.env("TARGET_CHECK_ONLY", std::env::var("TARGET_CHECK_ONLY").unwrap_or("1".to_owned())); + + let doc_generator_guard = builder.msg( + Kind::Run, + self.compiler.stage, + "target-docs", + self.compiler.host, + self.target, + ); + builder.run(&mut cmd); + drop(doc_generator_guard); + // Run rustbook/mdbook to generate the HTML pages. builder.ensure(RustbookSrc { target: self.target, diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index c4fae69645e85..240a3da593364 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -521,6 +521,7 @@ bootstrap_tool!( RustInstaller, "src/tools/rust-installer", "rust-installer"; RustdocTheme, "src/tools/rustdoc-themes", "rustdoc-themes"; LintDocs, "src/tools/lint-docs", "lint-docs"; + TargetDocs, "src/tools/target-docs", "target-docs"; JsonDocCk, "src/tools/jsondocck", "jsondocck"; JsonDocLint, "src/tools/jsondoclint", "jsondoclint"; HtmlChecker, "src/tools/html-checker", "html-checker"; diff --git a/src/bootstrap/src/core/session.rs b/src/bootstrap/src/core/session.rs index 1808f1b8855bc..b796f3b50ab46 100644 --- a/src/bootstrap/src/core/session.rs +++ b/src/bootstrap/src/core/session.rs @@ -1495,7 +1495,7 @@ impl Session { /// and copied to `dst` instead of the symlink itself. #[track_caller] pub(crate) fn resolve_symlink_and_copy(&self, src: &Path, dst: &Path) { - self.copy_link_internal(src, dst, true); + self.copy_internal(src, dst, true, true); } /// Links a file from `src` to `dst`. @@ -1504,21 +1504,35 @@ impl Session { /// so do not write to dst. #[track_caller] pub(crate) fn copy_link(&self, src: &Path, dst: &Path, file_type: FileType) { - self.copy_link_internal(src, dst, false); + self.copy_internal(src, dst, false, true); if file_type.could_have_split_debuginfo() && let Some(dbg_file) = split_debuginfo(src) { - self.copy_link_internal( + self.copy_internal( &dbg_file, &dst.with_extension(dbg_file.extension().unwrap()), false, + true, ); } } + /// Links a file from `src` to `dst`. + /// Unlike, [`Build::copy_link`], this makes an actual copy, which is usually not required, + /// so `copy_link` should be used instead if possible. + pub fn copy(&self, src: &Path, dst: &Path) { + self.copy_internal(src, dst, false, false); + } + #[track_caller] - fn copy_link_internal(&self, src: &Path, dst: &Path, dereference_symlinks: bool) { + fn copy_internal( + &self, + src: &Path, + dst: &Path, + dereference_symlinks: bool, + link_if_possible: bool, + ) { if self.config.dry_run() { return; } @@ -1554,7 +1568,8 @@ impl Session { return; } } - if let Ok(()) = fs::hard_link(&src, dst) { + + if link_if_possible && fs::hard_link(&src, dst).is_ok() { // Attempt to "easy copy" by creating a hard link (symlinks are privileged on windows), // but if that fails just fall back to a slow `copy` operation. } else { @@ -1593,6 +1608,28 @@ impl Session { } } + /// Copies the `src` directory recursively to `dst`. Both are assumed to exist + /// when this function is called. + /// Unlike, [`Build::cp_link_r`], this makes an actual copy, which is usually not required, + /// so `cp_link_r` should be used instead if possible. + pub fn cp_r(&self, src: &Path, dst: &Path) { + if self.config.dry_run() { + return; + } + for f in self.read_dir(src) { + let path = f.path(); + let name = path.file_name().unwrap(); + let dst = dst.join(name); + if t!(f.file_type()).is_dir() { + t!(fs::create_dir_all(&dst)); + self.cp_r(&path, &dst); + } else { + let _ = fs::remove_file(&dst); + self.copy(&path, &dst); + } + } + } + /// Copies the `src` directory recursively to `dst`. Both are assumed to exist /// when this function is called. /// Will attempt to use hard links if possible and fall back to copying. @@ -1651,7 +1688,7 @@ impl Session { panic!("ERROR: File \"{}\" not found!", src.display()); } - self.copy_link_internal(src, &dst, true); + self.copy_internal(src, &dst, true, true); chmod(&dst, file_type.perms()); // If this file can have debuginfo, look for split debuginfo and install it too. diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 1c2935ff264e0..1bec8f230ff4b 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -166,3 +166,6 @@ - [x86_64-unknown-linux-gnutsan](platform-support/x86_64-unknown-linux-gnutsan.md) - [xtensa-\*-none-elf](platform-support/xtensa.md) - [\*-nuttx-\*](platform-support/nuttx.md) + + + diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 11a6ada2c4f89..2025402e20b0e 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -47,6 +47,9 @@ target | notes [`x86_64-pc-windows-gnu`](platform-support/windows-gnu.md) | 64-bit MinGW (Windows 10+, Windows Server 2016+) [`x86_64-pc-windows-msvc`](platform-support/windows-msvc.md) | 64-bit MSVC (Windows 10+, Windows Server 2016+) `x86_64-unknown-linux-gnu` | 64-bit Linux (kernel 3.2+, glibc 2.17+) + + + [^x86_32-floats-return-ABI]: Due to limitations of the C ABI, floating-point support on `i686` targets is non-compliant: floating-point return values are passed via an x87 register, so NaN payload bits can be lost. Functions with the default Rust ABI are not affected. See [issue #115567][x86-32-float-return-issue]. @@ -118,6 +121,9 @@ target | notes [`x86_64-unknown-netbsd`](platform-support/netbsd.md) | NetBSD/amd64 [`x86_64-pc-solaris`](platform-support/solaris.md) | 64-bit x86 Solaris 11.4 [`sparcv9-sun-solaris`](platform-support/solaris.md) | SPARC V9 Solaris 11.4 + + + ## Tier 2 without Host Tools @@ -234,6 +240,9 @@ target | std | notes [`x86_64-unknown-none`](platform-support/x86_64-unknown-none.md) | * | Freestanding/bare-metal x86_64, softfloat [`x86_64-unknown-redox`](platform-support/redox.md) | ✓ | Redox OS [`x86_64-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 64-bit UEFI + + + [^x86_32-floats-x87]: Floating-point support on `i586` targets is non-compliant: the `x87` registers and instructions used for these targets do not provide IEEE-754-compliant behavior, in particular when it comes to rounding and NaN payload bits. See [issue #114479][x86-32-float-issue]. @@ -482,6 +491,9 @@ target | std | host | notes [`xtensa-esp32s2-none-elf`](platform-support/xtensa.md) | * | | Xtensa ESP32-S2 [`xtensa-esp32s3-espidf`](platform-support/esp-idf.md) | ✓ | | Xtensa ESP32-S3 [`xtensa-esp32s3-none-elf`](platform-support/xtensa.md) | * | | Xtensa ESP32-S3 + + + [runs on NVIDIA GPUs]: https://github.com/japaric-archived/nvptx#targets [the AMD GPU]: https://llvm.org/docs/AMDGPUUsage.html#processors diff --git a/src/doc/rustc/src/platform-support/aix.md b/src/doc/rustc/src/platform-support/aix.md index c06092eb123c8..b48a037d12cae 100644 --- a/src/doc/rustc/src/platform-support/aix.md +++ b/src/doc/rustc/src/platform-support/aix.md @@ -1,5 +1,15 @@ # `powerpc64-ibm-aix` + + **Tier: 3** Rust for AIX operating system, currently only 64-bit PowerPC is supported. diff --git a/src/doc/rustc/src/platform-support/apple-tvos.md b/src/doc/rustc/src/platform-support/apple-tvos.md index 38c6d47a9c1d3..a75c7a72c8bd4 100644 --- a/src/doc/rustc/src/platform-support/apple-tvos.md +++ b/src/doc/rustc/src/platform-support/apple-tvos.md @@ -7,6 +7,16 @@ Apple tvOS targets. - `aarch64-apple-tvos`: Apple tvOS on ARM64. - `aarch64-apple-tvos-sim`: Apple tvOS Simulator on ARM64. + + **Tier: 3** - `x86_64-apple-tvos`: Apple tvOS Simulator on x86_64. diff --git a/src/doc/rustc/src/platform-support/loongarch-linux.md b/src/doc/rustc/src/platform-support/loongarch-linux.md index 7817ce6138a04..fa4eb8487f14b 100644 --- a/src/doc/rustc/src/platform-support/loongarch-linux.md +++ b/src/doc/rustc/src/platform-support/loongarch-linux.md @@ -1,5 +1,14 @@ # `loongarch*-unknown-linux-*` + **Tier: 2 (with Host Tools)** [LoongArch][la-docs] Linux targets. diff --git a/src/doc/rustc/src/platform-support/targets.md b/src/doc/rustc/src/platform-support/targets.md new file mode 100644 index 0000000000000..2a34bac74b377 --- /dev/null +++ b/src/doc/rustc/src/platform-support/targets.md @@ -0,0 +1,7 @@ +# List of all targets + +An alphabetical list of all targets. + + + + diff --git a/src/doc/rustc/target_infos/_-apple-tvos.md b/src/doc/rustc/target_infos/_-apple-tvos.md new file mode 100644 index 0000000000000..97c9344e6d91c --- /dev/null +++ b/src/doc/rustc/target_infos/_-apple-tvos.md @@ -0,0 +1,79 @@ +--- +pattern: "*-apple-tvos" +maintainers: ["@thomcc"] +--- + +## Overview + +Apple tvOS targets: +- Apple tvOS on aarch64 +- Apple tvOS Simulator on x86_64 + +## Requirements + +These targets are cross-compiled. You will need appropriate versions of Xcode +and the SDKs for tvOS (`AppleTVOS.sdk`) and/or the tvOS Simulator +(`AppleTVSimulator.sdk`) to build a toolchain and target these platforms. + +The targets support most (see below) of the standard library including the +allocator to the best of my knowledge, however they are very new, not yet +well-tested, and it is possible that there are various bugs. + +In theory we support back to tvOS version 7.0, although the actual minimum +version you can target may be newer than this, for example due to the versions +of Xcode and your SDKs. + +As with the other Apple targets, `rustc` respects the common environment +variables used by Xcode to configure this, in this case +`TVOS_DEPLOYMENT_TARGET`. + +As mentioned, "most" of the standard library is supported, which means that some portions +are known to be unsupported. The following APIs are currently known to have +missing or incomplete support: + +- `std::process::Command`'s API will return an error if it is configured in a + manner which cannot be performed using `posix_spawn` -- this is because the + more flexible `fork`/`exec`-based approach is prohibited on these platforms in + favor of `posix_spawn{,p}` (which still probably will get you rejected from + app stores, so is likely sideloading-only). A concrete set of cases where this + will occur is difficult to enumerate (and would quickly become stale), but in + some cases it may be worked around by tweaking the manner in which `Command` + is invoked. + +## Building the target + +The targets can be built by enabling them for a `rustc` build in `config.toml`, by adding, for example: + +```toml +[build] +build-stage = 1 +target = ["aarch64-apple-tvos", "x86_64-apple-tvos", "aarch64-apple-tvos-sim"] +``` + +It's possible that cargo under `-Zbuild-std` may also be used to target them. + +## Building Rust programs + +*Note: Building for this target requires the corresponding TVOS SDK, as provided by Xcode.* + +Rust programs can be built for these targets + +```text +$ rustc --target aarch64-apple-tvos your-code.rs +... +$ rustc --target x86_64-apple-tvos your-code.rs +... +$ rustc --target aarch64-apple-tvos-sim your-code.rs +``` + +## Testing + +There is no support for running the Rust or standard library testsuite on tvOS or the simulators at the moment. Testing has mostly been done manually with builds of static libraries called from Xcode or a simulator. + +It hopefully will be possible to improve this in the future. + +## Cross compilation + +This target can be cross-compiled from x86_64 or aarch64 macOS hosts. + +Other hosts are not supported for cross-compilation, but might work when also providing the required Xcode SDK. diff --git a/src/doc/rustc/target_infos/i686-pc-windows-gnu.md b/src/doc/rustc/target_infos/i686-pc-windows-gnu.md new file mode 100644 index 0000000000000..3b7951f588fbb --- /dev/null +++ b/src/doc/rustc/target_infos/i686-pc-windows-gnu.md @@ -0,0 +1,11 @@ +--- +pattern: "i686-pc-windows-gnu" +footnotes: + i686-pc-windows-gnu: + - "x86_32-floats-return-ABI" + - "windows-support" +--- + +## Overview + +32-bit Windows using MinGW. diff --git a/src/doc/rustc/target_infos/loongarch_-unknown-linux-_.md b/src/doc/rustc/target_infos/loongarch_-unknown-linux-_.md new file mode 100644 index 0000000000000..a47343065abb6 --- /dev/null +++ b/src/doc/rustc/target_infos/loongarch_-unknown-linux-_.md @@ -0,0 +1,94 @@ +--- +pattern: "loongarch*-unknown-linux-*" +maintainers: + - "[WANG Rui](https://github.com/heiher) `wangrui@loongson.cn`" + - "[ZHAI Xiang](https://github.com/xiangzhai) `zhaixiang@loongson.cn`" + - "[ZHAI Xiaojuan](https://github.com/zhaixiaojuan) `zhaixiaojuan@loongson.cn`" + - "[WANG Xuerui](https://github.com/xen0n) `git@xen0n.name`" +--- + +## Overview + +[LoongArch] is a new RISC ISA developed by Loongson Technology Corporation Limited. + +[LoongArch]: https://loongson.github.io/LoongArch-Documentation/README-EN.html + +The target name follow this format: `--`, where `` specifies the CPU family/model, `` specifies the vendor and `` the operating system name. +While the integer base ABI is implied by the machine field, the floating point base ABI type is encoded into the os field of the specifier using the string suffix ``. + +| `` | `Description` | +|------------------------|--------------------------------------------------------------------| +| f64 | The base ABI use 64-bits FPRs for parameter passing. (lp64d)| +| f32 | The base ABI uses 32-bit FPRs for parameter passing. (lp64f)| +| sf | The base ABI uses no FPR for parameter passing. (lp64s) | + +
+ +|`ABI type(Base ABI/ABI extension)`| `C library` | `kernel` | `target tuple` | +|----------------------------------|-------------|----------|----------------------------------| +| lp64d/base | glibc | linux | loongarch64-unknown-linux-gnu | +| lp64f/base | glibc | linux | loongarch64-unknown-linux-gnuf32 | +| lp64s/base | glibc | linux | loongarch64-unknown-linux-gnusf | +| lp64d/base | musl libc | linux | loongarch64-unknown-linux-musl| +| lp64f/base | musl libc | linux | loongarch64-unknown-linux-muslf32| +| lp64s/base | musl libc | linux | loongarch64-unknown-linux-muslsf | + +## Requirements + +This target is cross-compiled. +A GNU toolchain for LoongArch target is required. It can be downloaded from https://github.com/loongson/build-tools/releases, or built from the source code of GCC (12.1.0 or later) and Binutils (2.40 or later). + +## Building the target + +The target can be built by enabling it for a `rustc` build. + +```toml +[build] +target = ["loongarch64-unknown-linux-gnu"] +``` + +Make sure `loongarch64-unknown-linux-gnu-gcc` can be searched from the directories specified in`$PATH`. Alternatively, you can use GNU LoongArch Toolchain by adding the following to `config.toml`: + +```toml +[target.loongarch64-unknown-linux-gnu] +# ADJUST THIS PATH TO POINT AT YOUR TOOLCHAIN +cc = "/TOOLCHAIN_PATH/bin/loongarch64-unknown-linux-gnu-gcc" +cxx = "/TOOLCHAIN_PATH/bin/loongarch64-unknown-linux-gnu-g++" +ar = "/TOOLCHAIN_PATH/bin/loongarch64-unknown-linux-gnu-ar" +ranlib = "/TOOLCHAIN_PATH/bin/loongarch64-unknown-linux-gnu-ranlib" +linker = "/TOOLCHAIN_PATH/bin/loongarch64-unknown-linux-gnu-gcc" +``` + +## Cross compilation + +This target can be cross-compiled on a `x86_64-unknown-linux-gnu` host. Cross-compilation on other hosts may work but is not tested. + +## Testing +To test a cross-compiled binary on your build system, install the qemu binary that supports the LoongArch architecture and execute the following commands. +```text +CC_loongarch64_unknown_linux_gnu=/TOOLCHAIN_PATH/bin/loongarch64-unknown-linux-gnu-gcc \ +CXX_loongarch64_unknown_linux_gnu=/TOOLCHAIN_PATH/bin/loongarch64-unknown-linux-gnu-g++ \ +AR_loongarch64_unknown_linux_gnu=/TOOLCHAIN_PATH/bin/loongarch64-unknown-linux-gnu-gcc-ar \ +CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNUN_LINKER=/TOOLCHAIN_PATH/bin/loongarch64-unknown-linux-gnu-gcc \ +# SET TARGET SYSTEM LIBRARY PATH +CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNUN_RUNNER="qemu-loongarch64 -L /TOOLCHAIN_PATH/TARGET_LIBRARY_PATH" \ +cargo run --target loongarch64-unknown-linux-gnu --release +``` +Tested on x86 architecture, other architectures not tested. + +## Building Rust programs + +Rust does not yet ship pre-compiled artifacts for this target. To compile for this target, you will either need to build Rust with the target enabled (see "Building the target" above), or build your own copy of `std` by using `build-std` or similar. + +If `rustc` has support for that target and the library artifacts are available, then Rust static libraries can be built for that target: + +```shell +$ rustc --target loongarch64-unknown-linux-gnu your-code.rs --crate-type staticlib +$ ls libyour_code.a +``` + +On Rust Nightly it's possible to build without the target artifacts available: + +```text +cargo build -Z build-std --target loongarch64-unknown-linux-gnu +``` diff --git a/src/doc/rustc/target_infos/powerpc64-ibm-aix.md b/src/doc/rustc/target_infos/powerpc64-ibm-aix.md new file mode 100644 index 0000000000000..7377ccda3ffd1 --- /dev/null +++ b/src/doc/rustc/target_infos/powerpc64-ibm-aix.md @@ -0,0 +1,19 @@ +--- +pattern: "powerpc64-ibm-aix" +maintainers: [ + "QIU Chaofan `qiucofan@cn.ibm.com`, https://github.com/ecnelises", + "Kai LUO, `lkail@cn.ibm.com`, https://github.com/bzEq", +] +--- + +## Requirements + +This target supports host tools, std and alloc. This target cannot be cross-compiled as for now, mainly because of the unavailability of system linker on other platforms. + +Binary built for this target is expected to run on Power7 or newer CPU, and AIX 7.2 or newer version. + +Binary format of this platform is XCOFF. Archive file format is 'AIX big format'. + +## Testing + +This target supports running test suites natively, but it's not available to cross-compile and execute in emulator. diff --git a/src/tools/target-docs/Cargo.toml b/src/tools/target-docs/Cargo.toml new file mode 100644 index 0000000000000..b4b8209660b65 --- /dev/null +++ b/src/tools/target-docs/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "target-docs" +version = "0.1.0" +edition = "2021" +license = "MIT OR Apache-2.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +eyre = "0.6.12" +glob = "0.3.1" +serde = { version = "1.0.185", features = ["derive"] } +serde_json = "1.0.114" +serde_yaml = "0.9.31" diff --git a/src/tools/target-docs/README.md b/src/tools/target-docs/README.md new file mode 100644 index 0000000000000..3a4fa38eda3f7 --- /dev/null +++ b/src/tools/target-docs/README.md @@ -0,0 +1,40 @@ +# target-docs + +This tool generates target documentation for all targets in the rustc book. + +To achieve this, it uses a list of input markdown files provided in `src/doc/rustc/target_infos`. These files follow a strict format. +Every file covers a glob pattern of targets. The glob is specified in the `pattern` field in the front matter. +The file name must be this same glob, expect with the `*` replaced with `_` (because of Windows file name support). + +For every rustc target, we iterate through all the target infos and find matching globs. +When a glob matches, it extracts the h2 markdown sections and saves them for the target. + +In the end, a page is generated for every target using these sections. +Sections that are not provided are stubbed out. Currently, the sections are + +- Overview +- Requirements +- Testing +- Building the target +- Cross compilation +- Building Rust programs + +In addition to the markdown sections, we also have extra data about the targets. +This is achieved through YAML frontmatter. + +The frontmatter follows the following format: + +```yaml +pattern: i686-pc-windows-gnu +maintainers: ["@someone"] +footnotes: + i686-pc-windows-gnu: + - "x86_32-floats-return-ABI" + - "windows-support" +``` + +The top level keys are: + +- `pattern` (required): the glob pattern of the file, must match the file name +- `maintainers` (optional): list of strings +- `footnotes` (optional): for every *specific* target a list of footnotes. The footnotes have to be defined manually below the correct table in platform-support. diff --git a/src/tools/target-docs/src/main.rs b/src/tools/target-docs/src/main.rs new file mode 100644 index 0000000000000..5dbac0f7a2b9e --- /dev/null +++ b/src/tools/target-docs/src/main.rs @@ -0,0 +1,239 @@ +mod parse; +mod render; + +use std::{ + collections::HashMap, + path::{Path, PathBuf}, + process::Command, +}; + +use eyre::{bail, Context, OptionExt, Result}; +use parse::ParsedTargetInfoFile; +use serde::Deserialize; + +/// Information about a target obtained from the markdown and rustc. +struct TargetInfo { + name: String, + maintainers: Vec, + sections: Vec<(String, String)>, + footnotes: Vec, + target_cfgs: Vec<(String, String)>, + metadata: RustcTargetMetadata, +} + +/// All the sections that we want every doc page to have. +/// It may make sense to relax this into two kinds of sections, "required" sections +/// and "optional" sections, where required sections will get stubbed out when not found +/// while optional sections will just not exist when not found. +// IMPORTANT: This is also documented in the README, keep it in sync. +const SECTIONS: &[&str] = &[ + "Overview", + "Requirements", + "Testing", + "Building the target", + "Cross compilation", + "Building Rust programs", +]; + +fn main() -> Result<()> { + let args = std::env::args().collect::>(); + let input_dir = args + .get(1) + .ok_or_eyre("first argument must be path to target_infos directory containing target source md files (src/doc/rustc/target_infos/)")?; + let output_src = args.get(2).ok_or_eyre( + "second argument must be path to `src` output directory (build/$target/md-doc/rustc/src)", + )?; + + println!("Loading target info docs from {input_dir}"); + println!("Writing output to {output_src}"); + + let targets_to_skip = std::env::var("TARGET_DOCS_SKIP_TARGETS"); + let targets_to_skip = + targets_to_skip.as_deref().map(|s| s.split(",").collect::>()).unwrap_or_default(); + + let rustc = + PathBuf::from(std::env::var("RUSTC").expect("must pass RUSTC env var pointing to rustc")); + let check_only = std::env::var("TARGET_CHECK_ONLY") == Ok("1".to_owned()); + + let targets = rustc_stdout(&rustc, &["--print", "target-list"]); + let targets = + targets.lines().filter(|target| !targets_to_skip.contains(target)).collect::>(); + + let mut info_patterns = parse::load_target_infos(Path::new(input_dir)) + .wrap_err("failed loading target_info")? + .into_iter() + .map(|info| { + let footnotes_used = + info.footnotes.keys().map(|target| (target.clone(), false)).collect(); + TargetPatternEntry { info, used: false, footnotes_used } + }) + .collect::>(); + + eprintln!("Collecting rustc information"); + let rustc_infos = + targets.iter().map(|target| rustc_target_info(&rustc, target)).collect::>(); + + let targets = targets + .into_iter() + .map(|target| target_doc_info(&mut info_patterns, target)) + .zip(rustc_infos) + .map(|(md, rustc)| TargetInfo { + name: md.name, + maintainers: md.maintainers, + sections: md.sections, + footnotes: md.footnotes, + target_cfgs: rustc.target_cfgs, + metadata: rustc.metadata, + }) + .collect::>(); + + eprintln!("Rendering targets check_only={check_only}"); + let targets_dir = Path::new(output_src).join("platform-support").join("targets"); + if !check_only { + std::fs::create_dir_all(&targets_dir).wrap_err("creating platform-support/targets dir")?; + } + for info in &targets { + let doc = render::render_target_md(info); + + if !check_only { + std::fs::write(targets_dir.join(format!("{}.md", info.name)), doc) + .wrap_err("writing target file")?; + } + } + + for target_pattern in info_patterns { + if !target_pattern.used { + bail!( + "{}: target pattern `{}` was never used.\nnote that you must use `*` globs in the pattern, not `_` like in the file name.", + target_pattern.info.full_path.display(), + target_pattern.info.pattern + ); + } + + for footnote_target in target_pattern.info.footnotes.keys() { + let used = target_pattern.footnotes_used[footnote_target]; + if !used { + bail!( + "{}: in target pattern `{}`, the footnotes for target `{}` were never used", + target_pattern.info.full_path.display(), + target_pattern.info.pattern, + footnote_target, + ); + } + } + } + + render::render_static(check_only, Path::new(output_src), &targets)?; + + eprintln!("Finished generating target docs"); + Ok(()) +} + +struct TargetPatternEntry { + info: ParsedTargetInfoFile, + used: bool, + footnotes_used: HashMap, +} + +/// Information about a target obtained from the target_info markdown file. +struct TargetInfoMd { + name: String, + maintainers: Vec, + sections: Vec<(String, String)>, + footnotes: Vec, +} + +fn target_doc_info(info_patterns: &mut [TargetPatternEntry], target: &str) -> TargetInfoMd { + let mut maintainers = Vec::new(); + let mut sections = Vec::new(); + + let mut footnotes = Vec::new(); + + for target_pattern_entry in info_patterns { + if target_pattern_entry.info.pattern_glob.matches(target) { + target_pattern_entry.used = true; + let target_pattern = &target_pattern_entry.info; + + maintainers.extend_from_slice(&target_pattern.maintainers); + + for (section_name, content) in &target_pattern.sections { + if sections.iter().any(|(name, _)| name == section_name) { + panic!( + "target {target} inherits the section {section_name} from multiple patterns, create a more specific pattern and add it there" + ); + } + sections.push((section_name.clone(), content.clone())); + } + + if let Some(target_footnotes) = target_pattern.footnotes.get(target) { + target_pattern_entry.footnotes_used.insert(target.to_owned(), true); + + if !footnotes.is_empty() { + panic!("target {target} is assigned metadata from more than one pattern"); + } + footnotes = target_footnotes.clone(); + } + } + } + + TargetInfoMd { name: target.to_owned(), maintainers, sections, footnotes } +} + +/// Information about a target obtained from rustc. +struct RustcTargetInfo { + target_cfgs: Vec<(String, String)>, + metadata: RustcTargetMetadata, +} + +#[derive(Deserialize)] +struct RustcTargetMetadata { + description: Option, + tier: Option, + host_tools: Option, + std: Option, +} + +/// Get information about a target from rustc. +fn rustc_target_info(rustc: &Path, target: &str) -> RustcTargetInfo { + let cfgs = rustc_stdout(rustc, &["--print", "cfg", "--target", target]); + let target_cfgs = cfgs + .lines() + .filter_map(|line| { + if line.starts_with("target_") { + let Some((key, value)) = line.split_once('=') else { + // For example `unix` + return None; + }; + Some((key.to_owned(), value.to_owned())) + } else { + None + } + }) + .collect(); + + #[derive(Deserialize)] + struct TargetJson { + metadata: RustcTargetMetadata, + } + + let json_spec = rustc_stdout( + rustc, + &["-Zunstable-options", "--print", "target-spec-json", "--target", target], + ); + let spec = serde_json::from_str::(&json_spec) + .expect("parsing --print target-spec-json for metadata"); + + RustcTargetInfo { target_cfgs, metadata: spec.metadata } +} + +fn rustc_stdout(rustc: &Path, args: &[&str]) -> String { + let output = Command::new(rustc).args(args).output().unwrap(); + if !output.status.success() { + panic!( + "rustc failed: {}, {}", + output.status, + String::from_utf8(output.stderr).unwrap_or_default() + ) + } + String::from_utf8(output.stdout).unwrap() +} diff --git a/src/tools/target-docs/src/parse.rs b/src/tools/target-docs/src/parse.rs new file mode 100644 index 0000000000000..4c727af2a44a8 --- /dev/null +++ b/src/tools/target-docs/src/parse.rs @@ -0,0 +1,138 @@ +//! Suboptimal half-markdown parser that's just good-enough for this. + +use eyre::{bail, OptionExt, Result, WrapErr}; +use serde::Deserialize; +use std::{ + collections::HashMap, + fs::DirEntry, + path::{Path, PathBuf}, +}; + +#[derive(Debug)] +pub(crate) struct ParsedTargetInfoFile { + pub(crate) pattern: String, + pub(crate) pattern_glob: glob::Pattern, + pub(crate) maintainers: Vec, + pub(crate) sections: Vec<(String, String)>, + pub(crate) footnotes: HashMap>, + /// Only used for error messages. + pub(crate) full_path: PathBuf, +} + +// IMPORTANT: This is also documented in the README, keep it in sync. +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +struct Frontmatter { + pattern: String, + #[serde(default)] + maintainers: Vec, + #[serde(default)] + footnotes: HashMap>, +} + +pub fn load_target_infos(directory: &Path) -> Result> { + let dir = std::fs::read_dir(directory).unwrap(); + let mut infos = Vec::new(); + + for entry in dir { + let entry = entry?; + infos.push( + load_single_target_info(&entry) + .wrap_err_with(|| format!("loading {}", entry.path().display()))?, + ) + } + + Ok(infos) +} + +fn load_single_target_info(entry: &DirEntry) -> Result { + let pattern = entry.file_name(); + let name = pattern + .to_str() + .ok_or_eyre("file name is invalid utf8")? + .strip_suffix(".md") + .ok_or_eyre("target_info files must end with .md")?; + let content: String = std::fs::read_to_string(entry.path()).wrap_err("reading content")?; + + parse_file(entry.path(), name, &content) +} + +fn parse_file(full_path: PathBuf, file_name: &str, content: &str) -> Result { + let mut frontmatter_splitter = content.split("---\n"); + + let frontmatter = frontmatter_splitter.nth(1).ok_or_eyre("missing frontmatter")?; + + let frontmatter_line_count = frontmatter.lines().count() + 2; // 2 from --- + + let frontmatter = + serde_yaml::from_str::(frontmatter).wrap_err("invalid frontmatter")?; + + let expected_file_name = frontmatter.pattern.replace('*', "_"); + if expected_file_name != file_name { + bail!( + "`target pattern does not match file name. file name must be pattern with `*` replaced with `_`.\n\ + Expected file name `{expected_file_name}.md`" + ); + } + + let body = frontmatter_splitter.next().ok_or_eyre("no body")?; + + let mut sections = Vec::<(String, String)>::new(); + let mut in_codeblock = false; + + for (idx, line) in body.lines().enumerate() { + let number = frontmatter_line_count + idx + 1; // 1 because "line numbers" are off by 1 + + let push_line = |sections: &mut Vec<(String, String)>, line| { + match sections.last_mut() { + Some((_, content)) => { + content.push_str(line); + content.push('\n'); + } + None if line.trim().is_empty() => {} + None => { + bail!("line {number} with content not allowed before the first heading") + } + } + Ok(()) + }; + + if line.starts_with("```") { + in_codeblock ^= true; // toggle + push_line(&mut sections, line)?; + } else if line.starts_with('#') { + if in_codeblock { + push_line(&mut sections, line)?; + } else if let Some(header) = line.strip_prefix("## ") { + if !crate::SECTIONS.contains(&header) { + bail!( + "on line {number}, `{header}` is not an allowed section name, must be one of {:?}", + super::SECTIONS + ); + } + sections.push((header.to_owned(), String::new())); + } else { + bail!("on line {number}, the only allowed headings are `## `: `{line}`"); + } + } else { + push_line(&mut sections, line)?; + } + } + + sections.iter_mut().for_each(|section| section.1 = section.1.trim().to_owned()); + + let pattern_glob = + glob::Pattern::new(&frontmatter.pattern).wrap_err("pattern is not a valid glob pattern")?; + + Ok(ParsedTargetInfoFile { + pattern: frontmatter.pattern, + pattern_glob, + maintainers: frontmatter.maintainers, + sections, + footnotes: frontmatter.footnotes, + full_path, + }) +} + +#[cfg(test)] +mod tests; diff --git a/src/tools/target-docs/src/parse/tests.rs b/src/tools/target-docs/src/parse/tests.rs new file mode 100644 index 0000000000000..b87220efd3f40 --- /dev/null +++ b/src/tools/target-docs/src/parse/tests.rs @@ -0,0 +1,137 @@ +use std::path::PathBuf; + +fn assert_is_err_contains(pat: &str, err: Result) { + let s = err.expect_err("expected error").to_string(); + if !s.contains(pat) { + panic!("error did not contain '{pat}': {s}"); + } +} + +#[test] +fn no_frontmatter() { + let name = "archlinux-unknown-linux-gnu"; // arch linux is an arch, right? + let content = ""; + assert_is_err_contains( + "missing frontmatter", + super::parse_file(PathBuf::from(name), name, content), + ); +} + +#[test] +fn invalid_section() { + let name = "6502-nintendo-nes"; + let content = " +--- +pattern: \"6502-nintendo-nes\" +--- + +## Not A Real Section +"; + + assert_is_err_contains( + "is not an allowed section name", + super::parse_file(PathBuf::from(name), name, content), + ); +} + +#[test] +fn wrong_header() { + let name = "x86_64-known-linux-gnu"; + let content = " +--- +pattern: \"x86_64-known-linux-gnu\" +--- + +# x86_64-known-linux-gnu +"; + + assert_is_err_contains( + "the only allowed headings are `## `", + super::parse_file(PathBuf::from(name), name, content), + ); +} + +#[test] +fn parse_correctly() { + let name = "cat-unknown-linux-gnu"; + let content = r#" +--- +pattern: cat-unknown-linux-gnu +maintainers: ["who maintains the cat?"] +--- +## Requirements + +This target mostly just meows and doesn't do much. + +## Testing + +You can pet the cat and it might respond positively. + +## Cross compilation + +If you're on a dog system, there might be conflicts with the cat, be careful. +But it should be possible. + "#; + + let info = super::parse_file(PathBuf::from(name), name, content).unwrap(); + + assert_eq!(info.maintainers, vec!["who maintains the cat?"]); + assert_eq!(info.pattern, name); + assert_eq!( + info.sections, + vec![ + ( + "Requirements".to_owned(), + "This target mostly just meows and doesn't do much.".to_owned(), + ), + ( + "Testing".to_owned(), + "You can pet the cat and it might respond positively.".to_owned(), + ), + ( + "Cross compilation".to_owned(), + "If you're on a dog system, there might be conflicts with the cat, be careful.\nBut it should be possible.".to_owned(), + ), + ] + ); +} + +#[test] +fn backticks() { + let name = "microservices-unknown-linux-gnu"; // microservices are my favourite architecture + let content = r#" +--- +pattern: "microservices-unknown-linux-gnu" +--- + +## Testing + +```text +# hello world +``` + "#; + + let info = super::parse_file(PathBuf::from(name), name, content).unwrap(); + + assert_eq!(info.pattern, name); + assert_eq!( + info.sections, + vec![("Testing".to_owned(), "```text\n# hello world\n```".to_owned(),),] + ); +} + +#[test] +fn wrong_file_name() { + let name = "6502-nintendo-nes"; + let content = " +--- +pattern: uwu +--- + +## Not A Real Section +"; + assert_is_err_contains( + "target pattern does not match file name", + super::parse_file(PathBuf::from(name), name, content), + ) +} diff --git a/src/tools/target-docs/src/render.rs b/src/tools/target-docs/src/render.rs new file mode 100644 index 0000000000000..ecb18049a2b33 --- /dev/null +++ b/src/tools/target-docs/src/render.rs @@ -0,0 +1,257 @@ +use eyre::{Context, OptionExt, Result}; +use std::{fs, path::Path}; + +use crate::TargetInfo; + +/// Renders a single target markdown file from the information obtained. +pub fn render_target_md(target: &TargetInfo) -> String { + let render_header_option_bool = |bool| match bool { + Some(true) => "Yes", + Some(false) => "No", + None => "?", + }; + + let mut doc = format!( + "# {}\n\n**Tier: {}**\n\n**std: {}**\n\n**host tools: {}**\n\n", + target.name, + match target.metadata.tier { + Some(1) => "1", + Some(2) => "2", + Some(3) => "3", + _ => "UNKNOWN", + }, + render_header_option_bool(target.metadata.std), + render_header_option_bool(target.metadata.host_tools), + ); + + let mut section = |name: &str, content: &str| { + doc.push_str("## "); + doc.push_str(name.trim()); + doc.push('\n'); + doc.push_str(content.trim()); + doc.push_str("\n\n"); + }; + + let maintainers_content = if target.maintainers.is_empty() { + "This target does not have any maintainers!".to_owned() + } else { + format!( + "This target is maintained by:\n{}", + target + .maintainers + .iter() + .map(|maintainer| { + let maintainer = if maintainer.starts_with('@') && !maintainer.contains(' ') { + format!( + "[@{0}](https://github.com/{0})", + maintainer.strip_prefix("@").unwrap() + ) + } else { + maintainer.to_owned() + }; + + format!("- {maintainer}") + }) + .collect::>() + .join("\n") + ) + }; + + section("Maintainers", &maintainers_content); + + for section_name in crate::SECTIONS { + let value = target.sections.iter().find(|(name, _)| name == section_name); + + let section_content = match value { + Some((_, value)) => value.clone(), + None => "Unknown.".to_owned(), + }; + section(section_name, §ion_content); + } + + let cfg_text = target + .target_cfgs + .iter() + .map(|(key, value)| format!("- `{key}` = `{value}`")) + .collect::>() + .join("\n"); + let cfg_content = + format!("This target defines the following target-specific cfg values:\n{cfg_text}\n"); + + section("cfg", &cfg_content); + + doc +} + +/// Replaces inner part of the form +/// `` +/// with replacement`. +fn replace_section(prev_content: &str, section_name: &str, replacement: &str) -> Result { + let magic_summary_start = format!(""); + let magic_summary_end = format!(""); + + let (pre_target, target_and_after) = prev_content + .split_once(&magic_summary_start) + .ok_or_eyre(" not found")?; + + let (_, post_target) = target_and_after + .split_once(&magic_summary_end) + .ok_or_eyre(" not found")?; + + let new = format!("{pre_target}{replacement}{post_target}"); + Ok(new) +} + +/// Renders the non-target files like `SUMMARY.md` that depend on the target. +pub fn render_static(check_only: bool, src_output: &Path, targets: &[TargetInfo]) -> Result<()> { + let targets_file = src_output.join("platform-support").join("targets.md"); + let old_targets = fs::read_to_string(&targets_file).wrap_err("reading summary file")?; + + let target_list = targets + .iter() + .map(|target| format!("- [{0}](platform-support/targets/{0}.md)", target.name)) + .collect::>() + .join("\n"); + + let new_targets = + replace_section(&old_targets, "TARGET", &target_list).wrap_err("replacing targets.md")?; + + if !check_only { + fs::write(targets_file, new_targets).wrap_err("writing targets.md")?; + } + + let platform_support_main = src_output.join("platform-support.md"); + let platform_support_main_old = + fs::read_to_string(&platform_support_main).wrap_err("reading platform-support.md")?; + let platform_support_main_new = + render_platform_support_tables(&platform_support_main_old, targets)?; + + if !check_only { + fs::write(platform_support_main, platform_support_main_new) + .wrap_err("writing platform-support.md")?; + } + + let summary = src_output.join("SUMMARY.md"); + let summary_old = fs::read_to_string(&summary).wrap_err("reading SUMMARY.md")?; + // indent the list + let summary_new = + replace_section(&summary_old, "TARGET_LIST", &target_list.replace("- ", " - ")) + .wrap_err("replacig SUMMARY.md")?; + if !check_only { + fs::write(summary, summary_new).wrap_err("writing SUMAMRY.md")?; + } + + Ok(()) +} + +impl TargetInfo { + fn has_host_tools(&self) -> bool { + self.metadata.host_tools.unwrap_or(false) + } +} + +fn render_platform_support_tables(content: &str, targets: &[TargetInfo]) -> Result { + let replace_table = |content, name, tier_table| -> Result { + let section_string = render_table(targets, tier_table)?; + replace_section(content, name, §ion_string).wrap_err("replacing platform support.md") + }; + + let content = replace_table( + content, + "TIER1HOST", + TierTable { + filter: |target| target.metadata.tier == Some(1), + include_host: false, + include_std: false, + }, + )?; + let content = replace_table( + &content, + "TIER2HOST", + TierTable { + filter: |target| target.metadata.tier == Some(2) && target.has_host_tools(), + include_host: false, + include_std: false, + }, + )?; + let content = replace_table( + &content, + "TIER2", + TierTable { + filter: |target| target.metadata.tier == Some(2) && !target.has_host_tools(), + include_host: false, + include_std: true, + }, + )?; + let content = replace_table( + &content, + "TIER3", + TierTable { + filter: |target| target.metadata.tier == Some(3), + include_host: true, + include_std: true, + }, + )?; + + Ok(content) +} + +fn render_table_option_bool(bool: Option) -> &'static str { + match bool { + Some(true) => "✓", + Some(false) => " ", + None => "?", + } +} + +struct TierTable { + filter: fn(&TargetInfo) -> bool, + include_std: bool, + include_host: bool, +} + +fn render_table(targets: &[TargetInfo], table: TierTable) -> Result { + let mut rows = Vec::new(); + + let targets = targets.iter().filter(|target| (table.filter)(target)); + + for target in targets { + let meta = &target.metadata; + + let mut notes = meta.description.as_deref().unwrap_or("unknown").to_owned(); + + if !target.footnotes.is_empty() { + let footnotes_str = target + .footnotes + .iter() + .map(|footnote| format!("[^{}]", footnote)) + .collect::>() + .join(" "); + + notes = format!("{notes} {footnotes_str}"); + } + + let std = if table.include_std { + let std = render_table_option_bool(meta.std); + format!(" | {std}") + } else { + String::new() + }; + + let host = if table.include_host { + let host = render_table_option_bool(meta.host_tools); + format!(" | {host}") + } else { + String::new() + }; + + rows.push(format!( + "[`{0}`](platform-support/targets/{0}.md){std}{host} | {notes}", + target.name + )); + } + + let result = rows.join("\n"); + + Ok(result) +} From fb94522f2411d50b850ff6daad9300c03b891b2e Mon Sep 17 00:00:00 2001 From: qb42 <42453758+qb42@users.noreply.github.com> Date: Sun, 19 Jul 2026 17:06:41 -0400 Subject: [PATCH 02/11] TODO: serde_yaml deprecation --- src/tools/target-docs/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/target-docs/Cargo.toml b/src/tools/target-docs/Cargo.toml index b4b8209660b65..73e69b0347372 100644 --- a/src/tools/target-docs/Cargo.toml +++ b/src/tools/target-docs/Cargo.toml @@ -11,4 +11,5 @@ eyre = "0.6.12" glob = "0.3.1" serde = { version = "1.0.185", features = ["derive"] } serde_json = "1.0.114" +#FIXME: serde_yaml is unmaintained, find an alternative serde_yaml = "0.9.31" From b57b498c80c9b6d1b69688a2155767e880930d8a Mon Sep 17 00:00:00 2001 From: qb42 <42453758+qb42@users.noreply.github.com> Date: Sun, 19 Jul 2026 19:01:23 -0400 Subject: [PATCH 03/11] Cleanup after rebase --- Cargo.toml | 1 - src/bootstrap/src/core/build_steps/doc.rs | 27 +++++++++---------- src/bootstrap/src/core/builder/tests.rs | 12 +++++++++ src/bootstrap/src/core/session.rs | 17 ++++++++++-- src/doc/rustc/src/platform-support.md | 2 +- src/doc/rustc/src/platform-support/targets.md | 6 ++--- src/tools/target-docs/README.md | 2 +- src/tools/target-docs/src/main.rs | 18 ++++++------- src/tools/target-docs/src/parse.rs | 11 ++++---- src/tools/target-docs/src/render.rs | 16 ++++++----- 10 files changed, 66 insertions(+), 46 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 543fa411a711a..836c6fa74612c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -87,4 +87,3 @@ codegen-units = 1 # If you want to use a crate with local modifications, you can set a path or git dependency here. # For git dependencies, also add your source to ALLOWED_SOURCES in src/tools/tidy/src/extdeps.rs. #[patch.crates-io] - diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 363cf7c7bbedc..2d7108ea02ddd 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -24,7 +24,7 @@ use crate::core::builder::{ use crate::core::compiler::Compiler; use crate::core::config::TargetSelection; use crate::core::session::{FileType, Mode}; -use crate::utils::helpers::{exit_process, submodule_path_of, symlink_dir, t, up_to_date}; +use crate::utils::helpers::{exit_process, fail, submodule_path_of, symlink_dir, t, up_to_date}; macro_rules! book { ($($name:ident, $path:expr, $book_name:expr, $lang:expr ;)+) => { @@ -1672,19 +1672,20 @@ impl CommandLineStep for RustcBook { // (by requiring that the targets match and also that nightly flags can be used) // Using the bootstrap compiler for this doesn't work. if builder.top_stage < 1 { - crate::fail("building the rustc book with stage 0 is not supported, use --stage 1"); + fail("building the rustc book with stage 0 is not supported, use --stage 1"); } - let out_base = builder.md_doc_out(self.target).join("rustc"); + let out_base = builder.out.join(self.target).join("md-doc").join("rustc"); t!(fs::create_dir_all(&out_base)); let out_lints_listing = out_base.join("src/lints"); let out_src_listing = out_base.join("src"); + let rustc = builder.rustc(self.build_compiler); // target-docs will be modifying the files in-place, so we need an actual copy. builder.cp_r(&builder.src.join("src/doc/rustc"), &out_base); + builder.info(&format!("Generating lint docs ({})", self.target)); - let rustc = builder.rustc(self.build_compiler); // The tool runs `rustc` for extracting output examples, so it needs a // functional sysroot. builder.std(self.build_compiler, self.target); @@ -1721,7 +1722,8 @@ impl CommandLineStep for RustcBook { cmd.run(builder); drop(doc_generator_guard); - // Run target-docs generator + builder.info(&format!("Generating target docs ({})", self.target)); + let mut cmd = builder.tool_cmd(Tool::TargetDocs); cmd.arg(builder.src.join("src/doc/rustc/target_infos")); cmd.arg(&out_src_listing); @@ -1729,16 +1731,11 @@ impl CommandLineStep for RustcBook { // For now, we just check that the files are correct but do not generate output. // Let the user override it to TARGET_CHECK_ONLY=0 for testing, but use 1 by default. // See https://github.com/rust-lang/rust/issues/120745 for more info. - cmd.env("TARGET_CHECK_ONLY", std::env::var("TARGET_CHECK_ONLY").unwrap_or("1".to_owned())); - - let doc_generator_guard = builder.msg( - Kind::Run, - self.compiler.stage, - "target-docs", - self.compiler.host, - self.target, - ); - builder.run(&mut cmd); + cmd.env("TARGET_CHECK_ONLY", std::env::var("TARGET_CHECK_ONLY").as_deref().unwrap_or("1")); + + let doc_generator_guard = + builder.msg(Kind::Run, "target-docs", None, self.build_compiler, self.target); + cmd.run(builder); drop(doc_generator_guard); // Run rustbook/mdbook to generate the HTML pages. diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 893fc4f7ee017..880051489cdc2 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -966,6 +966,7 @@ mod snapshot { [doc] rustdoc (book) [doc] rust-by-example (book) [build] rustc 0 -> LintDocs 1 + [build] rustc 0 -> TargetDocs 1 [doc] rustc (book) [doc] cargo (book) [doc] clippy (book) @@ -1030,6 +1031,7 @@ mod snapshot { [doc] rustdoc (book) [doc] rust-by-example (book) [build] rustc 0 -> LintDocs 1 + [build] rustc 0 -> TargetDocs 1 [doc] rustc (book) [doc] cargo (book) [doc] clippy (book) @@ -1112,6 +1114,7 @@ mod snapshot { [doc] rust-by-example (book) [doc] rust-by-example (book) [build] rustc 0 -> LintDocs 1 + [build] rustc 0 -> TargetDocs 1 [doc] rustc (book) [doc] cargo (book) [doc] cargo (book) @@ -1181,6 +1184,7 @@ mod snapshot { [doc] rustdoc (book) [doc] rust-by-example (book) [build] rustc 0 -> LintDocs 1 + [build] rustc 0 -> TargetDocs 1 [doc] rustc (book) [doc] rustc (book) [doc] cargo (book) @@ -1255,6 +1259,7 @@ mod snapshot { [doc] rust-by-example (book) [doc] rust-by-example (book) [build] rustc 0 -> LintDocs 1 + [build] rustc 0 -> TargetDocs 1 [doc] rustc (book) [doc] rustc (book) [doc] cargo (book) @@ -1371,6 +1376,7 @@ mod snapshot { [doc] rustdoc (book) [doc] rust-by-example (book) [build] rustc 0 -> LintDocs 1 + [build] rustc 0 -> TargetDocs 1 [doc] rustc (book) [doc] cargo (book) [doc] clippy (book) @@ -1513,6 +1519,7 @@ mod snapshot { [doc] rustdoc (book) [doc] rust-by-example (book) [build] rustc 0 -> LintDocs 1 + [build] rustc 0 -> TargetDocs 1 [doc] rustc (book) [doc] cargo (book) [doc] clippy (book) @@ -1913,6 +1920,7 @@ mod snapshot { [doc] rustdoc (book) [doc] rust-by-example (book) [build] rustc 0 -> LintDocs 1 + [build] rustc 0 -> TargetDocs 1 [doc] rustc (book) [doc] cargo (book) [doc] clippy (book) @@ -2096,6 +2104,7 @@ mod snapshot { [doc] rustdoc (book) [doc] rust-by-example (book) [build] rustc 0 -> LintDocs 1 + [build] rustc 0 -> TargetDocs 1 [doc] rustc (book) [doc] cargo (book) [doc] clippy (book) @@ -2355,6 +2364,7 @@ mod snapshot { [doc] rustdoc (book) [doc] rust-by-example (book) [build] rustc 0 -> LintDocs 1 + [build] rustc 0 -> TargetDocs 1 [doc] rustc (book) [doc] cargo (book) [doc] clippy (book) @@ -2741,6 +2751,7 @@ mod snapshot { [doc] rustdoc (book) [doc] rust-by-example (book) [build] rustc 0 -> LintDocs 1 + [build] rustc 0 -> TargetDocs 1 [doc] rustc (book) [doc] cargo (book) [doc] clippy (book) @@ -2857,6 +2868,7 @@ mod snapshot { [doc] rustdoc (book) [doc] rust-by-example (book) [build] rustc 0 -> LintDocs 1 + [build] rustc 0 -> TargetDocs 1 [doc] rustc (book) [doc] cargo (book) [doc] clippy (book) diff --git a/src/bootstrap/src/core/session.rs b/src/bootstrap/src/core/session.rs index b796f3b50ab46..dbddeab79e571 100644 --- a/src/bootstrap/src/core/session.rs +++ b/src/bootstrap/src/core/session.rs @@ -1521,8 +1521,20 @@ impl Session { /// Links a file from `src` to `dst`. /// Unlike, [`Build::copy_link`], this makes an actual copy, which is usually not required, /// so `copy_link` should be used instead if possible. - pub fn copy(&self, src: &Path, dst: &Path) { + #[track_caller] + pub fn copy(&self, src: &Path, dst: &Path, file_type: FileType) { self.copy_internal(src, dst, false, false); + + if file_type.could_have_split_debuginfo() + && let Some(dbg_file) = split_debuginfo(src) + { + self.copy_internal( + &dbg_file, + &dst.with_extension(dbg_file.extension().unwrap()), + false, + true, + ); + } } #[track_caller] @@ -1612,6 +1624,7 @@ impl Session { /// when this function is called. /// Unlike, [`Build::cp_link_r`], this makes an actual copy, which is usually not required, /// so `cp_link_r` should be used instead if possible. + #[track_caller] pub fn cp_r(&self, src: &Path, dst: &Path) { if self.config.dry_run() { return; @@ -1625,7 +1638,7 @@ impl Session { self.cp_r(&path, &dst); } else { let _ = fs::remove_file(&dst); - self.copy(&path, &dst); + self.copy(&path, &dst, FileType::Regular); } } } diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 2025402e20b0e..23c1aef2238d1 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -15,7 +15,7 @@ the compiler what kind of output should be produced. Component availability is tracked [here](https://rust-lang.github.io/rustup-components-history/). -Support for targets can vary during their lifespan (a target con be promoted because the support +Support for targets can vary during their lifespan (a target can be promoted because the support improves or can be demoted because lack of resources). Procedures for promoting and demoting targets are documented at our [Forge documentation site][forge]. diff --git a/src/doc/rustc/src/platform-support/targets.md b/src/doc/rustc/src/platform-support/targets.md index 2a34bac74b377..6d2c57364e30a 100644 --- a/src/doc/rustc/src/platform-support/targets.md +++ b/src/doc/rustc/src/platform-support/targets.md @@ -1,7 +1,7 @@ -# List of all targets +# List of all Targets An alphabetical list of all targets. - + - + diff --git a/src/tools/target-docs/README.md b/src/tools/target-docs/README.md index 3a4fa38eda3f7..3a4b425aa4ce7 100644 --- a/src/tools/target-docs/README.md +++ b/src/tools/target-docs/README.md @@ -4,7 +4,7 @@ This tool generates target documentation for all targets in the rustc book. To achieve this, it uses a list of input markdown files provided in `src/doc/rustc/target_infos`. These files follow a strict format. Every file covers a glob pattern of targets. The glob is specified in the `pattern` field in the front matter. -The file name must be this same glob, expect with the `*` replaced with `_` (because of Windows file name support). +The file name must be this same glob, except with the `*` replaced with `_` (because of Windows file name support). For every rustc target, we iterate through all the target infos and find matching globs. When a glob matches, it extracts the h2 markdown sections and saves them for the target. diff --git a/src/tools/target-docs/src/main.rs b/src/tools/target-docs/src/main.rs index 5dbac0f7a2b9e..caa2f2fa87812 100644 --- a/src/tools/target-docs/src/main.rs +++ b/src/tools/target-docs/src/main.rs @@ -1,13 +1,11 @@ mod parse; mod render; -use std::{ - collections::HashMap, - path::{Path, PathBuf}, - process::Command, -}; +use std::collections::HashMap; +use std::path::{Path, PathBuf}; +use std::process::Command; -use eyre::{bail, Context, OptionExt, Result}; +use eyre::{Context, OptionExt, Result, bail}; use parse::ParsedTargetInfoFile; use serde::Deserialize; @@ -44,8 +42,8 @@ fn main() -> Result<()> { "second argument must be path to `src` output directory (build/$target/md-doc/rustc/src)", )?; - println!("Loading target info docs from {input_dir}"); - println!("Writing output to {output_src}"); + eprintln!("Loading target info docs from {input_dir}"); + eprintln!("Writing output to {output_src}"); let targets_to_skip = std::env::var("TARGET_DOCS_SKIP_TARGETS"); let targets_to_skip = @@ -53,7 +51,7 @@ fn main() -> Result<()> { let rustc = PathBuf::from(std::env::var("RUSTC").expect("must pass RUSTC env var pointing to rustc")); - let check_only = std::env::var("TARGET_CHECK_ONLY") == Ok("1".to_owned()); + let check_only = std::env::var("TARGET_CHECK_ONLY").as_deref() == Ok("1"); let targets = rustc_stdout(&rustc, &["--print", "target-list"]); let targets = @@ -87,7 +85,7 @@ fn main() -> Result<()> { }) .collect::>(); - eprintln!("Rendering targets check_only={check_only}"); + eprintln!("Rendering targets, check_only={check_only}"); let targets_dir = Path::new(output_src).join("platform-support").join("targets"); if !check_only { std::fs::create_dir_all(&targets_dir).wrap_err("creating platform-support/targets dir")?; diff --git a/src/tools/target-docs/src/parse.rs b/src/tools/target-docs/src/parse.rs index 4c727af2a44a8..cbd0a8ac19fbe 100644 --- a/src/tools/target-docs/src/parse.rs +++ b/src/tools/target-docs/src/parse.rs @@ -1,12 +1,11 @@ //! Suboptimal half-markdown parser that's just good-enough for this. -use eyre::{bail, OptionExt, Result, WrapErr}; +use std::collections::HashMap; +use std::fs::DirEntry; +use std::path::{Path, PathBuf}; + +use eyre::{OptionExt, Result, WrapErr, bail}; use serde::Deserialize; -use std::{ - collections::HashMap, - fs::DirEntry, - path::{Path, PathBuf}, -}; #[derive(Debug)] pub(crate) struct ParsedTargetInfoFile { diff --git a/src/tools/target-docs/src/render.rs b/src/tools/target-docs/src/render.rs index ecb18049a2b33..d0a454ba50032 100644 --- a/src/tools/target-docs/src/render.rs +++ b/src/tools/target-docs/src/render.rs @@ -1,5 +1,7 @@ +use std::fs; +use std::path::Path; + use eyre::{Context, OptionExt, Result}; -use std::{fs, path::Path}; use crate::TargetInfo; @@ -113,8 +115,8 @@ pub fn render_static(check_only: bool, src_output: &Path, targets: &[TargetInfo] .collect::>() .join("\n"); - let new_targets = - replace_section(&old_targets, "TARGET", &target_list).wrap_err("replacing targets.md")?; + let new_targets = replace_section(&old_targets, "TARGET_LIST", &target_list) + .wrap_err("replacing targets.md")?; if !check_only { fs::write(targets_file, new_targets).wrap_err("writing targets.md")?; @@ -136,9 +138,9 @@ pub fn render_static(check_only: bool, src_output: &Path, targets: &[TargetInfo] // indent the list let summary_new = replace_section(&summary_old, "TARGET_LIST", &target_list.replace("- ", " - ")) - .wrap_err("replacig SUMMARY.md")?; + .wrap_err("replacing SUMMARY.md")?; if !check_only { - fs::write(summary, summary_new).wrap_err("writing SUMAMRY.md")?; + fs::write(summary, summary_new).wrap_err("writing SUMMARY.md")?; } Ok(()) @@ -153,7 +155,7 @@ impl TargetInfo { fn render_platform_support_tables(content: &str, targets: &[TargetInfo]) -> Result { let replace_table = |content, name, tier_table| -> Result { let section_string = render_table(targets, tier_table)?; - replace_section(content, name, §ion_string).wrap_err("replacing platform support.md") + replace_section(content, name, §ion_string).wrap_err("replacing platform-support.md") }; let content = replace_table( @@ -199,7 +201,7 @@ fn render_platform_support_tables(content: &str, targets: &[TargetInfo]) -> Resu fn render_table_option_bool(bool: Option) -> &'static str { match bool { Some(true) => "✓", - Some(false) => " ", + Some(false) => "*", None => "?", } } From 345335d940c74a40d4cd81b71db57271eba61ff5 Mon Sep 17 00:00:00 2001 From: qb42 <42453758+qb42@users.noreply.github.com> Date: Mon, 20 Jul 2026 01:35:19 -0400 Subject: [PATCH 04/11] Generate table headers and empty message --- src/doc/rustc/src/platform-support.md | 20 +++--- src/tools/target-docs/Cargo.toml | 2 +- src/tools/target-docs/src/render.rs | 91 +++++++++++++++++---------- 3 files changed, 72 insertions(+), 41 deletions(-) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 23c1aef2238d1..730d444a2e44a 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -38,6 +38,9 @@ the Target Tier Policy. All tier 1 (with host tools) targets support the full standard library. + + + target | notes -------|------- [`aarch64-apple-darwin`](platform-support/apple-darwin.md) | ARM64 macOS (11.0+, Big Sur+) @@ -47,8 +50,6 @@ target | notes [`x86_64-pc-windows-gnu`](platform-support/windows-gnu.md) | 64-bit MinGW (Windows 10+, Windows Server 2016+) [`x86_64-pc-windows-msvc`](platform-support/windows-msvc.md) | 64-bit MSVC (Windows 10+, Windows Server 2016+) `x86_64-unknown-linux-gnu` | 64-bit Linux (kernel 3.2+, glibc 2.17+) - - [^x86_32-floats-return-ABI]: Due to limitations of the C ABI, floating-point support on `i686` targets is non-compliant: floating-point return values are passed via an x87 register, so NaN payload bits can be lost. Functions with the default Rust ABI are not affected. See [issue #115567][x86-32-float-return-issue]. @@ -57,9 +58,12 @@ target | notes ## Tier 1 without Host Tools + + target | notes -------|------- [`i686-pc-windows-msvc`](platform-support/windows-msvc.md) | 32-bit MSVC (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI] [^win32-msvc-alignment] + [^win32-msvc-alignment]: Due to non-standard behavior of MSVC, native C code on this target can cause types with an alignment of more than 4 bytes to be incorrectly aligned to only 4 bytes (this affects, e.g., `u64` and `i64`). Rust applies some mitigations to reduce the impact of this issue, but this can still cause unsoundness due to unsafe code that (correctly) assumes that references are always properly aligned. See [issue #112480](https://github.com/rust-lang/rust/issues/112480). @@ -92,6 +96,8 @@ All tier 2 targets with host tools support the full standard library. **NOTE:** The `rust-docs` component is not usually built for tier 2 targets, so Rustup may install the documentation for a similar tier 1 target instead. + + target | notes -------|------- [`aarch64-pc-windows-gnullvm`](platform-support/windows-gnullvm.md) | ARM64 MinGW (Windows 10+), LLVM ABI @@ -121,8 +127,6 @@ target | notes [`x86_64-unknown-netbsd`](platform-support/netbsd.md) | NetBSD/amd64 [`x86_64-pc-solaris`](platform-support/solaris.md) | 64-bit x86 Solaris 11.4 [`sparcv9-sun-solaris`](platform-support/solaris.md) | SPARC V9 Solaris 11.4 - - ## Tier 2 without Host Tools @@ -153,6 +157,8 @@ details on the review practices for standard library code. **NOTE:** The `rust-docs` component is not usually built for tier 2 targets, so Rustup may install the documentation for a similar tier 1 target instead. + + target | std | notes -------|:---:|------- [`aarch64-apple-ios`](platform-support/apple-ios.md) | ✓ | ARM64 iOS @@ -240,8 +246,6 @@ target | std | notes [`x86_64-unknown-none`](platform-support/x86_64-unknown-none.md) | * | Freestanding/bare-metal x86_64, softfloat [`x86_64-unknown-redox`](platform-support/redox.md) | ✓ | Redox OS [`x86_64-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 64-bit UEFI - - [^x86_32-floats-x87]: Floating-point support on `i586` targets is non-compliant: the `x87` registers and instructions used for these targets do not provide IEEE-754-compliant behavior, in particular when it comes to rounding and NaN payload bits. See [issue #114479][x86-32-float-issue]. @@ -275,6 +279,8 @@ details on the review practices for standard library code. The `host` column indicates whether the codebase includes support for building host tools. + + target | std | host | notes -------|:---:|:----:|------- [`aarch64-kmc-solid_asp3`](platform-support/kmc-solid.md) | ✓ | | ARM64 SOLID with TOPPERS/ASP3 @@ -491,8 +497,6 @@ target | std | host | notes [`xtensa-esp32s2-none-elf`](platform-support/xtensa.md) | * | | Xtensa ESP32-S2 [`xtensa-esp32s3-espidf`](platform-support/esp-idf.md) | ✓ | | Xtensa ESP32-S3 [`xtensa-esp32s3-none-elf`](platform-support/xtensa.md) | * | | Xtensa ESP32-S3 - - [runs on NVIDIA GPUs]: https://github.com/japaric-archived/nvptx#targets diff --git a/src/tools/target-docs/Cargo.toml b/src/tools/target-docs/Cargo.toml index 73e69b0347372..9535b82b4651e 100644 --- a/src/tools/target-docs/Cargo.toml +++ b/src/tools/target-docs/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "target-docs" version = "0.1.0" -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/src/tools/target-docs/src/render.rs b/src/tools/target-docs/src/render.rs index d0a454ba50032..a6b3b4316ec2d 100644 --- a/src/tools/target-docs/src/render.rs +++ b/src/tools/target-docs/src/render.rs @@ -1,3 +1,4 @@ +use std::fmt::Write; use std::fs; use std::path::Path; @@ -162,9 +163,22 @@ fn render_platform_support_tables(content: &str, targets: &[TargetInfo]) -> Resu content, "TIER1HOST", TierTable { - filter: |target| target.metadata.tier == Some(1), + filter: |target| target.metadata.tier == Some(1) && target.has_host_tools(), include_host: false, include_std: false, + empty_msg: None, + }, + )?; + let content = replace_table( + &content, + "TIER1", + TierTable { + filter: |target| target.metadata.tier == Some(1) && !target.has_host_tools(), + include_host: false, + include_std: true, + empty_msg: Some( + "At this time, all Tier 1 targets are [Tier 1 with Host Tools](#tier-1-with-host-tools).", + ), }, )?; let content = replace_table( @@ -174,6 +188,7 @@ fn render_platform_support_tables(content: &str, targets: &[TargetInfo]) -> Resu filter: |target| target.metadata.tier == Some(2) && target.has_host_tools(), include_host: false, include_std: false, + empty_msg: None, }, )?; let content = replace_table( @@ -183,6 +198,7 @@ fn render_platform_support_tables(content: &str, targets: &[TargetInfo]) -> Resu filter: |target| target.metadata.tier == Some(2) && !target.has_host_tools(), include_host: false, include_std: true, + empty_msg: None, }, )?; let content = replace_table( @@ -192,6 +208,7 @@ fn render_platform_support_tables(content: &str, targets: &[TargetInfo]) -> Resu filter: |target| target.metadata.tier == Some(3), include_host: true, include_std: true, + empty_msg: None, }, )?; @@ -210,50 +227,60 @@ struct TierTable { filter: fn(&TargetInfo) -> bool, include_std: bool, include_host: bool, + empty_msg: Option<&'static str>, } fn render_table(targets: &[TargetInfo], table: TierTable) -> Result { - let mut rows = Vec::new(); + let mut count = 0; + let mut out = String::with_capacity(1024); - let targets = targets.iter().filter(|target| (table.filter)(target)); + let mut row2 = String::with_capacity(32); - for target in targets { - let meta = &target.metadata; + out.push_str("target"); + row2.push_str("-------"); - let mut notes = meta.description.as_deref().unwrap_or("unknown").to_owned(); + if table.include_std { + out.push_str(" | std"); + row2.push_str("|:---:"); + } - if !target.footnotes.is_empty() { - let footnotes_str = target - .footnotes - .iter() - .map(|footnote| format!("[^{}]", footnote)) - .collect::>() - .join(" "); + if table.include_host { + out.push_str(" | host"); + row2.push_str("|:----:"); + } + + out.push_str(" | notes\n"); + row2.push_str("|-------"); + out.push_str(&row2); + drop(row2); + + for target in targets.iter().filter(|target| (table.filter)(target)) { + count += 1; + let meta = &target.metadata; + + write!(out, "\n[`{0}`](platform-support/targets/{0}.md)", target.name)?; - notes = format!("{notes} {footnotes_str}"); + if table.include_std { + write!(out, " | {}", render_table_option_bool(meta.std))?; } - let std = if table.include_std { - let std = render_table_option_bool(meta.std); - format!(" | {std}") - } else { - String::new() - }; + if table.include_host { + write!(out, " | {}", render_table_option_bool(meta.host_tools))?; + } - let host = if table.include_host { - let host = render_table_option_bool(meta.host_tools); - format!(" | {host}") - } else { - String::new() - }; + write!(out, " | {}", meta.description.as_deref().unwrap_or_default())?; - rows.push(format!( - "[`{0}`](platform-support/targets/{0}.md){std}{host} | {notes}", - target.name - )); + for footnote in &target.footnotes { + write!(out, " [^{}]", footnote)?; + } } - let result = rows.join("\n"); + if count == 0 + && let Some(msg) = table.empty_msg + { + out.clear(); + out.push_str(msg); + } - Ok(result) + Ok(out) } From cc45a87454d6979a05904fecef260de563ec4efd Mon Sep 17 00:00:00 2001 From: qb42 <42453758+qb42@users.noreply.github.com> Date: Mon, 20 Jul 2026 13:53:17 -0400 Subject: [PATCH 05/11] render_static refactor --- src/tools/target-docs/src/render.rs | 70 ++++++++++++++++------------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/src/tools/target-docs/src/render.rs b/src/tools/target-docs/src/render.rs index a6b3b4316ec2d..f82d9b0ec1783 100644 --- a/src/tools/target-docs/src/render.rs +++ b/src/tools/target-docs/src/render.rs @@ -2,7 +2,7 @@ use std::fmt::Write; use std::fs; use std::path::Path; -use eyre::{Context, OptionExt, Result}; +use eyre::{Context, Result, eyre}; use crate::TargetInfo; @@ -95,54 +95,62 @@ fn replace_section(prev_content: &str, section_name: &str, replacement: &str) -> let (pre_target, target_and_after) = prev_content .split_once(&magic_summary_start) - .ok_or_eyre(" not found")?; + .ok_or_else(|| eyre!("{magic_summary_start} not found"))?; let (_, post_target) = target_and_after .split_once(&magic_summary_end) - .ok_or_eyre(" not found")?; + .ok_or_else(|| eyre!("{magic_summary_end} not found"))?; + //let new = format!("{pre_target}{magic_summary_start}\n\n{replacement}\n{magic_summary_end}{post_target}"); let new = format!("{pre_target}{replacement}{post_target}"); Ok(new) } +fn replace_file, F: FnOnce(&str) -> Result>( + check_only: bool, + src_output: &Path, + file: P, + replace_fn: F, +) -> Result<()> { + let rel_path = file.as_ref(); + let filepath = src_output.join(rel_path); + + let old_content = + fs::read_to_string(&filepath).wrap_err_with(|| eyre!("reading {:?}", rel_path))?; + let new_content = + replace_fn(&old_content).wrap_err_with(|| eyre!("replacing {:?}", rel_path))?; + + /*if new_content != old_content { + eprintln!("{}: out of sync"); + }*/ + + if !check_only { + fs::write(filepath, new_content).wrap_err_with(|| eyre!("writing {:?}", rel_path))?; + } + + Ok(()) +} + /// Renders the non-target files like `SUMMARY.md` that depend on the target. pub fn render_static(check_only: bool, src_output: &Path, targets: &[TargetInfo]) -> Result<()> { - let targets_file = src_output.join("platform-support").join("targets.md"); - let old_targets = fs::read_to_string(&targets_file).wrap_err("reading summary file")?; - let target_list = targets .iter() .map(|target| format!("- [{0}](platform-support/targets/{0}.md)", target.name)) .collect::>() .join("\n"); - let new_targets = replace_section(&old_targets, "TARGET_LIST", &target_list) - .wrap_err("replacing targets.md")?; + replace_file(check_only, src_output, "platform-support/targets.md", |content| { + replace_section(content, "TARGET_LIST", &target_list) + })?; - if !check_only { - fs::write(targets_file, new_targets).wrap_err("writing targets.md")?; - } + replace_file(check_only, src_output, "platform-support.md", |content| { + render_platform_support_tables(content, targets) + })?; - let platform_support_main = src_output.join("platform-support.md"); - let platform_support_main_old = - fs::read_to_string(&platform_support_main).wrap_err("reading platform-support.md")?; - let platform_support_main_new = - render_platform_support_tables(&platform_support_main_old, targets)?; - - if !check_only { - fs::write(platform_support_main, platform_support_main_new) - .wrap_err("writing platform-support.md")?; - } - - let summary = src_output.join("SUMMARY.md"); - let summary_old = fs::read_to_string(&summary).wrap_err("reading SUMMARY.md")?; - // indent the list - let summary_new = - replace_section(&summary_old, "TARGET_LIST", &target_list.replace("- ", " - ")) - .wrap_err("replacing SUMMARY.md")?; - if !check_only { - fs::write(summary, summary_new).wrap_err("writing SUMMARY.md")?; - } + replace_file(check_only, src_output, "SUMMARY.md", |content| { + // indent the list + replace_section(content, "TARGET_LIST", &target_list.replace("- ", " - ")) + })?; Ok(()) } From ff7125772a0cd9a1612601b5cd8b5a443a2e36b6 Mon Sep 17 00:00:00 2001 From: qb42 <42453758+qb42@users.noreply.github.com> Date: Mon, 20 Jul 2026 14:11:40 -0400 Subject: [PATCH 06/11] Track unmaintained targets --- src/doc/rustc/src/SUMMARY.md | 1 + .../rustc/src/platform-support/unmaintained-targets.md | 9 +++++++++ src/tools/target-docs/src/render.rs | 10 ++++++++++ 3 files changed, 20 insertions(+) create mode 100644 src/doc/rustc/src/platform-support/unmaintained-targets.md diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 1bec8f230ff4b..35cfa6703a4ef 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -169,3 +169,4 @@ + diff --git a/src/doc/rustc/src/platform-support/unmaintained-targets.md b/src/doc/rustc/src/platform-support/unmaintained-targets.md new file mode 100644 index 0000000000000..54cedc9481090 --- /dev/null +++ b/src/doc/rustc/src/platform-support/unmaintained-targets.md @@ -0,0 +1,9 @@ +# Currently Unmaintained Targets + +A list of all targets that do not currently have a maintainer on record. +If this is empty, that's a good thing. If you see a target on this list +that you're familiar with, please consider helping out. + + + + diff --git a/src/tools/target-docs/src/render.rs b/src/tools/target-docs/src/render.rs index f82d9b0ec1783..1b1fc6bec5a86 100644 --- a/src/tools/target-docs/src/render.rs +++ b/src/tools/target-docs/src/render.rs @@ -143,6 +143,16 @@ pub fn render_static(check_only: bool, src_output: &Path, targets: &[TargetInfo] replace_section(content, "TARGET_LIST", &target_list) })?; + let unmaintained_target_list = targets + .iter() + .filter(|target| target.maintainers.is_empty()) + .map(|target| format!("- [{0}](platform-support/targets/{0}.md)", target.name)) + .collect::>() + .join("\n"); + replace_file(check_only, src_output, "platform-support/unmaintained-targets.md", |content| { + replace_section(content, "TARGET_LIST", &unmaintained_target_list) + })?; + replace_file(check_only, src_output, "platform-support.md", |content| { render_platform_support_tables(content, targets) })?; From 6e86bc004cd41905755e5ec61a2b58eb69673da9 Mon Sep 17 00:00:00 2001 From: qb42 <42453758+qb42@users.noreply.github.com> Date: Mon, 20 Jul 2026 14:13:31 -0400 Subject: [PATCH 07/11] Use old target info --- src/tools/target-docs/src/main.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/tools/target-docs/src/main.rs b/src/tools/target-docs/src/main.rs index caa2f2fa87812..dc4e575cbe744 100644 --- a/src/tools/target-docs/src/main.rs +++ b/src/tools/target-docs/src/main.rs @@ -87,11 +87,23 @@ fn main() -> Result<()> { eprintln!("Rendering targets, check_only={check_only}"); let targets_dir = Path::new(output_src).join("platform-support").join("targets"); + let old_targets_dir = Path::new(output_src).join("platform-support"); if !check_only { std::fs::create_dir_all(&targets_dir).wrap_err("creating platform-support/targets dir")?; } for info in &targets { - let doc = render::render_target_md(info); + let mut doc = render::render_target_md(info); + + //FIXME: This is temporary during migration + if info.maintainers.is_empty() && info.sections.is_empty() { + let old_path = old_targets_dir.join(format!("{}.md", info.name)); + if old_path.is_file() { + doc = std::fs::read_to_string(old_path)?; + //eprintln!("WARN: {}: using legacy target info", info.name); + } /*else { + eprintln!("WARN: {}: no target info", info.name); + }*/ + } if !check_only { std::fs::write(targets_dir.join(format!("{}.md", info.name)), doc) From 148f9ad1dc522e3e2775b255648364421de182d9 Mon Sep 17 00:00:00 2001 From: qb42 <42453758+qb42@users.noreply.github.com> Date: Mon, 20 Jul 2026 18:34:33 -0400 Subject: [PATCH 08/11] Switch to all-target-specs-json --- src/bootstrap/src/core/build_steps/doc.rs | 2 + src/tools/target-docs/src/main.rs | 114 ++++++++++++---------- 2 files changed, 64 insertions(+), 52 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 2d7108ea02ddd..ce0ade9c5d05d 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -1732,6 +1732,8 @@ impl CommandLineStep for RustcBook { // Let the user override it to TARGET_CHECK_ONLY=0 for testing, but use 1 by default. // See https://github.com/rust-lang/rust/issues/120745 for more info. cmd.env("TARGET_CHECK_ONLY", std::env::var("TARGET_CHECK_ONLY").as_deref().unwrap_or("1")); + // target-docs uses -Zunstable-options --print all-target-specs-json + cmd.env("RUSTC_BOOTSTRAP", "1"); let doc_generator_guard = builder.msg(Kind::Run, "target-docs", None, self.build_compiler, self.target); diff --git a/src/tools/target-docs/src/main.rs b/src/tools/target-docs/src/main.rs index dc4e575cbe744..0b2149c6f7783 100644 --- a/src/tools/target-docs/src/main.rs +++ b/src/tools/target-docs/src/main.rs @@ -49,14 +49,15 @@ fn main() -> Result<()> { let targets_to_skip = targets_to_skip.as_deref().map(|s| s.split(",").collect::>()).unwrap_or_default(); - let rustc = - PathBuf::from(std::env::var("RUSTC").expect("must pass RUSTC env var pointing to rustc")); + let rustc = PathBuf::from( + std::env::var_os("RUSTC").ok_or_eyre("must pass RUSTC env var pointing to rustc")?, + ); let check_only = std::env::var("TARGET_CHECK_ONLY").as_deref() == Ok("1"); - let targets = rustc_stdout(&rustc, &["--print", "target-list"]); - let targets = - targets.lines().filter(|target| !targets_to_skip.contains(target)).collect::>(); + eprintln!("Collecting rustc target information"); + let rustc_targets = rustc_target_info(&rustc, &targets_to_skip)?; + eprintln!("Collecting target_infos"); let mut info_patterns = parse::load_target_infos(Path::new(input_dir)) .wrap_err("failed loading target_info")? .into_iter() @@ -67,21 +68,18 @@ fn main() -> Result<()> { }) .collect::>(); - eprintln!("Collecting rustc information"); - let rustc_infos = - targets.iter().map(|target| rustc_target_info(&rustc, target)).collect::>(); - - let targets = targets + let targets = rustc_targets .into_iter() - .map(|target| target_doc_info(&mut info_patterns, target)) - .zip(rustc_infos) - .map(|(md, rustc)| TargetInfo { - name: md.name, - maintainers: md.maintainers, - sections: md.sections, - footnotes: md.footnotes, - target_cfgs: rustc.target_cfgs, - metadata: rustc.metadata, + .map(|rustc| { + let md = target_doc_info(&mut info_patterns, &rustc.name); + TargetInfo { + name: rustc.name, + maintainers: md.maintainers, + sections: md.sections, + footnotes: md.footnotes, + target_cfgs: rustc.target_cfgs, + metadata: rustc.metadata, + } }) .collect::>(); @@ -147,7 +145,6 @@ struct TargetPatternEntry { /// Information about a target obtained from the target_info markdown file. struct TargetInfoMd { - name: String, maintainers: Vec, sections: Vec<(String, String)>, footnotes: Vec, @@ -186,11 +183,12 @@ fn target_doc_info(info_patterns: &mut [TargetPatternEntry], target: &str) -> Ta } } - TargetInfoMd { name: target.to_owned(), maintainers, sections, footnotes } + TargetInfoMd { maintainers, sections, footnotes } } /// Information about a target obtained from rustc. struct RustcTargetInfo { + name: String, target_cfgs: Vec<(String, String)>, metadata: RustcTargetMetadata, } @@ -203,47 +201,59 @@ struct RustcTargetMetadata { std: Option, } -/// Get information about a target from rustc. -fn rustc_target_info(rustc: &Path, target: &str) -> RustcTargetInfo { - let cfgs = rustc_stdout(rustc, &["--print", "cfg", "--target", target]); - let target_cfgs = cfgs - .lines() - .filter_map(|line| { - if line.starts_with("target_") { - let Some((key, value)) = line.split_once('=') else { - // For example `unix` - return None; - }; - Some((key.to_owned(), value.to_owned())) - } else { - None - } - }) - .collect(); - +/// Get information about targets from rustc. +fn rustc_target_info(rustc: &Path, targets_to_skip: &[&str]) -> Result> { #[derive(Deserialize)] struct TargetJson { metadata: RustcTargetMetadata, } - let json_spec = rustc_stdout( - rustc, - &["-Zunstable-options", "--print", "target-spec-json", "--target", target], - ); - let spec = serde_json::from_str::(&json_spec) - .expect("parsing --print target-spec-json for metadata"); + let json_specs = + rustc_stdout(rustc, &["-Zunstable-options", "--print", "all-target-specs-json"])?; + let specs = serde_json::from_str::>(&json_specs) + .wrap_err("parsing --print all-target-specs-json for metadata")?; + + let mut rustc_targets = Vec::with_capacity(specs.len()); + + for (target, spec) in specs { + if targets_to_skip.contains(&&*target) { + continue; + } + + let cfgs = rustc_stdout(rustc, &["--print", "cfg", "--target", &target]) + .wrap_err_with(|| format!("failed to get target cfgs for {target}"))?; + let target_cfgs = cfgs + .lines() + .filter_map(|line| { + if line.starts_with("target_") { + let Some((key, value)) = line.split_once('=') else { + // For example `unix` + return None; + }; + Some((key.to_owned(), value.to_owned())) + } else { + None + } + }) + .collect(); + + rustc_targets.push(RustcTargetInfo { name: target, target_cfgs, metadata: spec.metadata }); + } + + rustc_targets.sort_unstable_by(|a, b| a.name.cmp(&b.name)); - RustcTargetInfo { target_cfgs, metadata: spec.metadata } + Ok(rustc_targets) } -fn rustc_stdout(rustc: &Path, args: &[&str]) -> String { - let output = Command::new(rustc).args(args).output().unwrap(); - if !output.status.success() { - panic!( +fn rustc_stdout(rustc: &Path, args: &[&str]) -> Result { + let output = Command::new(rustc).args(args).output()?; + if output.status.success() { + Ok(String::from_utf8(output.stdout)?) + } else { + bail!( "rustc failed: {}, {}", output.status, String::from_utf8(output.stderr).unwrap_or_default() - ) + ); } - String::from_utf8(output.stdout).unwrap() } From 2c257f2a14c092333a792d5e94784d39e9389a5d Mon Sep 17 00:00:00 2001 From: qb42 <42453758+qb42@users.noreply.github.com> Date: Thu, 8 Oct 2026 18:07:36 -0400 Subject: [PATCH 09/11] TEMP --- src/tools/target-docs/src/main.rs | 8 +++++++- src/tools/target-docs/src/render.rs | 12 +++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/tools/target-docs/src/main.rs b/src/tools/target-docs/src/main.rs index 0b2149c6f7783..ddd4a1cd8ab42 100644 --- a/src/tools/target-docs/src/main.rs +++ b/src/tools/target-docs/src/main.rs @@ -99,7 +99,7 @@ fn main() -> Result<()> { doc = std::fs::read_to_string(old_path)?; //eprintln!("WARN: {}: using legacy target info", info.name); } /*else { - eprintln!("WARN: {}: no target info", info.name); + eprintln!("WARN: {}: no target info", info.name); }*/ } @@ -137,6 +137,12 @@ fn main() -> Result<()> { Ok(()) } +impl TargetInfo { + fn doc_path(&self) -> Option { + Some(format!("platform-support/targets/{}.md", self.name)) + } +} + struct TargetPatternEntry { info: ParsedTargetInfoFile, used: bool, diff --git a/src/tools/target-docs/src/render.rs b/src/tools/target-docs/src/render.rs index 1b1fc6bec5a86..937ec2c68d53d 100644 --- a/src/tools/target-docs/src/render.rs +++ b/src/tools/target-docs/src/render.rs @@ -101,8 +101,10 @@ fn replace_section(prev_content: &str, section_name: &str, replacement: &str) -> .split_once(&magic_summary_end) .ok_or_else(|| eyre!("{magic_summary_end} not found"))?; - //let new = format!("{pre_target}{magic_summary_start}\n\n{replacement}\n{magic_summary_end}{post_target}"); - let new = format!("{pre_target}{replacement}{post_target}"); + let new = format!( + "{pre_target}{magic_summary_start}\n\n{replacement}\n{magic_summary_end}{post_target}" + ); + //let new = format!("{pre_target}{replacement}{post_target}"); Ok(new) } @@ -276,7 +278,11 @@ fn render_table(targets: &[TargetInfo], table: TierTable) -> Result { count += 1; let meta = &target.metadata; - write!(out, "\n[`{0}`](platform-support/targets/{0}.md)", target.name)?; + if let Some(doc_path) = target.doc_path() { + write!(out, "\n[`{}`]({})", target.name, doc_path)?; + } else { + write!(out, "\n`{}`", target.name)?; + } if table.include_std { write!(out, " | {}", render_table_option_bool(meta.std))?; From d348dbb899b49f376321dca3e2169a98381e243b Mon Sep 17 00:00:00 2001 From: qb42 <42453758+qb42@users.noreply.github.com> Date: Fri, 9 Oct 2026 16:44:21 -0400 Subject: [PATCH 10/11] Skip csky in test-*-gnu-llvm-22 Dockerfiles --- src/ci/docker/host-aarch64/test-aarch64-gnu-llvm-22/Dockerfile | 3 +++ src/ci/docker/host-x86_64/test-x86_64-gnu-llvm-22/Dockerfile | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/ci/docker/host-aarch64/test-aarch64-gnu-llvm-22/Dockerfile b/src/ci/docker/host-aarch64/test-aarch64-gnu-llvm-22/Dockerfile index 53030a098841d..dcecc9c456733 100644 --- a/src/ci/docker/host-aarch64/test-aarch64-gnu-llvm-22/Dockerfile +++ b/src/ci/docker/host-aarch64/test-aarch64-gnu-llvm-22/Dockerfile @@ -42,6 +42,9 @@ RUN sh /scripts/sccache.sh ENV NO_DOWNLOAD_CI_LLVM="1" ENV EXTERNAL_LLVM="1" +# Ubuntu LLVM 22 does not have support for experimental targets like csky. +ENV TARGET_DOCS_SKIP_TARGETS="csky-unknown-linux-gnuabiv2,csky-unknown-linux-gnuabiv2hf" + # Override the default debugger `discover` behavior ENV LLDB_PATH="lldb-22" diff --git a/src/ci/docker/host-x86_64/test-x86_64-gnu-llvm-22/Dockerfile b/src/ci/docker/host-x86_64/test-x86_64-gnu-llvm-22/Dockerfile index 3552f4bc30e53..653dd7552cd6e 100644 --- a/src/ci/docker/host-x86_64/test-x86_64-gnu-llvm-22/Dockerfile +++ b/src/ci/docker/host-x86_64/test-x86_64-gnu-llvm-22/Dockerfile @@ -46,6 +46,9 @@ RUN sh /scripts/sccache.sh ENV NO_DOWNLOAD_CI_LLVM="1" ENV EXTERNAL_LLVM="1" +# Ubuntu LLVM 22 does not have support for experimental targets like csky. +ENV TARGET_DOCS_SKIP_TARGETS="csky-unknown-linux-gnuabiv2,csky-unknown-linux-gnuabiv2hf" + # Using llvm-link-shared due to libffi issues -- see #34486 ENV RUST_CONFIGURE_ARGS="--build=x86_64-unknown-linux-gnu \ --llvm-root=/usr/lib/llvm-22 \ From ac2c540d10a3df0ced1448e8752f4337676ffc99 Mon Sep 17 00:00:00 2001 From: qb42 <42453758+qb42@users.noreply.github.com> Date: Sat, 10 Oct 2026 16:40:11 -0400 Subject: [PATCH 11/11] default check_only=false --- src/bootstrap/src/core/build_steps/doc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index ce0ade9c5d05d..f3ef73090b5e6 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -1731,7 +1731,7 @@ impl CommandLineStep for RustcBook { // For now, we just check that the files are correct but do not generate output. // Let the user override it to TARGET_CHECK_ONLY=0 for testing, but use 1 by default. // See https://github.com/rust-lang/rust/issues/120745 for more info. - cmd.env("TARGET_CHECK_ONLY", std::env::var("TARGET_CHECK_ONLY").as_deref().unwrap_or("1")); + //cmd.env("TARGET_CHECK_ONLY", std::env::var("TARGET_CHECK_ONLY").as_deref().unwrap_or("1")); // target-docs uses -Zunstable-options --print all-target-specs-json cmd.env("RUSTC_BOOTSTRAP", "1");