Skip to content

Commit 05a76cb

Browse files
committed
Auto-resolve collisions if using a meta-hash would help
Collisions in binaries or dylibs are quite easy to get into when compiling artifacts deps in conjunction with build overrides. The latter will cause multiple builds and prevent deduplication, while certain platform rules prevent unique filename to be generated while dropping files into the same folder. Now we recomputate outputs after detecting certain avoidable collisions to be more usable in this specific situation.
1 parent 10ffdd5 commit 05a76cb

File tree

3 files changed

+45
-11
lines changed

3 files changed

+45
-11
lines changed

src/cargo/core/compiler/context/compilation_files.rs

+13-2
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ struct MetaInfo {
8989
///
9090
/// If this is `true`, the `meta_hash` is used for the filename.
9191
use_extra_filename: bool,
92+
93+
/// This flag is set by collision detection and forces the use of a more unique hash
94+
/// instead of the stable one.
95+
output_conflicts_without_meta_hash: bool,
9296
}
9397

9498
/// Collection of information about the files emitted by the compiler, and the
@@ -220,7 +224,7 @@ impl<'a, 'cfg: 'a> CompilationFiles<'a, 'cfg> {
220224
fn pkg_dir(&self, unit: &Unit) -> String {
221225
let name = unit.pkg.package_id().name();
222226
let meta = &self.metas[unit];
223-
if meta.use_extra_filename {
227+
if meta.use_extra_filename || meta.output_conflicts_without_meta_hash {
224228
format!("{}-{}", name, meta.meta_hash)
225229
} else {
226230
format!("{}-{}", name, self.target_short_hash(unit))
@@ -370,6 +374,12 @@ impl<'a, 'cfg: 'a> CompilationFiles<'a, 'cfg> {
370374
.map(Arc::clone)
371375
}
372376

377+
pub(super) fn set_non_colliding_outputs(&mut self, unit: &Unit, outputs: Arc<Vec<OutputFile>>) {
378+
let cell = LazyCell::new();
379+
cell.fill(outputs).unwrap();
380+
self.outputs.insert(unit.clone(), cell);
381+
}
382+
373383
/// Returns the path where the output for the given unit and FileType
374384
/// should be uplifted to.
375385
///
@@ -421,7 +431,7 @@ impl<'a, 'cfg: 'a> CompilationFiles<'a, 'cfg> {
421431
Some(uplift_path)
422432
}
423433

424-
fn calc_outputs(
434+
pub(super) fn calc_outputs(
425435
&self,
426436
unit: &Unit,
427437
bcx: &BuildContext<'a, 'cfg>,
@@ -625,6 +635,7 @@ fn compute_metadata(
625635
MetaInfo {
626636
meta_hash: Metadata(hasher.finish()),
627637
use_extra_filename: should_use_metadata(bcx, unit),
638+
output_conflicts_without_meta_hash: false,
628639
}
629640
}
630641

src/cargo/core/compiler/context/mod.rs

+31-3
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
128128
self.prepare_units()?;
129129
self.prepare()?;
130130
custom_build::build_map(&mut self)?;
131-
self.check_collisions()?;
131+
self.check_collisions_and_avoid_some()?;
132132
self.compute_metadata_for_doc_units();
133133

134134
// We need to make sure that if there were any previous docs
@@ -421,7 +421,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
421421
}
422422
}
423423

424-
fn check_collisions(&self) -> CargoResult<()> {
424+
fn check_collisions_and_avoid_some(&mut self) -> CargoResult<()> {
425425
let mut output_collisions = HashMap::new();
426426
let describe_collision = |unit: &Unit, other_unit: &Unit, path: &PathBuf| -> String {
427427
format!(
@@ -528,17 +528,36 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
528528
doc_collision_error(unit, prev)?;
529529
}
530530
}
531+
let mut alt_outputs = None;
532+
let mut collisions_prevented = 0;
531533
for output in self.outputs(unit)?.iter() {
532534
if let Some(other_unit) = output_collisions.insert(output.path.clone(), unit) {
533535
if unit.mode.is_doc() {
534536
// See https://github.com/rust-lang/rust/issues/56169
535537
// and https://github.com/rust-lang/rust/issues/61378
536538
report_collision(unit, other_unit, &output.path, rustdoc_suggestion)?;
537539
} else {
538-
report_collision(unit, other_unit, &output.path, suggestion)?;
540+
let files = self.files.as_mut().unwrap();
541+
if !files.use_extra_filename(unit)
542+
&& !alt_outputs
543+
.get_or_insert_with(|| {
544+
files
545+
.calc_outputs(unit, &self.bcx)
546+
.expect("infallible by now")
547+
})
548+
.iter()
549+
.any(|out| out.path == output.path)
550+
{
551+
collisions_prevented += 1;
552+
continue;
553+
} else {
554+
report_collision(unit, other_unit, &output.path, suggestion)?;
555+
}
539556
}
540557
}
541558
if let Some(hardlink) = output.hardlink.as_ref() {
559+
// We don't check for collision prevention on unix as typically these only happen on MSVC platforms
560+
// which doesn't use hardlinks. If this changes, one would do another collision here.
542561
if let Some(other_unit) = output_collisions.insert(hardlink.clone(), unit) {
543562
report_collision(unit, other_unit, hardlink, suggestion)?;
544563
}
@@ -556,6 +575,15 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
556575
}
557576
}
558577
}
578+
579+
if collisions_prevented != 0 {
580+
if let Some(alt_outputs) = alt_outputs {
581+
self.files
582+
.as_mut()
583+
.unwrap()
584+
.set_non_colliding_outputs(unit, alt_outputs);
585+
}
586+
}
559587
}
560588
Ok(())
561589
}

tests/testsuite/artifact_dep.rs

+1-6
Original file line numberDiff line numberDiff line change
@@ -1384,10 +1384,6 @@ fn profile_override_basic() {
13841384
.run();
13851385
}
13861386

1387-
// TODO: replace profile_override_basic() with this test if it fixes msvc issues.
1388-
// Answer: it doesn't as it's a race of the same dep being built twice, in parallel.
1389-
// This needs a fix on scheduler level or maybe fixes itself once we uplift binaries.
1390-
// But probably not as the scheduling problem and similar file paths still remains an issue.
13911387
#[cargo_test]
13921388
fn profile_override_basic_multidep() {
13931389
let p = project()
@@ -1403,8 +1399,7 @@ fn profile_override_basic_multidep() {
14031399
bar = { path = "bar", artifact = "bin" }
14041400
14051401
[dependencies]
1406-
# renamed just to avoid collisions on MSVC
1407-
bardep = { package = "bar", path = "bar", artifact = "bin" }
1402+
bar = { path = "bar", artifact = "bin" }
14081403
14091404
[profile.dev.build-override]
14101405
opt-level = 1

0 commit comments

Comments
 (0)