rust/src/tools/tidy/src/deps.rs

428 lines
13 KiB
Rust
Raw Normal View History

2019-01-11 17:32:31 +01:00
//! Checks the licenses of third-party dependencies by inspecting vendors.
2016-12-11 00:27:42 +01:00
2019-12-22 23:42:04 +01:00
use std::collections::{BTreeSet, HashMap, HashSet};
use std::fs;
2016-12-11 00:27:42 +01:00
use std::path::Path;
use std::process::Command;
use serde::Deserialize;
use serde_json;
const LICENSES: &[&str] = &[
"MIT/Apache-2.0",
rustbuild: Add support for compiling Cargo This commit adds support to rustbuild for compiling Cargo as part of the release process. Previously rustbuild would simply download a Cargo snapshot and repackage it. With this change we should be able to turn off artifacts from the rust-lang/cargo repository and purely rely on the artifacts Cargo produces here. The infrastructure added here is intended to be extensible to other components, such as the RLS. It won't exactly be a one-line addition, but the addition of Cargo didn't require too much hooplah anyway. The process for release Cargo will now look like: * The rust-lang/rust repository has a Cargo submodule which is used to build a Cargo to pair with the rust-lang/rust release * Periodically we'll update the cargo submodule as necessary on rust-lang/rust's master branch * When branching beta we'll create a new branch of Cargo (as we do today), and the first commit to the beta branch will be to update the Cargo submodule to this exact revision. * When branching stable, we'll ensure that the Cargo submodule is updated and then make a stable release. Backports to Cargo will look like: * Send a PR to cargo's master branch * Send a PR to cargo's release branch (e.g. rust-1.16.0) * Send a PR to rust-lang/rust's beta branch updating the submodule * Eventually send a PR to rust-lang/rust's master branch updating the submodule For reference, the process to add a new component to the rust-lang/rust release would look like: * Add `$foo` as a submodule in `src/tools` * Add a `tool-$foo` step which compiles `$foo` with the specified compiler, likely mirroring what Cargo does. * Add a `dist-$foo` step which uses `src/tools/$foo` and the `tool-$foo` output to create a rust-installer package for `$foo` likely mirroring what Cargo does. * Update the `dist-extended` step with a new dependency on `dist-$foo` * Update `src/tools/build-manifest` for the new component.
2017-02-16 00:57:06 +01:00
"MIT / Apache-2.0",
"Apache-2.0/MIT",
"Apache-2.0 / MIT",
"MIT OR Apache-2.0",
"Apache-2.0 OR MIT",
"Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", // wasi license
"MIT",
"Unlicense/MIT",
"Unlicense OR MIT",
];
/// These are exceptions to Rust's permissive licensing policy, and
/// should be considered bugs. Exceptions are only allowed in Rust
/// tooling. It is _crucial_ that no exception crates be dependencies
2019-01-11 17:32:31 +01:00
/// of the Rust runtime (std/test).
const EXCEPTIONS: &[&str] = &[
2018-02-23 01:59:04 +01:00
"mdbook", // MPL2, mdbook
"openssl", // BSD+advertising clause, cargo, mdbook
"pest", // MPL2, mdbook via handlebars
"arrayref", // BSD-2-Clause, mdbook via handlebars via pest
2018-02-23 01:59:04 +01:00
"thread-id", // Apache-2.0, mdbook
"toml-query", // MPL-2.0, mdbook
"is-match", // MPL-2.0, mdbook
"cssparser", // MPL-2.0, rustdoc
"smallvec", // MPL-2.0, rustdoc
2019-06-10 20:22:31 +02:00
"rdrand", // ISC, mdbook, rustfmt
"fuchsia-cprng", // BSD-3-Clause, mdbook, rustfmt
"fuchsia-zircon-sys", // BSD-3-Clause, rustdoc, rustc, cargo
2018-02-23 01:59:04 +01:00
"fuchsia-zircon", // BSD-3-Clause, rustdoc, rustc, cargo (jobserver & tempdir)
"cssparser-macros", // MPL-2.0, rustdoc
"selectors", // MPL-2.0, rustdoc
2018-04-26 05:10:11 +02:00
"clippy_lints", // MPL-2.0, rls
"colored", // MPL-2.0, rustfmt
2018-07-06 02:34:00 +02:00
"ordslice", // Apache-2.0, rls
2018-07-29 05:54:27 +02:00
"cloudabi", // BSD-2-Clause, (rls -> crossbeam-channel 0.2 -> rand 0.5)
2019-01-11 17:32:31 +01:00
"ryu", // Apache-2.0, rls/cargo/... (because of serde)
"bytesize", // Apache-2.0, cargo
2018-12-03 02:33:20 +01:00
"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
2019-05-23 22:27:49 +02:00
"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
2019-11-25 17:14:07 +01:00
"bitmaps", // MPL-2.0+, cargo via im-rc
2019-06-21 17:35:48 +02:00
// FIXME: this dependency violates the documentation comment above:
"fortanix-sgx-abi", // MPL-2.0+, libstd but only for `sgx` target
2019-11-12 19:45:33 +01:00
"dunce", // CC0-1.0 mdbook-linkcheck
"codespan-reporting", // Apache-2.0 mdbook-linkcheck
"codespan", // Apache-2.0 mdbook-linkcheck
2016-12-11 00:27:42 +01:00
];
/// Which crates to check against the whitelist?
2019-12-22 23:42:04 +01:00
const WHITELIST_CRATES: &[CrateVersion<'_>] =
&[CrateVersion("rustc", "0.0.0"), CrateVersion("rustc_codegen_llvm", "0.0.0")];
/// Whitelist of crates rustc is allowed to depend on. Avoid adding to the list if possible.
2019-03-01 10:14:09 +01:00
const WHITELIST: &[Crate<'_>] = &[
Crate("adler32"),
Crate("aho-corasick"),
Crate("annotate-snippets"),
Crate("ansi_term"),
2018-04-26 00:49:52 +02:00
Crate("arrayvec"),
Crate("atty"),
std: Depend on `backtrace` crate from crates.io This commit removes all in-tree support for generating backtraces in favor of depending on the `backtrace` crate on crates.io. This resolves a very longstanding piece of duplication where the standard library has long contained the ability to generate a backtrace on panics, but the code was later extracted and duplicated on crates.io with the `backtrace` crate. Since that fork each implementation has seen various improvements one way or another, but typically `backtrace`-the-crate has lagged behind libstd in one way or another. The goal here is to remove this duplication of a fairly critical piece of code and ensure that there's only one source of truth for generating backtraces between the standard library and the crate on crates.io. Recently I've been working to bring the `backtrace` crate on crates.io up to speed with the support in the standard library which includes: * Support for `StackWalkEx` on MSVC to recover inline frames with debuginfo. * Using `libbacktrace` by default on MinGW targets. * Supporting `libbacktrace` on OSX as an option. * Ensuring all the requisite support in `backtrace`-the-crate compiles with `#![no_std]`. * Updating the `libbacktrace` implementation in `backtrace`-the-crate to initialize the global state with the correct filename where necessary. After reviewing the code in libstd the `backtrace` crate should be at exact feature parity with libstd today. The backtraces generated should have the same symbols and same number of frames in general, and there's not known divergence from libstd currently. Note that one major difference between libstd's backtrace support and the `backtrace` crate is that on OSX the crates.io crate enables the `coresymbolication` feature by default. This feature, however, uses private internal APIs that aren't published for OSX. While they provide more accurate backtraces this isn't appropriate for libstd distributed as a binary, so libstd's dependency on the `backtrace` crate explicitly disables this feature and forces OSX to use `libbacktrace` as a symbolication strategy. The long-term goal of this refactoring is to eventually move us towards a world where we can drop `libbacktrace` entirely and simply use Gimli and the surrounding crates for backtrace support. That's still aways off but hopefully will much more easily enabled by having the source of truth for backtraces live in crates.io! Procedurally if we go forward with this I'd like to transfer the `backtrace-rs` crate to the rust-lang GitHub organization as well, but I figured I'd hold off on that until we get closer to merging.
2019-05-15 16:30:15 +02:00
Crate("autocfg"),
2018-03-02 04:22:06 +01:00
Crate("backtrace"),
Crate("backtrace-sys"),
Crate("bitflags"),
Crate("build_const"),
2018-03-02 04:22:06 +01:00
Crate("byteorder"),
2019-07-24 17:02:35 +02:00
Crate("c2-chacha"),
2018-03-02 04:22:06 +01:00
Crate("cc"),
Crate("cfg-if"),
Crate("chalk-engine"),
Crate("chalk-macros"),
Crate("cloudabi"),
2018-03-02 04:22:06 +01:00
Crate("cmake"),
Crate("compiler_builtins"),
Crate("crc"),
Crate("crc32fast"),
2018-04-26 00:49:52 +02:00
Crate("crossbeam-deque"),
Crate("crossbeam-epoch"),
2019-10-07 22:43:10 +02:00
Crate("crossbeam-queue"),
2018-04-26 00:49:52 +02:00
Crate("crossbeam-utils"),
2018-05-28 18:54:11 +02:00
Crate("datafrog"),
bootstrap: Merge the libtest build step with libstd Since its inception rustbuild has always worked in three stages: one for libstd, one for libtest, and one for rustc. These three stages were architected around crates.io dependencies, where rustc wants to depend on crates.io crates but said crates don't explicitly depend on libstd, requiring a sysroot assembly step in the middle. This same logic was applied for libtest where libtest wants to depend on crates.io crates (`getopts`) but `getopts` didn't say that it depended on std, so it needed `std` built ahead of time. Lots of time has passed since the inception of rustbuild, however, and we've since gotten to the point where even `std` itself is depending on crates.io crates (albeit with some wonky configuration). This commit applies the same logic to the two dependencies that the `test` crate pulls in from crates.io, `getopts` and `unicode-width`. Over the many years since rustbuild's inception `unicode-width` was the only dependency picked up by the `test` crate, so the extra configuration necessary to get crates building in this crate graph is unlikely to be too much of a burden on developers. After this patch it means that there are now only two build phasese of rustbuild, one for libstd and one for rustc. The libtest/libproc_macro build phase is all lumped into one now with `std`. This was originally motivated by rust-lang/cargo#7216 where Cargo was having to deal with synthesizing dependency edges but this commit makes them explicit in this repository.
2019-08-16 17:29:08 +02:00
Crate("dlmalloc"),
2018-04-26 00:49:52 +02:00
Crate("either"),
2018-03-02 19:15:02 +01:00
Crate("ena"),
Crate("env_logger"),
2018-03-02 04:22:06 +01:00
Crate("filetime"),
Crate("flate2"),
bootstrap: Merge the libtest build step with libstd Since its inception rustbuild has always worked in three stages: one for libstd, one for libtest, and one for rustc. These three stages were architected around crates.io dependencies, where rustc wants to depend on crates.io crates but said crates don't explicitly depend on libstd, requiring a sysroot assembly step in the middle. This same logic was applied for libtest where libtest wants to depend on crates.io crates (`getopts`) but `getopts` didn't say that it depended on std, so it needed `std` built ahead of time. Lots of time has passed since the inception of rustbuild, however, and we've since gotten to the point where even `std` itself is depending on crates.io crates (albeit with some wonky configuration). This commit applies the same logic to the two dependencies that the `test` crate pulls in from crates.io, `getopts` and `unicode-width`. Over the many years since rustbuild's inception `unicode-width` was the only dependency picked up by the `test` crate, so the extra configuration necessary to get crates building in this crate graph is unlikely to be too much of a burden on developers. After this patch it means that there are now only two build phasese of rustbuild, one for libstd and one for rustc. The libtest/libproc_macro build phase is all lumped into one now with `std`. This was originally motivated by rust-lang/cargo#7216 where Cargo was having to deal with synthesizing dependency edges but this commit makes them explicit in this repository.
2019-08-16 17:29:08 +02:00
Crate("fortanix-sgx-abi"),
2018-03-02 04:22:06 +01:00
Crate("fuchsia-zircon"),
Crate("fuchsia-zircon-sys"),
2018-04-08 13:44:29 +02:00
Crate("getopts"),
2019-07-24 17:02:35 +02:00
Crate("getrandom"),
bootstrap: Merge the libtest build step with libstd Since its inception rustbuild has always worked in three stages: one for libstd, one for libtest, and one for rustc. These three stages were architected around crates.io dependencies, where rustc wants to depend on crates.io crates but said crates don't explicitly depend on libstd, requiring a sysroot assembly step in the middle. This same logic was applied for libtest where libtest wants to depend on crates.io crates (`getopts`) but `getopts` didn't say that it depended on std, so it needed `std` built ahead of time. Lots of time has passed since the inception of rustbuild, however, and we've since gotten to the point where even `std` itself is depending on crates.io crates (albeit with some wonky configuration). This commit applies the same logic to the two dependencies that the `test` crate pulls in from crates.io, `getopts` and `unicode-width`. Over the many years since rustbuild's inception `unicode-width` was the only dependency picked up by the `test` crate, so the extra configuration necessary to get crates building in this crate graph is unlikely to be too much of a burden on developers. After this patch it means that there are now only two build phasese of rustbuild, one for libstd and one for rustc. The libtest/libproc_macro build phase is all lumped into one now with `std`. This was originally motivated by rust-lang/cargo#7216 where Cargo was having to deal with synthesizing dependency edges but this commit makes them explicit in this repository.
2019-08-16 17:29:08 +02:00
Crate("hashbrown"),
Crate("humantime"),
Crate("indexmap"),
2019-03-20 16:34:30 +01:00
Crate("itertools"),
2018-03-02 04:22:06 +01:00
Crate("jobserver"),
Crate("kernel32-sys"),
Crate("lazy_static"),
Crate("libc"),
Crate("libz-sys"),
2018-08-05 00:24:39 +02:00
Crate("lock_api"),
2018-03-02 04:22:06 +01:00
Crate("log"),
Crate("log_settings"),
Crate("measureme"),
Crate("memchr"),
2018-09-03 12:59:30 +02:00
Crate("memmap"),
2018-04-26 00:49:52 +02:00
Crate("memoffset"),
2018-03-02 04:22:06 +01:00
Crate("miniz-sys"),
Crate("miniz_oxide"),
Crate("miniz_oxide_c_api"),
2018-04-26 00:49:52 +02:00
Crate("nodrop"),
2018-03-02 04:22:06 +01:00
Crate("num_cpus"),
Crate("owning_ref"),
Crate("parking_lot"),
Crate("parking_lot_core"),
Crate("pkg-config"),
2018-08-05 00:24:39 +02:00
Crate("polonius-engine"),
2019-07-24 17:02:35 +02:00
Crate("ppv-lite86"),
2018-12-17 19:23:04 +01:00
Crate("proc-macro2"),
rustc: Link LLVM directly into rustc again This commit builds on #65501 continue to simplify the build system and compiler now that we no longer have multiple LLVM backends to ship by default. Here this switches the compiler back to what it once was long long ago, which is linking LLVM directly to the compiler rather than dynamically loading it at runtime. The `codegen-backends` directory of the sysroot no longer exists and all relevant support in the build system is removed. Note that `rustc` still supports a dynamically loaded codegen backend as it did previously, it just no longer supports dynamically loaded codegen backends in its own sysroot. Additionally as part of this the `librustc_codegen_llvm` crate now once again explicitly depends on all of its crates instead of implicitly loading them through the sysroot. This involved filling out its `Cargo.toml` and deleting all the now-unnecessary `extern crate` annotations in the header of the crate. (this in turn required adding a number of imports for names of macros too). The end results of this change are: * Rustbuild's build process for the compiler as all the "oh don't forget the codegen backend" checks can be easily removed. * Building `rustc_codegen_llvm` is much simpler since it's simply another compiler crate. * Managing the dependencies of `rustc_codegen_llvm` is much simpler since it's "just another `Cargo.toml` to edit" * The build process should be a smidge faster because there's more parallelism in the main rustc build step rather than splitting `librustc_codegen_llvm` out to its own step. * The compiler is expected to be slightly faster by default because the codegen backend does not need to be dynamically loaded. * Disabling LLVM as part of rustbuild is still supported, supporting multiple codegen backends is still supported, and dynamic loading of a codegen backend is still supported.
2019-10-22 17:51:35 +02:00
Crate("punycode"),
Crate("quick-error"),
2018-12-17 19:23:04 +01:00
Crate("quote"),
2018-03-02 04:22:06 +01:00
Crate("rand"),
Crate("rand_chacha"),
Crate("rand_core"),
Crate("rand_hc"),
Crate("rand_isaac"),
Crate("rand_pcg"),
Crate("rand_xorshift"),
2018-03-02 04:22:06 +01:00
Crate("redox_syscall"),
Crate("redox_termios"),
Crate("regex"),
Crate("regex-syntax"),
Crate("remove_dir_all"),
2018-03-02 04:22:06 +01:00
Crate("rustc-demangle"),
Crate("rustc-hash"),
2018-04-26 00:49:52 +02:00
Crate("rustc-rayon"),
Crate("rustc-rayon-core"),
Crate("rustc_version"),
Crate("scoped-tls"),
2018-04-26 00:49:52 +02:00
Crate("scopeguard"),
Crate("semver"),
Crate("semver-parser"),
Crate("serde"),
2018-12-17 19:23:04 +01:00
Crate("serde_derive"),
2018-03-02 04:22:06 +01:00
Crate("smallvec"),
Crate("stable_deref_trait"),
2018-12-17 19:23:04 +01:00
Crate("syn"),
2018-12-03 01:14:35 +01:00
Crate("synstructure"),
2018-05-13 15:26:55 +02:00
Crate("tempfile"),
Crate("termcolor"),
Crate("terminon"),
Crate("termion"),
2020-01-29 01:13:48 +01:00
Crate("termize"),
Crate("thread_local"),
Crate("ucd-util"),
Crate("unicode-normalization"),
2020-01-02 13:02:22 +01:00
Crate("unicode-script"),
Crate("unicode-security"),
2018-03-02 04:22:06 +01:00
Crate("unicode-width"),
2018-12-17 19:23:04 +01:00
Crate("unicode-xid"),
Crate("unreachable"),
Crate("utf8-ranges"),
Crate("vcpkg"),
Crate("version_check"),
Crate("void"),
2019-08-29 20:08:13 +02:00
Crate("wasi"),
2018-03-02 04:22:06 +01:00
Crate("winapi"),
Crate("winapi-build"),
Crate("winapi-i686-pc-windows-gnu"),
2018-08-05 00:24:39 +02:00
Crate("winapi-util"),
2018-03-02 04:22:06 +01:00
Crate("winapi-x86_64-pc-windows-gnu"),
Crate("wincolor"),
Crate("hermit-abi"),
2018-02-24 01:25:21 +01:00
];
2019-01-11 17:32:31 +01:00
// Some types for Serde to deserialize the output of `cargo metadata` to.
#[derive(Deserialize)]
struct Output {
resolve: Resolve,
2018-02-24 01:01:51 +01:00
}
#[derive(Deserialize)]
struct Resolve {
nodes: Vec<ResolveNode>,
}
#[derive(Deserialize)]
struct ResolveNode {
id: String,
dependencies: Vec<String>,
}
2019-01-11 17:32:31 +01:00
/// A unique identifier for a crate.
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug, Hash)]
2019-01-11 17:32:31 +01:00
struct Crate<'a>(&'a str); // (name)
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug, Hash)]
struct CrateVersion<'a>(&'a str, &'a str); // (name, version)
2019-03-01 10:14:09 +01:00
impl Crate<'_> {
pub fn id_str(&self) -> String {
format!("{} ", self.0)
}
}
impl<'a> CrateVersion<'a> {
2019-01-11 17:32:31 +01:00
/// Returns the struct and whether or not the dependency is in-tree.
2018-02-28 21:12:15 +01:00
pub fn from_str(s: &'a str) -> (Self, bool) {
let mut parts = s.split(' ');
let name = parts.next().unwrap();
let version = parts.next().unwrap();
2018-02-28 21:12:15 +01:00
let path = parts.next().unwrap();
2018-02-28 21:12:15 +01:00
let is_path_dep = path.starts_with("(path+");
(CrateVersion(name, version), is_path_dep)
}
pub fn id_str(&self) -> String {
format!("{} {}", self.0, self.1)
}
}
impl<'a> From<CrateVersion<'a>> for Crate<'a> {
fn from(cv: CrateVersion<'a>) -> Crate<'a> {
Crate(cv.0)
}
}
/// Checks the dependency at the given path. Changes `bad` to `true` if a check failed.
///
2018-02-24 01:01:51 +01:00
/// Specifically, this checks that the license is correct.
2016-12-11 00:27:42 +01:00
pub fn check(path: &Path, bad: &mut bool) {
2019-01-11 17:32:31 +01:00
// Check licences.
let path = path.join("../vendor");
2016-12-11 00:27:42 +01:00
assert!(path.exists(), "vendor directory missing");
let mut saw_dir = false;
for dir in t!(path.read_dir()) {
2016-12-11 00:27:42 +01:00
saw_dir = true;
let dir = t!(dir);
2019-01-11 17:32:31 +01:00
// Skip our exceptions.
let is_exception = EXCEPTIONS.iter().any(|exception| {
2019-12-22 23:42:04 +01:00
dir.path().to_str().unwrap().contains(&format!("vendor/{}", exception))
});
if is_exception {
continue;
}
2016-12-11 00:27:42 +01:00
let toml = dir.path().join("Cargo.toml");
*bad = !check_license(&toml) || *bad;
2016-12-11 00:27:42 +01:00
}
assert!(saw_dir, "no vendored source");
2018-02-24 01:01:51 +01:00
}
2019-01-11 17:32:31 +01:00
/// Checks the dependency of `WHITELIST_CRATES` at the given path. Changes `bad` to `true` if a
/// check failed.
2018-02-24 01:01:51 +01:00
///
2019-01-11 17:32:31 +01:00
/// Specifically, this checks that the dependencies are on the `WHITELIST`.
2018-02-26 18:05:43 +01:00
pub fn check_whitelist(path: &Path, cargo: &Path, bad: &mut bool) {
2019-01-11 17:32:31 +01:00
// Get dependencies from Cargo metadata.
let resolve = get_deps(path, cargo);
2018-02-24 01:01:51 +01:00
2019-01-11 17:32:31 +01:00
// Get the whitelist in a convenient form.
let whitelist: HashSet<_> = WHITELIST.iter().cloned().collect();
2019-01-11 17:32:31 +01:00
// Check dependencies.
2018-02-27 19:02:54 +01:00
let mut visited = BTreeSet::new();
let mut unapproved = BTreeSet::new();
for &krate in WHITELIST_CRATES.iter() {
2018-02-28 23:28:30 +01:00
let mut bad = check_crate_whitelist(&whitelist, &resolve, &mut visited, krate, false);
unapproved.append(&mut bad);
}
2018-02-24 01:01:51 +01:00
if !unapproved.is_empty() {
2018-02-24 01:01:51 +01:00
println!("Dependencies not on the whitelist:");
for dep in unapproved {
2018-02-27 19:02:54 +01:00
println!("* {}", dep.id_str());
2018-02-24 01:01:51 +01:00
}
*bad = true;
}
check_crate_duplicate(&resolve, bad);
2016-12-11 00:27:42 +01:00
}
fn check_license(path: &Path) -> bool {
if !path.exists() {
panic!("{} does not exist", path.display());
}
let contents = t!(fs::read_to_string(&path));
2016-12-11 00:27:42 +01:00
let mut found_license = false;
for line in contents.lines() {
if !line.starts_with("license") {
continue;
}
let license = extract_license(line);
if !LICENSES.contains(&&*license) {
println!("invalid license {} in {}", license, path.display());
return false;
}
found_license = true;
break;
}
if !found_license {
println!("no license in {}", path.display());
return false;
}
true
}
fn extract_license(line: &str) -> String {
let first_quote = line.find('"');
let last_quote = line.rfind('"');
if let (Some(f), Some(l)) = (first_quote, last_quote) {
2018-02-23 01:59:04 +01:00
let license = &line[f + 1..l];
2016-12-11 00:27:42 +01:00
license.into()
} else {
"bad-license-parse".into()
}
}
2019-01-11 17:32:31 +01:00
/// Gets the dependencies of the crate at the given path using `cargo metadata`.
fn get_deps(path: &Path, cargo: &Path) -> Resolve {
2019-01-11 17:32:31 +01:00
// Run `cargo metadata` to get the set of dependencies.
2018-02-26 18:05:43 +01:00
let output = Command::new(cargo)
.arg("metadata")
.arg("--format-version")
.arg("1")
.arg("--manifest-path")
.arg(path.join("../Cargo.toml"))
.output()
.expect("Unable to run `cargo metadata`")
.stdout;
let output = String::from_utf8_lossy(&output);
let output: Output = serde_json::from_str(&output).unwrap();
output.resolve
}
/// Checks the dependencies of the given crate from the given cargo metadata to see if they are on
/// the whitelist. Returns a list of illegal dependencies.
2019-03-01 10:14:09 +01:00
fn check_crate_whitelist<'a>(
whitelist: &'a HashSet<Crate<'_>>,
resolve: &'a Resolve,
2019-03-01 10:14:09 +01:00
visited: &mut BTreeSet<CrateVersion<'a>>,
krate: CrateVersion<'a>,
2018-02-28 23:28:30 +01:00
must_be_on_whitelist: bool,
2018-02-27 19:02:54 +01:00
) -> BTreeSet<Crate<'a>> {
2019-01-11 17:32:31 +01:00
// This will contain bad deps.
2018-02-27 19:02:54 +01:00
let mut unapproved = BTreeSet::new();
2019-01-11 17:32:31 +01:00
// Check if we have already visited this crate.
2018-02-27 19:02:54 +01:00
if visited.contains(&krate) {
return unapproved;
}
visited.insert(krate);
2019-01-11 17:32:31 +01:00
// If this path is in-tree, we don't require it to be on the whitelist.
2018-02-28 23:28:30 +01:00
if must_be_on_whitelist {
2019-01-11 17:32:31 +01:00
// If this dependency is not on `WHITELIST`, add to bad set.
2018-02-28 23:28:30 +01:00
if !whitelist.contains(&krate.into()) {
unapproved.insert(krate.into());
}
}
2019-01-11 17:32:31 +01:00
// Do a DFS in the crate graph (it's a DAG, so we know we have no cycles!).
let to_check = resolve
.nodes
.iter()
.find(|n| n.id.starts_with(&krate.id_str()))
.expect("crate does not exist");
for dep in to_check.dependencies.iter() {
2018-02-28 21:12:15 +01:00
let (krate, is_path_dep) = CrateVersion::from_str(dep);
2018-02-28 23:28:30 +01:00
let mut bad = check_crate_whitelist(whitelist, resolve, visited, krate, !is_path_dep);
unapproved.append(&mut bad);
}
unapproved
}
fn check_crate_duplicate(resolve: &Resolve, bad: &mut bool) {
const FORBIDDEN_TO_HAVE_DUPLICATES: &[&str] = &[
2019-01-11 17:32:31 +01:00
// These two crates take quite a long time to build, so don't allow two versions of them
// to accidentally sneak into our dependency graph, in order to ensure we keep our CI times
// under control.
"cargo",
"rustc-ap-syntax",
];
let mut name_to_id: HashMap<_, Vec<_>> = HashMap::new();
for node in resolve.nodes.iter() {
2019-12-22 23:42:04 +01:00
name_to_id.entry(node.id.split_whitespace().next().unwrap()).or_default().push(&node.id);
}
for name in FORBIDDEN_TO_HAVE_DUPLICATES {
if name_to_id[name].len() <= 1 {
2019-12-22 23:42:04 +01:00
continue;
}
println!("crate `{}` is duplicated in `Cargo.lock`", name);
for id in name_to_id[name].iter() {
println!(" * {}", id);
}
*bad = true;
}
}