blob: 051f9980b100a0641682426dc61d3af984db1b29 [file] [log] [blame]
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -08001#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# Copyright 2021 The Chromium OS Authors. All rights reserved.
4# 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
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +000010import hashlib
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -080011import json
12import os
13import pathlib
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -070014import re
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -070015import shutil
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +000016import subprocess
Abhishek Pandit-Subedice0f5b22021-09-10 15:50:08 -070017import toml
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +000018
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -070019# We only care about crates we're actually going to use and that's usually
20# limited to ones with cfg(linux). For running `cargo metadata`, limit results
21# to only this platform
22DEFAULT_PLATFORM_FILTER = "x86_64-unknown-linux-gnu"
23
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +000024
25def _rerun_checksums(package_path):
26 """Re-run checksums for given package.
27
28 Writes resulting checksums to $package_path/.cargo-checksum.json.
29 """
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -070030 hashes = dict()
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +000031 checksum_path = os.path.join(package_path, '.cargo-checksum.json')
32 if not pathlib.Path(checksum_path).is_file():
33 return False
34
35 with open(checksum_path, 'r') as fread:
36 contents = json.load(fread)
37
38 for root, _, files in os.walk(package_path, topdown=True):
39 for f in files:
40 # Don't checksum an existing checksum file
41 if f == ".cargo-checksum.json":
42 continue
43
44 file_path = os.path.join(root, f)
45 with open(file_path, 'rb') as frb:
46 m = hashlib.sha256()
47 m.update(frb.read())
48 d = m.hexdigest()
49
50 # Key is relative to the package path so strip from beginning
51 key = os.path.relpath(file_path, package_path)
52 hashes[key] = d
53
54 if hashes:
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -070055 print("{} regenerated {} hashes".format(package_path,
56 len(hashes.keys())))
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +000057 contents['files'] = hashes
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +000058 with open(checksum_path, 'w') as fwrite:
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -070059 json.dump(contents, fwrite, sort_keys=True)
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +000060
61 return True
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -080062
63
64def _remove_OWNERS_checksum(root):
65 """ Delete all OWNERS files from the checksum file.
66
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +000067 Args:
68 root: Root directory for the vendored crate.
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -080069
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +000070 Returns:
71 True if OWNERS was found and cleaned up. Otherwise False.
72 """
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -080073 checksum_path = os.path.join(root, '.cargo-checksum.json')
74 if not pathlib.Path(checksum_path).is_file():
75 return False
76
77 with open(checksum_path, 'r') as fread:
78 contents = json.load(fread)
79
80 del_keys = []
81 for cfile in contents['files']:
82 if 'OWNERS' in cfile:
83 del_keys.append(cfile)
84
85 for key in del_keys:
86 del contents['files'][key]
87
88 if del_keys:
89 print('{} deleted: {}'.format(root, del_keys))
90 with open(checksum_path, 'w') as fwrite:
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -070091 json.dump(contents, fwrite, sort_keys=True)
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -080092
93 return bool(del_keys)
94
95
96def cleanup_owners(vendor_path):
97 """ Remove owners checksums from the vendor directory.
98
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +000099 We currently do not check in the OWNERS files from vendored crates because
100 they interfere with the find-owners functionality in gerrit. This cleanup
101 simply finds all instances of "OWNERS" in the checksum files within and
102 removes them.
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -0800103
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000104 Args:
105 vendor_path: Absolute path to vendor directory.
106 """
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -0800107 deps_cleaned = []
108 for root, dirs, _ in os.walk(vendor_path):
109 for d in dirs:
110 removed = _remove_OWNERS_checksum(os.path.join(root, d))
111 if removed:
112 deps_cleaned.append(d)
113
114 if deps_cleaned:
115 print('Cleanup owners:\n {}'.format("\n".join(deps_cleaned)))
116
117
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000118def apply_single_patch(patch, workdir):
119 """Apply a single patch and return whether it was successful.
120
121 Returns:
122 True if successful. False otherwise.
123 """
George Burgess IV635f7262022-08-09 21:32:20 -0700124 print(f"-- Applying {patch} to {workdir}")
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000125 proc = subprocess.run(["patch", "-p1", "-i", patch], cwd=workdir)
126 return proc.returncode == 0
127
128
George Burgess IV635f7262022-08-09 21:32:20 -0700129def determine_vendor_crates(vendor_path):
130 """Returns a map of {crate_name: [directory]} at the given vendor_path."""
131 result = collections.defaultdict(list)
132 for crate_name_plus_ver in os.listdir(vendor_path):
133 name, _ = crate_name_plus_ver.rsplit('-', 1)
134 result[name].append(crate_name_plus_ver)
135
136 for crate_list in result.values():
137 crate_list.sort()
138 return result
139
140
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000141def apply_patches(patches_path, vendor_path):
142 """Finds patches and applies them to sub-folders in the vendored crates.
143
144 Args:
145 patches_path: Path to folder with patches. Expect all patches to be one
146 level down (matching the crate name).
147 vendor_path: Root path to vendored crates directory.
148 """
149 checksums_for = {}
150
151 # Don't bother running if patches directory is empty
152 if not pathlib.Path(patches_path).is_dir():
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700153 return
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000154
George Burgess IV635f7262022-08-09 21:32:20 -0700155 vendor_crate_map = determine_vendor_crates(vendor_path)
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000156 # Look for all patches and apply them
157 for d in os.listdir(patches_path):
158 dir_path = os.path.join(patches_path, d)
159
160 # We don't process patches in root dir
161 if not os.path.isdir(dir_path):
162 continue
163
George Burgess IV635f7262022-08-09 21:32:20 -0700164 for patch in os.listdir(dir_path):
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000165 file_path = os.path.join(dir_path, patch)
166
167 # Skip if not a patch file
168 if not os.path.isfile(file_path) or not patch.endswith(".patch"):
169 continue
170
George Burgess IV635f7262022-08-09 21:32:20 -0700171 # We accept one of two forms here:
172 # - direct targets (these name # `${crate_name}-${version}`)
173 # - simply the crate name (which applies to all versions of the
174 # crate)
175 direct_target = os.path.join(vendor_path, d)
176 if os.path.isdir(direct_target):
177 # If there are any patches, queue checksums for that folder.
178 checksums_for[d] = True
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000179
George Burgess IV635f7262022-08-09 21:32:20 -0700180 # Apply the patch. Exit from patch loop if patching failed.
181 if not apply_single_patch(file_path, direct_target):
182 print("Failed to apply patch: {}".format(patch))
183 break
184 elif d in vendor_crate_map:
185 for crate in vendor_crate_map[d]:
186 checksums_for[crate] = True
187 target = os.path.join(vendor_path, crate)
188 if not apply_single_patch(file_path, target):
189 print(f'Failed to apply patch {patch} to {target}')
190 break
191 else:
192 raise RuntimeError(f'Unknown crate in {vendor_path}: {d}')
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000193
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000194 # Re-run checksums for all modified packages since we applied patches.
195 for key in checksums_for.keys():
196 _rerun_checksums(os.path.join(vendor_path, key))
197
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700198
Abhishek Pandit-Subedifa902382021-08-20 11:04:33 -0700199def run_cargo_vendor(working_dir):
200 """Runs cargo vendor.
201
202 Args:
203 working_dir: Directory to run inside. This should be the directory where
Abhishek Pandit-Subedice0f5b22021-09-10 15:50:08 -0700204 Cargo.toml is kept.
Abhishek Pandit-Subedifa902382021-08-20 11:04:33 -0700205 """
George Burgess IV635f7262022-08-09 21:32:20 -0700206 # Cargo will refuse to revendor into versioned directories, which leads to
207 # repeated `./vendor.py` invocations trying to apply patches to
208 # already-patched sources. Remove the existing vendor directory to avoid
209 # this.
210 vendor_dir = working_dir / 'vendor'
211 if vendor_dir.exists():
212 shutil.rmtree(vendor_dir)
213 subprocess.check_call(
214 ['cargo', 'vendor', '--versioned-dirs', '-v'],
215 cwd=working_dir,
216 )
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000217
Abhishek Pandit-Subedice0f5b22021-09-10 15:50:08 -0700218
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700219def load_metadata(working_dir, filter_platform=DEFAULT_PLATFORM_FILTER):
220 """Load metadata for manifest at given directory.
221
222 Args:
223 working_dir: Directory to run from.
224 filter_platform: Filter packages to ones configured for this platform.
225 """
226 manifest_path = os.path.join(working_dir, 'Cargo.toml')
227 cmd = [
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700228 'cargo', 'metadata', '--format-version', '1', '--manifest-path',
229 manifest_path
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700230 ]
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700231
232 # Conditionally add platform filter
233 if filter_platform:
234 cmd.append("--filter-platform")
235 cmd.append(filter_platform)
236
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700237 output = subprocess.check_output(cmd, cwd=working_dir)
238
239 return json.loads(output)
240
241
242class LicenseManager:
243 """ Manage consolidating licenses for all packages."""
244
245 # These are all the licenses we support. Keys are what is seen in metadata and
246 # values are what is expected by the ebuild.
247 SUPPORTED_LICENSES = {
248 'Apache-2.0': 'Apache-2.0',
249 'MIT': 'MIT',
250 'BSD-3-Clause': 'BSD-3',
251 'ISC': 'ISC'
252 }
253
254 # Prefer to take attribution licenses in this order. All these require that
255 # we actually use the license file found in the package so they MUST have
256 # a license file set.
257 PREFERRED_ATTRIB_LICENSE_ORDER = ['MIT', 'BSD-3', 'ISC']
258
259 # If Apache license is found, always prefer it (simplifies attribution)
260 APACHE_LICENSE = 'Apache-2.0'
261
262 # Regex for license files found in the vendored directories. Search for
263 # these files with re.IGNORECASE.
264 #
265 # These will be searched in order with the earlier entries being preferred.
266 LICENSE_NAMES_REGEX = [
267 r'^license-mit$',
268 r'^copyright$',
269 r'^licen[cs]e.*$',
270 ]
271
272 # Some crates have their license file in other crates. This usually occurs
273 # because multiple crates are published from the same git repository and the
274 # license isn't updated in each sub-crate. In these cases, we can just
275 # ignore these packages.
276 MAP_LICENSE_TO_OTHER = {
277 'failure_derive': 'failure',
278 'grpcio-compiler': 'grpcio',
279 'grpcio-sys': 'grpcio',
280 'rustyline-derive': 'rustyline',
281 }
282
283 # Map a package to a specific license and license file. Only use this if
284 # a package doesn't have an easily discoverable license or exports its
285 # license in a weird way. Prefer to patch the project with a license and
286 # upstream the patch instead.
287 STATIC_LICENSE_MAP = {
288 # "package name": ( "license name", "license file relative location")
289 }
290
291 def __init__(self, working_dir, vendor_dir):
292 self.working_dir = working_dir
293 self.vendor_dir = vendor_dir
294
295 def _find_license_in_dir(self, search_dir):
296 for p in os.listdir(search_dir):
297 # Ignore anything that's not a file
298 if not os.path.isfile(os.path.join(search_dir, p)):
299 continue
300
301 # Now check if the name matches any of the regexes
302 # We'll return the first matching file.
303 for regex in self.LICENSE_NAMES_REGEX:
304 if re.search(regex, p, re.IGNORECASE):
305 yield os.path.join(search_dir, p)
306 break
307
308 def _guess_license_type(self, license_file):
309 if '-MIT' in license_file:
310 return 'MIT'
311 elif '-APACHE' in license_file:
312 return 'APACHE'
313 elif '-BSD' in license_file:
314 return 'BSD-3'
315
316 with open(license_file, 'r') as f:
317 lines = f.read()
318 if 'MIT' in lines:
319 return 'MIT'
320 elif 'Apache' in lines:
321 return 'APACHE'
322 elif 'BSD 3-Clause' in lines:
323 return 'BSD-3'
324
325 return ''
326
327 def generate_license(self, skip_license_check, print_map_to_file):
328 """Generate single massive license file from metadata."""
329 metadata = load_metadata(self.working_dir)
330
331 has_license_types = set()
332 bad_licenses = {}
333
334 # Keep license map ordered so it generates a consistent license map
335 license_map = {}
336
337 skip_license_check = skip_license_check or []
338
339 for package in metadata['packages']:
340 pkg_name = package['name']
341
342 # Skip vendor libs directly
343 if pkg_name == "vendor_libs":
344 continue
345
346 if pkg_name in skip_license_check:
347 print(
348 "Skipped license check on {}. Reason: Skipped from command line"
349 .format(pkg_name))
350 continue
351
352 if pkg_name in self.MAP_LICENSE_TO_OTHER:
353 print(
354 'Skipped license check on {}. Reason: License already in {}'
355 .format(pkg_name, self.MAP_LICENSE_TO_OTHER[pkg_name]))
356 continue
357
358 # Check if we have a static license map for this package. Use the
359 # static values if we have it already set.
360 if pkg_name in self.STATIC_LICENSE_MAP:
361 (license, license_file) = self.STATIC_LICENSE_MAP[pkg_name]
362 license_map[pkg_name] = {
363 "license": license,
364 "license_file": license_file,
365 }
366 continue
367
368 license_files = []
369 license = package.get('license', '')
370
371 # We ignore the metadata for license file because most crates don't
372 # have it set. Just scan the source for licenses.
George Burgess IV635f7262022-08-09 21:32:20 -0700373 pkg_version = package['version']
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700374 license_files = [
375 x for x in self._find_license_in_dir(
George Burgess IV635f7262022-08-09 21:32:20 -0700376 os.path.join(self.vendor_dir, f'{pkg_name}-{pkg_version}'))
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700377 ]
378
379 # If there are multiple licenses, they are delimited with "OR" or "/"
380 delim = ' OR ' if ' OR ' in license else '/'
381 found = license.split(delim)
382
383 # Filter licenses to ones we support
384 licenses_or = [
385 self.SUPPORTED_LICENSES[f] for f in found
386 if f in self.SUPPORTED_LICENSES
387 ]
388
389 # If apache license is found, always prefer it because it simplifies
390 # license attribution (we can use existing Apache notice)
391 if self.APACHE_LICENSE in licenses_or:
392 has_license_types.add(self.APACHE_LICENSE)
393 license_map[pkg_name] = {'license': self.APACHE_LICENSE}
394
395 # Handle single license that has at least one license file
396 # We pick the first license file and the license
397 elif len(licenses_or) == 1:
398 if license_files:
399 l = licenses_or[0]
400 lf = license_files[0]
401
402 has_license_types.add(l)
403 license_map[pkg_name] = {
404 'license': l,
405 'license_file': os.path.relpath(lf, self.working_dir),
406 }
407 else:
408 bad_licenses[pkg_name] = "{} missing license file".format(
409 licenses_or[0])
410 # Handle multiple licenses
411 elif len(licenses_or) > 1:
412 # Check preferred licenses in order
413 license_found = False
414 for l in self.PREFERRED_ATTRIB_LICENSE_ORDER:
415 if not l in licenses_or:
416 continue
417
418 for f in license_files:
419 if self._guess_license_type(f) == l:
420 license_found = True
421 has_license_types.add(l)
422 license_map[pkg_name] = {
423 'license':
424 l,
425 'license_file':
426 os.path.relpath(f, self.working_dir),
427 }
428 break
429
430 # Break out of loop if license is found
431 if license_found:
432 break
433 else:
434 bad_licenses[pkg_name] = license
435
436 # If we had any bad licenses, we need to abort
437 if bad_licenses:
438 for k in bad_licenses.keys():
439 print("{} had no acceptable licenses: {}".format(
440 k, bad_licenses[k]))
441 raise Exception("Bad licenses in vendored packages.")
442
443 # Write license map to file
444 if print_map_to_file:
445 with open(os.path.join(self.working_dir, print_map_to_file),
446 'w') as lfile:
447 json.dump(license_map, lfile, sort_keys=True)
448
449 # Raise missing licenses unless we have a valid reason to ignore them
450 raise_missing_license = False
451 for name, v in license_map.items():
452 if 'license_file' not in v and v.get('license',
453 '') != self.APACHE_LICENSE:
454 raise_missing_license = True
455 print(" {}: Missing license file. Fix or add to ignorelist.".
456 format(name))
457
458 if raise_missing_license:
459 raise Exception(
460 "Unhandled missing license file. "
461 "Make sure all are accounted for before continuing.")
462
463 print("Add the following licenses to the ebuild: \n",
464 sorted([x for x in has_license_types]))
465
466
Abhishek Pandit-Subedice0f5b22021-09-10 15:50:08 -0700467# TODO(abps) - This needs to be replaced with datalog later. We should compile
468# all crab files into datalog and query it with our requirements
469# instead.
470class CrabManager:
471 """Manage audit files."""
472 def __init__(self, working_dir, crab_dir):
473 self.working_dir = working_dir
474 self.crab_dir = crab_dir
475
476 def _check_bad_traits(self, crabdata):
477 """Checks that a package's crab audit meets our requirements.
478
479 Args:
480 crabdata: Dict with crab keys in standard templated format.
481 """
482 common = crabdata['common']
483 # TODO(b/200578411) - Figure out what conditions we should enforce as
484 # part of the audit.
485 conditions = [
486 common.get('deny', None),
487 ]
488
489 # If any conditions are true, this crate is not acceptable.
490 return any(conditions)
491
492 def verify_traits(self):
493 """ Verify that all required CRAB traits for this repository are met.
494 """
495 metadata = load_metadata(self.working_dir)
496
497 failing_crates = {}
498
499 # Verify all packages have a CRAB file associated with it and they meet
500 # all our required traits
501 for package in metadata['packages']:
502 # Skip vendor_libs
503 if package['name'] == 'vendor_libs':
504 continue
505
506 crabname = "{}-{}".format(package['name'], package['version'])
507 filename = os.path.join(self.crab_dir, "{}.toml".format(crabname))
508
509 # If crab file doesn't exist, the crate fails
510 if not os.path.isfile(filename):
511 failing_crates[crabname] = "No crab file".format(filename)
512 continue
513
514 with open(filename, 'r') as f:
515 crabdata = toml.loads(f.read())
516
517 # If crab file's crate_name and version keys don't match this
518 # package, it also fails. This is just housekeeping...
519 if package['name'] != crabdata['crate_name'] or package[
520 'version'] != crabdata['version']:
521 failing_crates[crabname] = "Crate name or version don't match"
522 continue
523
524 if self._check_bad_traits(crabdata):
525 failing_crates[crabname] = "Failed bad traits check"
526
527 # If we had any failing crates, list them now
528 if failing_crates:
529 print('Failed CRAB audit:')
530 for k, v in failing_crates.items():
531 print(' {}: {}'.format(k, v))
532
533
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700534class CrateDestroyer():
535 LIB_RS_BODY = """compile_error!("This crate cannot be built for this configuration.");\n"""
536
537 def __init__(self, working_dir, vendor_dir):
538 self.working_dir = working_dir
539 self.vendor_dir = vendor_dir
540
541 def _modify_cargo_toml(self, pkg_path):
542 with open(os.path.join(pkg_path, "Cargo.toml"), "r") as cargo:
543 contents = toml.load(cargo)
544
545 # Change description, license and delete license key
546 contents["package"]["description"] = "Empty crate that should not build."
547 contents["package"]["license"] = "Apache-2.0"
548 if contents["package"].get("license_file"):
549 del contents["package"]["license_file"]
550
551 with open(os.path.join(pkg_path, "Cargo.toml"), "w") as cargo:
552 toml.dump(contents, cargo)
553
554 def _replace_source_contents(self, package_path):
555 # First load the checksum file before starting
556 checksum_file = os.path.join(package_path, ".cargo-checksum.json")
557 with open(checksum_file, 'r') as csum:
558 checksum_contents = json.load(csum)
559
560 # Also load the cargo.toml file which we need to write back
561 cargo_file = os.path.join(package_path, "Cargo.toml")
George Burgess IV3e344e42022-08-09 21:07:04 -0700562 with open(cargo_file, 'rb') as cfile:
563 cargo_contents = cfile.read()
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700564
565 shutil.rmtree(package_path)
566
567 # Make package and src dirs and replace lib.rs
568 os.makedirs(os.path.join(package_path, "src"), exist_ok=True)
569 with open(os.path.join(package_path, "src", "lib.rs"), "w") as librs:
570 librs.write(self.LIB_RS_BODY)
571
572 # Restore cargo.toml
George Burgess IV3e344e42022-08-09 21:07:04 -0700573 with open(cargo_file, 'wb') as cfile:
574 cfile.write(cargo_contents)
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700575
576 # Restore checksum
577 with open(checksum_file, 'w') as csum:
578 json.dump(checksum_contents, csum)
579
580 def destroy_unused_crates(self):
581 all_packages = load_metadata(self.working_dir, filter_platform=None)
582 used_packages = set([p["name"] for p in load_metadata(self.working_dir)["packages"]])
583
584 cleaned_packages = []
585 for package in all_packages["packages"]:
586
587 # Skip used packages
588 if package["name"] in used_packages:
589 continue
590
591 # Detect the correct package path to destroy
592 pkg_path = os.path.join(self.vendor_dir, "{}-{}".format(package["name"], package["version"]))
593 if not os.path.isdir(pkg_path):
George Burgess IV635f7262022-08-09 21:32:20 -0700594 print(f'Crate {package["name"]} not found at {pkg_path}')
595 continue
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700596
597 self._replace_source_contents(pkg_path)
598 self._modify_cargo_toml(pkg_path)
599 _rerun_checksums(pkg_path)
600 cleaned_packages.append(package["name"])
601
602 for pkg in cleaned_packages:
George Burgess IV635f7262022-08-09 21:32:20 -0700603 print("Removed unused crate", pkg)
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700604
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700605def main(args):
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -0800606 current_path = pathlib.Path(__file__).parent.absolute()
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000607 patches = os.path.join(current_path, "patches")
608 vendor = os.path.join(current_path, "vendor")
Abhishek Pandit-Subedice0f5b22021-09-10 15:50:08 -0700609 crab_dir = os.path.join(current_path, "crab", "crates")
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -0800610
Abhishek Pandit-Subedifa902382021-08-20 11:04:33 -0700611 # First, actually run cargo vendor
612 run_cargo_vendor(current_path)
613
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000614 # Order matters here:
615 # - Apply patches (also re-calculates checksums)
616 # - Cleanup any owners files (otherwise, git check-in or checksums are
617 # unhappy)
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700618 # - Destroy unused crates
Abhishek Pandit-Subedi5065a0f2021-06-13 20:38:55 +0000619 apply_patches(patches, vendor)
620 cleanup_owners(vendor)
Abhishek Pandit-Subedif0eb6e02021-09-24 16:36:12 -0700621 destroyer = CrateDestroyer(current_path, vendor)
622 destroyer.destroy_unused_crates()
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -0800623
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700624 # Combine license file and check for any bad licenses
625 lm = LicenseManager(current_path, vendor)
626 lm.generate_license(args.skip_license_check, args.license_map)
627
Abhishek Pandit-Subedice0f5b22021-09-10 15:50:08 -0700628 # Run crab audit on all packages
629 crab = CrabManager(current_path, crab_dir)
630 crab.verify_traits()
631
Abhishek Pandit-Subedib75bd562021-02-25 15:32:22 -0800632
633if __name__ == '__main__':
Abhishek Pandit-Subedie393cb72021-08-22 10:41:13 -0700634 parser = argparse.ArgumentParser(description='Vendor packages properly')
635 parser.add_argument('--skip-license-check',
636 '-s',
637 help='Skip the license check on a specific package',
638 action='append')
639 parser.add_argument('--license-map', help='Write license map to this file')
640 args = parser.parse_args()
641
642 main(args)