Skip to content

Commit b948fc8

Browse files
committed
Add test / documentation for scrape-examples cycle-avoidance, add map for doc unit -> metadata to Context
1 parent e52a9d9 commit b948fc8

File tree

4 files changed

+99
-26
lines changed

4 files changed

+99
-26
lines changed

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

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ pub struct Context<'a, 'cfg> {
8181
/// compilation is happening (only object, only bitcode, both, etc), and is
8282
/// precalculated early on.
8383
pub lto: HashMap<Unit, Lto>,
84+
85+
/// Map of Doc/Docscrape units to metadata for their -Cmetadata flag.
86+
/// See Context::find_metadata_units for more details.
87+
pub metadata_for_doc_units: HashMap<Unit, Metadata>,
8488
}
8589

8690
impl<'a, 'cfg> Context<'a, 'cfg> {
@@ -121,6 +125,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
121125
rustc_clients: HashMap::new(),
122126
pipelining,
123127
lto: HashMap::new(),
128+
metadata_for_doc_units: HashMap::new(),
124129
})
125130
}
126131

@@ -135,6 +140,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
135140
self.prepare()?;
136141
custom_build::build_map(&mut self)?;
137142
self.check_collisions()?;
143+
self.compute_metadata_for_doc_units();
138144

139145
// We need to make sure that if there were any previous docs
140146
// already compiled, they were compiled with the same Rustc version that we're currently
@@ -614,4 +620,40 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
614620

615621
Ok(client)
616622
}
623+
624+
/// Finds metadata for Doc/Docscrape units.
625+
///
626+
/// rustdoc needs a -Cmetadata flag in order to recognize StableCrateIds that refer to
627+
/// items in the crate being documented. The -Cmetadata flag used by reverse-dependencies
628+
/// will be the metadata of the Cargo unit that generated the current library's rmeta file,
629+
/// which should be a Check unit.
630+
///
631+
/// If the current crate has reverse-dependencies, such a Check unit should exist, and so
632+
/// we use that crate's metadata. If not, we use the crate's Doc unit so at least examples
633+
/// scraped from the current crate can be used when documenting the current crate.
634+
pub fn compute_metadata_for_doc_units(&mut self) {
635+
for unit in self.bcx.unit_graph.keys() {
636+
if !unit.mode.is_doc() && !unit.mode.is_doc_scrape() {
637+
continue;
638+
}
639+
640+
let matching_units = self
641+
.bcx
642+
.unit_graph
643+
.keys()
644+
.filter(|other| {
645+
unit.pkg == other.pkg
646+
&& unit.target == other.target
647+
&& !other.mode.is_doc_scrape()
648+
})
649+
.collect::<Vec<_>>();
650+
let metadata_unit = matching_units
651+
.iter()
652+
.find(|other| other.mode.is_check())
653+
.or_else(|| matching_units.iter().find(|other| other.mode.is_doc()))
654+
.unwrap_or(&unit);
655+
self.metadata_for_doc_units
656+
.insert(unit.clone(), self.files().metadata(metadata_unit));
657+
}
658+
}
617659
}

src/cargo/core/compiler/mod.rs

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -648,28 +648,7 @@ 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);
651+
let metadata = cx.metadata_for_doc_units[&unit];
673652
rustdoc.arg("-C").arg(format!("metadata={}", metadata));
674653

675654
let scrape_output_path = |unit: &Unit| -> CargoResult<PathBuf> {

src/cargo/core/compiler/unit_dependencies.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -714,9 +714,14 @@ fn connect_run_custom_build_deps(state: &mut State<'_, '_>) {
714714
&& other.unit.target.is_linkable()
715715
&& other.unit.pkg.manifest().links().is_some()
716716
})
717-
// Avoid cycles when using the --scrape-examples feature
718-
// FIXME(wcrichto): unclear why this exact filter is the fix
719-
.filter(|(_, other)| !other.unit.mode.is_doc_scrape())
717+
// Avoid cycles when using the doc --scrape-examples feature:
718+
// Say a workspace has crates A and B where A has a build-dependency on B.
719+
// The Doc units for A and B will have a dependency on the Docscrape for both A and B.
720+
// So this would add a dependency from B-build to A-build, causing a cycle:
721+
// B (build) -> A (build) -> B(build)
722+
// See the test scrape_examples_avoid_build_script_cycle for a concrete example.
723+
// To avoid this cycle, we filter out the B -> A (docscrape) dependency.
724+
.filter(|(_parent, other)| !other.unit.mode.is_doc_scrape())
720725
// Skip dependencies induced via dev-dependencies since
721726
// connections between `links` and build scripts only happens
722727
// via normal dependencies. Otherwise since dev-dependencies can

tests/testsuite/doc.rs

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2150,7 +2150,7 @@ fn doc_fingerprint_unusual_behavior() {
21502150
}
21512151

21522152
#[cargo_test]
2153-
fn scrape_examples() {
2153+
fn scrape_examples_basic() {
21542154
if !is_nightly() {
21552155
// --scrape-examples is unstable
21562156
return;
@@ -2185,3 +2185,50 @@ fn scrape_examples() {
21852185
assert!(doc_html.contains("Examples found in repository"));
21862186
assert!(doc_html.contains("More examples"));
21872187
}
2188+
2189+
#[cargo_test]
2190+
fn scrape_examples_avoid_build_script_cycle() {
2191+
if !is_nightly() {
2192+
// --scrape-examples is unstable
2193+
return;
2194+
}
2195+
2196+
let p = project()
2197+
// package with build dependency
2198+
.file(
2199+
"Cargo.toml",
2200+
r#"
2201+
[package]
2202+
name = "foo"
2203+
version = "0.0.1"
2204+
authors = []
2205+
links = "foo"
2206+
2207+
[workspace]
2208+
members = ["bar"]
2209+
2210+
[build-dependencies]
2211+
bar = {path = "bar"}
2212+
"#,
2213+
)
2214+
.file("src/lib.rs", "")
2215+
.file("build.rs", "fn main(){}")
2216+
// dependency
2217+
.file(
2218+
"bar/Cargo.toml",
2219+
r#"
2220+
[package]
2221+
name = "bar"
2222+
version = "0.0.1"
2223+
authors = []
2224+
links = "bar"
2225+
"#,
2226+
)
2227+
.file("bar/src/lib.rs", "")
2228+
.file("bar/build.rs", "fn main(){}")
2229+
.build();
2230+
2231+
p.cargo("doc --all -Zunstable-options --scrape-examples all")
2232+
.masquerade_as_nightly_cargo()
2233+
.run();
2234+
}

0 commit comments

Comments
 (0)