Skip to content

Commit 530f2dd

Browse files
committed
Mix feature flags into fingerprint/metadata shorthash
Since building dependencies results in different libraries depending on the feature flags, I added the feature flags into the short_hash. This solves an issue when multiple crates share a target directory or multiple targets share a common library with divergent feature flag choice. - Only link binaries, build scripts, and top level deps - Handle dylibs differently (no metadata filename / linking) - Fingerprint based on link_dst rather than file_stem. This (sadly) limits the effects of dep caching to things which don't produce a hard-link. Currently, this is only dependent library crates. Stil a big win.
1 parent 14a28af commit 530f2dd

18 files changed

Lines changed: 301 additions & 154 deletions

src/cargo/core/manifest.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,13 @@ impl Target {
395395
}
396396
}
397397

398+
pub fn is_dylib(&self) -> bool {
399+
match self.kind {
400+
TargetKind::Lib(ref libs) => libs.iter().any(|l| *l == LibKind::Dylib),
401+
_ => false
402+
}
403+
}
404+
398405
pub fn linkable(&self) -> bool {
399406
match self.kind {
400407
TargetKind::Lib(ref kinds) => {

src/cargo/ops/cargo_clean.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,11 @@ pub fn clean(ws: &Workspace, opts: &CleanOptions) -> CargoResult<()> {
7777
try!(rm_rf(&layout.proxy().fingerprint(&unit.pkg)));
7878
try!(rm_rf(&layout.build(&unit.pkg)));
7979

80-
let root = cx.out_dir(&unit);
81-
for (filename, _) in try!(cx.target_filenames(&unit)) {
82-
try!(rm_rf(&root.join(&filename)));
80+
for (src, link_dst, _) in try!(cx.target_filenames(&unit)) {
81+
try!(rm_rf(&src));
82+
if let Some(dst) = link_dst {
83+
try!(rm_rf(&dst));
84+
}
8385
}
8486
}
8587

src/cargo/ops/cargo_rustc/context.rs

Lines changed: 86 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -309,49 +309,55 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
309309

310310
/// Get the metadata for a target in a specific profile
311311
pub fn target_metadata(&self, unit: &Unit) -> Option<Metadata> {
312-
let metadata = unit.target.metadata();
312+
// No metadata for dylibs because of a couple issues
313+
// - OSX encodes the dylib name in the executable
314+
// - Windows rustc multiple files of which we can't easily link all of them
315+
if !unit.profile.test && unit.target.is_dylib() {
316+
return None;
317+
}
318+
319+
let metadata = unit.target.metadata().cloned().map(|mut m| {
320+
if let Some(features) = self.resolve.features(unit.pkg.package_id()) {
321+
let mut feat_vec: Vec<&String> = features.iter().collect();
322+
feat_vec.sort();
323+
for feat in feat_vec {
324+
m.mix(feat);
325+
}
326+
}
327+
m.mix(unit.profile);
328+
m
329+
});
330+
let mut pkg_metadata = {
331+
let mut m = unit.pkg.generate_metadata();
332+
if let Some(features) = self.resolve.features(unit.pkg.package_id()) {
333+
let mut feat_vec: Vec<&String> = features.iter().collect();
334+
feat_vec.sort();
335+
for feat in feat_vec {
336+
m.mix(feat);
337+
}
338+
}
339+
m.mix(unit.profile);
340+
m
341+
};
342+
313343
if unit.target.is_lib() && unit.profile.test {
314344
// Libs and their tests are built in parallel, so we need to make
315345
// sure that their metadata is different.
316-
metadata.cloned().map(|mut m| {
346+
metadata.map(|mut m| {
317347
m.mix(&"test");
318348
m
319349
})
320350
} else if unit.target.is_bin() && unit.profile.test {
321351
// Make sure that the name of this test executable doesn't
322352
// conflict with a library that has the same name and is
323353
// being tested
324-
let mut metadata = unit.pkg.generate_metadata();
325-
metadata.mix(&format!("bin-{}", unit.target.name()));
326-
Some(metadata)
354+
pkg_metadata.mix(&format!("bin-{}", unit.target.name()));
355+
Some(pkg_metadata)
327356
} else if unit.pkg.package_id().source_id().is_path() &&
328357
!unit.profile.test {
329-
// If we're not building a unit test but we're building a path
330-
// dependency, then we're likely compiling the "current package" or
331-
// some package in a workspace. In this situation we pass no
332-
// metadata by default so we'll have predictable
333-
// file names like `target/debug/libfoo.{a,so,rlib}` and such.
334-
//
335-
// Note, though, that the compiler's build system at least wants
336-
// path dependencies to have hashes in filenames. To account for
337-
// that we have an extra hack here which reads the
338-
// `__CARGO_DEFAULT_METADATA` environment variable and creates a
339-
// hash in the filename if that's present.
340-
//
341-
// This environment variable should not be relied on! It's basically
342-
// just here for rustbuild. We need a more principled method of
343-
// doing this eventually.
344-
if unit.target.is_lib() {
345-
env::var("__CARGO_DEFAULT_LIB_METADATA").ok().map(|meta| {
346-
let mut metadata = unit.pkg.generate_metadata();
347-
metadata.mix(&meta);
348-
metadata
349-
})
350-
} else {
351-
None
352-
}
358+
Some(pkg_metadata)
353359
} else {
354-
metadata.cloned()
360+
metadata
355361
}
356362
}
357363

@@ -360,19 +366,57 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
360366
match self.target_metadata(unit) {
361367
Some(ref metadata) => format!("{}{}", unit.target.crate_name(),
362368
metadata.extra_filename),
363-
None if unit.target.allows_underscores() => {
364-
unit.target.name().to_string()
369+
None => self.bin_stem(unit),
370+
}
371+
}
372+
373+
fn bin_stem(&self, unit: &Unit) -> String {
374+
if unit.target.allows_underscores() {
375+
unit.target.name().to_string()
376+
} else {
377+
unit.target.crate_name()
378+
}
379+
}
380+
381+
pub fn link_stem(&self, unit: &Unit) -> Option<(PathBuf, String)> {
382+
let src_dir = self.out_dir(unit);
383+
let bin_stem = self.bin_stem(unit);
384+
let file_stem = self.file_stem(unit);
385+
386+
// We currently only lift files up from the `deps` directory. If
387+
// it was compiled into something like `example/` or `doc/` then
388+
// we don't want to link it up.
389+
if src_dir.ends_with("deps") {
390+
// Don't lift up library dependencies
391+
if unit.pkg.package_id() != &self.current_package && !unit.target.is_bin() {
392+
None
393+
} else {
394+
Some((
395+
src_dir.parent().unwrap().to_owned(),
396+
if unit.profile.test {file_stem} else {bin_stem},
397+
))
365398
}
366-
None => unit.target.crate_name(),
399+
} else if bin_stem == file_stem {
400+
None
401+
} else if src_dir.ends_with("examples") {
402+
Some((src_dir, bin_stem))
403+
} else if src_dir.parent().unwrap().ends_with("build") {
404+
Some((src_dir, bin_stem))
405+
} else {
406+
None
367407
}
368408
}
369409

370410
/// Return the filenames that the given target for the given profile will
371-
/// generate, along with whether you can link against that file (e.g. it's a
372-
/// library).
411+
/// generate as a list of 3-tuples (filename, link_dst, linkable)
412+
/// filename: filename rustc compiles to. (Often has metadata suffix).
413+
/// link_dst: Optional file to link/copy the result to (without metadata suffix)
414+
/// linkable: Whether possible to link against file (eg it's a library)
373415
pub fn target_filenames(&self, unit: &Unit)
374-
-> CargoResult<Vec<(String, bool)>> {
416+
-> CargoResult<Vec<(PathBuf, Option<PathBuf>, bool)>> {
417+
let out_dir = self.out_dir(unit);
375418
let stem = self.file_stem(unit);
419+
let link_stem = self.link_stem(unit);
376420
let info = if unit.target.for_host() {
377421
&self.host_info
378422
} else {
@@ -386,8 +430,11 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
386430
let crate_type = if crate_type == "lib" {"rlib"} else {crate_type};
387431
match info.crate_types.get(crate_type) {
388432
Some(&Some((ref prefix, ref suffix))) => {
389-
ret.push((format!("{}{}{}", prefix, stem, suffix),
390-
linkable));
433+
let filename = out_dir.join(format!("{}{}{}", prefix, stem, suffix));
434+
let link_dst = link_stem.clone().map(|(ld, ls)| {
435+
ld.join(format!("{}{}{}", prefix, ls, suffix))
436+
});
437+
ret.push((filename, link_dst, linkable));
391438
Ok(())
392439
}
393440
// not supported, don't worry about it
@@ -429,6 +476,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
429476
support any of the output crate types",
430477
unit.pkg, self.target_triple());
431478
}
479+
info!("Target filenames: {:?}", ret);
432480
Ok(ret)
433481
}
434482

src/cargo/ops/cargo_rustc/fingerprint.rs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ pub fn prepare_target<'a, 'cfg>(cx: &mut Context<'a, 'cfg>,
4949
let _p = profile::start(format!("fingerprint: {} / {}",
5050
unit.pkg.package_id(), unit.target.name()));
5151
let new = dir(cx, unit);
52-
let loc = new.join(&filename(unit));
52+
let loc = new.join(&filename(cx, unit));
5353

5454
debug!("fingerprint at: {}", loc.display());
5555

@@ -82,8 +82,11 @@ pub fn prepare_target<'a, 'cfg>(cx: &mut Context<'a, 'cfg>,
8282
missing_outputs = !root.join(unit.target.crate_name())
8383
.join("index.html").exists();
8484
} else {
85-
for (filename, _) in try!(cx.target_filenames(unit)) {
86-
missing_outputs |= fs::metadata(root.join(filename)).is_err();
85+
for (src, link_dst, _) in try!(cx.target_filenames(unit)) {
86+
missing_outputs |= fs::metadata(&src).is_err();
87+
if let Some(link_dst) = link_dst {
88+
missing_outputs |= fs::metadata(link_dst).is_err();
89+
}
8790
}
8891
}
8992

@@ -529,7 +532,7 @@ pub fn dir(cx: &Context, unit: &Unit) -> PathBuf {
529532

530533
/// Returns the (old, new) location for the dep info file of a target.
531534
pub fn dep_info_loc(cx: &Context, unit: &Unit) -> PathBuf {
532-
dir(cx, unit).join(&format!("dep-{}", filename(unit)))
535+
dir(cx, unit).join(&format!("dep-{}", filename(cx, unit)))
533536
}
534537

535538
fn compare_old_fingerprint(loc: &Path, new_fingerprint: &Fingerprint)
@@ -650,7 +653,13 @@ fn mtime_if_fresh<I>(output: &Path, paths: I) -> Option<FileTime>
650653
}
651654
}
652655

653-
fn filename(unit: &Unit) -> String {
656+
fn filename(cx: &Context, unit: &Unit) -> String {
657+
// If there exists a link stem, we have to use that since multiple target filenames
658+
// may hardlink to the same target stem. If there's no link, we can use the original
659+
// file_stem (which can include a suffix)
660+
let file_stem = cx.link_stem(unit)
661+
.map(|(_path, stem)| stem)
662+
.unwrap_or_else(|| cx.file_stem(unit));
654663
let kind = match *unit.target.kind() {
655664
TargetKind::Lib(..) => "lib",
656665
TargetKind::Bin => "bin",
@@ -666,7 +675,7 @@ fn filename(unit: &Unit) -> String {
666675
} else {
667676
""
668677
};
669-
format!("{}{}-{}", flavor, kind, unit.target.name())
678+
format!("{}{}-{}", flavor, kind, file_stem)
670679
}
671680

672681
// The dep-info files emitted by the compiler all have their listed paths

src/cargo/ops/cargo_rustc/layout.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ impl<'a> LayoutProxy<'a> {
175175
} else if unit.target.is_lib() {
176176
self.deps().to_path_buf()
177177
} else {
178-
self.root().to_path_buf()
178+
self.deps().to_path_buf()
179179
}
180180
}
181181

0 commit comments

Comments
 (0)