Skip to content

Commit 76c1a0d

Browse files
committed
Auto merge of #32317 - taralx:master, r=alexcrichton
Deduplicate libraries on hash instead of filename. Removes the need for canonicalization to prevent #12459. (Now with passing tests!) Canonicalization breaks certain environments where the libraries are symlinks to files that don't end in .rlib (e.g. /remote/cas/$HASH).
2 parents 2174bd9 + 2218245 commit 76c1a0d

File tree

7 files changed

+84
-64
lines changed

7 files changed

+84
-64
lines changed

src/librustc/hir/svh.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
4949
use std::fmt;
5050

51-
#[derive(Clone, PartialEq, Debug)]
51+
#[derive(Clone, Eq, Hash, PartialEq, Debug)]
5252
pub struct Svh {
5353
hash: String,
5454
}

src/librustc_metadata/loader.rs

+43-55
Original file line numberDiff line numberDiff line change
@@ -470,20 +470,17 @@ impl<'a> Context<'a> {
470470
// A Library candidate is created if the metadata for the set of
471471
// libraries corresponds to the crate id and hash criteria that this
472472
// search is being performed for.
473-
let mut libraries = Vec::new();
473+
let mut libraries = HashMap::new();
474474
for (_hash, (rlibs, dylibs)) in candidates {
475-
let mut metadata = None;
476-
let rlib = self.extract_one(rlibs, CrateFlavor::Rlib, &mut metadata);
477-
let dylib = self.extract_one(dylibs, CrateFlavor::Dylib, &mut metadata);
478-
match metadata {
479-
Some(metadata) => {
480-
libraries.push(Library {
481-
dylib: dylib,
482-
rlib: rlib,
483-
metadata: metadata,
484-
})
485-
}
486-
None => {}
475+
let mut slot = None;
476+
let rlib = self.extract_one(rlibs, CrateFlavor::Rlib, &mut slot);
477+
let dylib = self.extract_one(dylibs, CrateFlavor::Dylib, &mut slot);
478+
if let Some((h, m)) = slot {
479+
libraries.insert(h, Library {
480+
dylib: dylib,
481+
rlib: rlib,
482+
metadata: m,
483+
});
487484
}
488485
}
489486

@@ -492,13 +489,13 @@ impl<'a> Context<'a> {
492489
// libraries or not.
493490
match libraries.len() {
494491
0 => None,
495-
1 => Some(libraries.into_iter().next().unwrap()),
492+
1 => Some(libraries.into_iter().next().unwrap().1),
496493
_ => {
497494
let mut err = struct_span_err!(self.sess, self.span, E0464,
498495
"multiple matching crates for `{}`",
499496
self.crate_name);
500497
err.note("candidates:");
501-
for lib in &libraries {
498+
for (_, lib) in libraries {
502499
match lib.dylib {
503500
Some((ref p, _)) => {
504501
err.note(&format!("path: {}",
@@ -532,13 +529,13 @@ impl<'a> Context<'a> {
532529
// be read, it is assumed that the file isn't a valid rust library (no
533530
// errors are emitted).
534531
fn extract_one(&mut self, m: HashMap<PathBuf, PathKind>, flavor: CrateFlavor,
535-
slot: &mut Option<MetadataBlob>) -> Option<(PathBuf, PathKind)> {
536-
let mut ret = None::<(PathBuf, PathKind)>;
532+
slot: &mut Option<(Svh, MetadataBlob)>) -> Option<(PathBuf, PathKind)> {
533+
let mut ret: Option<(PathBuf, PathKind)> = None;
537534
let mut error = 0;
538535

539536
if slot.is_some() {
540537
// FIXME(#10786): for an optimization, we only read one of the
541-
// library's metadata sections. In theory we should
538+
// libraries' metadata sections. In theory we should
542539
// read both, but reading dylib metadata is quite
543540
// slow.
544541
if m.is_empty() {
@@ -551,10 +548,10 @@ impl<'a> Context<'a> {
551548
let mut err: Option<DiagnosticBuilder> = None;
552549
for (lib, kind) in m {
553550
info!("{} reading metadata from: {}", flavor, lib.display());
554-
let metadata = match get_metadata_section(self.target, flavor, &lib) {
551+
let (hash, metadata) = match get_metadata_section(self.target, flavor, &lib) {
555552
Ok(blob) => {
556-
if self.crate_matches(blob.as_slice(), &lib) {
557-
blob
553+
if let Some(h) = self.crate_matches(blob.as_slice(), &lib) {
554+
(h, blob)
558555
} else {
559556
info!("metadata mismatch");
560557
continue
@@ -565,12 +562,8 @@ impl<'a> Context<'a> {
565562
continue
566563
}
567564
};
568-
// If we've already found a candidate and we're not matching hashes,
569-
// emit an error about duplicate candidates found. If we're matching
570-
// based on a hash, however, then if we've gotten this far both
571-
// candidates have the same hash, so they're not actually
572-
// duplicates that we should warn about.
573-
if ret.is_some() && self.hash.is_none() {
565+
// If we see multiple hashes, emit an error about duplicate candidates.
566+
if slot.as_ref().map_or(false, |s| s.0 != hash) {
574567
let mut e = struct_span_err!(self.sess, self.span, E0465,
575568
"multiple {} candidates for `{}` found",
576569
flavor, self.crate_name);
@@ -583,7 +576,7 @@ impl<'a> Context<'a> {
583576
}
584577
err = Some(e);
585578
error = 1;
586-
ret = None;
579+
*slot = None;
587580
}
588581
if error > 0 {
589582
error += 1;
@@ -592,7 +585,7 @@ impl<'a> Context<'a> {
592585
lib.display()));
593586
continue
594587
}
595-
*slot = Some(metadata);
588+
*slot = Some((hash, metadata));
596589
ret = Some((lib, kind));
597590
}
598591

@@ -604,22 +597,20 @@ impl<'a> Context<'a> {
604597
}
605598
}
606599

607-
fn crate_matches(&mut self, crate_data: &[u8], libpath: &Path) -> bool {
600+
fn crate_matches(&mut self, crate_data: &[u8], libpath: &Path) -> Option<Svh> {
608601
if self.should_match_name {
609602
match decoder::maybe_get_crate_name(crate_data) {
610603
Some(ref name) if self.crate_name == *name => {}
611-
_ => { info!("Rejecting via crate name"); return false }
604+
_ => { info!("Rejecting via crate name"); return None }
612605
}
613606
}
614607
let hash = match decoder::maybe_get_crate_hash(crate_data) {
615-
Some(hash) => hash, None => {
616-
info!("Rejecting via lack of crate hash");
617-
return false;
618-
}
608+
None => { info!("Rejecting via lack of crate hash"); return None; }
609+
Some(h) => h,
619610
};
620611

621612
let triple = match decoder::get_crate_triple(crate_data) {
622-
None => { debug!("triple not present"); return false }
613+
None => { debug!("triple not present"); return None }
623614
Some(t) => t,
624615
};
625616
if triple != self.triple {
@@ -628,24 +619,21 @@ impl<'a> Context<'a> {
628619
path: libpath.to_path_buf(),
629620
got: triple.to_string()
630621
});
631-
return false;
622+
return None;
632623
}
633624

634-
match self.hash {
635-
None => true,
636-
Some(myhash) => {
637-
if *myhash != hash {
638-
info!("Rejecting via hash: expected {} got {}", *myhash, hash);
639-
self.rejected_via_hash.push(CrateMismatch {
640-
path: libpath.to_path_buf(),
641-
got: myhash.as_str().to_string()
642-
});
643-
false
644-
} else {
645-
true
646-
}
625+
if let Some(myhash) = self.hash {
626+
if *myhash != hash {
627+
info!("Rejecting via hash: expected {} got {}", *myhash, hash);
628+
self.rejected_via_hash.push(CrateMismatch {
629+
path: libpath.to_path_buf(),
630+
got: myhash.as_str().to_string()
631+
});
632+
return None;
647633
}
648634
}
635+
636+
Some(hash)
649637
}
650638

651639

@@ -717,13 +705,13 @@ impl<'a> Context<'a> {
717705
};
718706

719707
// Extract the rlib/dylib pair.
720-
let mut metadata = None;
721-
let rlib = self.extract_one(rlibs, CrateFlavor::Rlib, &mut metadata);
722-
let dylib = self.extract_one(dylibs, CrateFlavor::Dylib, &mut metadata);
708+
let mut slot = None;
709+
let rlib = self.extract_one(rlibs, CrateFlavor::Rlib, &mut slot);
710+
let dylib = self.extract_one(dylibs, CrateFlavor::Dylib, &mut slot);
723711

724712
if rlib.is_none() && dylib.is_none() { return None }
725-
match metadata {
726-
Some(metadata) => Some(Library {
713+
match slot {
714+
Some((_, metadata)) => Some(Library {
727715
dylib: dylib,
728716
rlib: rlib,
729717
metadata: metadata,

src/test/run-make/compiler-lookup-paths/Makefile

+8
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,21 @@ all: $(TMPDIR)/libnative.a
1818
$(RUSTC) d.rs -L crate=$(TMPDIR)/native && exit 1 || exit 0
1919
$(RUSTC) d.rs -L native=$(TMPDIR)/native
2020
$(RUSTC) d.rs -L all=$(TMPDIR)/native
21+
# Deduplication tests:
22+
# Same hash, no errors.
2123
mkdir -p $(TMPDIR)/e1
2224
mkdir -p $(TMPDIR)/e2
2325
$(RUSTC) e.rs -o $(TMPDIR)/e1/libe.rlib
2426
$(RUSTC) e.rs -o $(TMPDIR)/e2/libe.rlib
27+
$(RUSTC) f.rs -L $(TMPDIR)/e1 -L $(TMPDIR)/e2
28+
$(RUSTC) f.rs -L crate=$(TMPDIR)/e1 -L $(TMPDIR)/e2
29+
$(RUSTC) f.rs -L crate=$(TMPDIR)/e1 -L crate=$(TMPDIR)/e2
30+
# Different hash, errors.
31+
$(RUSTC) e2.rs -o $(TMPDIR)/e2/libe.rlib
2532
$(RUSTC) f.rs -L $(TMPDIR)/e1 -L $(TMPDIR)/e2 && exit 1 || exit 0
2633
$(RUSTC) f.rs -L crate=$(TMPDIR)/e1 -L $(TMPDIR)/e2 && exit 1 || exit 0
2734
$(RUSTC) f.rs -L crate=$(TMPDIR)/e1 -L crate=$(TMPDIR)/e2 && exit 1 || exit 0
35+
# Native/dependency paths don't cause errors.
2836
$(RUSTC) f.rs -L native=$(TMPDIR)/e1 -L $(TMPDIR)/e2
2937
$(RUSTC) f.rs -L dependency=$(TMPDIR)/e1 -L $(TMPDIR)/e2
3038
$(RUSTC) f.rs -L dependency=$(TMPDIR)/e1 -L crate=$(TMPDIR)/e2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![crate_name = "e"]
12+
#![crate_type = "rlib"]
13+
14+
pub fn f() {}

src/test/run-make/extern-flag-fun/Makefile

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33
all:
44
$(RUSTC) bar.rs --crate-type=rlib
55
$(RUSTC) bar.rs --crate-type=rlib -C extra-filename=-a
6+
$(RUSTC) bar-alt.rs --crate-type=rlib
67
$(RUSTC) foo.rs --extern hello && exit 1 || exit 0
78
$(RUSTC) foo.rs --extern bar=no-exist && exit 1 || exit 0
89
$(RUSTC) foo.rs --extern bar=foo.rs && exit 1 || exit 0
910
$(RUSTC) foo.rs \
1011
--extern bar=$(TMPDIR)/libbar.rlib \
11-
--extern bar=$(TMPDIR)/libbar-a.rlib \
12+
--extern bar=$(TMPDIR)/libbar-alt.rlib \
1213
&& exit 1 || exit 0
1314
$(RUSTC) foo.rs \
1415
--extern bar=$(TMPDIR)/libbar.rlib \
15-
--extern bar=$(TMPDIR)/libbar.rlib
16+
--extern bar=$(TMPDIR)/libbar-a.rlib
1617
$(RUSTC) foo.rs --extern bar=$(TMPDIR)/libbar.rlib
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
pub fn f() {}

src/test/run-make/issue-11908/Makefile

+4-6
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,13 @@
99

1010
all:
1111
mkdir $(TMPDIR)/other
12-
$(RUSTC) foo.rs --crate-type=dylib
12+
$(RUSTC) foo.rs --crate-type=dylib -C prefer-dynamic
1313
mv $(call DYLIB,foo) $(TMPDIR)/other
14-
$(RUSTC) foo.rs --crate-type=dylib
15-
$(RUSTC) bar.rs -L $(TMPDIR)/other 2>&1 | \
16-
grep "multiple dylib candidates"
14+
$(RUSTC) foo.rs --crate-type=dylib -C prefer-dynamic
15+
$(RUSTC) bar.rs -L $(TMPDIR)/other
1716
rm -rf $(TMPDIR)
1817
mkdir -p $(TMPDIR)/other
1918
$(RUSTC) foo.rs --crate-type=rlib
2019
mv $(TMPDIR)/libfoo.rlib $(TMPDIR)/other
2120
$(RUSTC) foo.rs --crate-type=rlib
22-
$(RUSTC) bar.rs -L $(TMPDIR)/other 2>&1 | \
23-
grep "multiple rlib candidates"
21+
$(RUSTC) bar.rs -L $(TMPDIR)/other

0 commit comments

Comments
 (0)