blob: f37e7ceceb4b6b30c965d9d1e18cfa3f5dfbd400 [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 Beltranb619a112022-06-22 21:14:58 +000041 if build_target.is_host():
42 ebuild_path = portage_util.FindEbuildForPackage(
43 package, build_target.root)
44 else:
45 ebuild_path = portage_util.FindEbuildForBoardPackage(
46 package, build_target.name, build_target.root)
Ryan Beltranb2175862022-04-28 19:55:57 +000047 ebuild_data = portage_util.EBuild(ebuild_path)
48 parsed = package_info.parse(ebuild_data.package)
49 package_infos.append(parsed)
50 return package_infos
51
52
Ryan Beltran1f2dd082022-04-25 18:42:32 +000053def get_arg_parser() -> commandline.ArgumentParser:
54 """Creates an argument parser for this script."""
55 default_board = cros_build_lib.GetDefaultBoard()
56 parser = commandline.ArgumentParser(description=__doc__)
Ryan Beltrandbd7b812022-06-08 23:36:16 +000057
58 board_group = parser.add_mutually_exclusive_group(required=not default_board)
59 board_group.add_argument(
Ryan Beltran1f2dd082022-04-25 18:42:32 +000060 '-b',
61 '--board',
62 '--build-target',
63 dest='board',
64 default=default_board,
Ryan Beltran1f2dd082022-04-25 18:42:32 +000065 help='The board to emerge packages for')
Ryan Beltrandbd7b812022-06-08 23:36:16 +000066 board_group.add_argument(
Ryan Beltranb619a112022-06-22 21:14:58 +000067 '--host',
68 action='store_true',
69 help='emerge for host instead of board.')
70 board_group.add_argument(
Ryan Beltrandbd7b812022-06-08 23:36:16 +000071 '--fetch-only',
72 action='store_true',
73 help='Fetch lints from previous run without reseting or calling emerge.')
74
75 parser.add_argument(
76 '--differential',
77 action='store_true',
78 help='only lint lines touched by the last commit')
Ryan Beltran1f2dd082022-04-25 18:42:32 +000079 parser.add_argument(
80 '-o',
81 '--output',
82 default=sys.stdout,
83 help='File to use instead of stdout.')
84 parser.add_argument(
85 '--no-clippy',
86 dest='clippy',
87 action='store_false',
88 help='Disable cargo clippy linter.')
89 parser.add_argument(
90 '--no-tidy',
91 dest='tidy',
92 action='store_false',
93 help='Disable clang tidy linter.')
94 parser.add_argument(
Ryan Beltrandbd7b812022-06-08 23:36:16 +000095 'packages', nargs='*', help='package(s) to emerge and retrieve lints for')
Ryan Beltran1f2dd082022-04-25 18:42:32 +000096 return parser
97
98
99def parse_args(argv: List[str]):
100 """Parses arguments in argv and returns the options."""
101 parser = get_arg_parser()
102 opts = parser.parse_args(argv)
103 opts.Freeze()
Ryan Beltrandbd7b812022-06-08 23:36:16 +0000104
105 # A package must be specified unless we are in fetch-only mode
106 if not(opts.fetch_only or opts.packages):
107 parser.error('Emerge requires specified package(s).')
108 if opts.fetch_only and opts.packages:
109 parser.error('Cannot specify packages for fetch-only mode.')
110
Ryan Beltran1f2dd082022-04-25 18:42:32 +0000111 return opts
112
113
114def main(argv: List[str]) -> None:
115 cros_build_lib.AssertInsideChroot()
116 opts = parse_args(argv)
117
Ryan Beltranb619a112022-06-22 21:14:58 +0000118 if opts.host:
119 # BuildTarget interprets None as host target
120 build_target = build_target_lib.BuildTarget(None)
121 else:
122 build_target = build_target_lib.BuildTarget(opts.board)
Ryan Beltran5514eab2022-04-28 21:40:24 +0000123 packages = parse_packages(build_target, opts.packages)
124 package_atoms = [x.atom for x in packages]
Ryan Beltran1f2dd082022-04-25 18:42:32 +0000125
Ryan Beltran5514eab2022-04-28 21:40:24 +0000126 with workon_helper.WorkonScope(build_target, package_atoms):
127 build_linter = toolchain.BuildLinter(packages, build_target.root,
128 opts.differential)
Ryan Beltrandbd7b812022-06-08 23:36:16 +0000129 if opts.fetch_only:
130 lints = build_linter.fetch_findings(
131 use_clippy=opts.clippy, use_tidy=opts.tidy)
132 else:
133 lints = build_linter.emerge_with_linting(
134 use_clippy=opts.clippy, use_tidy=opts.tidy)
Ryan Beltran1f2dd082022-04-25 18:42:32 +0000135
136 with file_util.Open(opts.output, 'w') as output_file:
137 json.dump(lints, output_file)