Fix BuildScriptOutput when a build script is run multiple times.

This commit is contained in:
Eric Huss 2020-02-01 11:38:05 -08:00
parent 972b9f55a7
commit 2296af27ae
9 changed files with 296 additions and 81 deletions

View File

@ -11,6 +11,7 @@ use crate::core::compiler::CompileKind;
use crate::core::{Edition, Package, PackageId, Target}; use crate::core::{Edition, Package, PackageId, Target};
use crate::util::{self, config, join_paths, process, CargoResult, Config, ProcessBuilder}; use crate::util::{self, config, join_paths, process, CargoResult, Config, ProcessBuilder};
/// Structure with enough information to run `rustdoc --test`.
pub struct Doctest { pub struct Doctest {
/// The package being doc-tested. /// The package being doc-tested.
pub package: Package, pub package: Package,

View File

@ -41,7 +41,7 @@ use crate::util::{self, CargoResult};
/// ///
/// Note that the `Fingerprint` is in charge of tracking everything needed to determine if a /// Note that the `Fingerprint` is in charge of tracking everything needed to determine if a
/// rebuild is needed. /// rebuild is needed.
#[derive(Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] #[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct Metadata(u64); pub struct Metadata(u64);
impl fmt::Display for Metadata { impl fmt::Display for Metadata {
@ -50,6 +50,12 @@ impl fmt::Display for Metadata {
} }
} }
impl fmt::Debug for Metadata {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Metadata({:016x})", self.0)
}
}
/// Collection of information about the files emitted by the compiler, and the /// Collection of information about the files emitted by the compiler, and the
/// output directory structure. /// output directory structure.
pub struct CompilationFiles<'a, 'cfg> { pub struct CompilationFiles<'a, 'cfg> {
@ -213,6 +219,7 @@ impl<'a, 'cfg: 'a> CompilationFiles<'a, 'cfg> {
pub fn build_script_dir(&self, unit: &Unit<'a>) -> PathBuf { pub fn build_script_dir(&self, unit: &Unit<'a>) -> PathBuf {
assert!(unit.target.is_custom_build()); assert!(unit.target.is_custom_build());
assert!(!unit.mode.is_run_custom_build()); assert!(!unit.mode.is_run_custom_build());
assert!(self.metas.contains_key(unit));
let dir = self.pkg_dir(unit); let dir = self.pkg_dir(unit);
self.layout(CompileKind::Host).build().join(dir) self.layout(CompileKind::Host).build().join(dir)
} }

View File

@ -122,8 +122,8 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
}) })
} }
// Returns a mapping of the root package plus its immediate dependencies to /// Starts compilation, waits for it to finish, and returns information
// where the compiled libraries are all located. /// about the result of compilation.
pub fn compile( pub fn compile(
mut self, mut self,
units: &[Unit<'a>], units: &[Unit<'a>],
@ -245,16 +245,16 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
super::output_depinfo(&mut self, unit)?; super::output_depinfo(&mut self, unit)?;
} }
for (&(ref pkg, _), output) in self.build_script_outputs.lock().unwrap().iter() { for (pkg_id, output) in self.build_script_outputs.lock().unwrap().iter() {
self.compilation self.compilation
.cfgs .cfgs
.entry(pkg.clone()) .entry(pkg_id)
.or_insert_with(HashSet::new) .or_insert_with(HashSet::new)
.extend(output.cfgs.iter().cloned()); .extend(output.cfgs.iter().cloned());
self.compilation self.compilation
.extra_env .extra_env
.entry(pkg.clone()) .entry(pkg_id)
.or_insert_with(Vec::new) .or_insert_with(Vec::new)
.extend(output.env.iter().cloned()); .extend(output.env.iter().cloned());
@ -347,6 +347,39 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
&self.unit_dependencies[unit] &self.unit_dependencies[unit]
} }
/// Returns the RunCustomBuild Unit associated with the given Unit.
///
/// If the package does not have a build script, this returns None.
pub fn find_build_script_unit(&self, unit: Unit<'a>) -> Option<Unit<'a>> {
if unit.mode.is_run_custom_build() {
return Some(unit);
}
self.unit_dependencies[&unit]
.iter()
.find(|unit_dep| {
unit_dep.unit.mode.is_run_custom_build()
&& unit_dep.unit.pkg.package_id() == unit.pkg.package_id()
})
.map(|unit_dep| unit_dep.unit)
}
/// Returns the metadata hash for the RunCustomBuild Unit associated with
/// the given unit.
///
/// If the package does not have a build script, this returns None.
pub fn find_build_script_metadata(&self, unit: Unit<'a>) -> Option<Metadata> {
let script_unit = self.find_build_script_unit(unit)?;
Some(self.get_run_build_script_metadata(&script_unit))
}
/// Returns the metadata hash for a RunCustomBuild unit.
pub fn get_run_build_script_metadata(&self, unit: &Unit<'a>) -> Metadata {
assert!(unit.mode.is_run_custom_build());
self.files()
.metadata(unit)
.expect("build script should always have hash")
}
pub fn is_primary_package(&self, unit: &Unit<'a>) -> bool { pub fn is_primary_package(&self, unit: &Unit<'a>) -> bool {
self.primary_packages.contains(&unit.pkg.package_id()) self.primary_packages.contains(&unit.pkg.package_id())
} }

