diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 792512d0..a096ac8a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -138,7 +138,7 @@ jobs: name: Minimum Stable Rust Version Check runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@stable with: toolchain: "1.60.0" diff --git a/docs/src/README.md b/docs/src/README.md index 068b4a95..e75f97e5 100644 --- a/docs/src/README.md +++ b/docs/src/README.md @@ -33,7 +33,7 @@ jobs: cargo-deny: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: EmbarkStudios/cargo-deny-action@v1 ``` diff --git a/docs/src/checks/cfg.md b/docs/src/checks/cfg.md index 86dc4705..56f6bbd8 100644 --- a/docs/src/checks/cfg.md +++ b/docs/src/checks/cfg.md @@ -38,6 +38,22 @@ Note that excluding a crate is recursive, if any of its transitive dependencies Rust `cfg()` expressions support the [`target_feature = "feature-name"`](https://doc.rust-lang.org/reference/attributes/codegen.html#the-target_feature-attribute) predicate, but at the moment, the only way to actually pass them when compiling is to use the `RUSTFLAGS` environment variable. The `features` field allows you to specify 1 or more `target_feature`s you plan to build with, for a particular target triple. At the time of this writing, cargo-deny does not attempt to validate that the features you specify are actually valid for the target triple, but this is [planned](https://github.com/EmbarkStudios/cfg-expr/issues/1). +### The `all-features` field (optional) + +If set to `true`, `--all-features` will be used when collecting metadata. + +### The `no-default-features` field (optional) + +If set to `true`, `--no-default-features` will be used when collecting metadata. + +### The `features` field (optional) + +If set, and `--features` is not specified on the cmd line, these features will be used when collecting metadata. + +### The `feature-depth` field (optional) + +The maximum depth that features will be displayed when inclusion graphs are included in diagnostics, unless specified via `--feature-depth` on the command line. Only applies to diagnostics that actually print features. If not specified defaults to `1`. + ### The `[licenses]` section See the [licenses config](licenses/cfg.html) for more info. diff --git a/docs/src/cli/check.md b/docs/src/cli/check.md index e9ed08f6..62f0c882 100644 --- a/docs/src/cli/check.md +++ b/docs/src/cli/check.md @@ -6,19 +6,53 @@ The check command is the primary subcommand of cargo-deny as it is what actually ### `` -The check(s) to perform. By default, **all** checks will be performed, unless one or more specific checks are specified. +The check(s) to perform. By default, **all** checks will be performed, unless one or more checks are specified here. See [checks](../checks/index.html) for the list of available checks. -## Flags +## Options + +### `-A, --allow ` + +Set lint allowed + +### `--audit-compatible-output` + +To ease transition from cargo-audit to cargo-deny, this flag will tell cargo-deny to output the exact same output as cargo-audit would, to `stdout` instead of `stderr`, just as with cargo-audit. + +Note that this flag only applies when the output format is JSON, and note that since cargo-deny supports multiple advisory databases, instead of a single JSON object, there will be 1 for each unique advisory database. + +### `-c, --config ` + +Path to the config to use + +Defaults to `/deny.toml` if not specified ### `-d, --disable-fetch` -Disables fetching of advisory databases, if they would be loaded. If disabled, and there is not already an existing advisory database locally, an error will occur. +Disable fetching of the advisory database + +When running the `advisories` check, the configured advisory database will be fetched and opened. If this flag is passed, the database won't be fetched, but an error will occur if it doesn't already exist locally. + +### `-D, --deny ` + +Set lint denied + +### `--feature-depth ` + +Specifies the depth at which feature edges are added in inclusion graphs + +### `-g, --graph ` + +Path to graph_output root directory + +If set, a dotviz graph will be created for whenever multiple versions of the same crate are detected. + +Each file will be created at `/graph_output/.dot`. `/graph_output/*` is deleted and recreated each run. ### `--hide-inclusion-graph` -Hides the inclusion graph when printing out info for a crate. +Hides the inclusion graph when printing out info for a crate By default, if a diagnostic message pertains to a specific crate, cargo-deny will append an inverse dependency graph to the diagnostic to show you how that crate was pulled into your project. @@ -31,12 +65,10 @@ the-crate └── c-crate ``` -## Options - -### `-c, --config` +### `-s, --show-stats` -The path to the config file used to determine which crates are allowed or denied. Will default to `/deny.toml` if not specified. +Show stats for all the checks, regardless of the log-level -### `-g, --graph` +### `-W, --warn ` -A root directory to place dotviz graphs into when duplicate crate versions are detected. Will be `/graph_output/.dot`. The `/graph_output/*` is deleted and recreated each run. +Set lint warnings diff --git a/src/advisories/helpers.rs b/src/advisories/helpers.rs index 588b5ff9..156e7ac1 100644 --- a/src/advisories/helpers.rs +++ b/src/advisories/helpers.rs @@ -528,7 +528,7 @@ where if let Ok(s) = std::env::var("USER").or_else(|_| std::env::var("USERNAME")) { attempts.push(s); } - if let Some(ref s) = cred_helper.username { + if let Some(s) = &cred_helper.username { attempts.push(s.clone()); } diff --git a/src/cargo-deny/check.rs b/src/cargo-deny/check.rs index 100729a1..22aeda55 100644 --- a/src/cargo-deny/check.rs +++ b/src/cargo-deny/check.rs @@ -103,6 +103,9 @@ pub struct Args { pub show_stats: bool, #[clap(flatten)] pub lint_levels: LintLevels, + /// Specifies the depth at which feature edges are added in inclusion graphs + #[clap(long, conflicts_with = "hide-inclusion-graph")] + pub feature_depth: Option, /// The check(s) to perform #[clap(value_enum, action)] pub which: Vec, @@ -118,6 +121,13 @@ struct Config { targets: Vec, #[serde(default)] exclude: Vec, + feature_depth: Option, + #[serde(default)] + all_features: bool, + #[serde(default)] + no_default_features: bool, + #[serde(default)] + features: Vec, } struct ValidConfig { @@ -127,6 +137,10 @@ struct ValidConfig { sources: sources::ValidConfig, targets: Vec<(krates::Target, Vec)>, exclude: Vec, + feature_depth: Option, + all_features: bool, + no_default_features: bool, + features: Vec, } impl ValidConfig { @@ -178,6 +192,10 @@ impl ValidConfig { let targets = crate::common::load_targets(cfg.targets, &mut diags, id); let exclude = cfg.exclude; + let feature_depth = cfg.feature_depth; + let all_features = cfg.all_features; + let no_default_features = cfg.no_default_features; + let features = cfg.features; ( diags, @@ -188,6 +206,10 @@ impl ValidConfig { sources, targets, exclude, + feature_depth, + all_features, + no_default_features, + features, }, ) }; @@ -197,7 +219,7 @@ impl ValidConfig { return; } - if let Some(printer) = crate::common::DiagPrinter::new(log_ctx, None) { + if let Some(printer) = crate::common::DiagPrinter::new(log_ctx, None, None) { let mut lock = printer.lock(); for diag in diags { lock.print(diag, files); @@ -226,7 +248,7 @@ impl ValidConfig { pub(crate) fn cmd( log_ctx: crate::common::LogContext, args: Args, - krate_ctx: crate::common::KrateContext, + mut krate_ctx: crate::common::KrateContext, ) -> anyhow::Result { let mut files = Files::new(); let ValidConfig { @@ -236,6 +258,10 @@ pub(crate) fn cmd( sources, targets, exclude, + feature_depth, + all_features, + no_default_features, + features, } = ValidConfig::load( krate_ctx.get_config_path(args.config.clone()), &mut files, @@ -265,6 +291,21 @@ pub(crate) fn cmd( .iter() .any(|w| *w == WhichCheck::Sources || *w == WhichCheck::All); + let feature_depth = args.feature_depth.or(feature_depth); + + // If not specified on the cmd line, fallback to the feature related config options + if !krate_ctx.all_features { + krate_ctx.all_features = all_features; + } + + if !krate_ctx.no_default_features { + krate_ctx.no_default_features = no_default_features; + } + + if krate_ctx.features.is_empty() { + krate_ctx.features = features; + } + let mut krates = None; let mut license_store = None; let mut advisory_dbs = None; @@ -328,7 +369,7 @@ pub(crate) fn cmd( s.spawn(|_| { let gathered = krate_ctx.gather_krates(targets, exclude); - if let Ok(ref krates) = gathered { + if let Ok(krates) = &gathered { rayon::scope(|s| { if check_advisories { s.spawn(|_| { @@ -461,6 +502,7 @@ pub(crate) fn cmd( }, files, &mut stats, + feature_depth, ); }); @@ -607,10 +649,11 @@ fn print_diagnostics( krates: Option<&cargo_deny::Krates>, files: Files, stats: &mut AllStats, + feature_depth: Option, ) { use cargo_deny::diag::Check; - match crate::common::DiagPrinter::new(log_ctx, krates) { + match crate::common::DiagPrinter::new(log_ctx, krates, feature_depth) { Some(printer) => { for pack in rx { let check_stats = match pack.check { diff --git a/src/cargo-deny/common.rs b/src/cargo-deny/common.rs index 82243ce6..bf4a180b 100644 --- a/src/cargo-deny/common.rs +++ b/src/cargo-deny/common.rs @@ -153,7 +153,7 @@ impl KrateContext { } }); - if let Ok(ref krates) = graph { + if let Ok(krates) = &graph { log::info!( "gathered {} crates in {}ms", krates.len(), @@ -287,6 +287,7 @@ pub struct Human<'a> { stream: term::termcolor::StandardStream, grapher: Option>, config: term::Config, + feature_depth: Option, } pub enum StdioStream { @@ -317,8 +318,13 @@ enum OutputFormat<'a> { impl<'a> OutputFormat<'a> { fn lock(&'a self, max_severity: Severity) -> OutputLock<'a, '_> { match self { - Self::Human(ref human) => OutputLock::Human(human, max_severity, human.stream.lock()), - Self::Json(ref json) => OutputLock::Json(json, max_severity, json.stream.lock()), + Self::Human(human) => OutputLock::Human( + human, + max_severity, + human.stream.lock(), + human.feature_depth, + ), + Self::Json(json) => OutputLock::Json(json, max_severity, json.stream.lock()), } } } @@ -349,6 +355,7 @@ pub enum OutputLock<'a, 'b> { &'a Human<'a>, Severity, term::termcolor::StandardStreamLock<'b>, + Option, ), Json(&'a Json<'a>, Severity, StdLock<'b>), } @@ -356,7 +363,7 @@ pub enum OutputLock<'a, 'b> { impl<'a, 'b> OutputLock<'a, 'b> { pub fn print(&mut self, diag: CsDiag, files: &Files) { match self { - Self::Human(cfg, max, l) => { + Self::Human(cfg, max, l, _) => { if diag.severity < *max { return; } @@ -385,7 +392,7 @@ impl<'a, 'b> OutputLock<'a, 'b> { let mut emitted = std::collections::BTreeSet::new(); match self { - Self::Human(cfg, max, l) => { + Self::Human(cfg, max, l, fd) => { for mut diag in pack { if diag.diag.severity < *max { continue; @@ -399,9 +406,14 @@ impl<'a, 'b> OutputLock<'a, 'b> { diag.diag .notes .push(format!("{} v{} (*)", krate.name, krate.version)); - } else if let Ok(graph) = - grapher.build_graph(&gn, if diag.with_features { 1 } else { 0 }) - { + } else if let Ok(graph) = grapher.build_graph( + &gn, + if diag.with_features { + fd.unwrap_or(1) as usize + } else { + 0 + }, + ) { let graph_text = diag::write_graph_as_text(&graph); diag.diag.notes.push(graph_text); emitted.insert(gn.kid); @@ -446,7 +458,11 @@ pub struct DiagPrinter<'a> { } impl<'a> DiagPrinter<'a> { - pub fn new(ctx: LogContext, krates: Option<&'a cargo_deny::Krates>) -> Option { + pub fn new( + ctx: LogContext, + krates: Option<&'a cargo_deny::Krates>, + feature_depth: Option, + ) -> Option { let max_severity = log_level_to_severity(ctx.log_level); max_severity.map(|max_severity| match ctx.format { @@ -461,6 +477,7 @@ impl<'a> DiagPrinter<'a> { stream, grapher: krates.map(diag::InclusionGrapher::new), config: term::Config::default(), + feature_depth, }), max_severity, } diff --git a/src/cargo-deny/fetch.rs b/src/cargo-deny/fetch.rs index 9d44ef25..af949c28 100644 --- a/src/cargo-deny/fetch.rs +++ b/src/cargo-deny/fetch.rs @@ -74,7 +74,7 @@ impl ValidConfig { return; } - if let Some(printer) = crate::common::DiagPrinter::new(log_ctx, None) { + if let Some(printer) = crate::common::DiagPrinter::new(log_ctx, None, None) { let mut lock = printer.lock(); for diag in diags { lock.print(diag, files); diff --git a/src/cargo-deny/list.rs b/src/cargo-deny/list.rs index c2c11593..3371ae43 100644 --- a/src/cargo-deny/list.rs +++ b/src/cargo-deny/list.rs @@ -109,7 +109,7 @@ impl ValidConfig { return; } - if let Some(printer) = crate::common::DiagPrinter::new(log_ctx, None) { + if let Some(printer) = crate::common::DiagPrinter::new(log_ctx, None, None) { let mut lock = printer.lock(); for diag in diags { lock.print(diag, files); diff --git a/src/diag/grapher.rs b/src/diag/grapher.rs index d97c7b23..05660eb0 100644 --- a/src/diag/grapher.rs +++ b/src/diag/grapher.rs @@ -105,7 +105,7 @@ impl<'a> InclusionGrapher<'a> { Node::Krate { krate, .. } => { let kind = np.edge.and_then(|eid| match self.krates.graph()[eid] { Edge::Dep { kind, .. } | Edge::DepFeature { kind, .. } => match kind { - DepKind::Normal => None, //Some("feature (normal)"), + DepKind::Normal => None, DepKind::Dev => Some("dev"), DepKind::Build => Some("build"), }, diff --git a/src/licenses/gather.rs b/src/licenses/gather.rs index 49e91643..403e6b76 100644 --- a/src/licenses/gather.rs +++ b/src/licenses/gather.rs @@ -145,7 +145,7 @@ impl LicensePack { // Add the explicitly specified license if it wasn't // already found in the root directory - if let Some(ref lf) = krate.license_file { + if let Some(lf) = &krate.license_file { if !lic_paths.iter().any(|l| l.ends_with(lf)) { // The `krate.license_file` is relative to the crate, while files found with // `find_license_files()` are absolute. We prepend the directory of the current diff --git a/tests/snapshots/cargo_deny__test__cargo_deny-check.snap b/tests/snapshots/cargo_deny__test__cargo_deny-check.snap index 49cf4fd9..88d89bc3 100644 --- a/tests/snapshots/cargo_deny__test__cargo_deny-check.snap +++ b/tests/snapshots/cargo_deny__test__cargo_deny-check.snap @@ -45,6 +45,10 @@ OPTIONS: -D, --deny Set lint denied + --feature-depth + Specifies the depth at which feature edges are added in inclusion + graphs + -g, --graph Path to graph_output root directory