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