View File

@ -1,5 +1,6 @@
use super::job::{Freshness, Job, Work}; use super::job::{Freshness, Job, Work};
use super::{fingerprint, CompileKind, Context, Unit}; use super::{fingerprint, Context, Unit};
use crate::core::compiler::context::Metadata;
use crate::core::compiler::job_queue::JobState; use crate::core::compiler::job_queue::JobState;
use crate::core::{profiles::ProfileRoot, PackageId}; use crate::core::{profiles::ProfileRoot, PackageId};
use crate::util::errors::{CargoResult, CargoResultExt}; use crate::util::errors::{CargoResult, CargoResultExt};
@ -41,13 +42,24 @@ pub struct BuildOutput {
/// This initially starts out as empty. Overridden build scripts get /// This initially starts out as empty. Overridden build scripts get
/// inserted during `build_map`. The rest of the entries are added /// inserted during `build_map`. The rest of the entries are added
/// immediately after each build script runs. /// immediately after each build script runs.
pub type BuildScriptOutputs = HashMap<(PackageId, CompileKind), BuildOutput>; ///
/// The `Metadata` is the unique metadata hash for the RunCustomBuild Unit of
/// the package. It needs a unique key, since the build script can be run
/// multiple times with different profiles or features. We can't embed a
/// `Unit` because this structure needs to be shareable between threads.
#[derive(Default)]
pub struct BuildScriptOutputs {
outputs: HashMap<(PackageId, Metadata), BuildOutput>,
}
/// Linking information for a `Unit`. /// Linking information for a `Unit`.
/// ///
/// See `build_map` for more details. /// See `build_map` for more details.
#[derive(Default)] #[derive(Default)]
pub struct BuildScripts { pub struct BuildScripts {
/// List of build script outputs this Unit needs to include for linking. Each
/// element is an index into `BuildScriptOutputs`.
///
/// Cargo will use this `to_link` vector to add `-L` flags to compiles as we /// Cargo will use this `to_link` vector to add `-L` flags to compiles as we
/// propagate them upwards towards the final build. Note, however, that we /// propagate them upwards towards the final build. Note, however, that we
/// need to preserve the ordering of `to_link` to be topologically sorted. /// need to preserve the ordering of `to_link` to be topologically sorted.
@ -55,23 +67,24 @@ pub struct BuildScripts {
/// correctly pick up the files they generated (if there are duplicates /// correctly pick up the files they generated (if there are duplicates
/// elsewhere). /// elsewhere).
/// ///
/// To preserve this ordering, the (id, kind) is stored in two places, once /// To preserve this ordering, the (id, metadata) is stored in two places, once
/// in the `Vec` and once in `seen_to_link` for a fast lookup. We maintain /// in the `Vec` and once in `seen_to_link` for a fast lookup. We maintain
/// this as we're building interactively below to ensure that the memory /// this as we're building interactively below to ensure that the memory
/// usage here doesn't blow up too much. /// usage here doesn't blow up too much.
/// ///
/// For more information, see #2354. /// For more information, see #2354.
pub to_link: Vec<(PackageId, CompileKind)>, pub to_link: Vec<(PackageId, Metadata)>,
/// This is only used while constructing `to_link` to avoid duplicates. /// This is only used while constructing `to_link` to avoid duplicates.
seen_to_link: HashSet<(PackageId, CompileKind)>, seen_to_link: HashSet<(PackageId, Metadata)>,
/// Host-only dependencies that have build scripts. /// Host-only dependencies that have build scripts. Each element is an
/// index into `BuildScriptOutputs`.
/// ///
/// This is the set of transitive dependencies that are host-only /// This is the set of transitive dependencies that are host-only
/// (proc-macro, plugin, build-dependency) that contain a build script. /// (proc-macro, plugin, build-dependency) that contain a build script.
/// Any `BuildOutput::library_paths` path relative to `target` will be /// Any `BuildOutput::library_paths` path relative to `target` will be
/// added to LD_LIBRARY_PATH so that the compiler can find any dynamic /// added to LD_LIBRARY_PATH so that the compiler can find any dynamic
/// libraries a build script may have generated. /// libraries a build script may have generated.
pub plugins: BTreeSet<PackageId>, pub plugins: BTreeSet<(PackageId, Metadata)>,
} }
/// Dependency information as declared by a build script. /// 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
unit.target.name() unit.target.name()
)); ));
let key = (unit.pkg.package_id(), unit.kind); let metadata = cx.get_run_build_script_metadata(unit);
if cx
if cx.build_script_outputs.lock().unwrap().contains_key(&key) { .build_script_outputs
.lock()
.unwrap()
.contains_key(unit.pkg.package_id(), metadata)
{
// The output is already set, thus the build script is overridden. // The output is already set, thus the build script is overridden.
fingerprint::prepare_target(cx, unit, false) fingerprint::prepare_target(cx, unit, false)
} else { } else {
@ -231,9 +248,11 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes
.iter() .iter()
.filter_map(|dep| { .filter_map(|dep| {
if dep.unit.mode.is_run_custom_build() { if dep.unit.mode.is_run_custom_build() {
let dep_metadata = cx.get_run_build_script_metadata(&dep.unit);
Some(( Some((
dep.unit.pkg.manifest().links().unwrap().to_string(), dep.unit.pkg.manifest().links().unwrap().to_string(),
dep.unit.pkg.package_id(), dep.unit.pkg.package_id(),
dep_metadata,
)) ))
} else { } else {
None None
@ -255,10 +274,10 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes
script_out_dir.clone(), script_out_dir.clone(),
); );
let build_scripts = cx.build_scripts.get(unit).cloned(); let build_scripts = cx.build_scripts.get(unit).cloned();
let kind = unit.kind;
let json_messages = bcx.build_config.emit_json(); let json_messages = bcx.build_config.emit_json();
let extra_verbose = bcx.config.extra_verbose(); let extra_verbose = bcx.config.extra_verbose();
let (prev_output, prev_script_out_dir) = prev_build_output(cx, unit); let (prev_output, prev_script_out_dir) = prev_build_output(cx, unit);
let metadata_hash = cx.get_run_build_script_metadata(unit);
paths::create_dir_all(&script_dir)?; paths::create_dir_all(&script_dir)?;
paths::create_dir_all(&script_out_dir)?; 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
// native dynamic libraries. // native dynamic libraries.
if !build_plan { if !build_plan {
let build_script_outputs = build_script_outputs.lock().unwrap(); let build_script_outputs = build_script_outputs.lock().unwrap();
for (name, id) in lib_deps { for (name, dep_id, dep_metadata) in lib_deps {
let key = (id, kind); let script_output =
let script_output = build_script_outputs.get(&key).ok_or_else(|| { build_script_outputs
internal(format!( .get(dep_id, dep_metadata)
"failed to locate build state for env \ .ok_or_else(|| {
vars: {}/{:?}", internal(format!(
id, kind "failed to locate build state for env vars: {}/{}",
)) dep_id, dep_metadata
})?; ))
})?;
let data = &script_output.metadata; let data = &script_output.metadata;
for &(ref key, ref value) in data.iter() { for &(ref key, ref value) in data.iter() {
cmd.env( cmd.env(
@ -360,7 +380,7 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes
build_script_outputs build_script_outputs
.lock() .lock()
.unwrap() .unwrap()
.insert((id, kind), parsed_output); .insert(id, metadata_hash, parsed_output);
Ok(()) Ok(())
}); });
@ -386,7 +406,7 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes
build_script_outputs build_script_outputs
.lock() .lock()
.unwrap() .unwrap()
.insert((id, kind), output); .insert(id, metadata_hash, output);
Ok(()) Ok(())
}); });
@ -614,7 +634,7 @@ impl BuildDeps {
/// scripts. /// scripts.
/// ///
/// The important one here is `build_scripts`, which for each `(package, /// The important one here is `build_scripts`, which for each `(package,
/// kind)` stores a `BuildScripts` object which contains a list of /// metadata)` stores a `BuildScripts` object which contains a list of
/// dependencies with build scripts that the unit should consider when /// dependencies with build scripts that the unit should consider when
/// linking. For example this lists all dependencies' `-L` flags which need to /// linking. For example this lists all dependencies' `-L` flags which need to
/// be propagated transitively. /// be propagated transitively.
@ -644,20 +664,27 @@ pub fn build_map<'b, 'cfg>(cx: &mut Context<'b, 'cfg>, units: &[Unit<'b>]) -> Ca
} }
// If there is a build script override, pre-fill the build output. // If there is a build script override, pre-fill the build output.
if let Some(links) = unit.pkg.manifest().links() { if unit.mode.is_run_custom_build() {
if let Some(output) = cx.bcx.script_override(links, unit.kind) { if let Some(links) = unit.pkg.manifest().links() {
let key = (unit.pkg.package_id(), unit.kind); if let Some(output) = cx.bcx.script_override(links, unit.kind) {
cx.build_script_outputs let metadata = cx.get_run_build_script_metadata(unit);
.lock() cx.build_script_outputs.lock().unwrap().insert(
.unwrap() unit.pkg.package_id(),
.insert(key, output.clone()); metadata,
output.clone(),
);
}
} }
} }
let mut ret = BuildScripts::default(); let mut ret = BuildScripts::default();
// If a package has a build script, add itself as something to inspect for linking.
if !unit.target.is_custom_build() && unit.pkg.has_custom_build() { if !unit.target.is_custom_build() && unit.pkg.has_custom_build() {
add_to_link(&mut ret, unit.pkg.package_id(), unit.kind); let script_meta = cx
.find_build_script_metadata(*unit)
.expect("has_custom_build should have RunCustomBuild");
add_to_link(&mut ret, unit.pkg.package_id(), script_meta);
} }
// Load any dependency declarations from a previous run. // 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
let dep_scripts = build(out, cx, dep_unit)?; let dep_scripts = build(out, cx, dep_unit)?;
if dep_unit.target.for_host() { if dep_unit.target.for_host() {
ret.plugins ret.plugins.extend(dep_scripts.to_link.iter().cloned());
.extend(dep_scripts.to_link.iter().map(|p| &p.0).cloned());
} else if dep_unit.target.linkable() { } else if dep_unit.target.linkable() {
for &(pkg, kind) in dep_scripts.to_link.iter() { for &(pkg, metadata) in dep_scripts.to_link.iter() {
add_to_link(&mut ret, pkg, kind); add_to_link(&mut ret, pkg, metadata);
} }
} }
} }
@ -693,9 +719,9 @@ pub fn build_map<'b, 'cfg>(cx: &mut Context<'b, 'cfg>, units: &[Unit<'b>]) -> Ca
// When adding an entry to 'to_link' we only actually push it on if the // When adding an entry to 'to_link' we only actually push it on if the
// script hasn't seen it yet (e.g., we don't push on duplicates). // script hasn't seen it yet (e.g., we don't push on duplicates).
fn add_to_link(scripts: &mut BuildScripts, pkg: PackageId, kind: CompileKind) { fn add_to_link(scripts: &mut BuildScripts, pkg: PackageId, metadata: Metadata) {
if scripts.seen_to_link.insert((pkg, kind)) { if scripts.seen_to_link.insert((pkg, metadata)) {
scripts.to_link.push((pkg, kind)); scripts.to_link.push((pkg, metadata));
} }
} }
@ -741,3 +767,37 @@ fn prev_build_output<'a, 'cfg>(
prev_script_out_dir, prev_script_out_dir,
) )
} }
impl BuildScriptOutputs {
/// Inserts a new entry into the map.
fn insert(&mut self, pkg_id: PackageId, metadata: Metadata, parsed_output: BuildOutput) {
match self.outputs.entry((pkg_id, metadata)) {
Entry::Vacant(entry) => {
entry.insert(parsed_output);
}
Entry::Occupied(entry) => panic!(
"build script output collision for {}/{}\n\
old={:?}\nnew={:?}",
pkg_id,
metadata,
entry.get(),
parsed_output
),
}
}
/// Returns `true` if the given key already exists.
fn contains_key(&self, pkg_id: PackageId, metadata: Metadata) -> bool {
self.outputs.contains_key(&(pkg_id, metadata))
}
/// Gets the build output for the given key.
pub fn get(&self, pkg_id: PackageId, meta: Metadata) -> Option<&BuildOutput> {
self.outputs.get(&(pkg_id, meta))
}
/// Returns an iterator over all entries.
pub fn iter(&self) -> impl Iterator<Item = (PackageId, &BuildOutput)> {
self.outputs.iter().map(|(key, value)| (key.0, value))
}
}

