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 ("inferno", "CDDL-1.0"),
246 ("option-ext", "MPL-2.0"),
247 ];
249
250const EXCEPTIONS_RUSTBOOK: ExceptionList = &[
251 ("font-awesome-as-a-crate", "CC-BY-4.0 AND MIT"),
253 ("mdbook-core", "MPL-2.0"),
254 ("mdbook-driver", "MPL-2.0"),
255 ("mdbook-html", "MPL-2.0"),
256 ("mdbook-markdown", "MPL-2.0"),
257 ("mdbook-preprocessor", "MPL-2.0"),
258 ("mdbook-renderer", "MPL-2.0"),
259 ("mdbook-summary", "MPL-2.0"),
260 ];
262
263const EXCEPTIONS_STDARCH: ExceptionList = &[];
264
265const EXCEPTIONS_CRANELIFT: ExceptionList = &[];
266
267const EXCEPTIONS_GCC: ExceptionList = &[
268 ("gccjit", "GPL-3.0"),
270 ("gccjit_sys", "GPL-3.0"),
271 ];
273
274const EXCEPTIONS_BOOTSTRAP: ExceptionList = &[];
275
276const EXCEPTIONS_UEFI_QEMU_TEST: ExceptionList = &[];
277
278const PERMITTED_RUSTC_DEPS_LOCATION: ListLocation = location!(+6);
279
280const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
285 "adler2",
287 "aho-corasick",
288 "allocator-api2", "annotate-snippets",
290 "anstream",
291 "anstyle",
292 "anstyle-parse",
293 "anstyle-query",
294 "anstyle-wincon",
295 "ar_archive_writer",
296 "arrayref",
297 "arrayvec",
298 "bitflags",
299 "blake3",
300 "block-buffer",
301 "block2",
302 "bstr",
303 "cc",
304 "cfg-if",
305 "cfg_aliases",
306 "colorchoice",
307 "constant_time_eq",
308 "cpufeatures",
309 "crc32fast",
310 "crossbeam-deque",
311 "crossbeam-epoch",
312 "crossbeam-utils",
313 "crypto-common",
314 "ctrlc",
315 "darling",
316 "darling_core",
317 "darling_macro",
318 "datafrog",
319 "derive-where",
320 "derive_setters",
321 "digest",
322 "dispatch2",
323 "displaydoc",
324 "dissimilar",
325 "dyn-clone",
326 "either",
327 "elsa",
328 "ena",
329 "equivalent",
330 "errno",
331 "expect-test",
332 "fallible-iterator", "fastrand",
334 "find-msvc-tools",
335 "flate2",
336 "fluent-bundle",
337 "fluent-langneg",
338 "fluent-syntax",
339 "fnv",
340 "foldhash",
341 "generic-array",
342 "getopts",
343 "getrandom",
344 "gimli",
345 "gsgdt",
346 "hashbrown",
347 "icu_collections",
348 "icu_list",
349 "icu_locale",
350 "icu_locale_core",
351 "icu_locale_data",
352 "icu_provider",
353 "ident_case",
354 "indexmap",
355 "intl-memoizer",
356 "intl_pluralrules",
357 "is_terminal_polyfill",
358 "itertools",
359 "itoa",
360 "jiff",
361 "jiff-static",
362 "jobserver",
363 "lazy_static",
364 "leb128",
365 "libc",
366 "libloading",
367 "linux-raw-sys",
368 "litemap",
369 "lock_api",
370 "log",
371 "matchers",
372 "md-5",
373 "measureme",
374 "memchr",
375 "memmap2",
376 "miniz_oxide",
377 "nix",
378 "nu-ansi-term",
379 "objc2",
380 "objc2-encode",
381 "object",
382 "odht",
383 "once_cell",
384 "once_cell_polyfill",
385 "parking_lot",
386 "parking_lot_core",
387 "pathdiff",
388 "perf-event-open-sys",
389 "pin-project-lite",
390 "polonius-engine",
391 "portable-atomic", "portable-atomic-util",
393 "potential_utf",
394 "ppv-lite86",
395 "proc-macro-hack",
396 "proc-macro2",
397 "psm",
398 "pulldown-cmark",
399 "pulldown-cmark-escape",
400 "punycode",
401 "quote",
402 "r-efi",
403 "rand",
404 "rand_chacha",
405 "rand_core",
406 "rand_xorshift", "rand_xoshiro",
408 "redox_syscall",
409 "ref-cast",
410 "ref-cast-impl",
411 "regex",
412 "regex-automata",
413 "regex-syntax",
414 "rustc-demangle",
415 "rustc-hash",
416 "rustc-literal-escaper",
417 "rustc-stable-hash",
418 "rustc_apfloat",
419 "rustix",
420 "ruzstd", "ryu",
422 "schemars",
423 "schemars_derive",
424 "scoped-tls",
425 "scopeguard",
426 "self_cell",
427 "serde",
428 "serde_core",
429 "serde_derive",
430 "serde_derive_internals",
431 "serde_json",
432 "serde_path_to_error",
433 "sha1",
434 "sha2",
435 "sharded-slab",
436 "shlex",
437 "simd-adler32",
438 "smallvec",
439 "stable_deref_trait",
440 "stacker",
441 "static_assertions",
442 "strsim",
443 "syn",
444 "synstructure",
445 "tempfile",
446 "termize",
447 "thin-vec",
448 "thiserror",
449 "thiserror-impl",
450 "thorin-dwp",
451 "thread_local",
452 "tikv-jemalloc-sys",
453 "tinystr",
454 "tinyvec",
455 "tinyvec_macros",
456 "tracing",
457 "tracing-attributes",
458 "tracing-core",
459 "tracing-log",
460 "tracing-serde",
461 "tracing-subscriber",
462 "tracing-tree",
463 "twox-hash",
464 "type-map",
465 "typenum",
466 "unic-langid",
467 "unic-langid-impl",
468 "unic-langid-macros",
469 "unic-langid-macros-impl",
470 "unicase",
471 "unicode-ident",
472 "unicode-normalization",
473 "unicode-properties",
474 "unicode-script",
475 "unicode-security",
476 "unicode-width",
477 "utf8_iter",
478 "utf8parse",
479 "valuable",
480 "version_check",
481 "wasi",
482 "wasm-encoder",
483 "wasmparser",
484 "windows",
485 "windows-collections",
486 "windows-core",
487 "windows-future",
488 "windows-implement",
489 "windows-interface",
490 "windows-link",
491 "windows-numerics",
492 "windows-result",
493 "windows-strings",
494 "windows-sys",
495 "windows-targets",
496 "windows-threading",
497 "windows_aarch64_gnullvm",
498 "windows_aarch64_msvc",
499 "windows_i686_gnu",
500 "windows_i686_gnullvm",
501 "windows_i686_msvc",
502 "windows_x86_64_gnu",
503 "windows_x86_64_gnullvm",
504 "windows_x86_64_msvc",
505 "wit-bindgen-rt@0.39.0", "writeable",
507 "yoke",
508 "yoke-derive",
509 "zerocopy",
510 "zerocopy-derive",
511 "zerofrom",
512 "zerofrom-derive",
513 "zerotrie",
514 "zerovec",
515 "zerovec-derive",
516 "zlib-rs",
517 ];
519
520const PERMITTED_STDLIB_DEPS_LOCATION: ListLocation = location!(+2);
521
522const PERMITTED_STDLIB_DEPENDENCIES: &[&str] = &[
523 "addr2line",
525 "adler2",
526 "cc",
527 "cfg-if",
528 "compiler_builtins",
529 "dlmalloc",
530 "foldhash", "fortanix-sgx-abi",
532 "getopts",
533 "gimli",
534 "hashbrown",
535 "hermit-abi",
536 "libc",
537 "memchr",
538 "miniz_oxide",
539 "moto-rt",
540 "object",
541 "r-efi",
542 "r-efi-alloc",
543 "rand",
544 "rand_core",
545 "rand_xorshift",
546 "rustc-demangle",
547 "rustc-literal-escaper",
548 "shlex",
549 "unwinding",
550 "vex-sdk",
551 "wasip1",
552 "wasip2",
553 "wasip3",
554 "windows-link",
555 "windows-sys@0.61.100", "wit-bindgen",
557 ];
559
560const PERMITTED_CRANELIFT_DEPS_LOCATION: ListLocation = location!(+2);
561
562const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[
563 "allocator-api2",
565 "anyhow",
566 "arbitrary",
567 "bitflags",
568 "bumpalo",
569 "cfg-if",
570 "cranelift-assembler-x64",
571 "cranelift-assembler-x64-meta",
572 "cranelift-bforest",
573 "cranelift-bitset",
574 "cranelift-codegen",
575 "cranelift-codegen-meta",
576 "cranelift-codegen-shared",
577 "cranelift-control",
578 "cranelift-entity",
579 "cranelift-frontend",
580 "cranelift-isle",
581 "cranelift-jit",
582 "cranelift-module",
583 "cranelift-native",
584 "cranelift-object",
585 "cranelift-srcgen",
586 "crc32fast",
587 "equivalent",
588 "fnv",
589 "foldhash",
590 "gimli",
591 "hashbrown",
592 "heck",
593 "indexmap",
594 "libc",
595 "libloading",
596 "libm",
597 "log",
598 "mach2",
599 "memchr",
600 "memmap2",
601 "object",
602 "proc-macro2",
603 "quote",
604 "regalloc2",
605 "region",
606 "rustc-hash",
607 "serde",
608 "serde_core",
609 "serde_derive",
610 "smallvec",
611 "stable_deref_trait",
612 "syn",
613 "target-lexicon",
614 "unicode-ident",
615 "wasmtime-internal-core",
616 "wasmtime-internal-jit-icache-coherence",
617 "windows-link",
618 "windows-sys",
619 "windows-targets",
620 "windows_aarch64_gnullvm",
621 "windows_aarch64_msvc",
622 "windows_i686_gnu",
623 "windows_i686_gnullvm",
624 "windows_i686_msvc",
625 "windows_x86_64_gnu",
626 "windows_x86_64_gnullvm",
627 "windows_x86_64_msvc",
628 ];
630
631pub fn check(root: &Path, cargo: &Path, tidy_ctx: TidyCtx) {
636 let mut check = tidy_ctx.start_check("deps");
637 let bless = tidy_ctx.is_bless_enabled();
638
639 let mut checked_runtime_licenses = false;
640
641 check_proc_macro_dep_list(root, cargo, bless, &mut check);
642
643 for &WorkspaceInfo { path, exceptions, crates_and_deps, submodules } in WORKSPACES {
644 if has_missing_submodule(root, submodules, tidy_ctx.is_running_on_ci()) {
645 continue;
646 }
647
648 if !root.join(path).join("Cargo.lock").exists() {
649 check.error(format!("the `{path}` workspace doesn't have a Cargo.lock"));
650 continue;
651 }
652
653 let mut cmd = cargo_metadata::MetadataCommand::new();
654 cmd.cargo_path(cargo)
655 .manifest_path(root.join(path).join("Cargo.toml"))
656 .features(cargo_metadata::CargoOpt::AllFeatures)
657 .other_options(vec!["--locked".to_owned()]);
658 let metadata = t!(cmd.exec());
659
660 let absolute_root =
662 if path == "." { root.to_path_buf() } else { t!(std::path::absolute(root.join(path))) };
663 let absolute_root_real = t!(std::path::absolute(&metadata.workspace_root));
664 if absolute_root_real != absolute_root {
665 check.error(format!("{path} is part of another workspace ({} != {}), remove from `WORKSPACES` ({WORKSPACE_LOCATION})", absolute_root.display(), absolute_root_real.display()));
666 }
667 check_license_exceptions(&metadata, path, exceptions, &mut check);
668 if let Some((crates, permitted_deps, location)) = crates_and_deps {
669 let descr = crates.get(0).unwrap_or(&path);
670 check_permitted_dependencies(
671 &metadata,
672 descr,
673 permitted_deps,
674 crates,
675 location,
676 &mut check,
677 );
678 }
679
680 if path == "library" {
681 check_runtime_license_exceptions(&metadata, &mut check);
682 check_runtime_no_duplicate_dependencies(&metadata, &mut check);
683 check_runtime_no_proc_macros(&metadata, &mut check);
684 checked_runtime_licenses = true;
685 }
686 }
687
688 assert!(checked_runtime_licenses);
691}
692
693fn check_proc_macro_dep_list(root: &Path, cargo: &Path, bless: bool, check: &mut RunningCheck) {
695 if std::env::var("RUSTC").is_err() {
696 panic!("tidy must be run under bootstrap (./x test tidy), not as a standalone command");
697 }
698 let mut cmd = cargo_metadata::MetadataCommand::new();
699 cmd.cargo_path(cargo)
700 .manifest_path(root.join("Cargo.toml"))
701 .features(cargo_metadata::CargoOpt::AllFeatures)
702 .other_options(vec!["--locked".to_owned()]);
703 let metadata = t!(cmd.exec());
704 let is_proc_macro_pkg = |pkg: &Package| pkg.targets.iter().any(|target| target.is_proc_macro());
705
706 let mut proc_macro_deps = HashSet::new();
707 for pkg in metadata.packages.iter().filter(|pkg| is_proc_macro_pkg(pkg)) {
708 deps_of(&metadata, &pkg.id, &mut proc_macro_deps);
709 }
710 proc_macro_deps.retain(|pkg| !is_proc_macro_pkg(&metadata[pkg]));
712
713 let proc_macro_deps: HashSet<_> =
714 proc_macro_deps.into_iter().map(|dep| metadata[dep].name.as_ref()).collect();
715 let expected = proc_macro_deps::CRATES.iter().copied().collect::<HashSet<_>>();
716
717 let needs_blessing = proc_macro_deps.difference(&expected).next().is_some()
718 || expected.difference(&proc_macro_deps).next().is_some();
719
720 if needs_blessing && bless {
721 let mut proc_macro_deps: Vec<_> = proc_macro_deps.into_iter().collect();
722 proc_macro_deps.sort();
723 let mut file = File::create(root.join("src/bootstrap/src/utils/proc_macro_deps.rs"))
724 .expect("`proc_macro_deps` should exist");
725 writeln!(
726 &mut file,
727 "/// Do not update manually - use `./x.py test tidy --bless`
728/// Holds all direct and indirect dependencies of proc-macro crates in tree.
729/// See <https://github.com/rust-lang/rust/issues/134863>
730pub static CRATES: &[&str] = &[
731 // tidy-alphabetical-start"
732 )
733 .unwrap();
734 for dep in proc_macro_deps {
735 writeln!(&mut file, " {dep:?},").unwrap();
736 }
737 writeln!(
738 &mut file,
739 " // tidy-alphabetical-end
740];"
741 )
742 .unwrap();
743 } else {
744 let mut error_found = false;
745
746 for missing in proc_macro_deps.difference(&expected) {
747 error_found = true;
748 check.error(format!(
749 "proc-macro crate dependency `{missing}` is not registered in `src/bootstrap/src/utils/proc_macro_deps.rs`",
750 ));
751 }
752 for extra in expected.difference(&proc_macro_deps) {
753 error_found = true;
754 check.error(format!(
755 "`{extra}` is registered in `src/bootstrap/src/utils/proc_macro_deps.rs`, but is not a proc-macro crate dependency",
756 ));
757 }
758 if error_found {
759 check.message("Run `./x.py test tidy --bless` to regenerate the list");
760 }
761 }
762}
763
764pub fn has_missing_submodule(root: &Path, submodules: &[&str], is_ci: bool) -> bool {
768 !is_ci
769 && submodules.iter().any(|submodule| {
770 let path = root.join(submodule);
771 !path.exists()
772 || read_dir(path).unwrap().next().is_none()
774 })
775}
776
777fn check_runtime_license_exceptions(metadata: &Metadata, check: &mut RunningCheck) {
782 for pkg in &metadata.packages {
783 if pkg.source.is_none() {
784 continue;
786 }
787 let license = match &pkg.license {
788 Some(license) => license,
789 None => {
790 check
791 .error(format!("dependency `{}` does not define a license expression", pkg.id));
792 continue;
793 }
794 };
795 if !LICENSES.contains(&license.as_str()) {
796 if *pkg.name == "fortanix-sgx-abi" && pkg.license.as_deref() == Some("MPL-2.0") {
801 continue;
802 }
803
804 check.error(format!("invalid license `{}` in `{}`", license, pkg.id));
805 }
806 }
807}
808
809fn check_license_exceptions(
813 metadata: &Metadata,
814 workspace: &str,
815 exceptions: &[(&str, &str)],
816 check: &mut RunningCheck,
817) {
818 for (name, license) in exceptions {
820 if !metadata.packages.iter().any(|p| *p.name == *name) {
822 check.error(format!(
823 "could not find exception package `{name}` in workspace `{workspace}`\n\
824 Remove from EXCEPTIONS list if it is no longer used.",
825 ));
826 }
827 for pkg in metadata.packages.iter().filter(|p| *p.name == *name) {
829 match &pkg.license {
830 None => {
831 check.error(format!(
832 "dependency exception `{}` in workspace `{workspace}` does not declare a license expression",
833 pkg.id
834 ));
835 }
836 Some(pkg_license) => {
837 if pkg_license.as_str() != *license {
838 check.error(format!(r#"dependency exception `{name}` license in workspace `{workspace}` has changed
839 previously `{license}` now `{pkg_license}`
840 update EXCEPTIONS for the new license
841"#));
842 }
843 }
844 }
845 }
846 if LICENSES.contains(license) || LICENSES_TOOLS.contains(license) {
847 check.error(format!(
848 "dependency exception `{name}` is not necessary. `{license}` is an allowed license"
849 ));
850 }
851 }
852
853 let exception_names: Vec<_> = exceptions.iter().map(|(name, _license)| *name).collect();
854
855 for pkg in &metadata.packages {
857 if pkg.source.is_none() {
858 continue;
860 }
861 if exception_names.contains(&pkg.name.as_str()) {
862 continue;
863 }
864 let license = match &pkg.license {
865 Some(license) => license,
866 None => {
867 check.error(format!(
868 "dependency `{}` in workspace `{workspace}` does not define a license expression",
869 pkg.id
870 ));
871 continue;
872 }
873 };
874 if !LICENSES.contains(&license.as_str()) && !LICENSES_TOOLS.contains(&license.as_str()) {
875 check.error(format!(
876 "invalid license `{}` for package `{}` in workspace `{workspace}`",
877 license, pkg.id
878 ));
879 }
880 }
881}
882
883fn check_runtime_no_duplicate_dependencies(metadata: &Metadata, check: &mut RunningCheck) {
884 let mut seen_pkgs = HashSet::new();
885 for pkg in &metadata.packages {
886 if pkg.source.is_none() {
887 continue;
888 }
889
890 if !seen_pkgs.insert(&*pkg.name) {
891 check.error(format!(
892 "duplicate package `{}` is not allowed for the standard library",
893 pkg.name
894 ));
895 }
896 }
897}
898
899fn check_runtime_no_proc_macros(metadata: &Metadata, check: &mut RunningCheck) {
900 for pkg in &metadata.packages {
901 if pkg.targets.iter().any(|target| target.is_proc_macro()) {
902 check.error(format!(
903 "proc macro `{}` is not allowed as standard library dependency.\n\
904 Using proc macros in the standard library would break cross-compilation \
905 as proc-macros don't get shipped for the host tuple.",
906 pkg.name
907 ));
908 }
909 }
910}
911
912fn check_permitted_dependencies(
917 metadata: &Metadata,
918 descr: &str,
919 permitted_dependencies: &[&'static str],
920 restricted_dependency_crates: &[&'static str],
921 permitted_location: ListLocation,
922 check: &mut RunningCheck,
923) {
924 let mut has_permitted_dep_error = false;
925 let mut deps = HashSet::new();
926 for to_check in restricted_dependency_crates {
927 let to_check = pkg_from_name(metadata, to_check);
928 deps_of(metadata, &to_check.id, &mut deps);
929 }
930
931 for permitted in permitted_dependencies {
933 fn compare(pkg: &Package, permitted: &str) -> bool {
934 if let Some((name, version)) = permitted.split_once("@") {
935 let Ok(version) = Version::parse(version) else {
936 return false;
937 };
938 *pkg.name == name && pkg.version == version
939 } else {
940 *pkg.name == permitted
941 }
942 }
943 if !deps.iter().any(|dep_id| compare(pkg_from_id(metadata, dep_id), permitted)) {
944 check.error(format!(
945 "could not find allowed package `{permitted}`\n\
946 Remove from PERMITTED_DEPENDENCIES list if it is no longer used.",
947 ));
948 has_permitted_dep_error = true;
949 }
950 }
951
952 let permitted_dependencies: HashMap<_, _> = permitted_dependencies
954 .iter()
955 .map(|s| {
956 if let Some((name, version)) = s.split_once('@') {
957 (name, Version::parse(version).ok())
958 } else {
959 (*s, None)
960 }
961 })
962 .collect();
963
964 for dep in deps {
965 let dep = pkg_from_id(metadata, dep);
966 if dep.source.is_some() {
968 let is_eq = if let Some(version) = permitted_dependencies.get(dep.name.as_str()) {
969 if let Some(version) = version { version == &dep.version } else { true }
970 } else {
971 false
972 };
973 if !is_eq {
974 check.error(format!("Dependency for {descr} not explicitly permitted: {}", dep.id));
975 has_permitted_dep_error = true;
976 }
977 }
978 }
979
980 if has_permitted_dep_error {
981 eprintln!("Go to `{}:{}` for the list.", permitted_location.path, permitted_location.line);
982 }
983}
984
985fn pkg_from_name<'a>(metadata: &'a Metadata, name: &'static str) -> &'a Package {
987 let mut i = metadata.packages.iter().filter(|p| *p.name == name);
988 let result =
989 i.next().unwrap_or_else(|| panic!("could not find package `{name}` in package list"));
990 assert!(i.next().is_none(), "more than one package found for `{name}`");
991 result
992}
993
994fn pkg_from_id<'a>(metadata: &'a Metadata, id: &PackageId) -> &'a Package {
995 metadata.packages.iter().find(|p| &p.id == id).unwrap()
996}
997
998fn deps_of<'a>(metadata: &'a Metadata, pkg_id: &'a PackageId, result: &mut HashSet<&'a PackageId>) {
1000 if !result.insert(pkg_id) {
1001 return;
1002 }
1003 let node = metadata
1004 .resolve
1005 .as_ref()
1006 .unwrap()
1007 .nodes
1008 .iter()
1009 .find(|n| &n.id == pkg_id)
1010 .unwrap_or_else(|| panic!("could not find `{pkg_id}` in resolve"));
1011 for dep in &node.deps {
1012 deps_of(metadata, &dep.pkg, result);
1013 }
1014}