Skip to content

Commit fe0c18b

Browse files
committed
Auto merge of #4978 - Eh2406:master, r=alexcrichton
Only allow one of each links attribute in resolver This is a start on fixing the `links` part of #4902. It needs code that adds the links attribute to the index. But, I wanted to get feedback.
2 parents f75f403 + 68a40ad commit fe0c18b

File tree

11 files changed

+195
-82
lines changed

11 files changed

+195
-82
lines changed

src/cargo/core/manifest.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ pub struct ManifestMetadata {
7878
pub repository: Option<String>, // url
7979
pub documentation: Option<String>, // url
8080
pub badges: BTreeMap<String, BTreeMap<String, String>>,
81+
pub links: Option<String>,
8182
}
8283

8384
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]

src/cargo/core/resolver/mod.rs

Lines changed: 101 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@
2323
//! * Never try to activate a crate version which is incompatible. This means we
2424
//! only try crates which will actually satisfy a dependency and we won't ever
2525
//! try to activate a crate that's semver compatible with something else
26-
//! activated (as we're only allowed to have one).
26+
//! activated (as we're only allowed to have one) nor try to activate a crate
27+
//! that has the same links attribute as something else
28+
//! activated.
2729
//! * Always try to activate the highest version crate first. The default
2830
//! dependency in Cargo (e.g. when you write `foo = "0.1.2"`) is
2931
//! semver-compatible, so selecting the highest version possible will allow us
@@ -325,11 +327,12 @@ enum GraphNode {
325327
// possible.
326328
#[derive(Clone)]
327329
struct Context<'a> {
328-
// TODO: Both this and the map below are super expensive to clone. We should
330+
// TODO: Both this and the two maps below are super expensive to clone. We should
329331
// switch to persistent hash maps if we can at some point or otherwise
330332
// make these much cheaper to clone in general.
331333
activations: Activations,
332334
resolve_features: HashMap<PackageId, HashSet<String>>,
335+
links: HashMap<String, PackageId>,
333336

334337
// These are two cheaply-cloneable lists (O(1) clone) which are effectively
335338
// hash maps but are built up as "construction lists". We'll iterate these
@@ -354,9 +357,10 @@ pub fn resolve(summaries: &[(Summary, Method)],
354357
let cx = Context {
355358
resolve_graph: RcList::new(),
356359
resolve_features: HashMap::new(),
360+
links: HashMap::new(),
357361
resolve_replacements: RcList::new(),
358362
activations: HashMap::new(),
359-
replacements: replacements,
363+
replacements,
360364
warnings: RcList::new(),
361365
};
362366
let _p = profile::start("resolving");
@@ -416,13 +420,13 @@ fn activate(cx: &mut Context,
416420
candidate.summary.package_id().clone()));
417421
}
418422

419-
let activated = cx.flag_activated(&candidate.summary, method);
423+
let activated = cx.flag_activated(&candidate.summary, method)?;
420424

