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.
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>,
target_data,
profiles,
interner,
+ scrape_units,
dev_dependency_edges: HashSet::new(),
};
// 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)))
/// 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
} else {
UnitFor::new_normal()
};
- deps_of(unit, &mut state, unit_for)?;
+ deps_of(unit, state, unit_for)?;
}
Ok(())
if !dep.is_transitive()
&& !unit.target.is_test()
&& !unit.target.is_example()
+ && !unit.mode.is_doc_scrape()
&& !unit.mode.is_any_test()
{
return false;
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.
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
// 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)
}
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,
/// 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.
&& 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