mirror of
https://github.com/rust-lang/cargo.git
synced 2025-09-28 11:20:36 +00:00
Fix BuildScriptOutput when a build script is run multiple times.
This commit is contained in:
parent
972b9f55a7
commit
2296af27ae
@ -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,
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)?;
|
||||
|
@ -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);
|
||||
|
@ -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(())
|
||||
|
@ -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();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user