cargo-metadata: error out when encountering invalid artifact kind syntax

This refactor reuse the logic of
`core::compiler::unit_dependencies::match_artifacts_kind_with_targets`
to emit error if there is any syntax error in `ArtifactKind`.

It also put `match_artifacts_kind_with_targets` to a better place `core::compiler::artifact`.
This commit is contained in:
Weihang Lo 2023-01-06 21:56:49 +00:00
parent e5ec492b6f
commit 272193aa29
No known key found for this signature in database
GPG Key ID: D7DBF189825E82E7
4 changed files with 153 additions and 113 deletions

View File

@ -1,7 +1,8 @@
/// Generate artifact information from unit dependencies for configuring the compiler environment. /// Generate artifact information from unit dependencies for configuring the compiler environment.
use crate::core::compiler::unit_graph::UnitDep; use crate::core::compiler::unit_graph::UnitDep;
use crate::core::compiler::{Context, CrateType, FileFlavor, Unit}; use crate::core::compiler::{Context, CrateType, FileFlavor, Unit};
use crate::core::TargetKind; use crate::core::dependency::ArtifactKind;
use crate::core::{Dependency, Target, TargetKind};
use crate::CargoResult; use crate::CargoResult;
use std::collections::HashMap; use std::collections::HashMap;
use std::ffi::OsString; use std::ffi::OsString;
@ -55,3 +56,45 @@ fn unit_artifact_type_name_upper(unit: &Unit) -> &'static str {
invalid => unreachable!("BUG: artifacts cannot be of type {:?}", invalid), invalid => unreachable!("BUG: artifacts cannot be of type {:?}", invalid),
} }
} }
/// Given a dependency with an artifact `artifact_dep` and a set of available `targets`
/// of its package, find a target for each kind of artifacts that are to be built.
///
/// Failure to match any target results in an error mentioning the parent manifests
/// `parent_package` name.
pub(crate) fn match_artifacts_kind_with_targets<'a, F>(
artifact_dep: &Dependency,
targets: &'a [Target],
parent_package: &str,
mut callback: F,
) -> CargoResult<()>
where
F: FnMut(&ArtifactKind, &mut dyn Iterator<Item = &'a Target>),
{
let artifact_requirements = artifact_dep.artifact().expect("artifact present");
for artifact_kind in artifact_requirements.kinds() {
let mut extend = |kind: &ArtifactKind, filter: &dyn Fn(&&Target) -> bool| {
let mut iter = targets.iter().filter(filter).peekable();
let found = iter.peek().is_some();
callback(kind, &mut iter);
found
};
let found = match artifact_kind {
ArtifactKind::Cdylib => extend(artifact_kind, &|t| t.is_cdylib()),
ArtifactKind::Staticlib => extend(artifact_kind, &|t| t.is_staticlib()),
ArtifactKind::AllBinaries => extend(artifact_kind, &|t| t.is_bin()),
ArtifactKind::SelectedBinary(bin_name) => extend(artifact_kind, &|t| {
t.is_bin() && t.name() == bin_name.as_str()
}),
};
if !found {
anyhow::bail!(
"dependency `{}` in package `{}` requires a `{}` artifact to be present.",
artifact_dep.name_in_toml(),
parent_package,
artifact_kind
);
}
}
Ok(())
}

View File

@ -19,11 +19,12 @@ use std::collections::{HashMap, HashSet};
use log::trace; use log::trace;
use crate::core::compiler::artifact::match_artifacts_kind_with_targets;
use crate::core::compiler::unit_graph::{UnitDep, UnitGraph}; use crate::core::compiler::unit_graph::{UnitDep, UnitGraph};
use crate::core::compiler::{ use crate::core::compiler::{
CompileKind, CompileMode, CrateType, RustcTargetData, Unit, UnitInterner, CompileKind, CompileMode, CrateType, RustcTargetData, Unit, UnitInterner,
}; };
use crate::core::dependency::{Artifact, ArtifactKind, ArtifactTarget, DepKind}; use crate::core::dependency::{Artifact, ArtifactTarget, DepKind};
use crate::core::profiles::{Profile, Profiles, UnitFor}; use crate::core::profiles::{Profile, Profiles, UnitFor};
use crate::core::resolver::features::{FeaturesFor, ResolvedFeatures}; use crate::core::resolver::features::{FeaturesFor, ResolvedFeatures};
use crate::core::resolver::Resolve; use crate::core::resolver::Resolve;
@ -554,8 +555,14 @@ fn artifact_targets_to_unit_deps(
artifact_pkg: &Package, artifact_pkg: &Package,
dep: &Dependency, dep: &Dependency,
) -> CargoResult<Vec<UnitDep>> { ) -> CargoResult<Vec<UnitDep>> {
let ret = let mut targets = HashSet::new();
match_artifacts_kind_with_targets(dep, artifact_pkg.targets(), parent.pkg.name().as_str())? match_artifacts_kind_with_targets(
dep,
artifact_pkg.targets(),
parent.pkg.name().as_str(),
|_, iter| targets.extend(iter),
)?;
let ret = targets
.into_iter() .into_iter()
.flat_map(|target| { .flat_map(|target| {
// We split target libraries into individual units, even though rustc is able // We split target libraries into individual units, even though rustc is able
@ -598,45 +605,6 @@ fn artifact_targets_to_unit_deps(
Ok(ret) Ok(ret)
} }
/// Given a dependency with an artifact `artifact_dep` and a set of available `targets`
/// of its package, find a target for each kind of artifacts that are to be built.
///
/// Failure to match any target results in an error mentioning the parent manifests
/// `parent_package` name.
fn match_artifacts_kind_with_targets<'a>(
artifact_dep: &Dependency,
targets: &'a [Target],
parent_package: &str,
) -> CargoResult<HashSet<&'a Target>> {
let mut out = HashSet::new();
let artifact_requirements = artifact_dep.artifact().expect("artifact present");
for artifact_kind in artifact_requirements.kinds() {
let mut extend = |filter: &dyn Fn(&&Target) -> bool| {
let mut iter = targets.iter().filter(filter).peekable();
let found = iter.peek().is_some();
out.extend(iter);
found
};
let found = match artifact_kind {
ArtifactKind::Cdylib => extend(&|t| t.is_cdylib()),
ArtifactKind::Staticlib => extend(&|t| t.is_staticlib()),
ArtifactKind::AllBinaries => extend(&|t| t.is_bin()),
ArtifactKind::SelectedBinary(bin_name) => {
extend(&|t| t.is_bin() && t.name() == bin_name.as_str())
}
};
if !found {
anyhow::bail!(
"dependency `{}` in package `{}` requires a `{}` artifact to be present.",
artifact_dep.name_in_toml(),
parent_package,
artifact_kind
);
}
}
Ok(out)
}
/// Returns the dependencies necessary to document a package. /// Returns the dependencies necessary to document a package.
fn compute_deps_doc( fn compute_deps_doc(
unit: &Unit, unit: &Unit,

View File

@ -1,8 +1,9 @@
use crate::core::compiler::artifact::match_artifacts_kind_with_targets;
use crate::core::compiler::{CompileKind, RustcTargetData}; use crate::core::compiler::{CompileKind, RustcTargetData};
use crate::core::dependency::{ArtifactKind, DepKind}; use crate::core::dependency::DepKind;
use crate::core::package::SerializedPackage; use crate::core::package::SerializedPackage;
use crate::core::resolver::{features::CliFeatures, HasDevUnits, Resolve}; use crate::core::resolver::{features::CliFeatures, HasDevUnits, Resolve};
use crate::core::{Package, PackageId, Target, Workspace}; use crate::core::{Package, PackageId, Workspace};
use crate::ops::{self, Packages}; use crate::ops::{self, Packages};
use crate::util::interning::InternedString; use crate::util::interning::InternedString;
use crate::util::CargoResult; use crate::util::CargoResult;
@ -160,7 +161,7 @@ fn build_resolve_graph(
&package_map, &package_map,
&target_data, &target_data,
&requested_kinds, &requested_kinds,
); )?;
} }
// Get a Vec of Packages. // Get a Vec of Packages.
let actual_packages = package_map let actual_packages = package_map
@ -183,9 +184,9 @@ fn build_resolve_graph_r(
package_map: &BTreeMap<PackageId, Package>, package_map: &BTreeMap<PackageId, Package>,
target_data: &RustcTargetData<'_>, target_data: &RustcTargetData<'_>,
requested_kinds: &[CompileKind], requested_kinds: &[CompileKind],
) { ) -> CargoResult<()> {
if node_map.contains_key(&pkg_id) { if node_map.contains_key(&pkg_id) {
return; return Ok(());
} }
// This normalizes the IDs so that they are consistent between the // This normalizes the IDs so that they are consistent between the
// `packages` array and the `resolve` map. This is a bit of a hack to // `packages` array and the `resolve` map. This is a bit of a hack to
@ -268,27 +269,22 @@ fn build_resolve_graph_r(
None => None, None => None,
}; };
let mut extend = |kind: &ArtifactKind, filter: &dyn Fn(&&Target) -> bool| { if let Err(e) = match_artifacts_kind_with_targets(
let iter = targets.iter().filter(filter).map(|target| DepKindInfo { dep,
targets,
pkg_id.name().as_str(),
|kind, targets| {
dep_kinds.extend(targets.map(|target| DepKindInfo {
kind: dep.kind(), kind: dep.kind(),
target: dep.platform().cloned(), target: dep.platform().cloned(),
artifact: Some(kind.crate_type()), artifact: Some(kind.crate_type()),
extern_name: extern_name(target), extern_name: extern_name(target),
compile_target, compile_target,
bin_name: target.is_bin().then(|| target.name().to_string()), bin_name: target.is_bin().then(|| target.name().to_string()),
}); }))
dep_kinds.extend(iter); },
}; ) {
return Some(Err(e));
for kind in artifact_requirements.kinds() {
match kind {
ArtifactKind::Cdylib => extend(kind, &|t| t.is_cdylib()),
ArtifactKind::Staticlib => extend(kind, &|t| t.is_staticlib()),
ArtifactKind::AllBinaries => extend(kind, &|t| t.is_bin()),
ArtifactKind::SelectedBinary(bin_name) => {
extend(kind, &|t| t.is_bin() && t.name() == bin_name.as_str())
}
};
} }
} }
@ -296,7 +292,7 @@ fn build_resolve_graph_r(
let pkg = normalize_id(dep_id); let pkg = normalize_id(dep_id);
match (lib_target_name, dep_kinds.len()) { let dep = match (lib_target_name, dep_kinds.len()) {
(Some(name), _) => Some(Dep { (Some(name), _) => Some(Dep {
name, name,
pkg, pkg,
@ -311,10 +307,11 @@ fn build_resolve_graph_r(
// No lib or artifact dep exists. // No lib or artifact dep exists.
// Ususally this mean parent depending on non-lib bin crate. // Ususally this mean parent depending on non-lib bin crate.
(None, _) => None, (None, _) => None,
} };
dep.map(Ok)
}) })
.collect(); .collect::<CargoResult<_>>()?;
let dumb_deps: Vec<PackageId> = deps.iter().map(|dep| normalize_id(dep.pkg)).collect(); let dumb_deps: Vec<PackageId> = deps.iter().map(|dep| dep.pkg).collect();
let to_visit = dumb_deps.clone(); let to_visit = dumb_deps.clone();
let node = MetadataResolveNode { let node = MetadataResolveNode {
id: normalize_id(pkg_id), id: normalize_id(pkg_id),
@ -331,6 +328,8 @@ fn build_resolve_graph_r(
package_map, package_map,
target_data, target_data,
requested_kinds, requested_kinds,
); )?;
} }
Ok(())
} }

View File

@ -1857,6 +1857,36 @@ Caused by:
.run(); .run();
} }
#[cargo_test]
fn cargo_metadata_with_invalid_artifact_deps() {
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.5.0"
[dependencies]
artifact = { path = "artifact", artifact = "bin:notfound" }
"#,
)
.file("src/lib.rs", "")
.file("artifact/Cargo.toml", &basic_bin_manifest("artifact"))
.file("artifact/src/main.rs", "fn main() {}")
.build();
p.cargo("metadata -Z bindeps")
.masquerade_as_nightly_cargo(&["bindeps"])
.with_status(101)
.with_stderr(
"\
[WARNING] please specify `--format-version` flag explicitly to avoid compatibility problems
[ERROR] dependency `artifact` in package `foo` requires a `bin:notfound` artifact to be present.",
)
.run();
}
const MANIFEST_OUTPUT: &str = r#" const MANIFEST_OUTPUT: &str = r#"
{ {
"packages": [{ "packages": [{