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