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 "Zlib", ];
48
49#[rustfmt::skip]
51const LICENSES_TOOLS: &[&str] = &[
52 "(Apache-2.0 OR MIT) AND BSD-3-Clause",
54 "(MIT OR Apache-2.0) AND Unicode-3.0", "(MIT OR Apache-2.0) AND Unicode-DFS-2016", "0BSD",
57 "Apache-2.0 AND ISC",
58 "Apache-2.0 OR BSL-1.0", "Apache-2.0 OR GPL-2.0-only",
60 "Apache-2.0 WITH LLVM-exception",
61 "Apache-2.0",
62 "BSD-2-Clause",
63 "BSD-3-Clause",
64 "CC0-1.0 OR Apache-2.0 OR Apache-2.0 WITH LLVM-exception",
65 "CC0-1.0",
66 "Unicode-3.0", "Unicode-DFS-2016", "Zlib OR Apache-2.0 OR MIT", "Zlib",
70 ];
72
73type ExceptionList = &'static [(&'static str, &'static str)];
74
75#[derive(Clone, Copy)]
76pub(crate) struct WorkspaceInfo<'a> {
77 pub(crate) path: &'a str,
79 pub(crate) exceptions: ExceptionList,
81 crates_and_deps: Option<(&'a [&'a str], &'a [&'a str], ListLocation)>,
86 pub(crate) submodules: &'a [&'a str],
88}
89
90pub(crate) const WORKSPACES: &[WorkspaceInfo<'static>] = &[
93 WorkspaceInfo {
95 path: ".",
96 exceptions: EXCEPTIONS,
97 crates_and_deps: Some((
98 &["rustc-main"],
99 PERMITTED_RUSTC_DEPENDENCIES,
100 PERMITTED_RUSTC_DEPS_LOCATION,
101 )),
102 submodules: &[],
103 },
104 WorkspaceInfo {
105 path: "library",
106 exceptions: EXCEPTIONS_STDLIB,
107 crates_and_deps: Some((
108 &["sysroot"],
109 PERMITTED_STDLIB_DEPENDENCIES,
110 PERMITTED_STDLIB_DEPS_LOCATION,
111 )),
112 submodules: &[],
113 },
114 WorkspaceInfo {
115 path: "compiler/rustc_codegen_cranelift",
116 exceptions: EXCEPTIONS_CRANELIFT,
117 crates_and_deps: Some((
118 &["rustc_codegen_cranelift"],
119 PERMITTED_CRANELIFT_DEPENDENCIES,
120 PERMITTED_CRANELIFT_DEPS_LOCATION,
121 )),
122 submodules: &[],
123 },
124 WorkspaceInfo {
125 path: "compiler/rustc_codegen_gcc",
126 exceptions: EXCEPTIONS_GCC,
127 crates_and_deps: None,
128 submodules: &[],
129 },
130 WorkspaceInfo {
131 path: "src/bootstrap",
132 exceptions: EXCEPTIONS_BOOTSTRAP,
133 crates_and_deps: None,
134 submodules: &[],
135 },
136 WorkspaceInfo {
137 path: "src/tools/cargo",
138 exceptions: EXCEPTIONS_CARGO,
139 crates_and_deps: None,
140 submodules: &["src/tools/cargo"],
141 },
142 WorkspaceInfo {
154 path: "src/tools/rust-analyzer",
155 exceptions: EXCEPTIONS_RUST_ANALYZER,
156 crates_and_deps: None,
157 submodules: &[],
158 },
159 WorkspaceInfo {
160 path: "src/tools/rustbook",
161 exceptions: EXCEPTIONS_RUSTBOOK,
162 crates_and_deps: None,
163 submodules: &["src/doc/book", "src/doc/reference"],
164 },
165 WorkspaceInfo {
166 path: "src/tools/rustc-perf",
167 exceptions: EXCEPTIONS_RUSTC_PERF,
168 crates_and_deps: None,
169 submodules: &["src/tools/rustc-perf"],
170 },
171 WorkspaceInfo {
172 path: "tests/run-make-cargo/uefi-qemu/uefi_qemu_test",
173 exceptions: EXCEPTIONS_UEFI_QEMU_TEST,
174 crates_and_deps: None,
175 submodules: &[],
176 },
177];
178
179#[rustfmt::skip]
184const EXCEPTIONS: ExceptionList = &[
185 ("colored", "MPL-2.0"), ("option-ext", "MPL-2.0"), ];
190
191#[rustfmt::skip]
196const EXCEPTIONS_STDLIB: ExceptionList = &[
197 ("fortanix-sgx-abi", "MPL-2.0"), ];
201
202const EXCEPTIONS_CARGO: ExceptionList = &[
203 ("bitmaps", "MPL-2.0+"),
205 ("im-rc", "MPL-2.0+"),
206 ("sized-chunks", "MPL-2.0+"),
207 ];
209
210const EXCEPTIONS_RUST_ANALYZER: ExceptionList = &[
211 ("option-ext", "MPL-2.0"),
213 ];
215
216const EXCEPTIONS_RUSTC_PERF: ExceptionList = &[
217 ("inferno", "CDDL-1.0"),
219 ("option-ext", "MPL-2.0"),
220 ];
222
223const EXCEPTIONS_RUSTBOOK: ExceptionList = &[
224 ("cssparser", "MPL-2.0"),
226 ("cssparser-macros", "MPL-2.0"),
227 ("dtoa-short", "MPL-2.0"),
228 ("mdbook", "MPL-2.0"),
229 ];
231
232const EXCEPTIONS_CRANELIFT: ExceptionList = &[];
233
234const EXCEPTIONS_GCC: ExceptionList = &[
235 ("gccjit", "GPL-3.0"),
237 ("gccjit_sys", "GPL-3.0"),
238 ];
240
241const EXCEPTIONS_BOOTSTRAP: ExceptionList = &[];
242
243const EXCEPTIONS_UEFI_QEMU_TEST: ExceptionList = &[];
244
245#[derive(Clone, Copy)]
246struct ListLocation {
247 path: &'static str,
248 line: u32,
249}
250
251macro_rules! location {
253 (+ $offset:literal) => {
254 ListLocation { path: file!(), line: line!() + $offset }
255 };
256}
257
258const PERMITTED_RUSTC_DEPS_LOCATION: ListLocation = location!(+6);
259
260const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
265 "adler2",
267 "aho-corasick",
268 "allocator-api2", "annotate-snippets",
270 "anstream",
271 "anstyle",
272 "anstyle-parse",
273 "anstyle-query",
274 "anstyle-wincon",
275 "ar_archive_writer",
276 "arrayref",
277 "arrayvec",
278 "bitflags",
279 "blake3",
280 "block-buffer",
281 "block2",
282 "bstr",
283 "cc",
284 "cfg-if",
285 "cfg_aliases",
286 "colorchoice",
287 "constant_time_eq",
288 "cpufeatures",
289 "crc32fast",
290 "crossbeam-deque",
291 "crossbeam-epoch",
292 "crossbeam-utils",
293 "crypto-common",
294 "ctrlc",
295 "darling",
296 "darling_core",
297 "darling_macro",
298 "datafrog",
299 "derive-where",
300 "derive_setters",
301 "digest",
302 "dispatch2",
303 "displaydoc",
304 "dissimilar",
305 "dyn-clone",
306 "either",
307 "elsa",
308 "ena",
309 "equivalent",
310 "errno",
311 "expect-test",
312 "fallible-iterator", "fastrand",
314 "find-msvc-tools",
315 "flate2",
316 "fluent-bundle",
317 "fluent-langneg",
318 "fluent-syntax",
319 "fnv",
320 "foldhash",
321 "generic-array",
322 "getopts",
323 "getrandom",
324 "gimli",
325 "gsgdt",
326 "hashbrown",
327 "icu_collections",
328 "icu_list",
329 "icu_locale",
330 "icu_locale_core",
331 "icu_locale_data",
332 "icu_provider",
333 "ident_case",
334 "indexmap",
335 "intl-memoizer",
336 "intl_pluralrules",
337 "is_terminal_polyfill",
338 "itertools",
339 "itoa",
340 "jiff",
341 "jiff-static",
342 "jobserver",
343 "lazy_static",
344 "leb128",
345 "libc",
346 "libloading",
347 "linux-raw-sys",
348 "litemap",
349 "lock_api",
350 "log",
351 "matchers",
352 "md-5",
353 "measureme",
354 "memchr",
355 "memmap2",
356 "miniz_oxide",
357 "nix",
358 "nu-ansi-term",
359 "objc2",
360 "objc2-encode",
361 "object",
362 "odht",
363 "once_cell",
364 "once_cell_polyfill",
365 "parking_lot",
366 "parking_lot_core",
367 "pathdiff",
368 "perf-event-open-sys",
369 "pin-project-lite",
370 "polonius-engine",
371 "portable-atomic", "portable-atomic-util",
373 "potential_utf",
374 "ppv-lite86",
375 "proc-macro-hack",
376 "proc-macro2",
377 "psm",
378 "pulldown-cmark",
379 "pulldown-cmark-escape",
380 "punycode",
381 "quote",
382 "r-efi",
383 "rand",
384 "rand_chacha",
385 "rand_core",
386 "rand_xorshift", "rand_xoshiro",
388 "redox_syscall",
389 "ref-cast",
390 "ref-cast-impl",
391 "regex",
392 "regex-automata",
393 "regex-syntax",
394 "rustc-demangle",
395 "rustc-hash",
396 "rustc-literal-escaper",
397 "rustc-stable-hash",
398 "rustc_apfloat",
399 "rustix",
400 "ruzstd", "ryu",
402 "schemars",
403 "schemars_derive",
404 "scoped-tls",
405 "scopeguard",
406 "self_cell",
407 "serde",
408 "serde_core",
409 "serde_derive",
410 "serde_derive_internals",
411 "serde_json",
412 "serde_path_to_error",
413 "sha1",
414 "sha2",
415 "sharded-slab",
416 "shlex",
417 "simd-adler32",
418 "smallvec",
419 "stable_deref_trait",
420 "stacker",
421 "static_assertions",
422 "strsim",
423 "syn",
424 "synstructure",
425 "tempfile",
426 "termize",
427 "thin-vec",
428 "thiserror",
429 "thiserror-impl",
430 "thorin-dwp",
431 "thread_local",
432 "tikv-jemalloc-sys",
433 "tinystr",
434 "tinyvec",
435 "tinyvec_macros",
436 "tracing",
437 "tracing-attributes",
438 "tracing-core",
439 "tracing-log",
440 "tracing-subscriber",
441 "tracing-tree",
442 "twox-hash",
443 "type-map",
444 "typenum",
445 "unic-langid",
446 "unic-langid-impl",
447 "unic-langid-macros",
448 "unic-langid-macros-impl",
449 "unicase",
450 "unicode-ident",
451 "unicode-normalization",
452 "unicode-properties",
453 "unicode-script",
454 "unicode-security",
455 "unicode-width",
456 "unicode-xid",
457 "utf8parse",
458 "valuable",
459 "version_check",
460 "wasi",
461 "wasm-encoder",
462 "wasmparser",
463 "windows",
464 "windows-collections",
465 "windows-core",
466 "windows-future",
467 "windows-implement",
468 "windows-interface",
469 "windows-link",
470 "windows-numerics",
471 "windows-result",
472 "windows-strings",
473 "windows-sys",
474 "windows-targets",
475 "windows-threading",
476 "windows_aarch64_gnullvm",
477 "windows_aarch64_msvc",
478 "windows_i686_gnu",
479 "windows_i686_gnullvm",
480 "windows_i686_msvc",
481 "windows_x86_64_gnu",
482 "windows_x86_64_gnullvm",
483 "windows_x86_64_msvc",
484 "wit-bindgen-rt@0.39.0", "writeable",
486 "yoke",
487 "yoke-derive",
488 "zerocopy",
489 "zerocopy-derive",
490 "zerofrom",
491 "zerofrom-derive",
492 "zerotrie",
493 "zerovec",
494 "zerovec-derive",
495 ];
497
498const PERMITTED_STDLIB_DEPS_LOCATION: ListLocation = location!(+2);
499
500const PERMITTED_STDLIB_DEPENDENCIES: &[&str] = &[
501 "addr2line",
503 "adler2",
504 "cc",
505 "cfg-if",
506 "compiler_builtins",
507 "dlmalloc",
508 "foldhash", "fortanix-sgx-abi",
510 "getopts",
511 "gimli",
512 "hashbrown",
513 "hermit-abi",
514 "libc",
515 "memchr",
516 "miniz_oxide",
517 "moto-rt",
518 "object",
519 "r-efi",
520 "r-efi-alloc",
521 "rand",
522 "rand_core",
523 "rand_xorshift",
524 "rustc-demangle",
525 "rustc-literal-escaper",
526 "shlex",
527 "unwinding",
528 "vex-sdk",
529 "wasi",
530 "windows-link",
531 "windows-sys",
532 "windows-targets",
533 "windows_aarch64_gnullvm",
534 "windows_aarch64_msvc",
535 "windows_i686_gnu",
536 "windows_i686_gnullvm",
537 "windows_i686_msvc",
538 "windows_x86_64_gnu",
539 "windows_x86_64_gnullvm",
540 "windows_x86_64_msvc",
541 "wit-bindgen",
542 ];
544
545const PERMITTED_CRANELIFT_DEPS_LOCATION: ListLocation = location!(+2);
546
547const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[
548 "allocator-api2",
550 "anyhow",
551 "arbitrary",
552 "bitflags",
553 "bumpalo",
554 "cfg-if",
555 "cranelift-assembler-x64",
556 "cranelift-assembler-x64-meta",
557 "cranelift-bforest",
558 "cranelift-bitset",
559 "cranelift-codegen",
560 "cranelift-codegen-meta",
561 "cranelift-codegen-shared",
562 "cranelift-control",
563 "cranelift-entity",
564 "cranelift-frontend",
565 "cranelift-isle",
566 "cranelift-jit",
567 "cranelift-module",
568 "cranelift-native",
569 "cranelift-object",
570 "cranelift-srcgen",
571 "crc32fast",
572 "equivalent",
573 "fallible-iterator",
574 "foldhash",
575 "gimli",
576 "hashbrown",
577 "heck",
578 "indexmap",
579 "libc",
580 "libloading",
581 "libm",
582 "log",
583 "mach2",
584 "memchr",
585 "object",
586 "proc-macro2",
587 "quote",
588 "regalloc2",
589 "region",
590 "rustc-hash",
591 "serde",
592 "serde_derive",
593 "smallvec",
594 "stable_deref_trait",
595 "syn",
596 "target-lexicon",
597 "unicode-ident",
598 "wasmtime-internal-jit-icache-coherence",
599 "wasmtime-internal-math",
600 "windows-link",
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}