diff --git a/src/cargo/core/compiler/build_context/mod.rs b/src/cargo/core/compiler/build_context/mod.rs index 303bf6a873c..53441f0a0b0 100644 --- a/src/cargo/core/compiler/build_context/mod.rs +++ b/src/cargo/core/compiler/build_context/mod.rs @@ -37,7 +37,7 @@ pub struct BuildContext<'a, 'cfg> { pub packages: PackageSet<'cfg>, /// Information about rustc and the target platform. - pub target_data: RustcTargetData, + pub target_data: RustcTargetData<'cfg>, /// The root units of `unit_graph` (units requested on the command-line). pub roots: Vec, @@ -56,7 +56,7 @@ impl<'a, 'cfg> BuildContext<'a, 'cfg> { build_config: &'a BuildConfig, profiles: Profiles, extra_compiler_args: HashMap>, - target_data: RustcTargetData, + target_data: RustcTargetData<'cfg>, roots: Vec, unit_graph: UnitGraph, ) -> CargoResult> { @@ -86,12 +86,13 @@ impl<'a, 'cfg> BuildContext<'a, 'cfg> { } /// Gets the user-specified linker for a particular host or target. - pub fn linker(&self, kind: CompileKind) -> Option { - self.target_data - .target_config(kind) + pub fn linker(&self, kind: CompileKind) -> CargoResult> { + Ok(self + .target_data + .target_config(kind)? .linker .as_ref() - .map(|l| l.val.clone().resolve_program(self.config)) + .map(|l| l.val.clone().resolve_program(self.config))) } /// Gets the host architecture triple. @@ -109,12 +110,12 @@ impl<'a, 'cfg> BuildContext<'a, 'cfg> { self.build_config.jobs } - pub fn rustflags_args(&self, unit: &Unit) -> &[String] { - &self.target_data.info(unit.kind).rustflags + pub fn rustflags_args(&self, unit: &Unit) -> CargoResult<&[String]> { + Ok(&self.target_data.info(unit.kind)?.rustflags) } - pub fn rustdocflags_args(&self, unit: &Unit) -> &[String] { - &self.target_data.info(unit.kind).rustdocflags + pub fn rustdocflags_args(&self, unit: &Unit) -> CargoResult<&[String]> { + Ok(&self.target_data.info(unit.kind)?.rustdocflags) } pub fn extra_args_for(&self, unit: &Unit) -> Option<&Vec> { diff --git a/src/cargo/core/compiler/build_context/target_info.rs b/src/cargo/core/compiler/build_context/target_info.rs index 53fe197707c..36cec05f5b4 100644 --- a/src/cargo/core/compiler/build_context/target_info.rs +++ b/src/cargo/core/compiler/build_context/target_info.rs @@ -7,6 +7,7 @@ use std::cell::RefCell; use std::collections::hash_map::{Entry, HashMap}; use std::env; use std::path::PathBuf; +use std::rc::Rc; use std::str::{self, FromStr}; /// Information about the platform target gleaned from querying rustc. @@ -646,60 +647,63 @@ fn env_args( } /// Collection of information about `rustc` and the host and target. -pub struct RustcTargetData { +pub struct RustcTargetData<'cfg> { + /// Configuration from the Workspace this was built with. + config: &'cfg Config, + /// Information about `rustc` itself. pub rustc: Rustc, + /// Build information for the "host", which is information about when /// `rustc` is invoked without a `--target` flag. This is used for /// procedural macros, build scripts, etc. - host_config: TargetConfig, - host_info: TargetInfo, + host_config: Rc, + host_info: Rc, + + /// Build information for targets that we're building for. This is + /// a cache that will be filled on-demand. + target_config: RefCell>>, + target_info: RefCell>>, - /// Build information for targets that we're building for. This will be - /// empty if the `--target` flag is not passed. - target_config: HashMap, - target_info: HashMap, + // TODO: can we get rid of this? in other words, does + // TargetInfo::new really need the list of requested kinds? + requested_kinds: Vec, } -impl RustcTargetData { +impl<'cfg> RustcTargetData<'cfg> { pub fn new( - ws: &Workspace<'_>, + ws: &Workspace<'cfg>, requested_kinds: &[CompileKind], - ) -> CargoResult { + ) -> CargoResult> { let config = ws.config(); let rustc = config.load_global_rustc(Some(ws))?; - let host_config = config.target_cfg_triple(&rustc.host)?; - let host_info = TargetInfo::new(config, requested_kinds, &rustc, CompileKind::Host)?; - let mut target_config = HashMap::new(); - let mut target_info = HashMap::new(); - for kind in requested_kinds { - if let CompileKind::Target(target) = *kind { - let tcfg = config.target_cfg_triple(target.short_name())?; - target_config.insert(target, tcfg); - target_info.insert( - target, - TargetInfo::new(config, requested_kinds, &rustc, *kind)?, - ); - } - } + let host_config = Rc::new(config.target_cfg_triple(&rustc.host)?); + let host_info = Rc::new(TargetInfo::new( + config, + requested_kinds, + &rustc, + CompileKind::Host, + )?); // This is a hack. The unit_dependency graph builder "pretends" that // `CompileKind::Host` is `CompileKind::Target(host)` if the // `--target` flag is not specified. Since the unit_dependency code // needs access to the target config data, create a copy so that it // can be found. See `rebuild_unit_graph_shared` for why this is done. - if requested_kinds.iter().any(CompileKind::is_host) { - let ct = CompileTarget::new(&rustc.host)?; - target_info.insert(ct, host_info.clone()); - target_config.insert(ct, host_config.clone()); - } + let mut target_config = HashMap::new(); + let mut target_info = HashMap::new(); + let ct = CompileTarget::new(&rustc.host)?; + target_info.insert(ct, host_info.clone()); + target_config.insert(ct, host_config.clone()); Ok(RustcTargetData { + config, rustc, - target_config, - target_info, + target_config: RefCell::new(target_config), + target_info: RefCell::new(target_info), host_config, host_info, + requested_kinds: requested_kinds.to_owned(), }) } @@ -714,43 +718,62 @@ impl RustcTargetData { /// Whether a dependency should be compiled for the host or target platform, /// specified by `CompileKind`. - pub fn dep_platform_activated(&self, dep: &Dependency, kind: CompileKind) -> bool { + pub fn dep_platform_activated(&self, dep: &Dependency, kind: CompileKind) -> CargoResult { // If this dependency is only available for certain platforms, // make sure we're only enabling it for that platform. let platform = match dep.platform() { Some(p) => p, - None => return true, + None => return Ok(true), }; let name = self.short_name(&kind); - platform.matches(name, self.cfg(kind)) + Ok(platform.matches(name, self.cfg(kind)?)) } /// Gets the list of `cfg`s printed out from the compiler for the specified kind. - pub fn cfg(&self, kind: CompileKind) -> &[Cfg] { - self.info(kind).cfg() + pub fn cfg(&self, kind: CompileKind) -> CargoResult<&[Cfg]> { + Ok(self.info(kind)?.cfg()) } /// Information about the given target platform, learned by querying rustc. - pub fn info(&self, kind: CompileKind) -> &TargetInfo { - match kind { - CompileKind::Host => &self.host_info, - CompileKind::Target(s) => &self.target_info[&s], - } + pub fn info(&self, kind: CompileKind) -> CargoResult> { + Ok(match kind { + CompileKind::Host => self.host_info.clone(), + CompileKind::Target(s) => match self.target_info.borrow_mut().entry(s) { + std::collections::hash_map::Entry::Occupied(o) => o.get().clone(), + std::collections::hash_map::Entry::Vacant(v) => v + .insert(Rc::new(TargetInfo::new( + self.config, + &self.requested_kinds, + &self.rustc, + kind, + )?)) + .clone(), + }, + }) } /// Gets the target configuration for a particular host or target. - pub fn target_config(&self, kind: CompileKind) -> &TargetConfig { - match kind { - CompileKind::Host => &self.host_config, - CompileKind::Target(s) => &self.target_config[&s], - } + pub fn target_config(&self, kind: CompileKind) -> CargoResult> { + Ok(match kind { + CompileKind::Host => self.host_config.clone(), + CompileKind::Target(s) => match self.target_config.borrow_mut().entry(s) { + std::collections::hash_map::Entry::Occupied(o) => o.get().clone(), + std::collections::hash_map::Entry::Vacant(v) => v + .insert(Rc::new(self.config.target_cfg_triple(s.short_name())?)) + .clone(), + }, + }) } /// If a build script is overridden, this returns the `BuildOutput` to use. /// /// `lib_name` is the `links` library name and `kind` is whether it is for /// Host or Target. - pub fn script_override(&self, lib_name: &str, kind: CompileKind) -> Option<&BuildOutput> { - self.target_config(kind).links_overrides.get(lib_name) + pub fn script_override( + &self, + lib_name: &str, + kind: CompileKind, + ) -> CargoResult> { + Ok(self.target_config(kind)?.links_overrides.get(lib_name)) } } diff --git a/src/cargo/core/compiler/compilation.rs b/src/cargo/core/compiler/compilation.rs index 461196f6076..fa015c2b970 100644 --- a/src/cargo/core/compiler/compilation.rs +++ b/src/cargo/core/compiler/compilation.rs @@ -109,19 +109,19 @@ impl<'cfg> Compilation<'cfg> { deps_output: HashMap::new(), sysroot_host_libdir: bcx .target_data - .info(CompileKind::Host) + .info(CompileKind::Host)? .sysroot_host_libdir .clone(), sysroot_target_libdir: bcx .all_kinds .iter() .map(|kind| { - ( + Ok(( *kind, - bcx.target_data.info(*kind).sysroot_target_libdir.clone(), - ) + bcx.target_data.info(*kind)?.sysroot_target_libdir.clone(), + )) }) - .collect(), + .collect::>()?, tests: Vec::new(), binaries: Vec::new(), cdylibs: Vec::new(), @@ -351,7 +351,7 @@ fn target_runner( } // try target.'cfg(...)'.runner - let target_cfg = bcx.target_data.info(kind).cfg(); + let target_cfg = bcx.target_data.info(kind)?.cfg(); let mut cfgs = bcx .config .target_cfgs()? diff --git a/src/cargo/core/compiler/context/compilation_files.rs b/src/cargo/core/compiler/context/compilation_files.rs index 37b11da8471..502dd5c36ba 100644 --- a/src/cargo/core/compiler/context/compilation_files.rs +++ b/src/cargo/core/compiler/context/compilation_files.rs @@ -296,7 +296,7 @@ impl<'a, 'cfg: 'a> CompilationFiles<'a, 'cfg> { ) -> CargoResult { assert!(target.is_bin()); let dest = self.layout(kind).dest(); - let info = bcx.target_data.info(kind); + let info = bcx.target_data.info(kind)?; let (file_types, _) = info .rustc_outputs( CompileMode::Build, @@ -422,7 +422,7 @@ impl<'a, 'cfg: 'a> CompilationFiles<'a, 'cfg> { ) -> CargoResult> { let out_dir = self.out_dir(unit); - let info = bcx.target_data.info(unit.kind); + let info = bcx.target_data.info(unit.kind)?; let triple = bcx.target_data.short_name(&unit.kind); let (file_types, unsupported) = info.rustc_outputs(unit.mode, unit.target.kind(), triple)?; diff --git a/src/cargo/core/compiler/context/mod.rs b/src/cargo/core/compiler/context/mod.rs index 4e31fed0b12..d5596000f19 100644 --- a/src/cargo/core/compiler/context/mod.rs +++ b/src/cargo/core/compiler/context/mod.rs @@ -216,7 +216,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> { unit: unit.clone(), args, unstable_opts, - linker: self.bcx.linker(unit.kind), + linker: self.bcx.linker(unit.kind)?, }); } @@ -235,7 +235,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> { } // Collect rustdocflags. - let rustdocflags = self.bcx.rustdocflags_args(unit); + let rustdocflags = self.bcx.rustdocflags_args(unit)?; if !rustdocflags.is_empty() { self.compilation .rustdocflags diff --git a/src/cargo/core/compiler/custom_build.rs b/src/cargo/core/compiler/custom_build.rs index cf38449fcf1..f9a15780b42 100644 --- a/src/cargo/core/compiler/custom_build.rs +++ b/src/cargo/core/compiler/custom_build.rs @@ -198,7 +198,7 @@ fn build_work(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult { .env("RUSTDOC", &*bcx.config.rustdoc()?) .inherit_jobserver(&cx.jobserver); - if let Some(linker) = &bcx.target_data.target_config(unit.kind).linker { + if let Some(linker) = &bcx.target_data.target_config(unit.kind)?.linker { cmd.env( "RUSTC_LINKER", linker.val.clone().resolve_program(bcx.config), @@ -216,7 +216,7 @@ fn build_work(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult { } let mut cfg_map = HashMap::new(); - for cfg in bcx.target_data.cfg(unit.kind) { + for cfg in bcx.target_data.cfg(unit.kind)? { match *cfg { Cfg::Name(ref n) => { cfg_map.insert(n.clone(), None); @@ -727,7 +727,7 @@ pub fn build_map(cx: &mut Context<'_, '_>) -> CargoResult<()> { // If there is a build script override, pre-fill the build output. if unit.mode.is_run_custom_build() { if let Some(links) = unit.pkg.manifest().links() { - if let Some(output) = cx.bcx.target_data.script_override(links, unit.kind) { + if let Some(output) = cx.bcx.target_data.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(), diff --git a/src/cargo/core/compiler/fingerprint.rs b/src/cargo/core/compiler/fingerprint.rs index 5077c33645f..4859ef08c49 100644 --- a/src/cargo/core/compiler/fingerprint.rs +++ b/src/cargo/core/compiler/fingerprint.rs @@ -1296,9 +1296,9 @@ fn calculate_normal(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult ( Box< dyn FnOnce( - &BuildDeps, - Option<&dyn Fn() -> CargoResult>, - ) -> CargoResult>> + &BuildDeps, + Option<&dyn Fn() -> CargoResult>, + ) -> CargoResult>> + Send, >, bool, diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs index 7ac3842f4df..f18b45a5be3 100644 --- a/src/cargo/core/compiler/mod.rs +++ b/src/cargo/core/compiler/mod.rs @@ -232,7 +232,7 @@ fn rustc(cx: &mut Context<'_, '_>, unit: &Unit, exec: &Arc) -> Car let rustc_dep_info_loc = root.join(dep_info_name); let dep_info_loc = fingerprint::dep_info_loc(cx, unit); - rustc.args(cx.bcx.rustflags_args(unit)); + rustc.args(cx.bcx.rustflags_args(unit)?); if cx.bcx.config.cli_unstable().binary_dep_depinfo { rustc.arg("-Z").arg("binary-dep-depinfo"); } @@ -612,7 +612,7 @@ fn rustdoc(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult { build_deps_args(&mut rustdoc, cx, unit)?; rustdoc::add_root_urls(cx, unit, &mut rustdoc)?; - rustdoc.args(bcx.rustdocflags_args(unit)); + rustdoc.args(bcx.rustdocflags_args(unit)?); if !crate_version_flag_already_present(&rustdoc) { append_crate_version_flag(unit, &mut rustdoc); @@ -921,7 +921,7 @@ fn build_base_args( cmd, "-C", "linker=", - bcx.linker(unit.kind).as_ref().map(|s| s.as_ref()), + bcx.linker(unit.kind)?.as_ref().map(|s| s.as_ref()), ); if incremental { let dir = cx.files().layout(unit.kind).incremental().as_os_str(); diff --git a/src/cargo/core/compiler/rustdoc.rs b/src/cargo/core/compiler/rustdoc.rs index a6f940785b2..0ca5d6cae00 100644 --- a/src/cargo/core/compiler/rustdoc.rs +++ b/src/cargo/core/compiler/rustdoc.rs @@ -157,7 +157,7 @@ pub fn add_root_urls( let std_url = match &map.std { None | Some(RustdocExternMode::Remote) => None, Some(RustdocExternMode::Local) => { - let sysroot = &cx.bcx.target_data.info(CompileKind::Host).sysroot; + let sysroot = &cx.bcx.target_data.info(CompileKind::Host)?.sysroot; let html_root = sysroot.join("share").join("doc").join("rust").join("html"); if html_root.exists() { let url = Url::from_file_path(&html_root).map_err(|()| { diff --git a/src/cargo/core/compiler/standard_lib.rs b/src/cargo/core/compiler/standard_lib.rs index 0b14df8055a..acb731d9141 100644 --- a/src/cargo/core/compiler/standard_lib.rs +++ b/src/cargo/core/compiler/standard_lib.rs @@ -33,7 +33,7 @@ pub fn parse_unstable_flag(value: Option<&str>) -> Vec { /// Resolve the standard library dependencies. pub fn resolve_std<'cfg>( ws: &Workspace<'cfg>, - target_data: &RustcTargetData, + target_data: &RustcTargetData<'cfg>, requested_targets: &[CompileKind], crates: &[String], ) -> CargoResult<(PackageSet<'cfg>, Resolve, ResolvedFeatures)> { @@ -188,14 +188,14 @@ pub fn generate_std_roots( Ok(ret) } -fn detect_sysroot_src_path(target_data: &RustcTargetData) -> CargoResult { +fn detect_sysroot_src_path<'cfg>(target_data: &RustcTargetData<'cfg>) -> CargoResult { if let Some(s) = env::var_os("__CARGO_TESTS_ONLY_SRC_ROOT") { return Ok(s.into()); } // NOTE: This is temporary until we figure out how to acquire the source. let src_path = target_data - .info(CompileKind::Host) + .info(CompileKind::Host)? .sysroot .join("lib") .join("rustlib") diff --git a/src/cargo/core/compiler/unit_dependencies.rs b/src/cargo/core/compiler/unit_dependencies.rs index ee184dace36..9f100556f75 100644 --- a/src/cargo/core/compiler/unit_dependencies.rs +++ b/src/cargo/core/compiler/unit_dependencies.rs @@ -44,7 +44,7 @@ struct State<'a, 'cfg> { /// library. is_std: bool, global_mode: CompileMode, - target_data: &'a RustcTargetData, + target_data: &'a RustcTargetData<'cfg>, profiles: &'a Profiles, interner: &'a UnitInterner, } @@ -58,7 +58,7 @@ pub fn build_unit_dependencies<'a, 'cfg>( roots: &[Unit], std_roots: &HashMap>, global_mode: CompileMode, - target_data: &'a RustcTargetData, + target_data: &'a RustcTargetData<'cfg>, profiles: &'a Profiles, interner: &'a UnitInterner, ) -> CargoResult { @@ -230,7 +230,7 @@ fn compute_deps( let id = unit.pkg.package_id(); let filtered_deps = state - .deps(unit, unit_for) + .deps(unit, unit_for)? .into_iter() .filter(|&(_id, deps)| { deps.iter().any(|dep| { @@ -364,7 +364,7 @@ fn compute_deps_custom_build( if let Some(links) = unit.pkg.manifest().links() { if state .target_data - .script_override(links, unit.kind) + .script_override(links, unit.kind)? .is_some() { // Overridden build scripts don't have any dependencies. @@ -400,7 +400,7 @@ fn compute_deps_custom_build( /// Returns the dependencies necessary to document a package. fn compute_deps_doc(unit: &Unit, state: &mut State<'_, '_>) -> CargoResult> { let deps = state - .deps(unit, UnitFor::new_normal()) + .deps(unit, UnitFor::new_normal())? .into_iter() .filter(|&(_id, deps)| deps.iter().any(|dep| dep.kind() == DepKind::Normal)); @@ -746,32 +746,40 @@ impl<'a, 'cfg> State<'a, 'cfg> { } /// Returns a filtered set of dependencies for the given unit. - fn deps(&self, unit: &Unit, unit_for: UnitFor) -> Vec<(PackageId, &HashSet)> { + fn deps( + &self, + unit: &Unit, + unit_for: UnitFor, + ) -> CargoResult)>> { let pkg_id = unit.pkg.package_id(); let kind = unit.kind; - self.resolve() - .deps(pkg_id) - .filter(|&(_id, deps)| { - assert!(!deps.is_empty()); - deps.iter().any(|dep| { - // If this dependency is only available for certain platforms, - // make sure we're only enabling it for that platform. - if !self.target_data.dep_platform_activated(dep, kind) { - return false; - } + let mut res = Vec::new(); + for (id, deps) in self.resolve().deps(pkg_id) { + assert!(!deps.is_empty()); + let mut accepted = false; + for dep in deps { + // If this dependency is only available for certain platforms, + // make sure we're only enabling it for that platform. + if !self.target_data.dep_platform_activated(dep, kind)? { + continue; + } - // If this is an optional dependency, and the new feature resolver - // did not enable it, don't include it. - if dep.is_optional() { - let features_for = unit_for.map_to_features_for(); - if !self.is_dep_activated(pkg_id, features_for, dep.name_in_toml()) { - return false; - } + // If this is an optional dependency, and the new feature resolver + // did not enable it, don't include it. + if dep.is_optional() { + let features_for = unit_for.map_to_features_for(); + if !self.is_dep_activated(pkg_id, features_for, dep.name_in_toml()) { + continue; } + } - true - }) - }) - .collect() + accepted = true; + break; + } + if accepted { + res.push((id, deps)); + } + } + Ok(res) } } diff --git a/src/cargo/core/package.rs b/src/cargo/core/package.rs index 88bc43c7311..8e5ad3e1b19 100644 --- a/src/cargo/core/package.rs +++ b/src/cargo/core/package.rs @@ -492,7 +492,7 @@ impl<'cfg> PackageSet<'cfg> { root_ids: &[PackageId], has_dev_units: HasDevUnits, requested_kinds: &[CompileKind], - target_data: &RustcTargetData, + target_data: &RustcTargetData<'_>, force_all_targets: ForceAllTargets, ) -> CargoResult<()> { fn collect_used_deps( @@ -501,43 +501,52 @@ impl<'cfg> PackageSet<'cfg> { pkg_id: PackageId, has_dev_units: HasDevUnits, requested_kinds: &[CompileKind], - target_data: &RustcTargetData, + target_data: &RustcTargetData<'_>, force_all_targets: ForceAllTargets, ) -> CargoResult<()> { if !used.insert(pkg_id) { return Ok(()); } - let filtered_deps = resolve.deps(pkg_id).filter(|&(_id, deps)| { - deps.iter().any(|dep| { + for (dep_id, deps) in resolve.deps(pkg_id) { + let mut accepted = false; + + for dep in deps { if dep.kind() == DepKind::Development && has_dev_units == HasDevUnits::No { - return false; + continue; } + // This is overly broad, since not all target-specific // dependencies are used both for target and host. To tighten this // up, this function would need to track "for_host" similar to how // unit dependencies handles it. if force_all_targets == ForceAllTargets::No { - let activated = requested_kinds - .iter() - .chain(Some(&CompileKind::Host)) - .any(|kind| target_data.dep_platform_activated(dep, *kind)); + let mut activated = false; + for k in requested_kinds.iter().chain(Some(&CompileKind::Host)) { + if target_data.dep_platform_activated(dep, *k)? { + activated = true; + break; + } + } if !activated { - return false; + continue; } } - true - }) - }); - for (dep_id, _deps) in filtered_deps { - collect_used_deps( - used, - resolve, - dep_id, - has_dev_units, - requested_kinds, - target_data, - force_all_targets, - )?; + + accepted = true; + break; + } + + if accepted { + collect_used_deps( + used, + resolve, + dep_id, + has_dev_units, + requested_kinds, + target_data, + force_all_targets, + )?; + } } Ok(()) } diff --git a/src/cargo/core/resolver/features.rs b/src/cargo/core/resolver/features.rs index 8b0b73e762e..f83a12ab436 100644 --- a/src/cargo/core/resolver/features.rs +++ b/src/cargo/core/resolver/features.rs @@ -293,7 +293,7 @@ impl ResolvedFeatures { pub struct FeatureResolver<'a, 'cfg> { ws: &'a Workspace<'cfg>, - target_data: &'a RustcTargetData, + target_data: &'a RustcTargetData<'cfg>, /// The platforms to build for, requested by the user. requested_targets: &'a [CompileKind], resolve: &'a Resolve, @@ -331,7 +331,7 @@ impl<'a, 'cfg> FeatureResolver<'a, 'cfg> { /// with the result. pub fn resolve( ws: &Workspace<'cfg>, - target_data: &RustcTargetData, + target_data: &RustcTargetData<'cfg>, resolve: &Resolve, package_set: &'a PackageSet<'cfg>, requested_features: &RequestedFeatures, @@ -439,7 +439,7 @@ impl<'a, 'cfg> FeatureResolver<'a, 'cfg> { // features that enable other features. return Ok(()); } - for (dep_pkg_id, deps) in self.deps(pkg_id, for_host) { + for (dep_pkg_id, deps) in self.deps(pkg_id, for_host)? { for (dep, dep_for_host) in deps { if dep.is_optional() { // Optional dependencies are enabled in `activate_fv` when @@ -550,7 +550,7 @@ impl<'a, 'cfg> FeatureResolver<'a, 'cfg> { .deferred_weak_dependencies .remove(&(pkg_id, for_host, dep_name)); // Activate the optional dep. - for (dep_pkg_id, deps) in self.deps(pkg_id, for_host) { + for (dep_pkg_id, deps) in self.deps(pkg_id, for_host)? { for (dep, dep_for_host) in deps { if dep.name_in_toml() != dep_name { continue; @@ -588,7 +588,7 @@ impl<'a, 'cfg> FeatureResolver<'a, 'cfg> { dep_prefix: bool, weak: bool, ) -> CargoResult<()> { - for (dep_pkg_id, deps) in self.deps(pkg_id, for_host) { + for (dep_pkg_id, deps) in self.deps(pkg_id, for_host)? { for (dep, dep_for_host) in deps { if dep.name_in_toml() != dep_name { continue; @@ -685,9 +685,9 @@ impl<'a, 'cfg> FeatureResolver<'a, 'cfg> { &self, pkg_id: PackageId, for_host: bool, - ) -> Vec<(PackageId, Vec<(&'a Dependency, bool)>)> { + ) -> CargoResult)>> { // Helper for determining if a platform is activated. - let platform_activated = |dep: &Dependency| -> bool { + let platform_activated = |dep: &Dependency| -> CargoResult { // We always care about build-dependencies, and they are always // Host. If we are computing dependencies "for a build script", // even normal dependencies are host-only. @@ -697,37 +697,36 @@ impl<'a, 'cfg> FeatureResolver<'a, 'cfg> { .dep_platform_activated(dep, CompileKind::Host); } // Not a build dependency, and not for a build script, so must be Target. - self.requested_targets - .iter() - .any(|kind| self.target_data.dep_platform_activated(dep, *kind)) + for kind in self.requested_targets { + if self.target_data.dep_platform_activated(dep, *kind)? { + return Ok(true); + } + } + Ok(false) }; - self.resolve - .deps(pkg_id) - .map(|(dep_id, deps)| { - let deps = deps - .iter() - .filter(|dep| { - if dep.platform().is_some() - && self.opts.ignore_inactive_targets - && !platform_activated(dep) - { - return false; - } - if self.opts.decouple_dev_deps && dep.kind() == DepKind::Development { - return false; - } - true - }) - .map(|dep| { - let dep_for_host = self.track_for_host - && (for_host || dep.is_build() || self.is_proc_macro(dep_id)); - (dep, dep_for_host) - }) - .collect::>(); - (dep_id, deps) - }) - .filter(|(_id, deps)| !deps.is_empty()) - .collect() + + let res = Vec::new(); + for (dep_id, deps) in self.resolve.deps(pkg_id) { + let new_deps = Vec::new(); + for dep in deps.iter() { + if dep.platform().is_some() + && self.opts.ignore_inactive_targets + && !platform_activated(dep)? + { + continue; + } + if self.opts.decouple_dev_deps && dep.kind() == DepKind::Development { + continue; + } + let dep_for_host = self.track_for_host + && (for_host || dep.is_build() || self.is_proc_macro(dep_id)); + new_deps.push((dep, dep_for_host)); + } + if !new_deps.is_empty() { + res.push((dep_id, new_deps)) + } + } + Ok(res) } /// Compare the activated features to the resolver. Used for testing. diff --git a/src/cargo/ops/cargo_clean.rs b/src/cargo/ops/cargo_clean.rs index 68f82b3c146..e550b2e16a1 100644 --- a/src/cargo/ops/cargo_clean.rs +++ b/src/cargo/ops/cargo_clean.rs @@ -151,7 +151,7 @@ pub fn clean(ws: &Workspace<'_>, opts: &CleanOptions<'_>) -> CargoResult<()> { let triple = target_data.short_name(compile_kind); let (file_types, _unsupported) = target_data - .info(*compile_kind) + .info(*compile_kind)? .rustc_outputs(mode, target.kind(), triple)?; let (dir, uplift_dir) = match target.kind() { TargetKind::ExampleBin | TargetKind::ExampleLib(..) => { diff --git a/src/cargo/ops/cargo_fetch.rs b/src/cargo/ops/cargo_fetch.rs index 1e0d855d0d1..43c1ed42d6d 100644 --- a/src/cargo/ops/cargo_fetch.rs +++ b/src/cargo/ops/cargo_fetch.rs @@ -33,29 +33,33 @@ pub fn fetch<'a>( } to_download.push(id); - let deps = resolve - .deps(id) - .filter(|&(_id, deps)| { - deps.iter().any(|d| { - // If no target was specified then all dependencies are - // fetched. - if options.targets.is_empty() { - return true; - } - // Otherwise we only download this dependency if any of the - // requested platforms would match this dependency. Note - // that this is a bit lossy because not all dependencies are - // always compiled for all platforms, but it should be - // "close enough" for now. - build_config - .requested_kinds - .iter() - .any(|kind| data.dep_platform_activated(d, *kind)) - }) - }) - .map(|(id, _deps)| id); - deps_to_fetch.extend(deps); + for (id, deps) in resolve.deps(id) { + let mut accepted = false; + 'accepting_loop: for d in deps { + // If no target was specified then all dependencies are + // fetched. + if options.targets.is_empty() { + accepted = true; + break; + } + + // Otherwise we only download this dependency if any of the + // requested platforms would match this dependency. Note + // that this is a bit lossy because not all dependencies are + // always compiled for all platforms, but it should be + // "close enough" for now. + for kind in build_config.requested_kinds { + if data.dep_platform_activated(d, kind)? { + accepted = true; + break 'accepting_loop; + } + } + } + if accepted { + deps_to_fetch.push(id); + } + } } packages.get_many(to_download)?; diff --git a/src/cargo/ops/cargo_output_metadata.rs b/src/cargo/ops/cargo_output_metadata.rs index d4d911f17cd..bf29a899311 100644 --- a/src/cargo/ops/cargo_output_metadata.rs +++ b/src/cargo/ops/cargo_output_metadata.rs @@ -174,16 +174,16 @@ fn build_resolve_graph( Ok((actual_packages, mr)) } -fn build_resolve_graph_r( +fn build_resolve_graph_r<'cfg>( node_map: &mut BTreeMap, pkg_id: PackageId, resolve: &Resolve, package_map: &BTreeMap, - target_data: &RustcTargetData, + target_data: &RustcTargetData<'cfg>, requested_kinds: &[CompileKind], -) { +) -> CargoResult<()> { if node_map.contains_key(&pkg_id) { - return; + return Ok(()); } // This normalizes the IDs so that they are consistent between the // `packages` array and the `resolve` map. This is a bit of a hack to @@ -202,32 +202,36 @@ fn build_resolve_graph_r( let normalize_id = |id| -> PackageId { *package_map.get_key_value(&id).unwrap().0 }; let features = resolve.features(pkg_id).to_vec(); - let deps: Vec = resolve - .deps(pkg_id) - .filter(|(_dep_id, deps)| { - if requested_kinds == [CompileKind::Host] { - true - } else { - requested_kinds.iter().any(|kind| { - deps.iter() - .any(|dep| target_data.dep_platform_activated(dep, *kind)) - }) + let deps = Vec::new(); + for (dep_id, dep_deps) in resolve.deps(pkg_id) { + if requested_kinds != [CompileKind::Host] { + let mut skip = true; + for kind in requested_kinds { + for dep in dep_deps { + if target_data.dep_platform_activated(dep, *kind)? { + skip = false; + } + } } - }) - .filter_map(|(dep_id, deps)| { - let mut dep_kinds: Vec<_> = deps.iter().map(DepKindInfo::from).collect(); - dep_kinds.sort(); - package_map - .get(&dep_id) - .and_then(|pkg| pkg.targets().iter().find(|t| t.is_lib())) - .and_then(|lib_target| resolve.extern_crate_name(pkg_id, dep_id, lib_target).ok()) - .map(|name| Dep { - name, - pkg: normalize_id(dep_id), - dep_kinds, - }) - }) - .collect(); + if skip { + continue; + } + } + let mut dep_kinds: Vec<_> = dep_deps.iter().map(DepKindInfo::from).collect(); + dep_kinds.sort(); + if let Some(d) = package_map + .get(&dep_id) + .and_then(|pkg| pkg.targets().iter().find(|t| t.is_lib())) + .and_then(|lib_target| resolve.extern_crate_name(pkg_id, dep_id, lib_target).ok()) + .map(|name| Dep { + name, + pkg: normalize_id(dep_id), + dep_kinds, + }) + { + deps.push(d); + } + } let dumb_deps: Vec = deps.iter().map(|dep| normalize_id(dep.pkg)).collect(); let to_visit = dumb_deps.clone(); let node = MetadataResolveNode { @@ -247,4 +251,5 @@ fn build_resolve_graph_r( requested_kinds, ); } + Ok(()) } diff --git a/src/cargo/ops/resolve.rs b/src/cargo/ops/resolve.rs index c54a2bc42df..d02a46db9df 100644 --- a/src/cargo/ops/resolve.rs +++ b/src/cargo/ops/resolve.rs @@ -74,7 +74,7 @@ pub fn resolve_ws<'a>(ws: &Workspace<'a>) -> CargoResult<(PackageSet<'a>, Resolv /// members. In this case, `opts.all_features` must be `true`. pub fn resolve_ws_with_opts<'cfg>( ws: &Workspace<'cfg>, - target_data: &RustcTargetData, + target_data: &RustcTargetData<'cfg>, requested_targets: &[CompileKind], opts: &ResolveOpts, specs: &[PackageIdSpec], diff --git a/src/cargo/ops/tree/graph.rs b/src/cargo/ops/tree/graph.rs index 3f04a554d9e..429967f554a 100644 --- a/src/cargo/ops/tree/graph.rs +++ b/src/cargo/ops/tree/graph.rs @@ -243,13 +243,13 @@ impl<'a> Graph<'a> { } /// Builds the graph. -pub fn build<'a>( - ws: &Workspace<'_>, +pub fn build<'a, 'cfg>( + ws: &Workspace<'cfg>, resolve: &Resolve, resolved_features: &ResolvedFeatures, specs: &[PackageIdSpec], requested_features: &RequestedFeatures, - target_data: &RustcTargetData, + target_data: &RustcTargetData<'cfg>, requested_kinds: &[CompileKind], package_map: HashMap, opts: &TreeOptions, @@ -270,7 +270,7 @@ pub fn build<'a>( target_data, *kind, opts, - ); + )?; if opts.graph_features { let fmap = resolve.summary(member_id).features(); add_cli_features(&mut graph, member_index, &requested_features, fmap); @@ -294,10 +294,10 @@ fn add_pkg( resolved_features: &ResolvedFeatures, package_id: PackageId, features_for: FeaturesFor, - target_data: &RustcTargetData, + target_data: &RustcTargetData<'_>, requested_kind: CompileKind, opts: &TreeOptions, -) -> usize { +) -> CargoResult { let node_features = resolved_features.activated_features(package_id, features_for); let node_kind = match features_for { FeaturesFor::HostDep => CompileKind::Host, @@ -309,7 +309,7 @@ fn add_pkg( kind: node_kind, }; if let Some(idx) = graph.index.get(&node) { - return *idx; + return Ok(*idx); } let from_index = graph.add_node(node); // Compute the dep name map which is later used for foo/bar feature lookups. @@ -317,40 +317,35 @@ fn add_pkg( let mut deps: Vec<_> = resolve.deps(package_id).collect(); deps.sort_unstable_by_key(|(dep_id, _)| *dep_id); let show_all_targets = opts.target == super::Target::All; - for (dep_id, deps) in deps { - let mut deps: Vec<_> = deps - .iter() + for (dep_id, dep_deps) in deps { + let mut deps = Vec::new(); + for dep in dep_deps { // This filter is *similar* to the one found in `unit_dependencies::compute_deps`. // Try to keep them in sync! - .filter(|dep| { - let kind = match (node_kind, dep.kind()) { - (CompileKind::Host, _) => CompileKind::Host, - (_, DepKind::Build) => CompileKind::Host, - (_, DepKind::Normal) => node_kind, - (_, DepKind::Development) => node_kind, - }; - // Filter out inactivated targets. - if !show_all_targets && !target_data.dep_platform_activated(dep, kind) { - return false; - } - // Filter out dev-dependencies if requested. - if !opts.edge_kinds.contains(&EdgeKind::Dep(dep.kind())) { - return false; - } - if dep.is_optional() { - // If the new feature resolver does not enable this - // optional dep, then don't use it. - if !resolved_features.is_dep_activated( - package_id, - features_for, - dep.name_in_toml(), - ) { - return false; - } + let kind = match (node_kind, dep.kind()) { + (CompileKind::Host, _) => CompileKind::Host, + (_, DepKind::Build) => CompileKind::Host, + (_, DepKind::Normal) => node_kind, + (_, DepKind::Development) => node_kind, + }; + // Filter out inactivated targets. + if !show_all_targets && !target_data.dep_platform_activated(dep, kind)? { + continue; + } + // Filter out dev-dependencies if requested. + if !opts.edge_kinds.contains(&EdgeKind::Dep(dep.kind())) { + continue; + } + if dep.is_optional() { + // If the new feature resolver does not enable this + // optional dep, then don't use it. + if !resolved_features.is_dep_activated(package_id, features_for, dep.name_in_toml()) + { + continue; } - true - }) - .collect(); + } + deps.push(dep); + } // This dependency is eliminated from the dependency tree under // the current target and feature set. @@ -376,7 +371,7 @@ fn add_pkg( target_data, requested_kind, opts, - ); + )?; if opts.graph_features { // Add the dependency node with feature nodes in-between. dep_name_map @@ -417,7 +412,7 @@ fn add_pkg( .is_none()); } - from_index + Ok(from_index) } /// Adds a feature node between two nodes.