| 
1 | 1 | //! Checks the licenses of third-party dependencies.  | 
2 | 2 | 
  | 
3 | 3 | use std::collections::HashSet;  | 
4 |  | -use std::fs::read_dir;  | 
 | 4 | +use std::fs::{File, read_dir};  | 
 | 5 | +use std::io::Write;  | 
5 | 6 | use std::path::Path;  | 
6 | 7 | 
 
  | 
7 | 8 | use build_helper::ci::CiEnv;  | 
8 | 9 | use cargo_metadata::{Metadata, Package, PackageId};  | 
9 | 10 | 
 
  | 
 | 11 | +#[path = "../../../bootstrap/src/utils/proc_macro_deps.rs"]  | 
 | 12 | +mod proc_macro_deps;  | 
 | 13 | + | 
10 | 14 | /// These are licenses that are allowed for all crates, including the runtime,  | 
11 | 15 | /// rustc, tools, etc.  | 
12 | 16 | #[rustfmt::skip]  | 
@@ -564,9 +568,11 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[  | 
564 | 568 | ///  | 
565 | 569 | /// `root` is path to the directory with the root `Cargo.toml` (for the workspace). `cargo` is path  | 
566 | 570 | /// to the cargo executable.  | 
567 |  | -pub fn check(root: &Path, cargo: &Path, bad: &mut bool) {  | 
 | 571 | +pub fn check(root: &Path, cargo: &Path, bless: bool, bad: &mut bool) {  | 
568 | 572 |     let mut checked_runtime_licenses = false;  | 
569 | 573 | 
 
  | 
 | 574 | +    check_proc_macro_dep_list(root, cargo, bless, bad);  | 
 | 575 | + | 
570 | 576 |     for &(workspace, exceptions, permitted_deps, submodules) in WORKSPACES {  | 
571 | 577 |         if has_missing_submodule(root, submodules) {  | 
572 | 578 |             continue;  | 
@@ -600,6 +606,71 @@ pub fn check(root: &Path, cargo: &Path, bad: &mut bool) {  | 
600 | 606 |     assert!(checked_runtime_licenses);  | 
601 | 607 | }  | 
602 | 608 | 
 
  | 
 | 609 | +/// Ensure the list of proc-macro crate transitive dependencies is up to date  | 
 | 610 | +fn check_proc_macro_dep_list(root: &Path, cargo: &Path, bless: bool, bad: &mut bool) {  | 
 | 611 | +    let mut cmd = cargo_metadata::MetadataCommand::new();  | 
 | 612 | +    cmd.cargo_path(cargo)  | 
 | 613 | +        .manifest_path(root.join("Cargo.toml"))  | 
 | 614 | +        .features(cargo_metadata::CargoOpt::AllFeatures)  | 
 | 615 | +        .other_options(vec!["--locked".to_owned()]);  | 
 | 616 | +    let metadata = t!(cmd.exec());  | 
 | 617 | +    let is_proc_macro_pkg = |pkg: &Package| pkg.targets.iter().any(|target| target.is_proc_macro());  | 
 | 618 | + | 
 | 619 | +    let mut proc_macro_deps = HashSet::new();  | 
 | 620 | +    for pkg in metadata.packages.iter().filter(|pkg| is_proc_macro_pkg(*pkg)) {  | 
 | 621 | +        deps_of(&metadata, &pkg.id, &mut proc_macro_deps);  | 
 | 622 | +    }  | 
 | 623 | +    // Remove the proc-macro crates themselves  | 
 | 624 | +    proc_macro_deps.retain(|pkg| !is_proc_macro_pkg(&metadata[pkg]));  | 
 | 625 | +    let proc_macro_deps_iter = proc_macro_deps.into_iter().map(|dep| metadata[dep].name.clone());  | 
 | 626 | + | 
 | 627 | +    if bless {  | 
 | 628 | +        let mut proc_macro_deps: Vec<_> = proc_macro_deps_iter.collect();  | 
 | 629 | +        proc_macro_deps.sort();  | 
 | 630 | +        proc_macro_deps.dedup();  | 
 | 631 | +        let mut file = File::create(root.join("src/bootstrap/src/utils/proc_macro_deps.rs"))  | 
 | 632 | +            .expect("`proc_macro_deps` should exist");  | 
 | 633 | +        writeln!(  | 
 | 634 | +            &mut file,  | 
 | 635 | +            "/// Do not update manually - use `./x.py test tidy --bless`  | 
 | 636 | +/// Holds all direct and indirect dependencies of proc-macro crates in tree.  | 
 | 637 | +/// See <https://github.com/rust-lang/rust/issues/134863>  | 
 | 638 | +pub static CRATES: &[&str] = &[  | 
 | 639 | +    // tidy-alphabetical-start"  | 
 | 640 | +        )  | 
 | 641 | +        .unwrap();  | 
 | 642 | +        for dep in proc_macro_deps {  | 
 | 643 | +            writeln!(&mut file, "    {dep:?},").unwrap();  | 
 | 644 | +        }  | 
 | 645 | +        writeln!(  | 
 | 646 | +            &mut file,  | 
 | 647 | +            "    // tidy-alphabetical-end  | 
 | 648 | +];"  | 
 | 649 | +        )  | 
 | 650 | +        .unwrap();  | 
 | 651 | +    } else {  | 
 | 652 | +        let proc_macro_deps: HashSet<_> = proc_macro_deps_iter.collect();  | 
 | 653 | +        let expected =  | 
 | 654 | +            proc_macro_deps::CRATES.iter().map(|s| s.to_string()).collect::<HashSet<_>>();  | 
 | 655 | +        let old_bad = *bad;  | 
 | 656 | +        for missing in proc_macro_deps.difference(&expected) {  | 
 | 657 | +            tidy_error!(  | 
 | 658 | +                bad,  | 
 | 659 | +                "proc-macro crate dependency `{missing}` is not registered in `src/bootstrap/src/utils/proc_macro_deps.rs`",  | 
 | 660 | +            );  | 
 | 661 | +        }  | 
 | 662 | +        for extra in expected.difference(&proc_macro_deps) {  | 
 | 663 | +            tidy_error!(  | 
 | 664 | +                bad,  | 
 | 665 | +                "`{extra}` is not registered in `src/bootstrap/src/utils/proc_macro_deps.rs`, but is not a proc-macro crate dependency",  | 
 | 666 | +            );  | 
 | 667 | +        }  | 
 | 668 | +        if *bad != old_bad {  | 
 | 669 | +            eprintln!("Run `./x.py test tidy --bless` to regenerate the list");  | 
 | 670 | +        }  | 
 | 671 | +    }  | 
 | 672 | +}  | 
 | 673 | + | 
603 | 674 | /// Used to skip a check if a submodule is not checked out, and not in a CI environment.  | 
604 | 675 | ///  | 
605 | 676 | /// This helps prevent enforcing developers to fetch submodules for tidy.  | 
 | 
0 commit comments