1use std::collections::{HashMap, HashSet};
4use std::fmt::{Display, Formatter};
5use std::fs::{File, read_dir};
6use std::io::Write;
7use std::path::Path;
8
9use cargo_metadata::semver::Version;
10use cargo_metadata::{Metadata, Package, PackageId};
11
12use crate::diagnostics::{RunningCheck, TidyCtx};
13
14#[path = "../../../bootstrap/src/utils/proc_macro_deps.rs"]
15mod proc_macro_deps;
16
17#[derive(Clone, Copy)]
18struct ListLocation {
19 path: &'static str,
20 line: u32,
21}
22
23impl Display for ListLocation {
24 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
25 write!(f, "{}:{}", self.path, self.line)
26 }
27}
28
29macro_rules! location {
31 (+ $offset:literal) => {
32 ListLocation { path: file!(), line: line!() + $offset }
33 };
34}
35
36#[rustfmt::skip]
39const LICENSES: &[&str] = &[
40 "0BSD OR MIT OR Apache-2.0", "Apache-2.0 / MIT",
43 "Apache-2.0 OR ISC OR MIT",
44 "Apache-2.0 OR MIT",
45 "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", "Apache-2.0/MIT",
47 "BSD-2-Clause OR Apache-2.0 OR MIT", "BSD-2-Clause OR MIT OR Apache-2.0",
49 "BSD-3-Clause/MIT",
50 "CC0-1.0 OR MIT-0 OR Apache-2.0",
51 "ISC",
52 "MIT / Apache-2.0",
53 "MIT AND (MIT OR Apache-2.0)",
54 "MIT AND Apache-2.0 WITH LLVM-exception AND (MIT OR Apache-2.0)", "MIT OR Apache-2.0 OR BSD-1-Clause",
56 "MIT OR Apache-2.0 OR LGPL-2.1-or-later", "MIT OR Apache-2.0 OR Zlib", "MIT OR Apache-2.0",
59 "MIT OR Zlib OR Apache-2.0", "MIT",
61 "MIT/Apache-2.0",
62 "Unlicense OR MIT",
63 "Unlicense/MIT",
64 "Zlib", ];
67
68#[rustfmt::skip]
70const LICENSES_TOOLS: &[&str] = &[
71 "(Apache-2.0 OR MIT) AND BSD-3-Clause",
73 "(MIT OR Apache-2.0) AND Unicode-3.0", "(MIT OR Apache-2.0) AND Unicode-DFS-2016", "0BSD",
76 "Apache-2.0 AND ISC",
77 "Apache-2.0 OR BSL-1.0", "Apache-2.0 OR GPL-2.0-only",
79 "Apache-2.0 WITH LLVM-exception",
80 "Apache-2.0",
81 "BSD-2-Clause",
82 "BSD-3-Clause",
83 "CC0-1.0 OR Apache-2.0 OR Apache-2.0 WITH LLVM-exception",
84 "CC0-1.0",
85 "Unicode-3.0", "Unicode-DFS-2016", "Zlib OR Apache-2.0 OR MIT", "Zlib",
89 ];
91
92type ExceptionList = &'static [(&'static str, &'static str)];
93
94#[derive(Clone, Copy)]
95pub(crate) struct WorkspaceInfo<'a> {
96 pub(crate) path: &'a str,
98 pub(crate) exceptions: ExceptionList,
100 crates_and_deps: Option<(&'a [&'a str], &'a [&'a str], ListLocation)>,
105 pub(crate) submodules: &'a [&'a str],
107}
108
109const WORKSPACE_LOCATION: ListLocation = location!(+4);
110
111pub(crate) const WORKSPACES: &[WorkspaceInfo<'static>] = &[
114 WorkspaceInfo {
116 path: ".",
117 exceptions: EXCEPTIONS,
118 crates_and_deps: Some((
119 &["rustc-main"],
120 PERMITTED_RUSTC_DEPENDENCIES,
121 PERMITTED_RUSTC_DEPS_LOCATION,
122 )),
123 submodules: &[],
124 },
125 WorkspaceInfo {
126 path: "library",
127 exceptions: EXCEPTIONS_STDLIB,
128 crates_and_deps: Some((
129 &["sysroot"],
130 PERMITTED_STDLIB_DEPENDENCIES,
131 PERMITTED_STDLIB_DEPS_LOCATION,
132 )),
133 submodules: &[],
134 },
135 WorkspaceInfo {
136 path: "library/stdarch",
137 exceptions: EXCEPTIONS_STDARCH,
138 crates_and_deps: None,
139 submodules: &[],
140 },
141 WorkspaceInfo {
142 path: "compiler/rustc_codegen_cranelift",
143 exceptions: EXCEPTIONS_CRANELIFT,
144 crates_and_deps: Some((
145 &["rustc_codegen_cranelift"],
146 PERMITTED_CRANELIFT_DEPENDENCIES,
147 PERMITTED_CRANELIFT_DEPS_LOCATION,
148 )),
149 submodules: &[],
150 },
151 WorkspaceInfo {
152 path: "compiler/rustc_codegen_gcc",
153 exceptions: EXCEPTIONS_GCC,
154 crates_and_deps: None,
155 submodules: &[],
156 },
157 WorkspaceInfo {
158 path: "src/bootstrap",
159 exceptions: EXCEPTIONS_BOOTSTRAP,
160 crates_and_deps: None,
161 submodules: &[],
162 },
163 WorkspaceInfo {
164 path: "src/tools/cargo",
165 exceptions: EXCEPTIONS_CARGO,
166 crates_and_deps: None,
167 submodules: &["src/tools/cargo"],
168 },
169 WorkspaceInfo {
181 path: "src/tools/rust-analyzer",
182 exceptions: EXCEPTIONS_RUST_ANALYZER,
183 crates_and_deps: None,
184 submodules: &[],
185 },
186 WorkspaceInfo {
187 path: "src/tools/rustbook",
188 exceptions: EXCEPTIONS_RUSTBOOK,
189 crates_and_deps: None,
190 submodules: &["src/doc/book", "src/doc/reference"],
191 },
192 WorkspaceInfo {
193 path: "src/tools/rustc-perf",
194 exceptions: EXCEPTIONS_RUSTC_PERF,
195 crates_and_deps: None,
196 submodules: &["src/tools/rustc-perf"],
197 },
198 WorkspaceInfo {
199 path: "tests/run-make-cargo/uefi-qemu/uefi_qemu_test",
200 exceptions: EXCEPTIONS_UEFI_QEMU_TEST,
201 crates_and_deps: None,
202 submodules: &[],
203 },
204];
205
206#[rustfmt::skip]
211const EXCEPTIONS: ExceptionList = &[
212 ("colored", "MPL-2.0"), ("option-ext", "MPL-2.0"), ];
217
218#[rustfmt::skip]
223const EXCEPTIONS_STDLIB: ExceptionList = &[
224 ("fortanix-sgx-abi", "MPL-2.0"), ];
228
229const EXCEPTIONS_CARGO: ExceptionList = &[
230 ("bitmaps", "MPL-2.0+"),
232 ("im-rc", "MPL-2.0+"),
233 ("sized-chunks", "MPL-2.0+"),
234 ];
236
237const EXCEPTIONS_RUST_ANALYZER: ExceptionList = &[
238 ("option-ext", "MPL-2.0"),
240 ];
242
243const EXCEPTIONS_RUSTC_PERF: ExceptionList = &[
244 ("aws-lc-rs", "ISC AND (Apache-2.0 OR ISC)"),
246 (
247 "aws-lc-sys",
248 "ISC AND (Apache-2.0 OR ISC) AND Apache-2.0 AND MIT AND BSD-3-Clause AND (Apache-2.0 OR ISC OR MIT) AND (Apache-2.0 OR ISC OR MIT-0)",
249 ),
250 ("brotli", "BSD-3-Clause AND MIT"),
251 ("fast-srgb8", "MIT OR Apache-2.0 OR CC0-1.0"),
252 ("inferno", "CDDL-1.0"),
253 ("option-ext", "MPL-2.0"),
254 ("wasite", "Apache-2.0 OR BSL-1.0 OR MIT"),
255 ("webpki-root-certs", "CDLA-Permissive-2.0"),
256 ("whoami", "Apache-2.0 OR BSL-1.0 OR MIT"),
257 ];
259
260const EXCEPTIONS_RUSTBOOK: ExceptionList = &[
261 ("font-awesome-as-a-crate", "CC-BY-4.0 AND MIT"),
263 ("mdbook-core", "MPL-2.0"),
264 ("mdbook-driver", "MPL-2.0"),
265 ("mdbook-html", "MPL-2.0"),
266 ("mdbook-markdown", "MPL-2.0"),
267 ("mdbook-preprocessor", "MPL-2.0"),
268 ("mdbook-renderer", "MPL-2.0"),
269 ("mdbook-summary", "MPL-2.0"),
270 ];
272
273const EXCEPTIONS_STDARCH: ExceptionList = &[];
274
275const EXCEPTIONS_CRANELIFT: ExceptionList = &[];
276
277const EXCEPTIONS_GCC: ExceptionList = &[
278 ("gccjit", "GPL-3.0"),
280 ("gccjit_sys", "GPL-3.0"),
281 ];
283
284const EXCEPTIONS_BOOTSTRAP: ExceptionList = &[];
285
286const EXCEPTIONS_UEFI_QEMU_TEST: ExceptionList = &[];
287
288const PERMITTED_RUSTC_DEPS_LOCATION: ListLocation = location!(+6);
289
290const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
295 "adler2",
297 "aho-corasick",
298 "allocator-api2", "annotate-snippets",
300 "anstream",
301 "anstyle",
302 "anstyle-parse",
303 "anstyle-query",
304 "anstyle-wincon",
305 "ar_archive_writer",
306 "arrayref",
307 "arrayvec",
308 "bitflags",
309 "blake3",
310 "block-buffer",
311 "block2",
312 "bstr",
313 "cc",
314 "cfg-if",
315 "cfg_aliases",
316 "colorchoice",
317 "constant_time_eq",
318 "cpufeatures",
319 "crc32fast",
320 "crossbeam-deque",
321 "crossbeam-epoch",
322 "crossbeam-utils",
323 "crypto-common",
324 "ctrlc",
325 "darling",
326 "darling_core",
327 "darling_macro",
328 "datafrog",
329 "derive-where",
330 "derive_setters",
331 "digest",
332 "dispatch2",
333 "displaydoc",
334 "dissimilar",
335 "dyn-clone",
336 "either",
337 "elsa",
338 "ena",
339 "equivalent",
340 "errno",
341 "expect-test",
342 "fallible-iterator", "fastrand",
344 "find-msvc-tools",
345 "flate2",
346 "fluent-bundle",
347 "fluent-langneg",
348 "fluent-syntax",
349 "fnv",
350 "foldhash",
351 "generic-array",
352 "getopts",
353 "getrandom",
354 "gimli",
355 "gsgdt",
356 "hashbrown",
357 "icu_collections",
358 "icu_list",
359 "icu_locale",
360 "icu_locale_core",
361 "icu_locale_data",
362 "icu_provider",
363 "ident_case",
364 "indexmap",
365 "intl-memoizer",
366 "intl_pluralrules",
367 "is_terminal_polyfill",
368 "itertools",
369 "itoa",
370 "jiff",
371 "jiff-static",
372 "jiff-tzdb",
373 "jiff-tzdb-platform",
374 "jobserver",
375 "lazy_static",
376 "leb128",
377 "libc",
378 "libloading",
379 "linux-raw-sys",
380 "litemap",
381 "lock_api",
382 "log",
383 "matchers",
384 "md-5",
385 "measureme",
386 "memchr",
387 "memmap2",
388 "miniz_oxide",
389 "nix",
390 "nu-ansi-term",
391 "objc2",
392 "objc2-encode",
393 "object",
394 "odht",
395 "once_cell",
396 "once_cell_polyfill",
397 "parking_lot",
398 "parking_lot_core",
399 "pathdiff",
400 "perf-event-open-sys",
401 "pin-project-lite",
402 "polonius-engine",
403 "portable-atomic", "portable-atomic-util",
405 "potential_utf",
406 "ppv-lite86",
407 "proc-macro-hack",
408 "proc-macro2",
409 "psm",
410 "pulldown-cmark",
411 "pulldown-cmark-escape",
412 "punycode",
413 "quote",
414 "r-efi",
415 "rand",
416 "rand_chacha",
417 "rand_core",
418 "rand_xorshift", "rand_xoshiro",
420 "redox_syscall",
421 "ref-cast",
422 "ref-cast-impl",
423 "regex",
424 "regex-automata",
425 "regex-syntax",
426 "rustc-demangle",
427 "rustc-hash",
428 "rustc-literal-escaper",
429 "rustc-stable-hash",
430 "rustc_apfloat",
431 "rustix",
432 "ruzstd", "ryu",
434 "schemars",
435 "schemars_derive",
436 "scoped-tls",
437 "scopeguard",
438 "self_cell",
439 "serde",
440 "serde_core",
441 "serde_derive",
442 "serde_derive_internals",
443 "serde_json",
444 "serde_path_to_error",
445 "sha1",
446 "sha2",
447 "sharded-slab",
448 "shlex",
449 "simd-adler32",
450 "smallvec",
451 "stable_deref_trait",
452 "stacker",
453 "static_assertions",
454 "strsim",
455 "syn",
456 "synstructure",
457 "tempfile",
458 "termize",
459 "thin-vec",
460 "thiserror",
461 "thiserror-impl",
462 "thorin-dwp",
463 "thread_local",
464 "tikv-jemalloc-sys",
465 "tinystr",
466 "tinyvec",
467 "tinyvec_macros",
468 "tracing",
469 "tracing-attributes",
470 "tracing-core",
471 "tracing-log",
472 "tracing-serde",
473 "tracing-subscriber",
474 "tracing-tree",
475 "twox-hash",
476 "type-map",
477 "typenum",
478 "unic-langid",
479 "unic-langid-impl",
480 "unic-langid-macros",
481 "unic-langid-macros-impl",
482 "unicase",
483 "unicode-ident",
484 "unicode-normalization",
485 "unicode-properties",
486 "unicode-script",
487 "unicode-security",
488 "unicode-width",
489 "utf8_iter",
490 "utf8parse",
491 "valuable",
492 "version_check",
493 "wasi",
494 "wasm-encoder",
495 "wasmparser",
496 "windows",
497 "windows-collections",
498 "windows-core",
499 "windows-future",
500 "windows-implement",
501 "windows-interface",
502 "windows-link",
503 "windows-numerics",
504 "windows-result",
505 "windows-strings",
506 "windows-sys",
507 "windows-targets",
508 "windows-threading",
509 "windows_aarch64_gnullvm",
510 "windows_aarch64_msvc",
511 "windows_i686_gnu",
512 "windows_i686_gnullvm",
513 "windows_i686_msvc",
514 "windows_x86_64_gnu",
515 "windows_x86_64_gnullvm",
516 "windows_x86_64_msvc",
517 "wit-bindgen-rt@0.39.0", "writeable",
519 "yoke",
520 "yoke-derive",
521 "zerocopy",
522 "zerocopy-derive",
523 "zerofrom",
524 "zerofrom-derive",
525 "zerotrie",
526 "zerovec",
527 "zerovec-derive",
528 "zlib-rs",
529 ];
531
532const PERMITTED_STDLIB_DEPS_LOCATION: ListLocation = location!(+2);
533
534const PERMITTED_STDLIB_DEPENDENCIES: &[&str] = &[
535 "addr2line",
537 "adler2",
538 "cc",
539 "cfg-if",
540 "compiler_builtins",
541 "dlmalloc",
542 "foldhash", "fortanix-sgx-abi",
544 "getopts",
545 "gimli",
546 "hashbrown",
547 "hermit-abi",
548 "libc",
549 "memchr",
550 "miniz_oxide",
551 "moto-rt",
552 "object",
553 "r-efi",
554 "r-efi-alloc",
555 "rand",
556 "rand_core",
557 "rand_xorshift",
558 "rustc-demangle",
559 "rustc-literal-escaper",
560 "shlex",
561 "unwinding",
562 "vex-sdk",
563 "wasip1",
564 "wasip2",
565 "wasip3",
566 "windows-link",
567 "windows-sys@0.61.100", "wit-bindgen",
569 ];
571
572const PERMITTED_CRANELIFT_DEPS_LOCATION: ListLocation = location!(+2);
573
574const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[
575 "allocator-api2",
577 "anyhow",
578 "arbitrary",
579 "bitflags",
580 "bumpalo",
581 "cfg-if",
582 "cranelift-assembler-x64",
583 "cranelift-assembler-x64-meta",
584 "cranelift-bforest",
585 "cranelift-bitset",
586 "cranelift-codegen",
587 "cranelift-codegen-meta",
588 "cranelift-codegen-shared",
589 "cranelift-control",
590 "cranelift-entity",
591 "cranelift-frontend",
592 "cranelift-isle",
593 "cranelift-jit",
594 "cranelift-module",
595 "cranelift-native",
596 "cranelift-object",
597 "cranelift-srcgen",
598 "crc32fast",
599 "equivalent",
600 "fnv",
601 "foldhash",
602 "gimli",
603 "hashbrown",
604 "heck",
605 "indexmap",
606 "libc",
607 "libloading",
608 "libm",
609 "log",
610 "mach2",
611 "memchr",
612 "memmap2",
613 "object",
614 "proc-macro2",
615 "quote",
616 "regalloc2",
617 "region",
618 "rustc-hash",
619 "serde",
620 "serde_core",
621 "serde_derive",
622 "smallvec",
623 "stable_deref_trait",
624 "syn",
625 "target-lexicon",
626 "unicode-ident",
627 "wasmtime-internal-core",
628 "wasmtime-internal-jit-icache-coherence",
629 "windows-link",
630 "windows-sys",
631 "windows-targets",
632 "windows_aarch64_gnullvm",
633 "windows_aarch64_msvc",
634 "windows_i686_gnu",
635 "windows_i686_gnullvm",
636 "windows_i686_msvc",
637 "windows_x86_64_gnu",
638 "windows_x86_64_gnullvm",
639 "windows_x86_64_msvc",
640 ];
642
643pub fn check(root: &Path, cargo: &Path, tidy_ctx: TidyCtx) {
648 let mut check = tidy_ctx.start_check("deps");
649 let bless = tidy_ctx.is_bless_enabled();
650
651 let mut checked_runtime_licenses = false;
652
653 check_proc_macro_dep_list(root, cargo, bless, &mut check);
654
655 for &WorkspaceInfo { path, exceptions, crates_and_deps, submodules } in WORKSPACES {
656 if has_missing_submodule(root, submodules, tidy_ctx.is_running_on_ci()) {
657 continue;
658 }
659
660 if !root.join(path).join("Cargo.lock").exists() {
661 check.error(format!("the `{path}` workspace doesn't have a Cargo.lock"));
662 continue;
663 }
664
665 let mut cmd = cargo_metadata::MetadataCommand::new();
666 cmd.cargo_path(cargo)
667 .manifest_path(root.join(path).join("Cargo.toml"))
668 .features(cargo_metadata::CargoOpt::AllFeatures)
669 .other_options(vec!["--locked".to_owned()]);
670 let metadata = t!(cmd.exec());
671
672 let absolute_root =
674 if path == "." { root.to_path_buf() } else { t!(std::path::absolute(root.join(path))) };
675 let absolute_root_real = t!(std::path::absolute(&metadata.workspace_root));
676 if absolute_root_real != absolute_root {
677 check.error(format!("{path} is part of another workspace ({} != {}), remove from `WORKSPACES` ({WORKSPACE_LOCATION})", absolute_root.display(), absolute_root_real.display()));
678 }
679 check_license_exceptions(&metadata, path, exceptions, &mut check);
680 if let Some((crates, permitted_deps, location)) = crates_and_deps {
681 let descr = crates.get(0).unwrap_or(&path);
682 check_permitted_dependencies(
683 &metadata,
684 descr,
685 permitted_deps,
686 crates,
687 location,
688 &mut check,
689 );
690 }
691
692 if path == "library" {
693 check_runtime_license_exceptions(&metadata, &mut check);
694 check_runtime_no_duplicate_dependencies(&metadata, &mut check);
695 check_runtime_no_proc_macros(&metadata, &mut check);
696 checked_runtime_licenses = true;
697 }
698 }
699
700 assert!(checked_runtime_licenses);
703}
704
705fn check_proc_macro_dep_list(root: &Path, cargo: &Path, bless: bool, check: &mut RunningCheck) {
707 if std::env::var("RUSTC").is_err() {
708 panic!("tidy must be run under bootstrap (./x test tidy), not as a standalone command");
709 }
710 let mut cmd = cargo_metadata::MetadataCommand::new();
711 cmd.cargo_path(cargo)
712 .manifest_path(root.join("Cargo.toml"))
713 .features(cargo_metadata::CargoOpt::AllFeatures)
714 .other_options(vec!["--locked".to_owned()]);
715 let metadata = t!(cmd.exec());
716 let is_proc_macro_pkg = |pkg: &Package| pkg.targets.iter().any(|target| target.is_proc_macro());
717
718 let mut proc_macro_deps = HashSet::new();
719 for pkg in metadata.packages.iter().filter(|pkg| is_proc_macro_pkg(pkg)) {
720 deps_of(&metadata, &pkg.id, &mut proc_macro_deps);
721 }
722 proc_macro_deps.retain(|pkg| !is_proc_macro_pkg(&metadata[pkg]));
724
725 let proc_macro_deps: HashSet<_> =
726 proc_macro_deps.into_iter().map(|dep| metadata[dep].name.as_ref()).collect();
727 let expected = proc_macro_deps::CRATES.iter().copied().collect::<HashSet<_>>();
728
729 let needs_blessing = proc_macro_deps.difference(&expected).next().is_some()
730 || expected.difference(&proc_macro_deps).next().is_some();
731
732 if needs_blessing && bless {
733 let mut proc_macro_deps: Vec<_> = proc_macro_deps.into_iter().collect();
734 proc_macro_deps.sort();
735 let mut file = File::create(root.join("src/bootstrap/src/utils/proc_macro_deps.rs"))
736 .expect("`proc_macro_deps` should exist");
737 writeln!(
738 &mut file,
739 "/// Do not update manually - use `./x.py test tidy --bless`
740/// Holds all direct and indirect dependencies of proc-macro crates in tree.
741/// See <https://github.com/rust-lang/rust/issues/134863>
742pub static CRATES: &[&str] = &[
743 // tidy-alphabetical-start"
744 )
745 .unwrap();
746 for dep in proc_macro_deps {
747 writeln!(&mut file, " {dep:?},").unwrap();
748 }
749 writeln!(
750 &mut file,
751 " // tidy-alphabetical-end
752];"
753 )
754 .unwrap();
755 } else {
756 let mut error_found = false;
757
758 for missing in proc_macro_deps.difference(&expected) {
759 error_found = true;
760 check.error(format!(
761 "proc-macro crate dependency `{missing}` is not registered in `src/bootstrap/src/utils/proc_macro_deps.rs`",
762 ));
763 }
764 for extra in expected.difference(&proc_macro_deps) {
765 error_found = true;
766 check.error(format!(
767 "`{extra}` is registered in `src/bootstrap/src/utils/proc_macro_deps.rs`, but is not a proc-macro crate dependency",
768 ));
769 }
770 if error_found {
771 check.message("Run `./x.py test tidy --bless` to regenerate the list");
772 }
773 }
774}
775
776pub fn has_missing_submodule(root: &Path, submodules: &[&str], is_ci: bool) -> bool {
780 !is_ci
781 && submodules.iter().any(|submodule| {
782 let path = root.join(submodule);
783 !path.exists()
784 || read_dir(path).unwrap().next().is_none()
786 })
787}
788
789fn check_runtime_license_exceptions(metadata: &Metadata, check: &mut RunningCheck) {
794 for pkg in &metadata.packages {
795 if pkg.source.is_none() {
796 continue;
798 }
799 let license = match &pkg.license {
800 Some(license) => license,
801 None => {
802 check
803 .error(format!("dependency `{}` does not define a license expression", pkg.id));
804 continue;
805 }
806 };
807 if !LICENSES.contains(&license.as_str()) {
808 if *pkg.name == "fortanix-sgx-abi" && pkg.license.as_deref() == Some("MPL-2.0") {
813 continue;
814 }
815
816 check.error(format!("invalid license `{}` in `{}`", license, pkg.id));
817 }
818 }
819}
820
821fn check_license_exceptions(
825 metadata: &Metadata,
826 workspace: &str,
827 exceptions: &[(&str, &str)],
828 check: &mut RunningCheck,
829) {
830 for (name, license) in exceptions {
832 if !metadata.packages.iter().any(|p| *p.name == *name) {
834 check.error(format!(
835 "could not find exception package `{name}` in workspace `{workspace}`\n\
836 Remove from EXCEPTIONS list if it is no longer used.",
837 ));
838 }
839 for pkg in metadata.packages.iter().filter(|p| *p.name == *name) {
841 match &pkg.license {
842 None => {
843 check.error(format!(
844 "dependency exception `{}` in workspace `{workspace}` does not declare a license expression",
845 pkg.id
846 ));
847 }
848 Some(pkg_license) => {
849 if pkg_license.as_str() != *license {
850 check.error(format!(r#"dependency exception `{name}` license in workspace `{workspace}` has changed
851 previously `{license}` now `{pkg_license}`
852 update EXCEPTIONS for the new license
853"#));
854 }
855 }
856 }
857 }
858 if LICENSES.contains(license) || LICENSES_TOOLS.contains(license) {
859 check.error(format!(
860 "dependency exception `{name}` is not necessary. `{license}` is an allowed license"
861 ));
862 }
863 }
864
865 let exception_names: Vec<_> = exceptions.iter().map(|(name, _license)| *name).collect();
866
867 for pkg in &metadata.packages {
869 if pkg.source.is_none() {
870 continue;
872 }
873 if exception_names.contains(&pkg.name.as_str()) {
874 continue;
875 }
876 let license = match &pkg.license {
877 Some(license) => license,
878 None => {
879 check.error(format!(
880 "dependency `{}` in workspace `{workspace}` does not define a license expression",
881 pkg.id
882 ));
883 continue;
884 }
885 };
886 if !LICENSES.contains(&license.as_str()) && !LICENSES_TOOLS.contains(&license.as_str()) {
887 check.error(format!(
888 "invalid license `{}` for package `{}` in workspace `{workspace}`",
889 license, pkg.id
890 ));
891 }
892 }
893}
894
895fn check_runtime_no_duplicate_dependencies(metadata: &Metadata, check: &mut RunningCheck) {
896 let mut seen_pkgs = HashSet::new();
897 for pkg in &metadata.packages {
898 if pkg.source.is_none() {
899 continue;
900 }
901
902 if !seen_pkgs.insert(&*pkg.name) {
903 check.error(format!(
904 "duplicate package `{}` is not allowed for the standard library",
905 pkg.name
906 ));
907 }
908 }
909}
910
911fn check_runtime_no_proc_macros(metadata: &Metadata, check: &mut RunningCheck) {
912 for pkg in &metadata.packages {
913 if pkg.targets.iter().any(|target| target.is_proc_macro()) {
914 check.error(format!(
915 "proc macro `{}` is not allowed as standard library dependency.\n\
916 Using proc macros in the standard library would break cross-compilation \
917 as proc-macros don't get shipped for the host tuple.",
918 pkg.name
919 ));
920 }
921 }
922}
923
924fn check_permitted_dependencies(
929 metadata: &Metadata,
930 descr: &str,
931 permitted_dependencies: &[&'static str],
932 restricted_dependency_crates: &[&'static str],
933 permitted_location: ListLocation,
934 check: &mut RunningCheck,
935) {
936 let mut has_permitted_dep_error = false;
937 let mut deps = HashSet::new();
938 for to_check in restricted_dependency_crates {
939 let to_check = pkg_from_name(metadata, to_check);
940 deps_of(metadata, &to_check.id, &mut deps);
941 }
942
943 for permitted in permitted_dependencies {
945 fn compare(pkg: &Package, permitted: &str) -> bool {
946 if let Some((name, version)) = permitted.split_once("@") {
947 let Ok(version) = Version::parse(version) else {
948 return false;
949 };
950 *pkg.name == name && pkg.version == version
951 } else {
952 *pkg.name == permitted
953 }
954 }
955 if !deps.iter().any(|dep_id| compare(pkg_from_id(metadata, dep_id), permitted)) {
956 check.error(format!(
957 "could not find allowed package `{permitted}`\n\
958 Remove from PERMITTED_DEPENDENCIES list if it is no longer used.",
959 ));
960 has_permitted_dep_error = true;
961 }
962 }
963
964 let permitted_dependencies: HashMap<_, _> = permitted_dependencies
966 .iter()
967 .map(|s| {
968 if let Some((name, version)) = s.split_once('@') {
969 (name, Version::parse(version).ok())
970 } else {
971 (*s, None)
972 }
973 })
974 .collect();
975
976 for dep in deps {
977 let dep = pkg_from_id(metadata, dep);
978 if dep.source.is_some() {
980 let is_eq = if let Some(version) = permitted_dependencies.get(dep.name.as_str()) {
981 if let Some(version) = version { version == &dep.version } else { true }
982 } else {
983 false
984 };
985 if !is_eq {
986 check.error(format!("Dependency for {descr} not explicitly permitted: {}", dep.id));
987 has_permitted_dep_error = true;
988 }
989 }
990 }
991
992 if has_permitted_dep_error {
993 eprintln!("Go to `{}:{}` for the list.", permitted_location.path, permitted_location.line);
994 }
995}
996
997fn pkg_from_name<'a>(metadata: &'a Metadata, name: &'static str) -> &'a Package {
999 let mut i = metadata.packages.iter().filter(|p| *p.name == name);
1000 let result =
1001 i.next().unwrap_or_else(|| panic!("could not find package `{name}` in package list"));
1002 assert!(i.next().is_none(), "more than one package found for `{name}`");
1003 result
1004}
1005
1006fn pkg_from_id<'a>(metadata: &'a Metadata, id: &PackageId) -> &'a Package {
1007 metadata.packages.iter().find(|p| &p.id == id).unwrap()
1008}
1009
1010fn deps_of<'a>(metadata: &'a Metadata, pkg_id: &'a PackageId, result: &mut HashSet<&'a PackageId>) {
1012 if !result.insert(pkg_id) {
1013 return;
1014 }
1015 let node = metadata
1016 .resolve
1017 .as_ref()
1018 .unwrap()
1019 .nodes
1020 .iter()
1021 .find(|n| &n.id == pkg_id)
1022 .unwrap_or_else(|| panic!("could not find `{pkg_id}` in resolve"));
1023 for dep in &node.deps {
1024 deps_of(metadata, &dep.pkg, result);
1025 }
1026}