Skip to content

Commit

Permalink
rewrite check-workspace-deps as an xtask (#3710)
Browse files Browse the repository at this point in the history
  • Loading branch information
davepacheco authored Aug 23, 2023
1 parent 94cde6d commit 05a9fa3
Show file tree
Hide file tree
Showing 11 changed files with 172 additions and 75 deletions.
6 changes: 1 addition & 5 deletions .github/workflows/check-workspace-deps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,5 @@ jobs:
runs-on: ubuntu-22.04
steps:
- uses: actions/[email protected]
- name: Install jq
run: sudo apt-get install -y jq
- name: Install toml-cli
run: cargo install [email protected]
- name: Check Workspace Dependencies
run: ./tools/ci_check_workspace_deps.sh
run: cargo xtask check-workspace-deps
38 changes: 37 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ omicron-dev-tools = { path = "dev-tools" }
omicron-gateway = { path = "gateway" }
omicron-nexus = { path = "nexus" }
omicron-package = { path = "package" }
omicron-rpaths = { path = "rpaths" }
omicron-sled-agent = { path = "sled-agent" }
omicron-test-utils = { path = "test-utils" }
omicron-zone-package = "0.8.3"
Expand Down
4 changes: 2 additions & 2 deletions bootstore/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ version = "0.1.0"
edition = "2021"
license = "MPL-2.0"

[build-dependencies.omicron-rpaths]
path = "../rpaths"
[build-dependencies]
omicron-rpaths.workspace = true

[dependencies]
bytes.workspace = true
Expand Down
4 changes: 2 additions & 2 deletions dev-tools/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ version = "0.1.0"
edition = "2021"
license = "MPL-2.0"

[build-dependencies.omicron-rpaths]
path = "../rpaths"
[build-dependencies]
omicron-rpaths.workspace = true

[dependencies]
anyhow.workspace = true
Expand Down
4 changes: 2 additions & 2 deletions nexus/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ version = "0.1.0"
edition = "2021"
license = "MPL-2.0"

[build-dependencies.omicron-rpaths]
path = "../rpaths"
[build-dependencies]
omicron-rpaths.workspace = true

[dependencies]
anyhow.workspace = true
Expand Down
4 changes: 2 additions & 2 deletions nexus/db-model/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ version = "0.1.0"
edition = "2021"
license = "MPL-2.0"

[build-dependencies.omicron-rpaths]
path = "../../rpaths"
[build-dependencies]
omicron-rpaths.workspace = true

[dependencies]
anyhow.workspace = true
Expand Down
4 changes: 2 additions & 2 deletions nexus/db-queries/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ version = "0.1.0"
edition = "2021"
license = "MPL-2.0"

[build-dependencies.omicron-rpaths]
path = "../../rpaths"
[build-dependencies]
omicron-rpaths.workspace = true

[dependencies]
anyhow.workspace = true
Expand Down
58 changes: 0 additions & 58 deletions tools/ci_check_workspace_deps.sh

This file was deleted.

3 changes: 3 additions & 0 deletions xtask/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,7 @@ edition = "2021"

[dependencies]
anyhow.workspace = true
camino.workspace = true
cargo_toml = "0.15"
cargo_metadata = "0.15"
clap.workspace = true
121 changes: 120 additions & 1 deletion xtask/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@
//! See <https://github.com/matklad/cargo-xtask>.
use anyhow::{bail, Context, Result};
use camino::Utf8Path;
use cargo_metadata::Metadata;
use cargo_toml::{Dependency, Manifest};
use clap::{Parser, Subcommand};
use std::process::Command;
use std::{collections::BTreeMap, process::Command};

#[derive(Parser)]
#[command(name = "cargo xtask", about = "Workspace-related developer tools")]
Expand All @@ -19,6 +22,9 @@ struct Args {

#[derive(Subcommand)]
enum Cmds {
/// Check that dependencies are not duplicated in any packages in the
/// workspace
CheckWorkspaceDeps,
/// Run configured clippy checks
Clippy,
}
Expand All @@ -27,6 +33,7 @@ fn main() -> Result<()> {
let args = Args::parse();
match args.cmd {
Cmds::Clippy => cmd_clippy(),
Cmds::CheckWorkspaceDeps => cmd_check_workspace_deps(),
}
}

Expand Down Expand Up @@ -70,3 +77,115 @@ fn cmd_clippy() -> Result<()> {

Ok(())
}

fn cmd_check_workspace_deps() -> Result<()> {
// Ignore issues with "pq-sys". See the omicron-rpaths package for details.
const EXCLUDED: &[&'static str] = &["pq-sys"];

// Collect a list of all packages used in any workspace package as a
// workspace dependency.
let mut workspace_dependencies = BTreeMap::new();

// Collect a list of all packages used in any workspace package as a
// NON-workspace dependency.
let mut non_workspace_dependencies = BTreeMap::new();

// Load information about the Cargo workspace.
let workspace = load_workspace()?;
let mut nwarnings = 0;
let mut nerrors = 0;

// Iterate the workspace packages and fill out the maps above.
for pkg_info in workspace.workspace_packages() {
let manifest_path = &pkg_info.manifest_path;
let manifest = read_cargo_toml(manifest_path)?;
for tree in [
&manifest.dependencies,
&manifest.dev_dependencies,
&manifest.build_dependencies,
] {
for (name, dep) in tree {
if let Dependency::Inherited(inherited) = dep {
if inherited.workspace {
workspace_dependencies
.entry(name.to_owned())
.or_insert_with(Vec::new)
.push(pkg_info.name.clone());

if !inherited.features.is_empty() {
eprintln!(
"warning: package is used as a workspace dep \
with extra features: {:?} (in {:?})",
name, pkg_info.name,
);
nwarnings += 1;
}

continue;
}
}

non_workspace_dependencies
.entry(name.to_owned())
.or_insert_with(Vec::new)
.push(pkg_info.name.clone());
}
}
}

// Look for any packages that are used as both a workspace dependency and a
// non-workspace dependency. Generally, the non-workspace dependency should
// be replaced with a workspace dependency.
for (pkgname, ws_examples) in &workspace_dependencies {
if let Some(non_ws_examples) = non_workspace_dependencies.get(pkgname) {
eprintln!(
"error: package is used as both a workspace dep and a \
non-workspace dep: {:?}",
pkgname
);
eprintln!(" workspace dep: {}", ws_examples.join(", "));
eprintln!(" non-workspace dep: {}", non_ws_examples.join(", "));
nerrors += 1;
}
}

// Look for any packages used as non-workspace dependencies by more than one
// workspace package. These should generally be moved to a workspace
// dependency.
for (pkgname, examples) in
non_workspace_dependencies.iter().filter(|(pkgname, examples)| {
examples.len() > 1 && !EXCLUDED.contains(&pkgname.as_str())
})
{
eprintln!(
"error: package is used by multiple workspace packages without \
a workspace dependency: {:?}",
pkgname
);
eprintln!(" used in: {}", examples.join(", "));
nerrors += 1;
}

eprintln!(
"check-workspace-deps: errors: {}, warnings: {}",
nerrors, nwarnings
);

if nerrors != 0 {
bail!("errors with workspace dependencies");
}

Ok(())
}

fn read_cargo_toml(path: &Utf8Path) -> Result<Manifest> {
let bytes =
std::fs::read(path).with_context(|| format!("read {:?}", path))?;
Manifest::from_slice(&bytes).with_context(|| format!("parse {:?}", path))
}

fn load_workspace() -> Result<Metadata> {
cargo_metadata::MetadataCommand::new()
.exec()
.context("loading cargo metadata")
}

0 comments on commit 05a9fa3

Please sign in to comment.