Skip to content

Commit 2296af2

Browse files
committed
Fix BuildScriptOutput when a build script is run multiple times.
1 parent 972b9f5 commit 2296af2

File tree

9 files changed

+296
-81
lines changed

9 files changed

+296
-81
lines changed

src/cargo/core/compiler/compilation.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use crate::core::compiler::CompileKind;
1111
use crate::core::{Edition, Package, PackageId, Target};
1212
use crate::util::{self, config, join_paths, process, CargoResult, Config, ProcessBuilder};
1313

14+
/// Structure with enough information to run `rustdoc --test`.
1415
pub struct Doctest {
1516
/// The package being doc-tested.
1617
pub package: Package,

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ use crate::util::{self, CargoResult};
4141
///
4242
/// Note that the `Fingerprint` is in charge of tracking everything needed to determine if a
4343
/// rebuild is needed.
44-
#[derive(Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
44+
#[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
4545
pub struct Metadata(u64);
4646

4747
impl fmt::Display for Metadata {
@@ -50,6 +50,12 @@ impl fmt::Display for Metadata {
5050
}
5151
}
5252

53+
impl fmt::Debug for Metadata {
54+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55+
write!(f, "Metadata({:016x})", self.0)
56+
}
57+
}
58+
5359
/// Collection of information about the files emitted by the compiler, and the
5460
/// output directory structure.
5561
pub struct CompilationFiles<'a, 'cfg> {
@@ -213,6 +219,7 @@ impl<'a, 'cfg: 'a> CompilationFiles<'a, 'cfg> {
213219
pub fn build_script_dir(&self, unit: &Unit<'a>) -> PathBuf {
214220
assert!(unit.target.is_custom_build());
215221
assert!(!unit.mode.is_run_custom_build());
222+
assert!(self.metas.contains_key(unit));
216223
let dir = self.pkg_dir(unit);
217224
self.layout(CompileKind::Host).build().join(dir)
218225
}

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

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,8 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
122122
})
123123
}
124124

