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