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::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,

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
/// 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)
}

View File

@ -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<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 {
self.primary_packages.contains(&unit.pkg.package_id())
}

View File

@ -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<PackageId>,
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<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
// 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<Fingerprint> {
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)

View File

@ -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)?;

View File

@ -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<Metadata>,
) -> 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::<Vec<_>>();
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);

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>(
deps: &mut BTreeSet<PathBuf>,
context: &mut Context<'a, 'b>,
cx: &mut Context<'a, 'b>,
unit: &Unit<'a>,
visited: &mut HashSet<Unit<'a>>,
) -> 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(())

View File

@ -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();
}