Skip to content

Commit 7b4e438

Browse files
authored
Add Rust script to verify centralized dependencies (#1618)
* Add Rust script to verify centralized dependencies Partial, short-term solution for #1591. Still need to hook it into the build after we centralize all dependencies. * Check all dependencies
1 parent f72ad46 commit 7b4e438

File tree

1 file changed

+200
-0
lines changed

1 file changed

+200
-0
lines changed

eng/scripts/verify-dependencies.rs

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
#!/usr/bin/env -S cargo +nightly -Zscript
2+
---
3+
[package]
4+
edition = "2021"
5+
6+
[dependencies]
7+
cargo-util-schemas = "0.1.0"
8+
serde = { version = "1.0.197", features = ["derive"] }
9+
serde_json = "1.0.114"
10+
toml = "0.8.10"
11+
---
12+
13+
use cargo_util_schemas::manifest::{InheritableDependency, TomlDependency, TomlManifest};
14+
use serde::Deserialize;
15+
use std::{
16+
io::Read,
17+
path::PathBuf,
18+
process::{Command, Stdio},
19+
};
20+
21+
fn main() {
22+
let manifest_path = std::env::args()
23+
.nth(1)
24+
.or_else(|| {
25+
find_file(
26+
std::env::current_dir().expect("current directory"),
27+
"Cargo.toml",
28+
)
29+
})
30+
.expect("manifest path");
31+
32+
let package_manifest_path = package_manifest_path(&manifest_path);
33+
let workspace_manifest_path = workspace_manifest_path(&manifest_path);
34+
35+
eprintln!("Checking {}", package_manifest_path.display());
36+
let package_manifest_content =
37+
std::fs::read_to_string(package_manifest_path).expect("read package manifest");
38+
let package_manifest: TomlManifest =
39+
toml::from_str(&package_manifest_content).expect("deserialize package manifest");
40+
41+
// Collect all package dependencies including in platform targets.
42+
let mut all_dependencies = vec![
43+
(
44+
"dependencies".to_string(),
45+
package_manifest.dependencies.as_ref(),
46+
),
47+
(
48+
"dev-dependencies".to_string(),
49+
package_manifest.dev_dependencies(),
50+
),
51+
(
52+
"build-dependencies".to_string(),
53+
package_manifest.build_dependencies(),
54+
),
55+
];
56+
if let Some(targets) = package_manifest.target.as_ref() {
57+
for (target, platform) in targets {
58+
all_dependencies.push((
59+
format!("target.'{}'.dependencies", target),
60+
platform.dependencies.as_ref(),
61+
));
62+
all_dependencies.push((
63+
format!("target.'{}'.dev-dependencies", target),
64+
platform.dev_dependencies(),
65+
));
66+
all_dependencies.push((
67+
format!("target.'{}'.build-dependencies", target),
68+
platform.build_dependencies(),
69+
));
70+
}
71+
}
72+
73+
let mut dependencies: Vec<Package> = all_dependencies
74+
.into_iter()
75+
.filter_map(|v| {
76+
if let Some(dependencies) = v.1 {
77+
return Some((v.0, dependencies));
78+
}
79+
None
80+
})
81+
.flat_map(|v| std::iter::repeat(v.0).zip(v.1.into_iter()))
82+
.filter_map(|v| match v.1 .1 {
83+
InheritableDependency::Value(dep) => match dep {
84+
TomlDependency::Simple(_) => Some(Package {
85+
section: v.0,
86+
name: v.1 .0.to_string(),
87+
}),
88+
TomlDependency::Detailed(details) if details.path.is_none() => Some(Package {
89+
section: v.0,
90+
name: v.1 .0.to_string(),
91+
}),
92+
_ => None,
93+
},
94+
InheritableDependency::Inherit(_) => None,
95+
})
96+
.collect();
97+
98+
if !dependencies.is_empty() {
99+
dependencies.sort();
100+
println!(
101+
"The following dependencies do not inherit from workspace `{}`:\n",
102+
workspace_manifest_path.display()
103+
);
104+
println!(
105+
"* {}\n",
106+
dependencies
107+
.into_iter()
108+
.map(|v| v.to_string())
109+
.collect::<Vec<String>>()
110+
.join("\n* ")
111+
);
112+
println!("Add dependencies to workspace and change the package dependency to `{{ workspace = true }}`.");
113+
println!("See <https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#inheriting-a-dependency-from-a-workspace> for more information.");
114+
115+
std::process::exit(1);
116+
}
117+
}
118+
119+
fn package_manifest_path(manifest_path: &str) -> PathBuf {
120+
let mut cmd = Command::new("cargo")
121+
.args(&["read-manifest", "--manifest-path", manifest_path])
122+
.stdout(Stdio::piped())
123+
.spawn()
124+
.expect("executing cargo read-manifest");
125+
126+
let path: PathBuf = read_manifest(&mut cmd)
127+
.manifest_path
128+
.expect("manifest_path")
129+
.into();
130+
131+
if !path.exists() {
132+
panic!("package manifest not found");
133+
}
134+
135+
path
136+
}
137+
138+
fn workspace_manifest_path(manifest_path: &str) -> PathBuf {
139+
let mut cmd = Command::new("cargo")
140+
.args(&[
141+
"metadata",
142+
"--format-version",
143+
"1",
144+
"--all-features",
145+
"--manifest-path",
146+
manifest_path,
147+
])
148+
.stdout(Stdio::piped())
149+
.spawn()
150+
.expect("executing cargo metadata");
151+
152+
let path: PathBuf = read_manifest(&mut cmd)
153+
.workspace_root
154+
.expect("workspace_root")
155+
.into();
156+
let path = path.join("Cargo.toml");
157+
158+
if !path.exists() {
159+
panic!("workspace manifest not found");
160+
}
161+
162+
path
163+
}
164+
165+
fn find_file(dir: impl AsRef<std::path::Path>, name: &str) -> Option<String> {
166+
for dir in dir.as_ref().ancestors() {
167+
let path = dir.join(name);
168+
if path.exists() {
169+
return Some(path.to_str().unwrap().into());
170+
}
171+
}
172+
None
173+
}
174+
175+
fn read_manifest(cmd: &mut std::process::Child) -> CargoManifest {
176+
let mut reader = std::io::BufReader::new(cmd.stdout.take().expect("buffering stdout"));
177+
178+
let mut content: String = Default::default();
179+
reader.read_to_string(&mut content).expect("reading stdout");
180+
181+
serde_json::from_str::<CargoManifest>(&content).expect("deserializing manifest")
182+
}
183+
184+
#[derive(Deserialize)]
185+
struct CargoManifest {
186+
pub manifest_path: Option<String>,
187+
pub workspace_root: Option<String>,
188+
}
189+
190+
#[derive(PartialEq, Eq, PartialOrd, Ord)]
191+
struct Package {
192+
name: String,
193+
section: String,
194+
}
195+
196+
impl std::fmt::Display for Package {
197+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
198+
write!(f, "{} ({})", self.name, self.section)
199+
}
200+
}

0 commit comments

Comments
 (0)