Skip to content

Commit e0345fb

Browse files
committed
add udeps task
1 parent f231155 commit e0345fb

9 files changed

Lines changed: 99 additions & 76 deletions

File tree

new/.github/workflows/push.yml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,15 @@ jobs:
2828
- uses: ./.github/actions/deps
2929
- name: Check vendor
3030
run: cargo oro vendor --check
31+
check-udeps:
32+
runs-on: ubuntu-latest
33+
steps:
34+
- uses: actions/checkout@v4
35+
with:
36+
submodules: 'true'
37+
- uses: ./.github/actions/deps
38+
- name: Check unused dependencies
39+
run: cargo oro udeps
3140
build-artifacts:
3241
runs-on: ubuntu-latest
3342
strategy:
@@ -46,4 +55,4 @@ jobs:
4655
- name: Build (release)
4756
run: cargo oro build ${{ matrix.artifact }} --release
4857
- name: Clippy
49-
run: cargo oro clippy ${{ matrix.artifact }}
58+
run: cargo oro clippy ${{ matrix.artifact }}

new/Cargo.lock

Lines changed: 0 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

new/artifact/aarch64-limine/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ edition = "2024"
66
publish = false
77

88
[dependencies]
9-
limine = "*"
9+
#limine = "*"
1010

1111
[package.metadata.oro]
1212
arch = "aarch64"

new/artifact/riscv64-limine/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ edition = "2024"
66
publish = false
77

88
[dependencies]
9-
limine = "*"
9+
#limine = "*"
1010

1111
[package.metadata.oro]
1212
arch = "riscv64"

new/artifact/x86_64-limine/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ edition = "2024"
66
publish = false
77

88
[dependencies]
9-
limine = "*"
9+
#limine = "*"
1010

1111
[package.metadata.oro]
1212
arch = "x86_64"

new/utils/build/src/cmd/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
pub mod build;
22
pub mod check;
33
pub mod clippy;
4+
pub mod udeps;
45
pub mod vendor;
56

