]> git.lizzy.rs Git - rust.git/blobdiff - src/tools/tidy/src/deps.rs
tidy: Add `test` to RUNTIME_CRATES.
[rust.git] / src / tools / tidy / src / deps.rs
index f1750ba1a2bc4a23b870869c32ce4058d2fc5b57..1aee4d7cda43ca46acb4a88d76ce4852d5e22a03 100644 (file)
@@ -1,9 +1,11 @@
 //! Checks the licenses of third-party dependencies.
 
-use cargo_metadata::{Metadata, Package, PackageId};
+use cargo_metadata::{Metadata, Package, PackageId, Resolve};
 use std::collections::{BTreeSet, HashSet};
 use std::path::Path;
 
+/// These are licenses that are allowed for all crates, including the runtime,
+/// rustc, tools, etc.
 const LICENSES: &[&str] = &[
     "MIT/Apache-2.0",
     "MIT / Apache-2.0",
 /// should be considered bugs. Exceptions are only allowed in Rust
 /// tooling. It is _crucial_ that no exception crates be dependencies
 /// of the Rust runtime (std/test).
-const EXCEPTIONS: &[&str] = &[
-    "mdbook",             // MPL2, mdbook
-    "openssl",            // BSD+advertising clause, cargo, mdbook
-    "pest",               // MPL2, mdbook via handlebars
-    "arrayref",           // BSD-2-Clause, mdbook via handlebars via pest
-    "thread-id",          // Apache-2.0, mdbook
-    "toml-query",         // MPL-2.0, mdbook
-    "toml-query_derive",  // MPL-2.0, mdbook
-    "is-match",           // MPL-2.0, mdbook
-    "cssparser",          // MPL-2.0, rustdoc
-    "smallvec",           // MPL-2.0, rustdoc
-    "rdrand",             // ISC, mdbook, rustfmt
-    "fuchsia-cprng",      // BSD-3-Clause, mdbook, rustfmt
-    "fuchsia-zircon-sys", // BSD-3-Clause, rustdoc, rustc, cargo
-    "fuchsia-zircon",     // BSD-3-Clause, rustdoc, rustc, cargo (jobserver & tempdir)
-    "cssparser-macros",   // MPL-2.0, rustdoc
-    "selectors",          // MPL-2.0, rustdoc
-    "clippy_lints",       // MPL-2.0, rls
-    "colored",            // MPL-2.0, rustfmt
-    "ordslice",           // Apache-2.0, rls
-    "cloudabi",           // BSD-2-Clause, (rls -> crossbeam-channel 0.2 -> rand 0.5)
-    "ryu",                // Apache-2.0, rls/cargo/... (because of serde)
-    "bytesize",           // Apache-2.0, cargo
-    "im-rc",              // MPL-2.0+, cargo
-    "adler32",            // BSD-3-Clause AND Zlib, cargo dep that isn't used
-    "constant_time_eq",   // CC0-1.0, rustfmt
-    "utf8parse",          // Apache-2.0 OR MIT, cargo via strip-ansi-escapes
-    "vte",                // Apache-2.0 OR MIT, cargo via strip-ansi-escapes
-    "sized-chunks",       // MPL-2.0+, cargo via im-rc
-    "bitmaps",            // MPL-2.0+, cargo via im-rc
+const EXCEPTIONS: &[(&str, &str)] = &[
+    ("mdbook", "MPL-2.0"),                  // mdbook
+    ("openssl", "Apache-2.0"),              // cargo, mdbook
+    ("arrayref", "BSD-2-Clause"),           // mdbook via handlebars via pest
+    ("toml-query", "MPL-2.0"),              // mdbook
+    ("toml-query_derive", "MPL-2.0"),       // mdbook
+    ("is-match", "MPL-2.0"),                // mdbook
+    ("rdrand", "ISC"),                      // mdbook, rustfmt
+    ("fuchsia-cprng", "BSD-3-Clause"),      // mdbook, rustfmt
+    ("fuchsia-zircon-sys", "BSD-3-Clause"), // rustdoc, rustc, cargo
+    ("fuchsia-zircon", "BSD-3-Clause"),     // rustdoc, rustc, cargo (jobserver & tempdir)
+    ("colored", "MPL-2.0"),                 // rustfmt
+    ("ordslice", "Apache-2.0"),             // rls
+    ("cloudabi", "BSD-2-Clause"),           // (rls -> crossbeam-channel 0.2 -> rand 0.5)
+    ("ryu", "Apache-2.0 OR BSL-1.0"),       // rls/cargo/... (because of serde)
+    ("bytesize", "Apache-2.0"),             // cargo
+    ("im-rc", "MPL-2.0+"),                  // cargo
+    ("adler32", "BSD-3-Clause AND Zlib"),   // cargo dep that isn't used
+    ("constant_time_eq", "CC0-1.0"),        // rustfmt
+    ("sized-chunks", "MPL-2.0+"),           // cargo via im-rc
+    ("bitmaps", "MPL-2.0+"),                // cargo via im-rc
     // FIXME: this dependency violates the documentation comment above:
-    "fortanix-sgx-abi",   // MPL-2.0+, libstd but only for `sgx` target
-    "dunce",              // CC0-1.0 mdbook-linkcheck
-    "codespan-reporting", // Apache-2.0 mdbook-linkcheck
-    "codespan",           // Apache-2.0 mdbook-linkcheck
-    "crossbeam-channel",  // MIT/Apache-2.0 AND BSD-2-Clause, cargo
+    ("fortanix-sgx-abi", "MPL-2.0"), // libstd but only for `sgx` target
+    ("dunce", "CC0-1.0"),            // mdbook-linkcheck
+    ("codespan-reporting", "Apache-2.0"), // mdbook-linkcheck
+    ("codespan", "Apache-2.0"),      // mdbook-linkcheck
+    ("crossbeam-channel", "MIT/Apache-2.0 AND BSD-2-Clause"), // cargo
 ];
 
+/// These are the root crates that are part of the runtime. The licenses for
+/// these and all their dependencies *must not* be in the exception list.
+const RUNTIME_CRATES: &[&str] = &["std", "core", "alloc", "test", "panic_abort", "panic_unwind"];
+
 /// Which crates to check against the whitelist?
 const WHITELIST_CRATES: &[&str] = &["rustc", "rustc_codegen_llvm"];
 
 /// Whitelist of crates rustc is allowed to depend on. Avoid adding to the list if possible.
+///
+/// This list is here to provide a speed-bump to adding a new dependency to
+/// rustc. Please check with the compiler team before adding an entry.
 const WHITELIST: &[&str] = &[
     "adler32",
     "aho-corasick",
@@ -74,7 +74,6 @@
     "backtrace",
     "backtrace-sys",
     "bitflags",
-    "build_const",
     "byteorder",
     "c2-chacha",
     "cc",
@@ -84,7 +83,6 @@
     "cloudabi",
     "cmake",
     "compiler_builtins",
-    "crc",
     "crc32fast",
     "crossbeam-deque",
     "crossbeam-epoch",
     "memchr",
     "memmap",
     "memoffset",
-    "miniz-sys",
     "miniz_oxide",
-    "miniz_oxide_c_api",
     "nodrop",
     "num_cpus",
-    "owning_ref",
     "parking_lot",
     "parking_lot_core",
     "pkg-config",
     "synstructure",
     "tempfile",
     "termcolor",
-    "terminon",
     "termion",
     "termize",
     "thread_local",
     "unicode-security",
     "unicode-width",
     "unicode-xid",
-    "unreachable",
     "utf8-ranges",
     "vcpkg",
     "version_check",
-    "void",
     "wasi",
     "winapi",
     "winapi-build",
@@ -205,12 +197,56 @@ pub fn check(path: &Path, cargo: &Path, bad: &mut bool) {
 ///
 /// Packages listed in `EXCEPTIONS` are allowed for tools.
 fn check_exceptions(metadata: &Metadata, bad: &mut bool) {
+    // Validate the EXCEPTIONS list hasn't changed.
+    for (name, license) in EXCEPTIONS {
+        // Check that the package actually exists.
+        if !metadata.packages.iter().any(|p| p.name == *name) {
+            println!(
+                "could not find exception package `{}`\n\
+                Remove from EXCEPTIONS list if it is no longer used.",
+                name
+            );
+            *bad = true;
+        }
+        // Check that the license hasn't changed.
+        for pkg in metadata.packages.iter().filter(|p| p.name == *name) {
+            if pkg.name == "fuchsia-cprng" {
+                // This package doesn't declare a license expression. Manual
+                // inspection of the license file is necessary, which appears
+                // to be BSD-3-Clause.
+                assert!(pkg.license.is_none());
+                continue;
+            }
+            match &pkg.license {
+                None => {
+                    println!(
+                        "dependency exception `{}` does not declare a license expression",
+                        pkg.id
+                    );
+                    *bad = true;
+                }
+                Some(pkg_license) => {
+                    if pkg_license.as_str() != *license {
+                        println!("dependency exception `{}` license has changed", name);
+                        println!("    previously `{}` now `{}`", license, pkg_license);
+                        println!("    update EXCEPTIONS for the new license");
+                        *bad = true;
+                    }
+                }
+            }
+        }
+    }
+
+    let exception_names: Vec<_> = EXCEPTIONS.iter().map(|(name, _license)| *name).collect();
+    let runtime_ids = compute_runtime_crates(metadata);
+
+    // Check if any package does not have a valid license.
     for pkg in &metadata.packages {
         if pkg.source.is_none() {
             // No need to check local packages.
             continue;
         }
-        if EXCEPTIONS.contains(&pkg.name.as_str()) {
+        if !runtime_ids.contains(&pkg.id) && exception_names.contains(&pkg.name.as_str()) {
             continue;
         }
         let license = match &pkg.license {
@@ -222,6 +258,13 @@ fn check_exceptions(metadata: &Metadata, bad: &mut bool) {
             }
         };
         if !LICENSES.contains(&license.as_str()) {
+            if pkg.name == "fortanix-sgx-abi" {
+                // This is a specific exception because SGX is considered
+                // "third party". See
+                // https://github.com/rust-lang/rust/issues/62620 for more. In
+                // general, these should never be added.
+                continue;
+            }
             println!("invalid license `{}` in `{}`", license, pkg.id);
             *bad = true;
         }
@@ -233,6 +276,17 @@ fn check_exceptions(metadata: &Metadata, bad: &mut bool) {
 ///
 /// Specifically, this checks that the dependencies are on the `WHITELIST`.
 fn check_whitelist(metadata: &Metadata, bad: &mut bool) {
+    // Check that the WHITELIST does not have unused entries.
+    for name in WHITELIST {
+        if !metadata.packages.iter().any(|p| p.name == *name) {
+            println!(
+                "could not find whitelisted package `{}`\n\
+                Remove from WHITELIST list if it is no longer used.",
+                name
+            );
+            *bad = true;
+        }
+    }
     // Get the whitelist in a convenient form.
     let whitelist: HashSet<_> = WHITELIST.iter().cloned().collect();
 
@@ -331,10 +385,8 @@ fn check_crate_duplicate(metadata: &Metadata, bad: &mut bool) {
 
 /// Returns a list of dependencies for the given package.
 fn deps_of<'a>(metadata: &'a Metadata, pkg_id: &'a PackageId) -> Vec<&'a Package> {
-    let node = metadata
-        .resolve
-        .as_ref()
-        .unwrap()
+    let resolve = metadata.resolve.as_ref().unwrap();
+    let node = resolve
         .nodes
         .iter()
         .find(|n| &n.id == pkg_id)
@@ -357,3 +409,42 @@ fn pkg_from_name<'a>(metadata: &'a Metadata, name: &'static str) -> &'a Package
     assert!(i.next().is_none(), "more than one package found for `{}`", name);
     result
 }
+
+/// Finds all the packages that are in the rust runtime.
+fn compute_runtime_crates<'a>(metadata: &'a Metadata) -> HashSet<&'a PackageId> {
+    let resolve = metadata.resolve.as_ref().unwrap();
+    let mut result = HashSet::new();
+    for name in RUNTIME_CRATES {
+        let id = &pkg_from_name(metadata, name).id;
+        normal_deps_of_r(resolve, id, &mut result);
+    }
+    result
+}
+
+/// Recursively find all normal dependencies.
+fn normal_deps_of_r<'a>(
+    resolve: &'a Resolve,
+    pkg_id: &'a PackageId,
+    result: &mut HashSet<&'a PackageId>,
+) {
+    if !result.insert(pkg_id) {
+        return;
+    }
+    let node = resolve
+        .nodes
+        .iter()
+        .find(|n| &n.id == pkg_id)
+        .unwrap_or_else(|| panic!("could not find `{}` in resolve", pkg_id));
+    // Don't care about dev-dependencies.
+    // Build dependencies *shouldn't* matter unless they do some kind of
+    // codegen. For now we'll assume they don't.
+    let deps = node.deps.iter().filter(|node_dep| {
+        node_dep
+            .dep_kinds
+            .iter()
+            .any(|kind_info| kind_info.kind == cargo_metadata::DependencyKind::Normal)
+    });
+    for dep in deps {
+        normal_deps_of_r(resolve, &dep.pkg, result);
+    }
+}