Ensure external paths passed via flags end up in rustdoc depinfo

rustdoc has many flags to pass external HTML/Markdown/CSS files that end
up in the build. These need to be recorded in depinfo so that Cargo will
rebuild the crate if they change.
This commit is contained in:
Noratrieb 2025-07-28 20:47:01 +02:00
parent f32b23204a
commit 327ee15959
9 changed files with 53 additions and 17 deletions

View file

@ -377,7 +377,7 @@ impl Options {
early_dcx: &mut EarlyDiagCtxt,
matches: &getopts::Matches,
args: Vec<String>,
) -> Option<(InputMode, Options, RenderOptions)> {
) -> Option<(InputMode, Options, RenderOptions, Vec<PathBuf>)> {
// Check for unstable options.
nightly_options::check_nightly_options(early_dcx, matches, &opts());
@ -640,10 +640,13 @@ impl Options {
let extension_css = matches.opt_str("e").map(|s| PathBuf::from(&s));
if let Some(ref p) = extension_css
&& !p.is_file()
{
dcx.fatal("option --extend-css argument must be a file");
let mut loaded_paths = Vec::new();
if let Some(ref p) = extension_css {
loaded_paths.push(p.clone());
if !p.is_file() {
dcx.fatal("option --extend-css argument must be a file");
}
}
let mut themes = Vec::new();
@ -687,6 +690,7 @@ impl Options {
))
.emit();
}
loaded_paths.push(theme_file.clone());
themes.push(StylePath { path: theme_file });
}
}
@ -705,6 +709,7 @@ impl Options {
&mut id_map,
edition,
&None,
&mut loaded_paths,
) else {
dcx.fatal("`ExternalHtml::load` failed");
};
@ -796,7 +801,8 @@ impl Options {
let scrape_examples_options = ScrapeExamplesOptions::new(matches, dcx);
let with_examples = matches.opt_strs("with-examples");
let call_locations = crate::scrape_examples::load_call_locations(with_examples, dcx);
let call_locations =
crate::scrape_examples::load_call_locations(with_examples, dcx, &mut loaded_paths);
let doctest_build_args = matches.opt_strs("doctest-build-arg");
let unstable_features =
@ -881,7 +887,7 @@ impl Options {
parts_out_dir,
disable_minification,
};
Some((input, options, render_options))
Some((input, options, render_options, loaded_paths))
}
}

View file

@ -1,4 +1,4 @@
use std::path::Path;
use std::path::{Path, PathBuf};
use std::{fs, str};
use rustc_errors::DiagCtxtHandle;
@ -32,12 +32,13 @@ impl ExternalHtml {
id_map: &mut IdMap,
edition: Edition,
playground: &Option<Playground>,
loaded_paths: &mut Vec<PathBuf>,
) -> Option<ExternalHtml> {
let codes = ErrorCodes::from(nightly_build);
let ih = load_external_files(in_header, dcx)?;
let ih = load_external_files(in_header, dcx, loaded_paths)?;
let bc = {
let mut bc = load_external_files(before_content, dcx)?;
let m_bc = load_external_files(md_before_content, dcx)?;
let mut bc = load_external_files(before_content, dcx, loaded_paths)?;
let m_bc = load_external_files(md_before_content, dcx, loaded_paths)?;
Markdown {
content: &m_bc,
links: &[],
@ -52,8 +53,8 @@ impl ExternalHtml {
bc
};
let ac = {
let mut ac = load_external_files(after_content, dcx)?;
let m_ac = load_external_files(md_after_content, dcx)?;
let mut ac = load_external_files(after_content, dcx, loaded_paths)?;
let m_ac = load_external_files(md_after_content, dcx, loaded_paths)?;
Markdown {
content: &m_ac,
links: &[],
@ -79,8 +80,10 @@ pub(crate) enum LoadStringError {
pub(crate) fn load_string<P: AsRef<Path>>(
file_path: P,
dcx: DiagCtxtHandle<'_>,
loaded_paths: &mut Vec<PathBuf>,
) -> Result<String, LoadStringError> {
let file_path = file_path.as_ref();
loaded_paths.push(file_path.to_owned());
let contents = match fs::read(file_path) {
Ok(bytes) => bytes,
Err(e) => {
@ -101,10 +104,14 @@ pub(crate) fn load_string<P: AsRef<Path>>(
}
}
fn load_external_files(names: &[String], dcx: DiagCtxtHandle<'_>) -> Option<String> {
fn load_external_files(
names: &[String],
dcx: DiagCtxtHandle<'_>,
loaded_paths: &mut Vec<PathBuf>,
) -> Option<String> {
let mut out = String::new();
for name in names {
let Ok(s) = load_string(name, dcx) else { return None };
let Ok(s) = load_string(name, dcx, loaded_paths) else { return None };
out.push_str(&s);
out.push('\n');
}

View file

@ -799,7 +799,7 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) {
// Note that we discard any distinction between different non-zero exit
// codes from `from_matches` here.
let (input, options, render_options) =
let (input, options, render_options, loaded_paths) =
match config::Options::from_matches(early_dcx, &matches, args) {
Some(opts) => opts,
None => return,
@ -870,6 +870,12 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) {
interface::run_compiler(config, |compiler| {
let sess = &compiler.sess;
// Register the loaded external files in the source map so they show up in depinfo.
// We can't load them via the source map because it gets created after we process the options.
for external_path in &loaded_paths {
let _ = sess.source_map().load_file(external_path);
}
if sess.opts.describe_lints {
rustc_driver::describe_lints(sess, registered_lints);
return;

View file

@ -333,9 +333,11 @@ pub(crate) fn run(
pub(crate) fn load_call_locations(
with_examples: Vec<String>,
dcx: DiagCtxtHandle<'_>,
loaded_paths: &mut Vec<PathBuf>,
) -> AllCallLocations {
let mut all_calls: AllCallLocations = FxIndexMap::default();
for path in with_examples {
loaded_paths.push(path.clone().into());
let bytes = match fs::read(&path) {
Ok(bytes) => bytes,
Err(e) => dcx.fatal(format!("failed to load examples: {e}")),

View file

@ -0,0 +1 @@
meow! :3

View file

@ -9,13 +9,26 @@ use run_make_support::{path, rfs, rustdoc};
fn main() {
// We're only emitting dep info, so we shouldn't be running static analysis to
// figure out that this program is erroneous.
rustdoc().input("lib.rs").arg("-Zunstable-options").emit("dep-info").run();
// Ensure that all kinds of input reading flags end up in dep-info.
rustdoc()
.input("lib.rs")
.arg("-Zunstable-options")
.arg("--html-before-content=before.html")
.arg("--markdown-after-content=after.md")
.arg("--extend-css=extend.css")
.arg("--theme=theme.css")
.emit("dep-info")
.run();
let content = rfs::read_to_string("foo.d");
assert_contains(&content, "lib.rs:");
assert_contains(&content, "foo.rs:");
assert_contains(&content, "bar.rs:");
assert_contains(&content, "doc.md:");
assert_contains(&content, "after.md:");
assert_contains(&content, "before.html:");
assert_contains(&content, "extend.css:");
assert_contains(&content, "theme.css:");
// Now we check that we can provide a file name to the `dep-info` argument.
rustdoc().input("lib.rs").arg("-Zunstable-options").emit("dep-info=bla.d").run();

View file

@ -0,0 +1 @@
/* This is not a valid theme but that doesn't really matter */