diff --git a/src/bin/cargo/commands/tree.rs b/src/bin/cargo/commands/tree.rs index 0a75178f589f..11f889d18186 100644 --- a/src/bin/cargo/commands/tree.rs +++ b/src/bin/cargo/commands/tree.rs @@ -212,7 +212,8 @@ subtree of the package given to -p.\n\ /// Parses `--edges` option. /// /// Returns a tuple of `EdgeKind` map and `no_proc_marco` flag. -fn parse_edge_kinds(config: &Config, args: &ArgMatches) -> CargoResult<(HashSet, bool)> { +//TODO: Move it out of tree or figure out defauilt value for vendor command +pub fn parse_edge_kinds(config: &Config, args: &ArgMatches) -> CargoResult<(HashSet, bool)> { let (kinds, no_proc_macro) = { let mut no_proc_macro = false; let mut kinds = args.get_many::("edges").map_or_else( diff --git a/src/bin/cargo/commands/vendor.rs b/src/bin/cargo/commands/vendor.rs index 1fd79ec51fa6..91da21aa73c0 100644 --- a/src/bin/cargo/commands/vendor.rs +++ b/src/bin/cargo/commands/vendor.rs @@ -1,6 +1,7 @@ use crate::command_prelude::*; use cargo::ops; use std::path::PathBuf; +use crate::commands::tree::parse_edge_kinds; pub fn cli() -> Command { subcommand("vendor") @@ -34,6 +35,18 @@ pub fn cli() -> Command { "versioned-dirs", "Always include version in subdir name", )) + // TODO add more flags supported by cargo-tree + .arg_features() + .arg( + multi_opt( + "edges", + "KINDS", + "The kinds of dependencies to display \ + (features, normal, build, dev, all, \ + no-normal, no-build, no-dev, no-proc-macro)", + ) + .short('e'), + ) .arg(flag("no-merge-sources", "Not supported").hide(true)) .arg(flag("relative-path", "Not supported").hide(true)) .arg(flag("only-git-deps", "Not supported").hide(true)) @@ -83,12 +96,17 @@ https://github.com/rust-lang/cargo/issues/new .get_one::("path") .cloned() .unwrap_or_else(|| PathBuf::from("vendor")); + + let (edge_kinds, _no_proc_macro) = parse_edge_kinds(config, args)?; + ops::vendor( &ws, &ops::VendorOptions { + cli_features: args.cli_features()?, no_delete: args.flag("no-delete"), destination: &path, versioned_dirs: args.flag("versioned-dirs"), + edge_kinds, extra: args .get_many::("tomls") .unwrap_or_default() diff --git a/src/cargo/ops/tree/graph.rs b/src/cargo/ops/tree/graph.rs index 20a9ca0b6574..63c9b92b1772 100644 --- a/src/cargo/ops/tree/graph.rs +++ b/src/cargo/ops/tree/graph.rs @@ -1,6 +1,5 @@ //! Code for building the graph used by `cargo tree`. -use super::TreeOptions; use crate::core::compiler::{CompileKind, RustcTargetData}; use crate::core::dependency::DepKind; use crate::core::resolver::features::{CliFeatures, FeaturesFor, ResolvedFeatures}; @@ -9,6 +8,13 @@ use crate::core::{FeatureMap, FeatureValue, Package, PackageId, PackageIdSpec, W use crate::util::interning::InternedString; use crate::util::CargoResult; use std::collections::{HashMap, HashSet}; +use crate::ops::tree::Target; + +pub struct GraphOptions { + pub target: Target, + pub graph_features: bool, + pub edge_kinds: HashSet, +} #[derive(Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] pub enum Node { @@ -276,13 +282,14 @@ pub fn build<'a>( target_data: &RustcTargetData<'_>, requested_kinds: &[CompileKind], package_map: HashMap, - opts: &TreeOptions, + opts: &GraphOptions, ) -> CargoResult> { let mut graph = Graph::new(package_map); let mut members_with_features = ws.members_with_features(specs, cli_features)?; members_with_features.sort_unstable_by_key(|e| e.0.package_id()); for (member, cli_features) in members_with_features { let member_id = member.package_id(); + println!("Adding package {} to the graph", member_id); let features_for = FeaturesFor::from_for_host(member.proc_macro()); for kind in requested_kinds { let member_index = add_pkg( @@ -304,6 +311,7 @@ pub fn build<'a>( if opts.graph_features { add_internal_features(&mut graph, resolve); } + println!("Built graph of {} nodes", graph.nodes.len()); Ok(graph) } @@ -320,7 +328,7 @@ fn add_pkg( features_for: FeaturesFor, target_data: &RustcTargetData<'_>, requested_kind: CompileKind, - opts: &TreeOptions, + opts: &GraphOptions, ) -> usize { let node_features = resolved_features.activated_features(package_id, features_for); let node_kind = match features_for { @@ -356,6 +364,7 @@ fn add_pkg( }; // Filter out inactivated targets. if !show_all_targets && !target_data.dep_platform_activated(dep, kind) { + println!("Disabling {}", dep.package_name()); return false; } // Filter out dev-dependencies if requested. diff --git a/src/cargo/ops/tree/mod.rs b/src/cargo/ops/tree/mod.rs index 02459f78f43b..fc52c9b0a405 100644 --- a/src/cargo/ops/tree/mod.rs +++ b/src/cargo/ops/tree/mod.rs @@ -14,9 +14,12 @@ use std::collections::{HashMap, HashSet}; use std::str::FromStr; mod format; -mod graph; +// TODO: Move it one level up instead of making it public and have deps from vendor to tree +// What is the bast package structure? +pub mod graph; pub use {graph::EdgeKind, graph::Node}; +use crate::ops::tree::graph::GraphOptions; pub struct TreeOptions { pub cli_features: CliFeatures, @@ -51,7 +54,7 @@ pub struct TreeOptions { pub no_proc_macro: bool, } -#[derive(PartialEq)] +#[derive(PartialEq, Clone)] pub enum Target { Host, Specific(Vec), @@ -166,6 +169,12 @@ pub fn build_and_print(ws: &Workspace<'_>, opts: &TreeOptions) -> CargoResult<() .map(|pkg| (pkg.package_id(), pkg)) .collect(); + let graph_options = GraphOptions { + target: opts.target.clone(), + graph_features: opts.graph_features, + edge_kinds: opts.edge_kinds.clone(), + }; + let mut graph = graph::build( ws, &ws_resolve.targeted_resolve, @@ -175,7 +184,7 @@ pub fn build_and_print(ws: &Workspace<'_>, opts: &TreeOptions) -> CargoResult<() &target_data, &requested_kinds, package_map, - opts, + &graph_options, )?; let root_specs = if opts.invert.is_empty() { @@ -213,6 +222,9 @@ pub fn build_and_print(ws: &Workspace<'_>, opts: &TreeOptions) -> CargoResult<() }) .collect::>>()?; + println!("pkgs_to_prune count = {}", + pkgs_to_prune.len()); + if root_indexes.len() == 0 { ws.config().shell().warn( "nothing to print.\n\n\ diff --git a/src/cargo/ops/vendor.rs b/src/cargo/ops/vendor.rs index c09a936d03f0..912849368fb9 100644 --- a/src/cargo/ops/vendor.rs +++ b/src/cargo/ops/vendor.rs @@ -1,6 +1,12 @@ +use crate::core::compiler::{CompileKind, RustcTargetData}; +use crate::core::dependency::DepKind; +use crate::core::resolver::{CliFeatures, ForceAllTargets, HasDevUnits}; use crate::core::shell::Verbosity; -use crate::core::{GitReference, Workspace}; +use crate::core::{GitReference, Package, PackageId, PackageSet, Resolve, Workspace}; use crate::ops; +use crate::ops::tree::graph::{Graph, GraphOptions}; +use crate::ops::tree::{EdgeKind, Node, Target}; +use crate::ops::Packages; use crate::sources::path::PathSource; use crate::sources::CRATES_IO_REGISTRY; use crate::util::{CargoResult, Config}; @@ -15,10 +21,12 @@ use std::path::{Path, PathBuf}; use toml_edit::easy as toml; pub struct VendorOptions<'a> { + pub cli_features: CliFeatures, pub no_delete: bool, pub versioned_dirs: bool, pub destination: &'a Path, pub extra: Vec, + pub edge_kinds: HashSet, } pub fn vendor(ws: &Workspace<'_>, opts: &VendorOptions<'_>) -> CargoResult<()> { @@ -145,30 +153,97 @@ fn sync( // Next up let's actually download all crates and start storing internal // tables about them. for ws in workspaces { - let (packages, resolve) = - ops::resolve_ws(ws).with_context(|| "failed to load pkg lockfile")?; + // TODO: Derive it from CLI flags like cargo tree does + let requested_targets = vec!["x86_64-apple-darwin".to_string()]; + let requested_kinds = CompileKind::from_requested_targets(ws.config(), &requested_targets)?; + let target_data = RustcTargetData::new(ws, &requested_kinds)?; + let specs = Packages::Default.to_package_id_specs(ws)?; + let has_dev = HasDevUnits::Yes; + let force_all = ForceAllTargets::No; + + let graph_options = GraphOptions { + target: Target::Specific(requested_targets), + graph_features: false, + edge_kinds: opts.edge_kinds.clone(), + }; - packages - .get_many(resolve.iter()) + let ws_resolve = ops::resolve_ws_with_opts( + ws, + &target_data, + &requested_kinds, + &opts.cli_features, + &specs, + has_dev, + force_all, + ) + .with_context(|| "failed to load pkg lockfile")?; + println!( + "Resolved returned {}/{} packages with {} targets", + ws_resolve.pkg_set.packages().count(), + ws_resolve.pkg_set.package_ids().count(), + ws_resolve.targeted_resolve.iter().count() + ); + + let package_map: HashMap = ws_resolve + .pkg_set + .packages() + .map(|pkg| (pkg.package_id(), pkg)) + .collect(); + + println!("Collected the package map of size {}", package_map.len()); + + // TODO: Should not depend on tree here + let mut graph = ops::tree::graph::build( + ws, + &ws_resolve.targeted_resolve, + &ws_resolve.resolved_features, + &specs, + &opts.cli_features, + &target_data, + &requested_kinds, + package_map, + &graph_options, + )?; + let root_ids = ws_resolve.targeted_resolve.specs_to_ids(&specs)?; + let root_indexes = graph.indexes_from_ids(&root_ids); + + ws_resolve + .pkg_set + .get_many(ws_resolve.targeted_resolve.iter()) .with_context(|| "failed to download packages")?; - for pkg in resolve.iter() { - // No need to vendor path crates since they're already in the - // repository - if pkg.source_id().is_path() { - continue; - } - ids.insert( - pkg, - packages - .get_one(pkg) - .with_context(|| "failed to fetch package")? - .clone(), - ); + println!( + "Resolved package number of download is {}", + ws_resolve.pkg_set.packages().count() + ); - checksums.insert(pkg, resolve.checksums().get(&pkg).cloned()); - } + traverse_graph( + &mut ids, + &mut checksums, + &ws_resolve.pkg_set, + &ws_resolve.targeted_resolve, + root_indexes, + &graph, + ); + + // for pkg in resolve.iter() { + // // No need to vendor path crates since they're already in the + // // repository + // if pkg.source_id().is_path() { + // continue; + // } + // ids.insert( + // pkg, + // packages + // .get_one(pkg) + // .with_context(|| "failed to fetch package")? + // .clone(), + // ); + // + // checksums.insert(pkg, resolve.checksums().get(&pkg).cloned()); + // } } + println!("Collected if map of size {}", ids.len()); let mut versions = HashMap::new(); for id in ids.keys() { @@ -359,6 +434,115 @@ fn cp_sources( Ok(()) } +fn traverse_graph( + ids: &mut BTreeMap, + checksums: &mut HashMap>>, + packages: &PackageSet, + resolve: &Resolve, + roots: Vec, + graph: &Graph<'_>, +) { + let mut visited_deps = HashSet::new(); + for root_index in roots.into_iter() { + visit_node( + ids, + checksums, + packages, + resolve, + root_index, + &mut visited_deps, + graph, + ); + } +} + +fn visit_node( + ids: &mut BTreeMap, + checksums: &mut HashMap>>, + packages: &PackageSet, + resolve: &Resolve, + node_index: usize, + visited_deps: &mut HashSet, + graph: &Graph<'_>, +) { + if !visited_deps.insert(node_index) { + return; + } + let node = graph.node(node_index); + let package_id = match node { + Node::Package { package_id, .. } => package_id, + Node::Feature { node_index, .. } => { + let for_node = graph.node(*node_index); + match for_node { + Node::Package { package_id, .. } => package_id, + // The node_index in Node::Feature must point to a package + // node, see `add_feature`. + _ => panic!("unexpected feature node {:?}", for_node), + } + } + } + .clone(); + // No need to vendor path crates since they're already in the + // repository + if !package_id.source_id().is_path() { + ids.insert( + package_id.clone(), + packages + .get_one(package_id) + .expect("failed to fetch package") + .clone(), + ); + checksums.insert(package_id, resolve.checksums().get(&package_id).cloned()); + } + + for kind in &[ + EdgeKind::Dep(DepKind::Normal), + EdgeKind::Dep(DepKind::Build), + EdgeKind::Dep(DepKind::Development), + EdgeKind::Feature, + ] { + visit_dependencies( + ids, + checksums, + packages, + resolve, + node_index, + visited_deps, + graph, + kind, + ); + } +} + +fn visit_dependencies( + ids: &mut BTreeMap, + checksums: &mut HashMap>>, + packages: &PackageSet, + resolve: &Resolve, + node_index: usize, + visited_deps: &mut HashSet, + graph: &Graph<'_>, + kind: &EdgeKind, +) { + let deps = graph.connected_nodes(node_index, kind); + if deps.is_empty() { + return; + } + // TODO: Filter out packages to prune. + let mut it = deps.iter(); + while let Some(node_index) = it.next() { + visit_node( + ids, + checksums, + packages, + resolve, + *node_index, + visited_deps, + graph, + ); + } +} + fn copy_and_checksum(src_path: &Path, dst_path: &Path, buf: &mut [u8]) -> CargoResult { let mut src = File::open(src_path).with_context(|| format!("failed to open {:?}", src_path))?; let mut dst_opts = OpenOptions::new();