Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add rustdoc support for --emit=dep-info[=path] #137684

Merged
merged 4 commits into from
Mar 3, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 22 additions & 6 deletions src/librustdoc/config.rs
Original file line number Diff line number Diff line change
@@ -315,23 +315,30 @@ pub(crate) enum ModuleSorting {
Alphabetical,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum EmitType {
Unversioned,
Toolchain,
InvocationSpecific,
DepInfo(Option<PathBuf>),
}

impl FromStr for EmitType {
type Err = ();

fn from_str(s: &str) -> Result<Self, Self::Err> {
use EmitType::*;
match s {
"unversioned-shared-resources" => Ok(Unversioned),
"toolchain-shared-resources" => Ok(Toolchain),
"invocation-specific" => Ok(InvocationSpecific),
_ => Err(()),
"unversioned-shared-resources" => Ok(Self::Unversioned),
"toolchain-shared-resources" => Ok(Self::Toolchain),
"invocation-specific" => Ok(Self::InvocationSpecific),
"dep-info" => Ok(Self::DepInfo(None)),
option => {
if let Some(file) = option.strip_prefix("dep-info=") {
Ok(Self::DepInfo(Some(Path::new(file).into())))
} else {
Err(())
}
}
}
}
}
@@ -340,6 +347,15 @@ impl RenderOptions {
pub(crate) fn should_emit_crate(&self) -> bool {
self.emit.is_empty() || self.emit.contains(&EmitType::InvocationSpecific)
}

pub(crate) fn dep_info(&self) -> Option<Option<&Path>> {
for emit in &self.emit {
if let EmitType::DepInfo(file) = emit {
return Some(file.as_deref());
}
}
None
}
}

/// Create the input (string or file path)
24 changes: 18 additions & 6 deletions src/librustdoc/core.rs
Original file line number Diff line number Diff line change
@@ -15,11 +15,12 @@ use rustc_hir::def::Res;
use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId};
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{HirId, Path};
use rustc_interface::interface;
use rustc_lint::{MissingDoc, late_lint_mod};
use rustc_middle::hir::nested_filter;
use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt};
use rustc_session::config::{self, CrateType, ErrorOutputType, Input, ResolveDocLinks};
use rustc_session::config::{
self, CrateType, ErrorOutputType, Input, OutFileName, OutputType, OutputTypes, ResolveDocLinks,
};
pub(crate) use rustc_session::config::{Options, UnstableOptions};
use rustc_session::{Session, lint};
use rustc_span::source_map;
@@ -219,7 +220,7 @@ pub(crate) fn create_config(
remap_path_prefix,
..
}: RustdocOptions,
RenderOptions { document_private, .. }: &RenderOptions,
render_options: &RenderOptions,
) -> rustc_interface::Config {
// Add the doc cfg into the doc build.
cfgs.push("doc".to_string());
@@ -245,8 +246,11 @@ pub(crate) fn create_config(

let crate_types =
if proc_macro_crate { vec![CrateType::ProcMacro] } else { vec![CrateType::Rlib] };
let resolve_doc_links =
if *document_private { ResolveDocLinks::All } else { ResolveDocLinks::Exported };
let resolve_doc_links = if render_options.document_private {
ResolveDocLinks::All
} else {
ResolveDocLinks::Exported
};
let test = scrape_examples_options.map(|opts| opts.scrape_tests).unwrap_or(false);
// plays with error output here!
let sessopts = config::Options {
@@ -269,10 +273,18 @@ pub(crate) fn create_config(
crate_name,
test,
remap_path_prefix,
output_types: if let Some(file) = render_options.dep_info() {
OutputTypes::new(&[(
OutputType::DepInfo,
file.map(|f| OutFileName::Real(f.to_path_buf())),
)])
} else {
OutputTypes::new(&[])
},
..Options::default()
};

interface::Config {
rustc_interface::Config {
opts: sessopts,
crate_cfg: cfgs,
crate_check_cfg: check_cfgs,
2 changes: 1 addition & 1 deletion src/librustdoc/html/render/write_shared.rs
Original file line number Diff line number Diff line change
@@ -153,7 +153,7 @@ fn write_rendered_cross_crate_info(
include_sources: bool,
) -> Result<(), Error> {
let m = &opt.should_merge;
if opt.emit.is_empty() || opt.emit.contains(&EmitType::InvocationSpecific) {
if opt.should_emit_crate() {
if include_sources {
write_rendered_cci::<SourcesPart, _>(SourcesPart::blank, dst, crates, m)?;
}
10 changes: 8 additions & 2 deletions src/librustdoc/lib.rs
Original file line number Diff line number Diff line change
@@ -561,7 +561,7 @@ fn opts() -> Vec<RustcOptGroup> {
"",
"emit",
"Comma separated list of types of output for rustdoc to emit",
"[unversioned-shared-resources,toolchain-shared-resources,invocation-specific]",
"[unversioned-shared-resources,toolchain-shared-resources,invocation-specific,dep-info]",
),
opt(Unstable, FlagMulti, "", "no-run", "Compile doctests without running them", ""),
opt(
@@ -890,7 +890,13 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) {
// if we ran coverage, bail early, we don't need to also generate docs at this point
// (also we didn't load in any of the useful passes)
return;
} else if run_check {
}

if render_opts.dep_info().is_some() {
rustc_interface::passes::write_dep_info(tcx);
}

if run_check {
// Since we're in "check" mode, no need to generate anything beyond this point.
return;
}
7 changes: 7 additions & 0 deletions src/tools/run-make-support/src/external_deps/rustdoc.rs
Original file line number Diff line number Diff line change
@@ -132,4 +132,11 @@ impl Rustdoc {
self.cmd.arg(format);
self
}

/// Specify type(s) of output files to generate.
pub fn emit<S: AsRef<str>>(&mut self, kinds: S) -> &mut Self {
let kinds = kinds.as_ref();
self.cmd.arg(format!("--emit={kinds}"));
self
}
}
Original file line number Diff line number Diff line change
@@ -153,7 +153,7 @@ Options:
--generate-redirect-map
Generate JSON file at the top level instead of
generating HTML redirection files
--emit [unversioned-shared-resources,toolchain-shared-resources,invocation-specific]
--emit [unversioned-shared-resources,toolchain-shared-resources,invocation-specific,dep-info]
Comma separated list of types of output for rustdoc to
emit
--no-run Compile doctests without running them
1 change: 1 addition & 0 deletions tests/run-make/rustdoc-dep-info/bar.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include!("foo.rs");
1 change: 1 addition & 0 deletions tests/run-make/rustdoc-dep-info/doc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
blablabla
1 change: 1 addition & 0 deletions tests/run-make/rustdoc-dep-info/foo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub fn foo() {}
6 changes: 6 additions & 0 deletions tests/run-make/rustdoc-dep-info/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#![crate_name = "foo"]

#[cfg_attr(doc, doc = include_str!("doc.md"))]
pub struct Bar;

mod bar;
21 changes: 21 additions & 0 deletions tests/run-make/rustdoc-dep-info/rmake.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// This is a simple smoke test for rustdoc's `--emit dep-info` feature. It prints out
// information about dependencies in a Makefile-compatible format, as a `.d` file.

use run_make_support::assertion_helpers::assert_contains;
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();

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:");

// 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();
assert!(path("bla.d").exists());
}
Loading