Skip to content

Commit 4705566

Browse files
committed
Add documentation and a test
1 parent 19c8f05 commit 4705566

File tree

5 files changed

+78
-31
lines changed

5 files changed

+78
-31
lines changed

src/cargo/core/compiler/build_config.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -138,21 +138,18 @@ pub enum CompileMode {
138138
/// Building a target with `rustc` to emit `rmeta` metadata only. If
139139
/// `test` is true, then it is also compiled with `--test` to check it like
140140
/// a test.
141-
Check {
142-
test: bool,
143-
},
141+
Check { test: bool },
144142
/// Used to indicate benchmarks should be built. This is not used in
145143
/// `Unit`, because it is essentially the same as `Test` (indicating
146144
/// `--test` should be passed to rustc) and by using `Test` instead it
147145
/// allows some de-duping of Units to occur.
148146
Bench,
149147
/// A target that will be documented with `rustdoc`.
150148
/// If `deps` is true, then it will also document all dependencies.
151-
Doc {
152-
deps: bool,
153-
},
149+
Doc { deps: bool },
154150
/// A target that will be tested with `rustdoc`.
155151
Doctest,
152+
/// An example or library that will be scraped for function calls by `rustdoc`.
156153
Docscrape,
157154
/// A marker for Units that represent the execution of a `build.rs` script.
158155
RunCustomBuild,

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ pub struct BuildContext<'a, 'cfg> {
4747
/// The dependency graph of units to compile.
4848
pub unit_graph: UnitGraph,
4949

50+
/// Reverse-dependencies of documented units, used by the rustdoc --scrape-examples flag.
5051
pub scrape_units: Vec<Unit>,
5152

5253
/// The list of all kinds that are involved in this build

src/cargo/core/compiler/mod.rs

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,30 @@ fn rustdoc(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Work> {
648648
rustdoc.args(args);
649649
}
650650

651+
// rustdoc needs a -Cmetadata flag in order to recognize StableCrateIds that refer to
652+
// items in the crate being documented. The -Cmetadata flag used by reverse-dependencies
653+
// will be the metadata of the Cargo unit that generated the current library's rmeta file,
654+
// which should be a Check unit.
655+
//
656+
// If the current crate has reverse-dependencies, such a Check unit should exist, and so
657+
// we use that crate's metadata. If not, we use the crate's Doc unit so at least examples
658+
// scraped from the current crate can be used when documenting the current crate.
659+
let matching_units = cx
660+
.bcx
661+
.unit_graph
662+
.keys()
663+
.filter(|other| {
664+
unit.pkg == other.pkg && unit.target == other.target && !other.mode.is_doc_scrape()
665+
})
666+
.collect::<Vec<_>>();
667+
let metadata_unit = matching_units
668+
.iter()
669+
.find(|other| other.mode.is_check())
670+
.or_else(|| matching_units.iter().find(|other| other.mode.is_doc()))
671+
.unwrap_or(&unit);
672+
let metadata = cx.files().metadata(metadata_unit);
673+
rustdoc.arg("-C").arg(format!("metadata={}", metadata));
674+
651675
let scrape_output_path = |unit: &Unit| -> CargoResult<PathBuf> {
652676
let layout = cx.files().layout(unit.kind);
653677
let output_dir = layout.prepare_tmp()?;
@@ -661,6 +685,7 @@ fn rustdoc(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Work> {
661685
.arg("--scrape-examples-output-path")
662686
.arg(scrape_output_path(unit)?);
663687

688+
// Limit the scraped examples to just crates in the root set
664689
let root_packages = cx
665690
.bcx
666691
.roots
@@ -671,26 +696,12 @@ fn rustdoc(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Work> {
671696
rustdoc.arg("--scrape-examples-target-crate").arg(pkg);
672697
}
673698
} else if cx.bcx.scrape_units.len() > 0 && cx.bcx.roots.contains(unit) {
674-
let all_units = cx
675-
.bcx
676-
.unit_graph
677-
.values()
678-
.map(|deps| deps.iter().map(|dep| &dep.unit))
679-
.flatten();
680-
let check_unit = all_units.into_iter().find(|other| {
681-
unit.pkg == other.pkg && unit.target == other.target && other.mode.is_check()
682-
});
683-
if let Some(check_unit) = check_unit {
684-
let metadata = cx.files().metadata(check_unit);
685-
rustdoc.arg("-C").arg(format!("metadata={}", metadata));
686-
687-
rustdoc.arg("-Zunstable-options");
699+
rustdoc.arg("-Zunstable-options");
688700

689-
for scrape_unit in &cx.bcx.scrape_units {
690-
rustdoc
691-
.arg("--with-examples")
692-
.arg(scrape_output_path(scrape_unit)?);
693-
}
701+
for scrape_unit in &cx.bcx.scrape_units {
702+
rustdoc
703+
.arg("--with-examples")
704+
.arg(scrape_output_path(scrape_unit)?);
694705
}
695706
}
696707

src/cargo/core/compiler/unit_dependencies.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ struct State<'a, 'cfg> {
4747
target_data: &'a RustcTargetData<'cfg>,
4848
profiles: &'a Profiles,
4949
interner: &'a UnitInterner,
50-
scrape_roots: &'a [Unit],
50+
scrape_units: &'a [Unit],
5151

5252
/// A set of edges in `unit_dependencies` where (a, b) means that the
5353
/// dependency from a to b was added purely because it was a dev-dependency.
@@ -62,7 +62,7 @@ pub fn build_unit_dependencies<'a, 'cfg>(
6262
features: &'a ResolvedFeatures,
6363
std_resolve: Option<&'a (Resolve, ResolvedFeatures)>,
6464
roots: &[Unit],
65-
scrape_roots: &[Unit],
65+
scrape_units: &[Unit],
6666
std_roots: &HashMap<CompileKind, Vec<Unit>>,
6767
global_mode: CompileMode,
6868
target_data: &'a RustcTargetData<'cfg>,
@@ -93,7 +93,7 @@ pub fn build_unit_dependencies<'a, 'cfg>(
9393
target_data,
9494
profiles,
9595
interner,
96-
scrape_roots,
96+
scrape_units,
9797
dev_dependency_edges: HashSet::new(),
9898
};
9999

@@ -422,8 +422,6 @@ fn compute_deps_doc(
422422
state: &mut State<'_, '_>,
423423
unit_for: UnitFor,
424424
) -> CargoResult<Vec<UnitDep>> {
425-
// FIXME(wcrichto): target.is_lib() is probably not the correct way to check
426-
// if the unit needs dev-dependencies
427425
let deps = state.deps(unit, unit_for, &|dep| dep.kind() == DepKind::Normal);
428426

429427
// To document a library, we depend on dependencies actually being
@@ -473,7 +471,8 @@ fn compute_deps_doc(
473471
ret.extend(maybe_lib(unit, state, unit_for)?);
474472
}
475473

476-
for scrape_unit in state.scrape_roots.iter() {
474+
// Add all units being scraped for examples as a dependency of Doc units.
475+
for scrape_unit in state.scrape_units.iter() {
477476
let unit_for = UnitFor::new_normal();
478477
deps_of(scrape_unit, state, unit_for)?;
479478
ret.push(new_unit_dep(
@@ -715,6 +714,8 @@ fn connect_run_custom_build_deps(state: &mut State<'_, '_>) {
715714
&& other.unit.target.is_linkable()
716715
&& other.unit.pkg.manifest().links().is_some()
717716
})
717+
// Avoid cycles when using the --scrape-examples feature
718+
// FIXME(wcrichto): unclear why this exact filter is the fix
718719
.filter(|(_, other)| !other.unit.mode.is_doc_scrape())
719720
// Skip dependencies induced via dev-dependencies since
720721
// connections between `links` and build scripts only happens

tests/testsuite/doc.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2148,3 +2148,40 @@ fn doc_fingerprint_unusual_behavior() {
21482148
assert!(build_doc.join("somefile").exists());
21492149
assert!(real_doc.join("somefile").exists());
21502150
}
2151+
2152+
#[cargo_test]
2153+
fn scrape_examples() {
2154+
if !is_nightly() {
2155+
// --scrape-examples is unstable
2156+
return;
2157+
}
2158+
2159+
let p = project()
2160+
.file(
2161+
"Cargo.toml",
2162+
r#"
2163+
[package]
2164+
name = "foo"
2165+
version = "0.0.1"
2166+
authors = []
2167+
"#,
2168+
)
2169+
.file("examples/ex.rs", "fn main() { foo::foo(); }")
2170+
.file("src/lib.rs", "pub fn foo() {}\npub fn bar() { foo(); }")
2171+
.build();
2172+
2173+
p.cargo("doc -Zunstable-options --scrape-examples all")
2174+
.masquerade_as_nightly_cargo()
2175+
.with_stderr(
2176+
"\
2177+
[..] foo v0.0.1 ([CWD])
2178+
[..] foo v0.0.1 ([CWD])
2179+
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
2180+
",
2181+
)
2182+
.run();
2183+
2184+
let doc_html = p.read_file("target/doc/foo/fn.foo.html");
2185+
assert!(doc_html.contains("Examples found in repository"));
2186+
assert!(doc_html.contains("More examples"));
2187+
}

0 commit comments

Comments
 (0)