blob: cd82f3a3da1ca7002029a9cda64bf1c15ba083ca [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(
Uwem Wilson05b28b52022-06-29 21:16:37 +000095 '--no-golint',
96 dest='golint',
97 action='store_false',
98 help='Disable golint linter.')
99 parser.add_argument(
Ryan Beltrandbd7b812022-06-08 23:36:16 +0000100 'packages', nargs='*', help='package(s) to emerge and retrieve lints for')
Ryan Beltran1f2dd082022-04-25 18:42:32 +0000101 return parser
102
103
104def parse_args(argv: List[str]):
105 """Parses arguments in argv and returns the options."""
106 parser = get_arg_parser()
107 opts = parser.parse_args(argv)
108 opts.Freeze()
Ryan Beltrandbd7b812022-06-08 23:36:16 +0000109
110 # A package must be specified unless we are in fetch-only mode
111 if not(opts.fetch_only or opts.packages):
112 parser.error('Emerge requires specified package(s).')
113 if opts.fetch_only and opts.packages:
114 parser.error('Cannot specify packages for fetch-only mode.')
115
Ryan Beltran1f2dd082022-04-25 18:42:32 +0000116 return opts
117
118
119def main(argv: List[str]) -> None:
120 cros_build_lib.AssertInsideChroot()
121 opts = parse_args(argv)
122
Ryan Beltranb619a112022-06-22 21:14:58 +0000123 if opts.host:
124 # BuildTarget interprets None as host target
125 build_target = build_target_lib.BuildTarget(None)
126 else:
127 build_target = build_target_lib.BuildTarget(opts.board)
Ryan Beltran5514eab2022-04-28 21:40:24 +0000128 packages = parse_packages(build_target, opts.packages)
129 package_atoms = [x.atom for x in packages]
Ryan Beltran1f2dd082022-04-25 18:42:32 +0000130
Ryan Beltran5514eab2022-04-28 21:40:24 +0000131 with workon_helper.WorkonScope(build_target, package_atoms):
132 build_linter = toolchain.BuildLinter(packages, build_target.root,
133 opts.differential)
Ryan Beltrandbd7b812022-06-08 23:36:16 +0000134 if opts.fetch_only:
135 lints = build_linter.fetch_findings(
Uwem Wilson05b28b52022-06-29 21:16:37 +0000136 use_clippy=opts.clippy, use_tidy=opts.tidy, use_golint=opts.golint)
Ryan Beltrandbd7b812022-06-08 23:36:16 +0000137 else:
138 lints = build_linter.emerge_with_linting(
Uwem Wilson05b28b52022-06-29 21:16:37 +0000139 use_clippy=opts.clippy, use_tidy=opts.tidy, use_golint=opts.golint)
Ryan Beltran1f2dd082022-04-25 18:42:32 +0000140
141 with file_util.Open(opts.output, 'w') as output_file:
142 json.dump(lints, output_file)