]> git.proxmox.com Git - rustc.git/blob - src/tools/tidy/src/deps.rs
f9bf04626f7852017c4b7dccad011c7181ca106d
[rustc.git] / src / tools / tidy / src / deps.rs
1 //! Checks the licenses of third-party dependencies.
2
3 use build_helper::ci::CiEnv;
4 use cargo_metadata::{Metadata, Package, PackageId};
5 use std::collections::HashSet;
6 use std::fs::read_dir;
7 use std::path::Path;
8
9 /// These are licenses that are allowed for all crates, including the runtime,
10 /// rustc, tools, etc.
11 #[rustfmt::skip]
12 const LICENSES: &[&str] = &[
13 // tidy-alphabetical-start
14 "(MIT OR Apache-2.0) AND Unicode-DFS-2016", // unicode_ident
15 "0BSD OR MIT OR Apache-2.0", // adler license
16 "0BSD",
17 "Apache-2.0 / MIT",
18 "Apache-2.0 OR ISC OR MIT",
19 "Apache-2.0 OR MIT",
20 "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", // wasi license
21 "Apache-2.0",
22 "Apache-2.0/MIT",
23 "BSD-2-Clause OR Apache-2.0 OR MIT", // zerocopy
24 "ISC",
25 "MIT / Apache-2.0",
26 "MIT OR Apache-2.0 OR LGPL-2.1-or-later", // r-efi, r-efi-alloc
27 "MIT OR Apache-2.0 OR Zlib", // tinyvec_macros
28 "MIT OR Apache-2.0",
29 "MIT OR Zlib OR Apache-2.0", // miniz_oxide
30 "MIT",
31 "MIT/Apache-2.0",
32 "Unicode-3.0", // icu4x
33 "Unicode-DFS-2016", // tinystr
34 "Unlicense OR MIT",
35 "Unlicense/MIT",
36 "Zlib OR Apache-2.0 OR MIT", // tinyvec
37 // tidy-alphabetical-end
38 ];
39
40 type ExceptionList = &'static [(&'static str, &'static str)];
41
42 /// The workspaces to check for licensing and optionally permitted dependencies.
43 ///
44 /// Each entry consists of a tuple with the following elements:
45 ///
46 /// * The path to the workspace root Cargo.toml file.
47 /// * The list of license exceptions.
48 /// * Optionally a tuple of:
49 /// * A list of crates for which dependencies need to be explicitly allowed.
50 /// * The list of allowed dependencies.
51 // FIXME auto detect all cargo workspaces
52 pub(crate) const WORKSPACES: &[(&str, ExceptionList, Option<(&[&str], &[&str])>)] = &[
53 // The root workspace has to be first for check_rustfix to work.
54 (".", EXCEPTIONS, Some((&["rustc-main"], PERMITTED_RUSTC_DEPENDENCIES))),
55 // Outside of the alphabetical section because rustfmt formats it using multiple lines.
56 (
57 "compiler/rustc_codegen_cranelift",
58 EXCEPTIONS_CRANELIFT,
59 Some((&["rustc_codegen_cranelift"], PERMITTED_CRANELIFT_DEPENDENCIES)),
60 ),
61 // tidy-alphabetical-start
62 ("compiler/rustc_codegen_gcc", EXCEPTIONS_GCC, None),
63 //("library/backtrace", &[], None), // FIXME uncomment once rust-lang/backtrace#562 has been synced back to the rust repo
64 //("library/portable-simd", &[], None), // FIXME uncomment once rust-lang/portable-simd#363 has been synced back to the rust repo
65 //("library/stdarch", EXCEPTIONS_STDARCH, None), // FIXME uncomment once rust-lang/stdarch#1462 has been synced back to the rust repo
66 ("src/bootstrap", EXCEPTIONS_BOOTSTRAP, None),
67 ("src/ci/docker/host-x86_64/test-various/uefi_qemu_test", EXCEPTIONS_UEFI_QEMU_TEST, None),
68 //("src/etc/test-float-parse", &[], None), // FIXME uncomment once all deps are vendored
69 ("src/tools/cargo", EXCEPTIONS_CARGO, None),
70 //("src/tools/miri/test-cargo-miri", &[], None), // FIXME uncomment once all deps are vendored
71 //("src/tools/miri/test_dependencies", &[], None), // FIXME uncomment once all deps are vendored
72 ("src/tools/rust-analyzer", EXCEPTIONS_RUST_ANALYZER, None),
73 ("src/tools/rustc-perf", EXCEPTIONS_RUSTC_PERF, None),
74 ("src/tools/x", &[], None),
75 // tidy-alphabetical-end
76 ];
77
78 /// These are exceptions to Rust's permissive licensing policy, and
79 /// should be considered bugs. Exceptions are only allowed in Rust
80 /// tooling. It is _crucial_ that no exception crates be dependencies
81 /// of the Rust runtime (std/test).
82 #[rustfmt::skip]
83 const EXCEPTIONS: ExceptionList = &[
84 // tidy-alphabetical-start
85 ("ar_archive_writer", "Apache-2.0 WITH LLVM-exception"), // rustc
86 ("colored", "MPL-2.0"), // rustfmt
87 ("dissimilar", "Apache-2.0"), // rustdoc, rustc_lexer (few tests) via expect-test, (dev deps)
88 ("fluent-langneg", "Apache-2.0"), // rustc (fluent translations)
89 ("fortanix-sgx-abi", "MPL-2.0"), // libstd but only for `sgx` target. FIXME: this dependency violates the documentation comment above.
90 ("instant", "BSD-3-Clause"), // rustc_driver/tracing-subscriber/parking_lot
91 ("mdbook", "MPL-2.0"), // mdbook
92 ("option-ext", "MPL-2.0"), // cargo-miri (via `directories`)
93 ("rustc_apfloat", "Apache-2.0 WITH LLVM-exception"), // rustc (license is the same as LLVM uses)
94 ("ryu", "Apache-2.0 OR BSL-1.0"), // BSL is not acceptble, but we use it under Apache-2.0 // cargo/... (because of serde)
95 ("self_cell", "Apache-2.0"), // rustc (fluent translations)
96 ("snap", "BSD-3-Clause"), // rustc
97 ("wasm-encoder", "Apache-2.0 WITH LLVM-exception"), // rustc
98 ("wasm-metadata", "Apache-2.0 WITH LLVM-exception"), // rustc
99 ("wasmparser", "Apache-2.0 WITH LLVM-exception"), // rustc
100 ("wast", "Apache-2.0 WITH LLVM-exception"), // rustc
101 ("wat", "Apache-2.0 WITH LLVM-exception"), // rustc
102 ("wit-component", "Apache-2.0 WITH LLVM-exception"), // rustc
103 ("wit-parser", "Apache-2.0 WITH LLVM-exception"), // rustc
104 // tidy-alphabetical-end
105 ];
106
107 // FIXME uncomment once rust-lang/stdarch#1462 lands
108 /*
109 const EXCEPTIONS_STDARCH: ExceptionList = &[
110 // tidy-alphabetical-start
111 ("ryu", "Apache-2.0 OR BSL-1.0"), // BSL is not acceptble, but we use it under Apache-2.0
112 ("wasmparser", "Apache-2.0 WITH LLVM-exception"),
113 ("wasmprinter", "Apache-2.0 WITH LLVM-exception"),
114 // tidy-alphabetical-end
115 ];
116 */
117
118 const EXCEPTIONS_CARGO: ExceptionList = &[
119 // tidy-alphabetical-start
120 ("bitmaps", "MPL-2.0+"),
121 ("bytesize", "Apache-2.0"),
122 ("ciborium", "Apache-2.0"),
123 ("ciborium-io", "Apache-2.0"),
124 ("ciborium-ll", "Apache-2.0"),
125 ("dunce", "CC0-1.0 OR MIT-0 OR Apache-2.0"),
126 ("encoding_rs", "(Apache-2.0 OR MIT) AND BSD-3-Clause"),
127 ("fiat-crypto", "MIT OR Apache-2.0 OR BSD-1-Clause"),
128 ("im-rc", "MPL-2.0+"),
129 ("normalize-line-endings", "Apache-2.0"),
130 ("openssl", "Apache-2.0"),
131 ("ryu", "Apache-2.0 OR BSL-1.0"), // BSL is not acceptble, but we use it under Apache-2.0
132 ("sha1_smol", "BSD-3-Clause"),
133 ("similar", "Apache-2.0"),
134 ("sized-chunks", "MPL-2.0+"),
135 ("subtle", "BSD-3-Clause"),
136 ("supports-hyperlinks", "Apache-2.0"),
137 ("unicode-bom", "Apache-2.0"),
138 // tidy-alphabetical-end
139 ];
140
141 const EXCEPTIONS_RUST_ANALYZER: ExceptionList = &[
142 // tidy-alphabetical-start
143 ("dissimilar", "Apache-2.0"),
144 ("notify", "CC0-1.0"),
145 ("option-ext", "MPL-2.0"),
146 ("pulldown-cmark-to-cmark", "Apache-2.0"),
147 ("rustc_apfloat", "Apache-2.0 WITH LLVM-exception"),
148 ("ryu", "Apache-2.0 OR BSL-1.0"), // BSL is not acceptble, but we use it under Apache-2.0
149 ("scip", "Apache-2.0"),
150 ("snap", "BSD-3-Clause"),
151 // tidy-alphabetical-end
152 ];
153
154 const EXCEPTIONS_RUSTC_PERF: ExceptionList = &[
155 // tidy-alphabetical-start
156 ("alloc-no-stdlib", "BSD-3-Clause"),
157 ("alloc-stdlib", "BSD-3-Clause"),
158 ("brotli", "BSD-3-Clause/MIT"),
159 ("brotli-decompressor", "BSD-3-Clause/MIT"),
160 ("encoding_rs", "(Apache-2.0 OR MIT) AND BSD-3-Clause"),
161 ("inferno", "CDDL-1.0"),
162 ("instant", "BSD-3-Clause"),
163 ("ring", NON_STANDARD_LICENSE), // see EXCEPTIONS_NON_STANDARD_LICENSE_DEPS for more.
164 ("ryu", "Apache-2.0 OR BSL-1.0"),
165 ("snap", "BSD-3-Clause"),
166 ("subtle", "BSD-3-Clause"),
167 // tidy-alphabetical-end
168 ];
169
170 const EXCEPTIONS_CRANELIFT: ExceptionList = &[
171 // tidy-alphabetical-start
172 ("cranelift-bforest", "Apache-2.0 WITH LLVM-exception"),
173 ("cranelift-codegen", "Apache-2.0 WITH LLVM-exception"),
174 ("cranelift-codegen-meta", "Apache-2.0 WITH LLVM-exception"),
175 ("cranelift-codegen-shared", "Apache-2.0 WITH LLVM-exception"),
176 ("cranelift-control", "Apache-2.0 WITH LLVM-exception"),
177 ("cranelift-entity", "Apache-2.0 WITH LLVM-exception"),
178 ("cranelift-frontend", "Apache-2.0 WITH LLVM-exception"),
179 ("cranelift-isle", "Apache-2.0 WITH LLVM-exception"),
180 ("cranelift-jit", "Apache-2.0 WITH LLVM-exception"),
181 ("cranelift-module", "Apache-2.0 WITH LLVM-exception"),
182 ("cranelift-native", "Apache-2.0 WITH LLVM-exception"),
183 ("cranelift-object", "Apache-2.0 WITH LLVM-exception"),
184 ("mach", "BSD-2-Clause"),
185 ("regalloc2", "Apache-2.0 WITH LLVM-exception"),
186 ("target-lexicon", "Apache-2.0 WITH LLVM-exception"),
187 ("wasmtime-jit-icache-coherence", "Apache-2.0 WITH LLVM-exception"),
188 // tidy-alphabetical-end
189 ];
190
191 const EXCEPTIONS_GCC: ExceptionList = &[
192 // tidy-alphabetical-start
193 ("gccjit", "GPL-3.0"),
194 ("gccjit_sys", "GPL-3.0"),
195 // tidy-alphabetical-end
196 ];
197
198 const EXCEPTIONS_BOOTSTRAP: ExceptionList = &[
199 ("ryu", "Apache-2.0 OR BSL-1.0"), // through serde. BSL is not acceptble, but we use it under Apache-2.0
200 ];
201
202 const EXCEPTIONS_UEFI_QEMU_TEST: ExceptionList = &[
203 ("r-efi", "MIT OR Apache-2.0 OR LGPL-2.1-or-later"), // LGPL is not acceptible, but we use it under MIT OR Apache-2.0
204 ];
205
206 /// Placeholder for non-standard license file.
207 const NON_STANDARD_LICENSE: &str = "NON_STANDARD_LICENSE";
208
209 /// These dependencies have non-standard licenses but are genenrally permitted.
210 const EXCEPTIONS_NON_STANDARD_LICENSE_DEPS: &[&str] = &[
211 // `ring` is included because it is an optional dependency of `hyper`,
212 // which is a training data in rustc-perf for optimized build.
213 // The license of it is generally `ISC AND MIT AND OpenSSL`,
214 // though the `package.license` field is not set.
215 //
216 // See https://github.com/briansmith/ring/issues/902
217 "ring",
218 ];
219
220 /// These are the root crates that are part of the runtime. The licenses for
221 /// these and all their dependencies *must not* be in the exception list.
222 const RUNTIME_CRATES: &[&str] = &["std", "core", "alloc", "test", "panic_abort", "panic_unwind"];
223
224 const PERMITTED_DEPS_LOCATION: &str = concat!(file!(), ":", line!());
225
226 /// Crates rustc is allowed to depend on. Avoid adding to the list if possible.
227 ///
228 /// This list is here to provide a speed-bump to adding a new dependency to
229 /// rustc. Please check with the compiler team before adding an entry.
230 const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
231 // tidy-alphabetical-start
232 "addr2line",
233 "adler",
234 "ahash",
235 "aho-corasick",
236 "allocator-api2", // FIXME: only appears in Cargo.lock due to https://github.com/rust-lang/cargo/issues/10801
237 "annotate-snippets",
238 "anstyle",
239 "ar_archive_writer",
240 "arrayvec",
241 "autocfg",
242 "bitflags",
243 "block-buffer",
244 "byteorder", // via ruzstd in object in thorin-dwp
245 "cc",
246 "cfg-if",
247 "cfg_aliases",
248 "compiler_builtins",
249 "cpufeatures",
250 "crc32fast",
251 "crossbeam-channel",
252 "crossbeam-deque",
253 "crossbeam-epoch",
254 "crossbeam-utils",
255 "crypto-common",
256 "ctrlc",
257 "darling",
258 "darling_core",
259 "darling_macro",
260 "datafrog",
261 "deranged",
262 "derivative",
263 "derive_more",
264 "derive_setters",
265 "digest",
266 "displaydoc",
267 "dissimilar",
268 "dlmalloc",
269 "either",
270 "elsa",
271 "ena",
272 "equivalent",
273 "errno",
274 "expect-test",
275 "fallible-iterator", // dependency of `thorin`
276 "fastrand",
277 "field-offset",
278 "flate2",
279 "fluent-bundle",
280 "fluent-langneg",
281 "fluent-syntax",
282 "fnv",
283 "fortanix-sgx-abi",
284 "generic-array",
285 "getopts",
286 "getrandom",
287 "gimli",
288 "gsgdt",
289 "hashbrown",
290 "hermit-abi",
291 "icu_list",
292 "icu_list_data",
293 "icu_locid",
294 "icu_locid_transform",
295 "icu_locid_transform_data",
296 "icu_provider",
297 "icu_provider_adapters",
298 "icu_provider_macros",
299 "ident_case",
300 "indexmap",
301 "intl-memoizer",
302 "intl_pluralrules",
303 "itertools",
304 "itoa",
305 "jemalloc-sys",
306 "jobserver",
307 "lazy_static",
308 "leb128",
309 "libc",
310 "libloading",
311 "linux-raw-sys",
312 "litemap",
313 "lock_api",
314 "log",
315 "matchers",
316 "md-5",
317 "measureme",
318 "memchr",
319 "memmap2",
320 "memoffset",
321 "miniz_oxide",
322 "nix",
323 "nu-ansi-term",
324 "num-conv",
325 "num_cpus",
326 "object",
327 "odht",
328 "once_cell",
329 "overload",
330 "parking_lot",
331 "parking_lot_core",
332 "pathdiff",
333 "perf-event-open-sys",
334 "pin-project-lite",
335 "polonius-engine",
336 "portable-atomic", // dependency for platforms doesn't support `AtomicU64` in std
337 "powerfmt",
338 "ppv-lite86",
339 "proc-macro-hack",
340 "proc-macro2",
341 "psm",
342 "pulldown-cmark",
343 "pulldown-cmark-escape",
344 "punycode",
345 "quote",
346 "r-efi",
347 "r-efi-alloc",
348 "rand",
349 "rand_chacha",
350 "rand_core",
351 "rand_xorshift",
352 "rand_xoshiro",
353 "redox_syscall",
354 "regex",
355 "regex-automata",
356 "regex-syntax",
357 "rustc-demangle",
358 "rustc-hash",
359 "rustc-rayon",
360 "rustc-rayon-core",
361 "rustc-stable-hash",
362 "rustc_apfloat",
363 "rustc_version",
364 "rustix",
365 "ruzstd", // via object in thorin-dwp
366 "ryu",
367 "scoped-tls",
368 "scopeguard",
369 "self_cell",
370 "semver",
371 "serde",
372 "serde_derive",
373 "serde_json",
374 "sha1",
375 "sha2",
376 "sharded-slab",
377 "shlex",
378 "smallvec",
379 "snap",
380 "stable_deref_trait",
381 "stacker",
382 "static_assertions",
383 "strsim",
384 "syn",
385 "synstructure",
386 "tempfile",
387 "termcolor",
388 "termize",
389 "thin-vec",
390 "thiserror",
391 "thiserror-impl",
392 "thorin-dwp",
393 "thread_local",
394 "time",
395 "time-core",
396 "time-macros",
397 "tinystr",
398 "tinyvec",
399 "tinyvec_macros",
400 "tracing",
401 "tracing-attributes",
402 "tracing-core",
403 "tracing-log",
404 "tracing-subscriber",
405 "tracing-tree",
406 "twox-hash",
407 "type-map",
408 "typenum",
409 "unic-langid",
410 "unic-langid-impl",
411 "unic-langid-macros",
412 "unic-langid-macros-impl",
413 "unicase",
414 "unicode-ident",
415 "unicode-normalization",
416 "unicode-properties",
417 "unicode-script",
418 "unicode-security",
419 "unicode-width",
420 "unicode-xid",
421 "unwinding",
422 "valuable",
423 "version_check",
424 "wasi",
425 "wasm-encoder",
426 "wasmparser",
427 "winapi",
428 "winapi-i686-pc-windows-gnu",
429 "winapi-util",
430 "winapi-x86_64-pc-windows-gnu",
431 "windows",
432 "windows-core",
433 "windows-sys",
434 "windows-targets",
435 "windows_aarch64_gnullvm",
436 "windows_aarch64_msvc",
437 "windows_i686_gnu",
438 "windows_i686_gnullvm",
439 "windows_i686_msvc",
440 "windows_x86_64_gnu",
441 "windows_x86_64_gnullvm",
442 "windows_x86_64_msvc",
443 "writeable",
444 "yoke",
445 "yoke-derive",
446 "zerocopy",
447 "zerocopy-derive",
448 "zerofrom",
449 "zerofrom-derive",
450 "zerovec",
451 "zerovec-derive",
452 // tidy-alphabetical-end
453 ];
454
455 const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[
456 // tidy-alphabetical-start
457 "ahash",
458 "anyhow",
459 "arbitrary",
460 "bitflags",
461 "bumpalo",
462 "cfg-if",
463 "cranelift-bforest",
464 "cranelift-codegen",
465 "cranelift-codegen-meta",
466 "cranelift-codegen-shared",
467 "cranelift-control",
468 "cranelift-entity",
469 "cranelift-frontend",
470 "cranelift-isle",
471 "cranelift-jit",
472 "cranelift-module",
473 "cranelift-native",
474 "cranelift-object",
475 "crc32fast",
476 "equivalent",
477 "fallible-iterator",
478 "gimli",
479 "hashbrown",
480 "indexmap",
481 "libc",
482 "libloading",
483 "log",
484 "mach",
485 "memchr",
486 "object",
487 "once_cell",
488 "proc-macro2",
489 "quote",
490 "regalloc2",
491 "region",
492 "rustc-hash",
493 "slice-group-by",
494 "smallvec",
495 "stable_deref_trait",
496 "syn",
497 "target-lexicon",
498 "unicode-ident",
499 "version_check",
500 "wasmtime-jit-icache-coherence",
501 "winapi",
502 "winapi-i686-pc-windows-gnu",
503 "winapi-x86_64-pc-windows-gnu",
504 "windows-sys",
505 "windows-targets",
506 "windows_aarch64_gnullvm",
507 "windows_aarch64_msvc",
508 "windows_i686_gnu",
509 "windows_i686_gnullvm",
510 "windows_i686_msvc",
511 "windows_x86_64_gnu",
512 "windows_x86_64_gnullvm",
513 "windows_x86_64_msvc",
514 "zerocopy",
515 "zerocopy-derive",
516 // tidy-alphabetical-end
517 ];
518
519 /// Dependency checks.
520 ///
521 /// `root` is path to the directory with the root `Cargo.toml` (for the workspace). `cargo` is path
522 /// to the cargo executable.
523 pub fn check(root: &Path, cargo: &Path, bad: &mut bool) {
524 let mut checked_runtime_licenses = false;
525
526 let submodules = build_helper::util::parse_gitmodules(root);
527 for &(workspace, exceptions, permitted_deps) in WORKSPACES {
528 // Skip if it's a submodule, not in a CI environment, and not initialized.
529 //
530 // This prevents enforcing developers to fetch submodules for tidy.
531 if submodules.contains(&workspace.into())
532 && !CiEnv::is_ci()
533 // If the directory is empty, we can consider it as an uninitialized submodule.
534 && read_dir(root.join(workspace)).unwrap().next().is_none()
535 {
536 continue;
537 }
538
539 if !root.join(workspace).join("Cargo.lock").exists() {
540 tidy_error!(bad, "the `{workspace}` workspace doesn't have a Cargo.lock");
541 continue;
542 }
543
544 let mut cmd = cargo_metadata::MetadataCommand::new();
545 cmd.cargo_path(cargo)
546 .manifest_path(root.join(workspace).join("Cargo.toml"))
547 .features(cargo_metadata::CargoOpt::AllFeatures)
548 .other_options(vec!["--locked".to_owned()]);
549 let metadata = t!(cmd.exec());
550
551 check_license_exceptions(&metadata, exceptions, bad);
552 if let Some((crates, permitted_deps)) = permitted_deps {
553 check_permitted_dependencies(&metadata, workspace, permitted_deps, crates, bad);
554 }
555
556 if workspace == "." {
557 let runtime_ids = compute_runtime_crates(&metadata);
558 check_runtime_license_exceptions(&metadata, runtime_ids, bad);
559 checked_runtime_licenses = true;
560 }
561 }
562
563 // Sanity check to ensure we don't accidentally remove the workspace containing the runtime
564 // crates.
565 assert!(checked_runtime_licenses);
566 }
567
568 /// Check that all licenses of runtime dependencies are in the valid list in `LICENSES`.
569 ///
570 /// Unlike for tools we don't allow exceptions to the `LICENSES` list for the runtime with the sole
571 /// exception of `fortanix-sgx-abi` which is only used on x86_64-fortanix-unknown-sgx.
572 fn check_runtime_license_exceptions(
573 metadata: &Metadata,
574 runtime_ids: HashSet<&PackageId>,
575 bad: &mut bool,
576 ) {
577 for pkg in &metadata.packages {
578 if !runtime_ids.contains(&pkg.id) {
579 // Only checking dependencies of runtime libraries here.
580 continue;
581 }
582 if pkg.source.is_none() {
583 // No need to check local packages.
584 continue;
585 }
586 let license = match &pkg.license {
587 Some(license) => license,
588 None => {
589 tidy_error!(bad, "dependency `{}` does not define a license expression", pkg.id);
590 continue;
591 }
592 };
593 if !LICENSES.contains(&license.as_str()) {
594 // This is a specific exception because SGX is considered "third party".
595 // See https://github.com/rust-lang/rust/issues/62620 for more.
596 // In general, these should never be added and this exception
597 // should not be taken as precedent for any new target.
598 if pkg.name == "fortanix-sgx-abi" && pkg.license.as_deref() == Some("MPL-2.0") {
599 continue;
600 }
601
602 // This exception is due to the fact that the feature set of the
603 // `object` crate is different between rustc and libstd. In the
604 // standard library only a conservative set of features are enabled
605 // which notably does not include the `wasm` feature which pulls in
606 // this dependency. In the compiler, however, the `wasm` feature is
607 // enabled. This exception is intended to be here so long as the
608 // `EXCEPTIONS` above contains `wasmparser`, but once that goes away
609 // this can be removed.
610 if pkg.name == "wasmparser"
611 && pkg.license.as_deref() == Some("Apache-2.0 WITH LLVM-exception")
612 {
613 continue;
614 }
615
616 tidy_error!(bad, "invalid license `{}` in `{}`", license, pkg.id);
617 }
618 }
619 }
620
621 /// Check that all licenses of tool dependencies are in the valid list in `LICENSES`.
622 ///
623 /// Packages listed in `exceptions` are allowed for tools.
624 fn check_license_exceptions(metadata: &Metadata, exceptions: &[(&str, &str)], bad: &mut bool) {
625 // Validate the EXCEPTIONS list hasn't changed.
626 for (name, license) in exceptions {
627 // Check that the package actually exists.
628 if !metadata.packages.iter().any(|p| p.name == *name) {
629 tidy_error!(
630 bad,
631 "could not find exception package `{}`\n\
632 Remove from EXCEPTIONS list if it is no longer used.",
633 name
634 );
635 }
636 // Check that the license hasn't changed.
637 for pkg in metadata.packages.iter().filter(|p| p.name == *name) {
638 match &pkg.license {
639 None => {
640 if *license == NON_STANDARD_LICENSE
641 && EXCEPTIONS_NON_STANDARD_LICENSE_DEPS.contains(&pkg.name.as_str())
642 {
643 continue;
644 }
645 tidy_error!(
646 bad,
647 "dependency exception `{}` does not declare a license expression",
648 pkg.id
649 );
650 }
651 Some(pkg_license) => {
652 if pkg_license.as_str() != *license {
653 println!("dependency exception `{name}` license has changed");
654 println!(" previously `{license}` now `{pkg_license}`");
655 println!(" update EXCEPTIONS for the new license");
656 *bad = true;
657 }
658 }
659 }
660 }
661 }
662
663 let exception_names: Vec<_> = exceptions.iter().map(|(name, _license)| *name).collect();
664
665 // Check if any package does not have a valid license.
666 for pkg in &metadata.packages {
667 if pkg.source.is_none() {
668 // No need to check local packages.
669 continue;
670 }
671 if exception_names.contains(&pkg.name.as_str()) {
672 continue;
673 }
674 let license = match &pkg.license {
675 Some(license) => license,
676 None => {
677 tidy_error!(bad, "dependency `{}` does not define a license expression", pkg.id);
678 continue;
679 }
680 };
681 if !LICENSES.contains(&license.as_str()) {
682 tidy_error!(bad, "invalid license `{}` in `{}`", license, pkg.id);
683 }
684 }
685 }
686
687 /// Checks the dependency of `restricted_dependency_crates` at the given path. Changes `bad` to
688 /// `true` if a check failed.
689 ///
690 /// Specifically, this checks that the dependencies are on the `permitted_dependencies`.
691 fn check_permitted_dependencies(
692 metadata: &Metadata,
693 descr: &str,
694 permitted_dependencies: &[&'static str],
695 restricted_dependency_crates: &[&'static str],
696 bad: &mut bool,
697 ) {
698 let mut has_permitted_dep_error = false;
699 let mut deps = HashSet::new();
700 for to_check in restricted_dependency_crates {
701 let to_check = pkg_from_name(metadata, to_check);
702 deps_of(metadata, &to_check.id, &mut deps);
703 }
704
705 // Check that the PERMITTED_DEPENDENCIES does not have unused entries.
706 for permitted in permitted_dependencies {
707 if !deps.iter().any(|dep_id| &pkg_from_id(metadata, dep_id).name == permitted) {
708 tidy_error!(
709 bad,
710 "could not find allowed package `{permitted}`\n\
711 Remove from PERMITTED_DEPENDENCIES list if it is no longer used.",
712 );
713 has_permitted_dep_error = true;
714 }
715 }
716
717 // Get in a convenient form.
718 let permitted_dependencies: HashSet<_> = permitted_dependencies.iter().cloned().collect();
719
720 for dep in deps {
721 let dep = pkg_from_id(metadata, dep);
722 // If this path is in-tree, we don't require it to be explicitly permitted.
723 if dep.source.is_some() && !permitted_dependencies.contains(dep.name.as_str()) {
724 tidy_error!(bad, "Dependency for {descr} not explicitly permitted: {}", dep.id);
725 has_permitted_dep_error = true;
726 }
727 }
728
729 if has_permitted_dep_error {
730 eprintln!("Go to `{PERMITTED_DEPS_LOCATION}` for the list.");
731 }
732 }
733
734 /// Finds a package with the given name.
735 fn pkg_from_name<'a>(metadata: &'a Metadata, name: &'static str) -> &'a Package {
736 let mut i = metadata.packages.iter().filter(|p| p.name == name);
737 let result =
738 i.next().unwrap_or_else(|| panic!("could not find package `{name}` in package list"));
739 assert!(i.next().is_none(), "more than one package found for `{name}`");
740 result
741 }
742
743 fn pkg_from_id<'a>(metadata: &'a Metadata, id: &PackageId) -> &'a Package {
744 metadata.packages.iter().find(|p| &p.id == id).unwrap()
745 }
746
747 /// Finds all the packages that are in the rust runtime.
748 fn compute_runtime_crates<'a>(metadata: &'a Metadata) -> HashSet<&'a PackageId> {
749 let mut result = HashSet::new();
750 for name in RUNTIME_CRATES {
751 let id = &pkg_from_name(metadata, name).id;
752 deps_of(metadata, id, &mut result);
753 }
754 result
755 }
756
757 /// Recursively find all dependencies.
758 fn deps_of<'a>(metadata: &'a Metadata, pkg_id: &'a PackageId, result: &mut HashSet<&'a PackageId>) {
759 if !result.insert(pkg_id) {
760 return;
761 }
762 let node = metadata
763 .resolve
764 .as_ref()
765 .unwrap()
766 .nodes
767 .iter()
768 .find(|n| &n.id == pkg_id)
769 .unwrap_or_else(|| panic!("could not find `{pkg_id}` in resolve"));
770 for dep in &node.deps {
771 deps_of(metadata, &dep.pkg, result);
772 }
773 }