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_core",
593 "serde_derive",
594 "smallvec",
595 "stable_deref_trait",
596 "syn",
597 "target-lexicon",
598 "unicode-ident",
599 "wasmtime-internal-jit-icache-coherence",
600 "wasmtime-internal-math",
601 "windows-link",
602 "windows-sys",
603 "windows-targets",
604 "windows_aarch64_gnullvm",
605 "windows_aarch64_msvc",
606 "windows_i686_gnu",
607 "windows_i686_gnullvm",
608 "windows_i686_msvc",
609 "windows_x86_64_gnu",
610 "windows_x86_64_gnullvm",
611 "windows_x86_64_msvc",
612 ];
614
615pub fn check(root: &Path, cargo: &Path, tidy_ctx: TidyCtx) {
620 let mut check = tidy_ctx.start_check("deps");
621 let bless = tidy_ctx.is_bless_enabled();
622
623 let mut checked_runtime_licenses = false;
624
625 check_proc_macro_dep_list(root, cargo, bless, &mut check);
626
627 for &WorkspaceInfo { path, exceptions, crates_and_deps, submodules } in WORKSPACES {
628 if has_missing_submodule(root, submodules) {
629 continue;
630 }
631
632 if !root.join(path).join("Cargo.lock").exists() {
633 check.error(format!("the `{path}` workspace doesn't have a Cargo.lock"));
634 continue;
635 }
636
637 let mut cmd = cargo_metadata::MetadataCommand::new();
638 cmd.cargo_path(cargo)
639 .manifest_path(root.join(path).join("Cargo.toml"))
640 .features(cargo_metadata::CargoOpt::AllFeatures)
641 .other_options(vec!["--locked".to_owned()]);
642 let metadata = t!(cmd.exec());
643
644 check_license_exceptions(&metadata, path, exceptions, &mut check);
645 if let Some((crates, permitted_deps, location)) = crates_and_deps {
646 let descr = crates.get(0).unwrap_or(&path);
647 check_permitted_dependencies(
648 &metadata,
649 descr,
650 permitted_deps,
651 crates,
652 location,
653 &mut check,
654 );
655 }
656
657 if path == "library" {
658 check_runtime_license_exceptions(&metadata, &mut check);
659 check_runtime_no_duplicate_dependencies(&metadata, &mut check);
660 check_runtime_no_proc_macros(&metadata, &mut check);
661 checked_runtime_licenses = true;
662 }
663 }
664
665 assert!(checked_runtime_licenses);
668}
669
670fn check_proc_macro_dep_list(root: &Path, cargo: &Path, bless: bool, check: &mut RunningCheck) {
672 let mut cmd = cargo_metadata::MetadataCommand::new();
673 cmd.cargo_path(cargo)
674 .manifest_path(root.join("Cargo.toml"))
675 .features(cargo_metadata::CargoOpt::AllFeatures)
676 .other_options(vec!["--locked".to_owned()]);
677 let metadata = t!(cmd.exec());
678 let is_proc_macro_pkg = |pkg: &Package| pkg.targets.iter().any(|target| target.is_proc_macro());
679
680 let mut proc_macro_deps = HashSet::new();
681 for pkg in metadata.packages.iter().filter(|pkg| is_proc_macro_pkg(pkg)) {
682 deps_of(&metadata, &pkg.id, &mut proc_macro_deps);
683 }
684 proc_macro_deps.retain(|pkg| !is_proc_macro_pkg(&metadata[pkg]));
686
687 let proc_macro_deps: HashSet<_> =
688 proc_macro_deps.into_iter().map(|dep| metadata[dep].name.as_ref()).collect();
689 let expected = proc_macro_deps::CRATES.iter().copied().collect::<HashSet<_>>();
690
691 let needs_blessing = proc_macro_deps.difference(&expected).next().is_some()
692 || expected.difference(&proc_macro_deps).next().is_some();
693
694 if needs_blessing && bless {
695 let mut proc_macro_deps: Vec<_> = proc_macro_deps.into_iter().collect();
696 proc_macro_deps.sort();
697 let mut file = File::create(root.join("src/bootstrap/src/utils/proc_macro_deps.rs"))
698 .expect("`proc_macro_deps` should exist");
699 writeln!(
700 &mut file,
701 "/// Do not update manually - use `./x.py test tidy --bless`
702/// Holds all direct and indirect dependencies of proc-macro crates in tree.
703/// See <https://github.com/rust-lang/rust/issues/134863>
704pub static CRATES: &[&str] = &[
705 // tidy-alphabetical-start"
706 )
707 .unwrap();
708 for dep in proc_macro_deps {
709 writeln!(&mut file, " {dep:?},").unwrap();
710 }
711 writeln!(
712 &mut file,
713 " // tidy-alphabetical-end
714];"
715 )
716 .unwrap();
717 } else {
718 let mut error_found = false;
719
720 for missing in proc_macro_deps.difference(&expected) {
721 error_found = true;
722 check.error(format!(
723 "proc-macro crate dependency `{missing}` is not registered in `src/bootstrap/src/utils/proc_macro_deps.rs`",
724 ));
725 }
726 for extra in expected.difference(&proc_macro_deps) {
727 error_found = true;
728 check.error(format!(
729 "`{extra}` is registered in `src/bootstrap/src/utils/proc_macro_deps.rs`, but is not a proc-macro crate dependency",
730 ));
731 }
732 if error_found {
733 check.message("Run `./x.py test tidy --bless` to regenerate the list");
734 }
735 }
736}
737
738pub fn has_missing_submodule(root: &Path, submodules: &[&str]) -> bool {
742 !CiEnv::is_ci()
743 && submodules.iter().any(|submodule| {
744 let path = root.join(submodule);
745 !path.exists()
746 || read_dir(path).unwrap().next().is_none()
748 })
749}
750
751fn check_runtime_license_exceptions(metadata: &Metadata, check: &mut RunningCheck) {
756 for pkg in &metadata.packages {
757 if pkg.source.is_none() {
758 continue;
760 }
761 let license = match &pkg.license {
762 Some(license) => license,
763 None => {
764 check
765 .error(format!("dependency `{}` does not define a license expression", pkg.id));
766 continue;
767 }
768 };
769 if !LICENSES.contains(&license.as_str()) {
770 if *pkg.name == "fortanix-sgx-abi" && pkg.license.as_deref() == Some("MPL-2.0") {
775 continue;
776 }
777
778 check.error(format!("invalid license `{}` in `{}`", license, pkg.id));
779 }
780 }
781}
782
783fn check_license_exceptions(
787 metadata: &Metadata,
788 workspace: &str,
789 exceptions: &[(&str, &str)],
790 check: &mut RunningCheck,
791) {
792 for (name, license) in exceptions {
794 if !metadata.packages.iter().any(|p| *p.name == *name) {
796 check.error(format!(
797 "could not find exception package `{name}` in workspace `{workspace}`\n\
798 Remove from EXCEPTIONS list if it is no longer used.",
799 ));
800 }
801 for pkg in metadata.packages.iter().filter(|p| *p.name == *name) {
803 match &pkg.license {
804 None => {
805 check.error(format!(
806 "dependency exception `{}` in workspace `{workspace}` does not declare a license expression",
807 pkg.id
808 ));
809 }
810 Some(pkg_license) => {
811 if pkg_license.as_str() != *license {
812 check.error(format!(r#"dependency exception `{name}` license in workspace `{workspace}` has changed
813 previously `{license}` now `{pkg_license}`
814 update EXCEPTIONS for the new license
815"#));
816 }
817 }
818 }
819 }
820 if LICENSES.contains(license) || LICENSES_TOOLS.contains(license) {
821 check.error(format!(
822 "dependency exception `{name}` is not necessary. `{license}` is an allowed license"
823 ));
824 }
825 }
826
827 let exception_names: Vec<_> = exceptions.iter().map(|(name, _license)| *name).collect();
828
829 for pkg in &metadata.packages {
831 if pkg.source.is_none() {
832 continue;
834 }
835 if exception_names.contains(&pkg.name.as_str()) {
836 continue;
837 }
838 let license = match &pkg.license {
839 Some(license) => license,
840 None => {
841 check.error(format!(
842 "dependency `{}` in workspace `{workspace}` does not define a license expression",
843 pkg.id
844 ));
845 continue;
846 }
847 };
848 if !LICENSES.contains(&license.as_str()) && !LICENSES_TOOLS.contains(&license.as_str()) {
849 check.error(format!(
850 "invalid license `{}` for package `{}` in workspace `{workspace}`",
851 license, pkg.id
852 ));
853 }
854 }
855}
856
857fn check_runtime_no_duplicate_dependencies(metadata: &Metadata, check: &mut RunningCheck) {
858 let mut seen_pkgs = HashSet::new();
859 for pkg in &metadata.packages {
860 if pkg.source.is_none() {
861 continue;
862 }
863
864 if pkg.name.to_string() != "wasi" && !seen_pkgs.insert(&*pkg.name) {
868 check.error(format!(
869 "duplicate package `{}` is not allowed for the standard library",
870 pkg.name
871 ));
872 }
873 }
874}
875
876fn check_runtime_no_proc_macros(metadata: &Metadata, check: &mut RunningCheck) {
877 for pkg in &metadata.packages {
878 if pkg.targets.iter().any(|target| target.is_proc_macro()) {
879 check.error(format!(
880 "proc macro `{}` is not allowed as standard library dependency.\n\
881 Using proc macros in the standard library would break cross-compilation \
882 as proc-macros don't get shipped for the host tuple.",
883 pkg.name
884 ));
885 }
886 }
887}
888
889fn check_permitted_dependencies(
894 metadata: &Metadata,
895 descr: &str,
896 permitted_dependencies: &[&'static str],
897 restricted_dependency_crates: &[&'static str],
898 permitted_location: ListLocation,
899 check: &mut RunningCheck,
900) {
901 let mut has_permitted_dep_error = false;
902 let mut deps = HashSet::new();
903 for to_check in restricted_dependency_crates {
904 let to_check = pkg_from_name(metadata, to_check);
905 deps_of(metadata, &to_check.id, &mut deps);
906 }
907
908 for permitted in permitted_dependencies {
910 fn compare(pkg: &Package, permitted: &str) -> bool {
911 if let Some((name, version)) = permitted.split_once("@") {
912 let Ok(version) = Version::parse(version) else {
913 return false;
914 };
915 *pkg.name == name && pkg.version == version
916 } else {
917 *pkg.name == permitted
918 }
919 }
920 if !deps.iter().any(|dep_id| compare(pkg_from_id(metadata, dep_id), permitted)) {
921 check.error(format!(
922 "could not find allowed package `{permitted}`\n\
923 Remove from PERMITTED_DEPENDENCIES list if it is no longer used.",
924 ));
925 has_permitted_dep_error = true;
926 }
927 }
928
929 let permitted_dependencies: HashMap<_, _> = permitted_dependencies
931 .iter()
932 .map(|s| {
933 if let Some((name, version)) = s.split_once('@') {
934 (name, Version::parse(version).ok())
935 } else {
936 (*s, None)
937 }
938 })
939 .collect();
940
941 for dep in deps {
942 let dep = pkg_from_id(metadata, dep);
943 if dep.source.is_some() {
945 let is_eq = if let Some(version) = permitted_dependencies.get(dep.name.as_str()) {
946 if let Some(version) = version { version == &dep.version } else { true }
947 } else {
948 false
949 };
950 if !is_eq {
951 check.error(format!("Dependency for {descr} not explicitly permitted: {}", dep.id));
952 has_permitted_dep_error = true;
953 }
954 }
955 }
956
957 if has_permitted_dep_error {
958 eprintln!("Go to `{}:{}` for the list.", permitted_location.path, permitted_location.line);
959 }
960}
961
962fn pkg_from_name<'a>(metadata: &'a Metadata, name: &'static str) -> &'a Package {
964 let mut i = metadata.packages.iter().filter(|p| *p.name == name);
965 let result =
966 i.next().unwrap_or_else(|| panic!("could not find package `{name}` in package list"));
967 assert!(i.next().is_none(), "more than one package found for `{name}`");
968 result
969}
970
971fn pkg_from_id<'a>(metadata: &'a Metadata, id: &PackageId) -> &'a Package {
972 metadata.packages.iter().find(|p| &p.id == id).unwrap()
973}
974
975fn deps_of<'a>(metadata: &'a Metadata, pkg_id: &'a PackageId, result: &mut HashSet<&'a PackageId>) {
977 if !result.insert(pkg_id) {
978 return;
979 }
980 let node = metadata
981 .resolve
982 .as_ref()
983 .unwrap()
984 .nodes
985 .iter()
986 .find(|n| &n.id == pkg_id)
987 .unwrap_or_else(|| panic!("could not find `{pkg_id}` in resolve"));
988 for dep in &node.deps {
989 deps_of(metadata, &dep.pkg, result);
990 }
991}