From 5df2bf61e8660e9f5b1a77cebfabd97cc0172c4f Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Tue, 2 Dec 2025 23:02:54 -0500 Subject: [PATCH 1/6] refactor(fingerprint): rename to RustdocFingerprint --- src/cargo/core/compiler/build_runner/mod.rs | 7 +++---- src/cargo/core/compiler/fingerprint/mod.rs | 2 +- src/cargo/core/compiler/fingerprint/rustdoc.rs | 10 +++++----- src/cargo/core/compiler/mod.rs | 2 +- tests/testsuite/doc.rs | 13 +++++++------ 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/cargo/core/compiler/build_runner/mod.rs b/src/cargo/core/compiler/build_runner/mod.rs index 9d0533f7fce..405957909f1 100644 --- a/src/cargo/core/compiler/build_runner/mod.rs +++ b/src/cargo/core/compiler/build_runner/mod.rs @@ -16,15 +16,14 @@ use filetime::FileTime; use itertools::Itertools; use jobserver::Client; +use super::RustdocFingerprint; use super::custom_build::{self, BuildDeps, BuildScriptOutputs, BuildScripts}; use super::fingerprint::{Checksum, Fingerprint}; use super::job_queue::JobQueue; use super::layout::Layout; use super::lto::Lto; use super::unit_graph::UnitDep; -use super::{ - BuildContext, Compilation, CompileKind, CompileMode, Executor, FileFlavor, RustDocFingerprint, -}; +use super::{BuildContext, Compilation, CompileKind, CompileMode, Executor, FileFlavor}; mod compilation_files; use self::compilation_files::CompilationFiles; @@ -178,7 +177,7 @@ impl<'a, 'gctx> BuildRunner<'a, 'gctx> { // they were compiled with the same Rustc version that we're currently using. // See the function doc comment for more. if self.bcx.build_config.intent.is_doc() { - RustDocFingerprint::check_rustdoc_fingerprint(&self)? + RustdocFingerprint::check_rustdoc_fingerprint(&self)? } for unit in &self.bcx.roots { diff --git a/src/cargo/core/compiler/fingerprint/mod.rs b/src/cargo/core/compiler/fingerprint/mod.rs index 428743b74cf..cd48d4df82a 100644 --- a/src/cargo/core/compiler/fingerprint/mod.rs +++ b/src/cargo/core/compiler/fingerprint/mod.rs @@ -410,7 +410,7 @@ pub use self::dep_info::parse_dep_info; pub use self::dep_info::parse_rustc_dep_info; pub use self::dep_info::translate_dep_info; pub use self::dirty_reason::DirtyReason; -pub use self::rustdoc::RustDocFingerprint; +pub use self::rustdoc::RustdocFingerprint; /// Determines if a [`Unit`] is up-to-date, and if not prepares necessary work to /// update the persisted fingerprint. diff --git a/src/cargo/core/compiler/fingerprint/rustdoc.rs b/src/cargo/core/compiler/fingerprint/rustdoc.rs index 0bb9a16b636..33d56e4a91a 100644 --- a/src/cargo/core/compiler/fingerprint/rustdoc.rs +++ b/src/cargo/core/compiler/fingerprint/rustdoc.rs @@ -21,12 +21,12 @@ use crate::core::compiler::CompileKind; /// they were compiled with the same Rustc version that we're currently using. /// Otherwise we must remove the `doc/` folder and compile again forcing a rebuild. #[derive(Debug, Serialize, Deserialize)] -pub struct RustDocFingerprint { +pub struct RustdocFingerprint { /// `rustc -vV` verbose version output. pub rustc_vv: String, } -impl RustDocFingerprint { +impl RustdocFingerprint { /// Checks whether the latest version of rustc used to compile this workspace's docs /// was the same as the one is currently being used in this `cargo doc` call. /// @@ -51,7 +51,7 @@ impl RustDocFingerprint { { return Ok(()); } - let new_fingerprint = RustDocFingerprint { + let new_fingerprint = RustdocFingerprint { rustc_vv: build_runner.bcx.rustc().verbose_version.clone(), }; @@ -66,7 +66,7 @@ impl RustDocFingerprint { /// Checks rustdoc fingerprint file for a given [`CompileKind`]. fn check_fingerprint( build_runner: &BuildRunner<'_, '_>, - new_fingerprint: &RustDocFingerprint, + new_fingerprint: &RustdocFingerprint, kind: CompileKind, ) -> CargoResult<()> { let fingerprint_path = build_runner @@ -88,7 +88,7 @@ fn check_fingerprint( return write_fingerprint(); }; - match serde_json::from_str::(&rustdoc_data) { + match serde_json::from_str::(&rustdoc_data) { Ok(on_disk_fingerprint) => { if on_disk_fingerprint.rustc_vv == new_fingerprint.rustc_vv { return Ok(()); diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs index 62eb3df4d63..2e586a532a7 100644 --- a/src/cargo/core/compiler/mod.rs +++ b/src/cargo/core/compiler/mod.rs @@ -84,7 +84,7 @@ pub use self::crate_type::CrateType; pub use self::custom_build::LinkArgTarget; pub use self::custom_build::{BuildOutput, BuildScriptOutputs, BuildScripts, LibraryPath}; pub(crate) use self::fingerprint::DirtyReason; -pub use self::fingerprint::RustDocFingerprint; +pub use self::fingerprint::RustdocFingerprint; pub use self::job_queue::Freshness; use self::job_queue::{Job, JobQueue, JobState, Work}; pub(crate) use self::layout::Layout; diff --git a/tests/testsuite/doc.rs b/tests/testsuite/doc.rs index 871e7be31d4..9e102e7e801 100644 --- a/tests/testsuite/doc.rs +++ b/tests/testsuite/doc.rs @@ -5,7 +5,8 @@ use std::str; use crate::prelude::*; use crate::utils::tools; -use cargo::core::compiler::RustDocFingerprint; + +use cargo::core::compiler::RustdocFingerprint; use cargo_test_support::registry::Package; use cargo_test_support::str; use cargo_test_support::{basic_lib_manifest, basic_manifest, git, project}; @@ -2495,7 +2496,7 @@ LLVM version: 9.0 dummy_project.cargo("doc").run(); - let fingerprint: RustDocFingerprint = + let fingerprint: RustdocFingerprint = serde_json::from_str(&dummy_project.read_file("target/.rustdoc_fingerprint.json")) .expect("JSON Serde fail"); @@ -2534,7 +2535,7 @@ LLVM version: 9.0 assert!(!dummy_project.build_dir().join("doc/bogus_file").exists()); - let fingerprint: RustDocFingerprint = + let fingerprint: RustdocFingerprint = serde_json::from_str(&dummy_project.read_file("target/.rustdoc_fingerprint.json")) .expect("JSON Serde fail"); @@ -2582,11 +2583,11 @@ LLVM version: 9.0 let target_fingerprint_path = p.build_dir().join(host).join(".rustdoc_fingerprint.json"); - let host_fingerprint: RustDocFingerprint = + let host_fingerprint: RustdocFingerprint = serde_json::from_str(&fs::read_to_string(&host_fingerprint_path).unwrap()) .expect("JSON Serde fail"); - let target_fingerprint: RustDocFingerprint = + let target_fingerprint: RustdocFingerprint = serde_json::from_str(&fs::read_to_string(&target_fingerprint_path).unwrap()) .expect("JSON Serde fail"); @@ -2630,7 +2631,7 @@ LLVM version: 9.0 // target doc dir got cleaned assert!(!p.build_dir().join(host).join("doc/bogus_file").exists()); - let fingerprint: RustDocFingerprint = + let fingerprint: RustdocFingerprint = serde_json::from_str(&fs::read_to_string(&target_fingerprint_path).unwrap()).unwrap(); assert_eq!(&fingerprint.rustc_vv, ¤t_rustc_version); } From 918720bcf5c3c48465c8a64f1089fd335d4d0b22 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Tue, 2 Dec 2025 20:13:18 -0500 Subject: [PATCH 2/6] refactor(rustdoc): extract fingerprint path to fn --- src/cargo/core/compiler/fingerprint/rustdoc.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/cargo/core/compiler/fingerprint/rustdoc.rs b/src/cargo/core/compiler/fingerprint/rustdoc.rs index 33d56e4a91a..9472db81661 100644 --- a/src/cargo/core/compiler/fingerprint/rustdoc.rs +++ b/src/cargo/core/compiler/fingerprint/rustdoc.rs @@ -1,4 +1,5 @@ use std::path::Path; +use std::path::PathBuf; use anyhow::Context as _; use cargo_util::paths; @@ -63,18 +64,23 @@ impl RustdocFingerprint { } } +/// Returns the path to rustdoc fingerprint file for a given [`CompileKind`]. +fn fingerprint_path(build_runner: &BuildRunner<'_, '_>, kind: CompileKind) -> PathBuf { + build_runner + .files() + .layout(kind) + .build_dir() + .root() + .join(".rustdoc_fingerprint.json") +} + /// Checks rustdoc fingerprint file for a given [`CompileKind`]. fn check_fingerprint( build_runner: &BuildRunner<'_, '_>, new_fingerprint: &RustdocFingerprint, kind: CompileKind, ) -> CargoResult<()> { - let fingerprint_path = build_runner - .files() - .layout(kind) - .build_dir() - .root() - .join(".rustdoc_fingerprint.json"); + let fingerprint_path = fingerprint_path(build_runner, kind); let write_fingerprint = || -> CargoResult<()> { paths::write(&fingerprint_path, serde_json::to_string(new_fingerprint)?) From 783ab5ca4d1e0f5f7943281708479154b65d0553 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Tue, 2 Dec 2025 21:02:39 -0500 Subject: [PATCH 3/6] refactor(rustdoc): separate fingerprint data schema and logic --- .../core/compiler/fingerprint/rustdoc.rs | 20 +++++++++++-------- tests/testsuite/doc.rs | 6 +++++- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/cargo/core/compiler/fingerprint/rustdoc.rs b/src/cargo/core/compiler/fingerprint/rustdoc.rs index 9472db81661..93624fa041a 100644 --- a/src/cargo/core/compiler/fingerprint/rustdoc.rs +++ b/src/cargo/core/compiler/fingerprint/rustdoc.rs @@ -10,6 +10,13 @@ use crate::CargoResult; use crate::core::compiler::BuildRunner; use crate::core::compiler::CompileKind; +/// JSON Schema of the [`RustdocFingerprint`] file. +#[derive(Debug, Serialize, Deserialize)] +struct RustdocFingerprintJson { + /// `rustc -vV` verbose version output. + pub rustc_vv: String, +} + /// Structure used to deal with Rustdoc fingerprinting /// /// This is important because the `.js`/`.html` & `.css` files @@ -21,11 +28,8 @@ use crate::core::compiler::CompileKind; /// We need to make sure that if there were any previous docs already compiled, /// they were compiled with the same Rustc version that we're currently using. /// Otherwise we must remove the `doc/` folder and compile again forcing a rebuild. -#[derive(Debug, Serialize, Deserialize)] -pub struct RustdocFingerprint { - /// `rustc -vV` verbose version output. - pub rustc_vv: String, -} +#[derive(Debug)] +pub struct RustdocFingerprint {} impl RustdocFingerprint { /// Checks whether the latest version of rustc used to compile this workspace's docs @@ -52,7 +56,7 @@ impl RustdocFingerprint { { return Ok(()); } - let new_fingerprint = RustdocFingerprint { + let new_fingerprint = RustdocFingerprintJson { rustc_vv: build_runner.bcx.rustc().verbose_version.clone(), }; @@ -77,7 +81,7 @@ fn fingerprint_path(build_runner: &BuildRunner<'_, '_>, kind: CompileKind) -> Pa /// Checks rustdoc fingerprint file for a given [`CompileKind`]. fn check_fingerprint( build_runner: &BuildRunner<'_, '_>, - new_fingerprint: &RustdocFingerprint, + new_fingerprint: &RustdocFingerprintJson, kind: CompileKind, ) -> CargoResult<()> { let fingerprint_path = fingerprint_path(build_runner, kind); @@ -94,7 +98,7 @@ fn check_fingerprint( return write_fingerprint(); }; - match serde_json::from_str::(&rustdoc_data) { + match serde_json::from_str::(&rustdoc_data) { Ok(on_disk_fingerprint) => { if on_disk_fingerprint.rustc_vv == new_fingerprint.rustc_vv { return Ok(()); diff --git a/tests/testsuite/doc.rs b/tests/testsuite/doc.rs index 9e102e7e801..668dd35e8ba 100644 --- a/tests/testsuite/doc.rs +++ b/tests/testsuite/doc.rs @@ -6,12 +6,16 @@ use std::str; use crate::prelude::*; use crate::utils::tools; -use cargo::core::compiler::RustdocFingerprint; use cargo_test_support::registry::Package; use cargo_test_support::str; use cargo_test_support::{basic_lib_manifest, basic_manifest, git, project}; use cargo_test_support::{rustc_host, symlink_supported}; +#[derive(serde::Serialize, serde::Deserialize)] +struct RustdocFingerprint { + rustc_vv: String, +} + #[cargo_test] fn simple() { let p = project() From 81bad572fd31f96a48e62882a3d3c9f485b546c9 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Tue, 2 Dec 2025 23:35:45 -0500 Subject: [PATCH 4/6] feat(unstable): add `-Zrustdoc-mergeable-info` Co-authored-by: Michael Howell Co-authored-by: Weihang Lo --- src/cargo/core/features.rs | 2 ++ src/doc/src/reference/unstable.md | 12 ++++++++ tests/testsuite/cargo/z_help/stdout.term.svg | 30 +++++++++++--------- tests/testsuite/doc.rs | 17 +++++++++++ 4 files changed, 47 insertions(+), 14 deletions(-) diff --git a/src/cargo/core/features.rs b/src/cargo/core/features.rs index f9f13dc2951..36bb8414296 100644 --- a/src/cargo/core/features.rs +++ b/src/cargo/core/features.rs @@ -884,6 +884,7 @@ unstable_cli_options!( rustc_unicode: bool = ("Enable `rustc`'s unicode error format in Cargo's error messages"), rustdoc_depinfo: bool = ("Use dep-info files in rustdoc rebuild detection"), rustdoc_map: bool = ("Allow passing external documentation mappings to rustdoc"), + rustdoc_mergeable_info: bool = ("Use rustdoc mergeable cross-crate-info files"), rustdoc_scrape_examples: bool = ("Allows Rustdoc to scrape code examples from reverse-dependencies"), sbom: bool = ("Enable the `sbom` option in build config in .cargo/config.toml file"), script: bool = ("Enable support for single-file, `.rs` packages"), @@ -1415,6 +1416,7 @@ impl CliUnstable { "rustc-unicode" => self.rustc_unicode = parse_empty(k, v)?, "rustdoc-depinfo" => self.rustdoc_depinfo = parse_empty(k, v)?, "rustdoc-map" => self.rustdoc_map = parse_empty(k, v)?, + "rustdoc-mergeable-info" => self.rustdoc_mergeable_info = parse_empty(k, v)?, "rustdoc-scrape-examples" => self.rustdoc_scrape_examples = parse_empty(k, v)?, "sbom" => self.sbom = parse_empty(k, v)?, "section-timings" => self.section_timings = parse_empty(k, v)?, diff --git a/src/doc/src/reference/unstable.md b/src/doc/src/reference/unstable.md index fcab1f6e134..9e684dc1d9d 100644 --- a/src/doc/src/reference/unstable.md +++ b/src/doc/src/reference/unstable.md @@ -103,6 +103,7 @@ Each new feature described below should explain how to use it. * [scrape-examples](#scrape-examples) --- Shows examples within documentation. * [output-format](#output-format-for-rustdoc) --- Allows documentation to also be emitted in the experimental [JSON format](https://doc.rust-lang.org/nightly/nightly-rustc/rustdoc_json_types/). * [rustdoc-depinfo](#rustdoc-depinfo) --- Use dep-info files in rustdoc rebuild detection. + * [rustdoc-mergeable-info](#rustdoc-mergeable-info) --- Use rustdoc mergeable cross-crate-info files. * `Cargo.toml` extensions * [Profile `rustflags` option](#profile-rustflags-option) --- Passed directly to rustc. * [Profile `hint-mostly-unused` option](#profile-hint-mostly-unused-option) --- Hint that a dependency is mostly unused, to optimize compilation time. @@ -2059,6 +2060,17 @@ cargo +nightly check --compile-time-deps --all-targets -Z unstable-options Enable `rustc`'s unicode error format in Cargo's error messages +## rustdoc mergeable info + +* Original Pull Request: [#16309](https://github.com/rust-lang/cargo/pull/16309) +* Tracking issue: [#16306](https://github.com/rust-lang/cargo/issues/16306) +* Tracking rustc issue: [rust-lang/rust#130676](https://github.com/rust-lang/rust/issues/130676) + +The `-Z rustdoc-mergeable-info` leverage rustdoc's mergeable crate info, +so that `cargo doc` can merge cross-crate information +(like the search index, source files index, etc.) +from separate output directories, +and run `rustdoc` in parallel. # Stabilized and removed features diff --git a/tests/testsuite/cargo/z_help/stdout.term.svg b/tests/testsuite/cargo/z_help/stdout.term.svg index f3354ac179b..e1dddbc6cec 100644 --- a/tests/testsuite/cargo/z_help/stdout.term.svg +++ b/tests/testsuite/cargo/z_help/stdout.term.svg @@ -1,4 +1,4 @@ - +