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