Skip to content

Commit a18e604

Browse files
committed
feat(fingerprint): integrate rustdoc dep-info files
This leverages the unstable `--emit=depinfo` option from rustdoc, so that rustdoc invocation rebuild can be better tracked without traversing the entire directory. Some design decisions: * Rustdoc's depinfo doesn't and shouldn't emit to `target/doc`, as the directory is considered part of the final artifact directory. In regard to that, we specify the dep-info output path to the fingerprint directory of rustdoc invocation. It looks like this `target/debug/.fingerprint/serde-12d29d32b3b8b38f/doc-lib-serde.d`. * We also start supporting `-Zchecksum-freshness` as a side effect. Could make it a separate PR if desired. * `-Zbinary-dep-depinfo` is not enabled along with this, since doc generations don't really require any binary dependencies.
1 parent 1f6d4be commit a18e604

File tree

3 files changed

+87
-13
lines changed

3 files changed

+87
-13
lines changed

src/cargo/core/compiler/fingerprint/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1494,7 +1494,9 @@ fn calculate_normal(
14941494

14951495
// Afterwards calculate our own fingerprint information.
14961496
let build_root = build_root(build_runner);
1497-
let local = if unit.mode.is_doc() || unit.mode.is_doc_scrape() {
1497+
let is_any_doc_gen = unit.mode.is_doc() || unit.mode.is_doc_scrape();
1498+
let rustdoc_depinfo_enabled = build_runner.bcx.gctx.cli_unstable().rustdoc_depinfo;
1499+
let local = if is_any_doc_gen && !rustdoc_depinfo_enabled {
14981500
// rustdoc does not have dep-info files.
14991501
let fingerprint = pkg_fingerprint(build_runner.bcx, &unit.pkg).with_context(|| {
15001502
format!(

src/cargo/core/compiler/mod.rs

+58
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,19 @@ fn prepare_rustdoc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResu
763763
add_error_format_and_color(build_runner, &mut rustdoc);
764764
add_allow_features(build_runner, &mut rustdoc);
765765

766+
if build_runner.bcx.gctx.cli_unstable().rustdoc_depinfo {
767+
// invocation-specific is required for keeping the original rustdoc emission
768+
let mut arg = OsString::from("--emit=invocation-specific,dep-info=");
769+
arg.push(rustdoc_dep_info_loc(build_runner, unit));
770+
rustdoc.arg(arg);
771+
772+
if build_runner.bcx.gctx.cli_unstable().checksum_freshness {
773+
rustdoc.arg("-Z").arg("checksum-hash-algorithm=blake3");
774+
}
775+
776+
rustdoc.arg("-Zunstable-options");
777+
}
778+
766779
if let Some(trim_paths) = unit.profile.trim_paths.as_ref() {
767780
trim_paths_args_rustdoc(&mut rustdoc, build_runner, unit, trim_paths)?;
768781
}
@@ -838,6 +851,20 @@ fn rustdoc(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<W
838851
let package_id = unit.pkg.package_id();
839852
let manifest_path = PathBuf::from(unit.pkg.manifest_path());
840853
let target = Target::clone(&unit.target);
854+
855+
let rustdoc_dep_info_loc = rustdoc_dep_info_loc(build_runner, unit);
856+
let dep_info_loc = fingerprint::dep_info_loc(build_runner, unit);
857+
let build_dir = build_runner.bcx.ws.build_dir().into_path_unlocked();
858+
let pkg_root = unit.pkg.root().to_path_buf();
859+
let cwd = rustdoc
860+
.get_cwd()
861+
.unwrap_or_else(|| build_runner.bcx.gctx.cwd())
862+
.to_path_buf();
863+
let fingerprint_dir = build_runner.files().fingerprint_dir(unit);
864+
let is_local = unit.is_local();
865+
let env_config = Arc::clone(build_runner.bcx.gctx.env_config()?);
866+
let rustdoc_depinfo_enabled = build_runner.bcx.gctx.cli_unstable().rustdoc_depinfo;
867+
841868
let mut output_options = OutputOptions::new(build_runner, unit);
842869
let script_metadata = build_runner.find_build_script_metadata(unit);
843870
let scrape_outputs = if should_include_scrape_units(build_runner.bcx, unit) {
@@ -903,6 +930,7 @@ fn rustdoc(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<W
903930
paths::remove_dir_all(crate_dir)?;
904931
}
905932
state.running(&rustdoc);
933+
let timestamp = paths::set_invocation_time(&fingerprint_dir)?;
906934

907935
let result = rustdoc
908936
.exec_with_streaming(
@@ -930,6 +958,29 @@ fn rustdoc(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<W
930958
return Err(e);
931959
}
932960

961+
if rustdoc_depinfo_enabled && rustdoc_dep_info_loc.exists() {
962+
fingerprint::translate_dep_info(
963+
&rustdoc_dep_info_loc,
964+
&dep_info_loc,
965+
&cwd,
966+
&pkg_root,
967+
&build_dir,
968+
&rustdoc,
969+
// Should we track source file for doc gen?
970+
is_local,
971+
&env_config,
972+
)
973+
.with_context(|| {
974+
internal(format_args!(
975+
"could not parse/generate dep info at: {}",
976+
rustdoc_dep_info_loc.display()
977+
))
978+
})?;
979+
// This mtime shift allows Cargo to detect if a source file was
980+
// modified in the middle of the build.
981+
paths::set_file_time_no_err(dep_info_loc, timestamp);
982+
}
983+
933984
Ok(())
934985
}))
935986
}
@@ -2012,3 +2063,10 @@ fn scrape_output_path(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoR
20122063
.outputs(unit)
20132064
.map(|outputs| outputs[0].path.clone())
20142065
}
2066+
2067+
/// Gets the dep-info file emitted by rustdoc.
2068+
fn rustdoc_dep_info_loc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> PathBuf {
2069+
let mut loc = build_runner.files().fingerprint_file_path(unit, "");
2070+
loc.set_extension("d");
2071+
loc
2072+
}

tests/testsuite/doc.rs

+26-12
Original file line numberDiff line numberDiff line change
@@ -2949,15 +2949,17 @@ fn rebuild_tracks_target_src_outside_package_root() {
29492949
p.cargo("doc --verbose -Zrustdoc-depinfo")
29502950
.masquerade_as_nightly_cargo(&["rustdoc-depinfo"])
29512951
.with_stderr_data(str![[r#"
2952-
[FRESH] foo v0.0.0 ([ROOT]/parent/foo)
2952+
[DIRTY] foo v0.0.0 ([ROOT]/parent/foo): the file `../lib.rs` has changed ([TIME_DIFF_AFTER_LAST_BUILD])
2953+
[DOCUMENTING] foo v0.0.0 ([ROOT]/parent/foo)
2954+
[RUNNING] `rustdoc [..]`
29532955
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
29542956
[GENERATED] [ROOT]/parent/foo/target/doc/foo/index.html
29552957
29562958
"#]])
29572959
.run();
29582960

29592961
let doc_html = p.read_file("target/doc/foo/index.html");
2960-
assert!(!doc_html.contains("depinfo-after"));
2962+
assert!(doc_html.contains("depinfo-after"));
29612963
}
29622964

29632965
#[cargo_test(nightly, reason = "`rustdoc --emit` is unstable")]
@@ -2986,15 +2988,17 @@ fn rebuild_tracks_include_str() {
29862988
p.cargo("doc --verbose -Zrustdoc-depinfo")
29872989
.masquerade_as_nightly_cargo(&["rustdoc-depinfo"])
29882990
.with_stderr_data(str![[r#"
2989-
[FRESH] foo v0.5.0 ([ROOT]/parent/foo)
2991+
[DIRTY] foo v0.5.0 ([ROOT]/parent/foo): the file `src/../../README` has changed ([TIME_DIFF_AFTER_LAST_BUILD])
2992+
[DOCUMENTING] foo v0.5.0 ([ROOT]/parent/foo)
2993+
[RUNNING] `rustdoc [..]`
29902994
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
29912995
[GENERATED] [ROOT]/parent/foo/target/doc/foo/index.html
29922996
29932997
"#]])
29942998
.run();
29952999

29963000
let doc_html = p.read_file("target/doc/foo/index.html");
2997-
assert!(!doc_html.contains("depinfo-after"));
3001+
assert!(doc_html.contains("depinfo-after"));
29983002
}
29993003

30003004
#[cargo_test(nightly, reason = "`rustdoc --emit` is unstable")]
@@ -3023,15 +3027,17 @@ fn rebuild_tracks_path_attr() {
30233027
p.cargo("doc --verbose -Zrustdoc-depinfo")
30243028
.masquerade_as_nightly_cargo(&["rustdoc-depinfo"])
30253029
.with_stderr_data(str![[r#"
3026-
[FRESH] foo v0.5.0 ([ROOT]/parent/foo)
3030+
[DIRTY] foo v0.5.0 ([ROOT]/parent/foo): the file `src/../../bar.rs` has changed ([TIME_DIFF_AFTER_LAST_BUILD])
3031+
[DOCUMENTING] foo v0.5.0 ([ROOT]/parent/foo)
3032+
[RUNNING] `rustdoc [..]`
30273033
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
30283034
[GENERATED] [ROOT]/parent/foo/target/doc/foo/index.html
30293035
30303036
"#]])
30313037
.run();
30323038

30333039
let doc_html = p.read_file("target/doc/foo/index.html");
3034-
assert!(!doc_html.contains("depinfo-after"));
3040+
assert!(doc_html.contains("depinfo-after"));
30353041
}
30363042

30373043
#[cargo_test(nightly, reason = "`rustdoc --emit` is unstable")]
@@ -3060,15 +3066,17 @@ fn rebuild_tracks_env() {
30603066
.env(env, "# depinfo-after")
30613067
.masquerade_as_nightly_cargo(&["rustdoc-depinfo"])
30623068
.with_stderr_data(str![[r#"
3063-
[FRESH] foo v0.5.0 ([ROOT]/foo)
3069+
[DIRTY] foo v0.5.0 ([ROOT]/foo): the environment variable __RUSTDOC_INJECTED changed
3070+
[DOCUMENTING] foo v0.5.0 ([ROOT]/foo)
3071+
[RUNNING] `rustdoc [..]`
30643072
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
30653073
[GENERATED] [ROOT]/foo/target/doc/foo/index.html
30663074
30673075
"#]])
30683076
.run();
30693077

30703078
let doc_html = p.read_file("target/doc/foo/index.html");
3071-
assert!(!doc_html.contains("depinfo-after"));
3079+
assert!(doc_html.contains("depinfo-after"));
30723080
}
30733081

30743082
#[cargo_test(nightly, reason = "`rustdoc --emit` is unstable")]
@@ -3119,21 +3127,27 @@ fn rebuild_tracks_env_in_dep() {
31193127
p.cargo("doc --verbose -Zrustdoc-depinfo")
31203128
.env(env, "# depinfo-after")
31213129
.masquerade_as_nightly_cargo(&["rustdoc-depinfo"])
3122-
.with_stderr_data(str![[r#"
3130+
.with_stderr_data(
3131+
str![[r#"
3132+
[DIRTY] bar v0.1.0: the environment variable __RUSTDOC_INJECTED changed
3133+
[DOCUMENTING] bar v0.1.0
31233134
[DIRTY] bar v0.1.0: the environment variable __RUSTDOC_INJECTED changed
31243135
[CHECKING] bar v0.1.0
31253136
[RUNNING] `rustc --crate-name bar [..]`
3137+
[RUNNING] `rustdoc [..]--crate-name bar [..]`
31263138
[DIRTY] foo v0.0.0 ([ROOT]/foo): the dependency bar was rebuilt
31273139
[DOCUMENTING] foo v0.0.0 ([ROOT]/foo)
31283140
[RUNNING] `rustdoc [..]--crate-name foo [..]`
31293141
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
31303142
[GENERATED] [ROOT]/foo/target/doc/foo/index.html
31313143
3132-
"#]])
3144+
"#]]
3145+
.unordered(),
3146+
)
31333147
.run();
31343148

31353149
let doc_html = p.read_file("target/doc/bar/index.html");
3136-
assert!(!doc_html.contains("depinfo-after"));
3150+
assert!(doc_html.contains("depinfo-after"));
31373151
}
31383152

31393153
#[cargo_test(
@@ -3167,7 +3181,7 @@ fn rebuild_tracks_checksum() {
31673181
p.cargo("doc --verbose -Zrustdoc-depinfo -Zchecksum-freshness")
31683182
.masquerade_as_nightly_cargo(&["rustdoc-depinfo"])
31693183
.with_stderr_data(str![[r#"
3170-
[DIRTY] foo v0.5.0 ([ROOT]/parent/foo): the precalculated components changed
3184+
[DIRTY] foo v0.5.0 ([ROOT]/parent/foo): file size changed (16 != 15) for `src/../../README`
31713185
[DOCUMENTING] foo v0.5.0 ([ROOT]/parent/foo)
31723186
[RUNNING] `rustdoc [..]`
31733187
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s

0 commit comments

Comments
 (0)