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 ("terminfo", "WTFPL"),
248 ("wasite", "Apache-2.0 OR BSL-1.0 OR MIT"),
249 ("wezterm-bidi", "MIT AND Unicode-DFS-2016"),
250 ("whoami", "Apache-2.0 OR BSL-1.0 OR MIT"),
251 ];
253
254const EXCEPTIONS_RUSTBOOK: ExceptionList = &[
255 ("font-awesome-as-a-crate", "CC-BY-4.0 AND MIT"),
257 ("mdbook-core", "MPL-2.0"),
258 ("mdbook-driver", "MPL-2.0"),
259 ("mdbook-html", "MPL-2.0"),
260 ("mdbook-markdown", "MPL-2.0"),
261 ("mdbook-preprocessor", "MPL-2.0"),
262 ("mdbook-renderer", "MPL-2.0"),
263 ("mdbook-summary", "MPL-2.0"),
264 ];
266
267const EXCEPTIONS_STDARCH: ExceptionList = &[];
268
269const EXCEPTIONS_CRANELIFT: ExceptionList = &[];
270
271const EXCEPTIONS_GCC: ExceptionList = &[
272 ("gccjit", "GPL-3.0"),
274 ("gccjit_sys", "GPL-3.0"),
275 ];
277
278const EXCEPTIONS_BOOTSTRAP: ExceptionList = &[];
279
280const EXCEPTIONS_UEFI_QEMU_TEST: ExceptionList = &[];
281
282const PERMITTED_RUSTC_DEPS_LOCATION: ListLocation = location!(+6);
283
284const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
289 "adler2",
291 "aho-corasick",
292 "allocator-api2", "annotate-snippets",
294 "anstream",
295 "anstyle",
296 "anstyle-parse",
297 "anstyle-query",
298 "anstyle-wincon",
299 "ar_archive_writer",
300 "arrayref",
301 "arrayvec",
302 "bitflags",
303 "blake3",
304 "block-buffer",
305 "block2",
306 "bstr",
307 "cc",
308 "cfg-if",
309 "cfg_aliases",
310 "colorchoice",
311 "constant_time_eq",
312 "cpufeatures",
313 "crc32fast",
314 "crossbeam-deque",
315 "crossbeam-epoch",
316 "crossbeam-utils",
317 "crypto-common",
318 "ctrlc",
319 "darling",
320 "darling_core",
321 "darling_macro",
322 "datafrog",
323 "derive-where",
324 "derive_setters",
325 "digest",
326 "dispatch2",
327 "displaydoc",
328 "dissimilar",
329 "dyn-clone",
330 "either",
331 "elsa",
332 "ena",
333 "equivalent",
334 "errno",
335 "expect-test",
336 "fallible-iterator", "fastrand",
338 "find-msvc-tools",
339 "flate2",
340 "fluent-bundle",
341 "fluent-langneg",
342 "fluent-syntax",
343 "fnv",
344 "foldhash",
345 "generic-array",
346 "getopts",
347 "getrandom",
348 "gimli",
349 "gsgdt",
350 "hashbrown",
351 "icu_collections",
352 "icu_list",
353 "icu_locale",
354 "icu_locale_core",
355 "icu_locale_data",
356 "icu_provider",
357 "ident_case",
358 "indexmap",
359 "intl-memoizer",
360 "intl_pluralrules",
361 "is_terminal_polyfill",
362 "itertools",
363 "itoa",
364 "jiff",
365 "jiff-static",
366 "jiff-tzdb",
367 "jiff-tzdb-platform",
368 "jobserver",
369 "lazy_static",
370 "leb128",
371 "libc",
372 "libloading",
373 "linux-raw-sys",
374 "litemap",
375 "lock_api",
376 "log",
377 "matchers",
378 "md-5",
379 "measureme",
380 "memchr",
381 "memmap2",
382 "miniz_oxide",
383 "nix",
384 "nu-ansi-term",
385 "objc2",
386 "objc2-encode",
387 "object",
388 "odht",
389 "once_cell",
390 "once_cell_polyfill",
391 "parking_lot",
392 "parking_lot_core",
393 "pathdiff",
394 "perf-event-open-sys",
395 "pin-project-lite",
396 "polonius-engine",
397 "portable-atomic", "portable-atomic-util",
399 "potential_utf",
400 "ppv-lite86",
401 "proc-macro-hack",
402 "proc-macro2",
403 "psm",
404 "pulldown-cmark",
405 "pulldown-cmark-escape",
406 "punycode",
407 "quote",
408 "r-efi",
409 "rand",
410 "rand_chacha",
411 "rand_core",
412 "rand_xorshift", "rand_xoshiro",
414 "redox_syscall",
415 "ref-cast",
416 "ref-cast-impl",
417 "regex",
418 "regex-automata",
419 "regex-syntax",
420 "rustc-demangle",
421 "rustc-hash",
422 "rustc-literal-escaper",
423 "rustc-stable-hash",
424 "rustc_apfloat",
425 "rustix",
426 "ruzstd", "ryu",
428 "schemars",
429 "schemars_derive",
430 "scoped-tls",
431 "scopeguard",
432 "self_cell",
433 "serde",
434 "serde_core",
435 "serde_derive",
436 "serde_derive_internals",
437 "serde_json",
438 "serde_path_to_error",
439 "sha1",
440 "sha2",
441 "sharded-slab",
442 "shlex",
443 "simd-adler32",
444 "smallvec",
445 "stable_deref_trait",
446 "stacker",
447 "static_assertions",
448 "strsim",
449 "syn",
450 "synstructure",
451 "tempfile",
452 "termize",
453 "thin-vec",
454 "thiserror",
455 "thiserror-impl",
456 "thorin-dwp",
457 "thread_local",
458 "tikv-jemalloc-sys",
459 "tinystr",
460 "tinyvec",
461 "tinyvec_macros",
462 "tracing",
463 "tracing-attributes",
464 "tracing-core",
465 "tracing-log",
466 "tracing-serde",
467 "tracing-subscriber",
468 "tracing-tree",
469 "twox-hash",
470 "type-map",
471 "typenum",
472 "unic-langid",
473 "unic-langid-impl",
474 "unic-langid-macros",
475 "unic-langid-macros-impl",
476 "unicase",
477 "unicode-ident",
478 "unicode-normalization",
479 "unicode-properties",
480 "unicode-script",
481 "unicode-security",
482 "unicode-width",
483 "utf8_iter",
484 "utf8parse",
485 "valuable",
486 "version_check",
487 "wasi",
488 "wasm-encoder",
489 "wasmparser",
490 "windows",
491 "windows-collections",
492 "windows-core",
493 "windows-future",
494 "windows-implement",
495 "windows-interface",
496 "windows-link",
497 "windows-numerics",
498 "windows-result",
499 "windows-strings",
500 "windows-sys",
501 "windows-targets",
502 "windows-threading",
503 "windows_aarch64_gnullvm",
504 "windows_aarch64_msvc",
505 "windows_i686_gnu",
506 "windows_i686_gnullvm",
507 "windows_i686_msvc",
508 "windows_x86_64_gnu",
509 "windows_x86_64_gnullvm",
510 "windows_x86_64_msvc",
511 "wit-bindgen-rt@0.39.0", "writeable",
513 "yoke",
514 "yoke-derive",
515 "zerocopy",
516 "zerocopy-derive",
517 "zerofrom",
518 "zerofrom-derive",
519 "zerotrie",
520 "zerovec",
521 "zerovec-derive",
522 "zlib-rs",
523 ];
525
526const PERMITTED_STDLIB_DEPS_LOCATION: ListLocation = location!(+2);
527
528const PERMITTED_STDLIB_DEPENDENCIES: &[&str] = &[
529 "addr2line",
531 "adler2",
532 "cc",
533 "cfg-if",
534 "compiler_builtins",
535 "dlmalloc",
536 "foldhash", "fortanix-sgx-abi",
538 "getopts",
539 "gimli",
540 "hashbrown",
541 "hermit-abi",
542 "libc",
543 "memchr",
544 "miniz_oxide",
545 "moto-rt",
546 "object",
547 "r-efi",
548 "r-efi-alloc",
549 "rand",
550 "rand_core",
551 "rand_xorshift",
552 "rustc-demangle",
553 "rustc-literal-escaper",
554 "shlex",
555 "unwinding",
556 "vex-sdk",
557 "wasip1",
558 "wasip2",
559 "wasip3",
560 "windows-link",
561 "windows-sys@0.61.100", "wit-bindgen",
563 ];
565
566const PERMITTED_CRANELIFT_DEPS_LOCATION: ListLocation = location!(+2);
567
568const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[
569 "allocator-api2",
571 "anyhow",
572 "arbitrary",
573 "bitflags",
574 "bumpalo",
575 "cfg-if",
576 "cranelift-assembler-x64",
577 "cranelift-assembler-x64-meta",
578 "cranelift-bforest",
579 "cranelift-bitset",
580 "cranelift-codegen",
581 "cranelift-codegen-meta",
582 "cranelift-codegen-shared",
583 "cranelift-control",
584 "cranelift-entity",
585 "cranelift-frontend",
586 "cranelift-isle",
587 "cranelift-jit",
588 "cranelift-module",
589 "cranelift-native",
590 "cranelift-object",
591 "cranelift-srcgen",
592 "crc32fast",
593 "equivalent",
594 "fnv",
595 "foldhash",
596 "gimli",
597 "hashbrown",
598 "heck",
599 "indexmap",
600 "libc",
601 "libloading",
602 "libm",
603 "log",
604 "mach2",
605 "memchr",
606 "memmap2",
607 "object",
608 "proc-macro2",
609 "quote",
610 "regalloc2",
611 "region",
612 "rustc-hash",
613 "serde",
614 "serde_core",
615 "serde_derive",
616 "smallvec",
617 "stable_deref_trait",
618 "syn",
619 "target-lexicon",
620 "unicode-ident",
621 "wasmtime-internal-core",
622 "wasmtime-internal-jit-icache-coherence",
623 "windows-link",
624 "windows-sys",
625 "windows-targets",
626 "windows_aarch64_gnullvm",
627 "windows_aarch64_msvc",
628 "windows_i686_gnu",
629 "windows_i686_gnullvm",
630 "windows_i686_msvc",
631 "windows_x86_64_gnu",
632 "windows_x86_64_gnullvm",
633 "windows_x86_64_msvc",
634 ];
636
637pub fn check(root: &Path, cargo: &Path, tidy_ctx: TidyCtx) {
642 let mut check = tidy_ctx.start_check("deps");
643 let bless = tidy_ctx.is_bless_enabled();
644
645 let mut checked_runtime_licenses = false;
646
647 check_proc_macro_dep_list(root, cargo, bless, &mut check);
648
649 for &WorkspaceInfo { path, exceptions, crates_and_deps, submodules } in WORKSPACES {
650 if has_missing_submodule(root, submodules, tidy_ctx.is_running_on_ci()) {
651 continue;
652 }
653
654 if !root.join(path).join("Cargo.lock").exists() {
655 check.error(format!("the `{path}` workspace doesn't have a Cargo.lock"));
656 continue;
657 }
658
659 let mut cmd = cargo_metadata::MetadataCommand::new();
660 cmd.cargo_path(cargo)
661 .manifest_path(root.join(path).join("Cargo.toml"))
662 .features(cargo_metadata::CargoOpt::AllFeatures)
663 .other_options(vec!["--locked".to_owned()]);
664 let metadata = t!(cmd.exec());
665
666 let absolute_root =
668 if path == "." { root.to_path_buf() } else { t!(std::path::absolute(root.join(path))) };
669 let absolute_root_real = t!(std::path::absolute(&metadata.workspace_root));
670 if absolute_root_real != absolute_root {
671 check.error(format!("{path} is part of another workspace ({} != {}), remove from `WORKSPACES` ({WORKSPACE_LOCATION})", absolute_root.display(), absolute_root_real.display()));
672 }
673 check_license_exceptions(&metadata, path, exceptions, &mut check);
674 if let Some((crates, permitted_deps, location)) = crates_and_deps {
675 let descr = crates.get(0).unwrap_or(&path);
676 check_permitted_dependencies(
677 &metadata,
678 descr,
679 permitted_deps,
680 crates,
681 location,
682 &mut check,
683 );
684 }
685
686 if path == "library" {
687 check_runtime_license_exceptions(&metadata, &mut check);
688 check_runtime_no_duplicate_dependencies(&metadata, &mut check);
689 check_runtime_no_proc_macros(&metadata, &mut check);
690 checked_runtime_licenses = true;
691 }
692 }
693
694 assert!(checked_runtime_licenses);
697}
698
699fn check_proc_macro_dep_list(root: &Path, cargo: &Path, bless: bool, check: &mut RunningCheck) {
701 if std::env::var("RUSTC").is_err() {
702 panic!("tidy must be run under bootstrap (./x test tidy), not as a standalone command");
703 }
704 let mut cmd = cargo_metadata::MetadataCommand::new();
705 cmd.cargo_path(cargo)
706 .manifest_path(root.join("Cargo.toml"))
707 .features(cargo_metadata::CargoOpt::AllFeatures)
708 .other_options(vec!["--locked".to_owned()]);
709 let metadata = t!(cmd.exec());
710 let is_proc_macro_pkg = |pkg: &Package| pkg.targets.iter().any(|target| target.is_proc_macro());
711
712 let mut proc_macro_deps = HashSet::new();
713 for pkg in metadata.packages.iter().filter(|pkg| is_proc_macro_pkg(pkg)) {
714 deps_of(&metadata, &pkg.id, &mut proc_macro_deps);
715 }
716 proc_macro_deps.retain(|pkg| !is_proc_macro_pkg(&metadata[pkg]));
718
719 let proc_macro_deps: HashSet<_> =
720 proc_macro_deps.into_iter().map(|dep| metadata[dep].name.as_ref()).collect();
721 let expected = proc_macro_deps::CRATES.iter().copied().collect::<HashSet<_>>();
722
723 let needs_blessing = proc_macro_deps.difference(&expected).next().is_some()
724 || expected.difference(&proc_macro_deps).next().is_some();
725
726 if needs_blessing && bless {
727 let mut proc_macro_deps: Vec<_> = proc_macro_deps.into_iter().collect();
728 proc_macro_deps.sort();
729 let mut file = File::create(root.join("src/bootstrap/src/utils/proc_macro_deps.rs"))
730 .expect("`proc_macro_deps` should exist");
731 writeln!(
732 &mut file,
733 "/// Do not update manually - use `./x.py test tidy --bless`
734/// Holds all direct and indirect dependencies of proc-macro crates in tree.
735/// See <https://github.com/rust-lang/rust/issues/134863>
736pub static CRATES: &[&str] = &[
737 // tidy-alphabetical-start"
738 )
739 .unwrap();
740 for dep in proc_macro_deps {
741 writeln!(&mut file, " {dep:?},").unwrap();
742 }
743 writeln!(
744 &mut file,
745 " // tidy-alphabetical-end
746];"
747 )
748 .unwrap();
749 } else {
750 let mut error_found = false;
751
752 for missing in proc_macro_deps.difference(&expected) {
753 error_found = true;
754 check.error(format!(
755 "proc-macro crate dependency `{missing}` is not registered in `src/bootstrap/src/utils/proc_macro_deps.rs`",
756 ));
757 }
758 for extra in expected.difference(&proc_macro_deps) {
759 error_found = true;
760 check.error(format!(
761 "`{extra}` is registered in `src/bootstrap/src/utils/proc_macro_deps.rs`, but is not a proc-macro crate dependency",
762 ));
763 }
764 if error_found {
765 check.message("Run `./x.py test tidy --bless` to regenerate the list");
766 }
767 }
768}
769
770pub fn has_missing_submodule(root: &Path, submodules: &[&str], is_ci: bool) -> bool {
774 !is_ci
775 && submodules.iter().any(|submodule| {
776 let path = root.join(submodule);
777 !path.exists()
778 || read_dir(path).unwrap().next().is_none()
780 })
781}
782
783fn check_runtime_license_exceptions(metadata: &Metadata, check: &mut RunningCheck) {
788 for pkg in &metadata.packages {
789 if pkg.source.is_none() {
790 continue;
792 }
793 let license = match &pkg.license {
794 Some(license) => license,
795 None => {
796 check
797 .error(format!("dependency `{}` does not define a license expression", pkg.id));
798 continue;
799 }
800 };
801 if !LICENSES.contains(&license.as_str()) {
802 if *pkg.name == "fortanix-sgx-abi" && pkg.license.as_deref() == Some("MPL-2.0") {
807 continue;
808 }
809
810 check.error(format!("invalid license `{}` in `{}`", license, pkg.id));
811 }
812 }
813}
814
815fn check_license_exceptions(
819 metadata: &Metadata,
820 workspace: &str,
821 exceptions: &[(&str, &str)],
822 check: &mut RunningCheck,
823) {
824 for (name, license) in exceptions {
826 if !metadata.packages.iter().any(|p| *p.name == *name) {
828 check.error(format!(
829 "could not find exception package `{name}` in workspace `{workspace}`\n\
830 Remove from EXCEPTIONS list if it is no longer used.",
831 ));
832 }
833 for pkg in metadata.packages.iter().filter(|p| *p.name == *name) {
835 match &pkg.license {
836 None => {
837 check.error(format!(
838 "dependency exception `{}` in workspace `{workspace}` does not declare a license expression",
839 pkg.id
840 ));
841 }
842 Some(pkg_license) => {
843 if pkg_license.as_str() != *license {
844 check.error(format!(r#"dependency exception `{name}` license in workspace `{workspace}` has changed
845 previously `{license}` now `{pkg_license}`
846 update EXCEPTIONS for the new license
847"#));
848 }
849 }
850 }
851 }
852 if LICENSES.contains(license) || LICENSES_TOOLS.contains(license) {
853 check.error(format!(
854 "dependency exception `{name}` is not necessary. `{license}` is an allowed license"
855 ));
856 }
857 }
858
859 let exception_names: Vec<_> = exceptions.iter().map(|(name, _license)| *name).collect();
860
861 for pkg in &metadata.packages {
863 if pkg.source.is_none() {
864 continue;
866 }
867 if exception_names.contains(&pkg.name.as_str()) {
868 continue;
869 }
870 let license = match &pkg.license {
871 Some(license) => license,
872 None => {
873 check.error(format!(
874 "dependency `{}` in workspace `{workspace}` does not define a license expression",
875 pkg.id
876 ));
877 continue;
878 }
879 };
880 if !LICENSES.contains(&license.as_str()) && !LICENSES_TOOLS.contains(&license.as_str()) {
881 check.error(format!(
882 "invalid license `{}` for package `{}` in workspace `{workspace}`",
883 license, pkg.id
884 ));
885 }
886 }
887}
888
889fn check_runtime_no_duplicate_dependencies(metadata: &Metadata, check: &mut RunningCheck) {
890 let mut seen_pkgs = HashSet::new();
891 for pkg in &metadata.packages {
892 if pkg.source.is_none() {
893 continue;
894 }
895
896 if !seen_pkgs.insert(&*pkg.name) {
897 check.error(format!(
898 "duplicate package `{}` is not allowed for the standard library",
899 pkg.name
900 ));
901 }
902 }
903}
904
905fn check_runtime_no_proc_macros(metadata: &Metadata, check: &mut RunningCheck) {
906 for pkg in &metadata.packages {
907 if pkg.targets.iter().any(|target| target.is_proc_macro()) {
908 check.error(format!(
909 "proc macro `{}` is not allowed as standard library dependency.\n\
910 Using proc macros in the standard library would break cross-compilation \
911 as proc-macros don't get shipped for the host tuple.",
912 pkg.name
913 ));
914 }
915 }
916}
917
918fn check_permitted_dependencies(
923 metadata: &Metadata,
924 descr: &str,
925 permitted_dependencies: &[&'static str],
926 restricted_dependency_crates: &[&'static str],
927 permitted_location: ListLocation,
928 check: &mut RunningCheck,
929) {
930 let mut has_permitted_dep_error = false;
931 let mut deps = HashSet::new();
932 for to_check in restricted_dependency_crates {
933 let to_check = pkg_from_name(metadata, to_check);
934 deps_of(metadata, &to_check.id, &mut deps);
935 }
936
937 for permitted in permitted_dependencies {
939 fn compare(pkg: &Package, permitted: &str) -> bool {
940 if let Some((name, version)) = permitted.split_once("@") {
941 let Ok(version) = Version::parse(version) else {
942 return false;
943 };
944 *pkg.name == name && pkg.version == version
945 } else {
946 *pkg.name == permitted
947 }
948 }
949 if !deps.iter().any(|dep_id| compare(pkg_from_id(metadata, dep_id), permitted)) {
950 check.error(format!(
951 "could not find allowed package `{permitted}`\n\
952 Remove from PERMITTED_DEPENDENCIES list if it is no longer used.",
953 ));
954 has_permitted_dep_error = true;
955 }
956 }
957
958 let permitted_dependencies: HashMap<_, _> = permitted_dependencies
960 .iter()
961 .map(|s| {
962 if let Some((name, version)) = s.split_once('@') {
963 (name, Version::parse(version).ok())
964 } else {
965 (*s, None)
966 }
967 })
968 .collect();
969
970 for dep in deps {
971 let dep = pkg_from_id(metadata, dep);
972 if dep.source.is_some() {
974 let is_eq = if let Some(version) = permitted_dependencies.get(dep.name.as_str()) {
975 if let Some(version) = version { version == &dep.version } else { true }
976 } else {
977 false
978 };
979 if !is_eq {
980 check.error(format!("Dependency for {descr} not explicitly permitted: {}", dep.id));
981 has_permitted_dep_error = true;
982 }
983 }
984 }
985
986 if has_permitted_dep_error {
987 eprintln!("Go to `{}:{}` for the list.", permitted_location.path, permitted_location.line);
988 }
989}
990
991fn pkg_from_name<'a>(metadata: &'a Metadata, name: &'static str) -> &'a Package {
993 let mut i = metadata.packages.iter().filter(|p| *p.name == name);
994 let result =
995 i.next().unwrap_or_else(|| panic!("could not find package `{name}` in package list"));
996 assert!(i.next().is_none(), "more than one package found for `{name}`");
997 result
998}
999
1000fn pkg_from_id<'a>(metadata: &'a Metadata, id: &PackageId) -> &'a Package {
1001 metadata.packages.iter().find(|p| &p.id == id).unwrap()
1002}
1003
1004fn deps_of<'a>(metadata: &'a Metadata, pkg_id: &'a PackageId, result: &mut HashSet<&'a PackageId>) {
1006 if !result.insert(pkg_id) {
1007 return;
1008 }
1009 let node = metadata
1010 .resolve
1011 .as_ref()
1012 .unwrap()
1013 .nodes
1014 .iter()
1015 .find(|n| &n.id == pkg_id)
1016 .unwrap_or_else(|| panic!("could not find `{pkg_id}` in resolve"));
1017 for dep in &node.deps {
1018 deps_of(metadata, &dep.pkg, result);
1019 }
1020}