mirror of
https://github.com/rust-lang/cargo.git
synced 2025-10-01 11:30:39 +00:00
just give up and make it an error
This commit is contained in:
parent
a4737160bf
commit
35ff555b70
@ -9,7 +9,6 @@ use log::debug;
|
|||||||
|
|
||||||
use crate::core::interning::InternedString;
|
use crate::core::interning::InternedString;
|
||||||
use crate::core::{Dependency, FeatureValue, PackageId, SourceId, Summary};
|
use crate::core::{Dependency, FeatureValue, PackageId, SourceId, Summary};
|
||||||
use crate::util::config::Config;
|
|
||||||
use crate::util::CargoResult;
|
use crate::util::CargoResult;
|
||||||
use crate::util::Graph;
|
use crate::util::Graph;
|
||||||
|
|
||||||
@ -158,7 +157,7 @@ impl Context {
|
|||||||
// First, figure out our set of dependencies based on the requested set
|
// First, figure out our set of dependencies based on the requested set
|
||||||
// of features. This also calculates what features we're going to enable
|
// of features. This also calculates what features we're going to enable
|
||||||
// for our own dependencies.
|
// for our own dependencies.
|
||||||
let deps = self.resolve_features(parent, candidate, method, None)?;
|
let deps = self.resolve_features(parent, candidate, method)?;
|
||||||
|
|
||||||
// Next, transform all dependencies into a list of possible candidates
|
// Next, transform all dependencies into a list of possible candidates
|
||||||
// which can satisfy that dependency.
|
// which can satisfy that dependency.
|
||||||
@ -218,7 +217,6 @@ impl Context {
|
|||||||
parent: Option<&Summary>,
|
parent: Option<&Summary>,
|
||||||
s: &'b Summary,
|
s: &'b Summary,
|
||||||
method: &'b Method<'_>,
|
method: &'b Method<'_>,
|
||||||
config: Option<&Config>,
|
|
||||||
) -> ActivateResult<Vec<(Dependency, Vec<InternedString>)>> {
|
) -> ActivateResult<Vec<(Dependency, Vec<InternedString>)>> {
|
||||||
let dev_deps = match *method {
|
let dev_deps = match *method {
|
||||||
Method::Everything => true,
|
Method::Everything => true,
|
||||||
@ -246,23 +244,26 @@ impl Context {
|
|||||||
// name.
|
// name.
|
||||||
let base = reqs.deps.get(&dep.name_in_toml()).unwrap_or(&default_dep);
|
let base = reqs.deps.get(&dep.name_in_toml()).unwrap_or(&default_dep);
|
||||||
used_features.insert(dep.name_in_toml());
|
used_features.insert(dep.name_in_toml());
|
||||||
if let Some(config) = config {
|
let always_required = !dep.is_optional()
|
||||||
let mut shell = config.shell();
|
&& !s
|
||||||
let always_required = !dep.is_optional()
|
.dependencies()
|
||||||
&& !s
|
.iter()
|
||||||
.dependencies()
|
.any(|d| d.is_optional() && d.name_in_toml() == dep.name_in_toml());
|
||||||
.iter()
|
if always_required && base.0 {
|
||||||
.any(|d| d.is_optional() && d.name_in_toml() == dep.name_in_toml());
|
return Err(match parent {
|
||||||
if always_required && base.0 {
|
None => failure::format_err!(
|
||||||
shell.warn(&format!(
|
|
||||||
"Package `{}` does not have feature `{}`. It has a required dependency \
|
"Package `{}` does not have feature `{}`. It has a required dependency \
|
||||||
with that name, but only optional dependencies can be used as features. \
|
with that name, but only optional dependencies can be used as features.",
|
||||||
This is currently a warning to ease the transition, but it will become an \
|
|
||||||
error in the future.",
|
|
||||||
s.package_id(),
|
s.package_id(),
|
||||||
dep.name_in_toml()
|
dep.name_in_toml()
|
||||||
))?
|
)
|
||||||
}
|
.into(),
|
||||||
|
Some(p) => (
|
||||||
|
p.package_id(),
|
||||||
|
ConflictReason::RequiredDependencyAsFeatures(dep.name_in_toml()),
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
let mut base = base.1.clone();
|
let mut base = base.1.clone();
|
||||||
base.extend(dep.features().iter());
|
base.extend(dep.features().iter());
|
||||||
|
@ -127,7 +127,7 @@ pub(super) fn activation_error(
|
|||||||
msg.push_str(&describe_path(&cx.parents.path_to_bottom(p)));
|
msg.push_str(&describe_path(&cx.parents.path_to_bottom(p)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let (features_errors, other_errors): (Vec<_>, Vec<_>) = other_errors
|
let (features_errors, mut other_errors): (Vec<_>, Vec<_>) = other_errors
|
||||||
.drain(..)
|
.drain(..)
|
||||||
.partition(|&(_, r)| r.is_missing_features());
|
.partition(|&(_, r)| r.is_missing_features());
|
||||||
|
|
||||||
@ -146,6 +146,29 @@ pub(super) fn activation_error(
|
|||||||
// p == parent so the full path is redundant.
|
// p == parent so the full path is redundant.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let (required_dependency_as_features_errors, other_errors): (Vec<_>, Vec<_>) = other_errors
|
||||||
|
.drain(..)
|
||||||
|
.partition(|&(_, r)| r.is_required_dependency_as_features());
|
||||||
|
|
||||||
|
for &(p, r) in required_dependency_as_features_errors.iter() {
|
||||||
|
if let ConflictReason::RequiredDependencyAsFeatures(ref features) = *r {
|
||||||
|
msg.push_str("\n\nthe package `");
|
||||||
|
msg.push_str(&*p.name());
|
||||||
|
msg.push_str("` depends on `");
|
||||||
|
msg.push_str(&*dep.package_name());
|
||||||
|
msg.push_str("`, with features: `");
|
||||||
|
msg.push_str(features);
|
||||||
|
msg.push_str("` but `");
|
||||||
|
msg.push_str(&*dep.package_name());
|
||||||
|
msg.push_str("` does not have these features.\n");
|
||||||
|
msg.push_str(
|
||||||
|
" It has a required dependency with that name, \
|
||||||
|
but only optional dependencies can be used as features.\n",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// p == parent so the full path is redundant.
|
||||||
|
}
|
||||||
|
|
||||||
if !other_errors.is_empty() {
|
if !other_errors.is_empty() {
|
||||||
msg.push_str(
|
msg.push_str(
|
||||||
"\n\nall possible versions conflict with \
|
"\n\nall possible versions conflict with \
|
||||||
|
@ -124,7 +124,6 @@ pub fn resolve(
|
|||||||
registry: &mut dyn Registry,
|
registry: &mut dyn Registry,
|
||||||
try_to_use: &HashSet<PackageId>,
|
try_to_use: &HashSet<PackageId>,
|
||||||
config: Option<&Config>,
|
config: Option<&Config>,
|
||||||
print_warnings: bool,
|
|
||||||
check_public_visible_dependencies: bool,
|
check_public_visible_dependencies: bool,
|
||||||
) -> CargoResult<Resolve> {
|
) -> CargoResult<Resolve> {
|
||||||
let cx = Context::new(check_public_visible_dependencies);
|
let cx = Context::new(check_public_visible_dependencies);
|
||||||
@ -157,11 +156,6 @@ pub fn resolve(
|
|||||||
check_duplicate_pkgs_in_lockfile(&resolve)?;
|
check_duplicate_pkgs_in_lockfile(&resolve)?;
|
||||||
trace!("resolved: {:?}", resolve);
|
trace!("resolved: {:?}", resolve);
|
||||||
|
|
||||||
// If we have a shell, emit warnings about required deps used as feature.
|
|
||||||
if print_warnings && config.is_some() {
|
|
||||||
emit_warnings(&cx, &resolve, summaries, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(resolve)
|
Ok(resolve)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1087,65 +1081,3 @@ fn check_duplicate_pkgs_in_lockfile(resolve: &Resolve) -> CargoResult<()> {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Re-run all used `resolve_features` so it can print warnings.
|
|
||||||
/// Within the resolution loop we do not pass a `config` to `resolve_features`
|
|
||||||
/// this tells it not to print warnings. Hear we do pass `config` triggering it to print them.
|
|
||||||
/// This is done as the resolution is NP-Hard so the loop can potentially call `resolve features`
|
|
||||||
/// an exponential number of times, but this pass is linear in the number of dependencies.
|
|
||||||
fn emit_warnings(
|
|
||||||
cx: &Context,
|
|
||||||
resolve: &Resolve,
|
|
||||||
summaries: &[(Summary, Method<'_>)],
|
|
||||||
config: Option<&Config>,
|
|
||||||
) {
|
|
||||||
let mut new_cx = cx.clone();
|
|
||||||
new_cx.resolve_features = im_rc::HashMap::new();
|
|
||||||
let mut features_from_dep = HashMap::new();
|
|
||||||
for (summary, method) in summaries {
|
|
||||||
let replacement = &cx.activations[&resolve
|
|
||||||
.replacements()
|
|
||||||
.get(&summary.package_id())
|
|
||||||
.unwrap_or(&summary.package_id())
|
|
||||||
.as_activations_key()]
|
|
||||||
.0;
|
|
||||||
for (dep, features) in new_cx
|
|
||||||
.resolve_features(None, replacement, &method, config)
|
|
||||||
.expect("can not resolve_features for a required summery")
|
|
||||||
{
|
|
||||||
features_from_dep.insert((replacement.package_id(), dep), features);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// crates enable features for their dependencies.
|
|
||||||
// So by iterating reverse topologically we process all of the parents before each child.
|
|
||||||
// Then we know all the needed features for each crate.
|
|
||||||
let topological_sort = resolve.sort();
|
|
||||||
for id in topological_sort
|
|
||||||
.iter()
|
|
||||||
.rev()
|
|
||||||
.filter(|&id| !resolve.replacements().contains_key(id))
|
|
||||||
{
|
|
||||||
let summary = &cx.activations[&id.as_activations_key()].0;
|
|
||||||
|
|
||||||
for (parent, deps) in cx.parents.edges(&summary.package_id()) {
|
|
||||||
for dep in deps.iter() {
|
|
||||||
let features = features_from_dep
|
|
||||||
.remove(&(*parent, dep.clone()))
|
|
||||||
.expect("fulfilled a dep that was not needed");
|
|
||||||
let method = Method::Required {
|
|
||||||
dev_deps: false,
|
|
||||||
features: &features,
|
|
||||||
all_features: false,
|
|
||||||
uses_default_features: dep.uses_default_features(),
|
|
||||||
};
|
|
||||||
for (dep, features) in new_cx
|
|
||||||
.resolve_features(None, summary, &method, config)
|
|
||||||
.expect("can not resolve_features for a used dep")
|
|
||||||
{
|
|
||||||
features_from_dep.insert((summary.package_id(), dep), features);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert_eq!(cx.resolve_features, new_cx.resolve_features);
|
|
||||||
}
|
|
||||||
|
@ -426,6 +426,12 @@ pub enum ConflictReason {
|
|||||||
/// candidate we're activating didn't actually have the feature `foo`.
|
/// candidate we're activating didn't actually have the feature `foo`.
|
||||||
MissingFeatures(String),
|
MissingFeatures(String),
|
||||||
|
|
||||||
|
/// A dependency listed features that ended up being a required dependency.
|
||||||
|
/// For example we tried to activate feature `foo` but the
|
||||||
|
/// candidate we're activating didn't actually have the feature `foo`
|
||||||
|
/// it had a dependency `foo` instead.
|
||||||
|
RequiredDependencyAsFeatures(InternedString),
|
||||||
|
|
||||||
// TODO: needs more info for `activation_error`
|
// TODO: needs more info for `activation_error`
|
||||||
// TODO: needs more info for `find_candidate`
|
// TODO: needs more info for `find_candidate`
|
||||||
/// pub dep error
|
/// pub dep error
|
||||||
@ -446,6 +452,13 @@ impl ConflictReason {
|
|||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_required_dependency_as_features(&self) -> bool {
|
||||||
|
if let ConflictReason::RequiredDependencyAsFeatures(_) = *self {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A list of packages that have gotten in the way of resolving a dependency.
|
/// A list of packages that have gotten in the way of resolving a dependency.
|
||||||
|
@ -21,16 +21,8 @@ pub struct UpdateOptions<'a> {
|
|||||||
|
|
||||||
pub fn generate_lockfile(ws: &Workspace<'_>) -> CargoResult<()> {
|
pub fn generate_lockfile(ws: &Workspace<'_>) -> CargoResult<()> {
|
||||||
let mut registry = PackageRegistry::new(ws.config())?;
|
let mut registry = PackageRegistry::new(ws.config())?;
|
||||||
let resolve = ops::resolve_with_previous(
|
let resolve =
|
||||||
&mut registry,
|
ops::resolve_with_previous(&mut registry, ws, Method::Everything, None, None, &[], true)?;
|
||||||
ws,
|
|
||||||
Method::Everything,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
&[],
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
)?;
|
|
||||||
ops::write_pkg_lockfile(ws, &resolve)?;
|
ops::write_pkg_lockfile(ws, &resolve)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -92,7 +84,6 @@ pub fn update_lockfile(ws: &Workspace<'_>, opts: &UpdateOptions<'_>) -> CargoRes
|
|||||||
Some(&to_avoid),
|
Some(&to_avoid),
|
||||||
&[],
|
&[],
|
||||||
true,
|
true,
|
||||||
true,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Summarize what is changing for the user.
|
// Summarize what is changing for the user.
|
||||||
|
@ -23,7 +23,7 @@ version. This may also occur with an optional dependency that is not enabled.";
|
|||||||
/// lock file.
|
/// lock file.
|
||||||
pub fn resolve_ws<'a>(ws: &Workspace<'a>) -> CargoResult<(PackageSet<'a>, Resolve)> {
|
pub fn resolve_ws<'a>(ws: &Workspace<'a>) -> CargoResult<(PackageSet<'a>, Resolve)> {
|
||||||
let mut registry = PackageRegistry::new(ws.config())?;
|
let mut registry = PackageRegistry::new(ws.config())?;
|
||||||
let resolve = resolve_with_registry(ws, &mut registry, true)?;
|
let resolve = resolve_with_registry(ws, &mut registry)?;
|
||||||
let packages = get_resolved_packages(&resolve, registry)?;
|
let packages = get_resolved_packages(&resolve, registry)?;
|
||||||
Ok((packages, resolve))
|
Ok((packages, resolve))
|
||||||
}
|
}
|
||||||
@ -64,7 +64,7 @@ pub fn resolve_ws_with_method<'a>(
|
|||||||
} else if ws.require_optional_deps() {
|
} else if ws.require_optional_deps() {
|
||||||
// First, resolve the root_package's *listed* dependencies, as well as
|
// First, resolve the root_package's *listed* dependencies, as well as
|
||||||
// downloading and updating all remotes and such.
|
// downloading and updating all remotes and such.
|
||||||
let resolve = resolve_with_registry(ws, &mut registry, false)?;
|
let resolve = resolve_with_registry(ws, &mut registry)?;
|
||||||
add_patches = false;
|
add_patches = false;
|
||||||
|
|
||||||
// Second, resolve with precisely what we're doing. Filter out
|
// Second, resolve with precisely what we're doing. Filter out
|
||||||
@ -98,7 +98,6 @@ pub fn resolve_ws_with_method<'a>(
|
|||||||
None,
|
None,
|
||||||
specs,
|
specs,
|
||||||
add_patches,
|
add_patches,
|
||||||
true,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let packages = get_resolved_packages(&resolved_with_overrides, registry)?;
|
let packages = get_resolved_packages(&resolved_with_overrides, registry)?;
|
||||||
@ -109,7 +108,6 @@ pub fn resolve_ws_with_method<'a>(
|
|||||||
fn resolve_with_registry<'cfg>(
|
fn resolve_with_registry<'cfg>(
|
||||||
ws: &Workspace<'cfg>,
|
ws: &Workspace<'cfg>,
|
||||||
registry: &mut PackageRegistry<'cfg>,
|
registry: &mut PackageRegistry<'cfg>,
|
||||||
warn: bool,
|
|
||||||
) -> CargoResult<Resolve> {
|
) -> CargoResult<Resolve> {
|
||||||
let prev = ops::load_pkg_lockfile(ws)?;
|
let prev = ops::load_pkg_lockfile(ws)?;
|
||||||
let resolve = resolve_with_previous(
|
let resolve = resolve_with_previous(
|
||||||
@ -120,7 +118,6 @@ fn resolve_with_registry<'cfg>(
|
|||||||
None,
|
None,
|
||||||
&[],
|
&[],
|
||||||
true,
|
true,
|
||||||
warn,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if !ws.is_ephemeral() {
|
if !ws.is_ephemeral() {
|
||||||
@ -146,7 +143,6 @@ pub fn resolve_with_previous<'cfg>(
|
|||||||
to_avoid: Option<&HashSet<PackageId>>,
|
to_avoid: Option<&HashSet<PackageId>>,
|
||||||
specs: &[PackageIdSpec],
|
specs: &[PackageIdSpec],
|
||||||
register_patches: bool,
|
register_patches: bool,
|
||||||
warn: bool,
|
|
||||||
) -> CargoResult<Resolve> {
|
) -> CargoResult<Resolve> {
|
||||||
// Here we place an artificial limitation that all non-registry sources
|
// Here we place an artificial limitation that all non-registry sources
|
||||||
// cannot be locked at more than one revision. This means that if a Git
|
// cannot be locked at more than one revision. This means that if a Git
|
||||||
@ -334,7 +330,6 @@ pub fn resolve_with_previous<'cfg>(
|
|||||||
registry,
|
registry,
|
||||||
&try_to_use,
|
&try_to_use,
|
||||||
Some(ws.config()),
|
Some(ws.config()),
|
||||||
warn,
|
|
||||||
false, // TODO: use "public and private dependencies" feature flag
|
false, // TODO: use "public and private dependencies" feature flag
|
||||||
)?;
|
)?;
|
||||||
resolved.register_used_patches(registry.patches());
|
resolved.register_used_patches(registry.patches());
|
||||||
|
@ -291,13 +291,12 @@ fn invalid9() {
|
|||||||
.file("bar/src/lib.rs", "")
|
.file("bar/src/lib.rs", "")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
p.cargo("build --features bar").with_stderr("\
|
p.cargo("build --features bar")
|
||||||
warning: Package `foo v0.0.1 ([..])` does not have feature `bar`. It has a required dependency with \
|
.with_stderr(
|
||||||
that name, but only optional dependencies can be used as features. [..]
|
"\
|
||||||
Compiling bar v0.0.1 ([..])
|
error: Package `foo v0.0.1 ([..])` does not have feature `bar`. It has a required dependency with that name, but only optional dependencies can be used as features.
|
||||||
Compiling foo v0.0.1 ([..])
|
",
|
||||||
Finished dev [unoptimized + debuginfo] target(s) in [..]s
|
).with_status(101).run();
|
||||||
").run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -335,13 +334,17 @@ fn invalid10() {
|
|||||||
.build();
|
.build();
|
||||||
|
|
||||||
p.cargo("build").with_stderr("\
|
p.cargo("build").with_stderr("\
|
||||||
warning: Package `bar v0.0.1 ([..])` does not have feature `baz`. It has a required dependency with \
|
error: failed to select a version for `bar`.
|
||||||
that name, but only optional dependencies can be used as features. [..]
|
... required by package `foo v0.0.1 ([..])`
|
||||||
Compiling baz v0.0.1 ([..])
|
versions that meet the requirements `*` are: 0.0.1
|
||||||
Compiling bar v0.0.1 ([..])
|
|
||||||
Compiling foo v0.0.1 ([..])
|
the package `foo` depends on `bar`, with features: `baz` but `bar` does not have these features.
|
||||||
Finished dev [unoptimized + debuginfo] target(s) in [..]s
|
It has a required dependency with that name, but only optional dependencies can be used as features.
|
||||||
").run();
|
|
||||||
|
|
||||||
|
failed to select a version for `bar` which could resolve this conflict
|
||||||
|
").with_status(101)
|
||||||
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -115,7 +115,6 @@ pub fn resolve_with_config_raw(
|
|||||||
&HashSet::new(),
|
&HashSet::new(),
|
||||||
config,
|
config,
|
||||||
true,
|
true,
|
||||||
true,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// The largest test in our suite takes less then 30 sec.
|
// The largest test in our suite takes less then 30 sec.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user