67
pub fn cargo() -> std::process::Command {

new/utils/build/src/cmd/udeps.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
use crate::vfs::Lockfile;
2+
3+
pub fn run() {
4+
let vfs = crate::vfs::Vfs::new_from_cargo();
5+
let lockfile = vfs.lockfile().collect::<Vec<_>>();
6+
7+
let mut found = false;
8+
for dep in lockfile.unused() {
9+
eprintln!("unused dependency: {}", dep.package_name);
10+
found = true;
11+
}
12+
13+
if found {
14+
std::process::exit(1);
15+
}
16+
17+
eprintln!("no unused dependencies found");
18+
}

new/utils/build/src/main.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ enum Commands {
2626
/// Vendor sources and update references in `.cargo/config.toml`.
2727
#[command(override_usage = "cargo oro vendor [OPTIONS] [ARTIFACTS]")]
2828
Vendor(cmd::vendor::Args),
29+
/// Checks for unused dependencies.
30+
#[command(override_usage = "cargo oro udeps")]
31+
Udeps,
2932
}
3033

3134
fn main() {
@@ -36,5 +39,6 @@ fn main() {
3639
Commands::Clippy(args) => cmd::clippy::run(args),
3740
Commands::Check(args) => cmd::check::run(args),
3841
Commands::Vendor(args) => cmd::vendor::run(args),
42+
Commands::Udeps => cmd::udeps::run(),
3943
}
4044
}

new/utils/build/src/vfs.rs

Lines changed: 63 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use std::{
2-
collections::{HashMap, HashSet},
2+
collections::{HashMap, HashSet, VecDeque},
33
path::PathBuf,
44
};
55

@@ -82,39 +82,39 @@ pub struct Artifact {
8282
}
8383

8484
#[derive(serde::Deserialize)]
85-
struct CargoManifest {
86-
package: CargoPackage,
85+
pub struct CargoManifest {
86+
pub package: CargoPackage,
8787
}
8888

8989
#[derive(serde::Deserialize)]
90-
struct CargoPackage {
91-
name: String,
92-
description: Option<String>,
93-
metadata: CargoPackageMetadata,
90+
pub struct CargoPackage {
91+
pub name: String,
92+
pub description: Option<String>,
93+
pub metadata: Option<CargoPackageMetadata>,
9494
}
9595

9696
#[derive(serde::Deserialize)]
97-
struct CargoPackageMetadata {
98-
oro: CargoPackageMetadataOro,
97+
pub struct CargoPackageMetadata {
98+
pub oro: CargoPackageMetadataOro,
9999
}
100100

101101
#[derive(serde::Deserialize)]
102-
struct CargoPackageMetadataOro {
103-
arch: Arch,
104-
component: Component,
102+
pub struct CargoPackageMetadataOro {
103+
pub arch: Arch,
104+
pub component: Component,
105105
}
106106

107107
#[derive(serde::Deserialize)]
108-
struct CargoLockfile {
109-
version: u32,
110-
package: Vec<CargoLockfilePackage>,
108+
pub struct CargoLockfile {
109+
pub version: u32,
110+
pub package: Vec<CargoLockfilePackage>,
111111
}
112112

113113
#[derive(serde::Deserialize)]
114-
struct CargoLockfilePackage {
115-
name: String,
116-
source: Option<String>,
117-
dependencies: Option<Vec<String>>,
114+
pub struct CargoLockfilePackage {
115+
pub name: String,
116+
pub source: Option<String>,
117+
pub dependencies: Option<Vec<String>>,
118118
}
119119

120120
impl Vfs {
@@ -140,16 +140,16 @@ impl Vfs {
140140
self.read_artifact_dir().filter_map(|path| {
141141
let manifest = std::fs::read_to_string(path.join("Cargo.toml")).ok()?;
142142
let manifest: CargoManifest = toml::from_str(&manifest).ok()?;
143-
let target_triple = format!("{}-unknown-oro", manifest.package.metadata.oro.arch);
143+
let Some(metadata) = &manifest.package.metadata else {
144+
return None;
145+
};
146+
let target_triple = format!("{}-unknown-oro", metadata.oro.arch);
144147
Some(Artifact {
145148
path,
146-
name: format!(
147-
"{}-{}",
148-
manifest.package.metadata.oro.arch, manifest.package.metadata.oro.component
149-
),
149+
name: format!("{}-{}", metadata.oro.arch, metadata.oro.component),
150150
description: manifest.package.description,
151-
architecture: manifest.package.metadata.oro.arch,
152-
component: manifest.package.metadata.oro.component,
151+
architecture: metadata.oro.arch,
152+
component: metadata.oro.component,
153153
target_relative_path: PathBuf::from(&manifest.package.name),
154154
package_name: manifest.package.name,
155155
target_triple,
@@ -170,6 +170,14 @@ impl Vfs {
170170
.map(|entry| entry.path())
171171
}
172172

173+
/// # Panics
174+
/// Panics if the root `Cargo.toml` cannot be read.
175+
pub fn root_cargo_toml(&self) -> CargoManifest {
176+
let contents = std::fs::read_to_string(self.root_dir.join("Cargo.toml"))
177+
.expect("failed to read root Cargo.toml");
178+
toml::from_str(&contents).expect("failed to parse root Cargo.toml")
179+
}
180+
173181
/// # Panics
174182
/// Panics if the lockfile cannot be read.
175183
pub fn lockfile(&self) -> impl Iterator<Item = LockEntry> {
@@ -181,12 +189,15 @@ impl Vfs {
181189
lockfile.version, 4,
182190
"Cargo.lock version 4 is the only version supported"
183191
);
184-
lockfile.package.into_iter().map(|e| {
185-
LockEntry {
186-
package_name: e.name,
187-
is_registry: e.source.is_some_and(|s| s.starts_with("registry+")),
188-
dependencies: e.dependencies.unwrap_or_default(),
189-
}
192+
let root_package_name = self.root_cargo_toml().package.name;
193+
lockfile.package.into_iter().filter_map(move |e| {
194+
(e.name != root_package_name).then(|| {
195+
LockEntry {
196+
package_name: e.name,
197+
is_registry: e.source.is_some_and(|s| s.starts_with("registry+")),
198+
dependencies: e.dependencies.unwrap_or_default(),
199+
}
200+
})
190201
})
191202
}
192203

@@ -224,44 +235,33 @@ pub trait Lockfile: IntoIterator<Item = LockEntry> + Sized {
224235
.collect()
225236
}
226237

227-
fn used(self) -> impl Iterator<Item = LockEntry>
228-
where
229-
Self: Clone,
230-
{
238+
fn used(self) -> impl Iterator<Item = LockEntry> {
239+
let map = self.into_map();
231240
let mut seen = HashSet::new();
241+
let mut queue = map
242+
.iter()
243+
.filter_map(|(n, e)| (!e.is_registry).then(|| n.to_string()))
244+
.collect::<VecDeque<_>>();
245+
246+
while let Some(name) = queue.pop_back() {
247+
if !seen.insert(name.clone()) {
248+
continue;
249+
}
232250

233-
for item in self.clone().into_iter() {
234-
if item.is_registry {
235-
for dep in item.dependencies {
236-
seen.insert(dep);
237-
}
238-
} else {
239-
seen.insert(item.package_name);
251+
for dep in &map[&name].dependencies {
252+
queue.push_back(dep.into());
240253
}
241254
}
242255

243-
self.into_iter()
244-
.filter(move |e| seen.contains(&e.package_name))
256+
map.into_iter()
257+
.filter_map(move |(k, e)| seen.contains(&k).then(move || e))
245258
}
246259

247-
fn unused(self) -> impl Iterator<Item = LockEntry>
248-
where
249-
Self: Clone,
250-
{
251-
let mut seen = HashSet::new();
252-
253-
for item in self.clone().into_iter() {
254-
if item.is_registry {
255-
for dep in item.dependencies {
256-
seen.insert(dep);
257-
}
258-
} else {
259-
seen.insert(item.package_name);
260-
}
261-
}
262-
263-
self.into_iter()
264-
.filter(move |e| !seen.contains(&e.package_name))
260+
fn unused(self) -> impl Iterator<Item = LockEntry> {
261+
let all: Vec<LockEntry> = self.into_iter().collect();
262+
let used: HashSet<String> = all.iter().cloned().used().map(|e| e.package_name).collect();
263+
all.into_iter()
264+
.filter(move |e| !used.contains(&e.package_name))
265265
}
266266

267267
fn dependencies(self) -> impl Iterator<Item = LockEntry> {

0 commit comments

Comments
 (0)