use crate::core::dependency::DepKind;
use crate::core::package::Downloads;
use crate::core::profiles::{Profile, UnitFor};
+use crate::core::resolver::features::{FeaturesFor, ResolvedFeatures};
use crate::core::resolver::Resolve;
use crate::core::{InternedString, Package, PackageId, Target};
use crate::CargoResult;
unit_dependencies: UnitGraph<'a>,
package_cache: HashMap<PackageId, &'a Package>,
usr_resolve: &'a Resolve,
+ usr_features: &'a ResolvedFeatures,
std_resolve: Option<&'a Resolve>,
+ std_features: Option<&'a ResolvedFeatures>,
/// This flag is `true` while generating the dependencies for the standard
/// library.
is_std: bool,
pub fn build_unit_dependencies<'a, 'cfg>(
bcx: &'a BuildContext<'a, 'cfg>,
resolve: &'a Resolve,
- std_resolve: Option<&'a Resolve>,
+ features: &'a ResolvedFeatures,
+ std_resolve: Option<&'a (Resolve, ResolvedFeatures)>,
roots: &[Unit<'a>],
std_roots: &[Unit<'a>],
) -> CargoResult<UnitGraph<'a>> {
+ let (std_resolve, std_features) = match std_resolve {
+ Some((r, f)) => (Some(r), Some(f)),
+ None => (None, None),
+ };
let mut state = State {
bcx,
downloads: bcx.packages.enable_download()?,
unit_dependencies: HashMap::new(),
package_cache: HashMap::new(),
usr_resolve: resolve,
+ usr_features: features,
std_resolve,
+ std_features,
is_std: false,
};
} else if unit.target.is_custom_build() {
// This normally doesn't happen, except `clean` aggressively
// generates all units.
- UnitFor::new_build()
+ UnitFor::new_build(false)
} else if unit.target.for_host() {
// Proc macro / plugin should never have panic set.
UnitFor::new_compiler()
unit_for: UnitFor,
) -> CargoResult<Vec<UnitDep<'a>>> {
if unit.mode.is_run_custom_build() {
- return compute_deps_custom_build(unit, state);
+ return compute_deps_custom_build(unit, unit_for, state);
} else if unit.mode.is_doc() {
// Note: this does not include doc test.
return compute_deps_doc(unit, state);
let bcx = state.bcx;
let id = unit.pkg.package_id();
- let deps = state.resolve().deps(id).filter(|&(_id, deps)| {
+ let filtered_deps = state.resolve().deps(id).filter(|&(_id, deps)| {
assert!(!deps.is_empty());
deps.iter().any(|dep| {
// If this target is a build command, then we only want build
// If this dependency is only available for certain platforms,
// make sure we're only enabling it for that platform.
- if !bcx.dep_platform_activated(dep, unit.kind) {
+ if !bcx.target_data.dep_platform_activated(dep, unit.kind) {
return false;
}
});
let mut ret = Vec::new();
- for (id, _) in deps {
+ for (id, _) in filtered_deps {
let pkg = match state.get(id)? {
Some(pkg) => pkg,
None => continue,
None => continue,
};
let mode = check_or_build_mode(unit.mode, lib);
- let dep_unit_for = unit_for.with_for_host(lib.for_host());
+ let dep_unit_for = unit_for
+ .with_for_host(lib.for_host())
+ // If it is a custom build script, then it *only* has build dependencies.
+ .with_build_dep(unit.target.is_custom_build());
if bcx.config.cli_unstable().dual_proc_macros && lib.proc_macro() && !unit.kind.is_host() {
let unit_dep = new_unit_dep(state, unit, pkg, lib, dep_unit_for, unit.kind, mode)?;
if unit.target.is_custom_build() {
return Ok(ret);
}
- ret.extend(dep_build_script(unit, state)?);
+ ret.extend(dep_build_script(unit, unit_for, state)?);
// If this target is a binary, test, example, etc, then it depends on
// the library of the same package. The call to `resolve.deps` above
t.is_bin() &&
// Skip binaries with required features that have not been selected.
t.required_features().unwrap_or(&no_required_features).iter().all(|f| {
- unit.features.contains(&f.as_str())
+ unit.features.contains(&InternedString::new(f.as_str()))
})
})
.map(|t| {
/// the returned set of units must all be run before `unit` is run.
fn compute_deps_custom_build<'a, 'cfg>(
unit: &Unit<'a>,
+ unit_for: UnitFor,
state: &mut State<'a, 'cfg>,
) -> CargoResult<Vec<UnitDep<'a>>> {
if let Some(links) = unit.pkg.manifest().links() {
return Ok(Vec::new());
}
}
+ // All dependencies of this unit should use profiles for custom
+ // builds.
+ let script_unit_for = UnitFor::new_build(unit_for.is_for_build_dep());
// When not overridden, then the dependencies to run a build script are:
//
// 1. Compiling the build script itself.
unit,
unit.pkg,
unit.target,
- // All dependencies of this unit should use profiles for custom
- // builds.
- UnitFor::new_build(),
+ script_unit_for,
// Build scripts always compiled for the host.
CompileKind::Host,
CompileMode::Build,
.deps(unit.pkg.package_id())
.filter(|&(_id, deps)| {
deps.iter().any(|dep| match dep.kind() {
- DepKind::Normal => bcx.dep_platform_activated(dep, unit.kind),
+ DepKind::Normal => bcx.target_data.dep_platform_activated(dep, unit.kind),
_ => false,
})
});
}
// Be sure to build/run the build script for documented libraries.
- ret.extend(dep_build_script(unit, state)?);
+ ret.extend(dep_build_script(unit, UnitFor::new_normal(), state)?);
// If we document a binary/example, we need the library available.
if unit.target.is_bin() || unit.target.is_example() {
/// build script.
fn dep_build_script<'a>(
unit: &Unit<'a>,
+ unit_for: UnitFor,
state: &State<'a, '_>,
) -> CargoResult<Option<UnitDep<'a>>> {
unit.pkg
.bcx
.profiles
.get_profile_run_custom_build(&unit.profile);
+ // UnitFor::new_build is used because we want the `host` flag set
+ // for all of our build dependencies (so they all get
+ // build-override profiles), including compiling the build.rs
+ // script itself.
+ //
+ // If `is_for_build_dep` here is `false`, that means we are a
+ // build.rs script for a normal dependency and we want to set the
+ // CARGO_FEATURE_* environment variables to the features as a
+ // normal dep.
+ //
+ // If `is_for_build_dep` here is `true`, that means that this
+ // package is being used as a build dependency, and so we only
+ // want to set CARGO_FEATURE_* variables for the build-dependency
+ // side of the graph.
+ //
+ // Keep in mind that the RunCustomBuild unit and the Compile
+ // build.rs unit use the same features. This is because some
+ // people use `cfg!` and `#[cfg]` expressions to check for enabled
+ // features instead of just checking `CARGO_FEATURE_*` at runtime.
+ // In the case with `-Zfeatures=build_dep`, and a shared
+ // dependency has different features enabled for normal vs. build,
+ // then the build.rs script will get compiled twice. I believe it
+ // is not feasible to only build it once because it would break a
+ // large number of scripts (they would think they have the wrong
+ // set of features enabled).
+ let script_unit_for = UnitFor::new_build(unit_for.is_for_build_dep());
new_unit_dep_with_profile(
state,
unit,
unit.pkg,
t,
- UnitFor::new_build(),
+ script_unit_for,
unit.kind,
CompileMode::RunCustomBuild,
profile,
let public = state
.resolve()
.is_public_dep(parent.pkg.package_id(), pkg.package_id());
- let features = state.resolve().features_sorted(pkg.package_id());
+ let features_for = match unit_for.is_for_build_dep() {
+ true => FeaturesFor::BuildDep,
+ false => FeaturesFor::NormalOrDev,
+ };
+ let features = state.activated_features(pkg.package_id(), features_for);
let unit = state
.bcx
.units
}
}
+ fn activated_features(
+ &self,
+ pkg_id: PackageId,
+ features_for: FeaturesFor,
+ ) -> Vec<InternedString> {
+ let features = if self.is_std {
+ self.std_features.unwrap()
+ } else {
+ self.usr_features
+ };
+ features.activated_features(pkg_id, features_for)
+ }
+
fn get(&mut self, id: PackageId) -> CargoResult<Option<&'a Package>> {
if let Some(pkg) = self.package_cache.get(&id) {
return Ok(Some(pkg));