421425
let candidate = match candidate.replace {
422426
Some(replace) => {
423427
cx.resolve_replacements.push((candidate.summary.package_id().clone(),
424428
replace.package_id().clone()));
425-
if cx.flag_activated(&replace, method) && activated {
429+
if cx.flag_activated(&replace, method)? && activated {
426430
return Ok(None);
427431
}
428432
trace!("activating {} (replacing {})", replace.package_id(),
@@ -456,7 +460,7 @@ impl<T> RcVecIter<T> {
456460
fn new(vec: Rc<Vec<T>>) -> RcVecIter<T> {
457461
RcVecIter {
458462
rest: 0..vec.len(),
459-
vec: vec,
463+
vec,
460464
}
461465
}
462466
}
@@ -528,6 +532,21 @@ impl Ord for DepsFrame {
528532
}
529533
}
530534

535+
#[derive(Clone, PartialOrd, Ord, PartialEq, Eq)]
536+
enum ConflictReason {
537+
Semver,
538+
Links(String),
539+
}
540+
541+
impl ConflictReason {
542+
fn is_links(&self) -> bool {
543+
match self {
544+
&ConflictReason::Semver => false,
545+
&ConflictReason::Links(_) => true,
546+
}
547+
}
548+
}
549+
531550
struct BacktrackFrame<'a> {
532551
cur: usize,
533552
context_backup: Context<'a>,
@@ -542,26 +561,35 @@ struct BacktrackFrame<'a> {
542561
struct RemainingCandidates {
543562
remaining: RcVecIter<Candidate>,
544563
// note: change to RcList or something if clone is to expensive
545-
conflicting_prev_active: HashSet<PackageId>,
564+
conflicting_prev_active: HashMap<PackageId, ConflictReason>,
546565
}
547566

548567
impl RemainingCandidates {
549-
fn next(&mut self, prev_active: &[Summary]) -> Result<Candidate, HashSet<PackageId>> {
568+
fn next(&mut self, prev_active: &[Summary], links: &HashMap<String, PackageId>) -> Result<Candidate, HashMap<PackageId, ConflictReason>> {
550569
// Filter the set of candidates based on the previously activated
551570
// versions for this dependency. We can actually use a version if it
552571
// precisely matches an activated version or if it is otherwise
553572
// incompatible with all other activated versions. Note that we
554573
// define "compatible" here in terms of the semver sense where if
555574
// the left-most nonzero digit is the same they're considered
556-
// compatible.
575+
// compatible unless we have a `*-sys` crate (defined by having a
576+
// linked attribute) then we can only have one version.
557577
//
558578
// When we are done we return the set of previously activated
559579
// that conflicted with the ones we tried. If any of these change
560580
// then we would have considered different candidates.
561581
for (_, b) in self.remaining.by_ref() {
582+
if let Some(link) = b.summary.links() {
583+
if let Some(a) = links.get(link) {
584+
if a != b.summary.package_id() {
585+
self.conflicting_prev_active.insert(a.clone(), ConflictReason::Links(link.to_owned()));
586+
continue
587+
}
588+
}
589+
}
562590
if let Some(a) = prev_active.iter().find(|a| compatible(a.version(), b.summary.version())) {
563591
if *a != b.summary {
564-
self.conflicting_prev_active.insert(a.package_id().clone());
592+
self.conflicting_prev_active.insert(a.package_id().clone(), ConflictReason::Semver);
565593
continue
566594
}
567595
}
@@ -660,10 +688,10 @@ fn activate_deps_loop<'a>(mut cx: Context<'a>,
660688
dep.name(), prev_active.len());
661689
let mut candidates = RemainingCandidates {
662690
remaining: RcVecIter::new(Rc::clone(&candidates)),
663-
conflicting_prev_active: HashSet::new(),
691+
conflicting_prev_active: HashMap::new(),
664692
};
665-
(candidates.next(prev_active),
666-
candidates.clone().next(prev_active).is_ok(),
693+
(candidates.next(prev_active, &cx.links),
694+
candidates.clone().next(prev_active, &cx.links).is_ok(),
667695
candidates)
668696
};
669697

@@ -673,7 +701,7 @@ fn activate_deps_loop<'a>(mut cx: Context<'a>,
673701
// 1. The version matches the dependency requirement listed for this
674702
// package
675703
// 2. There are no activated versions for this package which are
676-
// semver-compatible, or there's an activated version which is
704+
// semver/links-compatible, or there's an activated version which is
677705
// precisely equal to `candidate`.
678706
//
679707
// This means that we're going to attempt to activate each candidate in
@@ -688,7 +716,7 @@ fn activate_deps_loop<'a>(mut cx: Context<'a>,
688716
cur,
689717
context_backup: Context::clone(&cx),
690718
deps_backup: <BinaryHeap<DepsFrame>>::clone(&remaining_deps),
691-
remaining_candidates: remaining_candidates,
719+
remaining_candidates,
692720
parent: Summary::clone(&parent),
693721
dep: Dependency::clone(&dep),
694722
features: Rc::clone(&features),
@@ -756,21 +784,21 @@ fn find_candidate<'a>(
756784
cur: &mut usize,
757785
dep: &mut Dependency,
758786
features: &mut Rc<Vec<String>>,
759-
conflicting_activations: &mut HashSet<PackageId>,
787+
conflicting_activations: &mut HashMap<PackageId, ConflictReason>,
760788
) -> Option<Candidate> {
761789
while let Some(mut frame) = backtrack_stack.pop() {
762790
let (next, has_another) = {
763791
let prev_active = frame.context_backup.prev_active(&frame.dep);
764792
(
765-
frame.remaining_candidates.next(prev_active),
766-
frame.remaining_candidates.clone().next(prev_active).is_ok(),
793+
frame.remaining_candidates.next(prev_active, &frame.context_backup.links),
794+
frame.remaining_candidates.clone().next(prev_active, &frame.context_backup.links).is_ok(),
767795
)
768796
};
769797
if frame.context_backup.is_active(parent.package_id())
770798
&& conflicting_activations
771799
.iter()
772800
// note: a lot of redundant work in is_active for similar debs
773-
.all(|con| frame.context_backup.is_active(con))
801+
.all(|(con, _)| frame.context_backup.is_active(con))
774802
{
775803
continue;
776804
}
@@ -800,7 +828,7 @@ fn activation_error(cx: &Context,
800828
registry: &mut Registry,
801829
parent: &Summary,
802830
dep: &Dependency,
803-
conflicting_activations: HashSet<PackageId>,
831+
conflicting_activations: HashMap<PackageId, ConflictReason>,
804832
candidates: &[Candidate],
805833
config: Option<&Config>) -> CargoError {
806834
let graph = cx.graph();
@@ -816,26 +844,53 @@ fn activation_error(cx: &Context,
816844
dep_path_desc
817845
};
818846
if !candidates.is_empty() {
819-
let mut msg = format!("failed to select a version for `{}`.\n\
820-
all possible versions conflict with \
821-
previously selected packages.\n",
822-
dep.name());
823-
msg.push_str("required by ");
847+
let mut msg = format!("failed to select a version for `{}`.", dep.name());
848+
msg.push_str("\n ... required by ");
824849
msg.push_str(&describe_path(parent.package_id()));
825-
let mut conflicting_activations: Vec<_> = conflicting_activations.iter().collect();
826-
conflicting_activations.sort_unstable();
827-
for v in conflicting_activations.iter().rev() {
828-
msg.push_str("\n previously selected ");
829-
msg.push_str(&describe_path(v));
830-
}
831850

832-
msg.push_str("\n possible versions to select: ");
851+
msg.push_str("\nversions that meet the requirements `");
852+
msg.push_str(&dep.version_req().to_string());
853+
msg.push_str("` are: ");
833854
msg.push_str(&candidates.iter()
834855
.map(|v| v.summary.version())
835856
.map(|v| v.to_string())
836857
.collect::<Vec<_>>()
837858
.join(", "));
838859

860+
let mut conflicting_activations: Vec<_> = conflicting_activations.iter().collect();
861+
conflicting_activations.sort_unstable();
862+
let (links_errors, other_errors): (Vec<_>, Vec<_>) = conflicting_activations.drain(..).rev().partition(|&(_, r)| r.is_links());
863+
864+
for &(p, r) in &links_errors {
865+
match r {
866+
&ConflictReason::Links(ref link) => {
867+
msg.push_str("\n\nthe package `");
868+
msg.push_str(dep.name());
869+
msg.push_str("` links to the native library `");
870+
msg.push_str(&link);
871+
msg.push_str("`, but it conflicts with a previous package which links to `");
872+
msg.push_str(&link);
873+
msg.push_str("` as well:\n");
874+
},
875+
_ => (),
876+
}
877+
msg.push_str(&describe_path(p));
878+
}
879+
880+
if links_errors.is_empty() {
881+
msg.push_str("\n\nall possible versions conflict with \
882+
previously selected packages.");
883+
}
884+
885+
for &(p, _) in &other_errors {
886+
msg.push_str("\n\n previously selected ");
887+
msg.push_str(&describe_path(p));
888+
}
889+
890+
msg.push_str("\n\nfailed to select a version for `");
891+
msg.push_str(dep.name());
892+
msg.push_str("` which could resolve this conflict");
893+
839894
return format_err!("{}", msg)
840895
}
841896

@@ -1046,12 +1101,12 @@ fn build_requirements<'a, 'b: 'a>(s: &'a Summary, method: &'b Method)
10461101
}
10471102

10481103
impl<'a> Context<'a> {
1049-
// Activate this summary by inserting it into our list of known activations.
1050-
//
1051-
// Returns if this summary with the given method is already activated.
1104+
/// Activate this summary by inserting it into our list of known activations.
1105+
///
1106+
/// Returns true if this summary with the given method is already activated.
10521107
fn flag_activated(&mut self,
10531108
summary: &Summary,
1054-
method: &Method) -> bool {
1109+
method: &Method) -> CargoResult<bool> {
10551110
let id = summary.package_id();
10561111
let prev = self.activations
10571112
.entry(id.name().to_string())
@@ -1060,26 +1115,31 @@ impl<'a> Context<'a> {
10601115
.or_insert(Vec::new());
10611116
if !prev.iter().any(|c| c == summary) {
10621117
self.resolve_graph.push(GraphNode::Add(id.clone()));
1118+
if let Some(link) = summary.links() {
1119+
ensure!(self.links.insert(link.to_owned(), id.clone()).is_none(),
1120+
"Attempting to resolve a with more then one crate with the links={}. \n\
1121+
This will not build as is. Consider rebuilding the .lock file.", link);
1122+
}
10631123
prev.push(summary.clone());
1064-
return false
1124+
return Ok(false)
10651125
}
10661126
debug!("checking if {} is already activated", summary.package_id());
10671127
let (features, use_default) = match *method {
10681128
Method::Required { features, uses_default_features, .. } => {
10691129
(features, uses_default_features)
10701130
}
1071-
Method::Everything => return false,
1131+
Method::Everything => return Ok(false),
10721132
};
10731133

10741134
let has_default_feature = summary.features().contains_key("default");
1075-
match self.resolve_features.get(id) {
1135+
Ok(match self.resolve_features.get(id) {
10761136
Some(prev) => {
10771137
features.iter().all(|f| prev.contains(f)) &&
10781138
(!use_default || prev.contains("default") ||
10791139
!has_default_feature)
10801140
}
10811141
None => features.is_empty() && (!use_default || !has_default_feature)
1082-
}
1142+
})
10831143
}
10841144

10851145
fn build_deps(&mut self,
@@ -1191,7 +1251,7 @@ impl<'a> Context<'a> {
11911251
.unwrap_or(&[])
11921252
}
11931253

1194-
fn is_active(&mut self, id: &PackageId) -> bool {
1254+
fn is_active(&self, id: &PackageId) -> bool {
11951255
self.activations.get(id.name())
11961256
.and_then(|v| v.get(id.source_id()))
11971257
.map(|v| v.iter().any(|s| s.package_id() == id))

src/cargo/core/summary.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,14 @@ struct Inner {
2222
dependencies: Vec<Dependency>,
2323
features: BTreeMap<String, Vec<String>>,
2424
checksum: Option<String>,
25+
links: Option<String>,
2526
}
2627

2728
impl Summary {
2829
pub fn new(pkg_id: PackageId,
2930
dependencies: Vec<Dependency>,
30-
features: BTreeMap<String, Vec<String>>) -> CargoResult<Summary> {
31+
features: BTreeMap<String, Vec<String>>,
32+
links: Option<String>) -> CargoResult<Summary> {
3133
for dep in dependencies.iter() {
3234
if features.get(dep.name()).is_some() {
3335
bail!("Features and dependencies cannot have the \
@@ -66,9 +68,10 @@ impl Summary {
6668
Ok(Summary {
6769
inner: Rc::new(Inner {
6870
package_id: pkg_id,
69-
dependencies: dependencies,
70-
features: features,
71+
dependencies,
72+
features,
7173
checksum: None,
74+
links,
7275
}),
7376
})
7477
}
@@ -82,6 +85,9 @@ impl Summary {
8285
pub fn checksum(&self) -> Option<&str> {
8386
self.inner.checksum.as_ref().map(|s| &s[..])
8487
}
88+
pub fn links(&self) -> Option<&str> {
89+
self.inner.links.as_ref().map(|s| &s[..])
90+
}
8591

8692
pub fn override_id(mut self, id: PackageId) -> Summary {
8793
Rc::make_mut(&mut self.inner).package_id = id;
@@ -94,7 +100,7 @@ impl Summary {
94100
}
95101

96102
pub fn map_dependencies<F>(mut self, f: F) -> Summary
97-
where F: FnMut(Dependency) -> Dependency {
103+
where F: FnMut(Dependency) -> Dependency {
98104
{
99105
let slot = &mut Rc::make_mut(&mut self.inner).dependencies;
100106
let deps = mem::replace(slot, Vec::new());

src/cargo/ops/registry.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ fn transmit(config: &Config,
159159
let ManifestMetadata {
160160
ref authors, ref description, ref homepage, ref documentation,
161161
ref keywords, ref readme, ref repository, ref license, ref license_file,
162-
ref categories, ref badges,
162+
ref categories, ref badges, ref links,
163163
} = *manifest.metadata();
164164
let readme_content = match *readme {
165165
Some(ref readme) => Some(paths::read(&pkg.root().join(readme))?),
@@ -194,6 +194,7 @@ fn transmit(config: &Config,
194194
license: license.clone(),
195195
license_file: license_file.clone(),
196196
badges: badges.clone(),
197+
links: links.clone(),
197198
}, tarball);
198199

199200
match publish {

0 commit comments

Comments
 (0)