blob: 1ce8e3c0913e4ee286d693b9f8a967444c19ddd0 [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
Abhishek Pandit-Subedice0f5b22021-09-10 15:50:08 -070020import toml
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +000021
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -070022# We only care about crates we're actually going to use and that's usually
23# limited to ones with cfg(linux). For running `cargo metadata`, limit results
George Burgess IVfb0a1c42022-11-15 13:47:19 -070024# to only these platforms.
25ALL_SUPPORTED_PLATFORMS = (
26 # Main targets.
27 "x86_64-cros-linux-gnu",
28 "armv7a-cros-linux-gnueabihf",
29 "aarch64-cros-linux-gnu",
30 # As far as we care, this is the same as x86_64-cros-linux-gnu.
31 # "x86_64-pc-linux-gnu",
32 # Baremetal targets.
33 "thumbv6m-none-eabi",
34 "thumbv7m-none-eabi",
35 "thumbv7em-none-eabihf",
36 "i686-unknown-uefi",
37 "x86_64-unknown-uefi",
38)
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -070039
George Burgess IV8e2cc042022-10-18 14:50:48 -060040# A series of crates which are to be made empty by having no (non-comment)
41# contents in their `lib.rs`, rather than by inserting a compilation error.
42NOP_EMPTY_CRATES = frozenset({"windows"})
43
44EMPTY_CRATE_BODY = """\
45compile_error!("This crate cannot be built for this configuration.");
46"""
47NOP_EMPTY_CRATE_BODY = "// " + EMPTY_CRATE_BODY
48
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +000049
50def _rerun_checksums(package_path):
51 """Re-run checksums for given package.
52
53 Writes resulting checksums to $package_path/.cargo-checksum.json.
54 """
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -070055 hashes = dict()
George Burgess IV7dffc252022-08-31 14:37:01 -070056 checksum_path = os.path.join(package_path, ".cargo-checksum.json")
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +000057 if not pathlib.Path(checksum_path).is_file():
58 return False
59
George Burgess IV7dffc252022-08-31 14:37:01 -070060 with open(checksum_path, "r") as fread:
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +000061 contents = json.load(fread)
62
63 for root, _, files in os.walk(package_path, topdown=True):
64 for f in files:
65 # Don't checksum an existing checksum file
66 if f == ".cargo-checksum.json":
67 continue
68
69 file_path = os.path.join(root, f)
George Burgess IV7dffc252022-08-31 14:37:01 -070070 with open(file_path, "rb") as frb:
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +000071 m = hashlib.sha256()
72 m.update(frb.read())
73 d = m.hexdigest()
74
75 # Key is relative to the package path so strip from beginning
76 key = os.path.relpath(file_path, package_path)
77 hashes[key] = d
78
79 if hashes:
George Burgess IV7dffc252022-08-31 14:37:01 -070080 print(
81 "{} regenerated {} hashes".format(package_path, len(hashes.keys()))
82 )
83 contents["files"] = hashes
84 with open(checksum_path, "w") as fwrite:
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -070085 json.dump(contents, fwrite, sort_keys=True)
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +000086
87 return True
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -080088
89
90def _remove_OWNERS_checksum(root):
George Burgess IV7dffc252022-08-31 14:37:01 -070091 """Delete all OWNERS files from the checksum file.
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -080092
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +000093 Args:
94 root: Root directory for the vendored crate.
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -080095
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +000096 Returns:
97 True if OWNERS was found and cleaned up. Otherwise False.
98 """
George Burgess IV7dffc252022-08-31 14:37:01 -070099 checksum_path = os.path.join(root, ".cargo-checksum.json")
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -0800100 if not pathlib.Path(checksum_path).is_file():
101 return False
102
George Burgess IV7dffc252022-08-31 14:37:01 -0700103 with open(checksum_path, "r") as fread:
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -0800104 contents = json.load(fread)
105
106 del_keys = []
George Burgess IV7dffc252022-08-31 14:37:01 -0700107 for cfile in contents["files"]:
108 if "OWNERS" in cfile:
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -0800109 del_keys.append(cfile)
110
111 for key in del_keys:
George Burgess IV7dffc252022-08-31 14:37:01 -0700112 del contents["files"][key]
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -0800113
114 if del_keys:
George Burgess IV7dffc252022-08-31 14:37:01 -0700115 print("{} deleted: {}".format(root, del_keys))
116 with open(checksum_path, "w") as fwrite:
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700117 json.dump(contents, fwrite, sort_keys=True)
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -0800118
119 return bool(del_keys)
120
121
122def cleanup_owners(vendor_path):
George Burgess IV7dffc252022-08-31 14:37:01 -0700123 """Remove owners checksums from the vendor directory.
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -0800124
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000125 We currently do not check in the OWNERS files from vendored crates because
126 they interfere with the find-owners functionality in gerrit. This cleanup
127 simply finds all instances of "OWNERS" in the checksum files within and
128 removes them.
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -0800129
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000130 Args:
131 vendor_path: Absolute path to vendor directory.
132 """
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -0800133 deps_cleaned = []
134 for root, dirs, _ in os.walk(vendor_path):
135 for d in dirs:
136 removed = _remove_OWNERS_checksum(os.path.join(root, d))
137 if removed:
138 deps_cleaned.append(d)
139
140 if deps_cleaned:
George Burgess IV7dffc252022-08-31 14:37:01 -0700141 print("Cleanup owners:\n {}".format("\n".join(deps_cleaned)))
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -0800142
143
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000144def apply_single_patch(patch, workdir):
145 """Apply a single patch and return whether it was successful.
146
147 Returns:
148 True if successful. False otherwise.
149 """
George Burgess IV08664ba2022-10-03 11:09:33 -0700150 proc = subprocess.run(
151 [
152 "patch",
153 "-p1",
154 "--no-backup-if-mismatch",
155 "-i",
156 patch,
157 ],
158 cwd=workdir,
159 )
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000160 return proc.returncode == 0
161
162
George Burgess IV30c5c362022-08-19 17:05:02 -0700163def apply_patch_script(script, workdir):
164 """Run the given patch script, returning whether it exited cleanly.
165
166 Returns:
167 True if successful. False otherwise.
168 """
169 return subprocess.run([script], cwd=workdir).returncode == 0
170
171
George Burgess IV635f7262022-08-09 21:32:20 -0700172def determine_vendor_crates(vendor_path):
173 """Returns a map of {crate_name: [directory]} at the given vendor_path."""
174 result = collections.defaultdict(list)
George Burgess IV76b60d02022-10-26 17:44:48 -0600175 crate_version_re = re.compile(r"-\d+\.\d+\.\d+(:?[+-]|$)")
George Burgess IV635f7262022-08-09 21:32:20 -0700176 for crate_name_plus_ver in os.listdir(vendor_path):
George Burgess IV76b60d02022-10-26 17:44:48 -0600177 version = crate_version_re.search(crate_name_plus_ver)
178 assert version, crate_name_plus_ver
179 name = crate_name_plus_ver[: version.start()]
George Burgess IV40cc91c2022-08-15 13:07:40 -0700180 result[name].append(crate_name_plus_ver)
George Burgess IV635f7262022-08-09 21:32:20 -0700181
182 for crate_list in result.values():
George Burgess IV40cc91c2022-08-15 13:07:40 -0700183 crate_list.sort()
George Burgess IV635f7262022-08-09 21:32:20 -0700184 return result
185
186
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000187def apply_patches(patches_path, vendor_path):
188 """Finds patches and applies them to sub-folders in the vendored crates.
189
190 Args:
191 patches_path: Path to folder with patches. Expect all patches to be one
192 level down (matching the crate name).
193 vendor_path: Root path to vendored crates directory.
194 """
195 checksums_for = {}
196
197 # Don't bother running if patches directory is empty
198 if not pathlib.Path(patches_path).is_dir():
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700199 return
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000200
George Burgess IV30c5c362022-08-19 17:05:02 -0700201 patches_failed = False
George Burgess IV635f7262022-08-09 21:32:20 -0700202 vendor_crate_map = determine_vendor_crates(vendor_path)
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000203 # Look for all patches and apply them
204 for d in os.listdir(patches_path):
205 dir_path = os.path.join(patches_path, d)
206
207 # We don't process patches in root dir
208 if not os.path.isdir(dir_path):
209 continue
210
George Burgess IV30c5c362022-08-19 17:05:02 -0700211 # We accept one of two forms here:
212 # - direct targets (these name # `${crate_name}-${version}`)
213 # - simply the crate name (which applies to all versions of the
214 # crate)
215 direct_target = os.path.join(vendor_path, d)
216 if os.path.isdir(direct_target):
217 patch_targets = [d]
218 elif d in vendor_crate_map:
219 patch_targets = vendor_crate_map[d]
220 else:
George Burgess IV7dffc252022-08-31 14:37:01 -0700221 raise RuntimeError(f"Unknown crate in {vendor_path}: {d}")
George Burgess IV30c5c362022-08-19 17:05:02 -0700222
George Burgess IV635f7262022-08-09 21:32:20 -0700223 for patch in os.listdir(dir_path):
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000224 file_path = os.path.join(dir_path, patch)
225
226 # Skip if not a patch file
George Burgess IV30c5c362022-08-19 17:05:02 -0700227 if not os.path.isfile(file_path):
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000228 continue
229
George Burgess IV30c5c362022-08-19 17:05:02 -0700230 if patch.endswith(".patch"):
231 apply = apply_single_patch
232 elif os.access(file_path, os.X_OK):
233 apply = apply_patch_script
George Burgess IV635f7262022-08-09 21:32:20 -0700234 else:
George Burgess IV30c5c362022-08-19 17:05:02 -0700235 # Unrecognized. Skip it.
236 continue
237
238 for target_name in patch_targets:
239 checksums_for[target_name] = True
240 target = os.path.join(vendor_path, target_name)
241 print(f"-- Applying {file_path} to {target}")
242 if not apply(file_path, target):
243 print(f"Failed to apply {file_path} to {target}")
244 patches_failed = True
245
246 # Do this late, so we can report all of the failing patches in one
247 # invocation.
248 if patches_failed:
George Burgess IV7dffc252022-08-31 14:37:01 -0700249 raise ValueError("Patches failed; please see above logs")
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000250
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000251 # Re-run checksums for all modified packages since we applied patches.
252 for key in checksums_for.keys():
253 _rerun_checksums(os.path.join(vendor_path, key))
254
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700255
George Burgess IV18af5632022-08-30 14:10:53 -0700256def get_workspace_cargo_toml(working_dir):
George Burgess IV40cc91c2022-08-15 13:07:40 -0700257 """Returns all Cargo.toml files under working_dir."""
George Burgess IV7dffc252022-08-31 14:37:01 -0700258 return [working_dir / "projects" / "Cargo.toml"]
George Burgess IV40cc91c2022-08-15 13:07:40 -0700259
260
Abhishek Pandit-Subedifa902382021-08-20 11:04:33 -0700261def run_cargo_vendor(working_dir):
262 """Runs cargo vendor.
263
264 Args:
265 working_dir: Directory to run inside. This should be the directory where
Abhishek Pandit-Subedice0f5b22021-09-10 15:50:08 -0700266 Cargo.toml is kept.
Abhishek Pandit-Subedifa902382021-08-20 11:04:33 -0700267 """
George Burgess IVfb0a1c42022-11-15 13:47:19 -0700268 # `cargo vendor` may update dependencies (which may update metadata).
269 load_all_package_metadata.cache_clear()
270
George Burgess IV635f7262022-08-09 21:32:20 -0700271 # Cargo will refuse to revendor into versioned directories, which leads to
272 # repeated `./vendor.py` invocations trying to apply patches to
273 # already-patched sources. Remove the existing vendor directory to avoid
274 # this.
George Burgess IV7dffc252022-08-31 14:37:01 -0700275 vendor_dir = working_dir / "vendor"
George Burgess IV635f7262022-08-09 21:32:20 -0700276 if vendor_dir.exists():
George Burgess IV40cc91c2022-08-15 13:07:40 -0700277 shutil.rmtree(vendor_dir)
278
George Burgess IV18af5632022-08-30 14:10:53 -0700279 cargo_cmdline = [
George Burgess IV7dffc252022-08-31 14:37:01 -0700280 "cargo",
281 "vendor",
282 "--versioned-dirs",
283 "-v",
284 "--manifest-path=projects/Cargo.toml",
285 "--",
286 "vendor",
George Burgess IV18af5632022-08-30 14:10:53 -0700287 ]
George Burgess IV40cc91c2022-08-15 13:07:40 -0700288 subprocess.check_call(cargo_cmdline, cwd=working_dir)
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000289
Abhishek Pandit-Subedice0f5b22021-09-10 15:50:08 -0700290
George Burgess IVfb0a1c42022-11-15 13:47:19 -0700291def load_single_metadata(working_dir, filter_platform):
George Burgess IV40cc91c2022-08-15 13:07:40 -0700292 """Load metadata for all projects under a given directory.
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700293
294 Args:
George Burgess IV40cc91c2022-08-15 13:07:40 -0700295 working_dir: Base directory to run from.
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700296 filter_platform: Filter packages to ones configured for this platform.
297 """
George Burgess IV40cc91c2022-08-15 13:07:40 -0700298 metadata_objects = []
George Burgess IV18af5632022-08-30 14:10:53 -0700299 cmd = [
George Burgess IV7dffc252022-08-31 14:37:01 -0700300 "cargo",
301 "metadata",
302 "--format-version=1",
303 "--manifest-path=projects/Cargo.toml",
George Burgess IV18af5632022-08-30 14:10:53 -0700304 ]
305 # Conditionally add platform filter
306 if filter_platform:
307 cmd += ("--filter-platform", filter_platform)
308 output = subprocess.check_output(cmd, cwd=working_dir)
309 return json.loads(output)
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700310
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700311
George Burgess IVfb0a1c42022-11-15 13:47:19 -0700312# Calls to this are somewhat expensive, and repeated a fair few times
313# throughout `./vendor.py`. Measuring locally, having a cache here speeds this
314# script up by 1.4x.
315@functools.lru_cache()
316def load_all_package_metadata(working_dir, platforms=ALL_SUPPORTED_PLATFORMS):
317 """Loads and merges metadata for all platforms in `platforms`.
318
319 This drops a lot of data from `cargo metadata`. Some of this metadata is
320 hard to merge, other bits of it just aren't worth keeping at the moment.
321 """
322 assert platforms, f"`platforms` should have things; has {platforms}"
323
324 found_package_ids = set()
325 results = []
326 for platform in platforms:
327 metadata = load_single_metadata(working_dir, platform)["packages"]
328 for package in metadata:
329 package_id = package["id"]
330 if package_id in found_package_ids:
331 continue
332
333 found_package_ids.add(package_id)
334 results.append(
335 {
336 "id": package["id"],
337 "license": package["license"],
338 "license_file": package["license_file"],
339 "name": package["name"],
340 "version": package["version"],
341 }
342 )
343
344 return results
345
346
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700347class LicenseManager:
George Burgess IV7dffc252022-08-31 14:37:01 -0700348 """Manage consolidating licenses for all packages."""
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700349
George Burgess IV124e6a12022-09-09 10:44:29 -0700350 # These are all the licenses we support. Keys are what is seen in metadata
351 # and values are what is expected by ebuilds.
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700352 SUPPORTED_LICENSES = {
George Burgess IV7dffc252022-08-31 14:37:01 -0700353 "0BSD": "0BSD",
354 "Apache-2.0": "Apache-2.0",
George Burgess IVb16816a2022-10-26 17:55:48 -0600355 "BSD-2-Clause": "BSD-2",
George Burgess IV7dffc252022-08-31 14:37:01 -0700356 "BSD-3-Clause": "BSD-3",
357 "ISC": "ISC",
358 "MIT": "MIT",
359 "MPL-2.0": "MPL-2.0",
360 "unicode": "unicode",
Dan Callaghan91f80542022-09-09 10:57:23 +1000361 "Zlib": "ZLIB",
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700362 }
363
364 # Prefer to take attribution licenses in this order. All these require that
365 # we actually use the license file found in the package so they MUST have
366 # a license file set.
George Burgess IV7dffc252022-08-31 14:37:01 -0700367 PREFERRED_ATTRIB_LICENSE_ORDER = ["MIT", "BSD-3", "ISC"]
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700368
369 # If Apache license is found, always prefer it (simplifies attribution)
George Burgess IV7dffc252022-08-31 14:37:01 -0700370 APACHE_LICENSE = "Apache-2.0"
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700371
372 # Regex for license files found in the vendored directories. Search for
373 # these files with re.IGNORECASE.
374 #
375 # These will be searched in order with the earlier entries being preferred.
376 LICENSE_NAMES_REGEX = [
George Burgess IV7dffc252022-08-31 14:37:01 -0700377 r"^license-mit$",
378 r"^copyright$",
379 r"^licen[cs]e.*$",
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700380 ]
381
382 # Some crates have their license file in other crates. This usually occurs
383 # because multiple crates are published from the same git repository and the
384 # license isn't updated in each sub-crate. In these cases, we can just
385 # ignore these packages.
386 MAP_LICENSE_TO_OTHER = {
George Burgess IV7dffc252022-08-31 14:37:01 -0700387 "failure_derive": "failure",
388 "grpcio-compiler": "grpcio",
389 "grpcio-sys": "grpcio",
Li-Yu Yu89d93c72022-12-19 03:36:50 +0800390 "protobuf-codegen": "protobuf",
391 "protobuf-parse": "protobuf",
392 "protobuf-support": "protobuf",
George Burgess IV7dffc252022-08-31 14:37:01 -0700393 "rustyline-derive": "rustyline",
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700394 }
395
396 # Map a package to a specific license and license file. Only use this if
397 # a package doesn't have an easily discoverable license or exports its
398 # license in a weird way. Prefer to patch the project with a license and
399 # upstream the patch instead.
400 STATIC_LICENSE_MAP = {
George Burgess IVb16816a2022-10-26 17:55:48 -0600401 # "package name": ("license name", "license file relative location")
George Burgess IV26642872022-10-18 19:46:58 -0600402 # Patch for adding these are upstream, but the patch application
403 # doesn't apply to `cargo metadata`. This is presumably because it
404 # can't detect our vendor directory.
George Burgess IVf4a5e362022-08-30 14:30:36 -0700405 # https://gitlab.freedesktop.org/slirp/libslirp-sys/-/merge_requests/6
George Burgess IV7dffc252022-08-31 14:37:01 -0700406 "libslirp-sys": ("MIT", "LICENSE"),
George Burgess IV26642872022-10-18 19:46:58 -0600407 # https://gitlab.freedesktop.org/anholt/deqp-runner/-/merge_requests/48
408 "deqp-runner": ("MIT", "LICENSE"),
Dan Callaghan91f80542022-09-09 10:57:23 +1000409 # Upstream prefers to embed license text inside README.md:
410 "riscv": ("ISC", "README.md"),
411 "riscv-rt": ("ISC", "README.md"),
George Burgess IVb16816a2022-10-26 17:55:48 -0600412 "zerocopy": ("BSD-2", "LICENSE"),
413 "zerocopy-derive": ("BSD-2", "LICENSE"),
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700414 }
415
416 def __init__(self, working_dir, vendor_dir):
417 self.working_dir = working_dir
418 self.vendor_dir = vendor_dir
419
420 def _find_license_in_dir(self, search_dir):
421 for p in os.listdir(search_dir):
422 # Ignore anything that's not a file
423 if not os.path.isfile(os.path.join(search_dir, p)):
424 continue
425
426 # Now check if the name matches any of the regexes
427 # We'll return the first matching file.
428 for regex in self.LICENSE_NAMES_REGEX:
429 if re.search(regex, p, re.IGNORECASE):
430 yield os.path.join(search_dir, p)
431 break
432
433 def _guess_license_type(self, license_file):
George Burgess IV7dffc252022-08-31 14:37:01 -0700434 if "-MIT" in license_file:
435 return "MIT"
436 elif "-APACHE" in license_file:
437 return "APACHE"
438 elif "-BSD" in license_file:
439 return "BSD-3"
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700440
George Burgess IV7dffc252022-08-31 14:37:01 -0700441 with open(license_file, "r") as f:
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700442 lines = f.read()
George Burgess IV7dffc252022-08-31 14:37:01 -0700443 if "MIT" in lines:
444 return "MIT"
445 elif "Apache" in lines:
446 return "APACHE"
447 elif "BSD 3-Clause" in lines:
448 return "BSD-3"
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700449
George Burgess IV7dffc252022-08-31 14:37:01 -0700450 return ""
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700451
George Burgess IV7dffc252022-08-31 14:37:01 -0700452 def generate_license(
453 self, skip_license_check, print_map_to_file, license_shorthand_file
454 ):
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700455 """Generate single massive license file from metadata."""
George Burgess IVfb0a1c42022-11-15 13:47:19 -0700456 metadata = load_all_package_metadata(self.working_dir)
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700457
George Burgess IVb16816a2022-10-26 17:55:48 -0600458 special_unicode_license = "(MIT OR Apache-2.0) AND Unicode-DFS-2016"
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700459 bad_licenses = {}
460
461 # Keep license map ordered so it generates a consistent license map
462 license_map = {}
463
464 skip_license_check = skip_license_check or []
George Burgess IV4ae42062022-08-15 18:54:51 -0700465 has_unicode_license = False
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700466
George Burgess IVfb0a1c42022-11-15 13:47:19 -0700467 for package in metadata:
George Burgess IV40cc91c2022-08-15 13:07:40 -0700468 # Skip the synthesized Cargo.toml packages that exist solely to
469 # list dependencies.
George Burgess IV7dffc252022-08-31 14:37:01 -0700470 if "path+file:///" in package["id"]:
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700471 continue
472
George Burgess IV7dffc252022-08-31 14:37:01 -0700473 pkg_name = package["name"]
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700474 if pkg_name in skip_license_check:
475 print(
George Burgess IV7dffc252022-08-31 14:37:01 -0700476 "Skipped license check on {}. Reason: Skipped from command line".format(
477 pkg_name
478 )
479 )
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700480 continue
481
482 if pkg_name in self.MAP_LICENSE_TO_OTHER:
483 print(
George Burgess IV7dffc252022-08-31 14:37:01 -0700484 "Skipped license check on {}. Reason: License already in {}".format(
485 pkg_name, self.MAP_LICENSE_TO_OTHER[pkg_name]
486 )
487 )
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700488 continue
489
490 # Check if we have a static license map for this package. Use the
491 # static values if we have it already set.
492 if pkg_name in self.STATIC_LICENSE_MAP:
George Burgess IVb16816a2022-10-26 17:55:48 -0600493 license, license_file = self.STATIC_LICENSE_MAP[pkg_name]
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700494 license_map[pkg_name] = {
495 "license": license,
496 "license_file": license_file,
497 }
498 continue
499
500 license_files = []
George Burgess IV93ba4732022-08-13 14:10:10 -0700501 # use `or ''` instead of get's default, since `package` may have a
502 # None value for 'license'.
George Burgess IV7dffc252022-08-31 14:37:01 -0700503 license = package.get("license") or ""
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700504
505 # We ignore the metadata for license file because most crates don't
506 # have it set. Just scan the source for licenses.
George Burgess IV7dffc252022-08-31 14:37:01 -0700507 pkg_version = package["version"]
508 license_files = list(
509 self._find_license_in_dir(
510 os.path.join(self.vendor_dir, f"{pkg_name}-{pkg_version}")
511 )
512 )
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700513
George Burgess IV4ae42062022-08-15 18:54:51 -0700514 # FIXME(b/240953811): The code later in this loop is only
515 # structured to handle ORs, not ANDs. Fortunately, this license in
516 # particular is `AND`ed between a super common license (Apache) and
517 # a more obscure one (unicode). This hack is specifically intended
518 # for the `unicode-ident` crate, though no crate name check is
519 # made, since it's OK other crates happen to have this license.
George Burgess IVb16816a2022-10-26 17:55:48 -0600520 if license == special_unicode_license:
George Burgess IV4ae42062022-08-15 18:54:51 -0700521 has_unicode_license = True
522 # We'll check later to be sure MIT or Apache-2.0 is represented
523 # properly.
524 for x in license_files:
George Burgess IV7dffc252022-08-31 14:37:01 -0700525 if os.path.basename(x) == "LICENSE-UNICODE":
George Burgess IV4ae42062022-08-15 18:54:51 -0700526 license_file = x
527 break
528 else:
George Burgess IV7dffc252022-08-31 14:37:01 -0700529 raise ValueError(
530 "No LICENSE-UNICODE found in " f"{license_files}"
531 )
George Burgess IV4ae42062022-08-15 18:54:51 -0700532 license_map[pkg_name] = {
533 "license": license,
534 "license_file": license_file,
535 }
George Burgess IV4ae42062022-08-15 18:54:51 -0700536 continue
537
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700538 # If there are multiple licenses, they are delimited with "OR" or "/"
George Burgess IV7dffc252022-08-31 14:37:01 -0700539 delim = " OR " if " OR " in license else "/"
George Burgess IV40cc91c2022-08-15 13:07:40 -0700540 found = [x.strip() for x in license.split(delim)]
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700541
542 # Filter licenses to ones we support
543 licenses_or = [
George Burgess IV7dffc252022-08-31 14:37:01 -0700544 self.SUPPORTED_LICENSES[f]
545 for f in found
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700546 if f in self.SUPPORTED_LICENSES
547 ]
548
549 # If apache license is found, always prefer it because it simplifies
550 # license attribution (we can use existing Apache notice)
551 if self.APACHE_LICENSE in licenses_or:
George Burgess IV7dffc252022-08-31 14:37:01 -0700552 license_map[pkg_name] = {"license": self.APACHE_LICENSE}
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700553
554 # Handle single license that has at least one license file
555 # We pick the first license file and the license
556 elif len(licenses_or) == 1:
557 if license_files:
558 l = licenses_or[0]
559 lf = license_files[0]
560
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700561 license_map[pkg_name] = {
George Burgess IV7dffc252022-08-31 14:37:01 -0700562 "license": l,
563 "license_file": os.path.relpath(lf, self.working_dir),
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700564 }
565 else:
566 bad_licenses[pkg_name] = "{} missing license file".format(
George Burgess IV7dffc252022-08-31 14:37:01 -0700567 licenses_or[0]
568 )
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700569 # Handle multiple licenses
570 elif len(licenses_or) > 1:
571 # Check preferred licenses in order
572 license_found = False
573 for l in self.PREFERRED_ATTRIB_LICENSE_ORDER:
574 if not l in licenses_or:
575 continue
576
577 for f in license_files:
578 if self._guess_license_type(f) == l:
579 license_found = True
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700580 license_map[pkg_name] = {
George Burgess IV7dffc252022-08-31 14:37:01 -0700581 "license": l,
582 "license_file": os.path.relpath(
583 f, self.working_dir
584 ),
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700585 }
586 break
587
588 # Break out of loop if license is found
589 if license_found:
590 break
591 else:
592 bad_licenses[pkg_name] = license
593
594 # If we had any bad licenses, we need to abort
595 if bad_licenses:
596 for k in bad_licenses.keys():
George Burgess IV7dffc252022-08-31 14:37:01 -0700597 print(
598 "{} had no acceptable licenses: {}".format(
599 k, bad_licenses[k]
600 )
601 )
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700602 raise Exception("Bad licenses in vendored packages.")
603
604 # Write license map to file
605 if print_map_to_file:
George Burgess IV7dffc252022-08-31 14:37:01 -0700606 with open(
607 os.path.join(self.working_dir, print_map_to_file), "w"
608 ) as lfile:
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700609 json.dump(license_map, lfile, sort_keys=True)
610
611 # Raise missing licenses unless we have a valid reason to ignore them
612 raise_missing_license = False
613 for name, v in license_map.items():
George Burgess IV7dffc252022-08-31 14:37:01 -0700614 if (
615 "license_file" not in v
616 and v.get("license", "") != self.APACHE_LICENSE
617 ):
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700618 raise_missing_license = True
George Burgess IV7dffc252022-08-31 14:37:01 -0700619 print(
620 " {}: Missing license file. Fix or add to ignorelist.".format(
621 name
622 )
623 )
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700624
625 if raise_missing_license:
626 raise Exception(
627 "Unhandled missing license file. "
George Burgess IV7dffc252022-08-31 14:37:01 -0700628 "Make sure all are accounted for before continuing."
629 )
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700630
George Burgess IVb16816a2022-10-26 17:55:48 -0600631 has_license_types = {x["license"] for x in license_map.values()}
George Burgess IV4ae42062022-08-15 18:54:51 -0700632 if has_unicode_license:
George Burgess IVb16816a2022-10-26 17:55:48 -0600633 # Replace this license with the actual SPDX license we plan to use.
634 has_license_types.remove(special_unicode_license)
635 has_license_types.add("unicode")
George Burgess IV4ae42062022-08-15 18:54:51 -0700636 if self.APACHE_LICENSE not in has_license_types:
George Burgess IV7dffc252022-08-31 14:37:01 -0700637 raise ValueError(
638 "Need the apache license; currently have: "
639 f"{sorted(has_license_types)}"
640 )
George Burgess IV4ae42062022-08-15 18:54:51 -0700641
George Burgess IV04833702022-08-09 22:00:38 -0700642 sorted_licenses = sorted(has_license_types)
George Burgess IV124e6a12022-09-09 10:44:29 -0700643 print("The following licenses are in use:", sorted_licenses)
George Burgess IV7dffc252022-08-31 14:37:01 -0700644 header = textwrap.dedent(
645 """\
George Burgess IV04833702022-08-09 22:00:38 -0700646 # File to describe the licenses used by this registry.
Daniel Verkampd9d085b2022-09-07 10:52:27 -0700647 # Used so it's easy to automatically verify ebuilds are updated.
George Burgess IV04833702022-08-09 22:00:38 -0700648 # Each line is a license. Lines starting with # are comments.
George Burgess IV7dffc252022-08-31 14:37:01 -0700649 """
650 )
651 with open(license_shorthand_file, "w", encoding="utf-8") as f:
George Burgess IV04833702022-08-09 22:00:38 -0700652 f.write(header)
George Burgess IV7dffc252022-08-31 14:37:01 -0700653 f.write("\n".join(sorted_licenses))
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700654
655
George Burgess IVd0261472022-10-17 18:59:10 -0600656def clean_source_related_lines_in_place(cargo_toml):
657 """Removes all [[bin]] (and similar) sections in `cargo_toml`."""
658 cargo_toml.pop("bench", None)
659 cargo_toml.pop("bin", None)
660 cargo_toml.pop("examples", None)
661 cargo_toml.pop("test", None)
662
663 lib = cargo_toml.get("lib")
664 if lib:
665 lib.pop("path", None)
666
667 package = cargo_toml.get("package")
668 if package:
669 package.pop("build", None)
670 package.pop("default-run", None)
671 package.pop("include", None)
672
673
George Burgess IVd4ff0502022-08-14 23:27:57 -0700674def clean_features_in_place(cargo_toml):
675 """Removes all side-effects of features in `cargo_toml`."""
George Burgess IV7dffc252022-08-31 14:37:01 -0700676 features = cargo_toml.get("features")
George Burgess IVd4ff0502022-08-14 23:27:57 -0700677 if not features:
678 return
679
George Burgess IVd0261472022-10-17 18:59:10 -0600680 for name in features:
681 features[name] = []
George Burgess IVd4ff0502022-08-14 23:27:57 -0700682
683
George Burgess IVd0261472022-10-17 18:59:10 -0600684def remove_all_dependencies_in_place(cargo_toml):
George Burgess IVd4ff0502022-08-14 23:27:57 -0700685 """Removes all `target.*.dependencies` from `cargo_toml`."""
George Burgess IVd0261472022-10-17 18:59:10 -0600686 cargo_toml.pop("build-dependencies", None)
687 cargo_toml.pop("dependencies", None)
688 cargo_toml.pop("dev-dependencies", None)
689
George Burgess IV7dffc252022-08-31 14:37:01 -0700690 target = cargo_toml.get("target")
George Burgess IVd4ff0502022-08-14 23:27:57 -0700691 if not target:
692 return
George Burgess IV0313d782022-08-15 23:45:44 -0700693
George Burgess IVd4ff0502022-08-14 23:27:57 -0700694 empty_keys = []
695 for key, values in target.items():
George Burgess IVd0261472022-10-17 18:59:10 -0600696 values.pop("build-dependencies", None)
George Burgess IV7dffc252022-08-31 14:37:01 -0700697 values.pop("dependencies", None)
698 values.pop("dev-dependencies", None)
George Burgess IVd4ff0502022-08-14 23:27:57 -0700699 if not values:
700 empty_keys.append(key)
George Burgess IV0313d782022-08-15 23:45:44 -0700701
George Burgess IVd4ff0502022-08-14 23:27:57 -0700702 if len(empty_keys) == len(target):
George Burgess IV7dffc252022-08-31 14:37:01 -0700703 del cargo_toml["target"]
George Burgess IVd4ff0502022-08-14 23:27:57 -0700704 else:
705 for key in empty_keys:
706 del target[key]
George Burgess IV0313d782022-08-15 23:45:44 -0700707
708
George Burgess IV7dffc252022-08-31 14:37:01 -0700709class CrateDestroyer:
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700710 def __init__(self, working_dir, vendor_dir):
711 self.working_dir = working_dir
712 self.vendor_dir = vendor_dir
713
714 def _modify_cargo_toml(self, pkg_path):
George Burgess IV7dffc252022-08-31 14:37:01 -0700715 with open(os.path.join(pkg_path, "Cargo.toml"), "r") as cargo:
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700716 contents = toml.load(cargo)
717
George Burgess IV7dffc252022-08-31 14:37:01 -0700718 package = contents["package"]
George Burgess IVd4ff0502022-08-14 23:27:57 -0700719
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700720 # Change description, license and delete license key
George Burgess IV7dffc252022-08-31 14:37:01 -0700721 package["description"] = "Empty crate that should not build."
722 package["license"] = "Apache-2.0"
George Burgess IVd4ff0502022-08-14 23:27:57 -0700723
George Burgess IV7dffc252022-08-31 14:37:01 -0700724 package.pop("license_file", None)
George Burgess IVd4ff0502022-08-14 23:27:57 -0700725 # If there's no build.rs but we specify `links = "foo"`, Cargo gets
726 # upset.
George Burgess IV7dffc252022-08-31 14:37:01 -0700727 package.pop("links", None)
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700728
George Burgess IV0313d782022-08-15 23:45:44 -0700729 # Some packages have cfg-specific dependencies. Remove them here; we
730 # don't care about the dependencies of an empty package.
731 #
732 # This is a load-bearing optimization: `dev-python/toml` doesn't
733 # always round-trip dumps(loads(x)) correctly when `x` has keys with
734 # strings (b/242589711#comment3). The place this has bitten us so far
735 # is target dependencies, which can be harmlessly removed for now.
George Burgess IVd4ff0502022-08-14 23:27:57 -0700736 #
737 # Cleaning features in-place is also necessary, since we're removing
738 # dependencies, and a feature can enable features in dependencies.
739 # Cargo errors out on `[features] foo = "bar/baz"` if `bar` isn't a
740 # dependency.
741 clean_features_in_place(contents)
George Burgess IVd0261472022-10-17 18:59:10 -0600742 remove_all_dependencies_in_place(contents)
743
744 # Since we're removing all source files, also be sure to remove
745 # source-related keys.
746 clean_source_related_lines_in_place(contents)
George Burgess IV0313d782022-08-15 23:45:44 -0700747
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700748 with open(os.path.join(pkg_path, "Cargo.toml"), "w") as cargo:
749 toml.dump(contents, cargo)
750
George Burgess IV8e2cc042022-10-18 14:50:48 -0600751 def _replace_source_contents(self, package_path, compile_error):
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700752 # First load the checksum file before starting
753 checksum_file = os.path.join(package_path, ".cargo-checksum.json")
George Burgess IV7dffc252022-08-31 14:37:01 -0700754 with open(checksum_file, "r") as csum:
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700755 checksum_contents = json.load(csum)
756
757 # Also load the cargo.toml file which we need to write back
758 cargo_file = os.path.join(package_path, "Cargo.toml")
George Burgess IV7dffc252022-08-31 14:37:01 -0700759 with open(cargo_file, "rb") as cfile:
George Burgess IV3e344e42022-08-09 21:07:04 -0700760 cargo_contents = cfile.read()
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700761
762 shutil.rmtree(package_path)
763
764 # Make package and src dirs and replace lib.rs
765 os.makedirs(os.path.join(package_path, "src"), exist_ok=True)
766 with open(os.path.join(package_path, "src", "lib.rs"), "w") as librs:
George Burgess IV8e2cc042022-10-18 14:50:48 -0600767 librs.write(
768 EMPTY_CRATE_BODY if compile_error else NOP_EMPTY_CRATE_BODY
769 )
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700770
771 # Restore cargo.toml
George Burgess IV7dffc252022-08-31 14:37:01 -0700772 with open(cargo_file, "wb") as cfile:
George Burgess IV3e344e42022-08-09 21:07:04 -0700773 cfile.write(cargo_contents)
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700774
775 # Restore checksum
George Burgess IV7dffc252022-08-31 14:37:01 -0700776 with open(checksum_file, "w") as csum:
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700777 json.dump(checksum_contents, csum)
778
George Burgess IV4196b082022-11-03 17:10:47 -0600779 def destroy_unused_crates(self, destroyed_crates_file: pathlib.Path):
George Burgess IVfb0a1c42022-11-15 13:47:19 -0700780 metadata = [
781 (x["name"], x["version"])
782 for x in load_single_metadata(
783 self.working_dir, filter_platform=None
784 )["packages"]
785 ]
George Burgess IV7dffc252022-08-31 14:37:01 -0700786 used_packages = {
George Burgess IV9a264302022-12-16 15:36:01 -0700787 (x["name"], x["version"])
788 for x in load_all_package_metadata(self.working_dir)
George Burgess IV7dffc252022-08-31 14:37:01 -0700789 }
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700790
791 cleaned_packages = []
George Burgess IV40cc91c2022-08-15 13:07:40 -0700792 # Since we're asking for _all_ metadata packages, we may see
793 # duplication.
George Burgess IV9a264302022-12-16 15:36:01 -0700794 for package_desc in metadata:
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700795 # Skip used packages
George Burgess IV9a264302022-12-16 15:36:01 -0700796 if package_desc in used_packages:
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700797 continue
798
George Burgess IV9a264302022-12-16 15:36:01 -0700799 package_name, package_version = package_desc
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700800 # Detect the correct package path to destroy
George Burgess IV7dffc252022-08-31 14:37:01 -0700801 pkg_path = os.path.join(
802 self.vendor_dir,
George Burgess IVfb0a1c42022-11-15 13:47:19 -0700803 "{}-{}".format(package_name, package_version),
George Burgess IV7dffc252022-08-31 14:37:01 -0700804 )
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700805 if not os.path.isdir(pkg_path):
George Burgess IV8e2cc042022-10-18 14:50:48 -0600806 print(f"Crate {package_name} not found at {pkg_path}")
George Burgess IV635f7262022-08-09 21:32:20 -0700807 continue
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700808
George Burgess IV8e2cc042022-10-18 14:50:48 -0600809 self._replace_source_contents(
810 pkg_path, compile_error=package_name not in NOP_EMPTY_CRATES
811 )
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700812 self._modify_cargo_toml(pkg_path)
813 _rerun_checksums(pkg_path)
George Burgess IV4196b082022-11-03 17:10:47 -0600814 cleaned_packages.append((package_name, package_version))
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700815
George Burgess IV4196b082022-11-03 17:10:47 -0600816 for pkg, ver in cleaned_packages:
817 print(f"Removed unused crate {pkg}@{ver}")
818
819 # Write a list of crates that've been destroyed. This is used by
820 # `scripts/cargo-vet.py`.
821 file_header = "# List of destroyed crates autogenerated by vendor.py."
822 file_lines = [f"{pkg} {ver}" for pkg, ver in cleaned_packages]
823 destroyed_crates_file.write_text(
824 "\n".join([file_header] + file_lines), encoding="utf-8"
825 )
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700826
George Burgess IV7dffc252022-08-31 14:37:01 -0700827
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700828def main(args):
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -0800829 current_path = pathlib.Path(__file__).parent.absolute()
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000830 patches = os.path.join(current_path, "patches")
831 vendor = os.path.join(current_path, "vendor")
Abhishek Pandit-Subedice0f5b22021-09-10 15:50:08 -0700832 crab_dir = os.path.join(current_path, "crab", "crates")
George Burgess IV4196b082022-11-03 17:10:47 -0600833 vendor_artifacts = current_path / "vendor_artifacts"
George Burgess IV56e384a2023-01-25 16:07:53 -0700834 scripts_dir = current_path / "scripts"
George Burgess IV4196b082022-11-03 17:10:47 -0600835 license_shorthand_file = os.path.join(vendor_artifacts, "licenses_used.txt")
836 destroyed_crates_file = vendor_artifacts / "destroyed_crates.txt"
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -0800837
Abhishek Pandit-Subedifa902382021-08-20 11:04:33 -0700838 # First, actually run cargo vendor
839 run_cargo_vendor(current_path)
840
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000841 # Order matters here:
842 # - Apply patches (also re-calculates checksums)
843 # - Cleanup any owners files (otherwise, git check-in or checksums are
844 # unhappy)
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700845 # - Destroy unused crates
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000846 apply_patches(patches, vendor)
847 cleanup_owners(vendor)
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700848 destroyer = CrateDestroyer(current_path, vendor)
George Burgess IV4196b082022-11-03 17:10:47 -0600849 destroyer.destroy_unused_crates(destroyed_crates_file)
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -0800850
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700851 # Combine license file and check for any bad licenses
852 lm = LicenseManager(current_path, vendor)
George Burgess IV7dffc252022-08-31 14:37:01 -0700853 lm.generate_license(
854 args.skip_license_check, args.license_map, license_shorthand_file
855 )
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700856
George Burgess IV56e384a2023-01-25 16:07:53 -0700857 # audit all packages
858 rc = subprocess.run([scripts_dir / "cargo-vet.py"]).returncode
859 if not rc:
860 return
861
862 sys.exit(
863 "cargo-vet audit failed. Please audit new packages. See "
864 "cargo-vet/README.md if you need help."
865 )
Abhishek Pandit-Subedice0f5b22021-09-10 15:50:08 -0700866
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -0800867
George Burgess IV7dffc252022-08-31 14:37:01 -0700868if __name__ == "__main__":
869 parser = argparse.ArgumentParser(description="Vendor packages properly")
870 parser.add_argument(
871 "--skip-license-check",
872 "-s",
873 help="Skip the license check on a specific package",
874 action="append",
875 )
876 parser.add_argument("--license-map", help="Write license map to this file")
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700877 args = parser.parse_args()
878
879 main(args)