blob: f3eae967b5458b33e315e64a82c1d366600e3a08 [file] [log] [blame]
Ryan Beltran1f2dd082022-04-25 18:42:32 +00001# Copyright 2022 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""This script emerges packages and retrieves their lints.
6
7Currently support is provided for both general and differential linting of C++
8with Clang Tidy and Rust with Cargo Clippy for all packages within platform2.
9"""
10
11import json
12import sys
13from typing import List
14
15from chromite.lib import build_target_lib
16from chromite.lib import commandline
17from chromite.lib import cros_build_lib
Ryan Beltranb2175862022-04-28 19:55:57 +000018from chromite.lib import portage_util
Ryan Beltran5514eab2022-04-28 21:40:24 +000019from chromite.lib import workon_helper
Ryan Beltran1f2dd082022-04-25 18:42:32 +000020from chromite.lib.parser import package_info
21from chromite.service import toolchain
22from chromite.utils import file_util
23
24
Ryan Beltran5514eab2022-04-28 21:40:24 +000025def parse_packages(build_target: build_target_lib.BuildTarget,
Ryan Beltranb2175862022-04-28 19:55:57 +000026 packages: List[str]) -> List[package_info.PackageInfo]:
27 """Parse packages and insert the category if none is given.
28
29 Args:
Ryan Beltran5514eab2022-04-28 21:40:24 +000030 build_target: build_target to find ebuild for
Ryan Beltranb2175862022-04-28 19:55:57 +000031 packages: user input package names to parse
32
33 Returns:
34 A list of parsed PackageInfo objects
35 """
36 package_infos: List[package_info.PackageInfo] = []
37 for package in packages:
38 parsed = package_info.parse(package)
39 if not parsed.category:
40 # If a category is not specified, we can get it from the ebuild path.
Ryan Beltran5514eab2022-04-28 21:40:24 +000041 ebuild_path = portage_util.FindEbuildForBoardPackage(
42 package, build_target.name, build_target.root)
Ryan Beltranb2175862022-04-28 19:55:57 +000043 ebuild_data = portage_util.EBuild(ebuild_path)
44 parsed = package_info.parse(ebuild_data.package)
45 package_infos.append(parsed)
46 return package_infos
47
48
Ryan Beltran1f2dd082022-04-25 18:42:32 +000049def get_arg_parser() -> commandline.ArgumentParser:
50 """Creates an argument parser for this script."""
51 default_board = cros_build_lib.GetDefaultBoard()
52 parser = commandline.ArgumentParser(description=__doc__)
Ryan Beltrandbd7b812022-06-08 23:36:16 +000053
54 board_group = parser.add_mutually_exclusive_group(required=not default_board)
55 board_group.add_argument(
Ryan Beltran1f2dd082022-04-25 18:42:32 +000056 '-b',
57 '--board',
58 '--build-target',
59 dest='board',
60 default=default_board,
Ryan Beltran1f2dd082022-04-25 18:42:32 +000061 help='The board to emerge packages for')
Ryan Beltrandbd7b812022-06-08 23:36:16 +000062 board_group.add_argument(
63 '--fetch-only',
64 action='store_true',
65 help='Fetch lints from previous run without reseting or calling emerge.')
66
67 parser.add_argument(
68 '--differential',
69 action='store_true',
70 help='only lint lines touched by the last commit')
Ryan Beltran1f2dd082022-04-25 18:42:32 +000071 parser.add_argument(
72 '-o',
73 '--output',
74 default=sys.stdout,
75 help='File to use instead of stdout.')
76 parser.add_argument(
77 '--no-clippy',
78 dest='clippy',
79 action='store_false',
80 help='Disable cargo clippy linter.')
81 parser.add_argument(
82 '--no-tidy',
83 dest='tidy',
84 action='store_false',
85 help='Disable clang tidy linter.')
86 parser.add_argument(
Ryan Beltrandbd7b812022-06-08 23:36:16 +000087 'packages', nargs='*', help='package(s) to emerge and retrieve lints for')
Ryan Beltran1f2dd082022-04-25 18:42:32 +000088 return parser
89
90
91def parse_args(argv: List[str]):
92 """Parses arguments in argv and returns the options."""
93 parser = get_arg_parser()
94 opts = parser.parse_args(argv)
95 opts.Freeze()
Ryan Beltrandbd7b812022-06-08 23:36:16 +000096
97 # A package must be specified unless we are in fetch-only mode
98 if not(opts.fetch_only or opts.packages):
99 parser.error('Emerge requires specified package(s).')
100 if opts.fetch_only and opts.packages:
101 parser.error('Cannot specify packages for fetch-only mode.')
102
Ryan Beltran1f2dd082022-04-25 18:42:32 +0000103 return opts
104
105
106def main(argv: List[str]) -> None:
107 cros_build_lib.AssertInsideChroot()
108 opts = parse_args(argv)
109
Ryan Beltran5514eab2022-04-28 21:40:24 +0000110 build_target = build_target_lib.BuildTarget(opts.board)
111 packages = parse_packages(build_target, opts.packages)
112 package_atoms = [x.atom for x in packages]
Ryan Beltran1f2dd082022-04-25 18:42:32 +0000113
Ryan Beltran5514eab2022-04-28 21:40:24 +0000114 with workon_helper.WorkonScope(build_target, package_atoms):
115 build_linter = toolchain.BuildLinter(packages, build_target.root,
116 opts.differential)
Ryan Beltrandbd7b812022-06-08 23:36:16 +0000117 if opts.fetch_only:
118 lints = build_linter.fetch_findings(
119 use_clippy=opts.clippy, use_tidy=opts.tidy)
120 else:
121 lints = build_linter.emerge_with_linting(
122 use_clippy=opts.clippy, use_tidy=opts.tidy)
Ryan Beltran1f2dd082022-04-25 18:42:32 +0000123
124 with file_util.Open(opts.output, 'w') as output_file:
125 json.dump(lints, output_file)