125-
// Returns a mapping of the root package plus its immediate dependencies to
126-
// where the compiled libraries are all located.
125+
/// Starts compilation, waits for it to finish, and returns information
126+
/// about the result of compilation.
127127
pub fn compile(
128128
mut self,
129129
units: &[Unit<'a>],
@@ -245,16 +245,16 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
245245
super::output_depinfo(&mut self, unit)?;
246246
}
247247

248-
for (&(ref pkg, _), output) in self.build_script_outputs.lock().unwrap().iter() {
248+
for (pkg_id, output) in self.build_script_outputs.lock().unwrap().iter() {
249249
self.compilation
250250
.cfgs
251-
.entry(pkg.clone())
251+
.entry(pkg_id)
252252
.or_insert_with(HashSet::new)
253253
.extend(output.cfgs.iter().cloned());
254254

255255
self.compilation
256256
.extra_env
257-
.entry(pkg.clone())
257+
.entry(pkg_id)
258258
.or_insert_with(Vec::new)
259259
.extend(output.env.iter().cloned());
260260

@@ -347,6 +347,39 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
347347
&self.unit_dependencies[unit]
348348
}
349349

350+
/// Returns the RunCustomBuild Unit associated with the given Unit.
351+
///
352+
/// If the package does not have a build script, this returns None.
353+
pub fn find_build_script_unit(&self, unit: Unit<'a>) -> Option<Unit<'a>> {
354+
if unit.mode.is_run_custom_build() {
355+
return Some(unit);
356+
}
357+
self.unit_dependencies[&unit]
358+
.iter()
359+
.find(|unit_dep| {
360+
unit_dep.unit.mode.is_run_custom_build()
361+
&& unit_dep.unit.pkg.package_id() == unit.pkg.package_id()
362+
})
363+
.map(|unit_dep| unit_dep.unit)
364+
}
365+
366+
/// Returns the metadata hash for the RunCustomBuild Unit associated with
367+
/// the given unit.
368+
///
369+
/// If the package does not have a build script, this returns None.
370+
pub fn find_build_script_metadata(&self, unit: Unit<'a>) -> Option<Metadata> {
371+
let script_unit = self.find_build_script_unit(unit)?;
372+
Some(self.get_run_build_script_metadata(&script_unit))
373+
}
374+
375+
/// Returns the metadata hash for a RunCustomBuild unit.
376+
pub fn get_run_build_script_metadata(&self, unit: &Unit<'a>) -> Metadata {
377+
assert!(unit.mode.is_run_custom_build());
378+
self.files()
379+
.metadata(unit)
380+
.expect("build script should always have hash")
381+
}
382+
350383
pub fn is_primary_package(&self, unit: &Unit<'a>) -> bool {
351384
self.primary_packages.contains(&unit.pkg.package_id())
352385
}

src/cargo/core/compiler/custom_build.rs

Lines changed: 98 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use super::job::{Freshness, Job, Work};
2-
use super::{fingerprint, CompileKind, Context, Unit};
2+
use super::{fingerprint, Context, Unit};
3+
use crate::core::compiler::context::Metadata;
34
use crate::core::compiler::job_queue::JobState;
45
use crate::core::{profiles::ProfileRoot, PackageId};
56
use crate::util::errors::{CargoResult, CargoResultExt};
@@ -41,37 +42,49 @@ pub struct BuildOutput {
4142
/// This initially starts out as empty. Overridden build scripts get
4243
/// inserted during `build_map`. The rest of the entries are added
4344
/// immediately after each build script runs.
44-
pub type BuildScriptOutputs = HashMap<(PackageId, CompileKind), BuildOutput>;
45+
///
46+
/// The `Metadata` is the unique metadata hash for the RunCustomBuild Unit of
47+
/// the package. It needs a unique key, since the build script can be run
48+
/// multiple times with different profiles or features. We can't embed a
49+
/// `Unit` because this structure needs to be shareable between threads.
50+
#[derive(Default)]
51+
pub struct BuildScriptOutputs {
52+
outputs: HashMap<(PackageId, Metadata), BuildOutput>,
53+
}
4554

4655
/// Linking information for a `Unit`.
4756
///
4857
/// See `build_map` for more details.
4958
#[derive(Default)]
5059
pub struct BuildScripts {
60+
/// List of build script outputs this Unit needs to include for linking. Each
61+
/// element is an index into `BuildScriptOutputs`.
62+
///
5163
/// Cargo will use this `to_link` vector to add `-L` flags to compiles as we
5264
/// propagate them upwards towards the final build. Note, however, that we
5365
/// need to preserve the ordering of `to_link` to be topologically sorted.
5466
/// This will ensure that build scripts which print their paths properly will
5567
/// correctly pick up the files they generated (if there are duplicates
5668
/// elsewhere).
5769
///
58-
/// To preserve this ordering, the (id, kind) is stored in two places, once
70+
/// To preserve this ordering, the (id, metadata) is stored in two places, once
5971
/// in the `Vec` and once in `seen_to_link` for a fast lookup. We maintain
6072
/// this as we're building interactively below to ensure that the memory
6173
/// usage here doesn't blow up too much.
6274
///
6375
/// For more information, see #2354.
64-
pub to_link: Vec<(PackageId, CompileKind)>,
76+
pub to_link: Vec<(PackageId, Metadata)>,
6577
/// This is only used while constructing `to_link` to avoid duplicates.
66-
seen_to_link: HashSet<(PackageId, CompileKind)>,
67-
/// Host-only dependencies that have build scripts.
78+
seen_to_link: HashSet<(PackageId, Metadata)>,
79+
/// Host-only dependencies that have build scripts. Each element is an
80+
/// index into `BuildScriptOutputs`.
6881
///
6982
/// This is the set of transitive dependencies that are host-only
7083
/// (proc-macro, plugin, build-dependency) that contain a build script.
7184
/// Any `BuildOutput::library_paths` path relative to `target` will be
7285
/// added to LD_LIBRARY_PATH so that the compiler can find any dynamic
7386
/// libraries a build script may have generated.
74-
pub plugins: BTreeSet<PackageId>,
87+
pub plugins: BTreeSet<(PackageId, Metadata)>,
7588
}
7689

7790
/// Dependency information as declared by a build script.
@@ -94,9 +107,13 @@ pub fn prepare<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRe
94107
unit.target.name()
95108
));
96109

97-
let key = (unit.pkg.package_id(), unit.kind);
98-
99-
if cx.build_script_outputs.lock().unwrap().contains_key(&key) {
110+
let metadata = cx.get_run_build_script_metadata(unit);
111+
if cx
112+
.build_script_outputs
113+
.lock()
114+
.unwrap()
115+
.contains_key(unit.pkg.package_id(), metadata)
116+
{
100117
// The output is already set, thus the build script is overridden.
101118
fingerprint::prepare_target(cx, unit, false)
102119
} else {
@@ -231,9 +248,11 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes
231248
.iter()
232249
.filter_map(|dep| {
233250
if dep.unit.mode.is_run_custom_build() {
251+
let dep_metadata = cx.get_run_build_script_metadata(&dep.unit);
234252
Some((
235253
dep.unit.pkg.manifest().links().unwrap().to_string(),
236254
dep.unit.pkg.package_id(),
255+
dep_metadata,
237256
))
238257
} else {
239258
None
@@ -255,10 +274,10 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes
255274
script_out_dir.clone(),
256275
);
257276
let build_scripts = cx.build_scripts.get(unit).cloned();
258-
let kind = unit.kind;
259277
let json_messages = bcx.build_config.emit_json();
260278
let extra_verbose = bcx.config.extra_verbose();
261279
let (prev_output, prev_script_out_dir) = prev_build_output(cx, unit);
280+
let metadata_hash = cx.get_run_build_script_metadata(unit);
262281

263282
paths::create_dir_all(&script_dir)?;
264283
paths::create_dir_all(&script_out_dir)?;
@@ -286,15 +305,16 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes
286305
// native dynamic libraries.
287306
if !build_plan {
288307
let build_script_outputs = build_script_outputs.lock().unwrap();
289-
for (name, id) in lib_deps {
290-
let key = (id, kind);
291-
let script_output = build_script_outputs.get(&key).ok_or_else(|| {
292-
internal(format!(
293-
"failed to locate build state for env \
294-
vars: {}/{:?}",
295-
id, kind
296-
))
297-
})?;
308+
for (name, dep_id, dep_metadata) in lib_deps {
309+
let script_output =
310+
build_script_outputs
311+
.get(dep_id, dep_metadata)
312+
.ok_or_else(|| {
313+
internal(format!(
314+
"failed to locate build state for env vars: {}/{}",
315+
dep_id, dep_metadata
316+
))
317+
})?;
298318
let data = &script_output.metadata;
299319
for &(ref key, ref value) in data.iter() {
300320
cmd.env(
@@ -360,7 +380,7 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes
360380
build_script_outputs
361381
.lock()
362382
.unwrap()
363-
.insert((id, kind), parsed_output);
383+
.insert(id, metadata_hash, parsed_output);
364384
Ok(())
365385
});
366386

@@ -386,7 +406,7 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes
386406
build_script_outputs
387407
.lock()
388408
.unwrap()
389-
.insert((id, kind), output);
409+
.insert(id, metadata_hash, output);
390410
Ok(())
391411
});
392412

@@ -614,7 +634,7 @@ impl BuildDeps {
614634
/// scripts.
615635
///
616636
/// The important one here is `build_scripts`, which for each `(package,
617-
/// kind)` stores a `BuildScripts` object which contains a list of
637+
/// metadata)` stores a `BuildScripts` object which contains a list of
618638
/// dependencies with build scripts that the unit should consider when
619639
/// linking. For example this lists all dependencies' `-L` flags which need to
620640
/// be propagated transitively.
@@ -644,20 +664,27 @@ pub fn build_map<'b, 'cfg>(cx: &mut Context<'b, 'cfg>, units: &[Unit<'b>]) -> Ca
644664
}
645665

646666
// If there is a build script override, pre-fill the build output.
647-
if let Some(links) = unit.pkg.manifest().links() {
648-
if let Some(output) = cx.bcx.script_override(links, unit.kind) {
649-
let key = (unit.pkg.package_id(), unit.kind);
650-
cx.build_script_outputs
651-
.lock()
652-
.unwrap()
653-
.insert(key, output.clone());
667+
if unit.mode.is_run_custom_build() {
668+
if let Some(links) = unit.pkg.manifest().links() {
669+
if let Some(output) = cx.bcx.script_override(links, unit.kind) {
670+
let metadata = cx.get_run_build_script_metadata(unit);
671+
cx.build_script_outputs.lock().unwrap().insert(
672+
unit.pkg.package_id(),
673+
metadata,
674+
output.clone(),
675+
);
676+
}
654677
}
655678
}
656679

657680
let mut ret = BuildScripts::default();
658681

682+
// If a package has a build script, add itself as something to inspect for linking.
659683
if !unit.target.is_custom_build() && unit.pkg.has_custom_build() {
660-
add_to_link(&mut ret, unit.pkg.package_id(), unit.kind);
684+
let script_meta = cx
685+
.find_build_script_metadata(*unit)
686+
.expect("has_custom_build should have RunCustomBuild");
687+
add_to_link(&mut ret, unit.pkg.package_id(), script_meta);
661688
}
662689

663690
// Load any dependency declarations from a previous run.
@@ -676,11 +703,10 @@ pub fn build_map<'b, 'cfg>(cx: &mut Context<'b, 'cfg>, units: &[Unit<'b>]) -> Ca
676703
let dep_scripts = build(out, cx, dep_unit)?;
677704

678705
if dep_unit.target.for_host() {
679-
ret.plugins
680-
.extend(dep_scripts.to_link.iter().map(|p| &p.0).cloned());
706+
ret.plugins.extend(dep_scripts.to_link.iter().cloned());
681707
} else if dep_unit.target.linkable() {
682-
for &(pkg, kind) in dep_scripts.to_link.iter() {
683-
add_to_link(&mut ret, pkg, kind);
708+
for &(pkg, metadata) in dep_scripts.to_link.iter() {
709+
add_to_link(&mut ret, pkg, metadata);
684710
}
685711
}
686712
}
@@ -693,9 +719,9 @@ pub fn build_map<'b, 'cfg>(cx: &mut Context<'b, 'cfg>, units: &[Unit<'b>]) -> Ca
693719

694720
// When adding an entry to 'to_link' we only actually push it on if the
695721
// script hasn't seen it yet (e.g., we don't push on duplicates).
696-
fn add_to_link(scripts: &mut BuildScripts, pkg: PackageId, kind: CompileKind) {
697-
if scripts.seen_to_link.insert((pkg, kind)) {
698-
scripts.to_link.push((pkg, kind));
722+
fn add_to_link(scripts: &mut BuildScripts, pkg: PackageId, metadata: Metadata) {
723+
if scripts.seen_to_link.insert((pkg, metadata)) {
724+
scripts.to_link.push((pkg, metadata));
699725
}
700726
}
701727

@@ -741,3 +767,37 @@ fn prev_build_output<'a, 'cfg>(
741767
prev_script_out_dir,
742768
)
743769
}
770+
771+
impl BuildScriptOutputs {
772+
/// Inserts a new entry into the map.
773+
fn insert(&mut self, pkg_id: PackageId, metadata: Metadata, parsed_output: BuildOutput) {
774+
match self.outputs.entry((pkg_id, metadata)) {
775+
Entry::Vacant(entry) => {
776+
entry.insert(parsed_output);
777+
}
778+
Entry::Occupied(entry) => panic!(
779+
"build script output collision for {}/{}\n\
780+
old={:?}\nnew={:?}",
781+
pkg_id,
782+
metadata,
783+
entry.get(),
784+
parsed_output
785+
),
786+
}
787+
}
788+
789+
/// Returns `true` if the given key already exists.
790+
fn contains_key(&self, pkg_id: PackageId, metadata: Metadata) -> bool {
791+
self.outputs.contains_key(&(pkg_id, metadata))
792+
}
793+
794+
/// Gets the build output for the given key.
795+
pub fn get(&self, pkg_id: PackageId, meta: Metadata) -> Option<&BuildOutput> {
796+
self.outputs.get(&(pkg_id, meta))
797+
}
798+
799+
/// Returns an iterator over all entries.
800+
pub fn iter(&self) -> impl Iterator<Item = (PackageId, &BuildOutput)> {
801+
self.outputs.iter().map(|(key, value)| (key.0, value))
802+
}
803+
}

0 commit comments

Comments
 (0)