@@ -47,6 +47,7 @@ struct State<'a, 'cfg> {
47
47
target_data : & ' a RustcTargetData < ' cfg > ,
48
48
profiles : & ' a Profiles ,
49
49
interner : & ' a UnitInterner ,
50
+ scrape_units : & ' a [ Unit ] ,
50
51
51
52
/// A set of edges in `unit_dependencies` where (a, b) means that the
52
53
/// dependency from a to b was added purely because it was a dev-dependency.
@@ -61,6 +62,7 @@ pub fn build_unit_dependencies<'a, 'cfg>(
61
62
features : & ' a ResolvedFeatures ,
62
63
std_resolve : Option < & ' a ( Resolve , ResolvedFeatures ) > ,
63
64
roots : & [ Unit ] ,
65
+ scrape_units : & [ Unit ] ,
64
66
std_roots : & HashMap < CompileKind , Vec < Unit > > ,
65
67
global_mode : CompileMode ,
66
68
target_data : & ' a RustcTargetData < ' cfg > ,
@@ -91,6 +93,7 @@ pub fn build_unit_dependencies<'a, 'cfg>(
91
93
target_data,
92
94
profiles,
93
95
interner,
96
+ scrape_units,
94
97
dev_dependency_edges : HashSet :: new ( ) ,
95
98
} ;
96
99
@@ -253,6 +256,7 @@ fn compute_deps(
253
256
if !dep. is_transitive ( )
254
257
&& !unit. target . is_test ( )
255
258
&& !unit. target . is_example ( )
259
+ && !unit. mode . is_doc_scrape ( )
256
260
&& !unit. mode . is_any_test ( )
257
261
{
258
262
return false ;
@@ -467,6 +471,25 @@ fn compute_deps_doc(
467
471
if unit. target . is_bin ( ) || unit. target . is_example ( ) {
468
472
ret. extend ( maybe_lib ( unit, state, unit_for) ?) ;
469
473
}
474
+
475
+ // Add all units being scraped for examples as a dependency of Doc units.
476
+ if state. ws . is_member ( & unit. pkg ) {
477
+ for scrape_unit in state. scrape_units . iter ( ) {
478
+ // This needs to match the FeaturesFor used in cargo_compile::generate_targets.
479
+ let unit_for = UnitFor :: new_host ( scrape_unit. target . proc_macro ( ) ) ;
480
+ deps_of ( scrape_unit, state, unit_for) ?;
481
+ ret. push ( new_unit_dep (
482
+ state,
483
+ scrape_unit,
484
+ & scrape_unit. pkg ,
485
+ & scrape_unit. target ,
486
+ unit_for,
487
+ scrape_unit. kind ,
488
+ scrape_unit. mode ,
489
+ ) ?) ;
490
+ }
491
+ }
492
+
470
493
Ok ( ret)
471
494
}
472
495
@@ -558,7 +581,7 @@ fn dep_build_script(
558
581
/// Choose the correct mode for dependencies.
559
582
fn check_or_build_mode ( mode : CompileMode , target : & Target ) -> CompileMode {
560
583
match mode {
561
- CompileMode :: Check { .. } | CompileMode :: Doc { .. } => {
584
+ CompileMode :: Check { .. } | CompileMode :: Doc { .. } | CompileMode :: Docscrape => {
562
585
if target. for_host ( ) {
563
586
// Plugin and proc macro targets should be compiled like
564
587
// normal.
@@ -695,6 +718,14 @@ fn connect_run_custom_build_deps(state: &mut State<'_, '_>) {
695
718
&& other. unit . target . is_linkable ( )
696
719
&& other. unit . pkg . manifest ( ) . links ( ) . is_some ( )
697
720
} )
721
+ // Avoid cycles when using the doc --scrape-examples feature:
722
+ // Say a workspace has crates A and B where A has a build-dependency on B.
723
+ // The Doc units for A and B will have a dependency on the Docscrape for both A and B.
724
+ // So this would add a dependency from B-build to A-build, causing a cycle:
725
+ // B (build) -> A (build) -> B(build)
726
+ // See the test scrape_examples_avoid_build_script_cycle for a concrete example.
727
+ // To avoid this cycle, we filter out the B -> A (docscrape) dependency.
728
+ . filter ( |( _parent, other) | !other. unit . mode . is_doc_scrape ( ) )
698
729
// Skip dependencies induced via dev-dependencies since
699
730
// connections between `links` and build scripts only happens
700
731
// via normal dependencies. Otherwise since dev-dependencies can
0 commit comments