From 2296af27ae2710b47ec5a1a9247ca1a4f50008a7 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 1 Feb 2020 11:38:05 -0800 Subject: [PATCH] Fix BuildScriptOutput when a build script is run multiple times. --- src/cargo/core/compiler/compilation.rs | 1 + .../compiler/context/compilation_files.rs | 9 +- src/cargo/core/compiler/context/mod.rs | 43 +++++- src/cargo/core/compiler/custom_build.rs | 136 +++++++++++++----- src/cargo/core/compiler/fingerprint.rs | 14 +- src/cargo/core/compiler/job_queue.rs | 8 +- src/cargo/core/compiler/mod.rs | 46 +++--- src/cargo/core/compiler/output_depinfo.rs | 30 ++-- tests/testsuite/profile_overrides.rs | 90 ++++++++++++ 9 files changed, 296 insertions(+), 81 deletions(-) diff --git a/src/cargo/core/compiler/compilation.rs b/src/cargo/core/compiler/compilation.rs index c3a3593f388..f91ca922bf9 100644 --- a/src/cargo/core/compiler/compilation.rs +++ b/src/cargo/core/compiler/compilation.rs @@ -11,6 +11,7 @@ use crate::core::compiler::CompileKind; use crate::core::{Edition, Package, PackageId, Target}; use crate::util::{self, config, join_paths, process, CargoResult, Config, ProcessBuilder}; +/// Structure with enough information to run `rustdoc --test`. pub struct Doctest { /// The package being doc-tested. pub package: Package, diff --git a/src/cargo/core/compiler/context/compilation_files.rs b/src/cargo/core/compiler/context/compilation_files.rs index 7ed94ac81dd..33415a8ccef 100644 --- a/src/cargo/core/compiler/context/compilation_files.rs +++ b/src/cargo/core/compiler/context/compilation_files.rs @@ -41,7 +41,7 @@ use crate::util::{self, CargoResult}; /// /// Note that the `Fingerprint` is in charge of tracking everything needed to determine if a /// rebuild is needed. -#[derive(Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] +#[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] pub struct Metadata(u64); 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 /// output directory structure. 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 { assert!(unit.target.is_custom_build()); assert!(!unit.mode.is_run_custom_build()); + assert!(self.metas.contains_key(unit)); let dir = self.pkg_dir(unit); self.layout(CompileKind::Host).build().join(dir) } diff --git a/src/cargo/core/compiler/context/mod.rs b/src/cargo/core/compiler/context/mod.rs index 8df9d799ccd..6597c9f968f 100644 --- a/src/cargo/core/compiler/context/mod.rs +++ b/src/cargo/core/compiler/context/mod.rs @@ -122,8 +122,8 @@ impl<'a, 'cfg> Context<'a, 'cfg> { }) } - // Returns a mapping of the root package plus its immediate dependencies to - // where the compiled libraries are all located. + /// Starts compilation, waits for it to finish, and returns information + /// about the result of compilation. pub fn compile( mut self, units: &[Unit<'a>], @@ -245,16 +245,16 @@ impl<'a, 'cfg> Context<'a, 'cfg> { 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 .cfgs - .entry(pkg.clone()) + .entry(pkg_id) .or_insert_with(HashSet::new) .extend(output.cfgs.iter().cloned()); self.compilation .extra_env - .entry(pkg.clone()) + .entry(pkg_id) .or_insert_with(Vec::new) .extend(output.env.iter().cloned()); @@ -347,6 +347,39 @@ impl<'a, 'cfg> Context<'a, 'cfg> { &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> { + 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 { + 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 { self.primary_packages.contains(&unit.pkg.package_id()) } diff --git a/src/cargo/core/compiler/custom_build.rs b/src/cargo/core/compiler/custom_build.rs index c35df661dd6..6f2314221a8 100644 --- a/src/cargo/core/compiler/custom_build.rs +++ b/src/cargo/core/compiler/custom_build.rs @@ -1,5 +1,6 @@ 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::{profiles::ProfileRoot, PackageId}; use crate::util::errors::{CargoResult, CargoResultExt}; @@ -41,13 +42,24 @@ pub struct BuildOutput { /// This initially starts out as empty. Overridden build scripts get /// inserted during `build_map`. The rest of the entries are added /// 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`. /// /// See `build_map` for more details. #[derive(Default)] 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 /// propagate them upwards towards the final build. Note, however, that we /// 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 /// 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 /// this as we're building interactively below to ensure that the memory /// usage here doesn't blow up too much. /// /// 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. - seen_to_link: HashSet<(PackageId, CompileKind)>, - /// Host-only dependencies that have build scripts. + seen_to_link: HashSet<(PackageId, Metadata)>, + /// 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 /// (proc-macro, plugin, build-dependency) that contain a build script. /// Any `BuildOutput::library_paths` path relative to `target` will be /// added to LD_LIBRARY_PATH so that the compiler can find any dynamic /// libraries a build script may have generated. - pub plugins: BTreeSet, + pub plugins: BTreeSet<(PackageId, Metadata)>, } /// 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() )); - let key = (unit.pkg.package_id(), unit.kind); - - if cx.build_script_outputs.lock().unwrap().contains_key(&key) { + let metadata = cx.get_run_build_script_metadata(unit); + if cx + .build_script_outputs + .lock() + .unwrap() + .contains_key(unit.pkg.package_id(), metadata) + { // The output is already set, thus the build script is overridden. fingerprint::prepare_target(cx, unit, false) } else { @@ -231,9 +248,11 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes .iter() .filter_map(|dep| { if dep.unit.mode.is_run_custom_build() { + let dep_metadata = cx.get_run_build_script_metadata(&dep.unit); Some(( dep.unit.pkg.manifest().links().unwrap().to_string(), dep.unit.pkg.package_id(), + dep_metadata, )) } else { None @@ -255,10 +274,10 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes script_out_dir.clone(), ); let build_scripts = cx.build_scripts.get(unit).cloned(); - let kind = unit.kind; let json_messages = bcx.build_config.emit_json(); let extra_verbose = bcx.config.extra_verbose(); 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_out_dir)?; @@ -286,15 +305,16 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes // native dynamic libraries. if !build_plan { let build_script_outputs = build_script_outputs.lock().unwrap(); - for (name, id) in lib_deps { - let key = (id, kind); - let script_output = build_script_outputs.get(&key).ok_or_else(|| { - internal(format!( - "failed to locate build state for env \ - vars: {}/{:?}", - id, kind - )) - })?; + for (name, dep_id, dep_metadata) in lib_deps { + let script_output = + build_script_outputs + .get(dep_id, dep_metadata) + .ok_or_else(|| { + internal(format!( + "failed to locate build state for env vars: {}/{}", + dep_id, dep_metadata + )) + })?; let data = &script_output.metadata; for &(ref key, ref value) in data.iter() { cmd.env( @@ -360,7 +380,7 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes build_script_outputs .lock() .unwrap() - .insert((id, kind), parsed_output); + .insert(id, metadata_hash, parsed_output); Ok(()) }); @@ -386,7 +406,7 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes build_script_outputs .lock() .unwrap() - .insert((id, kind), output); + .insert(id, metadata_hash, output); Ok(()) }); @@ -614,7 +634,7 @@ impl BuildDeps { /// scripts. /// /// 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 /// linking. For example this lists all dependencies' `-L` flags which need to /// 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 let Some(links) = unit.pkg.manifest().links() { - if let Some(output) = cx.bcx.script_override(links, unit.kind) { - let key = (unit.pkg.package_id(), unit.kind); - cx.build_script_outputs - .lock() - .unwrap() - .insert(key, output.clone()); + if unit.mode.is_run_custom_build() { + if let Some(links) = unit.pkg.manifest().links() { + if let Some(output) = cx.bcx.script_override(links, unit.kind) { + let metadata = cx.get_run_build_script_metadata(unit); + cx.build_script_outputs.lock().unwrap().insert( + unit.pkg.package_id(), + metadata, + output.clone(), + ); + } } } 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() { - 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. @@ -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)?; if dep_unit.target.for_host() { - ret.plugins - .extend(dep_scripts.to_link.iter().map(|p| &p.0).cloned()); + ret.plugins.extend(dep_scripts.to_link.iter().cloned()); } else if dep_unit.target.linkable() { - for &(pkg, kind) in dep_scripts.to_link.iter() { - add_to_link(&mut ret, pkg, kind); + for &(pkg, metadata) in dep_scripts.to_link.iter() { + 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 // 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) { - if scripts.seen_to_link.insert((pkg, kind)) { - scripts.to_link.push((pkg, kind)); + fn add_to_link(scripts: &mut BuildScripts, pkg: PackageId, metadata: Metadata) { + if scripts.seen_to_link.insert((pkg, metadata)) { + scripts.to_link.push((pkg, metadata)); } } @@ -741,3 +767,37 @@ fn prev_build_output<'a, 'cfg>( 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 { + self.outputs.iter().map(|(key, value)| (key.0, value)) + } +} diff --git a/src/cargo/core/compiler/fingerprint.rs b/src/cargo/core/compiler/fingerprint.rs index 84d20073400..564d0683970 100644 --- a/src/cargo/core/compiler/fingerprint.rs +++ b/src/cargo/core/compiler/fingerprint.rs @@ -287,13 +287,16 @@ pub fn prepare_target<'a, 'cfg>( // using the `build_script_local_fingerprints` function which returns a // thunk we can invoke on a foreign thread to calculate this. 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 output_path = cx.build_explicit_deps[unit].build_script_output.clone(); Work::new(move |_| { let outputs = build_script_outputs.lock().unwrap(); - let outputs = &outputs[&key]; - let deps = BuildDeps::new(&output_path, Some(outputs)); + let output = 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` // here. See documentation on `build_script_local_fingerprints` @@ -1134,6 +1137,7 @@ fn calculate_run_custom_build<'a, 'cfg>( cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>, ) -> CargoResult { + assert!(unit.mode.is_run_custom_build()); // Using the `BuildDeps` information we'll have previously parsed and // inserted into `build_explicit_deps` built an initial snapshot of the // `LocalFingerprint` list for this build script. If we previously executed @@ -1220,6 +1224,7 @@ fn build_script_local_fingerprints<'a, 'cfg>( >, bool, ) { + assert!(unit.mode.is_run_custom_build()); // 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! 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 // overridden. 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. - 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!( "overridden build state with hash: {}", util::hash_u64(output) diff --git a/src/cargo/core/compiler/job_queue.rs b/src/cargo/core/compiler/job_queue.rs index 55954911718..32c1f37fb4d 100644 --- a/src/cargo/core/compiler/job_queue.rs +++ b/src/cargo/core/compiler/job_queue.rs @@ -835,11 +835,15 @@ impl<'a, 'cfg> DrainState<'a, 'cfg> { &mut self, msg: Option<&str>, unit: &Unit<'a>, - cx: &mut Context<'_, '_>, + cx: &mut Context<'a, '_>, ) -> CargoResult<()> { 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; - 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 let Some(msg) = msg { writeln!(bcx.config.shell().err(), "{}\n", msg)?; diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs index 5052d790d8e..50795b6792f 100644 --- a/src/cargo/core/compiler/mod.rs +++ b/src/cargo/core/compiler/mod.rs @@ -32,7 +32,7 @@ pub use self::build_context::{BuildContext, FileFlavor, TargetInfo}; use self::build_plan::BuildPlan; pub use self::compilation::{Compilation, Doctest}; 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::job::Freshness; use self::job::{Job, Work}; @@ -181,7 +181,6 @@ fn rustc<'a, 'cfg>( let outputs = cx.outputs(unit)?; let root = cx.files().out_dir(unit); - let kind = unit.kind; // Prepare the native lib state (extra `-L` and `-l` flags). 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()) .to_path_buf(); let fingerprint_dir = cx.files().fingerprint_dir(unit); + let script_metadata = cx.find_build_script_metadata(*unit); return Ok(Work::new(move |state| { // 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_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() { @@ -340,9 +340,9 @@ fn rustc<'a, 'cfg>( current_id: PackageId, ) -> CargoResult<()> { 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!( - "couldn't find build script output for {}/{:?}", + "couldn't find build script output for {}/{}", key.0, key.1 )) })?; @@ -375,10 +375,13 @@ fn rustc<'a, 'cfg>( rustc: &mut ProcessBuilder, build_script_outputs: &BuildScriptOutputs, current_id: PackageId, - kind: CompileKind, + metadata: Option, ) -> CargoResult<()> { - let key = (current_id, kind); - if let Some(output) = build_script_outputs.get(&key) { + let metadata = match metadata { + 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() { rustc.env(name, value); } @@ -477,10 +480,10 @@ fn add_plugin_deps( let var = util::dylib_path_envvar(); let search_path = rustc.get_env(var).unwrap_or_default(); let mut search_path = env::split_paths(&search_path).collect::>(); - for &id in build_scripts.plugins.iter() { + for (pkg_id, metadata) in &build_scripts.plugins { let output = build_script_outputs - .get(&(id, CompileKind::Host)) - .ok_or_else(|| internal(format!("couldn't find libs for plugin dep {}", id)))?; + .get(*pkg_id, *metadata) + .ok_or_else(|| internal(format!("couldn't find libs for plugin dep {}", pkg_id)))?; search_path.append(&mut filter_dynamic_search_path( output.library_paths.iter(), 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 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 target = unit.target.clone(); 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| { - if let Some(output) = build_script_outputs.lock().unwrap().get(&key) { - for cfg in output.cfgs.iter() { - rustdoc.arg("--cfg").arg(cfg); - } - for &(ref name, ref value) in output.env.iter() { - rustdoc.env(name, value); + if let Some(script_metadata) = script_metadata { + if let Some(output) = build_script_outputs + .lock() + .unwrap() + .get(pkg_id, script_metadata) + { + 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); diff --git a/src/cargo/core/compiler/output_depinfo.rs b/src/cargo/core/compiler/output_depinfo.rs index b6e3d4606f0..6f50c1d2483 100644 --- a/src/cargo/core/compiler/output_depinfo.rs +++ b/src/cargo/core/compiler/output_depinfo.rs @@ -50,7 +50,7 @@ fn render_filename>(path: P, basedir: Option<&str>) -> CargoResul fn add_deps_for_unit<'a, 'b>( deps: &mut BTreeSet, - context: &mut Context<'a, 'b>, + cx: &mut Context<'a, 'b>, unit: &Unit<'a>, visited: &mut HashSet>, ) -> CargoResult<()> { @@ -62,12 +62,10 @@ fn add_deps_for_unit<'a, 'b>( // generate a dep info file, so we just keep on going below if !unit.mode.is_run_custom_build() { // Add dependencies from rustc dep-info output (stored in fingerprint directory) - let dep_info_loc = fingerprint::dep_info_loc(context, unit); - if let Some(paths) = fingerprint::parse_dep_info( - unit.pkg.root(), - context.files().host_root(), - &dep_info_loc, - )? { + let dep_info_loc = fingerprint::dep_info_loc(cx, unit); + if let Some(paths) = + fingerprint::parse_dep_info(unit.pkg.root(), cx.files().host_root(), &dep_info_loc)? + { for path in paths { deps.insert(path); } @@ -82,19 +80,25 @@ fn add_deps_for_unit<'a, 'b>( } // Add rerun-if-changed dependencies - let key = (unit.pkg.package_id(), unit.kind); - if let Some(output) = context.build_script_outputs.lock().unwrap().get(&key) { - for path in &output.rerun_if_changed { - deps.insert(path.into()); + if let Some(metadata) = cx.find_build_script_metadata(*unit) { + if let Some(output) = cx + .build_script_outputs + .lock() + .unwrap() + .get(unit.pkg.package_id(), metadata) + { + for path in &output.rerun_if_changed { + deps.insert(path.into()); + } } } // 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 { let source_id = dep.unit.pkg.package_id().source_id(); if source_id.is_path() { - add_deps_for_unit(deps, context, &dep.unit, visited)?; + add_deps_for_unit(deps, cx, &dep.unit, visited)?; } } Ok(()) diff --git a/tests/testsuite/profile_overrides.rs b/tests/testsuite/profile_overrides.rs index 59766187ae5..b2bc1764115 100644 --- a/tests/testsuite/profile_overrides.rs +++ b/tests/testsuite/profile_overrides.rs @@ -423,3 +423,93 @@ fn no_warning_ws() { ) .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(); +}