]> git.proxmox.com Git - rustc.git/blame - src/tools/tidy/src/deps.rs
Merge tag 'debian/1.52.1+dfsg1-1_exp2' into proxmox/buster
[rustc.git] / src / tools / tidy / src / deps.rs
CommitLineData
ba9703b0 1//! Checks the licenses of third-party dependencies.
476ff2be 2
ba9703b0
XL
3use cargo_metadata::{Metadata, Package, PackageId, Resolve};
4use std::collections::{BTreeSet, HashSet};
476ff2be
SL
5use std::path::Path;
6
ba9703b0
XL
7/// These are licenses that are allowed for all crates, including the runtime,
8/// rustc, tools, etc.
b7449926 9const LICENSES: &[&str] = &[
8bb4bdeb
XL
10 "MIT/Apache-2.0",
11 "MIT / Apache-2.0",
12 "Apache-2.0/MIT",
3b2f2976 13 "Apache-2.0 / MIT",
8bb4bdeb 14 "MIT OR Apache-2.0",
416331ca 15 "Apache-2.0 OR MIT",
e1599b0c 16 "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", // wasi license
8bb4bdeb
XL
17 "MIT",
18 "Unlicense/MIT",
8faf50e0 19 "Unlicense OR MIT",
3dfed10e
XL
20 "0BSD OR MIT OR Apache-2.0", // adler license
21 "Zlib OR Apache-2.0 OR MIT", // tinyvec
8bb4bdeb
XL
22];
23
0531ce1d
XL
24/// These are exceptions to Rust's permissive licensing policy, and
25/// should be considered bugs. Exceptions are only allowed in Rust
26/// tooling. It is _crucial_ that no exception crates be dependencies
9fa01778 27/// of the Rust runtime (std/test).
ba9703b0 28const EXCEPTIONS: &[(&str, &str)] = &[
3dfed10e
XL
29 ("mdbook", "MPL-2.0"), // mdbook
30 ("openssl", "Apache-2.0"), // cargo, mdbook
31 ("fuchsia-zircon-sys", "BSD-3-Clause"), // rustdoc, rustc, cargo
32 ("fuchsia-zircon", "BSD-3-Clause"), // rustdoc, rustc, cargo (jobserver & tempdir)
33 ("colored", "MPL-2.0"), // rustfmt
34 ("ordslice", "Apache-2.0"), // rls
35 ("cloudabi", "BSD-2-Clause"), // (rls -> crossbeam-channel 0.2 -> rand 0.5)
36 ("ryu", "Apache-2.0 OR BSL-1.0"), // rls/cargo/... (because of serde)
37 ("bytesize", "Apache-2.0"), // cargo
38 ("im-rc", "MPL-2.0+"), // cargo
39 ("constant_time_eq", "CC0-1.0"), // rustfmt
40 ("sized-chunks", "MPL-2.0+"), // cargo via im-rc
41 ("bitmaps", "MPL-2.0+"), // cargo via im-rc
42 ("crossbeam-queue", "MIT/Apache-2.0 AND BSD-2-Clause"), // rls via rayon
43 ("arrayref", "BSD-2-Clause"), // cargo-miri/directories/.../rust-argon2 (redox)
44 ("instant", "BSD-3-Clause"), // rustc_driver/tracing-subscriber/parking_lot
1b1a35ee 45 ("snap", "BSD-3-Clause"), // rustc
416331ca 46 // FIXME: this dependency violates the documentation comment above:
ba9703b0 47 ("fortanix-sgx-abi", "MPL-2.0"), // libstd but only for `sgx` target
0531ce1d
XL
48];
49
ba9703b0
XL
50/// These are the root crates that are part of the runtime. The licenses for
51/// these and all their dependencies *must not* be in the exception list.
52const RUNTIME_CRATES: &[&str] = &["std", "core", "alloc", "test", "panic_abort", "panic_unwind"];
53
f035d41b
XL
54/// Crates whose dependencies must be explicitly permitted.
55const RESTRICTED_DEPENDENCY_CRATES: &[&str] = &["rustc_middle", "rustc_codegen_llvm"];
476ff2be 56
f035d41b 57/// Crates rustc is allowed to depend on. Avoid adding to the list if possible.
ba9703b0
XL
58///
59/// This list is here to provide a speed-bump to adding a new dependency to
60/// rustc. Please check with the compiler team before adding an entry.
f035d41b 61const PERMITTED_DEPENDENCIES: &[&str] = &[
3dfed10e
XL
62 "addr2line",
63 "adler",
ba9703b0
XL
64 "aho-corasick",
65 "annotate-snippets",
66 "ansi_term",
67 "arrayvec",
68 "atty",
69 "autocfg",
ba9703b0
XL
70 "bitflags",
71 "block-buffer",
72 "block-padding",
ba9703b0 73 "byteorder",
3dfed10e 74 "byte-tools",
ba9703b0
XL
75 "cc",
76 "cfg-if",
f9f354fc 77 "chalk-derive",
f9f354fc 78 "chalk-ir",
ba9703b0
XL
79 "cloudabi",
80 "cmake",
81 "compiler_builtins",
29967ef6 82 "cpuid-bool",
ba9703b0
XL
83 "crc32fast",
84 "crossbeam-deque",
85 "crossbeam-epoch",
86 "crossbeam-queue",
87 "crossbeam-utils",
6a06907d 88 "cstr",
ba9703b0 89 "datafrog",
3dfed10e 90 "difference",
ba9703b0
XL
91 "digest",
92 "dlmalloc",
93 "either",
94 "ena",
95 "env_logger",
3dfed10e 96 "expect-test",
ba9703b0
XL
97 "fake-simd",
98 "filetime",
99 "flate2",
100 "fortanix-sgx-abi",
101 "fuchsia-zircon",
102 "fuchsia-zircon-sys",
103 "generic-array",
104 "getopts",
105 "getrandom",
3dfed10e 106 "gimli",
fc512014 107 "gsgdt",
ba9703b0
XL
108 "hashbrown",
109 "hermit-abi",
110 "humantime",
111 "indexmap",
3dfed10e 112 "instant",
ba9703b0
XL
113 "itertools",
114 "jobserver",
115 "kernel32-sys",
116 "lazy_static",
117 "libc",
118 "libz-sys",
119 "lock_api",
120 "log",
3dfed10e 121 "maybe-uninit",
ba9703b0
XL
122 "md-5",
123 "measureme",
124 "memchr",
125 "memmap",
6a06907d 126 "memmap2",
ba9703b0
XL
127 "memoffset",
128 "miniz_oxide",
ba9703b0 129 "num_cpus",
3dfed10e 130 "object",
f9f354fc 131 "once_cell",
ba9703b0
XL
132 "opaque-debug",
133 "parking_lot",
134 "parking_lot_core",
3dfed10e 135 "pathdiff",
6a06907d
XL
136 "perf-event-open-sys",
137 "pin-project-lite",
ba9703b0
XL
138 "pkg-config",
139 "polonius-engine",
140 "ppv-lite86",
141 "proc-macro2",
f9f354fc 142 "psm",
ba9703b0
XL
143 "punycode",
144 "quick-error",
145 "quote",
146 "rand",
147 "rand_chacha",
148 "rand_core",
149 "rand_hc",
ba9703b0
XL
150 "rand_pcg",
151 "rand_xorshift",
152 "redox_syscall",
ba9703b0
XL
153 "regex",
154 "regex-syntax",
155 "remove_dir_all",
156 "rustc-demangle",
157 "rustc-hash",
158 "rustc-rayon",
159 "rustc-rayon-core",
160 "rustc_version",
161 "scoped-tls",
162 "scopeguard",
163 "semver",
164 "semver-parser",
165 "serde",
166 "serde_derive",
167 "sha-1",
29967ef6 168 "sha2",
ba9703b0 169 "smallvec",
1b1a35ee 170 "snap",
ba9703b0 171 "stable_deref_trait",
f9f354fc 172 "stacker",
ba9703b0
XL
173 "syn",
174 "synstructure",
175 "tempfile",
176 "termcolor",
ba9703b0
XL
177 "termize",
178 "thread_local",
3dfed10e
XL
179 "tracing",
180 "tracing-attributes",
181 "tracing-core",
ba9703b0 182 "typenum",
ba9703b0
XL
183 "unicode-normalization",
184 "unicode-script",
185 "unicode-security",
186 "unicode-width",
187 "unicode-xid",
ba9703b0
XL
188 "vcpkg",
189 "version_check",
190 "wasi",
191 "winapi",
192 "winapi-build",
193 "winapi-i686-pc-windows-gnu",
194 "winapi-util",
195 "winapi-x86_64-pc-windows-gnu",
0531ce1d
XL
196];
197
ba9703b0
XL
198/// Dependency checks.
199///
3dfed10e
XL
200/// `root` is path to the directory with the root `Cargo.toml` (for the workspace). `cargo` is path
201/// to the cargo executable.
202pub fn check(root: &Path, cargo: &Path, bad: &mut bool) {
ba9703b0
XL
203 let mut cmd = cargo_metadata::MetadataCommand::new();
204 cmd.cargo_path(cargo)
3dfed10e 205 .manifest_path(root.join("Cargo.toml"))
ba9703b0
XL
206 .features(cargo_metadata::CargoOpt::AllFeatures);
207 let metadata = t!(cmd.exec());
208 check_exceptions(&metadata, bad);
f035d41b 209 check_dependencies(&metadata, bad);
ba9703b0 210 check_crate_duplicate(&metadata, bad);
0531ce1d
XL
211}
212
ba9703b0
XL
213/// Check that all licenses are in the valid list in `LICENSES`.
214///
215/// Packages listed in `EXCEPTIONS` are allowed for tools.
216fn check_exceptions(metadata: &Metadata, bad: &mut bool) {
217 // Validate the EXCEPTIONS list hasn't changed.
218 for (name, license) in EXCEPTIONS {
219 // Check that the package actually exists.
220 if !metadata.packages.iter().any(|p| p.name == *name) {
fc512014
XL
221 tidy_error!(
222 bad,
ba9703b0
XL
223 "could not find exception package `{}`\n\
224 Remove from EXCEPTIONS list if it is no longer used.",
225 name
226 );
ba9703b0
XL
227 }
228 // Check that the license hasn't changed.
229 for pkg in metadata.packages.iter().filter(|p| p.name == *name) {
230 if pkg.name == "fuchsia-cprng" {
231 // This package doesn't declare a license expression. Manual
232 // inspection of the license file is necessary, which appears
233 // to be BSD-3-Clause.
234 assert!(pkg.license.is_none());
235 continue;
236 }
237 match &pkg.license {
238 None => {
fc512014
XL
239 tidy_error!(
240 bad,
ba9703b0
XL
241 "dependency exception `{}` does not declare a license expression",
242 pkg.id
243 );
ba9703b0
XL
244 }
245 Some(pkg_license) => {
246 if pkg_license.as_str() != *license {
3dfed10e
XL
247 if *name == "crossbeam-queue"
248 && *license == "MIT/Apache-2.0 AND BSD-2-Clause"
249 {
250 // We have two versions of crossbeam-queue and both
251 // are fine.
252 continue;
253 }
254
ba9703b0
XL
255 println!("dependency exception `{}` license has changed", name);
256 println!(" previously `{}` now `{}`", license, pkg_license);
257 println!(" update EXCEPTIONS for the new license");
258 *bad = true;
259 }
260 }
261 }
262 }
0531ce1d 263 }
0531ce1d 264
ba9703b0
XL
265 let exception_names: Vec<_> = EXCEPTIONS.iter().map(|(name, _license)| *name).collect();
266 let runtime_ids = compute_runtime_crates(metadata);
8bb4bdeb 267
ba9703b0
XL
268 // Check if any package does not have a valid license.
269 for pkg in &metadata.packages {
270 if pkg.source.is_none() {
271 // No need to check local packages.
0531ce1d 272 continue;
8bb4bdeb 273 }
ba9703b0
XL
274 if !runtime_ids.contains(&pkg.id) && exception_names.contains(&pkg.name.as_str()) {
275 continue;
276 }
277 let license = match &pkg.license {
278 Some(license) => license,
279 None => {
fc512014 280 tidy_error!(bad, "dependency `{}` does not define a license expression", pkg.id);
ba9703b0
XL
281 continue;
282 }
283 };
284 if !LICENSES.contains(&license.as_str()) {
285 if pkg.name == "fortanix-sgx-abi" {
286 // This is a specific exception because SGX is considered
287 // "third party". See
288 // https://github.com/rust-lang/rust/issues/62620 for more. In
289 // general, these should never be added.
290 continue;
291 }
fc512014 292 tidy_error!(bad, "invalid license `{}` in `{}`", license, pkg.id);
ba9703b0 293 }
476ff2be 294 }
476ff2be
SL
295}
296
f035d41b
XL
297/// Checks the dependency of `RESTRICTED_DEPENDENCY_CRATES` at the given path. Changes `bad` to
298/// `true` if a check failed.
0531ce1d 299///
f035d41b
XL
300/// Specifically, this checks that the dependencies are on the `PERMITTED_DEPENDENCIES`.
301fn check_dependencies(metadata: &Metadata, bad: &mut bool) {
302 // Check that the PERMITTED_DEPENDENCIES does not have unused entries.
303 for name in PERMITTED_DEPENDENCIES {
ba9703b0 304 if !metadata.packages.iter().any(|p| p.name == *name) {
fc512014
XL
305 tidy_error!(
306 bad,
f035d41b
XL
307 "could not find allowed package `{}`\n\
308 Remove from PERMITTED_DEPENDENCIES list if it is no longer used.",
ba9703b0
XL
309 name
310 );
ba9703b0
XL
311 }
312 }
f035d41b
XL
313 // Get the list in a convenient form.
314 let permitted_dependencies: HashSet<_> = PERMITTED_DEPENDENCIES.iter().cloned().collect();
0531ce1d 315
9fa01778 316 // Check dependencies.
0531ce1d
XL
317 let mut visited = BTreeSet::new();
318 let mut unapproved = BTreeSet::new();
f035d41b 319 for &krate in RESTRICTED_DEPENDENCY_CRATES.iter() {
ba9703b0 320 let pkg = pkg_from_name(metadata, krate);
f035d41b
XL
321 let mut bad =
322 check_crate_dependencies(&permitted_dependencies, metadata, &mut visited, pkg);
0531ce1d
XL
323 unapproved.append(&mut bad);
324 }
325
b7449926 326 if !unapproved.is_empty() {
fc512014 327 tidy_error!(bad, "Dependencies not explicitly permitted:");
0531ce1d 328 for dep in unapproved {
ba9703b0 329 println!("* {}", dep);
0531ce1d 330 }
0531ce1d 331 }
0531ce1d
XL
332}
333
334/// Checks the dependencies of the given crate from the given cargo metadata to see if they are on
f035d41b
XL
335/// the list of permitted dependencies. Returns a list of disallowed dependencies.
336fn check_crate_dependencies<'a>(
337 permitted_dependencies: &'a HashSet<&'static str>,
ba9703b0
XL
338 metadata: &'a Metadata,
339 visited: &mut BTreeSet<&'a PackageId>,
340 krate: &'a Package,
341) -> BTreeSet<&'a PackageId> {
9fa01778 342 // This will contain bad deps.
0531ce1d
XL
343 let mut unapproved = BTreeSet::new();
344
9fa01778 345 // Check if we have already visited this crate.
ba9703b0 346 if visited.contains(&krate.id) {
0531ce1d
XL
347 return unapproved;
348 }
349
ba9703b0 350 visited.insert(&krate.id);
0531ce1d 351
f035d41b 352 // If this path is in-tree, we don't require it to be explicitly permitted.
ba9703b0 353 if krate.source.is_some() {
f035d41b
XL
354 // If this dependency is not on `PERMITTED_DEPENDENCIES`, add to bad set.
355 if !permitted_dependencies.contains(krate.name.as_str()) {
ba9703b0 356 unapproved.insert(&krate.id);
0531ce1d
XL
357 }
358 }
359
ba9703b0
XL
360 // Do a DFS in the crate graph.
361 let to_check = deps_of(metadata, &krate.id);
0531ce1d 362
ba9703b0 363 for dep in to_check {
f035d41b 364 let mut bad = check_crate_dependencies(permitted_dependencies, metadata, visited, dep);
0531ce1d
XL
365 unapproved.append(&mut bad);
366 }
367
368 unapproved
369}
b7449926 370
ba9703b0
XL
371/// Prevents multiple versions of some expensive crates.
372fn check_crate_duplicate(metadata: &Metadata, bad: &mut bool) {
b7449926 373 const FORBIDDEN_TO_HAVE_DUPLICATES: &[&str] = &[
9fa01778
XL
374 // These two crates take quite a long time to build, so don't allow two versions of them
375 // to accidentally sneak into our dependency graph, in order to ensure we keep our CI times
376 // under control.
532ac7d7 377 "cargo",
ba9703b0 378 "rustc-ap-rustc_ast",
b7449926 379 ];
b7449926 380
ba9703b0
XL
381 for &name in FORBIDDEN_TO_HAVE_DUPLICATES {
382 let matches: Vec<_> = metadata.packages.iter().filter(|pkg| pkg.name == name).collect();
383 match matches.len() {
384 0 => {
fc512014
XL
385 tidy_error!(
386 bad,
ba9703b0
XL
387 "crate `{}` is missing, update `check_crate_duplicate` \
388 if it is no longer used",
389 name
390 );
ba9703b0
XL
391 }
392 1 => {}
393 _ => {
fc512014
XL
394 tidy_error!(
395 bad,
ba9703b0
XL
396 "crate `{}` is duplicated in `Cargo.lock`, \
397 it is too expensive to build multiple times, \
398 so make sure only one version appears across all dependencies",
399 name
400 );
401 for pkg in matches {
402 println!(" * {}", pkg.id);
403 }
ba9703b0 404 }
b7449926 405 }
ba9703b0
XL
406 }
407}
408
409/// Returns a list of dependencies for the given package.
410fn deps_of<'a>(metadata: &'a Metadata, pkg_id: &'a PackageId) -> Vec<&'a Package> {
411 let resolve = metadata.resolve.as_ref().unwrap();
412 let node = resolve
413 .nodes
414 .iter()
415 .find(|n| &n.id == pkg_id)
416 .unwrap_or_else(|| panic!("could not find `{}` in resolve", pkg_id));
417 node.deps
418 .iter()
419 .map(|dep| {
420 metadata.packages.iter().find(|pkg| pkg.id == dep.pkg).unwrap_or_else(|| {
421 panic!("could not find dep `{}` for pkg `{}` in resolve", dep.pkg, pkg_id)
422 })
423 })
424 .collect()
425}
426
427/// Finds a package with the given name.
428fn pkg_from_name<'a>(metadata: &'a Metadata, name: &'static str) -> &'a Package {
429 let mut i = metadata.packages.iter().filter(|p| p.name == name);
430 let result =
431 i.next().unwrap_or_else(|| panic!("could not find package `{}` in package list", name));
432 assert!(i.next().is_none(), "more than one package found for `{}`", name);
433 result
434}
435
436/// Finds all the packages that are in the rust runtime.
437fn compute_runtime_crates<'a>(metadata: &'a Metadata) -> HashSet<&'a PackageId> {
438 let resolve = metadata.resolve.as_ref().unwrap();
439 let mut result = HashSet::new();
440 for name in RUNTIME_CRATES {
441 let id = &pkg_from_name(metadata, name).id;
442 normal_deps_of_r(resolve, id, &mut result);
443 }
444 result
445}
446
447/// Recursively find all normal dependencies.
448fn normal_deps_of_r<'a>(
449 resolve: &'a Resolve,
450 pkg_id: &'a PackageId,
451 result: &mut HashSet<&'a PackageId>,
452) {
453 if !result.insert(pkg_id) {
454 return;
455 }
456 let node = resolve
457 .nodes
458 .iter()
459 .find(|n| &n.id == pkg_id)
460 .unwrap_or_else(|| panic!("could not find `{}` in resolve", pkg_id));
461 // Don't care about dev-dependencies.
462 // Build dependencies *shouldn't* matter unless they do some kind of
463 // codegen. For now we'll assume they don't.
464 let deps = node.deps.iter().filter(|node_dep| {
465 node_dep
466 .dep_kinds
467 .iter()
468 .any(|kind_info| kind_info.kind == cargo_metadata::DependencyKind::Normal)
469 });
470 for dep in deps {
471 normal_deps_of_r(resolve, &dep.pkg, result);
b7449926
XL
472 }
473}