View File

@ -287,13 +287,16 @@ pub fn prepare_target<'a, 'cfg>(
// using the `build_script_local_fingerprints` function which returns a // using the `build_script_local_fingerprints` function which returns a
// thunk we can invoke on a foreign thread to calculate this. // thunk we can invoke on a foreign thread to calculate this.
let build_script_outputs = Arc::clone(&cx.build_script_outputs); let build_script_outputs = Arc::clone(&cx.build_script_outputs);
let key = (unit.pkg.package_id(), unit.kind); let pkg_id = unit.pkg.package_id();
let metadata = cx.get_run_build_script_metadata(unit);
let (gen_local, _overridden) = build_script_local_fingerprints(cx, unit); let (gen_local, _overridden) = build_script_local_fingerprints(cx, unit);
let output_path = cx.build_explicit_deps[unit].build_script_output.clone(); let output_path = cx.build_explicit_deps[unit].build_script_output.clone();
Work::new(move |_| { Work::new(move |_| {
let outputs = build_script_outputs.lock().unwrap(); let outputs = build_script_outputs.lock().unwrap();
let outputs = &outputs[&key]; let output = outputs
let deps = BuildDeps::new(&output_path, Some(outputs)); .get(pkg_id, metadata)
.expect("output must exist after running");
let deps = BuildDeps::new(&output_path, Some(output));
// FIXME: it's basically buggy that we pass `None` to `call_box` // FIXME: it's basically buggy that we pass `None` to `call_box`
// here. See documentation on `build_script_local_fingerprints` // here. See documentation on `build_script_local_fingerprints`
@ -1134,6 +1137,7 @@ fn calculate_run_custom_build<'a, 'cfg>(
cx: &mut Context<'a, 'cfg>, cx: &mut Context<'a, 'cfg>,
unit: &Unit<'a>, unit: &Unit<'a>,
) -> CargoResult<Fingerprint> { ) -> CargoResult<Fingerprint> {
assert!(unit.mode.is_run_custom_build());
// Using the `BuildDeps` information we'll have previously parsed and // Using the `BuildDeps` information we'll have previously parsed and
// inserted into `build_explicit_deps` built an initial snapshot of the // inserted into `build_explicit_deps` built an initial snapshot of the
// `LocalFingerprint` list for this build script. If we previously executed // `LocalFingerprint` list for this build script. If we previously executed
@ -1220,6 +1224,7 @@ fn build_script_local_fingerprints<'a, 'cfg>(
>, >,
bool, bool,
) { ) {
assert!(unit.mode.is_run_custom_build());
// First up, if this build script is entirely overridden, then we just // First up, if this build script is entirely overridden, then we just
// return the hash of what we overrode it with. This is the easy case! // return the hash of what we overrode it with. This is the easy case!
if let Some(fingerprint) = build_script_override_fingerprint(cx, unit) { if let Some(fingerprint) = build_script_override_fingerprint(cx, unit) {
@ -1285,8 +1290,9 @@ fn build_script_override_fingerprint<'a, 'cfg>(
// Build script output is only populated at this stage when it is // Build script output is only populated at this stage when it is
// overridden. // overridden.
let build_script_outputs = cx.build_script_outputs.lock().unwrap(); let build_script_outputs = cx.build_script_outputs.lock().unwrap();
let metadata = cx.get_run_build_script_metadata(unit);
// Returns None if it is not overridden. // Returns None if it is not overridden.
let output = build_script_outputs.get(&(unit.pkg.package_id(), unit.kind))?; let output = build_script_outputs.get(unit.pkg.package_id(), metadata)?;
let s = format!( let s = format!(
"overridden build state with hash: {}", "overridden build state with hash: {}",
util::hash_u64(output) util::hash_u64(output)

View File

@ -835,11 +835,15 @@ impl<'a, 'cfg> DrainState<'a, 'cfg> {
&mut self, &mut self,
msg: Option<&str>, msg: Option<&str>,
unit: &Unit<'a>, unit: &Unit<'a>,
cx: &mut Context<'_, '_>, cx: &mut Context<'a, '_>,
) -> CargoResult<()> { ) -> CargoResult<()> {
let outputs = cx.build_script_outputs.lock().unwrap(); let outputs = cx.build_script_outputs.lock().unwrap();
let metadata = match cx.find_build_script_metadata(*unit) {
Some(metadata) => metadata,
None => return Ok(()),
};
let bcx = &mut cx.bcx; let bcx = &mut cx.bcx;
if let Some(output) = outputs.get(&(unit.pkg.package_id(), unit.kind)) { if let Some(output) = outputs.get(unit.pkg.package_id(), metadata) {
if !output.warnings.is_empty() { if !output.warnings.is_empty() {
if let Some(msg) = msg { if let Some(msg) = msg {
writeln!(bcx.config.shell().err(), "{}\n", msg)?; writeln!(bcx.config.shell().err(), "{}\n", msg)?;

View File

@ -32,7 +32,7 @@ pub use self::build_context::{BuildContext, FileFlavor, TargetInfo};
use self::build_plan::BuildPlan; use self::build_plan::BuildPlan;
pub use self::compilation::{Compilation, Doctest}; pub use self::compilation::{Compilation, Doctest};
pub use self::compile_kind::{CompileKind, CompileTarget}; pub use self::compile_kind::{CompileKind, CompileTarget};
pub use self::context::Context; pub use self::context::{Context, Metadata};
pub use self::custom_build::{BuildOutput, BuildScriptOutputs, BuildScripts}; pub use self::custom_build::{BuildOutput, BuildScriptOutputs, BuildScripts};
pub use self::job::Freshness; pub use self::job::Freshness;
use self::job::{Job, Work}; use self::job::{Job, Work};
@ -181,7 +181,6 @@ fn rustc<'a, 'cfg>(
let outputs = cx.outputs(unit)?; let outputs = cx.outputs(unit)?;
let root = cx.files().out_dir(unit); let root = cx.files().out_dir(unit);
let kind = unit.kind;
// Prepare the native lib state (extra `-L` and `-l` flags). // Prepare the native lib state (extra `-L` and `-l` flags).
let build_script_outputs = Arc::clone(&cx.build_script_outputs); let build_script_outputs = Arc::clone(&cx.build_script_outputs);
@ -225,6 +224,7 @@ fn rustc<'a, 'cfg>(
.unwrap_or_else(|| cx.bcx.config.cwd()) .unwrap_or_else(|| cx.bcx.config.cwd())
.to_path_buf(); .to_path_buf();
let fingerprint_dir = cx.files().fingerprint_dir(unit); let fingerprint_dir = cx.files().fingerprint_dir(unit);
let script_metadata = cx.find_build_script_metadata(*unit);
return Ok(Work::new(move |state| { return Ok(Work::new(move |state| {
// Only at runtime have we discovered what the extra -L and -l // Only at runtime have we discovered what the extra -L and -l
@ -247,7 +247,7 @@ fn rustc<'a, 'cfg>(
)?; )?;
add_plugin_deps(&mut rustc, &script_outputs, &build_scripts, &root_output)?; add_plugin_deps(&mut rustc, &script_outputs, &build_scripts, &root_output)?;
} }
add_custom_env(&mut rustc, &script_outputs, current_id, kind)?; add_custom_env(&mut rustc, &script_outputs, current_id, script_metadata)?;
} }
for output in outputs.iter() { for output in outputs.iter() {
@ -340,9 +340,9 @@ fn rustc<'a, 'cfg>(
current_id: PackageId, current_id: PackageId,
) -> CargoResult<()> { ) -> CargoResult<()> {
for key in build_scripts.to_link.iter() { for key in build_scripts.to_link.iter() {
let output = build_script_outputs.get(key).ok_or_else(|| { let output = build_script_outputs.get(key.0, key.1).ok_or_else(|| {
internal(format!( internal(format!(
"couldn't find build script output for {}/{:?}", "couldn't find build script output for {}/{}",
key.0, key.1 key.0, key.1
)) ))
})?; })?;
@ -375,10 +375,13 @@ fn rustc<'a, 'cfg>(
rustc: &mut ProcessBuilder, rustc: &mut ProcessBuilder,
build_script_outputs: &BuildScriptOutputs, build_script_outputs: &BuildScriptOutputs,
current_id: PackageId, current_id: PackageId,
kind: CompileKind, metadata: Option<Metadata>,
) -> CargoResult<()> { ) -> CargoResult<()> {
let key = (current_id, kind); let metadata = match metadata {
if let Some(output) = build_script_outputs.get(&key) { Some(metadata) => metadata,
None => return Ok(()),
};
if let Some(output) = build_script_outputs.get(current_id, metadata) {
for &(ref name, ref value) in output.env.iter() { for &(ref name, ref value) in output.env.iter() {
rustc.env(name, value); rustc.env(name, value);
} }
@ -477,10 +480,10 @@ fn add_plugin_deps(
let var = util::dylib_path_envvar(); let var = util::dylib_path_envvar();
let search_path = rustc.get_env(var).unwrap_or_default(); let search_path = rustc.get_env(var).unwrap_or_default();
let mut search_path = env::split_paths(&search_path).collect::<Vec<_>>(); let mut search_path = env::split_paths(&search_path).collect::<Vec<_>>();
for &id in build_scripts.plugins.iter() { for (pkg_id, metadata) in &build_scripts.plugins {
let output = build_script_outputs let output = build_script_outputs
.get(&(id, CompileKind::Host)) .get(*pkg_id, *metadata)
.ok_or_else(|| internal(format!("couldn't find libs for plugin dep {}", id)))?; .ok_or_else(|| internal(format!("couldn't find libs for plugin dep {}", pkg_id)))?;
search_path.append(&mut filter_dynamic_search_path( search_path.append(&mut filter_dynamic_search_path(
output.library_paths.iter(), output.library_paths.iter(),
root_output, root_output,
@ -588,18 +591,25 @@ fn rustdoc<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoResult
let name = unit.pkg.name().to_string(); let name = unit.pkg.name().to_string();
let build_script_outputs = Arc::clone(&cx.build_script_outputs); let build_script_outputs = Arc::clone(&cx.build_script_outputs);
let key = (unit.pkg.package_id(), unit.kind);
let package_id = unit.pkg.package_id(); let package_id = unit.pkg.package_id();
let target = unit.target.clone(); let target = unit.target.clone();
let mut output_options = OutputOptions::new(cx, unit); let mut output_options = OutputOptions::new(cx, unit);
let pkg_id = unit.pkg.package_id();
let script_metadata = cx.find_build_script_metadata(*unit);
Ok(Work::new(move |state| { Ok(Work::new(move |state| {
if let Some(output) = build_script_outputs.lock().unwrap().get(&key) { if let Some(script_metadata) = script_metadata {
for cfg in output.cfgs.iter() { if let Some(output) = build_script_outputs
rustdoc.arg("--cfg").arg(cfg); .lock()
} .unwrap()
for &(ref name, ref value) in output.env.iter() { .get(pkg_id, script_metadata)
rustdoc.env(name, value); {
for cfg in output.cfgs.iter() {
rustdoc.arg("--cfg").arg(cfg);
}
for &(ref name, ref value) in output.env.iter() {
rustdoc.env(name, value);
}
} }
} }
state.running(&rustdoc); state.running(&rustdoc);

View File

@ -50,7 +50,7 @@ fn render_filename<P: AsRef<Path>>(path: P, basedir: Option<&str>) -> CargoResul
fn add_deps_for_unit<'a, 'b>( fn add_deps_for_unit<'a, 'b>(
deps: &mut BTreeSet<PathBuf>, deps: &mut BTreeSet<PathBuf>,
context: &mut Context<'a, 'b>, cx: &mut Context<'a, 'b>,
unit: &Unit<'a>, unit: &Unit<'a>,
visited: &mut HashSet<Unit<'a>>, visited: &mut HashSet<Unit<'a>>,
) -> CargoResult<()> { ) -> CargoResult<()> {
@ -62,12 +62,10 @@ fn add_deps_for_unit<'a, 'b>(
// generate a dep info file, so we just keep on going below // generate a dep info file, so we just keep on going below
if !unit.mode.is_run_custom_build() { if !unit.mode.is_run_custom_build() {
// Add dependencies from rustc dep-info output (stored in fingerprint directory) // Add dependencies from rustc dep-info output (stored in fingerprint directory)
let dep_info_loc = fingerprint::dep_info_loc(context, unit); let dep_info_loc = fingerprint::dep_info_loc(cx, unit);
if let Some(paths) = fingerprint::parse_dep_info( if let Some(paths) =
unit.pkg.root(), fingerprint::parse_dep_info(unit.pkg.root(), cx.files().host_root(), &dep_info_loc)?
context.files().host_root(), {
&dep_info_loc,
)? {
for path in paths { for path in paths {
deps.insert(path); deps.insert(path);
} }
@ -82,19 +80,25 @@ fn add_deps_for_unit<'a, 'b>(
} }
// Add rerun-if-changed dependencies // Add rerun-if-changed dependencies
let key = (unit.pkg.package_id(), unit.kind); if let Some(metadata) = cx.find_build_script_metadata(*unit) {
if let Some(output) = context.build_script_outputs.lock().unwrap().get(&key) { if let Some(output) = cx
for path in &output.rerun_if_changed { .build_script_outputs
deps.insert(path.into()); .lock()
.unwrap()
.get(unit.pkg.package_id(), metadata)
{
for path in &output.rerun_if_changed {
deps.insert(path.into());
}
} }
} }
// Recursively traverse all transitive dependencies // Recursively traverse all transitive dependencies
let unit_deps = Vec::from(context.unit_deps(unit)); // Create vec due to mutable borrow. let unit_deps = Vec::from(cx.unit_deps(unit)); // Create vec due to mutable borrow.
for dep in unit_deps { for dep in unit_deps {
let source_id = dep.unit.pkg.package_id().source_id(); let source_id = dep.unit.pkg.package_id().source_id();
if source_id.is_path() { if source_id.is_path() {
add_deps_for_unit(deps, context, &dep.unit, visited)?; add_deps_for_unit(deps, cx, &dep.unit, visited)?;
} }
} }
Ok(()) Ok(())

View File

@ -423,3 +423,93 @@ fn no_warning_ws() {
) )
.run(); .run();
} }
#[cargo_test]
fn build_override_shared() {
// A dependency with a build script that is shared with a build
// dependency, using different profile settings. That is:
//
// foo DEBUG=2
// ├── common DEBUG=2
// │ └── common Run build.rs DEBUG=2
// │ └── common build.rs DEBUG=0 (build_override)
// └── foo Run build.rs DEBUG=2
// └── foo build.rs DEBUG=0 (build_override)
// └── common DEBUG=0 (build_override)
// └── common Run build.rs DEBUG=0 (build_override)
// └── common build.rs DEBUG=0 (build_override)
//
// The key part here is that `common` RunCustomBuild is run twice, once
// with DEBUG=2 (as a dependency of foo) and once with DEBUG=0 (as a
// build-dependency of foo's build script).
Package::new("common", "1.0.0")
.file(
"build.rs",
r#"
fn main() {
if std::env::var("DEBUG").unwrap() != "false" {
println!("cargo:rustc-cfg=foo_debug");
} else {
println!("cargo:rustc-cfg=foo_release");
}
}
"#,
)
.file(
"src/lib.rs",
r#"
pub fn foo() -> u32 {
if cfg!(foo_debug) {
assert!(cfg!(debug_assertions));
1
} else if cfg!(foo_release) {
assert!(!cfg!(debug_assertions));
2
} else {
panic!("not set");
}
}
"#,
)
.publish();
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.1.0"
edition = "2018"
[build-dependencies]
common = "1.0"
[dependencies]
common = "1.0"
[profile.dev.build-override]
debug = 0
debug-assertions = false
"#,
)
.file(
"build.rs",
r#"
fn main() {
assert_eq!(common::foo(), 2);
}
"#,
)
.file(
"src/main.rs",
r#"
fn main() {
assert_eq!(common::foo(), 1);
}
"#,
)
.build();
p.cargo("run").run();
}