summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2022-09-22 07:21:08 +0000
committerLars Wirzenius <liw@liw.fi>2022-09-22 07:21:08 +0000
commit4b895e19b9e0167236b5dac46f438e43d8a74616 (patch)
tree8eec1ded60e1a54e638f6a926b72451ed3eeb0d1
parentfd61004c38bbd5d97fb2c36fedab014f3d6c36d9 (diff)
parent885c3bb497cbb1fd106b5d96ff406399c80ce75f (diff)
downloadmissing-dependencies-4b895e19b9e0167236b5dac46f438e43d8a74616.tar.gz
Merge branch 'improvements' into 'main'
fix problems, improve reporting, produce HTML report See merge request sequoia-pgp/missing-dependencies!3
-rw-r--r--Cargo.lock54
-rw-r--r--Cargo.toml1
-rwxr-xr-xdebian-crate-packages6
-rwxr-xr-xreport.sh9
-rw-r--r--src/main.rs193
-rw-r--r--style.css18
6 files changed, 235 insertions, 46 deletions
diff --git a/Cargo.lock b/Cargo.lock
index a043098..6ed5c09 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -41,6 +41,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
+name = "bytecount"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c"
+
+[[package]]
name = "camino"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -130,6 +136,12 @@ dependencies = [
]
[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -207,6 +219,7 @@ dependencies = [
"pretty_env_logger",
"regex",
"semver",
+ "tabled",
]
[[package]]
@@ -222,6 +235,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "648001efe5d5c0102d8cea768e348da85d90af8ba91f0bea908f157951493cd4"
[[package]]
+name = "papergrid"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "453cf71f2a37af495a1a124bf30d4d7469cfbea58e9f2479be9d222396a518a2"
+dependencies = [
+ "bytecount",
+ "fnv",
+ "unicode-width",
+]
+
+[[package]]
name = "pretty_env_logger"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -360,6 +384,30 @@ dependencies = [
]
[[package]]
+name = "tabled"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5b2f8c37d26d87d2252187b0a45ea3cbf42baca10377c7e7eaaa2800fa9bf97"
+dependencies = [
+ "papergrid",
+ "tabled_derive",
+ "unicode-width",
+]
+
+[[package]]
+name = "tabled_derive"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9ee618502f497abf593e1c5c9577f34775b111480009ffccd7ad70d23fcaba8"
+dependencies = [
+ "heck",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "termcolor"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -381,6 +429,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7"
[[package]]
+name = "unicode-width"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
+
+[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index fb920fb..4ba74b5 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,3 +13,4 @@ log = "0.4.17"
pretty_env_logger = "0.4.0"
regex = "1.6.0"
semver = "1.0.12"
+tabled = "0.8.0"
diff --git a/debian-crate-packages b/debian-crate-packages
index 89f28ae..3f74fe1 100755
--- a/debian-crate-packages
+++ b/debian-crate-packages
@@ -6,8 +6,11 @@ import subprocess
import sys
+verbose = False
+
+
def log(msg):
- if True:
+ if verbose:
sys.stderr.write(f"{msg}\n")
sys.stderr.flush()
@@ -49,6 +52,7 @@ def crate_version(version):
if "+really" in version:
version = version.split("really")[-1]
version = strip_prefix(version, ".")
+ version = "-".join(version.split("~"))
parts = version.split("-")
if len(parts) == 1:
return parts[0]
diff --git a/report.sh b/report.sh
new file mode 100755
index 0000000..aa8ccee
--- /dev/null
+++ b/report.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+set -euo pipefail
+
+sq_dir="$1"
+
+./debian-crate-packages >crates-in-debian
+cargo run -q -- --style=markdown crates-in-debian "$sq_dir" -f crypto-nettle |
+ pandoc -H style.css -f markdown /dev/stdin -o problems.html --metadata title="Problems packaging sq for Debian"
diff --git a/src/main.rs b/src/main.rs
index af31ec4..0c99d2f 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,12 +1,12 @@
use anyhow::anyhow;
-use cargo_metadata::MetadataCommand;
+use cargo_metadata::{CargoOpt, DependencyKind, Metadata, MetadataCommand};
use clap::Parser;
-use log::{debug, error, info};
+use log::{debug, error, info, trace};
use semver::{Version, VersionReq};
use std::collections::HashMap;
-use std::fmt;
use std::fs::read;
use std::path::{Path, PathBuf};
+use tabled::{Style, Table, Tabled};
fn main() {
if let Err(e) = fallible_main() {
@@ -23,56 +23,134 @@ fn fallible_main() -> anyhow::Result<()> {
let packaged = Crates::from_file(&args.packaged)?;
info!("load dependencies with version requirements");
- let metadata = MetadataCommand::new()
- .manifest_path(&args.dirname.join("Cargo.toml"))
- .exec()?;
+ let metadata = load_cargo_metadata(&args)?;
+ let dependencies = collect_dependencies(&metadata);
+ info!("found {} dependencies", dependencies.len());
+
+ info!("find dependencies that are not packaged");
+ let problems = find_problems(&packaged, &dependencies);
+ let count = problems.len();
+
+ if count == 0 {
+ info!("all good");
+ Ok(())
+ } else {
+ report_problems(problems, &args.style);
+ Err(anyhow!("there were {} missing dependencies", count))
+ }
+}
+
+fn load_cargo_metadata(args: &Args) -> anyhow::Result<Metadata> {
+ let mut metadata = MetadataCommand::new();
+ metadata.other_options(vec!["--filter-platform=x86_64-unknown-linux-gnu".into()]);
+ metadata.manifest_path(&args.dirname.join("Cargo.toml"));
+
+ if args.all_features {
+ metadata.features(CargoOpt::AllFeatures);
+ } else if !args.features.is_empty() {
+ metadata.features(CargoOpt::NoDefaultFeatures);
+ metadata.features(CargoOpt::SomeFeatures(args.features.clone()));
+ };
+
+ info!("run cargo command: {:?}", metadata.cargo_command());
+ Ok(metadata.exec()?)
+}
+
+fn collect_dependencies(metadata: &Metadata) -> HashMap<String, VersionReq> {
let mut dependencies: HashMap<String, VersionReq> = HashMap::new();
- for package in metadata.packages {
+ for package in metadata.packages.iter() {
for dep in &package.dependencies {
- let name = canonicalize_crate_name(&package.name);
- dependencies.insert(name, dep.req.clone());
+ if dep.target.is_none()
+ && matches!(dep.kind, DependencyKind::Build | DependencyKind::Normal)
+ {
+ trace!("package {} platform {:?}", package.name, dep.target);
+ let name = canonicalize_crate_name(&dep.name);
+ trace!("dependency on {} {}", name, dep.req);
+ dependencies.insert(name, dep.req.clone());
+ }
}
}
- info!("found {} dependencies", dependencies.len());
+ dependencies
+}
- info!("find dependencies that are not packaged");
+fn find_problems(packaged: &Crates, dependencies: &HashMap<String, VersionReq>) -> Vec<Problem> {
let mut problems = Problems::default();
for (name, req) in dependencies.iter() {
debug!("consider {}, version {} required", name, req);
if let Some(c) = packaged.get(name) {
if !req.matches(&c.version) {
- info!("crate {} is packaged, but version doesn't satisfy {}", c.orig_name, c.version);
- problems.push(Problem::MissingVersion(
- c.orig_name.clone(),
- req.clone(),
- c.version.clone(),
- ));
+ info!(
+ "crate {} is packaged, but version doesn't satisfy {}",
+ c.orig_name, c.version
+ );
+ problems.push(Problem::MissingVersion(MissingVersion {
+ name: c.orig_name.clone(),
+ req: req.clone(),
+ version: c.version.clone(),
+ }));
} else {
- info!("crate {} is packaged and version satisfies {}", c.orig_name, c.version);
+ info!(
+ "crate {} is packaged and version satisfies {}",
+ c.orig_name, c.version
+ );
}
} else {
info!("crate {} is not packaged at all", name);
- problems.push(Problem::MissingCrate(name.clone(), req.clone()));
+ problems.push(Problem::MissingCrate(MissingCrate {
+ name: name.clone(),
+ req: req.clone(),
+ }));
}
}
+ problems.to_vec()
+}
- let mut problems = problems.to_vec();
- if problems.is_empty() {
- info!("all good");
- Ok(())
- } else {
- problems.sort_by_key(|p| match p {
- Problem::MissingCrate(name, _) => name.clone(),
- Problem::MissingVersion(name, _, _) => name.clone(),
- });
- for p in problems.iter() {
- println!("{}", p);
+fn report_problems(mut problems: Vec<Problem>, style: &str) {
+ problems.sort_by_key(|p| match p {
+ Problem::MissingCrate(x) => x.name.clone(),
+ Problem::MissingVersion(x) => x.name.clone(),
+ });
+
+ let versions: Vec<&MissingVersion> = problems
+ .iter()
+ .filter_map(|p| match p {
+ Problem::MissingVersion(x) => Some(x),
+ _ => None,
+ })
+ .collect();
+
+ let crates: Vec<&MissingCrate> = problems
+ .iter()
+ .filter_map(|p| match p {
+ Problem::MissingCrate(x) => Some(x),
+ _ => None,
+ })
+ .collect();
+
+ let got_versions = !versions.is_empty();
+ if got_versions {
+
+ let table = match style {
+ "markdown" => Table::new(versions).with(Style::markdown()),
+ _ => Table::new(versions).with(Style::modern()),
+ };
+ println!("Table: Packaged version is not what is required");
+ println!();
+ println!("{}", table.to_string());
+ }
+
+ if !crates.is_empty() {
+ if got_versions {
+ println!();
}
- Err(anyhow!(
- "there were {} missing dependencies",
- problems.len()
- ))
+ let table = match style {
+ "markdown" => Table::new(crates).with(Style::markdown()),
+ _ => Table::new(crates).with(Style::modern()),
+ };
+ println!("Table: Required crate is not packaged at all");
+ println!();
+ println!("{}", table.to_string());
}
}
@@ -84,6 +162,18 @@ fn fallible_main() -> anyhow::Result<()> {
/// to "cargo tree" to select features you need.
#[derive(Parser)]
struct Args {
+ /// Ask cargo for all features.
+ #[clap(long)]
+ all_features: bool,
+
+ /// Ask cargo for specified features (use once per feature).
+ #[clap(short, long)]
+ features: Vec<String>,
+
+ /// Style of tables to use for reporting problems.
+ #[clap(long, possible_values = ["modern", "markdown"], default_value = "modern")]
+ style: String,
+
/// List of crates and versions packaged in the target operating system.
packaged: PathBuf,
@@ -99,6 +189,7 @@ struct Crate {
impl Crate {
fn new(name: &str, version: Version) -> Self {
+ trace!("found {} {}", name, version);
Self {
canonical_name: canonicalize_crate_name(name),
orig_name: name.into(),
@@ -123,6 +214,7 @@ impl Crates {
let mut crates = Crates::default();
for (lineno, line) in text.lines().enumerate() {
+ trace!("parsing line {}: {:?}", lineno, line);
let mut words = line.split(' ');
if let Some(name) = words.next() {
if let Some(version) = words.next() {
@@ -164,19 +256,30 @@ impl Crates {
#[derive(Debug, Clone, Eq, PartialEq)]
enum Problem {
- MissingCrate(String, VersionReq),
- MissingVersion(String, VersionReq, Version),
+ MissingCrate(MissingCrate),
+ MissingVersion(MissingVersion),
}
-impl fmt::Display for Problem {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- Self::MissingCrate(name, req) => write!(f, "missing-entirely {} {}", name, req),
- Self::MissingVersion(name, req, got) => {
- write!(f, "missing-version {} {} got {}", name, req, got)
- }
- }
- }
+#[derive(Debug, Clone, Eq, PartialEq, Tabled)]
+struct MissingCrate {
+ #[tabled(rename = "Crate")]
+ name: String,
+
+ #[tabled(rename = "Required version")]
+ req: VersionReq,
+}
+
+#[derive(Debug, Clone, Eq, PartialEq, Tabled)]
+#[tabled(rename = "Crates for which a packaged version is too old")]
+struct MissingVersion {
+ #[tabled(rename = "Crate")]
+ name: String,
+
+ #[tabled(rename = "Required version")]
+ req: VersionReq,
+
+ #[tabled(rename = "Packaged version")]
+ version: Version,
}
#[derive(Default, Debug)]
diff --git a/style.css b/style.css
new file mode 100644
index 0000000..83eacc7
--- /dev/null
+++ b/style.css
@@ -0,0 +1,18 @@
+<style>
+table {
+ margin-top: 5em;
+ width: 100%;
+}
+caption {
+ margin-bottom: 2em;
+}
+th, td {
+ text-align: left;
+}
+tr.even {
+ background: #f0f0f0;
+}
+tbody tr td:first-child {
+ font-family: monospace;
+}
+</style>