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 "utf8_iter",
470 "utf8parse",
471 "valuable",
472 "version_check",
473 "wasi",
474 "wasm-encoder",
475 "wasmparser",
476 "windows",
477 "windows-collections",
478 "windows-core",
479 "windows-future",
480 "windows-implement",
481 "windows-interface",
482 "windows-link",
483 "windows-numerics",
484 "windows-result",
485 "windows-strings",
486 "windows-sys",
487 "windows-targets",
488 "windows-threading",
489 "windows_aarch64_gnullvm",
490 "windows_aarch64_msvc",
491 "windows_i686_gnu",
492 "windows_i686_gnullvm",
493 "windows_i686_msvc",
494 "windows_x86_64_gnu",
495 "windows_x86_64_gnullvm",
496 "windows_x86_64_msvc",
497 "wit-bindgen-rt@0.39.0", "writeable",
499 "yoke",
500 "yoke-derive",
501 "zerocopy",
502 "zerocopy-derive",
503 "zerofrom",
504 "zerofrom-derive",
505 "zerotrie",
506 "zerovec",
507 "zerovec-derive",
508 "zlib-rs",
509 ];
511
512const PERMITTED_STDLIB_DEPS_LOCATION: ListLocation = location!(+2);
513
514const PERMITTED_STDLIB_DEPENDENCIES: &[&str] = &[
515 "addr2line",
517 "adler2",
518 "cc",
519 "cfg-if",
520 "compiler_builtins",
521 "dlmalloc",
522 "foldhash", "fortanix-sgx-abi",
524 "getopts",
525 "gimli",
526 "hashbrown",
527 "hermit-abi",
528 "libc",
529 "memchr",
530 "miniz_oxide",
531 "moto-rt",
532 "object",
533 "r-efi",
534 "r-efi-alloc",
535 "rand",
536 "rand_core",
537 "rand_xorshift",
538 "rustc-demangle",
539 "rustc-literal-escaper",
540 "shlex",
541 "unwinding",
542 "vex-sdk",
543 "wasip1",
544 "wasip2",
545 "wasip3",
546 "windows-link",
547 "windows-sys@0.61.100", "wit-bindgen",
549 ];
551
552const PERMITTED_CRANELIFT_DEPS_LOCATION: ListLocation = location!(+2);
553
554const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[
555 "allocator-api2",
557 "anyhow",
558 "arbitrary",
559 "bitflags",
560 "bumpalo",
561 "cfg-if",
562 "cranelift-assembler-x64",
563 "cranelift-assembler-x64-meta",
564 "cranelift-bforest",
565 "cranelift-bitset",
566 "cranelift-codegen",
567 "cranelift-codegen-meta",
568 "cranelift-codegen-shared",
569 "cranelift-control",
570 "cranelift-entity",
571 "cranelift-frontend",
572 "cranelift-isle",
573 "cranelift-jit",
574 "cranelift-module",
575 "cranelift-native",
576 "cranelift-object",
577 "cranelift-srcgen",
578 "crc32fast",
579 "equivalent",
580 "fnv",
581 "foldhash",
582 "gimli",
583 "hashbrown",
584 "heck",
585 "indexmap",
586 "libc",
587 "libloading",
588 "libm",
589 "log",
590 "mach2",
591 "memchr",
592 "object",
593 "proc-macro2",
594 "quote",
595 "regalloc2",
596 "region",
597 "rustc-hash",
598 "serde",
599 "serde_core",
600 "serde_derive",
601 "smallvec",
602 "stable_deref_trait",
603 "syn",
604 "target-lexicon",
605 "unicode-ident",
606 "wasmtime-internal-core",
607 "wasmtime-internal-jit-icache-coherence",
608 "windows-link",
609 "windows-sys",
610 "windows-targets",
611 "windows_aarch64_gnullvm",
612 "windows_aarch64_msvc",
613 "windows_i686_gnu",
614 "windows_i686_gnullvm",
615 "windows_i686_msvc",
616 "windows_x86_64_gnu",
617 "windows_x86_64_gnullvm",
618 "windows_x86_64_msvc",
619 ];
621
622pub fn check(root: &Path, cargo: &Path, tidy_ctx: TidyCtx) {
627 let mut check = tidy_ctx.start_check("deps");
628 let bless = tidy_ctx.is_bless_enabled();
629
630 let mut checked_runtime_licenses = false;
631
632 check_proc_macro_dep_list(root, cargo, bless, &mut check);
633
634 for &WorkspaceInfo { path, exceptions, crates_and_deps, submodules } in WORKSPACES {
635 if has_missing_submodule(root, submodules, tidy_ctx.is_running_on_ci()) {
636 continue;
637 }
638
639 if !root.join(path).join("Cargo.lock").exists() {
640 check.error(format!("the `{path}` workspace doesn't have a Cargo.lock"));
641 continue;
642 }
643
644 let mut cmd = cargo_metadata::MetadataCommand::new();
645 cmd.cargo_path(cargo)
646 .manifest_path(root.join(path).join("Cargo.toml"))
647 .features(cargo_metadata::CargoOpt::AllFeatures)
648 .other_options(vec!["--locked".to_owned()]);
649 let metadata = t!(cmd.exec());
650
651 let absolute_root =
653 if path == "." { root.to_path_buf() } else { t!(std::path::absolute(root.join(path))) };
654 let absolute_root_real = t!(std::path::absolute(&metadata.workspace_root));
655 if absolute_root_real != absolute_root {
656 check.error(format!("{path} is part of another workspace ({} != {}), remove from `WORKSPACES` ({WORKSPACE_LOCATION})", absolute_root.display(), absolute_root_real.display()));
657 }
658 check_license_exceptions(&metadata, path, exceptions, &mut check);
659 if let Some((crates, permitted_deps, location)) = crates_and_deps {
660 let descr = crates.get(0).unwrap_or(&path);
661 check_permitted_dependencies(
662 &metadata,
663 descr,
664 permitted_deps,
665 crates,
666 location,
667 &mut check,
668 );
669 }
670
671 if path == "library" {
672 check_runtime_license_exceptions(&metadata, &mut check);
673 check_runtime_no_duplicate_dependencies(&metadata, &mut check);
674 check_runtime_no_proc_macros(&metadata, &mut check);
675 checked_runtime_licenses = true;
676 }
677 }
678
679 assert!(checked_runtime_licenses);
682}
683
684fn check_proc_macro_dep_list(root: &Path, cargo: &Path, bless: bool, check: &mut RunningCheck) {
686 let mut cmd = cargo_metadata::MetadataCommand::new();
687 cmd.cargo_path(cargo)
688 .manifest_path(root.join("Cargo.toml"))
689 .features(cargo_metadata::CargoOpt::AllFeatures)
690 .other_options(vec!["--locked".to_owned()]);
691 let metadata = t!(cmd.exec());
692 let is_proc_macro_pkg = |pkg: &Package| pkg.targets.iter().any(|target| target.is_proc_macro());
693
694 let mut proc_macro_deps = HashSet::new();
695 for pkg in metadata.packages.iter().filter(|pkg| is_proc_macro_pkg(pkg)) {
696 deps_of(&metadata, &pkg.id, &mut proc_macro_deps);
697 }
698 proc_macro_deps.retain(|pkg| !is_proc_macro_pkg(&metadata[pkg]));
700
701 let proc_macro_deps: HashSet<_> =
702 proc_macro_deps.into_iter().map(|dep| metadata[dep].name.as_ref()).collect();
703 let expected = proc_macro_deps::CRATES.iter().copied().collect::<HashSet<_>>();
704
705 let needs_blessing = proc_macro_deps.difference(&expected).next().is_some()
706 || expected.difference(&proc_macro_deps).next().is_some();
707
708 if needs_blessing && bless {
709 let mut proc_macro_deps: Vec<_> = proc_macro_deps.into_iter().collect();
710 proc_macro_deps.sort();
711 let mut file = File::create(root.join("src/bootstrap/src/utils/proc_macro_deps.rs"))
712 .expect("`proc_macro_deps` should exist");
713 writeln!(
714 &mut file,
715 "/// Do not update manually - use `./x.py test tidy --bless`
716/// Holds all direct and indirect dependencies of proc-macro crates in tree.
717/// See <https://github.com/rust-lang/rust/issues/134863>
718pub static CRATES: &[&str] = &[
719 // tidy-alphabetical-start"
720 )
721 .unwrap();
722 for dep in proc_macro_deps {
723 writeln!(&mut file, " {dep:?},").unwrap();
724 }
725 writeln!(
726 &mut file,
727 " // tidy-alphabetical-end
728];"
729 )
730 .unwrap();
731 } else {
732 let mut error_found = false;
733
734 for missing in proc_macro_deps.difference(&expected) {
735 error_found = true;
736 check.error(format!(
737 "proc-macro crate dependency `{missing}` is not registered in `src/bootstrap/src/utils/proc_macro_deps.rs`",
738 ));
739 }
740 for extra in expected.difference(&proc_macro_deps) {
741 error_found = true;
742 check.error(format!(
743 "`{extra}` is registered in `src/bootstrap/src/utils/proc_macro_deps.rs`, but is not a proc-macro crate dependency",
744 ));
745 }
746 if error_found {
747 check.message("Run `./x.py test tidy --bless` to regenerate the list");
748 }
749 }
750}
751
752pub fn has_missing_submodule(root: &Path, submodules: &[&str], is_ci: bool) -> bool {
756 !is_ci
757 && submodules.iter().any(|submodule| {
758 let path = root.join(submodule);
759 !path.exists()
760 || read_dir(path).unwrap().next().is_none()
762 })
763}
764
765fn check_runtime_license_exceptions(metadata: &Metadata, check: &mut RunningCheck) {
770 for pkg in &metadata.packages {
771 if pkg.source.is_none() {
772 continue;
774 }
775 let license = match &pkg.license {
776 Some(license) => license,
777 None => {
778 check
779 .error(format!("dependency `{}` does not define a license expression", pkg.id));
780 continue;
781 }
782 };
783 if !LICENSES.contains(&license.as_str()) {
784 if *pkg.name == "fortanix-sgx-abi" && pkg.license.as_deref() == Some("MPL-2.0") {
789 continue;
790 }
791
792 check.error(format!("invalid license `{}` in `{}`", license, pkg.id));
793 }
794 }
795}
796
797fn check_license_exceptions(
801 metadata: &Metadata,
802 workspace: &str,
803 exceptions: &[(&str, &str)],
804 check: &mut RunningCheck,
805) {
806 for (name, license) in exceptions {
808 if !metadata.packages.iter().any(|p| *p.name == *name) {
810 check.error(format!(
811 "could not find exception package `{name}` in workspace `{workspace}`\n\
812 Remove from EXCEPTIONS list if it is no longer used.",
813 ));
814 }
815 for pkg in metadata.packages.iter().filter(|p| *p.name == *name) {
817 match &pkg.license {
818 None => {
819 check.error(format!(
820 "dependency exception `{}` in workspace `{workspace}` does not declare a license expression",
821 pkg.id
822 ));
823 }
824 Some(pkg_license) => {
825 if pkg_license.as_str() != *license {
826 check.error(format!(r#"dependency exception `{name}` license in workspace `{workspace}` has changed
827 previously `{license}` now `{pkg_license}`
828 update EXCEPTIONS for the new license
829"#));
830 }
831 }
832 }
833 }
834 if LICENSES.contains(license) || LICENSES_TOOLS.contains(license) {
835 check.error(format!(
836 "dependency exception `{name}` is not necessary. `{license}` is an allowed license"
837 ));
838 }
839 }
840
841 let exception_names: Vec<_> = exceptions.iter().map(|(name, _license)| *name).collect();
842
843 for pkg in &metadata.packages {
845 if pkg.source.is_none() {
846 continue;
848 }
849 if exception_names.contains(&pkg.name.as_str()) {
850 continue;
851 }
852 let license = match &pkg.license {
853 Some(license) => license,
854 None => {
855 check.error(format!(
856 "dependency `{}` in workspace `{workspace}` does not define a license expression",
857 pkg.id
858 ));
859 continue;
860 }
861 };
862 if !LICENSES.contains(&license.as_str()) && !LICENSES_TOOLS.contains(&license.as_str()) {
863 check.error(format!(
864 "invalid license `{}` for package `{}` in workspace `{workspace}`",
865 license, pkg.id
866 ));
867 }
868 }
869}
870
871fn check_runtime_no_duplicate_dependencies(metadata: &Metadata, check: &mut RunningCheck) {
872 let mut seen_pkgs = HashSet::new();
873 for pkg in &metadata.packages {
874 if pkg.source.is_none() {
875 continue;
876 }
877
878 if !seen_pkgs.insert(&*pkg.name) {
879 check.error(format!(
880 "duplicate package `{}` is not allowed for the standard library",
881 pkg.name
882 ));
883 }
884 }
885}
886
887fn check_runtime_no_proc_macros(metadata: &Metadata, check: &mut RunningCheck) {
888 for pkg in &metadata.packages {
889 if pkg.targets.iter().any(|target| target.is_proc_macro()) {
890 check.error(format!(
891 "proc macro `{}` is not allowed as standard library dependency.\n\
892 Using proc macros in the standard library would break cross-compilation \
893 as proc-macros don't get shipped for the host tuple.",
894 pkg.name
895 ));
896 }
897 }
898}
899
900fn check_permitted_dependencies(
905 metadata: &Metadata,
906 descr: &str,
907 permitted_dependencies: &[&'static str],
908 restricted_dependency_crates: &[&'static str],
909 permitted_location: ListLocation,
910 check: &mut RunningCheck,
911) {
912 let mut has_permitted_dep_error = false;
913 let mut deps = HashSet::new();
914 for to_check in restricted_dependency_crates {
915 let to_check = pkg_from_name(metadata, to_check);
916 deps_of(metadata, &to_check.id, &mut deps);
917 }
918
919 for permitted in permitted_dependencies {
921 fn compare(pkg: &Package, permitted: &str) -> bool {
922 if let Some((name, version)) = permitted.split_once("@") {
923 let Ok(version) = Version::parse(version) else {
924 return false;
925 };
926 *pkg.name == name && pkg.version == version
927 } else {
928 *pkg.name == permitted
929 }
930 }
931 if !deps.iter().any(|dep_id| compare(pkg_from_id(metadata, dep_id), permitted)) {
932 check.error(format!(
933 "could not find allowed package `{permitted}`\n\
934 Remove from PERMITTED_DEPENDENCIES list if it is no longer used.",
935 ));
936 has_permitted_dep_error = true;
937 }
938 }
939
940 let permitted_dependencies: HashMap<_, _> = permitted_dependencies
942 .iter()
943 .map(|s| {
944 if let Some((name, version)) = s.split_once('@') {
945 (name, Version::parse(version).ok())
946 } else {
947 (*s, None)
948 }
949 })
950 .collect();
951
952 for dep in deps {
953 let dep = pkg_from_id(metadata, dep);
954 if dep.source.is_some() {
956 let is_eq = if let Some(version) = permitted_dependencies.get(dep.name.as_str()) {
957 if let Some(version) = version { version == &dep.version } else { true }
958 } else {
959 false
960 };
961 if !is_eq {
962 check.error(format!("Dependency for {descr} not explicitly permitted: {}", dep.id));
963 has_permitted_dep_error = true;
964 }
965 }
966 }
967
968 if has_permitted_dep_error {
969 eprintln!("Go to `{}:{}` for the list.", permitted_location.path, permitted_location.line);
970 }
971}
972
973fn pkg_from_name<'a>(metadata: &'a Metadata, name: &'static str) -> &'a Package {
975 let mut i = metadata.packages.iter().filter(|p| *p.name == name);
976 let result =
977 i.next().unwrap_or_else(|| panic!("could not find package `{name}` in package list"));
978 assert!(i.next().is_none(), "more than one package found for `{name}`");
979 result
980}
981
982fn pkg_from_id<'a>(metadata: &'a Metadata, id: &PackageId) -> &'a Package {
983 metadata.packages.iter().find(|p| &p.id == id).unwrap()
984}
985
986fn deps_of<'a>(metadata: &'a Metadata, pkg_id: &'a PackageId, result: &mut HashSet<&'a PackageId>) {
988 if !result.insert(pkg_id) {
989 return;
990 }
991 let node = metadata
992 .resolve
993 .as_ref()
994 .unwrap()
995 .nodes
996 .iter()
997 .find(|n| &n.id == pkg_id)
998 .unwrap_or_else(|| panic!("could not find `{pkg_id}` in resolve"));
999 for dep in &node.deps {
1000 deps_of(metadata, &dep.pkg, result);
1001 }
1002}