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