use crate::core::compiler::unit_graph::{UnitDep, UnitGraph};
use crate::core::compiler::Unit;
use crate::core::compiler::{BuildContext, CompileKind, CompileMode};
use crate::core::dependency::DepKind;
use crate::core::profiles::{Profile, UnitFor};
use crate::core::resolver::features::{FeaturesFor, ResolvedFeatures};
use crate::core::resolver::Resolve;
use crate::core::{InternedString, Package, PackageId, Target, Workspace};
use crate::CargoResult;
use log::trace;
use std::collections::{HashMap, HashSet};
use std::iter::FromIterator;
struct State<'a, 'cfg> {
bcx: &'a BuildContext<'a, 'cfg>,
unit_dependencies: UnitGraph<'a>,
usr_resolve: &'a Resolve,
usr_features: &'a ResolvedFeatures,
std_resolve: Option<&'a Resolve>,
std_features: Option<&'a ResolvedFeatures>,
is_std: bool,
}
pub fn build_unit_dependencies<'a, 'cfg>(
bcx: &'a BuildContext<'a, 'cfg>,
resolve: &'a Resolve,
features: &'a ResolvedFeatures,
std_resolve: Option<&'a (Resolve, ResolvedFeatures)>,
roots: &[Unit<'a>],
std_roots: &[Unit<'a>],
) -> CargoResult<UnitGraph<'a>> {
let (std_resolve, std_features) = match std_resolve {
Some((r, f)) => (Some(r), Some(f)),
None => (None, None),
};
let mut state = State {
bcx,
unit_dependencies: HashMap::new(),
usr_resolve: resolve,
usr_features: features,
std_resolve,
std_features,
is_std: false,
};
let std_unit_deps = calc_deps_of_std(&mut state, std_roots)?;
deps_of_roots(roots, &mut state)?;
super::links::validate_links(state.resolve(), &state.unit_dependencies)?;
if let Some(std_unit_deps) = std_unit_deps {
attach_std_deps(&mut state, std_roots, std_unit_deps);
}
connect_run_custom_build_deps(&mut state.unit_dependencies);
for list in state.unit_dependencies.values_mut() {
list.sort();
}
trace!("ALL UNIT DEPENDENCIES {:#?}", state.unit_dependencies);
Ok(state.unit_dependencies)
}
fn calc_deps_of_std<'a, 'cfg>(
mut state: &mut State<'a, 'cfg>,
std_roots: &[Unit<'a>],
) -> CargoResult<Option<UnitGraph<'a>>> {
if std_roots.is_empty() {
return Ok(None);
}
state.is_std = true;
deps_of_roots(std_roots, &mut state)?;
state.is_std = false;
Ok(Some(std::mem::replace(
&mut state.unit_dependencies,
HashMap::new(),
)))
}
fn attach_std_deps<'a, 'cfg>(
state: &mut State<'a, 'cfg>,
std_roots: &[Unit<'a>],
std_unit_deps: UnitGraph<'a>,
) {
for (unit, deps) in state.unit_dependencies.iter_mut() {
if !unit.kind.is_host() && !unit.mode.is_run_custom_build() {
deps.extend(std_roots.iter().map(|unit| UnitDep {
unit: *unit,
unit_for: UnitFor::new_normal(),
extern_crate_name: unit.pkg.name(),
public: true,
noprelude: true,
}));
}
}
for (unit, deps) in std_unit_deps.into_iter() {
if let Some(other_unit) = state.unit_dependencies.insert(unit, deps) {
panic!("std unit collision with existing unit: {:?}", other_unit);
}
}
}
fn deps_of_roots<'a, 'cfg>(roots: &[Unit<'a>], mut state: &mut State<'a, 'cfg>) -> CargoResult<()> {
for unit in roots.iter() {
state.get(unit.pkg.package_id());
let unit_for = if unit.mode.is_any_test() || state.bcx.build_config.test() {
UnitFor::new_test(state.bcx.config)
} else if unit.target.is_custom_build() {
UnitFor::new_host(false)
} else if unit.target.proc_macro() {
UnitFor::new_host(true)
} else if unit.target.for_host() {
UnitFor::new_compiler()
} else {
UnitFor::new_normal()
};
deps_of(unit, &mut state, unit_for)?;
}
Ok(())
}
fn deps_of<'a, 'cfg>(
unit: &Unit<'a>,
state: &mut State<'a, 'cfg>,
unit_for: UnitFor,
) -> CargoResult<()> {
if !state.unit_dependencies.contains_key(unit) {
let unit_deps = compute_deps(unit, state, unit_for)?;
state.unit_dependencies.insert(*unit, unit_deps.clone());
for unit_dep in unit_deps {
deps_of(&unit_dep.unit, state, unit_dep.unit_for)?;
}
}
Ok(())
}
fn compute_deps<'a, 'cfg>(
unit: &Unit<'a>,
state: &mut State<'a, 'cfg>,
unit_for: UnitFor,
) -> CargoResult<Vec<UnitDep<'a>>> {
if unit.mode.is_run_custom_build() {
return compute_deps_custom_build(unit, unit_for, state);
} else if unit.mode.is_doc() {
return compute_deps_doc(unit, state);
}
let bcx = state.bcx;
let id = unit.pkg.package_id();
let filtered_deps = state.resolve().deps(id).filter(|&(_id, deps)| {
assert!(!deps.is_empty());
deps.iter().any(|dep| {
if unit.target.is_custom_build() != dep.is_build() {
return false;
}
if !dep.is_transitive()
&& !unit.target.is_test()
&& !unit.target.is_example()
&& !unit.mode.is_any_test()
{
return false;
}
if !bcx.target_data.dep_platform_activated(dep, unit.kind) {
return false;
}
if dep.is_optional() {
let features_for = unit_for.map_to_features_for();
let feats = state.activated_features(id, features_for);
if !feats.contains(&dep.name_in_toml()) {
return false;
}
}
true
})
});
let filtered_deps: Vec<_> = filtered_deps.collect();
let mut ret = Vec::new();
for (id, _) in filtered_deps {
let pkg = state.get(id);
let lib = match pkg.targets().iter().find(|t| t.is_lib()) {
Some(t) => t,
None => continue,
};
let mode = check_or_build_mode(unit.mode, lib);
let dep_unit_for = unit_for
.with_for_host(lib.for_host())
.with_host_features(unit.target.is_custom_build() || lib.proc_macro());
if bcx.config.cli_unstable().dual_proc_macros && lib.proc_macro() && !unit.kind.is_host() {
let unit_dep = new_unit_dep(state, unit, pkg, lib, dep_unit_for, unit.kind, mode)?;
ret.push(unit_dep);
let unit_dep =
new_unit_dep(state, unit, pkg, lib, dep_unit_for, CompileKind::Host, mode)?;
ret.push(unit_dep);
} else {
let unit_dep = new_unit_dep(
state,
unit,
pkg,
lib,
dep_unit_for,
unit.kind.for_target(lib),
mode,
)?;
ret.push(unit_dep);
}
}
if unit.target.is_custom_build() {
return Ok(ret);
}
ret.extend(dep_build_script(unit, unit_for, state)?);
if unit.target.is_lib() && unit.mode != CompileMode::Doctest {
return Ok(ret);
}
ret.extend(maybe_lib(unit, state, unit_for)?);
if !unit.mode.is_check()
&& unit.mode.is_any_test()
&& (unit.target.is_test() || unit.target.is_bench())
{
ret.extend(
unit.pkg
.targets()
.iter()
.filter(|t| {
let no_required_features = Vec::new();
t.is_bin() &&
t.required_features().unwrap_or(&no_required_features).iter().all(|f| {
unit.features.contains(&InternedString::new(f.as_str()))
})
})
.map(|t| {
new_unit_dep(
state,
unit,
unit.pkg,
t,
UnitFor::new_normal(),
unit.kind.for_target(t),
CompileMode::Build,
)
})
.collect::<CargoResult<Vec<UnitDep<'a>>>>()?,
);
}
Ok(ret)
}
fn compute_deps_custom_build<'a, 'cfg>(
unit: &Unit<'a>,
unit_for: UnitFor,
state: &mut State<'a, 'cfg>,
) -> CargoResult<Vec<UnitDep<'a>>> {
if let Some(links) = unit.pkg.manifest().links() {
if state.bcx.script_override(links, unit.kind).is_some() {
return Ok(Vec::new());
}
}
let script_unit_for = UnitFor::new_host(unit_for.is_for_host_features());
let unit_dep = new_unit_dep(
state,
unit,
unit.pkg,
unit.target,
script_unit_for,
CompileKind::Host,
CompileMode::Build,
)?;
Ok(vec![unit_dep])
}
fn compute_deps_doc<'a, 'cfg>(
unit: &Unit<'a>,
state: &mut State<'a, 'cfg>,
) -> CargoResult<Vec<UnitDep<'a>>> {
let bcx = state.bcx;
let deps = state
.resolve()
.deps(unit.pkg.package_id())
.filter(|&(_id, deps)| {
deps.iter().any(|dep| match dep.kind() {
DepKind::Normal => bcx.target_data.dep_platform_activated(dep, unit.kind),
_ => false,
})
});
let mut ret = Vec::new();
for (id, _deps) in deps {
let dep = state.get(id);
let lib = match dep.targets().iter().find(|t| t.is_lib()) {
Some(lib) => lib,
None => continue,
};
let mode = check_or_build_mode(unit.mode, lib);
let dep_unit_for = UnitFor::new_normal()
.with_for_host(lib.for_host())
.with_host_features(lib.proc_macro());
let lib_unit_dep = new_unit_dep(
state,
unit,
dep,
lib,
dep_unit_for,
unit.kind.for_target(lib),
mode,
)?;
ret.push(lib_unit_dep);
if let CompileMode::Doc { deps: true } = unit.mode {
let doc_unit_dep = new_unit_dep(
state,
unit,
dep,
lib,
dep_unit_for,
unit.kind.for_target(lib),
unit.mode,
)?;
ret.push(doc_unit_dep);
}
}
ret.extend(dep_build_script(unit, UnitFor::new_normal(), state)?);
if unit.target.is_bin() || unit.target.is_example() {
ret.extend(maybe_lib(unit, state, UnitFor::new_normal())?);
}
Ok(ret)
}
fn maybe_lib<'a>(
unit: &Unit<'a>,
state: &mut State<'a, '_>,
unit_for: UnitFor,
) -> CargoResult<Option<UnitDep<'a>>> {
unit.pkg
.targets()
.iter()
.find(|t| t.linkable())
.map(|t| {
let mode = check_or_build_mode(unit.mode, t);
new_unit_dep(
state,
unit,
unit.pkg,
t,
unit_for,
unit.kind.for_target(t),
mode,
)
})
.transpose()
}
fn dep_build_script<'a>(
unit: &Unit<'a>,
unit_for: UnitFor,
state: &State<'a, '_>,
) -> CargoResult<Option<UnitDep<'a>>> {
unit.pkg
.targets()
.iter()
.find(|t| t.is_custom_build())
.map(|t| {
let profile = state
.bcx
.profiles
.get_profile_run_custom_build(&unit.profile);
let script_unit_for = UnitFor::new_host(unit_for.is_for_host_features());
new_unit_dep_with_profile(
state,
unit,
unit.pkg,
t,
script_unit_for,
unit.kind,
CompileMode::RunCustomBuild,
profile,
)
})
.transpose()
}
fn check_or_build_mode(mode: CompileMode, target: &Target) -> CompileMode {
match mode {
CompileMode::Check { .. } | CompileMode::Doc { .. } => {
if target.for_host() {
CompileMode::Build
} else {
CompileMode::Check { test: false }
}
}
_ => CompileMode::Build,
}
}
fn new_unit_dep<'a>(
state: &State<'a, '_>,
parent: &Unit<'a>,
pkg: &'a Package,
target: &'a Target,
unit_for: UnitFor,
kind: CompileKind,
mode: CompileMode,
) -> CargoResult<UnitDep<'a>> {
let profile = state.bcx.profiles.get_profile(
pkg.package_id(),
state.bcx.ws.is_member(pkg),
unit_for,
mode,
);
new_unit_dep_with_profile(state, parent, pkg, target, unit_for, kind, mode, profile)
}
fn new_unit_dep_with_profile<'a>(
state: &State<'a, '_>,
parent: &Unit<'a>,
pkg: &'a Package,
target: &'a Target,
unit_for: UnitFor,
kind: CompileKind,
mode: CompileMode,
profile: Profile,
) -> CargoResult<UnitDep<'a>> {
let extern_crate_name = InternedString::new(&state.resolve().extern_crate_name(
parent.pkg.package_id(),
pkg.package_id(),
target,
)?);
let public = state
.resolve()
.is_public_dep(parent.pkg.package_id(), pkg.package_id());
let features_for = unit_for.map_to_features_for();
let features = state.activated_features(pkg.package_id(), features_for);
let unit = state
.bcx
.units
.intern(pkg, target, profile, kind, mode, features, state.is_std);
Ok(UnitDep {
unit,
unit_for,
extern_crate_name,
public,
noprelude: false,
})
}
fn connect_run_custom_build_deps(unit_dependencies: &mut UnitGraph<'_>) {
let mut new_deps = Vec::new();
{
let mut reverse_deps_map = HashMap::new();
for (unit, deps) in unit_dependencies.iter() {
for dep in deps {
if dep.unit.mode == CompileMode::RunCustomBuild {
reverse_deps_map
.entry(dep.unit)
.or_insert_with(HashSet::new)
.insert(unit);
}
}
}
for unit in unit_dependencies
.keys()
.filter(|k| k.mode == CompileMode::RunCustomBuild)
{
let reverse_deps = match reverse_deps_map.get(unit) {
Some(set) => set,
None => continue,
};
let to_add = reverse_deps
.iter()
.flat_map(|reverse_dep| unit_dependencies[reverse_dep].iter())
.filter(|other| {
other.unit.pkg != unit.pkg
&& other.unit.target.linkable()
&& other.unit.pkg.manifest().links().is_some()
})
.filter_map(|other| {
unit_dependencies[&other.unit]
.iter()
.find(|other_dep| other_dep.unit.mode == CompileMode::RunCustomBuild)
.cloned()
})
.collect::<HashSet<_>>();
if !to_add.is_empty() {
new_deps.push((*unit, to_add));
}
}
}
for (unit, new_deps) in new_deps {
unit_dependencies.get_mut(&unit).unwrap().extend(new_deps);
}
}
impl<'a, 'cfg> State<'a, 'cfg> {
fn resolve(&self) -> &'a Resolve {
if self.is_std {
self.std_resolve.unwrap()
} else {
self.usr_resolve
}
}
fn activated_features(
&self,
pkg_id: PackageId,
features_for: FeaturesFor,
) -> Vec<InternedString> {
let features = if self.is_std {
self.std_features.unwrap()
} else {
self.usr_features
};
features.activated_features(pkg_id, features_for)
}
fn get(&self, id: PackageId) -> &'a Package {
self.bcx
.packages
.get_one(id)
.unwrap_or_else(|_| panic!("expected {} to be downloaded", id))
}
}
pub fn filter_roots<'a, 'cfg>(
bcx: &BuildContext<'a, 'cfg>,
ws: &Workspace<'cfg>,
graph: &UnitGraph<'a>,
) {
let ws_members = graph.keys().filter(|unit| ws.is_member(unit.pkg)).cloned();
let root_set = HashSet::<_>::from_iter(ws_members);
bcx.skip_units.replace(root_set);
}
pub fn filter_locals<'a, 'cfg>(bcx: &BuildContext<'a, 'cfg>, graph: &UnitGraph<'a>) {
fn is_local(unit: &Unit<'_>) -> bool {
unit.pkg.package_id().source_id().is_path()
}
let iter_locals = graph.keys().filter(|ref unit| is_local(unit)).cloned();
let mut to_skip = HashSet::<_>::from_iter(iter_locals);
fn unskip_deps_of<'a>(
unit: &Unit<'a>,
to_remove: &mut HashSet<Unit<'a>>,
graph: &UnitGraph<'a>,
) {
let deps = &graph[unit];
for dep in deps {
if is_local(&dep.unit) {
to_remove.remove(&dep.unit);
unskip_deps_of(&dep.unit, to_remove, graph);
}
}
}
for unit in graph.keys() {
if !is_local(&unit) {
unskip_deps_of(unit, &mut to_skip, graph);
}
}
bcx.skip_units.replace(to_skip);
}