This repository has been archived by the owner on Jun 5, 2020. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This both makes the crate compile way faster and avoids having to touch tons of unstable, undocumented cargo internals. This requires Rust 1.41 (the current beta) for some cargo-metadata features. There are some subtle differences in the new output (particularly in handling dependencies between crates in workspaces), but they're pretty minor. Closes #73 Closes #69 Closes #66 Closes #65 Closes #63 Closes #61 Closes #57 Closes #20
- Loading branch information
Showing
8 changed files
with
702 additions
and
1,673 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
use std::path::PathBuf; | ||
use std::str::FromStr; | ||
use structopt::clap::AppSettings; | ||
use structopt::StructOpt; | ||
|
||
#[derive(StructOpt)] | ||
#[structopt(bin_name = "cargo")] | ||
pub enum Opts { | ||
#[structopt( | ||
name = "tree", | ||
setting = AppSettings::UnifiedHelpMessage, | ||
setting = AppSettings::DeriveDisplayOrder, | ||
setting = AppSettings::DontCollapseArgsInUsage | ||
)] | ||
/// Display a tree visualization of a dependency graph | ||
Tree(Args), | ||
} | ||
|
||
#[derive(StructOpt)] | ||
pub struct Args { | ||
#[structopt(long = "package", short = "p", value_name = "SPEC")] | ||
/// Package to be used as the root of the tree | ||
pub package: Option<String>, | ||
#[structopt(long = "features", value_name = "FEATURES")] | ||
/// Space-separated list of features to activate | ||
pub features: Option<String>, | ||
#[structopt(long = "all-features")] | ||
/// Activate all available features | ||
pub all_features: bool, | ||
#[structopt(long = "no-default-features")] | ||
/// Do not activate the `default` feature | ||
pub no_default_features: bool, | ||
#[structopt(long = "target", value_name = "TARGET")] | ||
/// Set the target triple | ||
pub target: Option<String>, | ||
#[structopt(long = "all-targets")] | ||
/// Return dependencies for all targets. By default only the host target is matched. | ||
pub all_targets: bool, | ||
#[structopt(long = "no-dev-dependencies")] | ||
/// Skip dev dependencies. | ||
pub no_dev_dependencies: bool, | ||
#[structopt(long = "manifest-path", value_name = "PATH", parse(from_os_str))] | ||
/// Path to Cargo.toml | ||
pub manifest_path: Option<PathBuf>, | ||
#[structopt(long = "invert", short = "i")] | ||
/// Invert the tree direction | ||
pub invert: bool, | ||
#[structopt(long = "no-indent")] | ||
/// Display the dependencies as a list (rather than a tree) | ||
pub no_indent: bool, | ||
#[structopt(long = "prefix-depth")] | ||
/// Display the dependencies as a list (rather than a tree), but prefixed with the depth | ||
pub prefix_depth: bool, | ||
#[structopt(long = "all", short = "a")] | ||
/// Don't truncate dependencies that have already been displayed | ||
pub all: bool, | ||
#[structopt(long = "duplicate", short = "d")] | ||
/// Show only dependencies which come in multiple versions (implies -i) | ||
pub duplicates: bool, | ||
#[structopt(long = "charset", value_name = "CHARSET", default_value = "utf8")] | ||
/// Character set to use in output: utf8, ascii | ||
pub charset: Charset, | ||
#[structopt( | ||
long = "format", | ||
short = "f", | ||
value_name = "FORMAT", | ||
default_value = "{p}" | ||
)] | ||
/// Format string used for printing dependencies | ||
pub format: String, | ||
#[structopt(long = "verbose", short = "v", parse(from_occurrences))] | ||
/// Use verbose output (-vv very verbose/build.rs output) | ||
pub verbose: u32, | ||
#[structopt(long = "quiet", short = "q")] | ||
/// No output printed to stdout other than the tree | ||
pub quiet: bool, | ||
#[structopt(long = "color", value_name = "WHEN")] | ||
/// Coloring: auto, always, never | ||
pub color: Option<String>, | ||
#[structopt(long = "frozen")] | ||
/// Require Cargo.lock and cache are up to date | ||
pub frozen: bool, | ||
#[structopt(long = "locked")] | ||
/// Require Cargo.lock is up to date | ||
pub locked: bool, | ||
#[structopt(long = "offline")] | ||
/// Do not access the network | ||
pub offline: bool, | ||
#[structopt(short = "Z", value_name = "FLAG")] | ||
/// Unstable (nightly-only) flags to Cargo | ||
pub unstable_flags: Vec<String>, | ||
} | ||
|
||
pub enum Charset { | ||
Utf8, | ||
Ascii, | ||
} | ||
|
||
impl FromStr for Charset { | ||
type Err = &'static str; | ||
|
||
fn from_str(s: &str) -> Result<Charset, &'static str> { | ||
match s { | ||
"utf8" => Ok(Charset::Utf8), | ||
"ascii" => Ok(Charset::Ascii), | ||
_ => Err("invalid charset"), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
use crate::args::Args; | ||
use anyhow::{anyhow, Error}; | ||
use cargo_metadata::{DependencyKind, Metadata, Package, PackageId}; | ||
use petgraph::graph::NodeIndex; | ||
use petgraph::stable_graph::StableGraph; | ||
use petgraph::visit::Dfs; | ||
use std::collections::HashMap; | ||
|
||
pub struct Graph { | ||
pub graph: StableGraph<Package, DependencyKind>, | ||
pub nodes: HashMap<PackageId, NodeIndex>, | ||
pub root: Option<PackageId>, | ||
} | ||
|
||
pub fn build(args: &Args, metadata: Metadata) -> Result<Graph, Error> { | ||
let resolve = metadata.resolve.unwrap(); | ||
|
||
let mut graph = Graph { | ||
graph: StableGraph::new(), | ||
nodes: HashMap::new(), | ||
root: resolve.root, | ||
}; | ||
|
||
for package in metadata.packages { | ||
let id = package.id.clone(); | ||
let index = graph.graph.add_node(package); | ||
graph.nodes.insert(id, index); | ||
} | ||
|
||
for node in resolve.nodes { | ||
if node.deps.len() != node.dependencies.len() { | ||
return Err(anyhow!("cargo tree requires cargo 1.41 or newer")); | ||
} | ||
|
||
let from = graph.nodes[&node.id]; | ||
for dep in node.deps { | ||
if dep.dep_kinds.is_empty() { | ||
return Err(anyhow!("cargo tree requires cargo 1.41 or newer")); | ||
} | ||
|
||
// https://github.com/rust-lang/cargo/issues/7752 | ||
let mut kinds = vec![]; | ||
for kind in dep.dep_kinds { | ||
if !kinds.iter().any(|k| *k == kind.kind) { | ||
kinds.push(kind.kind); | ||
} | ||
} | ||
|
||
let to = graph.nodes[&dep.pkg]; | ||
for kind in kinds { | ||
if args.no_dev_dependencies && kind == DependencyKind::Development { | ||
continue; | ||
} | ||
|
||
graph.graph.add_edge(from, to, kind); | ||
} | ||
} | ||
} | ||
|
||
// prune nodes not reachable from the root package (directionally) | ||
if let Some(root) = &graph.root { | ||
let mut dfs = Dfs::new(&graph.graph, graph.nodes[root]); | ||
while dfs.next(&graph.graph).is_some() {} | ||
|
||
let g = &mut graph.graph; | ||
graph.nodes.retain(|_, idx| { | ||
if !dfs.discovered.contains(idx.index()) { | ||
g.remove_node(*idx); | ||
false | ||
} else { | ||
true | ||
} | ||
}); | ||
} | ||
|
||
Ok(graph) | ||
} |
Oops, something went wrong.