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