]>
Commit | Line | Data |
---|---|---|
ba9703b0 | 1 | //! Checks the licenses of third-party dependencies. |
476ff2be | 2 | |
ba9703b0 XL |
3 | use cargo_metadata::{Metadata, Package, PackageId, Resolve}; |
4 | use std::collections::{BTreeSet, HashSet}; | |
476ff2be SL |
5 | use std::path::Path; |
6 | ||
ba9703b0 XL |
7 | /// These are licenses that are allowed for all crates, including the runtime, |
8 | /// rustc, tools, etc. | |
b7449926 | 9 | const LICENSES: &[&str] = &[ |
8bb4bdeb XL |
10 | "MIT/Apache-2.0", |
11 | "MIT / Apache-2.0", | |
12 | "Apache-2.0/MIT", | |
3b2f2976 | 13 | "Apache-2.0 / MIT", |
8bb4bdeb | 14 | "MIT OR Apache-2.0", |
416331ca | 15 | "Apache-2.0 OR MIT", |
e1599b0c | 16 | "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", // wasi license |
8bb4bdeb | 17 | "MIT", |
a2a8927a | 18 | "ISC", |
8bb4bdeb | 19 | "Unlicense/MIT", |
8faf50e0 | 20 | "Unlicense OR MIT", |
3dfed10e XL |
21 | "0BSD OR MIT OR Apache-2.0", // adler license |
22 | "Zlib OR Apache-2.0 OR MIT", // tinyvec | |
8bb4bdeb XL |
23 | ]; |
24 | ||
0531ce1d XL |
25 | /// These are exceptions to Rust's permissive licensing policy, and |
26 | /// should be considered bugs. Exceptions are only allowed in Rust | |
27 | /// tooling. It is _crucial_ that no exception crates be dependencies | |
9fa01778 | 28 | /// of the Rust runtime (std/test). |
ba9703b0 | 29 | const EXCEPTIONS: &[(&str, &str)] = &[ |
5099ac24 FG |
30 | ("mdbook", "MPL-2.0"), // mdbook |
31 | ("openssl", "Apache-2.0"), // cargo, mdbook | |
32 | ("colored", "MPL-2.0"), // rustfmt | |
33 | ("ordslice", "Apache-2.0"), // rls | |
34 | ("ryu", "Apache-2.0 OR BSL-1.0"), // rls/cargo/... (because of serde) | |
35 | ("bytesize", "Apache-2.0"), // cargo | |
36 | ("im-rc", "MPL-2.0+"), // cargo | |
37 | ("sized-chunks", "MPL-2.0+"), // cargo via im-rc | |
38 | ("bitmaps", "MPL-2.0+"), // cargo via im-rc | |
39 | ("instant", "BSD-3-Clause"), // rustc_driver/tracing-subscriber/parking_lot | |
40 | ("snap", "BSD-3-Clause"), // rustc | |
04454e1e FG |
41 | ("fluent-langneg", "Apache-2.0"), // rustc (fluent translations) |
42 | ("self_cell", "Apache-2.0"), // rustc (fluent translations) | |
416331ca | 43 | // FIXME: this dependency violates the documentation comment above: |
ba9703b0 | 44 | ("fortanix-sgx-abi", "MPL-2.0"), // libstd but only for `sgx` target |
04454e1e FG |
45 | ("dunce", "CC0-1.0"), // cargo (dev dependency) |
46 | ("similar", "Apache-2.0"), // cargo (dev dependency) | |
47 | ("normalize-line-endings", "Apache-2.0"), // cargo (dev dependency) | |
0531ce1d XL |
48 | ]; |
49 | ||
17df50a5 XL |
50 | const EXCEPTIONS_CRANELIFT: &[(&str, &str)] = &[ |
51 | ("cranelift-bforest", "Apache-2.0 WITH LLVM-exception"), | |
52 | ("cranelift-codegen", "Apache-2.0 WITH LLVM-exception"), | |
53 | ("cranelift-codegen-meta", "Apache-2.0 WITH LLVM-exception"), | |
54 | ("cranelift-codegen-shared", "Apache-2.0 WITH LLVM-exception"), | |
55 | ("cranelift-entity", "Apache-2.0 WITH LLVM-exception"), | |
56 | ("cranelift-frontend", "Apache-2.0 WITH LLVM-exception"), | |
57 | ("cranelift-jit", "Apache-2.0 WITH LLVM-exception"), | |
58 | ("cranelift-module", "Apache-2.0 WITH LLVM-exception"), | |
59 | ("cranelift-native", "Apache-2.0 WITH LLVM-exception"), | |
60 | ("cranelift-object", "Apache-2.0 WITH LLVM-exception"), | |
17df50a5 XL |
61 | ("mach", "BSD-2-Clause"), |
62 | ("regalloc", "Apache-2.0 WITH LLVM-exception"), | |
63 | ("target-lexicon", "Apache-2.0 WITH LLVM-exception"), | |
64 | ]; | |
65 | ||
ba9703b0 XL |
66 | /// These are the root crates that are part of the runtime. The licenses for |
67 | /// these and all their dependencies *must not* be in the exception list. | |
68 | const RUNTIME_CRATES: &[&str] = &["std", "core", "alloc", "test", "panic_abort", "panic_unwind"]; | |
69 | ||
f035d41b | 70 | /// Crates whose dependencies must be explicitly permitted. |
17df50a5 | 71 | const RESTRICTED_DEPENDENCY_CRATES: &[&str] = &["rustc_driver", "rustc_codegen_llvm"]; |
476ff2be | 72 | |
f035d41b | 73 | /// Crates rustc is allowed to depend on. Avoid adding to the list if possible. |
ba9703b0 XL |
74 | /// |
75 | /// This list is here to provide a speed-bump to adding a new dependency to | |
76 | /// rustc. Please check with the compiler team before adding an entry. | |
f035d41b | 77 | const PERMITTED_DEPENDENCIES: &[&str] = &[ |
3dfed10e XL |
78 | "addr2line", |
79 | "adler", | |
5099ac24 | 80 | "ahash", |
ba9703b0 XL |
81 | "aho-corasick", |
82 | "annotate-snippets", | |
83 | "ansi_term", | |
84 | "arrayvec", | |
85 | "atty", | |
86 | "autocfg", | |
ba9703b0 XL |
87 | "bitflags", |
88 | "block-buffer", | |
89 | "block-padding", | |
3dfed10e | 90 | "byte-tools", |
3c0e092e | 91 | "byteorder", |
ba9703b0 XL |
92 | "cc", |
93 | "cfg-if", | |
f9f354fc | 94 | "chalk-derive", |
17df50a5 | 95 | "chalk-engine", |
f9f354fc | 96 | "chalk-ir", |
17df50a5 XL |
97 | "chalk-solve", |
98 | "chrono", | |
ba9703b0 XL |
99 | "cmake", |
100 | "compiler_builtins", | |
5099ac24 | 101 | "cpufeatures", |
ba9703b0 | 102 | "crc32fast", |
923072b8 | 103 | "crossbeam-channel", |
ba9703b0 XL |
104 | "crossbeam-deque", |
105 | "crossbeam-epoch", | |
ba9703b0 | 106 | "crossbeam-utils", |
5099ac24 | 107 | "crypto-common", |
6a06907d | 108 | "cstr", |
ba9703b0 | 109 | "datafrog", |
3dfed10e | 110 | "difference", |
ba9703b0 XL |
111 | "digest", |
112 | "dlmalloc", | |
113 | "either", | |
114 | "ena", | |
115 | "env_logger", | |
3dfed10e | 116 | "expect-test", |
ba9703b0 | 117 | "fake-simd", |
a2a8927a | 118 | "fallible-iterator", // dependency of `thorin` |
ba9703b0 | 119 | "filetime", |
17df50a5 | 120 | "fixedbitset", |
ba9703b0 | 121 | "flate2", |
04454e1e FG |
122 | "fluent-bundle", |
123 | "fluent-langneg", | |
124 | "fluent-syntax", | |
ba9703b0 | 125 | "fortanix-sgx-abi", |
ba9703b0 XL |
126 | "generic-array", |
127 | "getopts", | |
128 | "getrandom", | |
3dfed10e | 129 | "gimli", |
fc512014 | 130 | "gsgdt", |
ba9703b0 XL |
131 | "hashbrown", |
132 | "hermit-abi", | |
133 | "humantime", | |
94222f64 | 134 | "if_chain", |
ba9703b0 | 135 | "indexmap", |
3dfed10e | 136 | "instant", |
04454e1e FG |
137 | "intl-memoizer", |
138 | "intl_pluralrules", | |
ba9703b0 | 139 | "itertools", |
17df50a5 | 140 | "itoa", |
ba9703b0 | 141 | "jobserver", |
ba9703b0 XL |
142 | "lazy_static", |
143 | "libc", | |
a2a8927a | 144 | "libloading", |
ba9703b0 XL |
145 | "libz-sys", |
146 | "lock_api", | |
147 | "log", | |
17df50a5 | 148 | "matchers", |
ba9703b0 XL |
149 | "md-5", |
150 | "measureme", | |
151 | "memchr", | |
6a06907d | 152 | "memmap2", |
ba9703b0 XL |
153 | "memoffset", |
154 | "miniz_oxide", | |
17df50a5 XL |
155 | "num-integer", |
156 | "num-traits", | |
3c0e092e | 157 | "num_cpus", |
3dfed10e | 158 | "object", |
c295e0f8 | 159 | "odht", |
f9f354fc | 160 | "once_cell", |
ba9703b0 XL |
161 | "opaque-debug", |
162 | "parking_lot", | |
163 | "parking_lot_core", | |
3dfed10e | 164 | "pathdiff", |
6a06907d | 165 | "perf-event-open-sys", |
17df50a5 | 166 | "petgraph", |
6a06907d | 167 | "pin-project-lite", |
ba9703b0 XL |
168 | "pkg-config", |
169 | "polonius-engine", | |
170 | "ppv-lite86", | |
04454e1e | 171 | "proc-macro-hack", |
ba9703b0 | 172 | "proc-macro2", |
f9f354fc | 173 | "psm", |
ba9703b0 XL |
174 | "punycode", |
175 | "quick-error", | |
176 | "quote", | |
177 | "rand", | |
178 | "rand_chacha", | |
179 | "rand_core", | |
180 | "rand_hc", | |
ba9703b0 | 181 | "rand_xorshift", |
c295e0f8 | 182 | "rand_xoshiro", |
ba9703b0 | 183 | "redox_syscall", |
ba9703b0 | 184 | "regex", |
17df50a5 | 185 | "regex-automata", |
ba9703b0 XL |
186 | "regex-syntax", |
187 | "remove_dir_all", | |
17df50a5 XL |
188 | "rls-data", |
189 | "rls-span", | |
ba9703b0 XL |
190 | "rustc-demangle", |
191 | "rustc-hash", | |
192 | "rustc-rayon", | |
193 | "rustc-rayon-core", | |
194 | "rustc_version", | |
17df50a5 | 195 | "ryu", |
ba9703b0 XL |
196 | "scoped-tls", |
197 | "scopeguard", | |
04454e1e | 198 | "self_cell", |
ba9703b0 | 199 | "semver", |
ba9703b0 XL |
200 | "serde", |
201 | "serde_derive", | |
17df50a5 | 202 | "serde_json", |
ba9703b0 | 203 | "sha-1", |
29967ef6 | 204 | "sha2", |
17df50a5 | 205 | "sharded-slab", |
3c0e092e | 206 | "smallvec", |
1b1a35ee | 207 | "snap", |
ba9703b0 | 208 | "stable_deref_trait", |
f9f354fc | 209 | "stacker", |
ba9703b0 XL |
210 | "syn", |
211 | "synstructure", | |
212 | "tempfile", | |
213 | "termcolor", | |
ba9703b0 | 214 | "termize", |
04454e1e FG |
215 | "thiserror", |
216 | "thiserror-impl", | |
a2a8927a | 217 | "thorin-dwp", |
ba9703b0 | 218 | "thread_local", |
17df50a5 | 219 | "time", |
04454e1e | 220 | "tinystr", |
17df50a5 | 221 | "tinyvec", |
3dfed10e XL |
222 | "tracing", |
223 | "tracing-attributes", | |
224 | "tracing-core", | |
17df50a5 | 225 | "tracing-log", |
17df50a5 XL |
226 | "tracing-subscriber", |
227 | "tracing-tree", | |
04454e1e | 228 | "type-map", |
ba9703b0 | 229 | "typenum", |
3c0e092e XL |
230 | "unic-char-property", |
231 | "unic-char-range", | |
232 | "unic-common", | |
233 | "unic-emoji-char", | |
04454e1e FG |
234 | "unic-langid", |
235 | "unic-langid-impl", | |
236 | "unic-langid-macros", | |
237 | "unic-langid-macros-impl", | |
3c0e092e | 238 | "unic-ucd-version", |
ba9703b0 XL |
239 | "unicode-normalization", |
240 | "unicode-script", | |
241 | "unicode-security", | |
242 | "unicode-width", | |
243 | "unicode-xid", | |
ba9703b0 XL |
244 | "vcpkg", |
245 | "version_check", | |
246 | "wasi", | |
247 | "winapi", | |
ba9703b0 XL |
248 | "winapi-i686-pc-windows-gnu", |
249 | "winapi-util", | |
250 | "winapi-x86_64-pc-windows-gnu", | |
04454e1e FG |
251 | // this is a false-positive: it's only used by rustfmt, but because it's enabled through a |
252 | // feature, tidy thinks it's used by rustc as well. | |
cdc7bbd5 | 253 | "yansi-term", |
0531ce1d XL |
254 | ]; |
255 | ||
17df50a5 XL |
256 | const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[ |
257 | "anyhow", | |
258 | "ar", | |
259 | "autocfg", | |
260 | "bitflags", | |
261 | "cfg-if", | |
262 | "cranelift-bforest", | |
263 | "cranelift-codegen", | |
264 | "cranelift-codegen-meta", | |
265 | "cranelift-codegen-shared", | |
266 | "cranelift-entity", | |
267 | "cranelift-frontend", | |
268 | "cranelift-jit", | |
269 | "cranelift-module", | |
270 | "cranelift-native", | |
271 | "cranelift-object", | |
272 | "crc32fast", | |
273 | "gimli", | |
274 | "hashbrown", | |
275 | "indexmap", | |
276 | "libc", | |
277 | "libloading", | |
278 | "log", | |
279 | "mach", | |
136023e0 | 280 | "memchr", |
17df50a5 | 281 | "object", |
5e7ed085 | 282 | "once_cell", |
17df50a5 XL |
283 | "regalloc", |
284 | "region", | |
285 | "rustc-hash", | |
286 | "smallvec", | |
287 | "target-lexicon", | |
288 | "winapi", | |
289 | "winapi-i686-pc-windows-gnu", | |
290 | "winapi-x86_64-pc-windows-gnu", | |
291 | ]; | |
292 | ||
293 | const FORBIDDEN_TO_HAVE_DUPLICATES: &[&str] = &[ | |
294 | // These two crates take quite a long time to build, so don't allow two versions of them | |
295 | // to accidentally sneak into our dependency graph, in order to ensure we keep our CI times | |
296 | // under control. | |
297 | "cargo", | |
17df50a5 XL |
298 | ]; |
299 | ||
ba9703b0 XL |
300 | /// Dependency checks. |
301 | /// | |
3dfed10e XL |
302 | /// `root` is path to the directory with the root `Cargo.toml` (for the workspace). `cargo` is path |
303 | /// to the cargo executable. | |
304 | pub fn check(root: &Path, cargo: &Path, bad: &mut bool) { | |
ba9703b0 XL |
305 | let mut cmd = cargo_metadata::MetadataCommand::new(); |
306 | cmd.cargo_path(cargo) | |
3dfed10e | 307 | .manifest_path(root.join("Cargo.toml")) |
ba9703b0 XL |
308 | .features(cargo_metadata::CargoOpt::AllFeatures); |
309 | let metadata = t!(cmd.exec()); | |
17df50a5 XL |
310 | let runtime_ids = compute_runtime_crates(&metadata); |
311 | check_exceptions(&metadata, EXCEPTIONS, runtime_ids, bad); | |
312 | check_dependencies(&metadata, PERMITTED_DEPENDENCIES, RESTRICTED_DEPENDENCY_CRATES, bad); | |
313 | check_crate_duplicate(&metadata, FORBIDDEN_TO_HAVE_DUPLICATES, bad); | |
136023e0 | 314 | check_rustfix(&metadata, bad); |
17df50a5 XL |
315 | |
316 | // Check rustc_codegen_cranelift independently as it has it's own workspace. | |
317 | let mut cmd = cargo_metadata::MetadataCommand::new(); | |
318 | cmd.cargo_path(cargo) | |
319 | .manifest_path(root.join("compiler/rustc_codegen_cranelift/Cargo.toml")) | |
320 | .features(cargo_metadata::CargoOpt::AllFeatures); | |
321 | let metadata = t!(cmd.exec()); | |
322 | let runtime_ids = HashSet::new(); | |
323 | check_exceptions(&metadata, EXCEPTIONS_CRANELIFT, runtime_ids, bad); | |
324 | check_dependencies( | |
325 | &metadata, | |
326 | PERMITTED_CRANELIFT_DEPENDENCIES, | |
327 | &["rustc_codegen_cranelift"], | |
328 | bad, | |
329 | ); | |
330 | check_crate_duplicate(&metadata, &[], bad); | |
0531ce1d XL |
331 | } |
332 | ||
ba9703b0 XL |
333 | /// Check that all licenses are in the valid list in `LICENSES`. |
334 | /// | |
335 | /// Packages listed in `EXCEPTIONS` are allowed for tools. | |
17df50a5 XL |
336 | fn check_exceptions( |
337 | metadata: &Metadata, | |
338 | exceptions: &[(&str, &str)], | |
339 | runtime_ids: HashSet<&PackageId>, | |
340 | bad: &mut bool, | |
341 | ) { | |
ba9703b0 | 342 | // Validate the EXCEPTIONS list hasn't changed. |
17df50a5 | 343 | for (name, license) in exceptions { |
ba9703b0 XL |
344 | // Check that the package actually exists. |
345 | if !metadata.packages.iter().any(|p| p.name == *name) { | |
fc512014 XL |
346 | tidy_error!( |
347 | bad, | |
ba9703b0 XL |
348 | "could not find exception package `{}`\n\ |
349 | Remove from EXCEPTIONS list if it is no longer used.", | |
350 | name | |
351 | ); | |
ba9703b0 XL |
352 | } |
353 | // Check that the license hasn't changed. | |
354 | for pkg in metadata.packages.iter().filter(|p| p.name == *name) { | |
ba9703b0 XL |
355 | match &pkg.license { |
356 | None => { | |
fc512014 XL |
357 | tidy_error!( |
358 | bad, | |
ba9703b0 XL |
359 | "dependency exception `{}` does not declare a license expression", |
360 | pkg.id | |
361 | ); | |
ba9703b0 XL |
362 | } |
363 | Some(pkg_license) => { | |
364 | if pkg_license.as_str() != *license { | |
5e7ed085 FG |
365 | println!("dependency exception `{name}` license has changed"); |
366 | println!(" previously `{license}` now `{pkg_license}`"); | |
ba9703b0 XL |
367 | println!(" update EXCEPTIONS for the new license"); |
368 | *bad = true; | |
369 | } | |
370 | } | |
371 | } | |
372 | } | |
0531ce1d | 373 | } |
0531ce1d | 374 | |
17df50a5 | 375 | let exception_names: Vec<_> = exceptions.iter().map(|(name, _license)| *name).collect(); |
8bb4bdeb | 376 | |
ba9703b0 XL |
377 | // Check if any package does not have a valid license. |
378 | for pkg in &metadata.packages { | |
379 | if pkg.source.is_none() { | |
380 | // No need to check local packages. | |
0531ce1d | 381 | continue; |
8bb4bdeb | 382 | } |
ba9703b0 XL |
383 | if !runtime_ids.contains(&pkg.id) && exception_names.contains(&pkg.name.as_str()) { |
384 | continue; | |
385 | } | |
386 | let license = match &pkg.license { | |
387 | Some(license) => license, | |
388 | None => { | |
fc512014 | 389 | tidy_error!(bad, "dependency `{}` does not define a license expression", pkg.id); |
ba9703b0 XL |
390 | continue; |
391 | } | |
392 | }; | |
393 | if !LICENSES.contains(&license.as_str()) { | |
394 | if pkg.name == "fortanix-sgx-abi" { | |
395 | // This is a specific exception because SGX is considered | |
396 | // "third party". See | |
397 | // https://github.com/rust-lang/rust/issues/62620 for more. In | |
398 | // general, these should never be added. | |
399 | continue; | |
400 | } | |
fc512014 | 401 | tidy_error!(bad, "invalid license `{}` in `{}`", license, pkg.id); |
ba9703b0 | 402 | } |
476ff2be | 403 | } |
476ff2be SL |
404 | } |
405 | ||
f035d41b XL |
406 | /// Checks the dependency of `RESTRICTED_DEPENDENCY_CRATES` at the given path. Changes `bad` to |
407 | /// `true` if a check failed. | |
0531ce1d | 408 | /// |
f035d41b | 409 | /// Specifically, this checks that the dependencies are on the `PERMITTED_DEPENDENCIES`. |
17df50a5 XL |
410 | fn check_dependencies( |
411 | metadata: &Metadata, | |
412 | permitted_dependencies: &[&'static str], | |
413 | restricted_dependency_crates: &[&'static str], | |
414 | bad: &mut bool, | |
415 | ) { | |
f035d41b | 416 | // Check that the PERMITTED_DEPENDENCIES does not have unused entries. |
17df50a5 | 417 | for name in permitted_dependencies { |
ba9703b0 | 418 | if !metadata.packages.iter().any(|p| p.name == *name) { |
fc512014 XL |
419 | tidy_error!( |
420 | bad, | |
f035d41b XL |
421 | "could not find allowed package `{}`\n\ |
422 | Remove from PERMITTED_DEPENDENCIES list if it is no longer used.", | |
ba9703b0 XL |
423 | name |
424 | ); | |
ba9703b0 XL |
425 | } |
426 | } | |
f035d41b | 427 | // Get the list in a convenient form. |
17df50a5 | 428 | let permitted_dependencies: HashSet<_> = permitted_dependencies.iter().cloned().collect(); |
0531ce1d | 429 | |
9fa01778 | 430 | // Check dependencies. |
0531ce1d XL |
431 | let mut visited = BTreeSet::new(); |
432 | let mut unapproved = BTreeSet::new(); | |
17df50a5 | 433 | for &krate in restricted_dependency_crates.iter() { |
ba9703b0 | 434 | let pkg = pkg_from_name(metadata, krate); |
f035d41b XL |
435 | let mut bad = |
436 | check_crate_dependencies(&permitted_dependencies, metadata, &mut visited, pkg); | |
0531ce1d XL |
437 | unapproved.append(&mut bad); |
438 | } | |
439 | ||
b7449926 | 440 | if !unapproved.is_empty() { |
fc512014 | 441 | tidy_error!(bad, "Dependencies not explicitly permitted:"); |
0531ce1d | 442 | for dep in unapproved { |
5e7ed085 | 443 | println!("* {dep}"); |
0531ce1d | 444 | } |
0531ce1d | 445 | } |
0531ce1d XL |
446 | } |
447 | ||
448 | /// Checks the dependencies of the given crate from the given cargo metadata to see if they are on | |
f035d41b XL |
449 | /// the list of permitted dependencies. Returns a list of disallowed dependencies. |
450 | fn check_crate_dependencies<'a>( | |
451 | permitted_dependencies: &'a HashSet<&'static str>, | |
ba9703b0 XL |
452 | metadata: &'a Metadata, |
453 | visited: &mut BTreeSet<&'a PackageId>, | |
454 | krate: &'a Package, | |
455 | ) -> BTreeSet<&'a PackageId> { | |
9fa01778 | 456 | // This will contain bad deps. |
0531ce1d XL |
457 | let mut unapproved = BTreeSet::new(); |
458 | ||
9fa01778 | 459 | // Check if we have already visited this crate. |
ba9703b0 | 460 | if visited.contains(&krate.id) { |
0531ce1d XL |
461 | return unapproved; |
462 | } | |
463 | ||
ba9703b0 | 464 | visited.insert(&krate.id); |
0531ce1d | 465 | |
f035d41b | 466 | // If this path is in-tree, we don't require it to be explicitly permitted. |
ba9703b0 | 467 | if krate.source.is_some() { |
f035d41b XL |
468 | // If this dependency is not on `PERMITTED_DEPENDENCIES`, add to bad set. |
469 | if !permitted_dependencies.contains(krate.name.as_str()) { | |
ba9703b0 | 470 | unapproved.insert(&krate.id); |
0531ce1d XL |
471 | } |
472 | } | |
473 | ||
ba9703b0 XL |
474 | // Do a DFS in the crate graph. |
475 | let to_check = deps_of(metadata, &krate.id); | |
0531ce1d | 476 | |
ba9703b0 | 477 | for dep in to_check { |
f035d41b | 478 | let mut bad = check_crate_dependencies(permitted_dependencies, metadata, visited, dep); |
0531ce1d XL |
479 | unapproved.append(&mut bad); |
480 | } | |
481 | ||
482 | unapproved | |
483 | } | |
b7449926 | 484 | |
ba9703b0 | 485 | /// Prevents multiple versions of some expensive crates. |
17df50a5 XL |
486 | fn check_crate_duplicate( |
487 | metadata: &Metadata, | |
488 | forbidden_to_have_duplicates: &[&str], | |
489 | bad: &mut bool, | |
490 | ) { | |
491 | for &name in forbidden_to_have_duplicates { | |
ba9703b0 XL |
492 | let matches: Vec<_> = metadata.packages.iter().filter(|pkg| pkg.name == name).collect(); |
493 | match matches.len() { | |
494 | 0 => { | |
fc512014 XL |
495 | tidy_error!( |
496 | bad, | |
ba9703b0 XL |
497 | "crate `{}` is missing, update `check_crate_duplicate` \ |
498 | if it is no longer used", | |
499 | name | |
500 | ); | |
ba9703b0 XL |
501 | } |
502 | 1 => {} | |
503 | _ => { | |
fc512014 XL |
504 | tidy_error!( |
505 | bad, | |
ba9703b0 XL |
506 | "crate `{}` is duplicated in `Cargo.lock`, \ |
507 | it is too expensive to build multiple times, \ | |
508 | so make sure only one version appears across all dependencies", | |
509 | name | |
510 | ); | |
511 | for pkg in matches { | |
512 | println!(" * {}", pkg.id); | |
513 | } | |
ba9703b0 | 514 | } |
b7449926 | 515 | } |
ba9703b0 XL |
516 | } |
517 | } | |
518 | ||
519 | /// Returns a list of dependencies for the given package. | |
520 | fn deps_of<'a>(metadata: &'a Metadata, pkg_id: &'a PackageId) -> Vec<&'a Package> { | |
521 | let resolve = metadata.resolve.as_ref().unwrap(); | |
522 | let node = resolve | |
523 | .nodes | |
524 | .iter() | |
525 | .find(|n| &n.id == pkg_id) | |
5e7ed085 | 526 | .unwrap_or_else(|| panic!("could not find `{pkg_id}` in resolve")); |
ba9703b0 XL |
527 | node.deps |
528 | .iter() | |
529 | .map(|dep| { | |
530 | metadata.packages.iter().find(|pkg| pkg.id == dep.pkg).unwrap_or_else(|| { | |
531 | panic!("could not find dep `{}` for pkg `{}` in resolve", dep.pkg, pkg_id) | |
532 | }) | |
533 | }) | |
534 | .collect() | |
535 | } | |
536 | ||
537 | /// Finds a package with the given name. | |
538 | fn pkg_from_name<'a>(metadata: &'a Metadata, name: &'static str) -> &'a Package { | |
539 | let mut i = metadata.packages.iter().filter(|p| p.name == name); | |
540 | let result = | |
5e7ed085 FG |
541 | i.next().unwrap_or_else(|| panic!("could not find package `{name}` in package list")); |
542 | assert!(i.next().is_none(), "more than one package found for `{name}`"); | |
ba9703b0 XL |
543 | result |
544 | } | |
545 | ||
546 | /// Finds all the packages that are in the rust runtime. | |
547 | fn compute_runtime_crates<'a>(metadata: &'a Metadata) -> HashSet<&'a PackageId> { | |
548 | let resolve = metadata.resolve.as_ref().unwrap(); | |
549 | let mut result = HashSet::new(); | |
550 | for name in RUNTIME_CRATES { | |
551 | let id = &pkg_from_name(metadata, name).id; | |
552 | normal_deps_of_r(resolve, id, &mut result); | |
553 | } | |
554 | result | |
555 | } | |
556 | ||
557 | /// Recursively find all normal dependencies. | |
558 | fn normal_deps_of_r<'a>( | |
559 | resolve: &'a Resolve, | |
560 | pkg_id: &'a PackageId, | |
561 | result: &mut HashSet<&'a PackageId>, | |
562 | ) { | |
563 | if !result.insert(pkg_id) { | |
564 | return; | |
565 | } | |
566 | let node = resolve | |
567 | .nodes | |
568 | .iter() | |
569 | .find(|n| &n.id == pkg_id) | |
5e7ed085 | 570 | .unwrap_or_else(|| panic!("could not find `{pkg_id}` in resolve")); |
17df50a5 | 571 | for dep in &node.deps { |
ba9703b0 | 572 | normal_deps_of_r(resolve, &dep.pkg, result); |
b7449926 XL |
573 | } |
574 | } | |
136023e0 XL |
575 | |
576 | fn check_rustfix(metadata: &Metadata, bad: &mut bool) { | |
577 | let cargo = pkg_from_name(metadata, "cargo"); | |
578 | let compiletest = pkg_from_name(metadata, "compiletest"); | |
579 | let cargo_deps = deps_of(metadata, &cargo.id); | |
580 | let compiletest_deps = deps_of(metadata, &compiletest.id); | |
581 | let cargo_rustfix = cargo_deps.iter().find(|p| p.name == "rustfix").unwrap(); | |
582 | let compiletest_rustfix = compiletest_deps.iter().find(|p| p.name == "rustfix").unwrap(); | |
583 | if cargo_rustfix.version != compiletest_rustfix.version { | |
584 | tidy_error!( | |
585 | bad, | |
586 | "cargo's rustfix version {} does not match compiletest's rustfix version {}\n\ | |
587 | rustfix should be kept in sync, update the cargo side first, and then update \ | |
588 | compiletest along with cargo.", | |
589 | cargo_rustfix.version, | |
590 | compiletest_rustfix.version | |
591 | ); | |
592 | } | |
593 | } |