blob: 01ed4907522d22c1f6428b898875a41ad45d6c91 [file] [log] [blame]
Mike Frysingerf1ba7ad2022-09-12 05:42:57 -04001# Copyright 2022 The ChromiumOS Authors
Ryan Beltran1f2dd082022-04-25 18:42:32 +00002# 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
Ryan Beltran179d6bb2023-09-14 23:37:33 +000011import collections
Ryan Beltran1f2dd082022-04-25 18:42:32 +000012import json
Ryan Beltran179d6bb2023-09-14 23:37:33 +000013import logging
Ryan Beltran378934c2022-11-23 00:44:26 +000014import os
15from pathlib import Path
Ryan Beltran1f2dd082022-04-25 18:42:32 +000016import sys
Ryan Beltran179d6bb2023-09-14 23:37:33 +000017from typing import DefaultDict, Dict, Iterable, List, Optional, Text, Tuple
Ryan Beltran1f2dd082022-04-25 18:42:32 +000018
19from chromite.lib import build_target_lib
20from chromite.lib import commandline
Ryan Beltran4706f342023-08-29 01:16:22 +000021from chromite.lib import constants
Ryan Beltran1f2dd082022-04-25 18:42:32 +000022from chromite.lib import cros_build_lib
Ryan Beltranf3a7c812023-09-27 22:17:13 +000023from chromite.lib import git
Ryan Beltranb2175862022-04-28 19:55:57 +000024from chromite.lib import portage_util
Ryan Beltrance85d0f2022-08-09 21:36:39 +000025from chromite.lib import terminal
Ryan Beltran5514eab2022-04-28 21:40:24 +000026from chromite.lib import workon_helper
Ryan Beltran1f2dd082022-04-25 18:42:32 +000027from chromite.lib.parser import package_info
28from chromite.service import toolchain
29from chromite.utils import file_util
30
31
Ryan Beltran179d6bb2023-09-14 23:37:33 +000032PLATFORM2_PATH = constants.CHROOT_SOURCE_ROOT / "src/platform2"
33
34
Ryan Beltranf3a7c812023-09-27 22:17:13 +000035def create_fixes_cl(formatted_fixes: Text, bug: Optional[Text]):
36 """Make a commit in src/platform2 with all changes."""
37 message = (
38 "Apply generated linter fixes\n\n"
39 "This CL was generated by the lint_package chromite script.\n"
40 f"The following lints should be fixed:\n\n{formatted_fixes}\n\n"
41 f"BUG={bug}\n"
42 "TEST=CQ\n"
43 )
44
45 git.RunGit(PLATFORM2_PATH, ["add", "--all"])
46 git.Commit(PLATFORM2_PATH, message)
47
48
49def check_plat2_diff() -> bool:
50 """Check if src/platform2 has changes in it's diff."""
51 diff = git.RawDiff(PLATFORM2_PATH, ".")
52 return bool(diff)
53
54
Alex Klein1699fab2022-09-08 08:46:06 -060055def parse_packages(
56 build_target: build_target_lib.BuildTarget, packages: List[str]
57) -> List[package_info.PackageInfo]:
58 """Parse packages and insert the category if none is given.
Ryan Beltranb2175862022-04-28 19:55:57 +000059
Alex Klein1699fab2022-09-08 08:46:06 -060060 Args:
Alex Klein68b270c2023-04-14 14:42:50 -060061 build_target: build_target to find ebuild for
62 packages: user input package names to parse
Ryan Beltranb2175862022-04-28 19:55:57 +000063
Alex Klein1699fab2022-09-08 08:46:06 -060064 Returns:
Alex Klein68b270c2023-04-14 14:42:50 -060065 A list of parsed PackageInfo objects
Alex Klein1699fab2022-09-08 08:46:06 -060066 """
67 package_infos: List[package_info.PackageInfo] = []
68 for package in packages:
69 parsed = package_info.parse(package)
70 if not parsed.category:
Alex Klein68b270c2023-04-14 14:42:50 -060071 # If a category is not specified, get it from the ebuild path.
Alex Klein1699fab2022-09-08 08:46:06 -060072 if build_target.is_host():
73 ebuild_path = portage_util.FindEbuildForPackage(
74 package, build_target.root
75 )
76 else:
77 ebuild_path = portage_util.FindEbuildForBoardPackage(
78 package, build_target.name, build_target.root
79 )
80 ebuild_data = portage_util.EBuild(ebuild_path)
81 parsed = package_info.parse(ebuild_data.package)
82 package_infos.append(parsed)
83 return package_infos
Ryan Beltranb2175862022-04-28 19:55:57 +000084
85
Ryan Beltran4706f342023-08-29 01:16:22 +000086def make_relative_to_cros(file_path: str) -> Path:
87 """removes /mnt/host/source from file_paths if present."""
88 path = Path(file_path)
89 try:
90 return path.relative_to(constants.CHROOT_SOURCE_ROOT)
91 except ValueError:
92 return path
93
94
Ryan Beltran179d6bb2023-09-14 23:37:33 +000095def process_fixes_by_file(
Ryan Beltrane522fe32023-09-26 23:06:16 +000096 lint: toolchain.LinterFinding,
97 file_lengths: Dict[Path, int],
98 allowed_subdirs: Optional[List[Text]],
Ryan Beltran179d6bb2023-09-14 23:37:33 +000099) -> Optional[DefaultDict[Path, List[toolchain.SuggestedFix]]]:
100 """Get fixes grouped by file if all the fixes apply to valid files.
101
102 If any fixes modify invalid files this returns None.
Ryan Beltranf83a3972023-09-21 00:22:25 +0000103
104 Args:
105 lint: LinterFinding to get fixes from
106 file_lengths: dictionary of previously determined file lengths which
107 may be modified with additional entries
Ryan Beltrane522fe32023-09-26 23:06:16 +0000108 allowed_subdirs: subdirectories in platform2 that we can modify or none
Ryan Beltran179d6bb2023-09-14 23:37:33 +0000109 """
110 if not lint.suggested_fixes:
111 return None
112
113 new_fixes_by_file: DefaultDict[
114 Path, List[toolchain.SuggestedFix]
115 ] = collections.defaultdict(list)
116 for fix in lint.suggested_fixes:
117 filepath = Path(fix.location.filepath)
118 # These are files that we locate, and are usually generated files.
119 if filepath.is_absolute():
120 logging.warning(
121 "Skipped applying fix due to invalid path: %s", filepath
122 )
123 return None
Ryan Beltrane522fe32023-09-26 23:06:16 +0000124 # Check if file is in an allowed subdirectory.
125 if allowed_subdirs and filepath.parts[0] not in allowed_subdirs:
126 logging.warning(
127 "Skipped applying fix due to invalid path: %s", filepath
128 )
129 return None
Ryan Beltran179d6bb2023-09-14 23:37:33 +0000130 # Make sure this file exists in platform2
131 file_in_platform2 = PLATFORM2_PATH / filepath
132 if not file_in_platform2.exists():
133 logging.warning(
134 "Skipped applying fix due to invalid path: %s", filepath
135 )
136 return None
137 if file_in_platform2 not in file_lengths:
138 file_lengths[file_in_platform2] = len(
139 file_in_platform2.read_text(encoding="utf-8")
140 )
141 if fix.location.end_offset > file_lengths[file_in_platform2]:
142 logging.warning(
143 "Skipped applying fix due to out of bounds change to: %s",
144 filepath,
145 )
146 return None
147 new_fixes_by_file[file_in_platform2].append(fix)
148
149 return new_fixes_by_file
150
151
152def get_noconflict_fixes(
153 lints: List[toolchain.LinterFinding],
Ryan Beltrane522fe32023-09-26 23:06:16 +0000154 allowed_subdirs: Optional[List[Text]],
Ryan Beltran179d6bb2023-09-14 23:37:33 +0000155) -> Tuple[
156 DefaultDict[Path, List[toolchain.SuggestedFix]],
157 List[toolchain.LinterFinding],
158]:
159 """Get a conflict free set of replacements to apply for each file.
160
161 Fixes will not be included in results if they:
162 A) include a replacement to a path which does not exist
163 B) include a replacement to a path outside of platform2
164 C) include a replacement to file location that exceeds the file size
165 D) overlap a previous replacement.
166
167 Args:
168 lints: List of lints to aggregate suggested fixes from.
Ryan Beltrane522fe32023-09-26 23:06:16 +0000169 allowed_subdirs: subdirectories in platform2 that we can modify or none
Ryan Beltran179d6bb2023-09-14 23:37:33 +0000170
171 Returns:
172 A tuple including:
173 0) the mapping of paths to a list of their suggested fixes
174 1) the list of lints which were fixed
175 """
176 fixes_by_file: DefaultDict[
177 Path, List[toolchain.SuggestedFix]
178 ] = collections.defaultdict(list)
179 lints_fixed = []
180 file_lengths = {}
181 for lint in lints:
Ryan Beltrane522fe32023-09-26 23:06:16 +0000182 new_fixes_by_file = process_fixes_by_file(
183 lint, file_lengths, allowed_subdirs
184 )
Ryan Beltran179d6bb2023-09-14 23:37:33 +0000185 if not new_fixes_by_file:
186 continue
187 files_with_overlap = set(
188 filepath
189 for filepath, new_fixes in new_fixes_by_file.items()
190 if has_overlap(fixes_by_file[filepath], new_fixes)
191 )
192 if files_with_overlap:
193 logging.warning(
194 "Skipped applying fix for %s due to conflicts in:\n\t%s.",
195 lint.name,
196 "\n\t".join(files_with_overlap),
197 )
198 else:
199 for filepath, new_fixes in new_fixes_by_file.items():
200 fixes_by_file[filepath].extend(new_fixes)
201 lints_fixed.append(lint)
202
203 return fixes_by_file, lints_fixed
204
205
206def has_overlap(
207 prior_fixes: List[toolchain.SuggestedFix],
208 new_fixes: List[toolchain.SuggestedFix],
209) -> bool:
210 """Check if new fixes have overlapping ranges with a prior replacement.
211
212 Note: this implementation is n^2, but the amount of lints in a single file
213 is experimentally pretty small, so optimizing this is probably not a large
214 concern.
215 """
216 for new in new_fixes:
217 for old in prior_fixes:
218 if (
219 (old.location.start_offset <= new.location.start_offset)
220 and (new.location.start_offset <= old.location.end_offset)
221 ) or (
222 (old.location.start_offset <= new.location.end_offset)
223 and (new.location.end_offset <= old.location.end_offset)
224 ):
225 return True
226 return False
227
228
229def apply_edits(content: Text, fixes: List[toolchain.SuggestedFix]) -> Text:
230 """Modify a file by applying a list of fixes."""
231
232 # We need to be able to apply fixes in reverse order within a file to
233 # preserve code locations.
234 def fix_sort_key(fix: toolchain.SuggestedFix) -> int:
235 return fix.location.start_offset
236
237 pieces = []
238 content_end = len(content)
239 for fix in sorted(fixes, key=fix_sort_key, reverse=True):
240 pieces += [
241 content[fix.location.end_offset : content_end],
242 fix.replacement,
243 ]
244 content_end = fix.location.start_offset
245 pieces.append(content[:content_end])
246
247 return "".join(reversed(pieces))
248
249
250def apply_fixes(
251 lints: List[toolchain.LinterFinding],
Ryan Beltrane522fe32023-09-26 23:06:16 +0000252 allowed_subdirs: Optional[List[Text]],
Ryan Beltran179d6bb2023-09-14 23:37:33 +0000253) -> Tuple[List[toolchain.LinterFinding], Iterable[Path]]:
254 """Modify files in Platform2 to apply suggested fixes from linter findings.
255
256 Some fixes which cannot be applied cleanly will be discarded (see the
257 `get_noconflict_fixes_by_file` description for more details).
258
259 Args:
260 lints: LinterFindings to apply potential fixes from.
Ryan Beltrane522fe32023-09-26 23:06:16 +0000261 allowed_subdirs: subdirectories in platform2 that we can modify or none
Ryan Beltran179d6bb2023-09-14 23:37:33 +0000262
263 Returns:
264 A tuple including:
265 0) The list of lints which were fixed
266 1) The list of files which were modified
267 """
268
Ryan Beltrane522fe32023-09-26 23:06:16 +0000269 fixes_by_file, lints_fixed = get_noconflict_fixes(lints, allowed_subdirs)
Ryan Beltran179d6bb2023-09-14 23:37:33 +0000270
271 for filepath, fixes in fixes_by_file.items():
272 file_content = filepath.read_text(encoding="utf-8")
273 rewrite = apply_edits(file_content, fixes)
274 filepath.write_text(rewrite, encoding="utf-8")
275
276 return lints_fixed, fixes_by_file.keys()
277
278
Ryan Beltrance85d0f2022-08-09 21:36:39 +0000279def format_lint(lint: toolchain.LinterFinding) -> Text:
Alex Klein68b270c2023-04-14 14:42:50 -0600280 """Formats a lint for human-readable printing.
Ryan Beltrance85d0f2022-08-09 21:36:39 +0000281
Alex Klein1699fab2022-09-08 08:46:06 -0600282 Example output:
283 [ClangTidy] In 'path/to/file.c' on line 36:
284 Also in 'path/to/file.c' on line 40:
285 Also in 'path/to/file.c' on lines 50-53:
286 You did something bad, don't do it.
Ryan Beltrance85d0f2022-08-09 21:36:39 +0000287
Alex Klein1699fab2022-09-08 08:46:06 -0600288 Args:
Alex Klein68b270c2023-04-14 14:42:50 -0600289 lint: A linter finding from the toolchain service.
Ryan Beltrance85d0f2022-08-09 21:36:39 +0000290
Alex Klein1699fab2022-09-08 08:46:06 -0600291 Returns:
Alex Klein68b270c2023-04-14 14:42:50 -0600292 A correctly formatted string ready to be displayed to the user.
Alex Klein1699fab2022-09-08 08:46:06 -0600293 """
Ryan Beltrance85d0f2022-08-09 21:36:39 +0000294
Alex Klein1699fab2022-09-08 08:46:06 -0600295 color = terminal.Color(True)
296 lines = []
297 linter_prefix = color.Color(
298 terminal.Color.YELLOW,
299 f"[{lint.linter}]",
300 background_color=terminal.Color.BLACK,
301 )
302 for loc in lint.locations:
Ryan Beltran4706f342023-08-29 01:16:22 +0000303 filepath = make_relative_to_cros(loc.filepath)
Alex Klein1699fab2022-09-08 08:46:06 -0600304 if not lines:
305 location_prefix = f"\n{linter_prefix} In"
306 else:
307 location_prefix = " and in"
308 if loc.line_start != loc.line_end:
309 lines.append(
Ryan Beltran4706f342023-08-29 01:16:22 +0000310 f"{location_prefix} '{filepath}' "
Alex Klein1699fab2022-09-08 08:46:06 -0600311 f"lines {loc.line_start}-{loc.line_end}:"
312 )
313 else:
314 lines.append(
Ryan Beltran4706f342023-08-29 01:16:22 +0000315 f"{location_prefix} '{filepath}' line {loc.line_start}:"
Alex Klein1699fab2022-09-08 08:46:06 -0600316 )
317 message_lines = lint.message.split("\n")
318 for line in message_lines:
319 lines.append(f" {line}")
320 lines.append("")
321 return "\n".join(lines)
Ryan Beltrance85d0f2022-08-09 21:36:39 +0000322
323
Ryan Beltrana32a1a12022-09-28 06:03:45 +0000324def json_format_lint(lint: toolchain.LinterFinding) -> Text:
325 """Formats a lint in json for machine parsing.
326
327 Args:
328 lint: A linter finding from the toolchain service.
329
330 Returns:
331 A correctly formatted json string ready to be displayed to the user.
332 """
333
334 def _dictify(original):
335 """Turns namedtuple's to dictionaries recursively."""
336 # Handle namedtuples
337 if isinstance(original, tuple) and hasattr(original, "_asdict"):
338 return _dictify(original._asdict())
339 # Handle collection types
340 elif hasattr(original, "__iter__"):
341 # Handle strings
342 if isinstance(original, (str, bytes)):
343 return original
344 # Handle dictionaries
345 elif isinstance(original, dict):
346 return {k: _dictify(v) for k, v in original.items()}
347 # Handle lists, sets, etc.
348 else:
349 return [_dictify(x) for x in original]
Ryan Beltranc37fb392023-05-11 18:24:40 +0000350 # Handle PackageInfo objects
351 elif isinstance(original, package_info.PackageInfo):
352 return original.atom
Ryan Beltrana32a1a12022-09-28 06:03:45 +0000353 # Handle everything else
354 return original
355
356 return json.dumps(_dictify(lint))
357
358
Ryan Beltran378934c2022-11-23 00:44:26 +0000359def get_all_sysroots() -> List[Text]:
360 """Gets all available sysroots for both host and boards."""
361 host_root = Path(build_target_lib.BuildTarget(None).root)
362 roots = [str(host_root)]
363 build_dir = host_root / "build"
364 for board in os.listdir(build_dir):
365 if board != "bin":
366 board_root = build_dir / board
367 if board_root.is_dir():
368 roots.append(str(board_root))
369 return roots
370
371
Ryan Beltran1f2dd082022-04-25 18:42:32 +0000372def get_arg_parser() -> commandline.ArgumentParser:
Alex Klein1699fab2022-09-08 08:46:06 -0600373 """Creates an argument parser for this script."""
374 default_board = cros_build_lib.GetDefaultBoard()
375 parser = commandline.ArgumentParser(description=__doc__)
Ryan Beltrandbd7b812022-06-08 23:36:16 +0000376
Ryan Beltran378934c2022-11-23 00:44:26 +0000377 board_group = parser.add_mutually_exclusive_group()
Alex Klein1699fab2022-09-08 08:46:06 -0600378 board_group.add_argument(
379 "-b",
380 "--board",
381 "--build-target",
382 dest="board",
383 default=default_board,
384 help="The board to emerge packages for",
385 )
386 board_group.add_argument(
387 "--host", action="store_true", help="emerge for host instead of board."
388 )
Ryan Beltran378934c2022-11-23 00:44:26 +0000389 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600390 "--fetch-only",
391 action="store_true",
Alex Klein68b270c2023-04-14 14:42:50 -0600392 help="Fetch lints from previous run without resetting or calling "
393 "emerge.",
Alex Klein1699fab2022-09-08 08:46:06 -0600394 )
Alex Klein1699fab2022-09-08 08:46:06 -0600395 parser.add_argument(
Ryan Beltran179d6bb2023-09-14 23:37:33 +0000396 "--apply-fixes",
397 action="store_true",
398 help="Apply suggested fixes from linters.",
399 )
400 parser.add_argument(
Ryan Beltranf3a7c812023-09-27 22:17:13 +0000401 "--create-cl",
402 action="store_true",
403 help="Generate a CL for fixes.",
404 )
405 parser.add_argument(
406 "--bug",
407 default="None",
408 help="Sets the tracking bug for the CL if --create_cl is used.",
409 )
410 parser.add_argument(
Ryan Beltranf83a3972023-09-21 00:22:25 +0000411 "--filter-names",
412 help="Only keep lints if the name contains one of the provided filters",
413 action="append",
414 )
415 parser.add_argument(
Ryan Beltrane522fe32023-09-26 23:06:16 +0000416 "--restrict-fix-subdirs",
417 help="Only fix lints if all fixes are in the given directories",
418 action="append",
419 )
420 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600421 "--differential",
422 action="store_true",
423 help="only lint lines touched by the last commit",
424 )
425 parser.add_argument(
426 "-o",
427 "--output",
428 default=sys.stdout,
429 help="File to use instead of stdout.",
430 )
431 parser.add_argument(
432 "--json", action="store_true", help="Output lints in JSON format."
433 )
434 parser.add_argument(
435 "--no-clippy",
436 dest="clippy",
437 action="store_false",
438 help="Disable cargo clippy linter.",
439 )
440 parser.add_argument(
441 "--no-tidy",
442 dest="tidy",
443 action="store_false",
444 help="Disable clang tidy linter.",
445 )
446 parser.add_argument(
447 "--no-golint",
448 dest="golint",
449 action="store_false",
450 help="Disable golint linter.",
451 )
452 parser.add_argument(
Ryan Beltran378934c2022-11-23 00:44:26 +0000453 "--iwyu",
454 action="store_true",
455 help="Enable include-what-you-use linter.",
456 )
457 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600458 "packages",
459 nargs="*",
460 help="package(s) to emerge and retrieve lints for",
461 )
Ryan Beltrane522fe32023-09-26 23:06:16 +0000462
Alex Klein1699fab2022-09-08 08:46:06 -0600463 return parser
Ryan Beltran1f2dd082022-04-25 18:42:32 +0000464
465
466def parse_args(argv: List[str]):
Alex Klein1699fab2022-09-08 08:46:06 -0600467 """Parses arguments in argv and returns the options."""
468 parser = get_arg_parser()
469 opts = parser.parse_args(argv)
470 opts.Freeze()
Ryan Beltrandbd7b812022-06-08 23:36:16 +0000471
Alex Klein1699fab2022-09-08 08:46:06 -0600472 # A package must be specified unless we are in fetch-only mode
473 if not (opts.fetch_only or opts.packages):
Ryan Beltran378934c2022-11-23 00:44:26 +0000474 parser.error("Emerge mode requires specified package(s).")
Alex Klein1699fab2022-09-08 08:46:06 -0600475 if opts.fetch_only and opts.packages:
476 parser.error("Cannot specify packages for fetch-only mode.")
Ryan Beltrandbd7b812022-06-08 23:36:16 +0000477
Ryan Beltran378934c2022-11-23 00:44:26 +0000478 # A board must be specified unless we are in fetch-only mode
479 if not (opts.fetch_only or opts.board or opts.host):
480 parser.error("Emerge mode requires either --board or --host.")
481
Ryan Beltrane522fe32023-09-26 23:06:16 +0000482 # Require apply_fix for flags that only affect this mode
483 if opts.restrict_fix_subdirs and not opts.apply_fixes:
484 parser.error(
485 "--restrict-fix-subdirs is meaningless if fixes aren't applied"
486 )
487
Ryan Beltranf3a7c812023-09-27 22:17:13 +0000488 if opts.create_cl and not opts.apply_fixes:
489 parser.error("--create-cl not allowed if fixes aren't applied")
490
491 if opts.bug != "None" and not opts.create_cl:
492 parser.error("--bug not allowed if a CL is not being created")
493
Alex Klein1699fab2022-09-08 08:46:06 -0600494 return opts
Ryan Beltran1f2dd082022-04-25 18:42:32 +0000495
496
Ryan Beltranf83a3972023-09-21 00:22:25 +0000497def filter_lints(
498 lints: List[toolchain.LinterFinding], names_filters: List[Text]
499) -> List[toolchain.LinterFinding]:
500 """Filter linter finding by name."""
501 return [l for l in lints if any(f in l.name for f in names_filters)]
502
503
Ryan Beltran1f2dd082022-04-25 18:42:32 +0000504def main(argv: List[str]) -> None:
Alex Klein1699fab2022-09-08 08:46:06 -0600505 cros_build_lib.AssertInsideChroot()
506 opts = parse_args(argv)
Ryan Beltran1f2dd082022-04-25 18:42:32 +0000507
Alex Klein1699fab2022-09-08 08:46:06 -0600508 if opts.host:
509 # BuildTarget interprets None as host target
510 build_target = build_target_lib.BuildTarget(None)
Ryan Beltrandbd7b812022-06-08 23:36:16 +0000511 else:
Alex Klein1699fab2022-09-08 08:46:06 -0600512 build_target = build_target_lib.BuildTarget(opts.board)
513 packages = parse_packages(build_target, opts.packages)
514 package_atoms = [x.atom for x in packages]
Ryan Beltran1f2dd082022-04-25 18:42:32 +0000515
Alex Klein1699fab2022-09-08 08:46:06 -0600516 with workon_helper.WorkonScope(build_target, package_atoms):
517 build_linter = toolchain.BuildLinter(
518 packages, build_target.root, opts.differential
519 )
Ryan Beltranf3a7c812023-09-27 22:17:13 +0000520 create_cl = False
521 if opts.apply_fixes and opts.create_cl:
522 if not check_plat2_diff():
523 create_cl = True
524 else:
525 create_cl = cros_build_lib.BooleanPrompt(
526 "Platform2 contains uncommited changes which will be "
527 "added to the generated cl. Would you still like to "
528 "create a CL from fixes?"
529 )
530
Alex Klein1699fab2022-09-08 08:46:06 -0600531 if opts.fetch_only:
Ryan Beltran179d6bb2023-09-14 23:37:33 +0000532 if opts.apply_fixes:
533 logging.warning(
534 "Apply fixes with fetch_only may lead to fixes being"
535 " applied incorrectly if source files have changed!"
536 )
Ryan Beltran378934c2022-11-23 00:44:26 +0000537 if opts.host or opts.board:
538 roots = [build_target.root]
539 else:
540 roots = get_all_sysroots()
541 lints = []
542 for root in roots:
543 build_linter.sysroot = root
544 lints.extend(
545 build_linter.fetch_findings(
546 use_clippy=opts.clippy,
547 use_tidy=opts.tidy,
548 use_golint=opts.golint,
549 use_iwyu=opts.iwyu,
550 )
551 )
Alex Klein1699fab2022-09-08 08:46:06 -0600552 else:
553 lints = build_linter.emerge_with_linting(
554 use_clippy=opts.clippy,
555 use_tidy=opts.tidy,
556 use_golint=opts.golint,
Ryan Beltran378934c2022-11-23 00:44:26 +0000557 use_iwyu=opts.iwyu,
Alex Klein1699fab2022-09-08 08:46:06 -0600558 )
Ryan Beltrance85d0f2022-08-09 21:36:39 +0000559
Ryan Beltranf83a3972023-09-21 00:22:25 +0000560 if opts.filter_names:
561 lints = filter_lints(lints, opts.filter_names)
562
Alex Klein1699fab2022-09-08 08:46:06 -0600563 if opts.json:
Ryan Beltrana32a1a12022-09-28 06:03:45 +0000564 formatted_output_inner = ",\n".join(json_format_lint(l) for l in lints)
565 formatted_output = f"[{formatted_output_inner}]"
Alex Klein1699fab2022-09-08 08:46:06 -0600566 else:
Ryan Beltrana32a1a12022-09-28 06:03:45 +0000567 formatted_output = "\n".join(format_lint(l) for l in lints)
Alex Klein1699fab2022-09-08 08:46:06 -0600568
Ryan Beltran179d6bb2023-09-14 23:37:33 +0000569 if opts.apply_fixes:
Ryan Beltrane522fe32023-09-26 23:06:16 +0000570 fixed_lints, modified_files = apply_fixes(
571 lints, opts.restrict_fix_subdirs
572 )
Ryan Beltran179d6bb2023-09-14 23:37:33 +0000573 if opts.json:
574 formatted_fixes_inner = ",\n".join(
575 json_format_lint(l) for l in lints
576 )
577 formatted_fixes = f"[{formatted_fixes_inner}]"
578 else:
579 formatted_fixes = "\n".join(format_lint(l) for l in fixed_lints)
580
Ryan Beltranf3a7c812023-09-27 22:17:13 +0000581 if create_cl:
582 if fixed_lints:
583 create_fixes_cl(formatted_fixes, opts.bug)
584 else:
585 logging.warning(
586 "Skipped creating CL since no fixes were applied."
587 )
588
Alex Klein1699fab2022-09-08 08:46:06 -0600589 with file_util.Open(opts.output, "w") as output_file:
590 output_file.write(formatted_output)
591 if not opts.json:
592 output_file.write(f"\nFound {len(lints)} lints.")
Ryan Beltran179d6bb2023-09-14 23:37:33 +0000593 if opts.apply_fixes:
594 output_file.write("\n\n\n--------- Fixed Problems ---------\n\n")
595 output_file.write(formatted_fixes)
596 if not opts.json:
597 output_file.write(
598 f"\nFixed {len(fixed_lints)}/{len(lints)} lints."
599 )
600 output_file.write("\n\n\n--------- Modified Files ---------\n\n")
Ryan Beltranf83a3972023-09-21 00:22:25 +0000601 output_file.write("\n".join(str(f) for f in sorted(modified_files)))
Alex Klein1699fab2022-09-08 08:46:06 -0600602 output_file.write("\n")