1use std::collections::{HashMap, HashSet};
4use std::fs::{File, read_dir};
5use std::io::Write;
6use std::path::Path;
7
8use build_helper::ci::CiEnv;
9use cargo_metadata::semver::Version;
10use cargo_metadata::{Metadata, Package, PackageId};
11
12use crate::diagnostics::{DiagCtx, RunningCheck};
13
14#[path = "../../../bootstrap/src/utils/proc_macro_deps.rs"]
15mod proc_macro_deps;
16
17#[rustfmt::skip]
20const LICENSES: &[&str] = &[
21 "(MIT OR Apache-2.0) AND Unicode-3.0", "(MIT OR Apache-2.0) AND Unicode-DFS-2016", "0BSD OR MIT OR Apache-2.0", "0BSD",
26 "Apache-2.0 / MIT",
27 "Apache-2.0 OR ISC OR MIT",
28 "Apache-2.0 OR MIT",
29 "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", "Apache-2.0",
31 "Apache-2.0/MIT",
32 "BSD-2-Clause OR Apache-2.0 OR MIT", "ISC",
34 "MIT / Apache-2.0",
35 "MIT AND (MIT OR Apache-2.0)",
36 "MIT AND Apache-2.0 WITH LLVM-exception AND (MIT OR Apache-2.0)", "MIT OR Apache-2.0 OR LGPL-2.1-or-later", "MIT OR Apache-2.0 OR Zlib", "MIT OR Apache-2.0",
40 "MIT OR Zlib OR Apache-2.0", "MIT",
42 "MIT/Apache-2.0",
43 "Unicode-3.0", "Unicode-DFS-2016", "Unlicense OR MIT",
46 "Unlicense/MIT",
47 "Zlib OR Apache-2.0 OR MIT", ];
50
51type ExceptionList = &'static [(&'static str, &'static str)];
52
53#[derive(Clone, Copy)]
54pub(crate) struct WorkspaceInfo<'a> {
55 pub(crate) path: &'a str,
57 pub(crate) exceptions: ExceptionList,
59 crates_and_deps: Option<(&'a [&'a str], &'a [&'a str], ListLocation)>,
64 pub(crate) submodules: &'a [&'a str],
66}
67
68pub(crate) const WORKSPACES: &[WorkspaceInfo<'static>] = &[
71 WorkspaceInfo {
73 path: ".",
74 exceptions: EXCEPTIONS,
75 crates_and_deps: Some((
76 &["rustc-main"],
77 PERMITTED_RUSTC_DEPENDENCIES,
78 PERMITTED_RUSTC_DEPS_LOCATION,
79 )),
80 submodules: &[],
81 },
82 WorkspaceInfo {
83 path: "library",
84 exceptions: EXCEPTIONS_STDLIB,
85 crates_and_deps: Some((
86 &["sysroot"],
87 PERMITTED_STDLIB_DEPENDENCIES,
88 PERMITTED_STDLIB_DEPS_LOCATION,
89 )),
90 submodules: &[],
91 },
92 {
93 WorkspaceInfo {
94 path: "compiler/rustc_codegen_cranelift",
95 exceptions: EXCEPTIONS_CRANELIFT,
96 crates_and_deps: Some((
97 &["rustc_codegen_cranelift"],
98 PERMITTED_CRANELIFT_DEPENDENCIES,
99 PERMITTED_CRANELIFT_DEPS_LOCATION,
100 )),
101 submodules: &[],
102 }
103 },
104 WorkspaceInfo {
105 path: "compiler/rustc_codegen_gcc",
106 exceptions: EXCEPTIONS_GCC,
107 crates_and_deps: None,
108 submodules: &[],
109 },
110 WorkspaceInfo {
111 path: "src/bootstrap",
112 exceptions: EXCEPTIONS_BOOTSTRAP,
113 crates_and_deps: None,
114 submodules: &[],
115 },
116 WorkspaceInfo {
117 path: "src/tools/cargo",
118 exceptions: EXCEPTIONS_CARGO,
119 crates_and_deps: None,
120 submodules: &["src/tools/cargo"],
121 },
122 WorkspaceInfo {
134 path: "src/tools/rust-analyzer",
135 exceptions: EXCEPTIONS_RUST_ANALYZER,
136 crates_and_deps: None,
137 submodules: &[],
138 },
139 WorkspaceInfo {
140 path: "src/tools/rustbook",
141 exceptions: EXCEPTIONS_RUSTBOOK,
142 crates_and_deps: None,
143 submodules: &["src/doc/book", "src/doc/reference"],
144 },
145 WorkspaceInfo {
146 path: "src/tools/rustc-perf",
147 exceptions: EXCEPTIONS_RUSTC_PERF,
148 crates_and_deps: None,
149 submodules: &["src/tools/rustc-perf"],
150 },
151 WorkspaceInfo {
152 path: "src/tools/test-float-parse",
153 exceptions: EXCEPTIONS,
154 crates_and_deps: None,
155 submodules: &[],
156 },
157 WorkspaceInfo {
158 path: "tests/run-make-cargo/uefi-qemu/uefi_qemu_test",
159 exceptions: EXCEPTIONS_UEFI_QEMU_TEST,
160 crates_and_deps: None,
161 submodules: &[],
162 },
163];
164
165#[rustfmt::skip]
170const EXCEPTIONS: ExceptionList = &[
171 ("ar_archive_writer", "Apache-2.0 WITH LLVM-exception"), ("arrayref", "BSD-2-Clause"), ("blake3", "CC0-1.0 OR Apache-2.0 OR Apache-2.0 WITH LLVM-exception"), ("colored", "MPL-2.0"), ("constant_time_eq", "CC0-1.0 OR MIT-0 OR Apache-2.0"), ("dissimilar", "Apache-2.0"), ("fluent-langneg", "Apache-2.0"), ("foldhash", "Zlib"), ("option-ext", "MPL-2.0"), ("rustc_apfloat", "Apache-2.0 WITH LLVM-exception"), ("ryu", "Apache-2.0 OR BSL-1.0"), ("self_cell", "Apache-2.0"), ("wasi-preview1-component-adapter-provider", "Apache-2.0 WITH LLVM-exception"), ];
187
188#[rustfmt::skip]
193const EXCEPTIONS_STDLIB: ExceptionList = &[
194 ("fortanix-sgx-abi", "MPL-2.0"), ];
198
199const EXCEPTIONS_CARGO: ExceptionList = &[
200 ("arrayref", "BSD-2-Clause"),
202 ("bitmaps", "MPL-2.0+"),
203 ("blake3", "CC0-1.0 OR Apache-2.0 OR Apache-2.0 WITH LLVM-exception"),
204 ("ciborium", "Apache-2.0"),
205 ("ciborium-io", "Apache-2.0"),
206 ("ciborium-ll", "Apache-2.0"),
207 ("constant_time_eq", "CC0-1.0 OR MIT-0 OR Apache-2.0"),
208 ("dunce", "CC0-1.0 OR MIT-0 OR Apache-2.0"),
209 ("encoding_rs", "(Apache-2.0 OR MIT) AND BSD-3-Clause"),
210 ("fiat-crypto", "MIT OR Apache-2.0 OR BSD-1-Clause"),
211 ("foldhash", "Zlib"),
212 ("im-rc", "MPL-2.0+"),
213 ("libz-rs-sys", "Zlib"),
214 ("normalize-line-endings", "Apache-2.0"),
215 ("openssl", "Apache-2.0"),
216 ("ring", "Apache-2.0 AND ISC"),
217 ("ryu", "Apache-2.0 OR BSL-1.0"), ("similar", "Apache-2.0"),
219 ("sized-chunks", "MPL-2.0+"),
220 ("subtle", "BSD-3-Clause"),
221 ("supports-hyperlinks", "Apache-2.0"),
222 ("unicode-bom", "Apache-2.0"),
223 ("zlib-rs", "Zlib"),
224 ];
226
227const EXCEPTIONS_RUST_ANALYZER: ExceptionList = &[
228 ("dissimilar", "Apache-2.0"),
230 ("foldhash", "Zlib"),
231 ("notify", "CC0-1.0"),
232 ("option-ext", "MPL-2.0"),
233 ("pulldown-cmark-to-cmark", "Apache-2.0"),
234 ("rustc_apfloat", "Apache-2.0 WITH LLVM-exception"),
235 ("ryu", "Apache-2.0 OR BSL-1.0"), ("scip", "Apache-2.0"),
237 ];
239
240const EXCEPTIONS_RUSTC_PERF: ExceptionList = &[
241 ("alloc-no-stdlib", "BSD-3-Clause"),
243 ("alloc-stdlib", "BSD-3-Clause"),
244 ("brotli", "BSD-3-Clause/MIT"),
245 ("brotli-decompressor", "BSD-3-Clause/MIT"),
246 ("encoding_rs", "(Apache-2.0 OR MIT) AND BSD-3-Clause"),
247 ("inferno", "CDDL-1.0"),
248 ("option-ext", "MPL-2.0"),
249 ("ryu", "Apache-2.0 OR BSL-1.0"),
250 ("snap", "BSD-3-Clause"),
251 ("subtle", "BSD-3-Clause"),
252 ];
254
255const EXCEPTIONS_RUSTBOOK: ExceptionList = &[
256 ("cssparser", "MPL-2.0"),
258 ("cssparser-macros", "MPL-2.0"),
259 ("dtoa-short", "MPL-2.0"),
260 ("mdbook", "MPL-2.0"),
261 ("ryu", "Apache-2.0 OR BSL-1.0"),
262 ];
264
265const EXCEPTIONS_CRANELIFT: ExceptionList = &[
266 ("cranelift-assembler-x64", "Apache-2.0 WITH LLVM-exception"),
268 ("cranelift-assembler-x64-meta", "Apache-2.0 WITH LLVM-exception"),
269 ("cranelift-bforest", "Apache-2.0 WITH LLVM-exception"),
270 ("cranelift-bitset", "Apache-2.0 WITH LLVM-exception"),
271 ("cranelift-codegen", "Apache-2.0 WITH LLVM-exception"),
272 ("cranelift-codegen-meta", "Apache-2.0 WITH LLVM-exception"),
273 ("cranelift-codegen-shared", "Apache-2.0 WITH LLVM-exception"),
274 ("cranelift-control", "Apache-2.0 WITH LLVM-exception"),
275 ("cranelift-entity", "Apache-2.0 WITH LLVM-exception"),
276 ("cranelift-frontend", "Apache-2.0 WITH LLVM-exception"),
277 ("cranelift-isle", "Apache-2.0 WITH LLVM-exception"),
278 ("cranelift-jit", "Apache-2.0 WITH LLVM-exception"),
279 ("cranelift-module", "Apache-2.0 WITH LLVM-exception"),
280 ("cranelift-native", "Apache-2.0 WITH LLVM-exception"),
281 ("cranelift-object", "Apache-2.0 WITH LLVM-exception"),
282 ("cranelift-srcgen", "Apache-2.0 WITH LLVM-exception"),
283 ("foldhash", "Zlib"),
284 ("mach2", "BSD-2-Clause OR MIT OR Apache-2.0"),
285 ("regalloc2", "Apache-2.0 WITH LLVM-exception"),
286 ("target-lexicon", "Apache-2.0 WITH LLVM-exception"),
287 ("wasmtime-jit-icache-coherence", "Apache-2.0 WITH LLVM-exception"),
288 ("wasmtime-math", "Apache-2.0 WITH LLVM-exception"),
289 ];
291
292const EXCEPTIONS_GCC: ExceptionList = &[
293 ("gccjit", "GPL-3.0"),
295 ("gccjit_sys", "GPL-3.0"),
296 ];
298
299const EXCEPTIONS_BOOTSTRAP: ExceptionList = &[
300 ("ryu", "Apache-2.0 OR BSL-1.0"), ];
302
303const EXCEPTIONS_UEFI_QEMU_TEST: ExceptionList = &[
304 ("r-efi", "MIT OR Apache-2.0 OR LGPL-2.1-or-later"), ];
306
307#[derive(Clone, Copy)]
308struct ListLocation {
309 path: &'static str,
310 line: u32,
311}
312
313macro_rules! location {
315 (+ $offset:literal) => {
316 ListLocation { path: file!(), line: line!() + $offset }
317 };
318}
319
320const PERMITTED_RUSTC_DEPS_LOCATION: ListLocation = location!(+6);
321
322const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
327 "adler2",
329 "aho-corasick",
330 "allocator-api2", "annotate-snippets",
332 "anstyle",
333 "ar_archive_writer",
334 "arrayref",
335 "arrayvec",
336 "autocfg",
337 "bitflags",
338 "blake3",
339 "block-buffer",
340 "bstr",
341 "cc",
342 "cfg-if",
343 "cfg_aliases",
344 "constant_time_eq",
345 "cpufeatures",
346 "crc32fast",
347 "crossbeam-deque",
348 "crossbeam-epoch",
349 "crossbeam-utils",
350 "crypto-common",
351 "ctrlc",
352 "darling",
353 "darling_core",
354 "darling_macro",
355 "datafrog",
356 "derive-where",
357 "derive_setters",
358 "digest",
359 "displaydoc",
360 "dissimilar",
361 "dyn-clone",
362 "either",
363 "elsa",
364 "ena",
365 "equivalent",
366 "errno",
367 "expect-test",
368 "fallible-iterator", "fastrand",
370 "find-msvc-tools",
371 "flate2",
372 "fluent-bundle",
373 "fluent-langneg",
374 "fluent-syntax",
375 "fnv",
376 "foldhash",
377 "generic-array",
378 "getopts",
379 "getrandom",
380 "gimli",
381 "gsgdt",
382 "hashbrown",
383 "icu_collections",
384 "icu_list",
385 "icu_locale",
386 "icu_locale_core",
387 "icu_locale_data",
388 "icu_provider",
389 "ident_case",
390 "indexmap",
391 "intl-memoizer",
392 "intl_pluralrules",
393 "itertools",
394 "itoa",
395 "jiff",
396 "jiff-static",
397 "jobserver",
398 "lazy_static",
399 "leb128",
400 "libc",
401 "libloading",
402 "linux-raw-sys",
403 "litemap",
404 "lock_api",
405 "log",
406 "matchers",
407 "md-5",
408 "measureme",
409 "memchr",
410 "memmap2",
411 "miniz_oxide",
412 "nix",
413 "nu-ansi-term",
414 "object",
415 "odht",
416 "once_cell",
417 "overload",
418 "parking_lot",
419 "parking_lot_core",
420 "pathdiff",
421 "perf-event-open-sys",
422 "pin-project-lite",
423 "polonius-engine",
424 "portable-atomic", "portable-atomic-util",
426 "potential_utf",
427 "ppv-lite86",
428 "proc-macro-hack",
429 "proc-macro2",
430 "psm",
431 "pulldown-cmark",
432 "pulldown-cmark-escape",
433 "punycode",
434 "quote",
435 "r-efi",
436 "rand",
437 "rand_chacha",
438 "rand_core",
439 "rand_xorshift", "rand_xoshiro",
441 "redox_syscall",
442 "ref-cast",
443 "ref-cast-impl",
444 "regex",
445 "regex-automata",
446 "regex-syntax",
447 "rustc-demangle",
448 "rustc-hash",
449 "rustc-literal-escaper",
450 "rustc-stable-hash",
451 "rustc_apfloat",
452 "rustix",
453 "ruzstd", "ryu",
455 "schemars",
456 "schemars_derive",
457 "scoped-tls",
458 "scopeguard",
459 "self_cell",
460 "serde",
461 "serde_core",
462 "serde_derive",
463 "serde_derive_internals",
464 "serde_json",
465 "serde_path_to_error",
466 "sha1",
467 "sha2",
468 "sharded-slab",
469 "shlex",
470 "smallvec",
471 "stable_deref_trait",
472 "stacker",
473 "static_assertions",
474 "strsim",
475 "syn",
476 "synstructure",
477 "tempfile",
478 "termcolor",
479 "termize",
480 "thin-vec",
481 "thiserror",
482 "thiserror-impl",
483 "thorin-dwp",
484 "thread_local",
485 "tikv-jemalloc-sys",
486 "tinystr",
487 "tinyvec",
488 "tinyvec_macros",
489 "tracing",
490 "tracing-attributes",
491 "tracing-core",
492 "tracing-log",
493 "tracing-subscriber",
494 "tracing-tree",
495 "twox-hash",
496 "type-map",
497 "typenum",
498 "unic-langid",
499 "unic-langid-impl",
500 "unic-langid-macros",
501 "unic-langid-macros-impl",
502 "unicase",
503 "unicode-ident",
504 "unicode-normalization",
505 "unicode-properties",
506 "unicode-script",
507 "unicode-security",
508 "unicode-width",
509 "unicode-xid",
510 "valuable",
511 "version_check",
512 "wasi",
513 "wasm-encoder",
514 "wasmparser",
515 "winapi",
516 "winapi-i686-pc-windows-gnu",
517 "winapi-util",
518 "winapi-x86_64-pc-windows-gnu",
519 "windows",
520 "windows-collections",
521 "windows-core",
522 "windows-future",
523 "windows-implement",
524 "windows-interface",
525 "windows-link",
526 "windows-numerics",
527 "windows-result",
528 "windows-strings",
529 "windows-sys",
530 "windows-targets",
531 "windows-threading",
532 "windows_aarch64_gnullvm",
533 "windows_aarch64_msvc",
534 "windows_i686_gnu",
535 "windows_i686_gnullvm",
536 "windows_i686_msvc",
537 "windows_x86_64_gnu",
538 "windows_x86_64_gnullvm",
539 "windows_x86_64_msvc",
540 "wit-bindgen-rt@0.39.0", "writeable",
542 "yoke",
543 "yoke-derive",
544 "zerocopy",
545 "zerocopy-derive",
546 "zerofrom",
547 "zerofrom-derive",
548 "zerotrie",
549 "zerovec",
550 "zerovec-derive",
551 ];
553
554const PERMITTED_STDLIB_DEPS_LOCATION: ListLocation = location!(+2);
555
556const PERMITTED_STDLIB_DEPENDENCIES: &[&str] = &[
557 "addr2line",
559 "adler2",
560 "cc",
561 "cfg-if",
562 "compiler_builtins",
563 "dlmalloc",
564 "fortanix-sgx-abi",
565 "getopts",
566 "gimli",
567 "hashbrown",
568 "hermit-abi",
569 "libc",
570 "memchr",
571 "miniz_oxide",
572 "object",
573 "r-efi",
574 "r-efi-alloc",
575 "rand",
576 "rand_core",
577 "rand_xorshift",
578 "rustc-demangle",
579 "rustc-literal-escaper",
580 "shlex",
581 "unwinding",
582 "vex-sdk",
583 "wasi",
584 "windows-sys",
585 "windows-targets",
586 "windows_aarch64_gnullvm",
587 "windows_aarch64_msvc",
588 "windows_i686_gnu",
589 "windows_i686_gnullvm",
590 "windows_i686_msvc",
591 "windows_x86_64_gnu",
592 "windows_x86_64_gnullvm",
593 "windows_x86_64_msvc",
594 "wit-bindgen",
595 ];
597
598const PERMITTED_CRANELIFT_DEPS_LOCATION: ListLocation = location!(+2);
599
600const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[
601 "allocator-api2",
603 "anyhow",
604 "arbitrary",
605 "bitflags",
606 "bumpalo",
607 "cfg-if",
608 "cranelift-assembler-x64",
609 "cranelift-assembler-x64-meta",
610 "cranelift-bforest",
611 "cranelift-bitset",
612 "cranelift-codegen",
613 "cranelift-codegen-meta",
614 "cranelift-codegen-shared",
615 "cranelift-control",
616 "cranelift-entity",
617 "cranelift-frontend",
618 "cranelift-isle",
619 "cranelift-jit",
620 "cranelift-module",
621 "cranelift-native",
622 "cranelift-object",
623 "cranelift-srcgen",
624 "crc32fast",
625 "equivalent",
626 "fallible-iterator",
627 "foldhash",
628 "gimli",
629 "hashbrown",
630 "indexmap",
631 "libc",
632 "libloading",
633 "libm",
634 "log",
635 "mach2",
636 "memchr",
637 "object",
638 "proc-macro2",
639 "quote",
640 "regalloc2",
641 "region",
642 "rustc-hash",
643 "serde",
644 "serde_derive",
645 "smallvec",
646 "stable_deref_trait",
647 "syn",
648 "target-lexicon",
649 "unicode-ident",
650 "wasmtime-jit-icache-coherence",
651 "wasmtime-math",
652 "windows-sys",
653 "windows-targets",
654 "windows_aarch64_gnullvm",
655 "windows_aarch64_msvc",
656 "windows_i686_gnu",
657 "windows_i686_gnullvm",
658 "windows_i686_msvc",
659 "windows_x86_64_gnu",
660 "windows_x86_64_gnullvm",
661 "windows_x86_64_msvc",
662 ];
664
665pub fn check(root: &Path, cargo: &Path, bless: bool, diag_ctx: DiagCtx) {
670 let mut check = diag_ctx.start_check("deps");
671
672 let mut checked_runtime_licenses = false;
673
674 check_proc_macro_dep_list(root, cargo, bless, &mut check);
675
676 for &WorkspaceInfo { path, exceptions, crates_and_deps, submodules } in WORKSPACES {
677 if has_missing_submodule(root, submodules) {
678 continue;
679 }
680
681 if !root.join(path).join("Cargo.lock").exists() {
682 check.error(format!("the `{path}` workspace doesn't have a Cargo.lock"));
683 continue;
684 }
685
686 let mut cmd = cargo_metadata::MetadataCommand::new();
687 cmd.cargo_path(cargo)
688 .manifest_path(root.join(path).join("Cargo.toml"))
689 .features(cargo_metadata::CargoOpt::AllFeatures)
690 .other_options(vec!["--locked".to_owned()]);
691 let metadata = t!(cmd.exec());
692
693 check_license_exceptions(&metadata, path, exceptions, &mut check);
694 if let Some((crates, permitted_deps, location)) = crates_and_deps {
695 let descr = crates.get(0).unwrap_or(&path);
696 check_permitted_dependencies(
697 &metadata,
698 descr,
699 permitted_deps,
700 crates,
701 location,
702 &mut check,
703 );
704 }
705
706 if path == "library" {
707 check_runtime_license_exceptions(&metadata, &mut check);
708 check_runtime_no_duplicate_dependencies(&metadata, &mut check);
709 check_runtime_no_proc_macros(&metadata, &mut check);
710 checked_runtime_licenses = true;
711 }
712 }
713
714 assert!(checked_runtime_licenses);
717}
718
719fn check_proc_macro_dep_list(root: &Path, cargo: &Path, bless: bool, check: &mut RunningCheck) {
721 let mut cmd = cargo_metadata::MetadataCommand::new();
722 cmd.cargo_path(cargo)
723 .manifest_path(root.join("Cargo.toml"))
724 .features(cargo_metadata::CargoOpt::AllFeatures)
725 .other_options(vec!["--locked".to_owned()]);
726 let metadata = t!(cmd.exec());
727 let is_proc_macro_pkg = |pkg: &Package| pkg.targets.iter().any(|target| target.is_proc_macro());
728
729 let mut proc_macro_deps = HashSet::new();
730 for pkg in metadata.packages.iter().filter(|pkg| is_proc_macro_pkg(pkg)) {
731 deps_of(&metadata, &pkg.id, &mut proc_macro_deps);
732 }
733 proc_macro_deps.retain(|pkg| !is_proc_macro_pkg(&metadata[pkg]));
735
736 let proc_macro_deps: HashSet<_> =
737 proc_macro_deps.into_iter().map(|dep| metadata[dep].name.as_ref()).collect();
738 let expected = proc_macro_deps::CRATES.iter().copied().collect::<HashSet<_>>();
739
740 let needs_blessing = proc_macro_deps.difference(&expected).next().is_some()
741 || expected.difference(&proc_macro_deps).next().is_some();
742
743 if needs_blessing && bless {
744 let mut proc_macro_deps: Vec<_> = proc_macro_deps.into_iter().collect();
745 proc_macro_deps.sort();
746 let mut file = File::create(root.join("src/bootstrap/src/utils/proc_macro_deps.rs"))
747 .expect("`proc_macro_deps` should exist");
748 writeln!(
749 &mut file,
750 "/// Do not update manually - use `./x.py test tidy --bless`
751/// Holds all direct and indirect dependencies of proc-macro crates in tree.
752/// See <https://github.com/rust-lang/rust/issues/134863>
753pub static CRATES: &[&str] = &[
754 // tidy-alphabetical-start"
755 )
756 .unwrap();
757 for dep in proc_macro_deps {
758 writeln!(&mut file, " {dep:?},").unwrap();
759 }
760 writeln!(
761 &mut file,
762 " // tidy-alphabetical-end
763];"
764 )
765 .unwrap();
766 } else {
767 let mut error_found = false;
768
769 for missing in proc_macro_deps.difference(&expected) {
770 error_found = true;
771 check.error(format!(
772 "proc-macro crate dependency `{missing}` is not registered in `src/bootstrap/src/utils/proc_macro_deps.rs`",
773 ));
774 }
775 for extra in expected.difference(&proc_macro_deps) {
776 error_found = true;
777 check.error(format!(
778 "`{extra}` is registered in `src/bootstrap/src/utils/proc_macro_deps.rs`, but is not a proc-macro crate dependency",
779 ));
780 }
781 if error_found {
782 check.message("Run `./x.py test tidy --bless` to regenerate the list");
783 }
784 }
785}
786
787pub fn has_missing_submodule(root: &Path, submodules: &[&str]) -> bool {
791 !CiEnv::is_ci()
792 && submodules.iter().any(|submodule| {
793 let path = root.join(submodule);
794 !path.exists()
795 || read_dir(path).unwrap().next().is_none()
797 })
798}
799
800fn check_runtime_license_exceptions(metadata: &Metadata, check: &mut RunningCheck) {
805 for pkg in &metadata.packages {
806 if pkg.source.is_none() {
807 continue;
809 }
810 let license = match &pkg.license {
811 Some(license) => license,
812 None => {
813 check
814 .error(format!("dependency `{}` does not define a license expression", pkg.id));
815 continue;
816 }
817 };
818 if !LICENSES.contains(&license.as_str()) {
819 if *pkg.name == "fortanix-sgx-abi" && pkg.license.as_deref() == Some("MPL-2.0") {
824 continue;
825 }
826
827 check.error(format!("invalid license `{}` in `{}`", license, pkg.id));
828 }
829 }
830}
831
832fn check_license_exceptions(
836 metadata: &Metadata,
837 workspace: &str,
838 exceptions: &[(&str, &str)],
839 check: &mut RunningCheck,
840) {
841 for (name, license) in exceptions {
843 if !metadata.packages.iter().any(|p| *p.name == *name) {
845 check.error(format!(
846 "could not find exception package `{name}` in workspace `{workspace}`\n\
847 Remove from EXCEPTIONS list if it is no longer used.",
848 ));
849 }
850 for pkg in metadata.packages.iter().filter(|p| *p.name == *name) {
852 match &pkg.license {
853 None => {
854 check.error(format!(
855 "dependency exception `{}` in workspace `{workspace}` does not declare a license expression",
856 pkg.id
857 ));
858 }
859 Some(pkg_license) => {
860 if pkg_license.as_str() != *license {
861 check.error(format!(r#"dependency exception `{name}` license in workspace `{workspace}` has changed
862 previously `{license}` now `{pkg_license}`
863 update EXCEPTIONS for the new license
864"#));
865 }
866 }
867 }
868 }
869 }
870
871 let exception_names: Vec<_> = exceptions.iter().map(|(name, _license)| *name).collect();
872
873 for pkg in &metadata.packages {
875 if pkg.source.is_none() {
876 continue;
878 }
879 if exception_names.contains(&pkg.name.as_str()) {
880 continue;
881 }
882 let license = match &pkg.license {
883 Some(license) => license,
884 None => {
885 check.error(format!(
886 "dependency `{}` in workspace `{workspace}` does not define a license expression",
887 pkg.id
888 ));
889 continue;
890 }
891 };
892 if !LICENSES.contains(&license.as_str()) {
893 check.error(format!(
894 "invalid license `{}` for package `{}` in workspace `{workspace}`",
895 license, pkg.id
896 ));
897 }
898 }
899}
900
901fn check_runtime_no_duplicate_dependencies(metadata: &Metadata, check: &mut RunningCheck) {
902 let mut seen_pkgs = HashSet::new();
903 for pkg in &metadata.packages {
904 if pkg.source.is_none() {
905 continue;
906 }
907
908 if pkg.name.to_string() != "wasi" && !seen_pkgs.insert(&*pkg.name) {
912 check.error(format!(
913 "duplicate package `{}` is not allowed for the standard library",
914 pkg.name
915 ));
916 }
917 }
918}
919
920fn check_runtime_no_proc_macros(metadata: &Metadata, check: &mut RunningCheck) {
921 for pkg in &metadata.packages {
922 if pkg.targets.iter().any(|target| target.is_proc_macro()) {
923 check.error(format!(
924 "proc macro `{}` is not allowed as standard library dependency.\n\
925 Using proc macros in the standard library would break cross-compilation \
926 as proc-macros don't get shipped for the host tuple.",
927 pkg.name
928 ));
929 }
930 }
931}
932
933fn check_permitted_dependencies(
938 metadata: &Metadata,
939 descr: &str,
940 permitted_dependencies: &[&'static str],
941 restricted_dependency_crates: &[&'static str],
942 permitted_location: ListLocation,
943 check: &mut RunningCheck,
944) {
945 let mut has_permitted_dep_error = false;
946 let mut deps = HashSet::new();
947 for to_check in restricted_dependency_crates {
948 let to_check = pkg_from_name(metadata, to_check);
949 deps_of(metadata, &to_check.id, &mut deps);
950 }
951
952 for permitted in permitted_dependencies {
954 fn compare(pkg: &Package, permitted: &str) -> bool {
955 if let Some((name, version)) = permitted.split_once("@") {
956 let Ok(version) = Version::parse(version) else {
957 return false;
958 };
959 *pkg.name == name && pkg.version == version
960 } else {
961 *pkg.name == permitted
962 }
963 }
964 if !deps.iter().any(|dep_id| compare(pkg_from_id(metadata, dep_id), permitted)) {
965 check.error(format!(
966 "could not find allowed package `{permitted}`\n\
967 Remove from PERMITTED_DEPENDENCIES list if it is no longer used.",
968 ));
969 has_permitted_dep_error = true;
970 }
971 }
972
973 let permitted_dependencies: HashMap<_, _> = permitted_dependencies
975 .iter()
976 .map(|s| {
977 if let Some((name, version)) = s.split_once('@') {
978 (name, Version::parse(version).ok())
979 } else {
980 (*s, None)
981 }
982 })
983 .collect();
984
985 for dep in deps {
986 let dep = pkg_from_id(metadata, dep);
987 if dep.source.is_some() {
989 let is_eq = if let Some(version) = permitted_dependencies.get(dep.name.as_str()) {
990 if let Some(version) = version { version == &dep.version } else { true }
991 } else {
992 false
993 };
994 if !is_eq {
995 check.error(format!("Dependency for {descr} not explicitly permitted: {}", dep.id));
996 has_permitted_dep_error = true;
997 }
998 }
999 }
1000
1001 if has_permitted_dep_error {
1002 eprintln!("Go to `{}:{}` for the list.", permitted_location.path, permitted_location.line);
1003 }
1004}
1005
1006fn pkg_from_name<'a>(metadata: &'a Metadata, name: &'static str) -> &'a Package {
1008 let mut i = metadata.packages.iter().filter(|p| *p.name == name);
1009 let result =
1010 i.next().unwrap_or_else(|| panic!("could not find package `{name}` in package list"));
1011 assert!(i.next().is_none(), "more than one package found for `{name}`");
1012 result
1013}
1014
1015fn pkg_from_id<'a>(metadata: &'a Metadata, id: &PackageId) -> &'a Package {
1016 metadata.packages.iter().find(|p| &p.id == id).unwrap()
1017}
1018
1019fn deps_of<'a>(metadata: &'a Metadata, pkg_id: &'a PackageId, result: &mut HashSet<&'a PackageId>) {
1021 if !result.insert(pkg_id) {
1022 return;
1023 }
1024 let node = metadata
1025 .resolve
1026 .as_ref()
1027 .unwrap()
1028 .nodes
1029 .iter()
1030 .find(|n| &n.id == pkg_id)
1031 .unwrap_or_else(|| panic!("could not find `{pkg_id}` in resolve"));
1032 for dep in &node.deps {
1033 deps_of(metadata, &dep.pkg, result);
1034 }
1035}