mirror of
https://github.com/rust-lang/cargo.git
synced 2025-10-01 11:30:39 +00:00
Add cargo tree
command.
This commit is contained in:
parent
c75216fc55
commit
96a393719b
@ -27,6 +27,7 @@ pub fn builtin() -> Vec<App> {
|
|||||||
rustdoc::cli(),
|
rustdoc::cli(),
|
||||||
search::cli(),
|
search::cli(),
|
||||||
test::cli(),
|
test::cli(),
|
||||||
|
tree::cli(),
|
||||||
uninstall::cli(),
|
uninstall::cli(),
|
||||||
update::cli(),
|
update::cli(),
|
||||||
vendor::cli(),
|
vendor::cli(),
|
||||||
@ -63,6 +64,7 @@ pub fn builtin_exec(cmd: &str) -> Option<fn(&mut Config, &ArgMatches<'_>) -> Cli
|
|||||||
"rustdoc" => rustdoc::exec,
|
"rustdoc" => rustdoc::exec,
|
||||||
"search" => search::exec,
|
"search" => search::exec,
|
||||||
"test" => test::exec,
|
"test" => test::exec,
|
||||||
|
"tree" => tree::exec,
|
||||||
"uninstall" => uninstall::exec,
|
"uninstall" => uninstall::exec,
|
||||||
"update" => update::exec,
|
"update" => update::exec,
|
||||||
"vendor" => vendor::exec,
|
"vendor" => vendor::exec,
|
||||||
@ -99,6 +101,7 @@ pub mod rustc;
|
|||||||
pub mod rustdoc;
|
pub mod rustdoc;
|
||||||
pub mod search;
|
pub mod search;
|
||||||
pub mod test;
|
pub mod test;
|
||||||
|
pub mod tree;
|
||||||
pub mod uninstall;
|
pub mod uninstall;
|
||||||
pub mod update;
|
pub mod update;
|
||||||
pub mod vendor;
|
pub mod vendor;
|
||||||
|
84
src/bin/cargo/commands/tree.rs
Normal file
84
src/bin/cargo/commands/tree.rs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
use crate::command_prelude::*;
|
||||||
|
use cargo::ops::tree;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
pub fn cli() -> App {
|
||||||
|
subcommand("tree")
|
||||||
|
.about("Display a tree visualization of a dependency graph")
|
||||||
|
.arg(opt("quiet", "Suppress status messages").short("q"))
|
||||||
|
.arg_manifest_path()
|
||||||
|
.arg_package_spec_no_all(
|
||||||
|
"Package to be used as the root of the tree",
|
||||||
|
"Display the tree for all packages in the workspace",
|
||||||
|
"Exclude specific workspace members",
|
||||||
|
)
|
||||||
|
.arg_features()
|
||||||
|
.arg_target_triple(
|
||||||
|
"Filter dependencies matching the given target-triple (default host platform)",
|
||||||
|
)
|
||||||
|
.arg(opt(
|
||||||
|
"no-filter-targets",
|
||||||
|
"Return dependencies for all targets",
|
||||||
|
))
|
||||||
|
.arg(opt("no-dev-dependencies", "Skip dev dependencies"))
|
||||||
|
.arg(opt("invert", "Invert the tree direction").short("i"))
|
||||||
|
.arg(opt(
|
||||||
|
"no-indent",
|
||||||
|
"Display the dependencies as a list (rather than a tree)",
|
||||||
|
))
|
||||||
|
.arg(opt(
|
||||||
|
"prefix-depth",
|
||||||
|
"Display the dependencies as a list (rather than a tree), but prefixed with the depth",
|
||||||
|
))
|
||||||
|
.arg(opt(
|
||||||
|
"no-dedupe",
|
||||||
|
"Do not de-duplicate (repeats all shared dependencies)",
|
||||||
|
))
|
||||||
|
.arg(
|
||||||
|
opt(
|
||||||
|
"duplicates",
|
||||||
|
"Show only dependencies which come in multiple versions (implies -i)",
|
||||||
|
)
|
||||||
|
.short("d")
|
||||||
|
.alias("duplicate"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
opt("charset", "Character set to use in output: utf8, ascii")
|
||||||
|
.value_name("CHARSET")
|
||||||
|
.possible_values(&["utf8", "ascii"])
|
||||||
|
.default_value("utf8"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
opt("format", "Format string used for printing dependencies")
|
||||||
|
.value_name("FORMAT")
|
||||||
|
.short("f")
|
||||||
|
.default_value("{p}"),
|
||||||
|
)
|
||||||
|
.arg(opt("graph-features", "Include features in the tree"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
|
||||||
|
let ws = args.workspace(config)?;
|
||||||
|
let charset = tree::Charset::from_str(args.value_of("charset").unwrap())
|
||||||
|
.map_err(|e| anyhow::anyhow!("{}", e))?;
|
||||||
|
let opts = tree::TreeOptions {
|
||||||
|
features: values(args, "features"),
|
||||||
|
all_features: args.is_present("all-features"),
|
||||||
|
no_default_features: args.is_present("no-default-features"),
|
||||||
|
packages: args.packages_from_flags()?,
|
||||||
|
target: args.target(),
|
||||||
|
no_filter_targets: args.is_present("no-filter-targets"),
|
||||||
|
no_dev_dependencies: args.is_present("no-dev-dependencies"),
|
||||||
|
invert: args.is_present("invert"),
|
||||||
|
no_indent: args.is_present("no-indent"),
|
||||||
|
prefix_depth: args.is_present("prefix-depth"),
|
||||||
|
no_dedupe: args.is_present("no-dedupe"),
|
||||||
|
duplicates: args.is_present("duplicates"),
|
||||||
|
charset,
|
||||||
|
format: args.value_of("format").unwrap().to_string(),
|
||||||
|
graph_features: args.is_present("graph-features"),
|
||||||
|
};
|
||||||
|
|
||||||
|
tree::build_and_print(&ws, &opts)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
use crate::core::compiler::{CompileKind, CompileTarget};
|
use crate::core::compiler::CompileKind;
|
||||||
use crate::core::interning::InternedString;
|
use crate::core::interning::InternedString;
|
||||||
use crate::util::ProcessBuilder;
|
use crate::util::ProcessBuilder;
|
||||||
use crate::util::{CargoResult, Config, RustfixDiagnosticServer};
|
use crate::util::{CargoResult, Config, RustfixDiagnosticServer};
|
||||||
@ -45,22 +45,8 @@ impl BuildConfig {
|
|||||||
mode: CompileMode,
|
mode: CompileMode,
|
||||||
) -> CargoResult<BuildConfig> {
|
) -> CargoResult<BuildConfig> {
|
||||||
let cfg = config.build_config()?;
|
let cfg = config.build_config()?;
|
||||||
let requested_kind = match requested_target {
|
let requested_kind =
|
||||||
Some(s) => CompileKind::Target(CompileTarget::new(s)?),
|
CompileKind::from_requested_target(config, requested_target.as_deref())?;
|
||||||
None => match &cfg.target {
|
|
||||||
Some(val) => {
|
|
||||||
let value = if val.raw_value().ends_with(".json") {
|
|
||||||
let path = val.clone().resolve_path(config);
|
|
||||||
path.to_str().expect("must be utf-8 in toml").to_string()
|
|
||||||
} else {
|
|
||||||
val.raw_value().to_string()
|
|
||||||
};
|
|
||||||
CompileKind::Target(CompileTarget::new(&value)?)
|
|
||||||
}
|
|
||||||
None => CompileKind::Host,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if jobs == Some(0) {
|
if jobs == Some(0) {
|
||||||
anyhow::bail!("jobs must be at least 1")
|
anyhow::bail!("jobs must be at least 1")
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use crate::core::{InternedString, Target};
|
use crate::core::{InternedString, Target};
|
||||||
use crate::util::errors::{CargoResult, CargoResultExt};
|
use crate::util::errors::{CargoResult, CargoResultExt};
|
||||||
|
use crate::util::Config;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
@ -39,6 +40,32 @@ impl CompileKind {
|
|||||||
CompileKind::Target(n) => CompileKind::Target(n),
|
CompileKind::Target(n) => CompileKind::Target(n),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a new `CompileKind` based on the requested target.
|
||||||
|
///
|
||||||
|
/// If no target is given, this consults the config if the default is set.
|
||||||
|
/// Otherwise returns `CompileKind::Host`.
|
||||||
|
pub fn from_requested_target(
|
||||||
|
config: &Config,
|
||||||
|
target: Option<&str>,
|
||||||
|
) -> CargoResult<CompileKind> {
|
||||||
|
let kind = match target {
|
||||||
|
Some(s) => CompileKind::Target(CompileTarget::new(s)?),
|
||||||
|
None => match &config.build_config()?.target {
|
||||||
|
Some(val) => {
|
||||||
|
let value = if val.raw_value().ends_with(".json") {
|
||||||
|
let path = val.clone().resolve_path(config);
|
||||||
|
path.to_str().expect("must be utf-8 in toml").to_string()
|
||||||
|
} else {
|
||||||
|
val.raw_value().to_string()
|
||||||
|
};
|
||||||
|
CompileKind::Target(CompileTarget::new(&value)?)
|
||||||
|
}
|
||||||
|
None => CompileKind::Host,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Ok(kind)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl serde::ser::Serialize for CompileKind {
|
impl serde::ser::Serialize for CompileKind {
|
||||||
|
@ -998,11 +998,7 @@ impl UnitFor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn map_to_features_for(&self) -> FeaturesFor {
|
pub(crate) fn map_to_features_for(&self) -> FeaturesFor {
|
||||||
if self.is_for_host_features() {
|
FeaturesFor::from_for_host(self.is_for_host_features())
|
||||||
FeaturesFor::HostDep
|
|
||||||
} else {
|
|
||||||
FeaturesFor::NormalOrDev
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,13 +92,23 @@ pub enum HasDevUnits {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Flag to indicate if features are requested for a build dependency or not.
|
/// Flag to indicate if features are requested for a build dependency or not.
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
pub enum FeaturesFor {
|
pub enum FeaturesFor {
|
||||||
NormalOrDev,
|
NormalOrDev,
|
||||||
/// Build dependency or proc-macro.
|
/// Build dependency or proc-macro.
|
||||||
HostDep,
|
HostDep,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FeaturesFor {
|
||||||
|
pub fn from_for_host(for_host: bool) -> FeaturesFor {
|
||||||
|
if for_host {
|
||||||
|
FeaturesFor::HostDep
|
||||||
|
} else {
|
||||||
|
FeaturesFor::NormalOrDev
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FeatureOpts {
|
impl FeatureOpts {
|
||||||
fn new(config: &Config, has_dev_units: HasDevUnits) -> CargoResult<FeatureOpts> {
|
fn new(config: &Config, has_dev_units: HasDevUnits) -> CargoResult<FeatureOpts> {
|
||||||
let mut opts = FeatureOpts::default();
|
let mut opts = FeatureOpts::default();
|
||||||
|
@ -305,6 +305,10 @@ unable to verify that `{0}` is the same as when the lockfile was generated
|
|||||||
PackageIdSpec::query_str(spec, self.iter())
|
PackageIdSpec::query_str(spec, self.iter())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn specs_to_ids(&self, specs: &[PackageIdSpec]) -> CargoResult<Vec<PackageId>> {
|
||||||
|
specs.iter().map(|s| s.query(self.iter())).collect()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn unused_patches(&self) -> &[PackageId] {
|
pub fn unused_patches(&self) -> &[PackageId] {
|
||||||
&self.unused_patches
|
&self.unused_patches
|
||||||
}
|
}
|
||||||
|
@ -351,12 +351,8 @@ pub fn compile_ws<'a>(
|
|||||||
|
|
||||||
// Find the packages in the resolver that the user wants to build (those
|
// Find the packages in the resolver that the user wants to build (those
|
||||||
// passed in with `-p` or the defaults from the workspace), and convert
|
// passed in with `-p` or the defaults from the workspace), and convert
|
||||||
// Vec<PackageIdSpec> to a Vec<&PackageId>.
|
// Vec<PackageIdSpec> to a Vec<PackageId>.
|
||||||
let to_build_ids = specs
|
let to_build_ids = resolve.specs_to_ids(&specs)?;
|
||||||
.iter()
|
|
||||||
.map(|s| s.query(resolve.iter()))
|
|
||||||
.collect::<CargoResult<Vec<_>>>()?;
|
|
||||||
|
|
||||||
// Now get the `Package` for each `PackageId`. This may trigger a download
|
// Now get the `Package` for each `PackageId`. This may trigger a download
|
||||||
// if the user specified `-p` for a dependency that is not downloaded.
|
// if the user specified `-p` for a dependency that is not downloaded.
|
||||||
// Dependencies will be downloaded during build_unit_dependencies.
|
// Dependencies will be downloaded during build_unit_dependencies.
|
||||||
@ -753,12 +749,8 @@ fn generate_targets<'a>(
|
|||||||
bcx.profiles
|
bcx.profiles
|
||||||
.get_profile(pkg.package_id(), ws.is_member(pkg), unit_for, target_mode);
|
.get_profile(pkg.package_id(), ws.is_member(pkg), unit_for, target_mode);
|
||||||
|
|
||||||
let features_for = if target.proc_macro() {
|
// No need to worry about build-dependencies, roots are never build dependencies.
|
||||||
FeaturesFor::HostDep
|
let features_for = FeaturesFor::from_for_host(target.proc_macro());
|
||||||
} else {
|
|
||||||
// Root units are never build dependencies.
|
|
||||||
FeaturesFor::NormalOrDev
|
|
||||||
};
|
|
||||||
let features =
|
let features =
|
||||||
Vec::from(resolved_features.activated_features(pkg.package_id(), features_for));
|
Vec::from(resolved_features.activated_features(pkg.package_id(), features_for));
|
||||||
bcx.units.intern(
|
bcx.units.intern(
|
||||||
@ -969,11 +961,7 @@ pub fn resolve_all_features(
|
|||||||
.expect("packages downloaded")
|
.expect("packages downloaded")
|
||||||
.proc_macro();
|
.proc_macro();
|
||||||
for dep in deps {
|
for dep in deps {
|
||||||
let features_for = if is_proc_macro || dep.is_build() {
|
let features_for = FeaturesFor::from_for_host(is_proc_macro || dep.is_build());
|
||||||
FeaturesFor::HostDep
|
|
||||||
} else {
|
|
||||||
FeaturesFor::NormalOrDev
|
|
||||||
};
|
|
||||||
for feature in resolved_features.activated_features_unverified(dep_id, features_for) {
|
for feature in resolved_features.activated_features_unverified(dep_id, features_for) {
|
||||||
features.insert(format!("{}/{}", dep.name_in_toml(), feature));
|
features.insert(format!("{}/{}", dep.name_in_toml(), feature));
|
||||||
}
|
}
|
||||||
|
@ -36,10 +36,7 @@ pub fn doc(ws: &Workspace<'_>, options: &DocOptions) -> CargoResult<()> {
|
|||||||
HasDevUnits::No,
|
HasDevUnits::No,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let ids = specs
|
let ids = ws_resolve.targeted_resolve.specs_to_ids(&specs)?;
|
||||||
.iter()
|
|
||||||
.map(|s| s.query(ws_resolve.targeted_resolve.iter()))
|
|
||||||
.collect::<CargoResult<Vec<_>>>()?;
|
|
||||||
let pkgs = ws_resolve.pkg_set.get_many(ids)?;
|
let pkgs = ws_resolve.pkg_set.get_many(ids)?;
|
||||||
|
|
||||||
let mut lib_names = HashMap::new();
|
let mut lib_names = HashMap::new();
|
||||||
|
@ -48,4 +48,5 @@ mod fix;
|
|||||||
mod lockfile;
|
mod lockfile;
|
||||||
mod registry;
|
mod registry;
|
||||||
mod resolve;
|
mod resolve;
|
||||||
|
pub mod tree;
|
||||||
mod vendor;
|
mod vendor;
|
||||||
|
108
src/cargo/ops/tree/format/mod.rs
Normal file
108
src/cargo/ops/tree/format/mod.rs
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
use self::parse::{Parser, RawChunk};
|
||||||
|
use super::{Graph, Node};
|
||||||
|
use anyhow::{anyhow, Error};
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
mod parse;
|
||||||
|
|
||||||
|
enum Chunk {
|
||||||
|
Raw(String),
|
||||||
|
Package,
|
||||||
|
License,
|
||||||
|
Repository,
|
||||||
|
Features,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Pattern(Vec<Chunk>);
|
||||||
|
|
||||||
|
impl Pattern {
|
||||||
|
pub fn new(format: &str) -> Result<Pattern, Error> {
|
||||||
|
let mut chunks = vec![];
|
||||||
|
|
||||||
|
for raw in Parser::new(format) {
|
||||||
|
let chunk = match raw {
|
||||||
|
RawChunk::Text(text) => Chunk::Raw(text.to_owned()),
|
||||||
|
RawChunk::Argument("p") => Chunk::Package,
|
||||||
|
RawChunk::Argument("l") => Chunk::License,
|
||||||
|
RawChunk::Argument("r") => Chunk::Repository,
|
||||||
|
RawChunk::Argument("f") => Chunk::Features,
|
||||||
|
RawChunk::Argument(a) => {
|
||||||
|
return Err(anyhow!("unsupported pattern `{}`", a));
|
||||||
|
}
|
||||||
|
RawChunk::Error(err) => return Err(anyhow!("{}", err)),
|
||||||
|
};
|
||||||
|
chunks.push(chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Pattern(chunks))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn display<'a>(&'a self, graph: &'a Graph<'a>, node_index: usize) -> Display<'a> {
|
||||||
|
Display {
|
||||||
|
pattern: self,
|
||||||
|
graph,
|
||||||
|
node_index,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Display<'a> {
|
||||||
|
pattern: &'a Pattern,
|
||||||
|
graph: &'a Graph<'a>,
|
||||||
|
node_index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> fmt::Display for Display<'a> {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let node = self.graph.node(self.node_index);
|
||||||
|
match node {
|
||||||
|
Node::Package {
|
||||||
|
package_id,
|
||||||
|
features,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let package = self.graph.package_for_id(*package_id);
|
||||||
|
for chunk in &self.pattern.0 {
|
||||||
|
match *chunk {
|
||||||
|
Chunk::Raw(ref s) => fmt.write_str(s)?,
|
||||||
|
Chunk::Package => {
|
||||||
|
write!(fmt, "{} v{}", package.name(), package.version())?;
|
||||||
|
|
||||||
|
let source_id = package.package_id().source_id();
|
||||||
|
if !source_id.is_default_registry() {
|
||||||
|
write!(fmt, " ({})", source_id)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Chunk::License => {
|
||||||
|
if let Some(ref license) = package.manifest().metadata().license {
|
||||||
|
write!(fmt, "{}", license)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Chunk::Repository => {
|
||||||
|
if let Some(ref repository) = package.manifest().metadata().repository {
|
||||||
|
write!(fmt, "{}", repository)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Chunk::Features => {
|
||||||
|
write!(fmt, "{}", features.join(","))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Node::Feature { name, node_index } => {
|
||||||
|
let for_node = self.graph.node(*node_index);
|
||||||
|
match for_node {
|
||||||
|
Node::Package { package_id, .. } => {
|
||||||
|
write!(fmt, "{} feature \"{}\"", package_id.name(), name)?;
|
||||||
|
if self.graph.is_cli_feature(self.node_index) {
|
||||||
|
write!(fmt, " (command-line)")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => panic!("unexpected feature node {:?}", for_node),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
97
src/cargo/ops/tree/format/parse.rs
Normal file
97
src/cargo/ops/tree/format/parse.rs
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
use std::iter;
|
||||||
|
use std::str;
|
||||||
|
|
||||||
|
pub enum RawChunk<'a> {
|
||||||
|
Text(&'a str),
|
||||||
|
Argument(&'a str),
|
||||||
|
Error(&'static str),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Parser<'a> {
|
||||||
|
s: &'a str,
|
||||||
|
it: iter::Peekable<str::CharIndices<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Parser<'a> {
|
||||||
|
pub fn new(s: &'a str) -> Parser<'a> {
|
||||||
|
Parser {
|
||||||
|
s,
|
||||||
|
it: s.char_indices().peekable(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn consume(&mut self, ch: char) -> bool {
|
||||||
|
match self.it.peek() {
|
||||||
|
Some(&(_, c)) if c == ch => {
|
||||||
|
self.it.next();
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn argument(&mut self) -> RawChunk<'a> {
|
||||||
|
RawChunk::Argument(self.name())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&mut self) -> &'a str {
|
||||||
|
let start = match self.it.peek() {
|
||||||
|
Some(&(pos, ch)) if ch.is_alphabetic() => {
|
||||||
|
self.it.next();
|
||||||
|
pos
|
||||||
|
}
|
||||||
|
_ => return "",
|
||||||
|
};
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match self.it.peek() {
|
||||||
|
Some(&(_, ch)) if ch.is_alphanumeric() => {
|
||||||
|
self.it.next();
|
||||||
|
}
|
||||||
|
Some(&(end, _)) => return &self.s[start..end],
|
||||||
|
None => return &self.s[start..],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn text(&mut self, start: usize) -> RawChunk<'a> {
|
||||||
|
while let Some(&(pos, ch)) = self.it.peek() {
|
||||||
|
match ch {
|
||||||
|
'{' | '}' | ')' => return RawChunk::Text(&self.s[start..pos]),
|
||||||
|
_ => {
|
||||||
|
self.it.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RawChunk::Text(&self.s[start..])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for Parser<'a> {
|
||||||
|
type Item = RawChunk<'a>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<RawChunk<'a>> {
|
||||||
|
match self.it.peek() {
|
||||||
|
Some(&(_, '{')) => {
|
||||||
|
self.it.next();
|
||||||
|
if self.consume('{') {
|
||||||
|
Some(RawChunk::Text("{"))
|
||||||
|
} else {
|
||||||
|
let chunk = self.argument();
|
||||||
|
if self.consume('}') {
|
||||||
|
Some(chunk)
|
||||||
|
} else {
|
||||||
|
for _ in &mut self.it {}
|
||||||
|
Some(RawChunk::Error("expected '}'"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(&(_, '}')) => {
|
||||||
|
self.it.next();
|
||||||
|
Some(RawChunk::Error("unexpected '}'"))
|
||||||
|
}
|
||||||
|
Some(&(i, _)) => Some(self.text(i)),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
562
src/cargo/ops/tree/graph.rs
Normal file
562
src/cargo/ops/tree/graph.rs
Normal file
@ -0,0 +1,562 @@
|
|||||||
|
//! 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::{FeaturesFor, RequestedFeatures, ResolvedFeatures};
|
||||||
|
use crate::core::resolver::Resolve;
|
||||||
|
use crate::core::{
|
||||||
|
FeatureMap, FeatureValue, InternedString, Package, PackageId, PackageIdSpec, Workspace,
|
||||||
|
};
|
||||||
|
use crate::util::CargoResult;
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||||
|
pub enum Node {
|
||||||
|
Package {
|
||||||
|
package_id: PackageId,
|
||||||
|
/// Features that are enabled on this package.
|
||||||
|
features: Vec<InternedString>,
|
||||||
|
kind: CompileKind,
|
||||||
|
},
|
||||||
|
Feature {
|
||||||
|
/// Index of the package node this feature is for.
|
||||||
|
node_index: usize,
|
||||||
|
/// Name of the feature.
|
||||||
|
name: InternedString,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Hash, Eq, Clone, PartialEq)]
|
||||||
|
pub enum Edge {
|
||||||
|
Dep(DepKind),
|
||||||
|
Feature,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct Edges(HashMap<Edge, Vec<usize>>);
|
||||||
|
|
||||||
|
impl Edges {
|
||||||
|
fn new() -> Edges {
|
||||||
|
Edges(HashMap::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_edge(&mut self, kind: Edge, index: usize) {
|
||||||
|
let indexes = self.0.entry(kind).or_default();
|
||||||
|
if !indexes.contains(&index) {
|
||||||
|
indexes.push(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A graph of dependencies.
|
||||||
|
pub struct Graph<'a> {
|
||||||
|
nodes: Vec<Node>,
|
||||||
|
edges: Vec<Edges>,
|
||||||
|
/// Index maps a node to an index, for fast lookup.
|
||||||
|
index: HashMap<Node, usize>,
|
||||||
|
/// Map for looking up packages.
|
||||||
|
package_map: HashMap<PackageId, &'a Package>,
|
||||||
|
/// Set of indexes of feature nodes that were added via the command-line.
|
||||||
|
///
|
||||||
|
/// For example `--features foo` will mark the "foo" node here.
|
||||||
|
cli_features: HashSet<usize>,
|
||||||
|
/// Map of dependency names, used for building internal feature map for
|
||||||
|
/// dep_name/feat_name syntax.
|
||||||
|
///
|
||||||
|
/// Key is the index of a package node, value is a map of dep_name to a
|
||||||
|
/// set of `(pkg_node_index, is_optional)`.
|
||||||
|
dep_name_map: HashMap<usize, HashMap<InternedString, HashSet<(usize, bool)>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Graph<'a> {
|
||||||
|
fn new(package_map: HashMap<PackageId, &'a Package>) -> Graph<'a> {
|
||||||
|
Graph {
|
||||||
|
nodes: Vec::new(),
|
||||||
|
edges: Vec::new(),
|
||||||
|
index: HashMap::new(),
|
||||||
|
package_map,
|
||||||
|
cli_features: HashSet::new(),
|
||||||
|
dep_name_map: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a new node to the graph, returning its new index.
|
||||||
|
fn add_node(&mut self, node: Node) -> usize {
|
||||||
|
let from_index = self.nodes.len();
|
||||||
|
self.nodes.push(node);
|
||||||
|
self.edges.push(Edges::new());
|
||||||
|
self.index
|
||||||
|
.insert(self.nodes[from_index].clone(), from_index);
|
||||||
|
from_index
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a list of nodes the given node index points to for the given kind.
|
||||||
|
///
|
||||||
|
/// Returns None if there are none.
|
||||||
|
pub fn connected_nodes(&self, from: usize, kind: &Edge) -> Option<Vec<usize>> {
|
||||||
|
match self.edges[from].0.get(kind) {
|
||||||
|
Some(indexes) => {
|
||||||
|
// Created a sorted list for consistent output.
|
||||||
|
let mut indexes = indexes.clone();
|
||||||
|
indexes.sort_unstable_by(|a, b| self.nodes[*a].cmp(&self.nodes[*b]));
|
||||||
|
Some(indexes)
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a node by index.
|
||||||
|
pub fn node(&self, index: usize) -> &Node {
|
||||||
|
&self.nodes[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Given a slice of PackageIds, returns the indexes of all nodes that match.
|
||||||
|
pub fn indexes_from_ids(&self, package_ids: &[PackageId]) -> Vec<usize> {
|
||||||
|
let mut result: Vec<(&Node, usize)> = self
|
||||||
|
.nodes
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter(|(_i, node)| match node {
|
||||||
|
Node::Package { package_id, .. } => package_ids.contains(package_id),
|
||||||
|
_ => false,
|
||||||
|
})
|
||||||
|
.map(|(i, node)| (node, i))
|
||||||
|
.collect();
|
||||||
|
result.sort_unstable();
|
||||||
|
result.into_iter().map(|(_node, i)| i).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn package_for_id(&self, id: PackageId) -> &Package {
|
||||||
|
self.package_map[&id]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn package_id_for_index(&self, index: usize) -> PackageId {
|
||||||
|
match self.nodes[index] {
|
||||||
|
Node::Package { package_id, .. } => package_id,
|
||||||
|
Node::Feature { .. } => panic!("unexpected feature node"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the given feature node index is a feature enabled
|
||||||
|
/// via the command-line.
|
||||||
|
pub fn is_cli_feature(&self, index: usize) -> bool {
|
||||||
|
self.cli_features.contains(&index)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a new graph by removing all nodes not reachable from the
|
||||||
|
/// given nodes.
|
||||||
|
pub fn from_reachable(&self, roots: &[usize]) -> Graph<'a> {
|
||||||
|
// Graph built with features does not (yet) support --duplicates.
|
||||||
|
assert!(self.dep_name_map.is_empty());
|
||||||
|
let mut new_graph = Graph::new(self.package_map.clone());
|
||||||
|
// Maps old index to new index. None if not yet visited.
|
||||||
|
let mut remap: Vec<Option<usize>> = vec![None; self.nodes.len()];
|
||||||
|
|
||||||
|
fn visit(
|
||||||
|
graph: &Graph<'_>,
|
||||||
|
new_graph: &mut Graph<'_>,
|
||||||
|
remap: &mut Vec<Option<usize>>,
|
||||||
|
index: usize,
|
||||||
|
) -> usize {
|
||||||
|
if let Some(new_index) = remap[index] {
|
||||||
|
// Already visited.
|
||||||
|
return new_index;
|
||||||
|
}
|
||||||
|
let node = graph.node(index).clone();
|
||||||
|
let new_from = new_graph.add_node(node);
|
||||||
|
remap[index] = Some(new_from);
|
||||||
|
// Visit dependencies.
|
||||||
|
for (edge_kind, edge_indexes) in &graph.edges[index].0 {
|
||||||
|
for edge_index in edge_indexes {
|
||||||
|
let new_to_index = visit(graph, new_graph, remap, *edge_index);
|
||||||
|
new_graph.edges[new_from].add_edge(*edge_kind, new_to_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
new_from
|
||||||
|
}
|
||||||
|
|
||||||
|
// Walk the roots, generating a new graph as it goes along.
|
||||||
|
for root in roots {
|
||||||
|
visit(self, &mut new_graph, &mut remap, *root);
|
||||||
|
}
|
||||||
|
|
||||||
|
new_graph
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inverts the direction of all edges.
|
||||||
|
pub fn invert(&mut self) {
|
||||||
|
let mut new_edges = vec![Edges::new(); self.edges.len()];
|
||||||
|
for (from_idx, node_edges) in self.edges.iter().enumerate() {
|
||||||
|
for (kind, edges) in &node_edges.0 {
|
||||||
|
for edge_idx in edges {
|
||||||
|
new_edges[*edge_idx].add_edge(*kind, from_idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.edges = new_edges;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a list of nodes that are considered "duplicates" (same package
|
||||||
|
/// name, with different versions/features/source/etc.).
|
||||||
|
pub fn find_duplicates(&self) -> Vec<usize> {
|
||||||
|
// Graph built with features does not (yet) support --duplicates.
|
||||||
|
assert!(self.dep_name_map.is_empty());
|
||||||
|
|
||||||
|
// Collect a map of package name to Vec<(&Node, usize)>.
|
||||||
|
let mut packages = HashMap::new();
|
||||||
|
for (i, node) in self.nodes.iter().enumerate() {
|
||||||
|
if let Node::Package { package_id, .. } = node {
|
||||||
|
packages
|
||||||
|
.entry(package_id.name())
|
||||||
|
.or_insert_with(Vec::new)
|
||||||
|
.push((node, i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut dupes: Vec<(&Node, usize)> = packages
|
||||||
|
.into_iter()
|
||||||
|
.filter(|(_name, indexes)| indexes.len() > 1)
|
||||||
|
.flat_map(|(_name, indexes)| indexes)
|
||||||
|
.collect();
|
||||||
|
// For consistent output.
|
||||||
|
dupes.sort_unstable();
|
||||||
|
dupes.into_iter().map(|(_node, i)| i).collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Builds the graph.
|
||||||
|
pub fn build<'a>(
|
||||||
|
ws: &Workspace<'_>,
|
||||||
|
resolve: &Resolve,
|
||||||
|
resolved_features: &ResolvedFeatures,
|
||||||
|
specs: &[PackageIdSpec],
|
||||||
|
requested_features: &RequestedFeatures,
|
||||||
|
target_data: &RustcTargetData,
|
||||||
|
requested_kind: CompileKind,
|
||||||
|
package_map: HashMap<PackageId, &'a Package>,
|
||||||
|
opts: &TreeOptions,
|
||||||
|
) -> CargoResult<Graph<'a>> {
|
||||||
|
let mut graph = Graph::new(package_map);
|
||||||
|
let mut members_with_features = ws.members_with_features(specs, requested_features)?;
|
||||||
|
members_with_features.sort_unstable_by_key(|e| e.0.package_id());
|
||||||
|
for (member, requested_features) in members_with_features {
|
||||||
|
let member_id = member.package_id();
|
||||||
|
let features_for = FeaturesFor::from_for_host(member.proc_macro());
|
||||||
|
let member_index = add_pkg(
|
||||||
|
&mut graph,
|
||||||
|
resolve,
|
||||||
|
resolved_features,
|
||||||
|
member_id,
|
||||||
|
features_for,
|
||||||
|
target_data,
|
||||||
|
requested_kind,
|
||||||
|
opts,
|
||||||
|
);
|
||||||
|
if opts.graph_features {
|
||||||
|
let fmap = resolve.summary(member_id).features();
|
||||||
|
add_cli_features(&mut graph, member_index, &requested_features, fmap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if opts.graph_features {
|
||||||
|
add_internal_features(&mut graph, resolve);
|
||||||
|
}
|
||||||
|
Ok(graph)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a single package node (if it does not already exist).
|
||||||
|
///
|
||||||
|
/// This will also recursively add all of its dependencies.
|
||||||
|
///
|
||||||
|
/// Returns the index to the package node.
|
||||||
|
fn add_pkg(
|
||||||
|
graph: &mut Graph<'_>,
|
||||||
|
resolve: &Resolve,
|
||||||
|
resolved_features: &ResolvedFeatures,
|
||||||
|
package_id: PackageId,
|
||||||
|
features_for: FeaturesFor,
|
||||||
|
target_data: &RustcTargetData,
|
||||||
|
requested_kind: CompileKind,
|
||||||
|
opts: &TreeOptions,
|
||||||
|
) -> usize {
|
||||||
|
let node_features = resolved_features.activated_features(package_id, features_for);
|
||||||
|
let node_kind = match features_for {
|
||||||
|
FeaturesFor::HostDep => CompileKind::Host,
|
||||||
|
FeaturesFor::NormalOrDev => requested_kind,
|
||||||
|
};
|
||||||
|
let node = Node::Package {
|
||||||
|
package_id,
|
||||||
|
features: node_features.clone(),
|
||||||
|
kind: node_kind,
|
||||||
|
};
|
||||||
|
if let Some(idx) = graph.index.get(&node) {
|
||||||
|
return *idx;
|
||||||
|
}
|
||||||
|
let from_index = graph.add_node(node);
|
||||||
|
// Compute the dep name map which is later used for foo/bar feature lookups.
|
||||||
|
let mut dep_name_map: HashMap<InternedString, HashSet<(usize, bool)>> = HashMap::new();
|
||||||
|
let mut deps: Vec<_> = resolve.deps(package_id).collect();
|
||||||
|
deps.sort_unstable_by_key(|(dep_id, _)| *dep_id);
|
||||||
|
for (dep_id, deps) in deps {
|
||||||
|
let mut deps: Vec<_> = deps
|
||||||
|
.iter()
|
||||||
|
// 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 !opts.no_filter_targets && !target_data.dep_platform_activated(dep, kind) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Filter out dev-dependencies if requested.
|
||||||
|
if opts.no_dev_dependencies && dep.kind() == DepKind::Development {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if dep.is_optional() {
|
||||||
|
// If the new feature resolver does not enable this
|
||||||
|
// optional dep, then don't use it.
|
||||||
|
if !node_features.contains(&dep.name_in_toml()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
deps.sort_unstable_by_key(|dep| dep.name_in_toml());
|
||||||
|
let dep_pkg = graph.package_map[&dep_id];
|
||||||
|
|
||||||
|
for dep in deps {
|
||||||
|
let dep_features_for = if dep.is_build() || dep_pkg.proc_macro() {
|
||||||
|
FeaturesFor::HostDep
|
||||||
|
} else {
|
||||||
|
features_for
|
||||||
|
};
|
||||||
|
let dep_index = add_pkg(
|
||||||
|
graph,
|
||||||
|
resolve,
|
||||||
|
resolved_features,
|
||||||
|
dep_id,
|
||||||
|
dep_features_for,
|
||||||
|
target_data,
|
||||||
|
requested_kind,
|
||||||
|
opts,
|
||||||
|
);
|
||||||
|
if opts.graph_features {
|
||||||
|
// Add the dependency node with feature nodes in-between.
|
||||||
|
dep_name_map
|
||||||
|
.entry(dep.name_in_toml())
|
||||||
|
.or_default()
|
||||||
|
.insert((dep_index, dep.is_optional()));
|
||||||
|
if dep.uses_default_features() {
|
||||||
|
add_feature(
|
||||||
|
graph,
|
||||||
|
InternedString::new("default"),
|
||||||
|
Some(from_index),
|
||||||
|
dep_index,
|
||||||
|
Edge::Dep(dep.kind()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for feature in dep.features() {
|
||||||
|
add_feature(
|
||||||
|
graph,
|
||||||
|
*feature,
|
||||||
|
Some(from_index),
|
||||||
|
dep_index,
|
||||||
|
Edge::Dep(dep.kind()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if !dep.uses_default_features() && dep.features().is_empty() {
|
||||||
|
// No features, use a direct connection.
|
||||||
|
graph.edges[from_index].add_edge(Edge::Dep(dep.kind()), dep_index);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
graph.edges[from_index].add_edge(Edge::Dep(dep.kind()), dep_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if opts.graph_features {
|
||||||
|
assert!(graph
|
||||||
|
.dep_name_map
|
||||||
|
.insert(from_index, dep_name_map)
|
||||||
|
.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
from_index
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a feature node between two nodes.
|
||||||
|
///
|
||||||
|
/// That is, it adds the following:
|
||||||
|
///
|
||||||
|
/// ```text
|
||||||
|
/// from -Edge-> featname -Edge::Feature-> to
|
||||||
|
/// ```
|
||||||
|
fn add_feature(
|
||||||
|
graph: &mut Graph<'_>,
|
||||||
|
name: InternedString,
|
||||||
|
from: Option<usize>,
|
||||||
|
to: usize,
|
||||||
|
kind: Edge,
|
||||||
|
) -> usize {
|
||||||
|
let node = Node::Feature {
|
||||||
|
node_index: to,
|
||||||
|
name,
|
||||||
|
};
|
||||||
|
let (node_index, _new) = match graph.index.get(&node) {
|
||||||
|
Some(idx) => (*idx, false),
|
||||||
|
None => (graph.add_node(node), true),
|
||||||
|
};
|
||||||
|
if let Some(from) = from {
|
||||||
|
graph.edges[from].add_edge(kind, node_index);
|
||||||
|
}
|
||||||
|
graph.edges[node_index].add_edge(Edge::Feature, to);
|
||||||
|
node_index
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds nodes for features requested on the command-line for the given member.
|
||||||
|
///
|
||||||
|
/// Feature nodes are added as "roots" (i.e., they have no "from" index),
|
||||||
|
/// because they come from the outside world. They usually only appear with
|
||||||
|
/// `--invert`.
|
||||||
|
fn add_cli_features(
|
||||||
|
graph: &mut Graph<'_>,
|
||||||
|
package_index: usize,
|
||||||
|
requested_features: &RequestedFeatures,
|
||||||
|
feature_map: &FeatureMap,
|
||||||
|
) {
|
||||||
|
// NOTE: Recursive enabling of features will be handled by
|
||||||
|
// add_internal_features.
|
||||||
|
|
||||||
|
// Create a list of feature names requested on the command-line.
|
||||||
|
let mut to_add: Vec<InternedString> = Vec::new();
|
||||||
|
if requested_features.all_features {
|
||||||
|
to_add.extend(feature_map.keys().copied());
|
||||||
|
// Add optional deps.
|
||||||
|
for (dep_name, deps) in &graph.dep_name_map[&package_index] {
|
||||||
|
if deps.iter().any(|(_idx, is_optional)| *is_optional) {
|
||||||
|
to_add.push(*dep_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if requested_features.uses_default_features {
|
||||||
|
to_add.push(InternedString::new("default"));
|
||||||
|
}
|
||||||
|
to_add.extend(requested_features.features.iter().copied());
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add each feature as a node, and mark as "from command-line" in graph.cli_features.
|
||||||
|
for name in to_add {
|
||||||
|
if name.contains('/') {
|
||||||
|
let mut parts = name.splitn(2, '/');
|
||||||
|
let dep_name = InternedString::new(parts.next().unwrap());
|
||||||
|
let feat_name = InternedString::new(parts.next().unwrap());
|
||||||
|
for (dep_index, is_optional) in graph.dep_name_map[&package_index][&dep_name].clone() {
|
||||||
|
if is_optional {
|
||||||
|
// Activate the optional dep on self.
|
||||||
|
let index = add_feature(graph, dep_name, None, package_index, Edge::Feature);
|
||||||
|
graph.cli_features.insert(index);
|
||||||
|
}
|
||||||
|
let index = add_feature(graph, feat_name, None, dep_index, Edge::Feature);
|
||||||
|
graph.cli_features.insert(index);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let index = add_feature(graph, name, None, package_index, Edge::Feature);
|
||||||
|
graph.cli_features.insert(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Recursively adds connections between features in the `[features]` table
|
||||||
|
/// for every package.
|
||||||
|
fn add_internal_features(graph: &mut Graph<'_>, resolve: &Resolve) {
|
||||||
|
// Collect features already activated by dependencies or command-line.
|
||||||
|
let feature_nodes: Vec<(PackageId, usize, usize, InternedString)> = graph
|
||||||
|
.nodes
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(|(i, node)| match node {
|
||||||
|
Node::Package { .. } => None,
|
||||||
|
Node::Feature { node_index, name } => {
|
||||||
|
let package_id = graph.package_id_for_index(*node_index);
|
||||||
|
Some((package_id, *node_index, i, *name))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
for (package_id, package_index, feature_index, feature_name) in feature_nodes {
|
||||||
|
add_feature_rec(
|
||||||
|
graph,
|
||||||
|
resolve,
|
||||||
|
feature_name,
|
||||||
|
package_id,
|
||||||
|
feature_index,
|
||||||
|
package_index,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Recursively add feature nodes for all features enabled by the given feature.
|
||||||
|
///
|
||||||
|
/// `from` is the index of the node that enables this feature.
|
||||||
|
/// `package_index` is the index of the package node for the feature.
|
||||||
|
fn add_feature_rec(
|
||||||
|
graph: &mut Graph<'_>,
|
||||||
|
resolve: &Resolve,
|
||||||
|
feature_name: InternedString,
|
||||||
|
package_id: PackageId,
|
||||||
|
from: usize,
|
||||||
|
package_index: usize,
|
||||||
|
) {
|
||||||
|
let feature_map = resolve.summary(package_id).features();
|
||||||
|
let fvs = match feature_map.get(&feature_name) {
|
||||||
|
Some(fvs) => fvs,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
for fv in fvs {
|
||||||
|
match fv {
|
||||||
|
FeatureValue::Feature(fv_name) | FeatureValue::Crate(fv_name) => {
|
||||||
|
let feat_index =
|
||||||
|
add_feature(graph, *fv_name, Some(from), package_index, Edge::Feature);
|
||||||
|
add_feature_rec(
|
||||||
|
graph,
|
||||||
|
resolve,
|
||||||
|
*fv_name,
|
||||||
|
package_id,
|
||||||
|
feat_index,
|
||||||
|
package_index,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
FeatureValue::CrateFeature(dep_name, fv_name) => {
|
||||||
|
let dep_indexes = match graph.dep_name_map[&package_index].get(dep_name) {
|
||||||
|
Some(indexes) => indexes.clone(),
|
||||||
|
None => {
|
||||||
|
log::debug!(
|
||||||
|
"enabling feature {} on {}, found {}/{}, \
|
||||||
|
dep appears to not be enabled",
|
||||||
|
feature_name,
|
||||||
|
package_id,
|
||||||
|
dep_name,
|
||||||
|
fv_name
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for (dep_index, is_optional) in dep_indexes {
|
||||||
|
let dep_pkg_id = graph.package_id_for_index(dep_index);
|
||||||
|
if is_optional {
|
||||||
|
// Activate the optional dep on self.
|
||||||
|
add_feature(graph, *dep_name, Some(from), package_index, Edge::Feature);
|
||||||
|
}
|
||||||
|
let feat_index =
|
||||||
|
add_feature(graph, *fv_name, Some(from), dep_index, Edge::Feature);
|
||||||
|
add_feature_rec(graph, resolve, *fv_name, dep_pkg_id, feat_index, dep_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
333
src/cargo/ops/tree/mod.rs
Normal file
333
src/cargo/ops/tree/mod.rs
Normal file
@ -0,0 +1,333 @@
|
|||||||
|
//! Implementation of `cargo tree`.
|
||||||
|
|
||||||
|
use self::format::Pattern;
|
||||||
|
use crate::core::compiler::{CompileKind, RustcTargetData};
|
||||||
|
use crate::core::dependency::DepKind;
|
||||||
|
use crate::core::resolver::{HasDevUnits, ResolveOpts};
|
||||||
|
use crate::core::{Package, PackageId, Workspace};
|
||||||
|
use crate::ops::{self, Packages};
|
||||||
|
use crate::util::CargoResult;
|
||||||
|
use anyhow::{bail, Context};
|
||||||
|
use graph::Graph;
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
mod format;
|
||||||
|
mod graph;
|
||||||
|
|
||||||
|
pub use {graph::Edge, graph::Node};
|
||||||
|
|
||||||
|
pub struct TreeOptions {
|
||||||
|
pub features: Vec<String>,
|
||||||
|
pub no_default_features: bool,
|
||||||
|
pub all_features: bool,
|
||||||
|
/// The packages to display the tree for.
|
||||||
|
pub packages: Packages,
|
||||||
|
/// The platform to filter for.
|
||||||
|
/// If `None`, use the host platform.
|
||||||
|
pub target: Option<String>,
|
||||||
|
/// If `true`, ignores the `target` field and returns all targets.
|
||||||
|
pub no_filter_targets: bool,
|
||||||
|
pub no_dev_dependencies: bool,
|
||||||
|
pub invert: bool,
|
||||||
|
/// Displays a list, with no indentation.
|
||||||
|
pub no_indent: bool,
|
||||||
|
/// Displays a list, with a number indicating the depth instead of using indentation.
|
||||||
|
pub prefix_depth: bool,
|
||||||
|
/// If `true`, duplicates will be repeated.
|
||||||
|
/// If `false`, duplicates will be marked with `*`, and their dependencies
|
||||||
|
/// won't be shown.
|
||||||
|
pub no_dedupe: bool,
|
||||||
|
/// If `true`, run in a special mode where it will scan for packages that
|
||||||
|
/// appear with different versions, and report if any where found. Implies
|
||||||
|
/// `invert`.
|
||||||
|
pub duplicates: bool,
|
||||||
|
/// The style of characters to use.
|
||||||
|
pub charset: Charset,
|
||||||
|
/// A format string indicating how each package should be displayed.
|
||||||
|
pub format: String,
|
||||||
|
/// Includes features in the tree as separate nodes.
|
||||||
|
pub graph_features: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
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"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
enum Prefix {
|
||||||
|
None,
|
||||||
|
Indent,
|
||||||
|
Depth,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Symbols {
|
||||||
|
down: &'static str,
|
||||||
|
tee: &'static str,
|
||||||
|
ell: &'static str,
|
||||||
|
right: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
static UTF8_SYMBOLS: Symbols = Symbols {
|
||||||
|
down: "│",
|
||||||
|
tee: "├",
|
||||||
|
ell: "└",
|
||||||
|
right: "─",
|
||||||
|
};
|
||||||
|
|
||||||
|
static ASCII_SYMBOLS: Symbols = Symbols {
|
||||||
|
down: "|",
|
||||||
|
tee: "|",
|
||||||
|
ell: "`",
|
||||||
|
right: "-",
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Entry point for the `cargo tree` command.
|
||||||
|
pub fn build_and_print(ws: &Workspace<'_>, opts: &TreeOptions) -> CargoResult<()> {
|
||||||
|
if opts.no_filter_targets && opts.target.is_some() {
|
||||||
|
bail!("cannot specify both `--target` and `--no-filter-targets`");
|
||||||
|
}
|
||||||
|
if opts.graph_features && opts.duplicates {
|
||||||
|
bail!("the `--graph-features` flag does not support `--duplicates`");
|
||||||
|
}
|
||||||
|
let requested_kind = CompileKind::from_requested_target(ws.config(), opts.target.as_deref())?;
|
||||||
|
let target_data = RustcTargetData::new(ws, requested_kind)?;
|
||||||
|
let specs = opts.packages.to_package_id_specs(ws)?;
|
||||||
|
let resolve_opts = ResolveOpts::new(
|
||||||
|
/*dev_deps*/ true,
|
||||||
|
&opts.features,
|
||||||
|
opts.all_features,
|
||||||
|
!opts.no_default_features,
|
||||||
|
);
|
||||||
|
let has_dev = if opts.no_dev_dependencies {
|
||||||
|
HasDevUnits::No
|
||||||
|
} else {
|
||||||
|
HasDevUnits::Yes
|
||||||
|
};
|
||||||
|
let ws_resolve = ops::resolve_ws_with_opts(
|
||||||
|
ws,
|
||||||
|
&target_data,
|
||||||
|
requested_kind,
|
||||||
|
&resolve_opts,
|
||||||
|
&specs,
|
||||||
|
has_dev,
|
||||||
|
)?;
|
||||||
|
// Download all Packages. Some display formats need to display package metadata.
|
||||||
|
let package_map: HashMap<PackageId, &Package> = ws_resolve
|
||||||
|
.pkg_set
|
||||||
|
.get_many(ws_resolve.pkg_set.package_ids())?
|
||||||
|
.into_iter()
|
||||||
|
.map(|pkg| (pkg.package_id(), pkg))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut graph = graph::build(
|
||||||
|
ws,
|
||||||
|
&ws_resolve.targeted_resolve,
|
||||||
|
&ws_resolve.resolved_features,
|
||||||
|
&specs,
|
||||||
|
&resolve_opts.features,
|
||||||
|
&target_data,
|
||||||
|
requested_kind,
|
||||||
|
package_map,
|
||||||
|
opts,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let root_ids = ws_resolve.targeted_resolve.specs_to_ids(&specs)?;
|
||||||
|
let root_indexes = graph.indexes_from_ids(&root_ids);
|
||||||
|
|
||||||
|
let root_indexes = if opts.duplicates {
|
||||||
|
// `-d -p foo` will only show duplicates within foo's subtree
|
||||||
|
graph = graph.from_reachable(root_indexes.as_slice());
|
||||||
|
graph.find_duplicates()
|
||||||
|
} else {
|
||||||
|
root_indexes
|
||||||
|
};
|
||||||
|
|
||||||
|
if opts.invert || opts.duplicates {
|
||||||
|
graph.invert();
|
||||||
|
}
|
||||||
|
|
||||||
|
print(opts, root_indexes, &graph)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prints a tree for each given root.
|
||||||
|
fn print(opts: &TreeOptions, roots: Vec<usize>, graph: &Graph<'_>) -> CargoResult<()> {
|
||||||
|
let format = Pattern::new(&opts.format)
|
||||||
|
.with_context(|| format!("tree format `{}` not valid", opts.format))?;
|
||||||
|
|
||||||
|
let symbols = match opts.charset {
|
||||||
|
Charset::Utf8 => &UTF8_SYMBOLS,
|
||||||
|
Charset::Ascii => &ASCII_SYMBOLS,
|
||||||
|
};
|
||||||
|
|
||||||
|
let prefix = if opts.prefix_depth {
|
||||||
|
Prefix::Depth
|
||||||
|
} else if opts.no_indent {
|
||||||
|
Prefix::None
|
||||||
|
} else {
|
||||||
|
Prefix::Indent
|
||||||
|
};
|
||||||
|
|
||||||
|
for (i, root_index) in roots.into_iter().enumerate() {
|
||||||
|
if i != 0 {
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
|
||||||
|
// The visited deps is used to display a (*) whenever a dep has
|
||||||
|
// already been printed (ignored with --no-dedupe).
|
||||||
|
let mut visited_deps = HashSet::new();
|
||||||
|
// A stack of bools used to determine where | symbols should appear
|
||||||
|
// when printing a line.
|
||||||
|
let mut levels_continue = vec![];
|
||||||
|
// The print stack is used to detect dependency cycles when
|
||||||
|
// --no-dedupe is used. It contains a Node for each level.
|
||||||
|
let mut print_stack = vec![];
|
||||||
|
|
||||||
|
print_node(
|
||||||
|
graph,
|
||||||
|
root_index,
|
||||||
|
&format,
|
||||||
|
symbols,
|
||||||
|
prefix,
|
||||||
|
opts.no_dedupe,
|
||||||
|
&mut visited_deps,
|
||||||
|
&mut levels_continue,
|
||||||
|
&mut print_stack,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prints a package and all of its dependencies.
|
||||||
|
fn print_node<'a>(
|
||||||
|
graph: &'a Graph<'_>,
|
||||||
|
node_index: usize,
|
||||||
|
format: &Pattern,
|
||||||
|
symbols: &Symbols,
|
||||||
|
prefix: Prefix,
|
||||||
|
no_dedupe: bool,
|
||||||
|
visited_deps: &mut HashSet<usize>,
|
||||||
|
levels_continue: &mut Vec<bool>,
|
||||||
|
print_stack: &mut Vec<usize>,
|
||||||
|
) {
|
||||||
|
let new = no_dedupe || visited_deps.insert(node_index);
|
||||||
|
|
||||||
|
match prefix {
|
||||||
|
Prefix::Depth => print!("{}", levels_continue.len()),
|
||||||
|
Prefix::Indent => {
|
||||||
|
if let Some((last_continues, rest)) = levels_continue.split_last() {
|
||||||
|
for continues in rest {
|
||||||
|
let c = if *continues { symbols.down } else { " " };
|
||||||
|
print!("{} ", c);
|
||||||
|
}
|
||||||
|
|
||||||
|
let c = if *last_continues {
|
||||||
|
symbols.tee
|
||||||
|
} else {
|
||||||
|
symbols.ell
|
||||||
|
};
|
||||||
|
print!("{0}{1}{1} ", c, symbols.right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Prefix::None => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
let in_cycle = print_stack.contains(&node_index);
|
||||||
|
let star = if new && !in_cycle { "" } else { " (*)" };
|
||||||
|
println!("{}{}", format.display(graph, node_index), star);
|
||||||
|
|
||||||
|
if !new || in_cycle {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
print_stack.push(node_index);
|
||||||
|
|
||||||
|
for kind in &[
|
||||||
|
Edge::Dep(DepKind::Normal),
|
||||||
|
Edge::Dep(DepKind::Build),
|
||||||
|
Edge::Dep(DepKind::Development),
|
||||||
|
Edge::Feature,
|
||||||
|
] {
|
||||||
|
print_dependencies(
|
||||||
|
graph,
|
||||||
|
node_index,
|
||||||
|
format,
|
||||||
|
symbols,
|
||||||
|
prefix,
|
||||||
|
no_dedupe,
|
||||||
|
visited_deps,
|
||||||
|
levels_continue,
|
||||||
|
print_stack,
|
||||||
|
kind,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
print_stack.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prints all the dependencies of a package for the given dependency kind.
|
||||||
|
fn print_dependencies<'a>(
|
||||||
|
graph: &'a Graph<'_>,
|
||||||
|
node_index: usize,
|
||||||
|
format: &Pattern,
|
||||||
|
symbols: &Symbols,
|
||||||
|
prefix: Prefix,
|
||||||
|
no_dedupe: bool,
|
||||||
|
visited_deps: &mut HashSet<usize>,
|
||||||
|
levels_continue: &mut Vec<bool>,
|
||||||
|
print_stack: &mut Vec<usize>,
|
||||||
|
kind: &Edge,
|
||||||
|
) {
|
||||||
|
let deps = match graph.connected_nodes(node_index, kind) {
|
||||||
|
Some(deps) => deps,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
let name = match kind {
|
||||||
|
Edge::Dep(DepKind::Normal) => None,
|
||||||
|
Edge::Dep(DepKind::Build) => Some("[build-dependencies]"),
|
||||||
|
Edge::Dep(DepKind::Development) => Some("[dev-dependencies]"),
|
||||||
|
Edge::Feature => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Prefix::Indent = prefix {
|
||||||
|
if let Some(name) = name {
|
||||||
|
for continues in &**levels_continue {
|
||||||
|
let c = if *continues { symbols.down } else { " " };
|
||||||
|
print!("{} ", c);
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("{}", name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut it = deps.iter().peekable();
|
||||||
|
while let Some(dependency) = it.next() {
|
||||||
|
levels_continue.push(it.peek().is_some());
|
||||||
|
print_node(
|
||||||
|
graph,
|
||||||
|
*dependency,
|
||||||
|
format,
|
||||||
|
symbols,
|
||||||
|
prefix,
|
||||||
|
no_dedupe,
|
||||||
|
visited_deps,
|
||||||
|
levels_continue,
|
||||||
|
print_stack,
|
||||||
|
);
|
||||||
|
levels_continue.pop();
|
||||||
|
}
|
||||||
|
}
|
@ -37,6 +37,20 @@ pub trait AppExt: Sized {
|
|||||||
._arg(multi_opt("exclude", "SPEC", exclude))
|
._arg(multi_opt("exclude", "SPEC", exclude))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Variant of arg_package_spec that does not include the `--all` flag
|
||||||
|
/// (but does include `--workspace`). Used to avoid confusion with
|
||||||
|
/// historical uses of `--all`.
|
||||||
|
fn arg_package_spec_no_all(
|
||||||
|
self,
|
||||||
|
package: &'static str,
|
||||||
|
all: &'static str,
|
||||||
|
exclude: &'static str,
|
||||||
|
) -> Self {
|
||||||
|
self.arg_package_spec_simple(package)
|
||||||
|
._arg(opt("workspace", all))
|
||||||
|
._arg(multi_opt("exclude", "SPEC", exclude))
|
||||||
|
}
|
||||||
|
|
||||||
fn arg_package_spec_simple(self, package: &'static str) -> Self {
|
fn arg_package_spec_simple(self, package: &'static str) -> Self {
|
||||||
self._arg(multi_opt("package", "SPEC", package).short("p"))
|
self._arg(multi_opt("package", "SPEC", package).short("p"))
|
||||||
}
|
}
|
||||||
@ -362,6 +376,15 @@ pub trait ArgMatchesExt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn packages_from_flags(&self) -> CargoResult<Packages> {
|
||||||
|
Packages::from_flags(
|
||||||
|
// TODO Integrate into 'workspace'
|
||||||
|
self._is_present("workspace") || self._is_present("all"),
|
||||||
|
self._values_of("exclude"),
|
||||||
|
self._values_of("package"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn compile_options(
|
fn compile_options(
|
||||||
&self,
|
&self,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
@ -369,13 +392,7 @@ pub trait ArgMatchesExt {
|
|||||||
workspace: Option<&Workspace<'_>>,
|
workspace: Option<&Workspace<'_>>,
|
||||||
profile_checking: ProfileChecking,
|
profile_checking: ProfileChecking,
|
||||||
) -> CargoResult<CompileOptions> {
|
) -> CargoResult<CompileOptions> {
|
||||||
let spec = Packages::from_flags(
|
let spec = self.packages_from_flags()?;
|
||||||
// TODO Integrate into 'workspace'
|
|
||||||
self._is_present("workspace") || self._is_present("all"),
|
|
||||||
self._values_of("exclude"),
|
|
||||||
self._values_of("package"),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let mut message_format = None;
|
let mut message_format = None;
|
||||||
let default_json = MessageFormat::Json {
|
let default_json = MessageFormat::Json {
|
||||||
short: false,
|
short: false,
|
||||||
|
193
src/doc/man/cargo-tree.adoc
Normal file
193
src/doc/man/cargo-tree.adoc
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
= cargo-tree(1)
|
||||||
|
:idprefix: cargo_tree_
|
||||||
|
:doctype: manpage
|
||||||
|
:actionverb: Display
|
||||||
|
:noall: true
|
||||||
|
|
||||||
|
== NAME
|
||||||
|
|
||||||
|
cargo-tree - Display a tree visualization of a dependency graph
|
||||||
|
|
||||||
|
== SYNOPSIS
|
||||||
|
|
||||||
|
`cargo tree [_OPTIONS_]`
|
||||||
|
|
||||||
|
== DESCRIPTION
|
||||||
|
|
||||||
|
This command will display a tree of dependencies to the terminal. An example of a simple project that depends on the "rand" package:
|
||||||
|
|
||||||
|
----
|
||||||
|
myproject v0.1.0 (/myproject)
|
||||||
|
└── rand v0.7.3
|
||||||
|
├── getrandom v0.1.14
|
||||||
|
│ ├── cfg-if v0.1.10
|
||||||
|
│ └── libc v0.2.68
|
||||||
|
├── libc v0.2.68 (*)
|
||||||
|
├── rand_chacha v0.2.2
|
||||||
|
│ ├── ppv-lite86 v0.2.6
|
||||||
|
│ └── rand_core v0.5.1
|
||||||
|
│ └── getrandom v0.1.14 (*)
|
||||||
|
└── rand_core v0.5.1 (*)
|
||||||
|
[build-dependencies]
|
||||||
|
└── cc v1.0.50
|
||||||
|
----
|
||||||
|
|
||||||
|
Packages marked with `(*)` have been "de-duplicated". The dependencies for the
|
||||||
|
package have already been shown elswhere in the graph, and so are not
|
||||||
|
repeated. Use the `--no-dedupe` option to repeat the duplicates.
|
||||||
|
|
||||||
|
== OPTIONS
|
||||||
|
|
||||||
|
=== Tree Options
|
||||||
|
|
||||||
|
*-i*::
|
||||||
|
*--invert*::
|
||||||
|
Invert the tree. Typically this flag is used with the `-p` flag to show
|
||||||
|
the dependencies for a specific package.
|
||||||
|
|
||||||
|
*--no-dedupe*::
|
||||||
|
Do not de-duplicate repeated dependencies. Usually, when a package has
|
||||||
|
already displayed its dependencies, further occurrences will not
|
||||||
|
re-display its dependencies, and will include a `(*)` to indicate it has
|
||||||
|
already been shown. This flag will cause those duplicates to be repeated.
|
||||||
|
|
||||||
|
*-d*::
|
||||||
|
*--duplicates*::
|
||||||
|
Show only dependencies which come in multiple versions (implies
|
||||||
|
`--invert`). When used with the `-p` flag, only shows duplicates within
|
||||||
|
the subtree of the given package.
|
||||||
|
+
|
||||||
|
It can be beneficial for build times and executable sizes to avoid building
|
||||||
|
that same package multiple times. This flag can help identify the offending
|
||||||
|
packages. You can then investigate if the package that depends on the
|
||||||
|
duplicate with the older version can be updated to the newer version so that
|
||||||
|
only one instance is built.
|
||||||
|
|
||||||
|
*--no-dev-dependencies*::
|
||||||
|
Do not include dev-dependencies.
|
||||||
|
|
||||||
|
*--target* _TRIPLE_::
|
||||||
|
Filter dependencies matching the given target-triple.
|
||||||
|
The default is the host platform.
|
||||||
|
|
||||||
|
*--no-filter-targets*::
|
||||||
|
Show dependencies for all target platforms. Cannot be specified with
|
||||||
|
`--target`.
|
||||||
|
|
||||||
|
*--graph-features*::
|
||||||
|
Runs in a special mode where features are included as individual nodes.
|
||||||
|
This is intended to be used to help explain why a feature is enabled on
|
||||||
|
any particular package. It is recommended to use with the `-p` and `-i`
|
||||||
|
flags to show how the features flow into the package. See the examples
|
||||||
|
below for more detail.
|
||||||
|
|
||||||
|
=== Tree Formatting Options
|
||||||
|
|
||||||
|
*--charset* _CHARSET_::
|
||||||
|
Chooses the character set to use for the tree. Valid values are "utf8" or
|
||||||
|
"ascii". Default is "utf8".
|
||||||
|
|
||||||
|
*-f* _FORMAT_::
|
||||||
|
*--format* _FORMAT_::
|
||||||
|
Set the format string for each package. The default is "{p}".
|
||||||
|
+
|
||||||
|
This is an arbitrary string which will be used to display each package. The following
|
||||||
|
strings will be replaced with the corresponding value:
|
||||||
|
+
|
||||||
|
- `{p}` — The package name.
|
||||||
|
- `{l}` — The package license.
|
||||||
|
- `{r}` — The package repository URL.
|
||||||
|
- `{f}` — Comma-separated list of package features that are enabled.
|
||||||
|
|
||||||
|
*--no-indent*::
|
||||||
|
Display the dependencies as a list (rather than a tree).
|
||||||
|
|
||||||
|
*--prefix-depth*::
|
||||||
|
Display the dependencies as a list (rather than a tree), but prefixed with
|
||||||
|
the depth.
|
||||||
|
|
||||||
|
=== Package Selection
|
||||||
|
|
||||||
|
include::options-packages.adoc[]
|
||||||
|
|
||||||
|
=== Manifest Options
|
||||||
|
|
||||||
|
include::options-manifest-path.adoc[]
|
||||||
|
|
||||||
|
include::options-features.adoc[]
|
||||||
|
|
||||||
|
=== Display Options
|
||||||
|
|
||||||
|
include::options-display.adoc[]
|
||||||
|
|
||||||
|
=== Common Options
|
||||||
|
|
||||||
|
include::options-common.adoc[]
|
||||||
|
|
||||||
|
include::options-locked.adoc[]
|
||||||
|
|
||||||
|
include::section-environment.adoc[]
|
||||||
|
|
||||||
|
include::section-exit-status.adoc[]
|
||||||
|
|
||||||
|
== EXAMPLES
|
||||||
|
|
||||||
|
. Display the tree for the package in the current directory:
|
||||||
|
|
||||||
|
cargo tree
|
||||||
|
|
||||||
|
. Display all the packages that depend on the specified package:
|
||||||
|
|
||||||
|
cargo tree -i -p syn
|
||||||
|
|
||||||
|
. Show the features enabled on each package:
|
||||||
|
|
||||||
|
cargo tree --format "{p} {f}"
|
||||||
|
|
||||||
|
. Show all packages that are built multiple times. This can happen if multiple
|
||||||
|
semver-incompatible versions appear in the tree (like 1.0.0 and 2.0.0).
|
||||||
|
|
||||||
|
cargo tree -d
|
||||||
|
|
||||||
|
. Explain why features are enabled for the given package:
|
||||||
|
|
||||||
|
cargo tree --graph-features -i -p syn
|
||||||
|
+
|
||||||
|
An example of what this would display:
|
||||||
|
+
|
||||||
|
----
|
||||||
|
syn v1.0.17
|
||||||
|
├── syn feature "clone-impls"
|
||||||
|
│ └── syn feature "default"
|
||||||
|
│ └── rustversion v1.0.2
|
||||||
|
│ └── rustversion feature "default"
|
||||||
|
│ └── myproject v0.1.0 (/myproject)
|
||||||
|
│ └── myproject feature "default" (command-line)
|
||||||
|
├── syn feature "default" (*)
|
||||||
|
├── syn feature "derive"
|
||||||
|
│ └── syn feature "default" (*)
|
||||||
|
├── syn feature "full"
|
||||||
|
│ └── rustversion v1.0.2 (*)
|
||||||
|
├── syn feature "parsing"
|
||||||
|
│ └── syn feature "default" (*)
|
||||||
|
├── syn feature "printing"
|
||||||
|
│ └── syn feature "default" (*)
|
||||||
|
├── syn feature "proc-macro"
|
||||||
|
│ └── syn feature "default" (*)
|
||||||
|
└── syn feature "quote"
|
||||||
|
├── syn feature "printing" (*)
|
||||||
|
└── syn feature "proc-macro" (*)
|
||||||
|
----
|
||||||
|
+
|
||||||
|
To read this graph, you can follow the chain for each feature from the root to
|
||||||
|
see why it was included. For example, the "full" feature was added by the
|
||||||
|
`rustversion` crate which was included from `myproject` (with the default
|
||||||
|
features), and `myproject` was the package selected on the command-line. All
|
||||||
|
of the other `syn` features are added by the "default" feature ("quote" is
|
||||||
|
added by "printing" and "proc-macro", both of which are default features).
|
||||||
|
+
|
||||||
|
If you're having difficulty cross-referencing the de-duplicated `(*)` entries,
|
||||||
|
try with the `--no-dedupe` flag to get the full output.
|
||||||
|
|
||||||
|
== SEE ALSO
|
||||||
|
man:cargo[1], man:cargo-metadata[1]
|
@ -71,6 +71,9 @@ man:cargo-metadata[1]::
|
|||||||
man:cargo-pkgid[1]::
|
man:cargo-pkgid[1]::
|
||||||
Print a fully qualified package specification.
|
Print a fully qualified package specification.
|
||||||
|
|
||||||
|
man:cargo-tree[1]::
|
||||||
|
Display a tree visualization of a dependency graph.
|
||||||
|
|
||||||
man:cargo-update[1]::
|
man:cargo-update[1]::
|
||||||
Update dependencies as recorded in the local lock file.
|
Update dependencies as recorded in the local lock file.
|
||||||
|
|
||||||
|
443
src/doc/man/generated/cargo-tree.html
Normal file
443
src/doc/man/generated/cargo-tree.html
Normal file
@ -0,0 +1,443 @@
|
|||||||
|
<h2 id="cargo_tree_name">NAME</h2>
|
||||||
|
<div class="sectionbody">
|
||||||
|
<p>cargo-tree - Display a tree visualization of a dependency graph</p>
|
||||||
|
</div>
|
||||||
|
<div class="sect1">
|
||||||
|
<h2 id="cargo_tree_synopsis">SYNOPSIS</h2>
|
||||||
|
<div class="sectionbody">
|
||||||
|
<div class="paragraph">
|
||||||
|
<p><code>cargo tree [<em>OPTIONS</em>]</code></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sect1">
|
||||||
|
<h2 id="cargo_tree_description">DESCRIPTION</h2>
|
||||||
|
<div class="sectionbody">
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>This command will display a tree of dependencies to the terminal. An example of a simple project that depends on the "rand" package:</p>
|
||||||
|
</div>
|
||||||
|
<div class="listingblock">
|
||||||
|
<div class="content">
|
||||||
|
<pre>myproject v0.1.0 (/myproject)
|
||||||
|
└── rand v0.7.3
|
||||||
|
├── getrandom v0.1.14
|
||||||
|
│ ├── cfg-if v0.1.10
|
||||||
|
│ └── libc v0.2.68
|
||||||
|
├── libc v0.2.68 (*)
|
||||||
|
├── rand_chacha v0.2.2
|
||||||
|
│ ├── ppv-lite86 v0.2.6
|
||||||
|
│ └── rand_core v0.5.1
|
||||||
|
│ └── getrandom v0.1.14 (*)
|
||||||
|
└── rand_core v0.5.1 (*)
|
||||||
|
[build-dependencies]
|
||||||
|
└── cc v1.0.50</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Packages marked with <code>(*)</code> have been "de-duplicated". The dependencies for the
|
||||||
|
package have already been shown elswhere in the graph, and so are not
|
||||||
|
repeated. Use the <code>--no-dedupe</code> option to repeat the duplicates.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sect1">
|
||||||
|
<h2 id="cargo_tree_options">OPTIONS</h2>
|
||||||
|
<div class="sectionbody">
|
||||||
|
<div class="sect2">
|
||||||
|
<h3 id="cargo_tree_tree_options">Tree Options</h3>
|
||||||
|
<div class="dlist">
|
||||||
|
<dl>
|
||||||
|
<dt class="hdlist1"><strong>-i</strong></dt>
|
||||||
|
<dt class="hdlist1"><strong>--invert</strong></dt>
|
||||||
|
<dd>
|
||||||
|
<p>Invert the tree. Typically this flag is used with the <code>-p</code> flag to show
|
||||||
|
the dependencies for a specific package.</p>
|
||||||
|
</dd>
|
||||||
|
<dt class="hdlist1"><strong>--no-dedupe</strong></dt>
|
||||||
|
<dd>
|
||||||
|
<p>Do not de-duplicate repeated dependencies. Usually, when a package has
|
||||||
|
already displayed its dependencies, further occurrences will not
|
||||||
|
re-display its dependencies, and will include a <code>(*)</code> to indicate it has
|
||||||
|
already been shown. This flag will cause those duplicates to be repeated.</p>
|
||||||
|
</dd>
|
||||||
|
<dt class="hdlist1"><strong>-d</strong></dt>
|
||||||
|
<dt class="hdlist1"><strong>--duplicates</strong></dt>
|
||||||
|
<dd>
|
||||||
|
<p>Show only dependencies which come in multiple versions (implies
|
||||||
|
<code>--invert</code>). When used with the <code>-p</code> flag, only shows duplicates within
|
||||||
|
the subtree of the given package.</p>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>It can be beneficial for build times and executable sizes to avoid building
|
||||||
|
that same package multiple times. This flag can help identify the offending
|
||||||
|
packages. You can then investigate if the package that depends on the
|
||||||
|
duplicate with the older version can be updated to the newer version so that
|
||||||
|
only one instance is built.</p>
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
<dt class="hdlist1"><strong>--no-dev-dependencies</strong></dt>
|
||||||
|
<dd>
|
||||||
|
<p>Do not include dev-dependencies.</p>
|
||||||
|
</dd>
|
||||||
|
<dt class="hdlist1"><strong>--target</strong> <em>TRIPLE</em></dt>
|
||||||
|
<dd>
|
||||||
|
<p>Filter dependencies matching the given target-triple.
|
||||||
|
The default is the host platform.</p>
|
||||||
|
</dd>
|
||||||
|
<dt class="hdlist1"><strong>--no-filter-targets</strong></dt>
|
||||||
|
<dd>
|
||||||
|
<p>Show dependencies for all target platforms. Cannot be specified with
|
||||||
|
<code>--target</code>.</p>
|
||||||
|
</dd>
|
||||||
|
<dt class="hdlist1"><strong>--graph-features</strong></dt>
|
||||||
|
<dd>
|
||||||
|
<p>Runs in a special mode where features are included as individual nodes.
|
||||||
|
This is intended to be used to help explain why a feature is enabled on
|
||||||
|
any particular package. It is recommended to use with the <code>-p</code> and <code>-i</code>
|
||||||
|
flags to show how the features flow into the package. See the examples
|
||||||
|
below for more detail.</p>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sect2">
|
||||||
|
<h3 id="cargo_tree_tree_formatting_options">Tree Formatting Options</h3>
|
||||||
|
<div class="dlist">
|
||||||
|
<dl>
|
||||||
|
<dt class="hdlist1"><strong>--charset</strong> <em>CHARSET</em></dt>
|
||||||
|
<dd>
|
||||||
|
<p>Chooses the character set to use for the tree. Valid values are "utf8" or
|
||||||
|
"ascii". Default is "utf8".</p>
|
||||||
|
</dd>
|
||||||
|
<dt class="hdlist1"><strong>-f</strong> <em>FORMAT</em></dt>
|
||||||
|
<dt class="hdlist1"><strong>--format</strong> <em>FORMAT</em></dt>
|
||||||
|
<dd>
|
||||||
|
<p>Set the format string for each package. The default is "{p}".</p>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>This is an arbitrary string which will be used to display each package. The following
|
||||||
|
strings will be replaced with the corresponding value:</p>
|
||||||
|
</div>
|
||||||
|
<div class="ulist">
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<p><code>{p}</code> — The package name.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><code>{l}</code> — The package license.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><code>{r}</code> — The package repository URL.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><code>{f}</code> — Comma-separated list of package features that are enabled.</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
<dt class="hdlist1"><strong>--no-indent</strong></dt>
|
||||||
|
<dd>
|
||||||
|
<p>Display the dependencies as a list (rather than a tree).</p>
|
||||||
|
</dd>
|
||||||
|
<dt class="hdlist1"><strong>--prefix-depth</strong></dt>
|
||||||
|
<dd>
|
||||||
|
<p>Display the dependencies as a list (rather than a tree), but prefixed with
|
||||||
|
the depth.</p>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sect2">
|
||||||
|
<h3 id="cargo_tree_package_selection">Package Selection</h3>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>By default, when no package selection options are given, the packages selected
|
||||||
|
depend on the selected manifest file (based on the current working directory if
|
||||||
|
<code>--manifest-path</code> is not given). If the manifest is the root of a workspace then
|
||||||
|
the workspaces default members are selected, otherwise only the package defined
|
||||||
|
by the manifest will be selected.</p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>The default members of a workspace can be set explicitly with the
|
||||||
|
<code>workspace.default-members</code> key in the root manifest. If this is not set, a
|
||||||
|
virtual workspace will include all workspace members (equivalent to passing
|
||||||
|
<code>--workspace</code>), and a non-virtual workspace will include only the root crate itself.</p>
|
||||||
|
</div>
|
||||||
|
<div class="dlist">
|
||||||
|
<dl>
|
||||||
|
<dt class="hdlist1"><strong>-p</strong> <em>SPEC</em>…​</dt>
|
||||||
|
<dt class="hdlist1"><strong>--package</strong> <em>SPEC</em>…​</dt>
|
||||||
|
<dd>
|
||||||
|
<p>Display only the specified packages. See <a href="cargo-pkgid.html">cargo-pkgid(1)</a> for the
|
||||||
|
SPEC format. This flag may be specified multiple times.</p>
|
||||||
|
</dd>
|
||||||
|
<dt class="hdlist1"><strong>--workspace</strong></dt>
|
||||||
|
<dd>
|
||||||
|
<p>Display all members in the workspace.</p>
|
||||||
|
</dd>
|
||||||
|
<dt class="hdlist1"><strong>--exclude</strong> <em>SPEC</em>…​</dt>
|
||||||
|
<dd>
|
||||||
|
<p>Exclude the specified packages. Must be used in conjunction with the
|
||||||
|
<code>--workspace</code> flag. This flag may be specified multiple times.</p>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sect2">
|
||||||
|
<h3 id="cargo_tree_manifest_options">Manifest Options</h3>
|
||||||
|
<div class="dlist">
|
||||||
|
<dl>
|
||||||
|
<dt class="hdlist1"><strong>--manifest-path</strong> <em>PATH</em></dt>
|
||||||
|
<dd>
|
||||||
|
<p>Path to the <code>Cargo.toml</code> file. By default, Cargo searches for the
|
||||||
|
<code>Cargo.toml</code> file in the current directory or any parent directory.</p>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sect2">
|
||||||
|
<h3 id="cargo_tree_feature_selection">Feature Selection</h3>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>The feature flags allow you to control the enabled features for the "current"
|
||||||
|
package. The "current" package is the package in the current directory, or the
|
||||||
|
one specified in <code>--manifest-path</code>. If running in the root of a virtual
|
||||||
|
workspace, then the default features are selected for all workspace members,
|
||||||
|
or all features if <code>--all-features</code> is specified.</p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>When no feature options are given, the <code>default</code> feature is activated for
|
||||||
|
every selected package.</p>
|
||||||
|
</div>
|
||||||
|
<div class="dlist">
|
||||||
|
<dl>
|
||||||
|
<dt class="hdlist1"><strong>--features</strong> <em>FEATURES</em></dt>
|
||||||
|
<dd>
|
||||||
|
<p>Space or comma separated list of features to activate. These features only
|
||||||
|
apply to the current directory’s package. Features of direct dependencies
|
||||||
|
may be enabled with <code><dep-name>/<feature-name></code> syntax. This flag may be
|
||||||
|
specified multiple times, which enables all specified features.</p>
|
||||||
|
</dd>
|
||||||
|
<dt class="hdlist1"><strong>--all-features</strong></dt>
|
||||||
|
<dd>
|
||||||
|
<p>Activate all available features of all selected packages.</p>
|
||||||
|
</dd>
|
||||||
|
<dt class="hdlist1"><strong>--no-default-features</strong></dt>
|
||||||
|
<dd>
|
||||||
|
<p>Do not activate the <code>default</code> feature of the current directory’s
|
||||||
|
package.</p>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sect2">
|
||||||
|
<h3 id="cargo_tree_display_options">Display Options</h3>
|
||||||
|
<div class="dlist">
|
||||||
|
<dl>
|
||||||
|
<dt class="hdlist1"><strong>-v</strong></dt>
|
||||||
|
<dt class="hdlist1"><strong>--verbose</strong></dt>
|
||||||
|
<dd>
|
||||||
|
<p>Use verbose output. May be specified twice for "very verbose" output which
|
||||||
|
includes extra output such as dependency warnings and build script output.
|
||||||
|
May also be specified with the <code>term.verbose</code>
|
||||||
|
<a href="../reference/config.html">config value</a>.</p>
|
||||||
|
</dd>
|
||||||
|
<dt class="hdlist1"><strong>-q</strong></dt>
|
||||||
|
<dt class="hdlist1"><strong>--quiet</strong></dt>
|
||||||
|
<dd>
|
||||||
|
<p>No output printed to stdout.</p>
|
||||||
|
</dd>
|
||||||
|
<dt class="hdlist1"><strong>--color</strong> <em>WHEN</em></dt>
|
||||||
|
<dd>
|
||||||
|
<p>Control when colored output is used. Valid values:</p>
|
||||||
|
<div class="ulist">
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<p><code>auto</code> (default): Automatically detect if color support is available on the
|
||||||
|
terminal.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><code>always</code>: Always display colors.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><code>never</code>: Never display colors.</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>May also be specified with the <code>term.color</code>
|
||||||
|
<a href="../reference/config.html">config value</a>.</p>
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sect2">
|
||||||
|
<h3 id="cargo_tree_common_options">Common Options</h3>
|
||||||
|
<div class="dlist">
|
||||||
|
<dl>
|
||||||
|
<dt class="hdlist1"><strong>-h</strong></dt>
|
||||||
|
<dt class="hdlist1"><strong>--help</strong></dt>
|
||||||
|
<dd>
|
||||||
|
<p>Prints help information.</p>
|
||||||
|
</dd>
|
||||||
|
<dt class="hdlist1"><strong>-Z</strong> <em>FLAG</em>…​</dt>
|
||||||
|
<dd>
|
||||||
|
<p>Unstable (nightly-only) flags to Cargo. Run <code>cargo -Z help</code> for
|
||||||
|
details.</p>
|
||||||
|
</dd>
|
||||||
|
<dt class="hdlist1"><strong>--frozen</strong></dt>
|
||||||
|
<dt class="hdlist1"><strong>--locked</strong></dt>
|
||||||
|
<dd>
|
||||||
|
<p>Either of these flags requires that the <code>Cargo.lock</code> file is
|
||||||
|
up-to-date. If the lock file is missing, or it needs to be updated, Cargo will
|
||||||
|
exit with an error. The <code>--frozen</code> flag also prevents Cargo from
|
||||||
|
attempting to access the network to determine if it is out-of-date.</p>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>These may be used in environments where you want to assert that the
|
||||||
|
<code>Cargo.lock</code> file is up-to-date (such as a CI build) or want to avoid network
|
||||||
|
access.</p>
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
<dt class="hdlist1"><strong>--offline</strong></dt>
|
||||||
|
<dd>
|
||||||
|
<p>Prevents Cargo from accessing the network for any reason. Without this
|
||||||
|
flag, Cargo will stop with an error if it needs to access the network and
|
||||||
|
the network is not available. With this flag, Cargo will attempt to
|
||||||
|
proceed without the network if possible.</p>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Beware that this may result in different dependency resolution than online
|
||||||
|
mode. Cargo will restrict itself to crates that are downloaded locally, even
|
||||||
|
if there might be a newer version as indicated in the local copy of the index.
|
||||||
|
See the <a href="cargo-fetch.html">cargo-fetch(1)</a> command to download dependencies before going
|
||||||
|
offline.</p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>May also be specified with the <code>net.offline</code> <a href="../reference/config.html">config value</a>.</p>
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sect1">
|
||||||
|
<h2 id="cargo_tree_environment">ENVIRONMENT</h2>
|
||||||
|
<div class="sectionbody">
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>See <a href="../reference/environment-variables.html">the reference</a> for
|
||||||
|
details on environment variables that Cargo reads.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sect1">
|
||||||
|
<h2 id="cargo_tree_exit_status">Exit Status</h2>
|
||||||
|
<div class="sectionbody">
|
||||||
|
<div class="dlist">
|
||||||
|
<dl>
|
||||||
|
<dt class="hdlist1">0</dt>
|
||||||
|
<dd>
|
||||||
|
<p>Cargo succeeded.</p>
|
||||||
|
</dd>
|
||||||
|
<dt class="hdlist1">101</dt>
|
||||||
|
<dd>
|
||||||
|
<p>Cargo failed to complete.</p>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sect1">
|
||||||
|
<h2 id="cargo_tree_examples">EXAMPLES</h2>
|
||||||
|
<div class="sectionbody">
|
||||||
|
<div class="olist arabic">
|
||||||
|
<ol class="arabic">
|
||||||
|
<li>
|
||||||
|
<p>Display the tree for the package in the current directory:</p>
|
||||||
|
<div class="literalblock">
|
||||||
|
<div class="content">
|
||||||
|
<pre>cargo tree</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Display all the packages that depend on the specified package:</p>
|
||||||
|
<div class="literalblock">
|
||||||
|
<div class="content">
|
||||||
|
<pre>cargo tree -i -p syn</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Show the features enabled on each package:</p>
|
||||||
|
<div class="literalblock">
|
||||||
|
<div class="content">
|
||||||
|
<pre>cargo tree --format "{p} {f}"</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Show all packages that are built multiple times. This can happen if multiple
|
||||||
|
semver-incompatible versions appear in the tree (like 1.0.0 and 2.0.0).</p>
|
||||||
|
<div class="literalblock">
|
||||||
|
<div class="content">
|
||||||
|
<pre>cargo tree -d</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Explain why features are enabled for the given package:</p>
|
||||||
|
<div class="literalblock">
|
||||||
|
<div class="content">
|
||||||
|
<pre>cargo tree --graph-features -i -p syn</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>An example of what this would display:</p>
|
||||||
|
</div>
|
||||||
|
<div class="listingblock">
|
||||||
|
<div class="content">
|
||||||
|
<pre>syn v1.0.17
|
||||||
|
├── syn feature "clone-impls"
|
||||||
|
│ └── syn feature "default"
|
||||||
|
│ └── rustversion v1.0.2
|
||||||
|
│ └── rustversion feature "default"
|
||||||
|
│ └── myproject v0.1.0 (/myproject)
|
||||||
|
│ └── myproject feature "default" (command-line)
|
||||||
|
├── syn feature "default" (*)
|
||||||
|
├── syn feature "derive"
|
||||||
|
│ └── syn feature "default" (*)
|
||||||
|
├── syn feature "full"
|
||||||
|
│ └── rustversion v1.0.2 (*)
|
||||||
|
├── syn feature "parsing"
|
||||||
|
│ └── syn feature "default" (*)
|
||||||
|
├── syn feature "printing"
|
||||||
|
│ └── syn feature "default" (*)
|
||||||
|
├── syn feature "proc-macro"
|
||||||
|
│ └── syn feature "default" (*)
|
||||||
|
└── syn feature "quote"
|
||||||
|
├── syn feature "printing" (*)
|
||||||
|
└── syn feature "proc-macro" (*)</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>To read this graph, you can follow the chain for each feature from the root to
|
||||||
|
see why it was included. For example, the "full" feature was added by the
|
||||||
|
<code>rustversion</code> crate which was included from <code>myproject</code> (with the default
|
||||||
|
features), and <code>myproject</code> was the package selected on the command-line. All
|
||||||
|
of the other <code>syn</code> features are added by the "default" feature ("quote" is
|
||||||
|
added by "printing" and "proc-macro", both of which are default features).</p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>If you’re having difficulty cross-referencing the de-duplicated <code>(*)</code> entries,
|
||||||
|
try with the <code>--no-dedupe</code> flag to get the full output.</p>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sect1">
|
||||||
|
<h2 id="cargo_tree_see_also">SEE ALSO</h2>
|
||||||
|
<div class="sectionbody">
|
||||||
|
<div class="paragraph">
|
||||||
|
<p><a href="index.html">cargo(1)</a>, <a href="cargo-metadata.html">cargo-metadata(1)</a></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -98,6 +98,10 @@ including overrides, in machine-readable format.</p>
|
|||||||
<dd>
|
<dd>
|
||||||
<p>Print a fully qualified package specification.</p>
|
<p>Print a fully qualified package specification.</p>
|
||||||
</dd>
|
</dd>
|
||||||
|
<dt class="hdlist1"><a href="cargo-tree.html">cargo-tree(1)</a></dt>
|
||||||
|
<dd>
|
||||||
|
<p>Display a tree visualization of a dependency graph.</p>
|
||||||
|
</dd>
|
||||||
<dt class="hdlist1"><a href="cargo-update.html">cargo-update(1)</a></dt>
|
<dt class="hdlist1"><a href="cargo-update.html">cargo-update(1)</a></dt>
|
||||||
<dd>
|
<dd>
|
||||||
<p>Update dependencies as recorded in the local lock file.</p>
|
<p>Update dependencies as recorded in the local lock file.</p>
|
||||||
|
@ -17,8 +17,10 @@ virtual workspace will include all workspace members (equivalent to passing
|
|||||||
*--workspace*::
|
*--workspace*::
|
||||||
{actionverb} all members in the workspace.
|
{actionverb} all members in the workspace.
|
||||||
|
|
||||||
|
ifndef::noall[]
|
||||||
*--all*::
|
*--all*::
|
||||||
Deprecated alias for `--workspace`.
|
Deprecated alias for `--workspace`.
|
||||||
|
endif::noall[]
|
||||||
|
|
||||||
*--exclude* _SPEC_...::
|
*--exclude* _SPEC_...::
|
||||||
Exclude the specified packages. Must be used in conjunction with the
|
Exclude the specified packages. Must be used in conjunction with the
|
||||||
|
@ -59,6 +59,7 @@
|
|||||||
* [cargo locate-project](commands/cargo-locate-project.md)
|
* [cargo locate-project](commands/cargo-locate-project.md)
|
||||||
* [cargo metadata](commands/cargo-metadata.md)
|
* [cargo metadata](commands/cargo-metadata.md)
|
||||||
* [cargo pkgid](commands/cargo-pkgid.md)
|
* [cargo pkgid](commands/cargo-pkgid.md)
|
||||||
|
* [cargo tree](commands/cargo-tree.md)
|
||||||
* [cargo update](commands/cargo-update.md)
|
* [cargo update](commands/cargo-update.md)
|
||||||
* [cargo vendor](commands/cargo-vendor.md)
|
* [cargo vendor](commands/cargo-vendor.md)
|
||||||
* [cargo verify-project](commands/cargo-verify-project.md)
|
* [cargo verify-project](commands/cargo-verify-project.md)
|
||||||
|
3
src/doc/src/commands/cargo-tree.md
Normal file
3
src/doc/src/commands/cargo-tree.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# cargo tree
|
||||||
|
{{#include command-common.html}}
|
||||||
|
{{#include ../../man/generated/cargo-tree.html}}
|
@ -3,6 +3,7 @@
|
|||||||
* [cargo locate-project](cargo-locate-project.md)
|
* [cargo locate-project](cargo-locate-project.md)
|
||||||
* [cargo metadata](cargo-metadata.md)
|
* [cargo metadata](cargo-metadata.md)
|
||||||
* [cargo pkgid](cargo-pkgid.md)
|
* [cargo pkgid](cargo-pkgid.md)
|
||||||
|
* [cargo tree](cargo-tree.md)
|
||||||
* [cargo update](cargo-update.md)
|
* [cargo update](cargo-update.md)
|
||||||
* [cargo vendor](cargo-vendor.md)
|
* [cargo vendor](cargo-vendor.md)
|
||||||
* [cargo verify-project](cargo-verify-project.md)
|
* [cargo verify-project](cargo-verify-project.md)
|
||||||
|
@ -271,6 +271,21 @@ _cargo() {
|
|||||||
'*: :_default'
|
'*: :_default'
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
tree)
|
||||||
|
_arguments -s -S $common $features $triple $manifest \
|
||||||
|
'(-p --package)'{-p+,--package=}'[package to use as the root]:package:_cargo_package_names' \
|
||||||
|
'--no-filter-targets[return dependencies for all targets]' \
|
||||||
|
'--no-dev-dependencies[skip dev dependencies]' \
|
||||||
|
'(-i --invert)'{-i,--invert}'[invert the tree]' \
|
||||||
|
'--no-indent[display as a list]' \
|
||||||
|
'--prefix-depth[display as a list with numeric depth]' \
|
||||||
|
'--no-dedupe[repeat shared dependencies]' \
|
||||||
|
'(-d --duplicates)'{-d,--duplicates}'[packages with multiple versions]' \
|
||||||
|
'--charset=[utf8 or ascii]' \
|
||||||
|
'(-f --format)'{-f,--format=}'[format string]' \
|
||||||
|
'--graph-features[show features]' \
|
||||||
|
;;
|
||||||
|
|
||||||
uninstall)
|
uninstall)
|
||||||
_arguments -s -S $common \
|
_arguments -s -S $common \
|
||||||
'(-p --package)'{-p+,--package=}'[specify package to uninstall]:package:_cargo_package_names' \
|
'(-p --package)'{-p+,--package=}'[specify package to uninstall]:package:_cargo_package_names' \
|
||||||
|
@ -73,6 +73,7 @@ _cargo()
|
|||||||
local opt__rustdoc="$opt_common $opt_pkg $opt_feat $opt_mani $opt_lock $opt_jobs $opt_targets --message-format --target --release --open --target-dir --profile"
|
local opt__rustdoc="$opt_common $opt_pkg $opt_feat $opt_mani $opt_lock $opt_jobs $opt_targets --message-format --target --release --open --target-dir --profile"
|
||||||
local opt__search="$opt_common $opt_lock --limit --index --registry"
|
local opt__search="$opt_common $opt_lock --limit --index --registry"
|
||||||
local opt__test="$opt_common $opt_pkg_spec $opt_feat $opt_mani $opt_lock $opt_jobs $opt_targets --message-format --doc --target --no-run --release --no-fail-fast --target-dir --profile"
|
local opt__test="$opt_common $opt_pkg_spec $opt_feat $opt_mani $opt_lock $opt_jobs $opt_targets --message-format --doc --target --no-run --release --no-fail-fast --target-dir --profile"
|
||||||
|
local opt__tree="$opt_common $opt_pkg_spec $opt_feat $opt_mani $opt_lock --target --no-filter-targets --no-dev-dependencies -i --invert --no-indent --prefix-depth --no-dedupe --duplicates -d --charset -f --format --graph-features"
|
||||||
local opt__uninstall="$opt_common $opt_lock $opt_pkg --bin --root"
|
local opt__uninstall="$opt_common $opt_lock $opt_pkg --bin --root"
|
||||||
local opt__update="$opt_common $opt_mani $opt_lock $opt_pkg --aggressive --precise --dry-run"
|
local opt__update="$opt_common $opt_mani $opt_lock $opt_pkg --aggressive --precise --dry-run"
|
||||||
local opt__vendor="$opt_common $opt_mani $opt_lock $opt_sync --no-delete --respect-source-config --versioned-dirs"
|
local opt__vendor="$opt_common $opt_mani $opt_lock $opt_sync --no-delete --respect-source-config --versioned-dirs"
|
||||||
|
491
src/etc/man/cargo-tree.1
Normal file
491
src/etc/man/cargo-tree.1
Normal file
@ -0,0 +1,491 @@
|
|||||||
|
'\" t
|
||||||
|
.\" Title: cargo-tree
|
||||||
|
.\" Author: [see the "AUTHOR(S)" section]
|
||||||
|
.\" Generator: Asciidoctor 2.0.10
|
||||||
|
.\" Date: 2020-03-30
|
||||||
|
.\" Manual: \ \&
|
||||||
|
.\" Source: \ \&
|
||||||
|
.\" Language: English
|
||||||
|
.\"
|
||||||
|
.TH "CARGO\-TREE" "1" "2020-03-30" "\ \&" "\ \&"
|
||||||
|
.ie \n(.g .ds Aq \(aq
|
||||||
|
.el .ds Aq '
|
||||||
|
.ss \n[.ss] 0
|
||||||
|
.nh
|
||||||
|
.ad l
|
||||||
|
.de URL
|
||||||
|
\fI\\$2\fP <\\$1>\\$3
|
||||||
|
..
|
||||||
|
.als MTO URL
|
||||||
|
.if \n[.g] \{\
|
||||||
|
. mso www.tmac
|
||||||
|
. am URL
|
||||||
|
. ad l
|
||||||
|
. .
|
||||||
|
. am MTO
|
||||||
|
. ad l
|
||||||
|
. .
|
||||||
|
. LINKSTYLE blue R < >
|
||||||
|
.\}
|
||||||
|
.SH "NAME"
|
||||||
|
cargo\-tree \- Display a tree visualization of a dependency graph
|
||||||
|
.SH "SYNOPSIS"
|
||||||
|
.sp
|
||||||
|
\fBcargo tree [\fIOPTIONS\fP]\fP
|
||||||
|
.SH "DESCRIPTION"
|
||||||
|
.sp
|
||||||
|
This command will display a tree of dependencies to the terminal. An example of a simple project that depends on the "rand" package:
|
||||||
|
.sp
|
||||||
|
.if n .RS 4
|
||||||
|
.nf
|
||||||
|
myproject v0.1.0 (/myproject)
|
||||||
|
└── rand v0.7.3
|
||||||
|
├── getrandom v0.1.14
|
||||||
|
│ ├── cfg\-if v0.1.10
|
||||||
|
│ └── libc v0.2.68
|
||||||
|
├── libc v0.2.68 (*)
|
||||||
|
├── rand_chacha v0.2.2
|
||||||
|
│ ├── ppv\-lite86 v0.2.6
|
||||||
|
│ └── rand_core v0.5.1
|
||||||
|
│ └── getrandom v0.1.14 (*)
|
||||||
|
└── rand_core v0.5.1 (*)
|
||||||
|
[build\-dependencies]
|
||||||
|
└── cc v1.0.50
|
||||||
|
.fi
|
||||||
|
.if n .RE
|
||||||
|
.sp
|
||||||
|
Packages marked with \fB(*)\fP have been "de\-duplicated". The dependencies for the
|
||||||
|
package have already been shown elswhere in the graph, and so are not
|
||||||
|
repeated. Use the \fB\-\-no\-dedupe\fP option to repeat the duplicates.
|
||||||
|
.SH "OPTIONS"
|
||||||
|
.SS "Tree Options"
|
||||||
|
.sp
|
||||||
|
\fB\-i\fP, \fB\-\-invert\fP
|
||||||
|
.RS 4
|
||||||
|
Invert the tree. Typically this flag is used with the \fB\-p\fP flag to show
|
||||||
|
the dependencies for a specific package.
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
\fB\-\-no\-dedupe\fP
|
||||||
|
.RS 4
|
||||||
|
Do not de\-duplicate repeated dependencies. Usually, when a package has
|
||||||
|
already displayed its dependencies, further occurrences will not
|
||||||
|
re\-display its dependencies, and will include a \fB(*)\fP to indicate it has
|
||||||
|
already been shown. This flag will cause those duplicates to be repeated.
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
\fB\-d\fP, \fB\-\-duplicates\fP
|
||||||
|
.RS 4
|
||||||
|
Show only dependencies which come in multiple versions (implies
|
||||||
|
\fB\-\-invert\fP). When used with the \fB\-p\fP flag, only shows duplicates within
|
||||||
|
the subtree of the given package.
|
||||||
|
.sp
|
||||||
|
It can be beneficial for build times and executable sizes to avoid building
|
||||||
|
that same package multiple times. This flag can help identify the offending
|
||||||
|
packages. You can then investigate if the package that depends on the
|
||||||
|
duplicate with the older version can be updated to the newer version so that
|
||||||
|
only one instance is built.
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
\fB\-\-no\-dev\-dependencies\fP
|
||||||
|
.RS 4
|
||||||
|
Do not include dev\-dependencies.
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
\fB\-\-target\fP \fITRIPLE\fP
|
||||||
|
.RS 4
|
||||||
|
Filter dependencies matching the given target\-triple.
|
||||||
|
The default is the host platform.
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
\fB\-\-no\-filter\-targets\fP
|
||||||
|
.RS 4
|
||||||
|
Show dependencies for all target platforms. Cannot be specified with
|
||||||
|
\fB\-\-target\fP.
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
\fB\-\-graph\-features\fP
|
||||||
|
.RS 4
|
||||||
|
Runs in a special mode where features are included as individual nodes.
|
||||||
|
This is intended to be used to help explain why a feature is enabled on
|
||||||
|
any particular package. It is recommended to use with the \fB\-p\fP and \fB\-i\fP
|
||||||
|
flags to show how the features flow into the package. See the examples
|
||||||
|
below for more detail.
|
||||||
|
.RE
|
||||||
|
.SS "Tree Formatting Options"
|
||||||
|
.sp
|
||||||
|
\fB\-\-charset\fP \fICHARSET\fP
|
||||||
|
.RS 4
|
||||||
|
Chooses the character set to use for the tree. Valid values are "utf8" or
|
||||||
|
"ascii". Default is "utf8".
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
\fB\-f\fP \fIFORMAT\fP, \fB\-\-format\fP \fIFORMAT\fP
|
||||||
|
.RS 4
|
||||||
|
Set the format string for each package. The default is "{p}".
|
||||||
|
.sp
|
||||||
|
This is an arbitrary string which will be used to display each package. The following
|
||||||
|
strings will be replaced with the corresponding value:
|
||||||
|
.sp
|
||||||
|
.RS 4
|
||||||
|
.ie n \{\
|
||||||
|
\h'-04'\(bu\h'+03'\c
|
||||||
|
.\}
|
||||||
|
.el \{\
|
||||||
|
. sp -1
|
||||||
|
. IP \(bu 2.3
|
||||||
|
.\}
|
||||||
|
\fB{p}\fP — The package name.
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
.RS 4
|
||||||
|
.ie n \{\
|
||||||
|
\h'-04'\(bu\h'+03'\c
|
||||||
|
.\}
|
||||||
|
.el \{\
|
||||||
|
. sp -1
|
||||||
|
. IP \(bu 2.3
|
||||||
|
.\}
|
||||||
|
\fB{l}\fP — The package license.
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
.RS 4
|
||||||
|
.ie n \{\
|
||||||
|
\h'-04'\(bu\h'+03'\c
|
||||||
|
.\}
|
||||||
|
.el \{\
|
||||||
|
. sp -1
|
||||||
|
. IP \(bu 2.3
|
||||||
|
.\}
|
||||||
|
\fB{r}\fP — The package repository URL.
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
.RS 4
|
||||||
|
.ie n \{\
|
||||||
|
\h'-04'\(bu\h'+03'\c
|
||||||
|
.\}
|
||||||
|
.el \{\
|
||||||
|
. sp -1
|
||||||
|
. IP \(bu 2.3
|
||||||
|
.\}
|
||||||
|
\fB{f}\fP — Comma\-separated list of package features that are enabled.
|
||||||
|
.RE
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
\fB\-\-no\-indent\fP
|
||||||
|
.RS 4
|
||||||
|
Display the dependencies as a list (rather than a tree).
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
\fB\-\-prefix\-depth\fP
|
||||||
|
.RS 4
|
||||||
|
Display the dependencies as a list (rather than a tree), but prefixed with
|
||||||
|
the depth.
|
||||||
|
.RE
|
||||||
|
.SS "Package Selection"
|
||||||
|
.sp
|
||||||
|
By default, when no package selection options are given, the packages selected
|
||||||
|
depend on the selected manifest file (based on the current working directory if
|
||||||
|
\fB\-\-manifest\-path\fP is not given). If the manifest is the root of a workspace then
|
||||||
|
the workspaces default members are selected, otherwise only the package defined
|
||||||
|
by the manifest will be selected.
|
||||||
|
.sp
|
||||||
|
The default members of a workspace can be set explicitly with the
|
||||||
|
\fBworkspace.default\-members\fP key in the root manifest. If this is not set, a
|
||||||
|
virtual workspace will include all workspace members (equivalent to passing
|
||||||
|
\fB\-\-workspace\fP), and a non\-virtual workspace will include only the root crate itself.
|
||||||
|
.sp
|
||||||
|
\fB\-p\fP \fISPEC\fP..., \fB\-\-package\fP \fISPEC\fP...
|
||||||
|
.RS 4
|
||||||
|
Display only the specified packages. See \fBcargo\-pkgid\fP(1) for the
|
||||||
|
SPEC format. This flag may be specified multiple times.
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
\fB\-\-workspace\fP
|
||||||
|
.RS 4
|
||||||
|
Display all members in the workspace.
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
\fB\-\-exclude\fP \fISPEC\fP...
|
||||||
|
.RS 4
|
||||||
|
Exclude the specified packages. Must be used in conjunction with the
|
||||||
|
\fB\-\-workspace\fP flag. This flag may be specified multiple times.
|
||||||
|
.RE
|
||||||
|
.SS "Manifest Options"
|
||||||
|
.sp
|
||||||
|
\fB\-\-manifest\-path\fP \fIPATH\fP
|
||||||
|
.RS 4
|
||||||
|
Path to the \fBCargo.toml\fP file. By default, Cargo searches for the
|
||||||
|
\fBCargo.toml\fP file in the current directory or any parent directory.
|
||||||
|
.RE
|
||||||
|
.SS "Feature Selection"
|
||||||
|
.sp
|
||||||
|
The feature flags allow you to control the enabled features for the "current"
|
||||||
|
package. The "current" package is the package in the current directory, or the
|
||||||
|
one specified in \fB\-\-manifest\-path\fP. If running in the root of a virtual
|
||||||
|
workspace, then the default features are selected for all workspace members,
|
||||||
|
or all features if \fB\-\-all\-features\fP is specified.
|
||||||
|
.sp
|
||||||
|
When no feature options are given, the \fBdefault\fP feature is activated for
|
||||||
|
every selected package.
|
||||||
|
.sp
|
||||||
|
\fB\-\-features\fP \fIFEATURES\fP
|
||||||
|
.RS 4
|
||||||
|
Space or comma separated list of features to activate. These features only
|
||||||
|
apply to the current directory\(cqs package. Features of direct dependencies
|
||||||
|
may be enabled with \fB<dep\-name>/<feature\-name>\fP syntax. This flag may be
|
||||||
|
specified multiple times, which enables all specified features.
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
\fB\-\-all\-features\fP
|
||||||
|
.RS 4
|
||||||
|
Activate all available features of all selected packages.
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
\fB\-\-no\-default\-features\fP
|
||||||
|
.RS 4
|
||||||
|
Do not activate the \fBdefault\fP feature of the current directory\(cqs
|
||||||
|
package.
|
||||||
|
.RE
|
||||||
|
.SS "Display Options"
|
||||||
|
.sp
|
||||||
|
\fB\-v\fP, \fB\-\-verbose\fP
|
||||||
|
.RS 4
|
||||||
|
Use verbose output. May be specified twice for "very verbose" output which
|
||||||
|
includes extra output such as dependency warnings and build script output.
|
||||||
|
May also be specified with the \fBterm.verbose\fP
|
||||||
|
.URL "https://doc.rust\-lang.org/cargo/reference/config.html" "config value" "."
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
\fB\-q\fP, \fB\-\-quiet\fP
|
||||||
|
.RS 4
|
||||||
|
No output printed to stdout.
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
\fB\-\-color\fP \fIWHEN\fP
|
||||||
|
.RS 4
|
||||||
|
Control when colored output is used. Valid values:
|
||||||
|
.sp
|
||||||
|
.RS 4
|
||||||
|
.ie n \{\
|
||||||
|
\h'-04'\(bu\h'+03'\c
|
||||||
|
.\}
|
||||||
|
.el \{\
|
||||||
|
. sp -1
|
||||||
|
. IP \(bu 2.3
|
||||||
|
.\}
|
||||||
|
\fBauto\fP (default): Automatically detect if color support is available on the
|
||||||
|
terminal.
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
.RS 4
|
||||||
|
.ie n \{\
|
||||||
|
\h'-04'\(bu\h'+03'\c
|
||||||
|
.\}
|
||||||
|
.el \{\
|
||||||
|
. sp -1
|
||||||
|
. IP \(bu 2.3
|
||||||
|
.\}
|
||||||
|
\fBalways\fP: Always display colors.
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
.RS 4
|
||||||
|
.ie n \{\
|
||||||
|
\h'-04'\(bu\h'+03'\c
|
||||||
|
.\}
|
||||||
|
.el \{\
|
||||||
|
. sp -1
|
||||||
|
. IP \(bu 2.3
|
||||||
|
.\}
|
||||||
|
\fBnever\fP: Never display colors.
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
May also be specified with the \fBterm.color\fP
|
||||||
|
.URL "https://doc.rust\-lang.org/cargo/reference/config.html" "config value" "."
|
||||||
|
.RE
|
||||||
|
.SS "Common Options"
|
||||||
|
.sp
|
||||||
|
\fB\-h\fP, \fB\-\-help\fP
|
||||||
|
.RS 4
|
||||||
|
Prints help information.
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
\fB\-Z\fP \fIFLAG\fP...
|
||||||
|
.RS 4
|
||||||
|
Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fP for
|
||||||
|
details.
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
\fB\-\-frozen\fP, \fB\-\-locked\fP
|
||||||
|
.RS 4
|
||||||
|
Either of these flags requires that the \fBCargo.lock\fP file is
|
||||||
|
up\-to\-date. If the lock file is missing, or it needs to be updated, Cargo will
|
||||||
|
exit with an error. The \fB\-\-frozen\fP flag also prevents Cargo from
|
||||||
|
attempting to access the network to determine if it is out\-of\-date.
|
||||||
|
.sp
|
||||||
|
These may be used in environments where you want to assert that the
|
||||||
|
\fBCargo.lock\fP file is up\-to\-date (such as a CI build) or want to avoid network
|
||||||
|
access.
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
\fB\-\-offline\fP
|
||||||
|
.RS 4
|
||||||
|
Prevents Cargo from accessing the network for any reason. Without this
|
||||||
|
flag, Cargo will stop with an error if it needs to access the network and
|
||||||
|
the network is not available. With this flag, Cargo will attempt to
|
||||||
|
proceed without the network if possible.
|
||||||
|
.sp
|
||||||
|
Beware that this may result in different dependency resolution than online
|
||||||
|
mode. Cargo will restrict itself to crates that are downloaded locally, even
|
||||||
|
if there might be a newer version as indicated in the local copy of the index.
|
||||||
|
See the \fBcargo\-fetch\fP(1) command to download dependencies before going
|
||||||
|
offline.
|
||||||
|
.sp
|
||||||
|
May also be specified with the \fBnet.offline\fP \c
|
||||||
|
.URL "https://doc.rust\-lang.org/cargo/reference/config.html" "config value" "."
|
||||||
|
.RE
|
||||||
|
.SH "ENVIRONMENT"
|
||||||
|
.sp
|
||||||
|
See \c
|
||||||
|
.URL "https://doc.rust\-lang.org/cargo/reference/environment\-variables.html" "the reference" " "
|
||||||
|
for
|
||||||
|
details on environment variables that Cargo reads.
|
||||||
|
.SH "EXIT STATUS"
|
||||||
|
.sp
|
||||||
|
0
|
||||||
|
.RS 4
|
||||||
|
Cargo succeeded.
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
101
|
||||||
|
.RS 4
|
||||||
|
Cargo failed to complete.
|
||||||
|
.RE
|
||||||
|
.SH "EXAMPLES"
|
||||||
|
.sp
|
||||||
|
.RS 4
|
||||||
|
.ie n \{\
|
||||||
|
\h'-04' 1.\h'+01'\c
|
||||||
|
.\}
|
||||||
|
.el \{\
|
||||||
|
. sp -1
|
||||||
|
. IP " 1." 4.2
|
||||||
|
.\}
|
||||||
|
Display the tree for the package in the current directory:
|
||||||
|
.sp
|
||||||
|
.if n .RS 4
|
||||||
|
.nf
|
||||||
|
cargo tree
|
||||||
|
.fi
|
||||||
|
.if n .RE
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
.RS 4
|
||||||
|
.ie n \{\
|
||||||
|
\h'-04' 2.\h'+01'\c
|
||||||
|
.\}
|
||||||
|
.el \{\
|
||||||
|
. sp -1
|
||||||
|
. IP " 2." 4.2
|
||||||
|
.\}
|
||||||
|
Display all the packages that depend on the specified package:
|
||||||
|
.sp
|
||||||
|
.if n .RS 4
|
||||||
|
.nf
|
||||||
|
cargo tree \-i \-p syn
|
||||||
|
.fi
|
||||||
|
.if n .RE
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
.RS 4
|
||||||
|
.ie n \{\
|
||||||
|
\h'-04' 3.\h'+01'\c
|
||||||
|
.\}
|
||||||
|
.el \{\
|
||||||
|
. sp -1
|
||||||
|
. IP " 3." 4.2
|
||||||
|
.\}
|
||||||
|
Show the features enabled on each package:
|
||||||
|
.sp
|
||||||
|
.if n .RS 4
|
||||||
|
.nf
|
||||||
|
cargo tree \-\-format "{p} {f}"
|
||||||
|
.fi
|
||||||
|
.if n .RE
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
.RS 4
|
||||||
|
.ie n \{\
|
||||||
|
\h'-04' 4.\h'+01'\c
|
||||||
|
.\}
|
||||||
|
.el \{\
|
||||||
|
. sp -1
|
||||||
|
. IP " 4." 4.2
|
||||||
|
.\}
|
||||||
|
Show all packages that are built multiple times. This can happen if multiple
|
||||||
|
semver\-incompatible versions appear in the tree (like 1.0.0 and 2.0.0).
|
||||||
|
.sp
|
||||||
|
.if n .RS 4
|
||||||
|
.nf
|
||||||
|
cargo tree \-d
|
||||||
|
.fi
|
||||||
|
.if n .RE
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
.RS 4
|
||||||
|
.ie n \{\
|
||||||
|
\h'-04' 5.\h'+01'\c
|
||||||
|
.\}
|
||||||
|
.el \{\
|
||||||
|
. sp -1
|
||||||
|
. IP " 5." 4.2
|
||||||
|
.\}
|
||||||
|
Explain why features are enabled for the given package:
|
||||||
|
.sp
|
||||||
|
.if n .RS 4
|
||||||
|
.nf
|
||||||
|
cargo tree \-\-graph\-features \-i \-p syn
|
||||||
|
.fi
|
||||||
|
.if n .RE
|
||||||
|
.sp
|
||||||
|
An example of what this would display:
|
||||||
|
.sp
|
||||||
|
.if n .RS 4
|
||||||
|
.nf
|
||||||
|
syn v1.0.17
|
||||||
|
├── syn feature "clone\-impls"
|
||||||
|
│ └── syn feature "default"
|
||||||
|
│ └── rustversion v1.0.2
|
||||||
|
│ └── rustversion feature "default"
|
||||||
|
│ └── myproject v0.1.0 (/myproject)
|
||||||
|
│ └── myproject feature "default" (command\-line)
|
||||||
|
├── syn feature "default" (*)
|
||||||
|
├── syn feature "derive"
|
||||||
|
│ └── syn feature "default" (*)
|
||||||
|
├── syn feature "full"
|
||||||
|
│ └── rustversion v1.0.2 (*)
|
||||||
|
├── syn feature "parsing"
|
||||||
|
│ └── syn feature "default" (*)
|
||||||
|
├── syn feature "printing"
|
||||||
|
│ └── syn feature "default" (*)
|
||||||
|
├── syn feature "proc\-macro"
|
||||||
|
│ └── syn feature "default" (*)
|
||||||
|
└── syn feature "quote"
|
||||||
|
├── syn feature "printing" (*)
|
||||||
|
└── syn feature "proc\-macro" (*)
|
||||||
|
.fi
|
||||||
|
.if n .RE
|
||||||
|
.sp
|
||||||
|
To read this graph, you can follow the chain for each feature from the root to
|
||||||
|
see why it was included. For example, the "full" feature was added by the
|
||||||
|
\fBrustversion\fP crate which was included from \fBmyproject\fP (with the default
|
||||||
|
features), and \fBmyproject\fP was the package selected on the command\-line. All
|
||||||
|
of the other \fBsyn\fP features are added by the "default" feature ("quote" is
|
||||||
|
added by "printing" and "proc\-macro", both of which are default features).
|
||||||
|
.sp
|
||||||
|
If you\(cqre having difficulty cross\-referencing the de\-duplicated \fB(*)\fP entries,
|
||||||
|
try with the \fB\-\-no\-dedupe\fP flag to get the full output.
|
||||||
|
.RE
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
.sp
|
||||||
|
\fBcargo\fP(1), \fBcargo\-metadata\fP(1)
|
@ -2,12 +2,12 @@
|
|||||||
.\" Title: cargo
|
.\" Title: cargo
|
||||||
.\" Author: [see the "AUTHOR(S)" section]
|
.\" Author: [see the "AUTHOR(S)" section]
|
||||||
.\" Generator: Asciidoctor 2.0.10
|
.\" Generator: Asciidoctor 2.0.10
|
||||||
.\" Date: 2019-12-04
|
.\" Date: 2020-03-30
|
||||||
.\" Manual: \ \&
|
.\" Manual: \ \&
|
||||||
.\" Source: \ \&
|
.\" Source: \ \&
|
||||||
.\" Language: English
|
.\" Language: English
|
||||||
.\"
|
.\"
|
||||||
.TH "CARGO" "1" "2019-12-04" "\ \&" "\ \&"
|
.TH "CARGO" "1" "2020-03-30" "\ \&" "\ \&"
|
||||||
.ie \n(.g .ds Aq \(aq
|
.ie \n(.g .ds Aq \(aq
|
||||||
.el .ds Aq '
|
.el .ds Aq '
|
||||||
.ss \n[.ss] 0
|
.ss \n[.ss] 0
|
||||||
@ -125,6 +125,11 @@ including overrides, in machine\-readable format.
|
|||||||
Print a fully qualified package specification.
|
Print a fully qualified package specification.
|
||||||
.RE
|
.RE
|
||||||
.sp
|
.sp
|
||||||
|
\fBcargo\-tree\fP(1)
|
||||||
|
.RS 4
|
||||||
|
Display a tree visualization of a dependency graph.
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
\fBcargo\-update\fP(1)
|
\fBcargo\-update\fP(1)
|
||||||
.RS 4
|
.RS 4
|
||||||
Update dependencies as recorded in the local lock file.
|
Update dependencies as recorded in the local lock file.
|
||||||
|
@ -103,6 +103,8 @@ mod standard_lib;
|
|||||||
mod test;
|
mod test;
|
||||||
mod timings;
|
mod timings;
|
||||||
mod tool_paths;
|
mod tool_paths;
|
||||||
|
mod tree;
|
||||||
|
mod tree_graph_features;
|
||||||
mod unit_graph;
|
mod unit_graph;
|
||||||
mod update;
|
mod update;
|
||||||
mod vendor;
|
mod vendor;
|
||||||
|
1371
tests/testsuite/tree.rs
Normal file
1371
tests/testsuite/tree.rs
Normal file
File diff suppressed because it is too large
Load Diff
364
tests/testsuite/tree_graph_features.rs
Normal file
364
tests/testsuite/tree_graph_features.rs
Normal file
@ -0,0 +1,364 @@
|
|||||||
|
//! Tests for the `cargo tree` command with --graph-features option.
|
||||||
|
|
||||||
|
use cargo_test_support::project;
|
||||||
|
use cargo_test_support::registry::{Dependency, Package};
|
||||||
|
|
||||||
|
#[cargo_test]
|
||||||
|
fn dep_feature_various() {
|
||||||
|
// Checks different ways of setting features via dependencies.
|
||||||
|
Package::new("optdep", "1.0.0")
|
||||||
|
.feature("default", &["cat"])
|
||||||
|
.feature("cat", &[])
|
||||||
|
.publish();
|
||||||
|
Package::new("defaultdep", "1.0.0")
|
||||||
|
.feature("default", &["f1"])
|
||||||
|
.feature("f1", &["optdep"])
|
||||||
|
.add_dep(Dependency::new("optdep", "1.0").optional(true))
|
||||||
|
.publish();
|
||||||
|
Package::new("nodefaultdep", "1.0.0")
|
||||||
|
.feature("default", &["f1"])
|
||||||
|
.feature("f1", &[])
|
||||||
|
.publish();
|
||||||
|
Package::new("nameddep", "1.0.0")
|
||||||
|
.add_dep(Dependency::new("serde", "1.0").optional(true))
|
||||||
|
.feature("default", &["serde-stuff"])
|
||||||
|
.feature("serde-stuff", &["serde/derive"])
|
||||||
|
.feature("vehicle", &["car"])
|
||||||
|
.feature("car", &[])
|
||||||
|
.publish();
|
||||||
|
Package::new("serde_derive", "1.0.0").publish();
|
||||||
|
Package::new("serde", "1.0.0")
|
||||||
|
.feature("derive", &["serde_derive"])
|
||||||
|
.add_dep(Dependency::new("serde_derive", "1.0").optional(true))
|
||||||
|
.publish();
|
||||||
|
|
||||||
|
let p = project()
|
||||||
|
.file(
|
||||||
|
"Cargo.toml",
|
||||||
|
r#"
|
||||||
|
[package]
|
||||||
|
name = "foo"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
defaultdep = "1.0"
|
||||||
|
nodefaultdep = {version="1.0", default-features = false}
|
||||||
|
nameddep = {version="1.0", features = ["vehicle", "serde"]}
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.file("src/lib.rs", "")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
p.cargo("tree --graph-features")
|
||||||
|
.with_stdout(
|
||||||
|
"\
|
||||||
|
foo v0.1.0 ([..]/foo)
|
||||||
|
├── nodefaultdep v1.0.0
|
||||||
|
├── defaultdep feature \"default\"
|
||||||
|
│ ├── defaultdep v1.0.0
|
||||||
|
│ │ └── optdep feature \"default\"
|
||||||
|
│ │ ├── optdep v1.0.0
|
||||||
|
│ │ └── optdep feature \"cat\"
|
||||||
|
│ │ └── optdep v1.0.0 (*)
|
||||||
|
│ └── defaultdep feature \"f1\"
|
||||||
|
│ ├── defaultdep v1.0.0 (*)
|
||||||
|
│ └── defaultdep feature \"optdep\"
|
||||||
|
│ └── defaultdep v1.0.0 (*)
|
||||||
|
├── nameddep feature \"default\"
|
||||||
|
│ ├── nameddep v1.0.0
|
||||||
|
│ │ └── serde feature \"default\"
|
||||||
|
│ │ └── serde v1.0.0
|
||||||
|
│ │ └── serde_derive feature \"default\"
|
||||||
|
│ │ └── serde_derive v1.0.0
|
||||||
|
│ └── nameddep feature \"serde-stuff\"
|
||||||
|
│ ├── nameddep v1.0.0 (*)
|
||||||
|
│ ├── nameddep feature \"serde\"
|
||||||
|
│ │ └── nameddep v1.0.0 (*)
|
||||||
|
│ └── serde feature \"derive\"
|
||||||
|
│ ├── serde v1.0.0 (*)
|
||||||
|
│ └── serde feature \"serde_derive\"
|
||||||
|
│ └── serde v1.0.0 (*)
|
||||||
|
├── nameddep feature \"serde\" (*)
|
||||||
|
└── nameddep feature \"vehicle\"
|
||||||
|
├── nameddep v1.0.0 (*)
|
||||||
|
└── nameddep feature \"car\"
|
||||||
|
└── nameddep v1.0.0 (*)
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cargo_test]
|
||||||
|
fn graph_features_ws_interdependent() {
|
||||||
|
// A workspace with interdependent crates.
|
||||||
|
let p = project()
|
||||||
|
.file(
|
||||||
|
"Cargo.toml",
|
||||||
|
r#"
|
||||||
|
[workspace]
|
||||||
|
members = ["a", "b"]
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.file(
|
||||||
|
"a/Cargo.toml",
|
||||||
|
r#"
|
||||||
|
[package]
|
||||||
|
name = "a"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
b = {path="../b", features=["feat2"]}
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["a1"]
|
||||||
|
a1 = []
|
||||||
|
a2 = []
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.file("a/src/lib.rs", "")
|
||||||
|
.file(
|
||||||
|
"b/Cargo.toml",
|
||||||
|
r#"
|
||||||
|
[package]
|
||||||
|
name = "b"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["feat1"]
|
||||||
|
feat1 = []
|
||||||
|
feat2 = []
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.file("b/src/lib.rs", "")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
p.cargo("tree --graph-features")
|
||||||
|
.with_stdout(
|
||||||
|
"\
|
||||||
|
a v0.1.0 ([..]/foo/a)
|
||||||
|
├── b feature \"default\" (command-line)
|
||||||
|
│ ├── b v0.1.0 ([..]/foo/b)
|
||||||
|
│ └── b feature \"feat1\"
|
||||||
|
│ └── b v0.1.0 ([..]/foo/b) (*)
|
||||||
|
└── b feature \"feat2\"
|
||||||
|
└── b v0.1.0 ([..]/foo/b) (*)
|
||||||
|
|
||||||
|
b v0.1.0 ([..]/foo/b)
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
|
||||||
|
p.cargo("tree --graph-features -i")
|
||||||
|
.with_stdout(
|
||||||
|
"\
|
||||||
|
a v0.1.0 ([..]/foo/a)
|
||||||
|
├── a feature \"a1\"
|
||||||
|
│ └── a feature \"default\" (command-line)
|
||||||
|
└── a feature \"default\" (command-line) (*)
|
||||||
|
|
||||||
|
b v0.1.0 ([..]/foo/b)
|
||||||
|
├── b feature \"default\" (command-line)
|
||||||
|
│ └── a v0.1.0 ([..]/foo/a)
|
||||||
|
│ ├── a feature \"a1\"
|
||||||
|
│ │ └── a feature \"default\" (command-line)
|
||||||
|
│ └── a feature \"default\" (command-line) (*)
|
||||||
|
├── b feature \"feat1\"
|
||||||
|
│ └── b feature \"default\" (command-line) (*)
|
||||||
|
└── b feature \"feat2\"
|
||||||
|
└── a v0.1.0 ([..]/foo/a) (*)
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cargo_test]
|
||||||
|
fn slash_feature_name() {
|
||||||
|
// dep_name/feat_name syntax
|
||||||
|
Package::new("opt", "1.0.0").feature("feat1", &[]).publish();
|
||||||
|
Package::new("notopt", "1.0.0")
|
||||||
|
.feature("cat", &[])
|
||||||
|
.feature("animal", &["cat"])
|
||||||
|
.publish();
|
||||||
|
Package::new("opt2", "1.0.0").publish();
|
||||||
|
|
||||||
|
let p = project()
|
||||||
|
.file(
|
||||||
|
"Cargo.toml",
|
||||||
|
r#"
|
||||||
|
[package]
|
||||||
|
name = "foo"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
opt = {version = "1.0", optional=true}
|
||||||
|
opt2 = {version = "1.0", optional=true}
|
||||||
|
notopt = "1.0"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
f1 = ["opt/feat1", "notopt/animal"]
|
||||||
|
f2 = ["f1"]
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.file("src/lib.rs", "")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
p.cargo("tree --graph-features --features f1")
|
||||||
|
.with_stdout(
|
||||||
|
"\
|
||||||
|
foo v0.1.0 ([..]/foo)
|
||||||
|
├── notopt feature \"default\"
|
||||||
|
│ └── notopt v1.0.0
|
||||||
|
└── opt feature \"default\"
|
||||||
|
└── opt v1.0.0
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
|
||||||
|
p.cargo("tree --graph-features --features f1 -i")
|
||||||
|
.with_stdout(
|
||||||
|
"\
|
||||||
|
foo v0.1.0 ([..]/foo)
|
||||||
|
├── foo feature \"default\" (command-line)
|
||||||
|
├── foo feature \"f1\" (command-line)
|
||||||
|
└── foo feature \"opt\"
|
||||||
|
└── foo feature \"f1\" (command-line) (*)
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
|
||||||
|
p.cargo("tree --graph-features --features f1 -p notopt -i")
|
||||||
|
.with_stdout(
|
||||||
|
"\
|
||||||
|
notopt v1.0.0
|
||||||
|
├── notopt feature \"animal\"
|
||||||
|
│ └── foo feature \"f1\" (command-line)
|
||||||
|
├── notopt feature \"cat\"
|
||||||
|
│ └── notopt feature \"animal\" (*)
|
||||||
|
└── notopt feature \"default\"
|
||||||
|
└── foo v0.1.0 ([..]/foo)
|
||||||
|
├── foo feature \"default\" (command-line)
|
||||||
|
├── foo feature \"f1\" (command-line) (*)
|
||||||
|
└── foo feature \"opt\"
|
||||||
|
└── foo feature \"f1\" (command-line) (*)
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
|
||||||
|
p.cargo("tree --graph-features --features notopt/animal -p notopt -i")
|
||||||
|
.with_stdout(
|
||||||
|
"\
|
||||||
|
notopt v1.0.0
|
||||||
|
├── notopt feature \"animal\" (command-line)
|
||||||
|
├── notopt feature \"cat\"
|
||||||
|
│ └── notopt feature \"animal\" (command-line) (*)
|
||||||
|
└── notopt feature \"default\"
|
||||||
|
└── foo v0.1.0 ([..]/foo)
|
||||||
|
└── foo feature \"default\" (command-line)
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
|
||||||
|
p.cargo("tree --graph-features --all-features")
|
||||||
|
.with_stdout(
|
||||||
|
"\
|
||||||
|
foo v0.1.0 ([..]/foo)
|
||||||
|
├── notopt feature \"default\"
|
||||||
|
│ └── notopt v1.0.0
|
||||||
|
├── opt feature \"default\"
|
||||||
|
│ └── opt v1.0.0
|
||||||
|
└── opt2 feature \"default\"
|
||||||
|
└── opt2 v1.0.0
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
|
||||||
|
p.cargo("tree --graph-features --all-features -p opt2 -i")
|
||||||
|
.with_stdout(
|
||||||
|
"\
|
||||||
|
opt2 v1.0.0
|
||||||
|
└── opt2 feature \"default\"
|
||||||
|
└── foo v0.1.0 ([..]/foo)
|
||||||
|
├── foo feature \"f1\" (command-line)
|
||||||
|
│ └── foo feature \"f2\" (command-line)
|
||||||
|
├── foo feature \"f2\" (command-line) (*)
|
||||||
|
├── foo feature \"opt\" (command-line)
|
||||||
|
│ └── foo feature \"f1\" (command-line) (*)
|
||||||
|
└── foo feature \"opt2\" (command-line)
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cargo_test]
|
||||||
|
fn features_enables_inactive_target() {
|
||||||
|
// Features that enable things on targets that are not enabled.
|
||||||
|
Package::new("optdep", "1.0.0")
|
||||||
|
.feature("feat1", &[])
|
||||||
|
.publish();
|
||||||
|
Package::new("dep1", "1.0.0")
|
||||||
|
.feature("somefeat", &[])
|
||||||
|
.publish();
|
||||||
|
Package::new("dep2", "1.0.0")
|
||||||
|
.add_dep(
|
||||||
|
Dependency::new("optdep", "1.0.0")
|
||||||
|
.optional(true)
|
||||||
|
.target("cfg(whatever)"),
|
||||||
|
)
|
||||||
|
.publish();
|
||||||
|
let p = project()
|
||||||
|
.file(
|
||||||
|
"Cargo.toml",
|
||||||
|
r#"
|
||||||
|
[package]
|
||||||
|
name = "foo"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[target.'cfg(whatever)'.dependencies]
|
||||||
|
optdep = {version="1.0", optional=true}
|
||||||
|
dep1 = "1.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
dep2 = "1.0"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
f1 = ["optdep"]
|
||||||
|
f2 = ["optdep/feat1"]
|
||||||
|
f3 = ["dep1/somefeat"]
|
||||||
|
f4 = ["dep2/optdep"]
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.file("src/lib.rs", "")
|
||||||
|
.build();
|
||||||
|
p.cargo("tree --graph-features")
|
||||||
|
.with_stdout(
|
||||||
|
"\
|
||||||
|
foo v0.1.0 ([..]/foo)
|
||||||
|
└── dep2 feature \"default\"
|
||||||
|
└── dep2 v1.0.0
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
p.cargo("tree --graph-features --all-features")
|
||||||
|
.with_stdout(
|
||||||
|
"\
|
||||||
|
foo v0.1.0 ([..]/foo)
|
||||||
|
└── dep2 feature \"default\"
|
||||||
|
└── dep2 v1.0.0
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
p.cargo("tree --graph-features --all-features --no-filter-targets")
|
||||||
|
.with_stdout(
|
||||||
|
"\
|
||||||
|
foo v0.1.0 ([..]/foo)
|
||||||
|
├── dep1 feature \"default\"
|
||||||
|
│ └── dep1 v1.0.0
|
||||||
|
├── dep2 feature \"default\"
|
||||||
|
│ └── dep2 v1.0.0
|
||||||
|
│ └── optdep feature \"default\"
|
||||||
|
│ └── optdep v1.0.0
|
||||||
|
└── optdep feature \"default\" (*)
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user