blob: fd68d5e7fd212b62f3eadfa01396601ec63bdae5 [file] [log] [blame]
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -08001#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
George Burgess IV9e0cfde2022-09-27 15:08:15 -07003# Copyright 2021 The ChromiumOS Authors
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -08004# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6""" This script cleans up the vendor directory.
7"""
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -07008import argparse
George Burgess IV635f7262022-08-09 21:32:20 -07009import collections
George Burgess IVfb0a1c42022-11-15 13:47:19 -070010import functools
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +000011import hashlib
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -080012import json
13import os
14import pathlib
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -070015import re
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -070016import shutil
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +000017import subprocess
George Burgess IV56e384a2023-01-25 16:07:53 -070018import sys
George Burgess IV04833702022-08-09 22:00:38 -070019import textwrap
George Burgess IVb4d0eaa2023-02-06 09:39:57 -070020
Abhishek Pandit-Subedice0f5b22021-09-10 15:50:08 -070021import toml
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +000022
George Burgess IVb4d0eaa2023-02-06 09:39:57 -070023
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -070024# We only care about crates we're actually going to use and that's usually
25# limited to ones with cfg(linux). For running `cargo metadata`, limit results
George Burgess IVfb0a1c42022-11-15 13:47:19 -070026# to only these platforms.
27ALL_SUPPORTED_PLATFORMS = (
28 # Main targets.
29 "x86_64-cros-linux-gnu",
30 "armv7a-cros-linux-gnueabihf",
31 "aarch64-cros-linux-gnu",
32 # As far as we care, this is the same as x86_64-cros-linux-gnu.
33 # "x86_64-pc-linux-gnu",
34 # Baremetal targets.
35 "thumbv6m-none-eabi",
36 "thumbv7m-none-eabi",
37 "thumbv7em-none-eabihf",
38 "i686-unknown-uefi",
39 "x86_64-unknown-uefi",
40)
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -070041
George Burgess IV8e2cc042022-10-18 14:50:48 -060042# A series of crates which are to be made empty by having no (non-comment)
43# contents in their `lib.rs`, rather than by inserting a compilation error.
44NOP_EMPTY_CRATES = frozenset({"windows"})
45
46EMPTY_CRATE_BODY = """\
47compile_error!("This crate cannot be built for this configuration.");
48"""
49NOP_EMPTY_CRATE_BODY = "// " + EMPTY_CRATE_BODY
50
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +000051
52def _rerun_checksums(package_path):
53 """Re-run checksums for given package.
54
55 Writes resulting checksums to $package_path/.cargo-checksum.json.
56 """
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -070057 hashes = dict()
George Burgess IV7dffc252022-08-31 14:37:01 -070058 checksum_path = os.path.join(package_path, ".cargo-checksum.json")
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +000059 if not pathlib.Path(checksum_path).is_file():
60 return False
61
George Burgess IV7dffc252022-08-31 14:37:01 -070062 with open(checksum_path, "r") as fread:
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +000063 contents = json.load(fread)
64
65 for root, _, files in os.walk(package_path, topdown=True):
66 for f in files:
67 # Don't checksum an existing checksum file
68 if f == ".cargo-checksum.json":
69 continue
70
71 file_path = os.path.join(root, f)
George Burgess IV7dffc252022-08-31 14:37:01 -070072 with open(file_path, "rb") as frb:
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +000073 m = hashlib.sha256()
74 m.update(frb.read())
75 d = m.hexdigest()
76
77 # Key is relative to the package path so strip from beginning
78 key = os.path.relpath(file_path, package_path)
79 hashes[key] = d
80
81 if hashes:
George Burgess IV7dffc252022-08-31 14:37:01 -070082 print(
83 "{} regenerated {} hashes".format(package_path, len(hashes.keys()))
84 )
85 contents["files"] = hashes
86 with open(checksum_path, "w") as fwrite:
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -070087 json.dump(contents, fwrite, sort_keys=True)
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +000088
89 return True
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -080090
91
92def _remove_OWNERS_checksum(root):
George Burgess IV7dffc252022-08-31 14:37:01 -070093 """Delete all OWNERS files from the checksum file.
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -080094
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +000095 Args:
96 root: Root directory for the vendored crate.
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -080097
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +000098 Returns:
99 True if OWNERS was found and cleaned up. Otherwise False.
100 """
George Burgess IV7dffc252022-08-31 14:37:01 -0700101 checksum_path = os.path.join(root, ".cargo-checksum.json")
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -0800102 if not pathlib.Path(checksum_path).is_file():
103 return False
104
George Burgess IV7dffc252022-08-31 14:37:01 -0700105 with open(checksum_path, "r") as fread:
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -0800106 contents = json.load(fread)
107
108 del_keys = []
George Burgess IV7dffc252022-08-31 14:37:01 -0700109 for cfile in contents["files"]:
110 if "OWNERS" in cfile:
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -0800111 del_keys.append(cfile)
112
113 for key in del_keys:
George Burgess IV7dffc252022-08-31 14:37:01 -0700114 del contents["files"][key]
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -0800115
116 if del_keys:
George Burgess IV7dffc252022-08-31 14:37:01 -0700117 print("{} deleted: {}".format(root, del_keys))
118 with open(checksum_path, "w") as fwrite:
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700119 json.dump(contents, fwrite, sort_keys=True)
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -0800120
121 return bool(del_keys)
122
123
124def cleanup_owners(vendor_path):
George Burgess IV7dffc252022-08-31 14:37:01 -0700125 """Remove owners checksums from the vendor directory.
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -0800126
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000127 We currently do not check in the OWNERS files from vendored crates because
128 they interfere with the find-owners functionality in gerrit. This cleanup
129 simply finds all instances of "OWNERS" in the checksum files within and
130 removes them.
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -0800131
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000132 Args:
133 vendor_path: Absolute path to vendor directory.
134 """
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -0800135 deps_cleaned = []
136 for root, dirs, _ in os.walk(vendor_path):
137 for d in dirs:
138 removed = _remove_OWNERS_checksum(os.path.join(root, d))
139 if removed:
140 deps_cleaned.append(d)
141
142 if deps_cleaned:
George Burgess IV7dffc252022-08-31 14:37:01 -0700143 print("Cleanup owners:\n {}".format("\n".join(deps_cleaned)))
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -0800144
145
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000146def apply_single_patch(patch, workdir):
147 """Apply a single patch and return whether it was successful.
148
149 Returns:
150 True if successful. False otherwise.
151 """
George Burgess IV08664ba2022-10-03 11:09:33 -0700152 proc = subprocess.run(
153 [
154 "patch",
155 "-p1",
156 "--no-backup-if-mismatch",
157 "-i",
158 patch,
159 ],
160 cwd=workdir,
161 )
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000162 return proc.returncode == 0
163
164
George Burgess IV30c5c362022-08-19 17:05:02 -0700165def apply_patch_script(script, workdir):
166 """Run the given patch script, returning whether it exited cleanly.
167
168 Returns:
169 True if successful. False otherwise.
170 """
171 return subprocess.run([script], cwd=workdir).returncode == 0
172
173
George Burgess IV635f7262022-08-09 21:32:20 -0700174def determine_vendor_crates(vendor_path):
175 """Returns a map of {crate_name: [directory]} at the given vendor_path."""
176 result = collections.defaultdict(list)
George Burgess IV76b60d02022-10-26 17:44:48 -0600177 crate_version_re = re.compile(r"-\d+\.\d+\.\d+(:?[+-]|$)")
George Burgess IV635f7262022-08-09 21:32:20 -0700178 for crate_name_plus_ver in os.listdir(vendor_path):
George Burgess IV76b60d02022-10-26 17:44:48 -0600179 version = crate_version_re.search(crate_name_plus_ver)
180 assert version, crate_name_plus_ver
181 name = crate_name_plus_ver[: version.start()]
George Burgess IV40cc91c2022-08-15 13:07:40 -0700182 result[name].append(crate_name_plus_ver)
George Burgess IV635f7262022-08-09 21:32:20 -0700183
184 for crate_list in result.values():
George Burgess IV40cc91c2022-08-15 13:07:40 -0700185 crate_list.sort()
George Burgess IV635f7262022-08-09 21:32:20 -0700186 return result
187
188
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000189def apply_patches(patches_path, vendor_path):
190 """Finds patches and applies them to sub-folders in the vendored crates.
191
192 Args:
193 patches_path: Path to folder with patches. Expect all patches to be one
194 level down (matching the crate name).
195 vendor_path: Root path to vendored crates directory.
196 """
197 checksums_for = {}
198
199 # Don't bother running if patches directory is empty
200 if not pathlib.Path(patches_path).is_dir():
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700201 return
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000202
George Burgess IV30c5c362022-08-19 17:05:02 -0700203 patches_failed = False
George Burgess IV635f7262022-08-09 21:32:20 -0700204 vendor_crate_map = determine_vendor_crates(vendor_path)
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000205 # Look for all patches and apply them
206 for d in os.listdir(patches_path):
207 dir_path = os.path.join(patches_path, d)
208
209 # We don't process patches in root dir
210 if not os.path.isdir(dir_path):
211 continue
212
George Burgess IV30c5c362022-08-19 17:05:02 -0700213 # We accept one of two forms here:
214 # - direct targets (these name # `${crate_name}-${version}`)
215 # - simply the crate name (which applies to all versions of the
216 # crate)
217 direct_target = os.path.join(vendor_path, d)
218 if os.path.isdir(direct_target):
219 patch_targets = [d]
220 elif d in vendor_crate_map:
221 patch_targets = vendor_crate_map[d]
222 else:
George Burgess IV7dffc252022-08-31 14:37:01 -0700223 raise RuntimeError(f"Unknown crate in {vendor_path}: {d}")
George Burgess IV30c5c362022-08-19 17:05:02 -0700224
George Burgess IV635f7262022-08-09 21:32:20 -0700225 for patch in os.listdir(dir_path):
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000226 file_path = os.path.join(dir_path, patch)
227
228 # Skip if not a patch file
George Burgess IV30c5c362022-08-19 17:05:02 -0700229 if not os.path.isfile(file_path):
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000230 continue
231
George Burgess IV30c5c362022-08-19 17:05:02 -0700232 if patch.endswith(".patch"):
233 apply = apply_single_patch
234 elif os.access(file_path, os.X_OK):
235 apply = apply_patch_script
George Burgess IV635f7262022-08-09 21:32:20 -0700236 else:
George Burgess IV30c5c362022-08-19 17:05:02 -0700237 # Unrecognized. Skip it.
238 continue
239
240 for target_name in patch_targets:
241 checksums_for[target_name] = True
242 target = os.path.join(vendor_path, target_name)
243 print(f"-- Applying {file_path} to {target}")
244 if not apply(file_path, target):
245 print(f"Failed to apply {file_path} to {target}")
246 patches_failed = True
247
248 # Do this late, so we can report all of the failing patches in one
249 # invocation.
250 if patches_failed:
George Burgess IV7dffc252022-08-31 14:37:01 -0700251 raise ValueError("Patches failed; please see above logs")
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000252
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000253 # Re-run checksums for all modified packages since we applied patches.
254 for key in checksums_for.keys():
255 _rerun_checksums(os.path.join(vendor_path, key))
256
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700257
George Burgess IV18af5632022-08-30 14:10:53 -0700258def get_workspace_cargo_toml(working_dir):
George Burgess IV40cc91c2022-08-15 13:07:40 -0700259 """Returns all Cargo.toml files under working_dir."""
George Burgess IV7dffc252022-08-31 14:37:01 -0700260 return [working_dir / "projects" / "Cargo.toml"]
George Burgess IV40cc91c2022-08-15 13:07:40 -0700261
262
Abhishek Pandit-Subedifa902382021-08-20 11:04:33 -0700263def run_cargo_vendor(working_dir):
264 """Runs cargo vendor.
265
266 Args:
267 working_dir: Directory to run inside. This should be the directory where
Abhishek Pandit-Subedice0f5b22021-09-10 15:50:08 -0700268 Cargo.toml is kept.
Abhishek Pandit-Subedifa902382021-08-20 11:04:33 -0700269 """
George Burgess IVfb0a1c42022-11-15 13:47:19 -0700270 # `cargo vendor` may update dependencies (which may update metadata).
271 load_all_package_metadata.cache_clear()
272
George Burgess IV635f7262022-08-09 21:32:20 -0700273 # Cargo will refuse to revendor into versioned directories, which leads to
274 # repeated `./vendor.py` invocations trying to apply patches to
275 # already-patched sources. Remove the existing vendor directory to avoid
276 # this.
George Burgess IV7dffc252022-08-31 14:37:01 -0700277 vendor_dir = working_dir / "vendor"
George Burgess IV635f7262022-08-09 21:32:20 -0700278 if vendor_dir.exists():
George Burgess IV40cc91c2022-08-15 13:07:40 -0700279 shutil.rmtree(vendor_dir)
280
George Burgess IV18af5632022-08-30 14:10:53 -0700281 cargo_cmdline = [
George Burgess IV7dffc252022-08-31 14:37:01 -0700282 "cargo",
283 "vendor",
284 "--versioned-dirs",
285 "-v",
286 "--manifest-path=projects/Cargo.toml",
287 "--",
288 "vendor",
George Burgess IV18af5632022-08-30 14:10:53 -0700289 ]
George Burgess IV40cc91c2022-08-15 13:07:40 -0700290 subprocess.check_call(cargo_cmdline, cwd=working_dir)
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000291
Abhishek Pandit-Subedice0f5b22021-09-10 15:50:08 -0700292
George Burgess IVfb0a1c42022-11-15 13:47:19 -0700293def load_single_metadata(working_dir, filter_platform):
George Burgess IV40cc91c2022-08-15 13:07:40 -0700294 """Load metadata for all projects under a given directory.
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700295
296 Args:
George Burgess IV40cc91c2022-08-15 13:07:40 -0700297 working_dir: Base directory to run from.
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700298 filter_platform: Filter packages to ones configured for this platform.
299 """
George Burgess IV40cc91c2022-08-15 13:07:40 -0700300 metadata_objects = []
George Burgess IV18af5632022-08-30 14:10:53 -0700301 cmd = [
George Burgess IV7dffc252022-08-31 14:37:01 -0700302 "cargo",
303 "metadata",
304 "--format-version=1",
305 "--manifest-path=projects/Cargo.toml",
George Burgess IV18af5632022-08-30 14:10:53 -0700306 ]
307 # Conditionally add platform filter
308 if filter_platform:
309 cmd += ("--filter-platform", filter_platform)
310 output = subprocess.check_output(cmd, cwd=working_dir)
311 return json.loads(output)
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700312
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700313
George Burgess IVfb0a1c42022-11-15 13:47:19 -0700314# Calls to this are somewhat expensive, and repeated a fair few times
315# throughout `./vendor.py`. Measuring locally, having a cache here speeds this
316# script up by 1.4x.
317@functools.lru_cache()
318def load_all_package_metadata(working_dir, platforms=ALL_SUPPORTED_PLATFORMS):
319 """Loads and merges metadata for all platforms in `platforms`.
320
321 This drops a lot of data from `cargo metadata`. Some of this metadata is
322 hard to merge, other bits of it just aren't worth keeping at the moment.
323 """
324 assert platforms, f"`platforms` should have things; has {platforms}"
325
326 found_package_ids = set()
327 results = []
328 for platform in platforms:
329 metadata = load_single_metadata(working_dir, platform)["packages"]
330 for package in metadata:
331 package_id = package["id"]
332 if package_id in found_package_ids:
333 continue
334
335 found_package_ids.add(package_id)
336 results.append(
337 {
338 "id": package["id"],
339 "license": package["license"],
340 "license_file": package["license_file"],
341 "name": package["name"],
342 "version": package["version"],
343 }
344 )
345
346 return results
347
348
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700349class LicenseManager:
George Burgess IV7dffc252022-08-31 14:37:01 -0700350 """Manage consolidating licenses for all packages."""
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700351
George Burgess IV124e6a12022-09-09 10:44:29 -0700352 # These are all the licenses we support. Keys are what is seen in metadata
353 # and values are what is expected by ebuilds.
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700354 SUPPORTED_LICENSES = {
George Burgess IV7dffc252022-08-31 14:37:01 -0700355 "0BSD": "0BSD",
356 "Apache-2.0": "Apache-2.0",
George Burgess IVb16816a2022-10-26 17:55:48 -0600357 "BSD-2-Clause": "BSD-2",
George Burgess IV7dffc252022-08-31 14:37:01 -0700358 "BSD-3-Clause": "BSD-3",
359 "ISC": "ISC",
360 "MIT": "MIT",
361 "MPL-2.0": "MPL-2.0",
362 "unicode": "unicode",
Dan Callaghan91f80542022-09-09 10:57:23 +1000363 "Zlib": "ZLIB",
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700364 }
365
366 # Prefer to take attribution licenses in this order. All these require that
367 # we actually use the license file found in the package so they MUST have
368 # a license file set.
George Burgess IV7dffc252022-08-31 14:37:01 -0700369 PREFERRED_ATTRIB_LICENSE_ORDER = ["MIT", "BSD-3", "ISC"]
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700370
371 # If Apache license is found, always prefer it (simplifies attribution)
George Burgess IV7dffc252022-08-31 14:37:01 -0700372 APACHE_LICENSE = "Apache-2.0"
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700373
374 # Regex for license files found in the vendored directories. Search for
375 # these files with re.IGNORECASE.
376 #
377 # These will be searched in order with the earlier entries being preferred.
378 LICENSE_NAMES_REGEX = [
George Burgess IV7dffc252022-08-31 14:37:01 -0700379 r"^license-mit$",
380 r"^copyright$",
381 r"^licen[cs]e.*$",
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700382 ]
383
384 # Some crates have their license file in other crates. This usually occurs
385 # because multiple crates are published from the same git repository and the
386 # license isn't updated in each sub-crate. In these cases, we can just
387 # ignore these packages.
388 MAP_LICENSE_TO_OTHER = {
George Burgess IV7dffc252022-08-31 14:37:01 -0700389 "failure_derive": "failure",
390 "grpcio-compiler": "grpcio",
391 "grpcio-sys": "grpcio",
Li-Yu Yu89d93c72022-12-19 03:36:50 +0800392 "protobuf-codegen": "protobuf",
393 "protobuf-parse": "protobuf",
394 "protobuf-support": "protobuf",
George Burgess IV7dffc252022-08-31 14:37:01 -0700395 "rustyline-derive": "rustyline",
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700396 }
397
398 # Map a package to a specific license and license file. Only use this if
399 # a package doesn't have an easily discoverable license or exports its
400 # license in a weird way. Prefer to patch the project with a license and
401 # upstream the patch instead.
402 STATIC_LICENSE_MAP = {
George Burgess IVb16816a2022-10-26 17:55:48 -0600403 # "package name": ("license name", "license file relative location")
George Burgess IV26642872022-10-18 19:46:58 -0600404 # Patch for adding these are upstream, but the patch application
405 # doesn't apply to `cargo metadata`. This is presumably because it
406 # can't detect our vendor directory.
George Burgess IVf4a5e362022-08-30 14:30:36 -0700407 # https://gitlab.freedesktop.org/slirp/libslirp-sys/-/merge_requests/6
George Burgess IV7dffc252022-08-31 14:37:01 -0700408 "libslirp-sys": ("MIT", "LICENSE"),
George Burgess IV26642872022-10-18 19:46:58 -0600409 # https://gitlab.freedesktop.org/anholt/deqp-runner/-/merge_requests/48
410 "deqp-runner": ("MIT", "LICENSE"),
Dan Callaghan91f80542022-09-09 10:57:23 +1000411 # Upstream prefers to embed license text inside README.md:
412 "riscv": ("ISC", "README.md"),
413 "riscv-rt": ("ISC", "README.md"),
George Burgess IVb16816a2022-10-26 17:55:48 -0600414 "zerocopy": ("BSD-2", "LICENSE"),
415 "zerocopy-derive": ("BSD-2", "LICENSE"),
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700416 }
417
418 def __init__(self, working_dir, vendor_dir):
419 self.working_dir = working_dir
420 self.vendor_dir = vendor_dir
421
422 def _find_license_in_dir(self, search_dir):
423 for p in os.listdir(search_dir):
424 # Ignore anything that's not a file
425 if not os.path.isfile(os.path.join(search_dir, p)):
426 continue
427
428 # Now check if the name matches any of the regexes
429 # We'll return the first matching file.
430 for regex in self.LICENSE_NAMES_REGEX:
431 if re.search(regex, p, re.IGNORECASE):
432 yield os.path.join(search_dir, p)
433 break
434
435 def _guess_license_type(self, license_file):
George Burgess IV7dffc252022-08-31 14:37:01 -0700436 if "-MIT" in license_file:
437 return "MIT"
438 elif "-APACHE" in license_file:
439 return "APACHE"
440 elif "-BSD" in license_file:
441 return "BSD-3"
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700442
George Burgess IV7dffc252022-08-31 14:37:01 -0700443 with open(license_file, "r") as f:
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700444 lines = f.read()
George Burgess IV7dffc252022-08-31 14:37:01 -0700445 if "MIT" in lines:
446 return "MIT"
447 elif "Apache" in lines:
448 return "APACHE"
449 elif "BSD 3-Clause" in lines:
450 return "BSD-3"
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700451
George Burgess IV7dffc252022-08-31 14:37:01 -0700452 return ""
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700453
George Burgess IV7dffc252022-08-31 14:37:01 -0700454 def generate_license(
455 self, skip_license_check, print_map_to_file, license_shorthand_file
456 ):
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700457 """Generate single massive license file from metadata."""
George Burgess IVfb0a1c42022-11-15 13:47:19 -0700458 metadata = load_all_package_metadata(self.working_dir)
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700459
George Burgess IVb16816a2022-10-26 17:55:48 -0600460 special_unicode_license = "(MIT OR Apache-2.0) AND Unicode-DFS-2016"
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700461 bad_licenses = {}
462
463 # Keep license map ordered so it generates a consistent license map
464 license_map = {}
465
466 skip_license_check = skip_license_check or []
George Burgess IV4ae42062022-08-15 18:54:51 -0700467 has_unicode_license = False
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700468
George Burgess IVfb0a1c42022-11-15 13:47:19 -0700469 for package in metadata:
George Burgess IV40cc91c2022-08-15 13:07:40 -0700470 # Skip the synthesized Cargo.toml packages that exist solely to
471 # list dependencies.
George Burgess IV7dffc252022-08-31 14:37:01 -0700472 if "path+file:///" in package["id"]:
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700473 continue
474
George Burgess IV7dffc252022-08-31 14:37:01 -0700475 pkg_name = package["name"]
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700476 if pkg_name in skip_license_check:
477 print(
George Burgess IV7dffc252022-08-31 14:37:01 -0700478 "Skipped license check on {}. Reason: Skipped from command line".format(
479 pkg_name
480 )
481 )
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700482 continue
483
484 if pkg_name in self.MAP_LICENSE_TO_OTHER:
485 print(
George Burgess IV7dffc252022-08-31 14:37:01 -0700486 "Skipped license check on {}. Reason: License already in {}".format(
487 pkg_name, self.MAP_LICENSE_TO_OTHER[pkg_name]
488 )
489 )
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700490 continue
491
492 # Check if we have a static license map for this package. Use the
493 # static values if we have it already set.
494 if pkg_name in self.STATIC_LICENSE_MAP:
George Burgess IVb16816a2022-10-26 17:55:48 -0600495 license, license_file = self.STATIC_LICENSE_MAP[pkg_name]
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700496 license_map[pkg_name] = {
497 "license": license,
498 "license_file": license_file,
499 }
500 continue
501
502 license_files = []
George Burgess IV93ba4732022-08-13 14:10:10 -0700503 # use `or ''` instead of get's default, since `package` may have a
504 # None value for 'license'.
George Burgess IV7dffc252022-08-31 14:37:01 -0700505 license = package.get("license") or ""
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700506
507 # We ignore the metadata for license file because most crates don't
508 # have it set. Just scan the source for licenses.
George Burgess IV7dffc252022-08-31 14:37:01 -0700509 pkg_version = package["version"]
510 license_files = list(
511 self._find_license_in_dir(
512 os.path.join(self.vendor_dir, f"{pkg_name}-{pkg_version}")
513 )
514 )
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700515
George Burgess IV4ae42062022-08-15 18:54:51 -0700516 # FIXME(b/240953811): The code later in this loop is only
517 # structured to handle ORs, not ANDs. Fortunately, this license in
518 # particular is `AND`ed between a super common license (Apache) and
519 # a more obscure one (unicode). This hack is specifically intended
520 # for the `unicode-ident` crate, though no crate name check is
521 # made, since it's OK other crates happen to have this license.
George Burgess IVb16816a2022-10-26 17:55:48 -0600522 if license == special_unicode_license:
George Burgess IV4ae42062022-08-15 18:54:51 -0700523 has_unicode_license = True
524 # We'll check later to be sure MIT or Apache-2.0 is represented
525 # properly.
526 for x in license_files:
George Burgess IV7dffc252022-08-31 14:37:01 -0700527 if os.path.basename(x) == "LICENSE-UNICODE":
George Burgess IV4ae42062022-08-15 18:54:51 -0700528 license_file = x
529 break
530 else:
George Burgess IV7dffc252022-08-31 14:37:01 -0700531 raise ValueError(
532 "No LICENSE-UNICODE found in " f"{license_files}"
533 )
George Burgess IV4ae42062022-08-15 18:54:51 -0700534 license_map[pkg_name] = {
535 "license": license,
536 "license_file": license_file,
537 }
George Burgess IV4ae42062022-08-15 18:54:51 -0700538 continue
539
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700540 # If there are multiple licenses, they are delimited with "OR" or "/"
George Burgess IV7dffc252022-08-31 14:37:01 -0700541 delim = " OR " if " OR " in license else "/"
George Burgess IV40cc91c2022-08-15 13:07:40 -0700542 found = [x.strip() for x in license.split(delim)]
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700543
544 # Filter licenses to ones we support
545 licenses_or = [
George Burgess IV7dffc252022-08-31 14:37:01 -0700546 self.SUPPORTED_LICENSES[f]
547 for f in found
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700548 if f in self.SUPPORTED_LICENSES
549 ]
550
551 # If apache license is found, always prefer it because it simplifies
552 # license attribution (we can use existing Apache notice)
553 if self.APACHE_LICENSE in licenses_or:
George Burgess IV7dffc252022-08-31 14:37:01 -0700554 license_map[pkg_name] = {"license": self.APACHE_LICENSE}
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700555
556 # Handle single license that has at least one license file
557 # We pick the first license file and the license
558 elif len(licenses_or) == 1:
559 if license_files:
560 l = licenses_or[0]
561 lf = license_files[0]
562
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700563 license_map[pkg_name] = {
George Burgess IV7dffc252022-08-31 14:37:01 -0700564 "license": l,
565 "license_file": os.path.relpath(lf, self.working_dir),
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700566 }
567 else:
568 bad_licenses[pkg_name] = "{} missing license file".format(
George Burgess IV7dffc252022-08-31 14:37:01 -0700569 licenses_or[0]
570 )
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700571 # Handle multiple licenses
572 elif len(licenses_or) > 1:
573 # Check preferred licenses in order
574 license_found = False
575 for l in self.PREFERRED_ATTRIB_LICENSE_ORDER:
576 if not l in licenses_or:
577 continue
578
579 for f in license_files:
580 if self._guess_license_type(f) == l:
581 license_found = True
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700582 license_map[pkg_name] = {
George Burgess IV7dffc252022-08-31 14:37:01 -0700583 "license": l,
584 "license_file": os.path.relpath(
585 f, self.working_dir
586 ),
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700587 }
588 break
589
590 # Break out of loop if license is found
591 if license_found:
592 break
593 else:
594 bad_licenses[pkg_name] = license
595
596 # If we had any bad licenses, we need to abort
597 if bad_licenses:
598 for k in bad_licenses.keys():
George Burgess IV7dffc252022-08-31 14:37:01 -0700599 print(
600 "{} had no acceptable licenses: {}".format(
601 k, bad_licenses[k]
602 )
603 )
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700604 raise Exception("Bad licenses in vendored packages.")
605
606 # Write license map to file
607 if print_map_to_file:
George Burgess IV7dffc252022-08-31 14:37:01 -0700608 with open(
609 os.path.join(self.working_dir, print_map_to_file), "w"
610 ) as lfile:
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700611 json.dump(license_map, lfile, sort_keys=True)
612
613 # Raise missing licenses unless we have a valid reason to ignore them
614 raise_missing_license = False
615 for name, v in license_map.items():
George Burgess IV7dffc252022-08-31 14:37:01 -0700616 if (
617 "license_file" not in v
618 and v.get("license", "") != self.APACHE_LICENSE
619 ):
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700620 raise_missing_license = True
George Burgess IV7dffc252022-08-31 14:37:01 -0700621 print(
622 " {}: Missing license file. Fix or add to ignorelist.".format(
623 name
624 )
625 )
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700626
627 if raise_missing_license:
628 raise Exception(
629 "Unhandled missing license file. "
George Burgess IV7dffc252022-08-31 14:37:01 -0700630 "Make sure all are accounted for before continuing."
631 )
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700632
George Burgess IVb16816a2022-10-26 17:55:48 -0600633 has_license_types = {x["license"] for x in license_map.values()}
George Burgess IV4ae42062022-08-15 18:54:51 -0700634 if has_unicode_license:
George Burgess IVb16816a2022-10-26 17:55:48 -0600635 # Replace this license with the actual SPDX license we plan to use.
636 has_license_types.remove(special_unicode_license)
637 has_license_types.add("unicode")
George Burgess IV4ae42062022-08-15 18:54:51 -0700638 if self.APACHE_LICENSE not in has_license_types:
George Burgess IV7dffc252022-08-31 14:37:01 -0700639 raise ValueError(
640 "Need the apache license; currently have: "
641 f"{sorted(has_license_types)}"
642 )
George Burgess IV4ae42062022-08-15 18:54:51 -0700643
George Burgess IV04833702022-08-09 22:00:38 -0700644 sorted_licenses = sorted(has_license_types)
George Burgess IV124e6a12022-09-09 10:44:29 -0700645 print("The following licenses are in use:", sorted_licenses)
George Burgess IV7dffc252022-08-31 14:37:01 -0700646 header = textwrap.dedent(
647 """\
George Burgess IV04833702022-08-09 22:00:38 -0700648 # File to describe the licenses used by this registry.
Daniel Verkampd9d085b2022-09-07 10:52:27 -0700649 # Used so it's easy to automatically verify ebuilds are updated.
George Burgess IV04833702022-08-09 22:00:38 -0700650 # Each line is a license. Lines starting with # are comments.
George Burgess IV7dffc252022-08-31 14:37:01 -0700651 """
652 )
653 with open(license_shorthand_file, "w", encoding="utf-8") as f:
George Burgess IV04833702022-08-09 22:00:38 -0700654 f.write(header)
George Burgess IV7dffc252022-08-31 14:37:01 -0700655 f.write("\n".join(sorted_licenses))
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700656
657
George Burgess IVd0261472022-10-17 18:59:10 -0600658def clean_source_related_lines_in_place(cargo_toml):
659 """Removes all [[bin]] (and similar) sections in `cargo_toml`."""
660 cargo_toml.pop("bench", None)
661 cargo_toml.pop("bin", None)
662 cargo_toml.pop("examples", None)
663 cargo_toml.pop("test", None)
664
665 lib = cargo_toml.get("lib")
666 if lib:
667 lib.pop("path", None)
668
669 package = cargo_toml.get("package")
670 if package:
671 package.pop("build", None)
672 package.pop("default-run", None)
673 package.pop("include", None)
674
675
George Burgess IVd4ff0502022-08-14 23:27:57 -0700676def clean_features_in_place(cargo_toml):
677 """Removes all side-effects of features in `cargo_toml`."""
George Burgess IV7dffc252022-08-31 14:37:01 -0700678 features = cargo_toml.get("features")
George Burgess IVd4ff0502022-08-14 23:27:57 -0700679 if not features:
680 return
681
George Burgess IVd0261472022-10-17 18:59:10 -0600682 for name in features:
683 features[name] = []
George Burgess IVd4ff0502022-08-14 23:27:57 -0700684
685
George Burgess IVd0261472022-10-17 18:59:10 -0600686def remove_all_dependencies_in_place(cargo_toml):
George Burgess IVd4ff0502022-08-14 23:27:57 -0700687 """Removes all `target.*.dependencies` from `cargo_toml`."""
George Burgess IVd0261472022-10-17 18:59:10 -0600688 cargo_toml.pop("build-dependencies", None)
689 cargo_toml.pop("dependencies", None)
690 cargo_toml.pop("dev-dependencies", None)
691
George Burgess IV7dffc252022-08-31 14:37:01 -0700692 target = cargo_toml.get("target")
George Burgess IVd4ff0502022-08-14 23:27:57 -0700693 if not target:
694 return
George Burgess IV0313d782022-08-15 23:45:44 -0700695
George Burgess IVd4ff0502022-08-14 23:27:57 -0700696 empty_keys = []
697 for key, values in target.items():
George Burgess IVd0261472022-10-17 18:59:10 -0600698 values.pop("build-dependencies", None)
George Burgess IV7dffc252022-08-31 14:37:01 -0700699 values.pop("dependencies", None)
700 values.pop("dev-dependencies", None)
George Burgess IVd4ff0502022-08-14 23:27:57 -0700701 if not values:
702 empty_keys.append(key)
George Burgess IV0313d782022-08-15 23:45:44 -0700703
George Burgess IVd4ff0502022-08-14 23:27:57 -0700704 if len(empty_keys) == len(target):
George Burgess IV7dffc252022-08-31 14:37:01 -0700705 del cargo_toml["target"]
George Burgess IVd4ff0502022-08-14 23:27:57 -0700706 else:
707 for key in empty_keys:
708 del target[key]
George Burgess IV0313d782022-08-15 23:45:44 -0700709
710
George Burgess IV7dffc252022-08-31 14:37:01 -0700711class CrateDestroyer:
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700712 def __init__(self, working_dir, vendor_dir):
713 self.working_dir = working_dir
714 self.vendor_dir = vendor_dir
715
716 def _modify_cargo_toml(self, pkg_path):
George Burgess IV7dffc252022-08-31 14:37:01 -0700717 with open(os.path.join(pkg_path, "Cargo.toml"), "r") as cargo:
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700718 contents = toml.load(cargo)
719
George Burgess IV7dffc252022-08-31 14:37:01 -0700720 package = contents["package"]
George Burgess IVd4ff0502022-08-14 23:27:57 -0700721
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700722 # Change description, license and delete license key
George Burgess IV7dffc252022-08-31 14:37:01 -0700723 package["description"] = "Empty crate that should not build."
724 package["license"] = "Apache-2.0"
George Burgess IVd4ff0502022-08-14 23:27:57 -0700725
George Burgess IV7dffc252022-08-31 14:37:01 -0700726 package.pop("license_file", None)
George Burgess IVd4ff0502022-08-14 23:27:57 -0700727 # If there's no build.rs but we specify `links = "foo"`, Cargo gets
728 # upset.
George Burgess IV7dffc252022-08-31 14:37:01 -0700729 package.pop("links", None)
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700730
George Burgess IV0313d782022-08-15 23:45:44 -0700731 # Some packages have cfg-specific dependencies. Remove them here; we
732 # don't care about the dependencies of an empty package.
733 #
734 # This is a load-bearing optimization: `dev-python/toml` doesn't
735 # always round-trip dumps(loads(x)) correctly when `x` has keys with
736 # strings (b/242589711#comment3). The place this has bitten us so far
737 # is target dependencies, which can be harmlessly removed for now.
George Burgess IVd4ff0502022-08-14 23:27:57 -0700738 #
739 # Cleaning features in-place is also necessary, since we're removing
740 # dependencies, and a feature can enable features in dependencies.
741 # Cargo errors out on `[features] foo = "bar/baz"` if `bar` isn't a
742 # dependency.
743 clean_features_in_place(contents)
George Burgess IVd0261472022-10-17 18:59:10 -0600744 remove_all_dependencies_in_place(contents)
745
746 # Since we're removing all source files, also be sure to remove
747 # source-related keys.
748 clean_source_related_lines_in_place(contents)
George Burgess IV0313d782022-08-15 23:45:44 -0700749
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700750 with open(os.path.join(pkg_path, "Cargo.toml"), "w") as cargo:
751 toml.dump(contents, cargo)
752
George Burgess IV8e2cc042022-10-18 14:50:48 -0600753 def _replace_source_contents(self, package_path, compile_error):
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700754 # First load the checksum file before starting
755 checksum_file = os.path.join(package_path, ".cargo-checksum.json")
George Burgess IV7dffc252022-08-31 14:37:01 -0700756 with open(checksum_file, "r") as csum:
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700757 checksum_contents = json.load(csum)
758
759 # Also load the cargo.toml file which we need to write back
760 cargo_file = os.path.join(package_path, "Cargo.toml")
George Burgess IV7dffc252022-08-31 14:37:01 -0700761 with open(cargo_file, "rb") as cfile:
George Burgess IV3e344e42022-08-09 21:07:04 -0700762 cargo_contents = cfile.read()
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700763
764 shutil.rmtree(package_path)
765
766 # Make package and src dirs and replace lib.rs
767 os.makedirs(os.path.join(package_path, "src"), exist_ok=True)
768 with open(os.path.join(package_path, "src", "lib.rs"), "w") as librs:
George Burgess IV8e2cc042022-10-18 14:50:48 -0600769 librs.write(
770 EMPTY_CRATE_BODY if compile_error else NOP_EMPTY_CRATE_BODY
771 )
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700772
773 # Restore cargo.toml
George Burgess IV7dffc252022-08-31 14:37:01 -0700774 with open(cargo_file, "wb") as cfile:
George Burgess IV3e344e42022-08-09 21:07:04 -0700775 cfile.write(cargo_contents)
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700776
777 # Restore checksum
George Burgess IV7dffc252022-08-31 14:37:01 -0700778 with open(checksum_file, "w") as csum:
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700779 json.dump(checksum_contents, csum)
780
George Burgess IV4196b082022-11-03 17:10:47 -0600781 def destroy_unused_crates(self, destroyed_crates_file: pathlib.Path):
George Burgess IVfb0a1c42022-11-15 13:47:19 -0700782 metadata = [
783 (x["name"], x["version"])
784 for x in load_single_metadata(
785 self.working_dir, filter_platform=None
786 )["packages"]
787 ]
George Burgess IV7dffc252022-08-31 14:37:01 -0700788 used_packages = {
George Burgess IV9a264302022-12-16 15:36:01 -0700789 (x["name"], x["version"])
790 for x in load_all_package_metadata(self.working_dir)
George Burgess IV7dffc252022-08-31 14:37:01 -0700791 }
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700792
793 cleaned_packages = []
George Burgess IV40cc91c2022-08-15 13:07:40 -0700794 # Since we're asking for _all_ metadata packages, we may see
795 # duplication.
George Burgess IV9a264302022-12-16 15:36:01 -0700796 for package_desc in metadata:
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700797 # Skip used packages
George Burgess IV9a264302022-12-16 15:36:01 -0700798 if package_desc in used_packages:
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700799 continue
800
George Burgess IV9a264302022-12-16 15:36:01 -0700801 package_name, package_version = package_desc
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700802 # Detect the correct package path to destroy
George Burgess IV7dffc252022-08-31 14:37:01 -0700803 pkg_path = os.path.join(
804 self.vendor_dir,
George Burgess IVfb0a1c42022-11-15 13:47:19 -0700805 "{}-{}".format(package_name, package_version),
George Burgess IV7dffc252022-08-31 14:37:01 -0700806 )
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700807 if not os.path.isdir(pkg_path):
George Burgess IV8e2cc042022-10-18 14:50:48 -0600808 print(f"Crate {package_name} not found at {pkg_path}")
George Burgess IV635f7262022-08-09 21:32:20 -0700809 continue
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700810
George Burgess IV8e2cc042022-10-18 14:50:48 -0600811 self._replace_source_contents(
812 pkg_path, compile_error=package_name not in NOP_EMPTY_CRATES
813 )
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700814 self._modify_cargo_toml(pkg_path)
815 _rerun_checksums(pkg_path)
George Burgess IV4196b082022-11-03 17:10:47 -0600816 cleaned_packages.append((package_name, package_version))
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700817
George Burgess IV4196b082022-11-03 17:10:47 -0600818 for pkg, ver in cleaned_packages:
819 print(f"Removed unused crate {pkg}@{ver}")
820
821 # Write a list of crates that've been destroyed. This is used by
822 # `scripts/cargo-vet.py`.
823 file_header = "# List of destroyed crates autogenerated by vendor.py."
824 file_lines = [f"{pkg} {ver}" for pkg, ver in cleaned_packages]
825 destroyed_crates_file.write_text(
826 "\n".join([file_header] + file_lines), encoding="utf-8"
827 )
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700828
George Burgess IV7dffc252022-08-31 14:37:01 -0700829
George Burgess IVb4d0eaa2023-02-06 09:39:57 -0700830def main():
831 if not pathlib.Path("/etc/cros_chroot_version").exists():
832 sys.exit("This script can only be run within the chroot.")
833
834 parser = argparse.ArgumentParser(description="Vendor packages properly")
835 parser.add_argument(
836 "--skip-license-check",
837 "-s",
838 help="Skip the license check on a specific package",
839 action="append",
840 )
841 parser.add_argument("--license-map", help="Write license map to this file")
842 args = parser.parse_args()
843
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -0800844 current_path = pathlib.Path(__file__).parent.absolute()
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000845 patches = os.path.join(current_path, "patches")
846 vendor = os.path.join(current_path, "vendor")
Abhishek Pandit-Subedice0f5b22021-09-10 15:50:08 -0700847 crab_dir = os.path.join(current_path, "crab", "crates")
George Burgess IV4196b082022-11-03 17:10:47 -0600848 vendor_artifacts = current_path / "vendor_artifacts"
George Burgess IV56e384a2023-01-25 16:07:53 -0700849 scripts_dir = current_path / "scripts"
George Burgess IV4196b082022-11-03 17:10:47 -0600850 license_shorthand_file = os.path.join(vendor_artifacts, "licenses_used.txt")
851 destroyed_crates_file = vendor_artifacts / "destroyed_crates.txt"
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -0800852
Abhishek Pandit-Subedifa902382021-08-20 11:04:33 -0700853 # First, actually run cargo vendor
854 run_cargo_vendor(current_path)
855
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000856 # Order matters here:
857 # - Apply patches (also re-calculates checksums)
858 # - Cleanup any owners files (otherwise, git check-in or checksums are
859 # unhappy)
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700860 # - Destroy unused crates
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000861 apply_patches(patches, vendor)
862 cleanup_owners(vendor)
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700863 destroyer = CrateDestroyer(current_path, vendor)
George Burgess IV4196b082022-11-03 17:10:47 -0600864 destroyer.destroy_unused_crates(destroyed_crates_file)
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -0800865
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700866 # Combine license file and check for any bad licenses
867 lm = LicenseManager(current_path, vendor)
George Burgess IV7dffc252022-08-31 14:37:01 -0700868 lm.generate_license(
869 args.skip_license_check, args.license_map, license_shorthand_file
870 )
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700871
George Burgess IV56e384a2023-01-25 16:07:53 -0700872 # audit all packages
873 rc = subprocess.run([scripts_dir / "cargo-vet.py"]).returncode
874 if not rc:
875 return
876
877 sys.exit(
878 "cargo-vet audit failed. Please audit new packages. See "
879 "cargo-vet/README.md if you need help."
880 )
Abhishek Pandit-Subedice0f5b22021-09-10 15:50:08 -0700881
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -0800882
George Burgess IV7dffc252022-08-31 14:37:01 -0700883if __name__ == "__main__":
George Burgess IVb4d0eaa2023-02-06 09:39:57 -0700884 main()