in guppy/src/graph/build.rs [191:345]
fn process_package(
&mut self,
package: Package,
) -> Result<(PackageId, PackageMetadataImpl), Error> {
let package_id = PackageId::from_metadata(package.id);
let (package_ix, _, _) = self.package_data(&package_id)?;
let source = if self.workspace_members.contains(&package_id) {
PackageSourceImpl::Workspace(self.workspace_path(&package_id, &package.manifest_path)?)
} else if let Some(source) = package.source {
if source.is_crates_io() {
PackageSourceImpl::CratesIo
} else {
PackageSourceImpl::External(source.repr.into())
}
} else {
// Path dependency: get the directory from the manifest path.
let dirname = match package.manifest_path.parent() {
Some(dirname) => dirname,
None => {
return Err(Error::PackageGraphConstructError(format!(
"package '{}': manifest path '{}' does not have parent",
package_id, package.manifest_path,
)));
}
};
let rel_path = pathdiff::diff_utf8_paths(dirname, self.workspace_root)
.expect("workspace root is absolute");
PackageSourceImpl::Path(convert_forward_slashes(rel_path).into_boxed_path())
};
let mut build_targets = BuildTargets::new(&package_id);
for build_target in package.targets {
build_targets.add(build_target)?;
}
let build_targets = build_targets.finish();
// resolved_deps is missing if the metadata was generated with --no-deps.
let resolved_deps = self.resolve_data.remove(&package_id).unwrap_or_default();
let dep_resolver =
DependencyResolver::new(&package_id, &self.package_data, &package.dependencies);
for NodeDep {
name: resolved_name,
pkg,
..
} in resolved_deps
{
let dep_id = PackageId::from_metadata(pkg);
let (name, deps) = dep_resolver.resolve(&resolved_name, &dep_id)?;
let (dep_idx, _, _) = self.package_data(&dep_id)?;
let edge = PackageLinkImpl::new(&package_id, name, &resolved_name, deps)?;
// Use update_edge instead of add_edge to prevent multiple edges from being added
// between these two nodes.
// XXX maybe check for an existing edge?
self.dep_graph.update_edge(package_ix, dep_idx, edge);
}
let has_default_feature = package.features.contains_key("default");
// Optional dependencies could in principle be computed by looking at the edges out of this
// package, but unresolved dependencies aren't part of the graph so we're going to miss them
// (and many optional dependencies will be unresolved).
//
// XXX: Consider modeling unresolved dependencies in the graph.
//
// A dependency might be listed multiple times (e.g. as a build dependency and as a normal
// one). Some of them might be optional, some might not be. List a dependency here if *any*
// of those specifications are optional, since that's how Cargo features work. But also
// dedup them.
let optional_deps: IndexSet<_> = package
.dependencies
.into_iter()
.filter_map(|dep| {
if dep.optional {
match dep.rename {
Some(rename) => Some(rename.into_boxed_str()),
None => Some(dep.name.into_boxed_str()),
}
} else {
None
}
})
.collect();
// Has the explicit feature by the name of this optional dep been seen?
let mut seen_explicit = FixedBitSet::with_capacity(optional_deps.len());
// The feature map contains both optional deps and named features.
let mut named_features: IndexMap<_, _> = package
.features
.into_iter()
.map(|(feature_name, deps)| {
let mut parsed_deps = SmallVec::with_capacity(deps.len());
for dep in deps {
let dep = NamedFeatureDep::from_cargo_string(dep);
if let NamedFeatureDep::OptionalDependency(d) = &dep {
let index = optional_deps.get_index_of(d.as_ref()).ok_or_else(|| {
Error::PackageGraphConstructError(format!(
"package '{}': named feature {} specifies 'dep:{d}', but {d} is not an optional dependency",
package_id,
feature_name,
d = d))
})?;
seen_explicit.set(index, true);
}
parsed_deps.push(dep);
}
Ok((feature_name.into_boxed_str(), parsed_deps))
})
.collect::<Result<_, Error>>()?;
// If an optional dependency was not seen explicitly, add an implicit named feature for it.
for (index, dep) in optional_deps.iter().enumerate() {
if !seen_explicit.contains(index) {
named_features.insert(
dep.clone(),
std::iter::once(NamedFeatureDep::OptionalDependency(dep.clone())).collect(),
);
}
}
Ok((
package_id,
PackageMetadataImpl {
name: package.name,
version: package.version,
authors: package.authors,
description: package.description.map(|s| s.into()),
license: package.license.map(|s| s.into()),
license_file: package.license_file.map(|f| f.into()),
manifest_path: package.manifest_path.into(),
categories: package.categories,
keywords: package.keywords,
readme: package.readme.map(|s| s.into()),
repository: package.repository.map(|s| s.into()),
homepage: package.homepage.map(|s| s.into()),
documentation: package.documentation.map(|s| s.into()),
edition: package.edition.into(),
metadata_table: package.metadata,
links: package.links.map(|s| s.into()),
publish: PackagePublishImpl::new(package.publish),
default_run: package.default_run.map(|s| s.into()),
rust_version: package.rust_version,
named_features,
optional_deps,
package_ix,
source,
build_targets,
has_default_feature,
},
))
}