]> git.proxmox.com Git - cargo.git/blobdiff - src/cargo/core/compiler/unit_dependencies.rs
Document lib before bin.
[cargo.git] / src / cargo / core / compiler / unit_dependencies.rs
index b09bf48653aa6b9dd2d7c7b2dd1634fa8783d212..5d8306fca4d7beab59cae73c40fe72126a35e16c 100644 (file)
@@ -47,6 +47,7 @@ struct State<'a, 'cfg> {
     target_data: &'a RustcTargetData<'cfg>,
     profiles: &'a Profiles,
     interner: &'a UnitInterner,
+    scrape_units: &'a [Unit],
 
     /// A set of edges in `unit_dependencies` where (a, b) means that the
     /// 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>(
     features: &'a ResolvedFeatures,
     std_resolve: Option<&'a (Resolve, ResolvedFeatures)>,
     roots: &[Unit],
+    scrape_units: &[Unit],
     std_roots: &HashMap<CompileKind, Vec<Unit>>,
     global_mode: CompileMode,
     target_data: &'a RustcTargetData<'cfg>,
@@ -91,6 +93,7 @@ pub fn build_unit_dependencies<'a, 'cfg>(
         target_data,
         profiles,
         interner,
+        scrape_units,
         dev_dependency_edges: HashSet::new(),
     };
 
@@ -129,7 +132,7 @@ fn calc_deps_of_std(
     // Compute dependencies for the standard library.
     state.is_std = true;
     for roots in std_roots.values() {
-        deps_of_roots(roots, &mut state)?;
+        deps_of_roots(roots, state)?;
     }
     state.is_std = false;
     Ok(Some(std::mem::take(&mut state.unit_dependencies)))
@@ -169,7 +172,7 @@ fn attach_std_deps(
 
 /// Compute all the dependencies of the given root units.
 /// The result is stored in state.unit_dependencies.
-fn deps_of_roots(roots: &[Unit], mut state: &mut State<'_, '_>) -> CargoResult<()> {
+fn deps_of_roots(roots: &[Unit], state: &mut State<'_, '_>) -> CargoResult<()> {
     for unit in roots.iter() {
         // Dependencies of tests/benches should not have `panic` set.
         // We check the global test mode to see if we are running in `cargo
@@ -197,7 +200,7 @@ fn deps_of_roots(roots: &[Unit], mut state: &mut State<'_, '_>) -> CargoResult<(
         } else {
             UnitFor::new_normal()
         };
-        deps_of(unit, &mut state, unit_for)?;
+        deps_of(unit, state, unit_for)?;
     }
 
     Ok(())
@@ -253,6 +256,7 @@ fn compute_deps(
         if !dep.is_transitive()
             && !unit.target.is_test()
             && !unit.target.is_example()
+            && !unit.mode.is_doc_scrape()
             && !unit.mode.is_any_test()
         {
             return false;
@@ -322,7 +326,7 @@ fn compute_deps(
     if unit.target.is_lib() && unit.mode != CompileMode::Doctest {
         return Ok(ret);
     }
-    ret.extend(maybe_lib(unit, state, unit_for)?);
+    ret.extend(maybe_lib(unit, state, unit_for, None)?);
 
     // If any integration tests/benches are being run, make sure that
     // binaries are built as well.
@@ -419,10 +423,7 @@ fn compute_deps_doc(
     state: &mut State<'_, '_>,
     unit_for: UnitFor,
 ) -> CargoResult<Vec<UnitDep>> {
-    let deps = state.deps(unit, unit_for, &|dep| match dep.kind() {
-        DepKind::Normal | DepKind::Development => true,
-        DepKind::Build => false,
-    });
+    let deps = state.deps(unit, unit_for, &|dep| dep.kind() == DepKind::Normal);
 
     // To document a library, we depend on dependencies actually being
     // built. If we're documenting *all* libraries, then we also depend on
@@ -468,8 +469,30 @@ fn compute_deps_doc(
 
     // If we document a binary/example, we need the library available.
     if unit.target.is_bin() || unit.target.is_example() {
-        ret.extend(maybe_lib(unit, state, unit_for)?);
+        // build the lib
+        ret.extend(maybe_lib(unit, state, unit_for, None)?);
+        // and also the lib docs for intra-doc links
+        ret.extend(maybe_lib(unit, state, unit_for, Some(unit.mode))?);
+    }
+
+    // Add all units being scraped for examples as a dependency of Doc units.
+    if state.ws.is_member(&unit.pkg) {
+        for scrape_unit in state.scrape_units.iter() {
+            // This needs to match the FeaturesFor used in cargo_compile::generate_targets.
+            let unit_for = UnitFor::new_host(scrape_unit.target.proc_macro());
+            deps_of(scrape_unit, state, unit_for)?;
+            ret.push(new_unit_dep(
+                state,
+                scrape_unit,
+                &scrape_unit.pkg,
+                &scrape_unit.target,
+                unit_for,
+                scrape_unit.kind,
+                scrape_unit.mode,
+            )?);
+        }
     }
+
     Ok(ret)
 }
 
@@ -477,13 +500,14 @@ fn maybe_lib(
     unit: &Unit,
     state: &mut State<'_, '_>,
     unit_for: UnitFor,
+    force_mode: Option<CompileMode>,
 ) -> CargoResult<Option<UnitDep>> {
     unit.pkg
         .targets()
         .iter()
         .find(|t| t.is_linkable())
         .map(|t| {
-            let mode = check_or_build_mode(unit.mode, t);
+            let mode = force_mode.unwrap_or_else(|| check_or_build_mode(unit.mode, t));
             let dep_unit_for = unit_for.with_dependency(unit, t);
             new_unit_dep(
                 state,
@@ -561,7 +585,7 @@ fn dep_build_script(
 /// Choose the correct mode for dependencies.
 fn check_or_build_mode(mode: CompileMode, target: &Target) -> CompileMode {
     match mode {
-        CompileMode::Check { .. } | CompileMode::Doc { .. } => {
+        CompileMode::Check { .. } | CompileMode::Doc { .. } | CompileMode::Docscrape => {
             if target.for_host() {
                 // Plugin and proc macro targets should be compiled like
                 // normal.
@@ -698,6 +722,14 @@ fn connect_run_custom_build_deps(state: &mut State<'_, '_>) {
                         && other.unit.target.is_linkable()
                         && other.unit.pkg.manifest().links().is_some()
                 })
+                // Avoid cycles when using the doc --scrape-examples feature:
+                // Say a workspace has crates A and B where A has a build-dependency on B.
+                // The Doc units for A and B will have a dependency on the Docscrape for both A and B.
+                // So this would add a dependency from B-build to A-build, causing a cycle:
+                //   B (build) -> A (build) -> B(build)
+                // See the test scrape_examples_avoid_build_script_cycle for a concrete example.
+                // To avoid this cycle, we filter out the B -> A (docscrape) dependency.
+                .filter(|(_parent, other)| !other.unit.mode.is_doc_scrape())
                 // Skip dependencies induced via dev-dependencies since
                 // connections between `links` and build scripts only happens
                 // via normal dependencies. Otherwise since dev-dependencies can